Increase code coverage

pull/502/head
NextTurn 2018-12-24 00:00:00 +08:00 committed by Next Turn
parent b0c09e1bb1
commit 459d5d7647
10 changed files with 216 additions and 141 deletions

View File

@ -31,7 +31,7 @@ namespace winsw
public readonly string From;
public readonly string To;
public readonly AuthType Auth = AuthType.none;
public readonly AuthType Auth;
public readonly string? Username;
public readonly string? Password;
public readonly bool UnsecureAuth;

View File

@ -17,37 +17,34 @@ namespace winswTests.Configuration
[Test]
public void AllOptionsConfigShouldDeclareDefaults()
{
ServiceDescriptor d = DoLoad("allOptions");
ServiceDescriptor desc = Load("allOptions");
Assert.AreEqual("myapp", d.Id);
Assert.AreEqual("MyApp Service (powered by WinSW)", d.Caption);
Assert.AreEqual("This service is a service created from a sample configuration", d.Description);
Assert.AreEqual("%BASE%\\myExecutable.exe", d.Executable);
Assert.That(desc.Id, Is.EqualTo("myapp"));
Assert.That(desc.Caption, Is.EqualTo("MyApp Service (powered by WinSW)"));
Assert.That(desc.Description, Is.EqualTo("This service is a service created from a sample configuration"));
Assert.That(desc.Executable, Is.EqualTo("%BASE%\\myExecutable.exe"));
ServiceDescriptorAssert.AssertAllOptionalPropertiesAreDefault(d);
ServiceDescriptorAssert.AssertAllOptionalPropertiesAreDefault(desc);
}
[Test]
public void MinimalConfigShouldDeclareDefaults()
{
ServiceDescriptor d = DoLoad("minimal");
ServiceDescriptor desc = Load("minimal");
Assert.AreEqual("myapp", d.Id);
Assert.AreEqual("MyApp Service (powered by WinSW)", d.Caption);
Assert.AreEqual("This service is a service created from a minimal configuration", d.Description);
Assert.AreEqual("%BASE%\\myExecutable.exe", d.Executable);
Assert.That(desc.Id, Is.EqualTo("myapp"));
Assert.That(desc.Caption, Is.EqualTo("MyApp Service (powered by WinSW)"));
Assert.That(desc.Description, Is.EqualTo("This service is a service created from a minimal configuration"));
Assert.That(desc.Executable, Is.EqualTo("%BASE%\\myExecutable.exe"));
ServiceDescriptorAssert.AssertAllOptionalPropertiesAreDefault(d);
ServiceDescriptorAssert.AssertAllOptionalPropertiesAreDefault(desc);
}
private ServiceDescriptor DoLoad(string exampleName)
private ServiceDescriptor Load(string exampleName)
{
var dir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string path = Path.GetFullPath(dir + "\\..\\..\\..\\..\\..\\..\\examples\\sample-" + exampleName + ".xml");
if (!File.Exists(path))
{
throw new FileNotFoundException("Cannot find the XML file " + path, path);
}
var directory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string path = Path.GetFullPath($@"{directory}\..\..\..\..\..\..\examples\sample-{exampleName}.xml");
Assert.That(path, Does.Exist);
XmlDocument dom = new XmlDocument();
dom.Load(path);

View File

@ -1,5 +1,8 @@
using System;
using System.IO;
using System.IO;
using System.Net;
#if VNEXT
using System.Threading.Tasks;
#endif
using NUnit.Framework;
using winsw;
using winswTests.Util;
@ -12,6 +15,56 @@ namespace winswTests
private const string From = "https://www.nosuchhostexists.foo.myorg/foo.xml";
private const string To = "%BASE%\\foo.xml";
[Test]
#if VNEXT
public async Task DownloadFileAsync()
#else
public void DownloadFile()
#endif
{
string from = Path.GetTempFileName();
string to = Path.GetTempFileName();
try
{
const string contents = "WinSW";
File.WriteAllText(from, contents);
#if VNEXT
await new Download(from, to).PerformAsync();
#else
new Download(from, to).Perform();
#endif
Assert.That(File.ReadAllText(to), Is.EqualTo(contents));
}
finally
{
File.Delete(from);
File.Delete(to);
}
}
[Test]
public void DownloadFile_NonExistent()
{
string from = Path.GetTempPath() + Path.GetRandomFileName();
string to = Path.GetTempFileName();
try
{
Assert.That(
#if VNEXT
async () => await new Download(from, to).PerformAsync(),
#else
() => new Download(from, to).Perform(),
#endif
Throws.TypeOf<WebException>());
}
finally
{
File.Delete(to);
}
}
[Test]
public void Roundtrip_Defaults()
{
@ -23,11 +76,11 @@ namespace winswTests
var loaded = GetSingleEntry(sd);
// Check default values
Assert.That(loaded.FailOnError, Is.EqualTo(false));
Assert.That(loaded.FailOnError, Is.False);
Assert.That(loaded.Auth, Is.EqualTo(Download.AuthType.none));
Assert.That(loaded.Username, Is.Null);
Assert.That(loaded.Password, Is.Null);
Assert.That(loaded.UnsecureAuth, Is.EqualTo(false));
Assert.That(loaded.UnsecureAuth, Is.False);
}
[Test]
@ -41,11 +94,11 @@ namespace winswTests
var loaded = GetSingleEntry(sd);
// Check default values
Assert.That(loaded.FailOnError, Is.EqualTo(true));
Assert.That(loaded.FailOnError, Is.True);
Assert.That(loaded.Auth, Is.EqualTo(Download.AuthType.basic));
Assert.That(loaded.Username, Is.EqualTo("aUser"));
Assert.That(loaded.Password, Is.EqualTo("aPassword"));
Assert.That(loaded.UnsecureAuth, Is.EqualTo(true));
Assert.That(loaded.UnsecureAuth, Is.True);
}
[Test]
@ -59,32 +112,34 @@ namespace winswTests
var loaded = GetSingleEntry(sd);
// Check default values
Assert.That(loaded.FailOnError, Is.EqualTo(false));
Assert.That(loaded.FailOnError, Is.False);
Assert.That(loaded.Auth, Is.EqualTo(Download.AuthType.sspi));
Assert.That(loaded.Username, Is.Null);
Assert.That(loaded.Password, Is.Null);
Assert.That(loaded.UnsecureAuth, Is.EqualTo(false));
Assert.That(loaded.UnsecureAuth, Is.False);
}
[TestCase("http://")]
[TestCase("ftp://")]
[TestCase("file:///")]
[TestCase("file://")]
[TestCase("jar://")]
[TestCase("\\\\")] // UNC
public void ShouldReject_BasicAuth_with_UnsecureProtocol(string protocolPrefix)
public void RejectBasicAuth_With_UnsecureProtocol(string protocolPrefix)
{
var d = new Download(protocolPrefix + "myServer.com:8080/file.txt", To,
auth: Download.AuthType.basic, username: "aUser", password: "aPassword");
AssertInitializationFails(d, "you're sending your credentials in clear text to the server");
string unsecureFrom = protocolPrefix + "myServer.com:8080/file.txt";
var d = new Download(unsecureFrom, To, auth: Download.AuthType.basic, username: "aUser", password: "aPassword");
AssertInitializationFails(d, "Warning: you're sending your credentials in clear text to the server");
}
public void ShouldRejectBasicAuth_without_username()
[Test]
public void RejectBasicAuth_Without_Username()
{
var d = new Download(From, To, auth: Download.AuthType.basic, username: null, password: "aPassword");
AssertInitializationFails(d, "Basic Auth is enabled, but username is not specified");
}
public void ShouldRejectBasicAuth_without_password()
[Test]
public void RejectBasicAuth_Without_Password()
{
var d = new Download(From, To, auth: Download.AuthType.basic, username: "aUser", password: null);
AssertInitializationFails(d, "Basic Auth is enabled, but password is not specified");
@ -144,7 +199,7 @@ namespace winswTests
.WithRawEntry("<download from=\"http://www.nosuchhostexists.foo.myorg/foo.xml\" to=\"%BASE%\\foo.xml\" auth=\"digest\"/>")
.ToServiceDescriptor(true);
ExceptionHelper.AssertFails("Cannot parse <auth> Enum value from string 'digest'", typeof(InvalidDataException), () => _ = GetSingleEntry(sd));
Assert.That(() => GetSingleEntry(sd), Throws.TypeOf<InvalidDataException>().With.Message.StartsWith("Cannot parse <auth> Enum value from string 'digest'"));
}
private Download GetSingleEntry(ServiceDescriptor sd)
@ -154,13 +209,13 @@ namespace winswTests
return downloads[0];
}
private void AssertInitializationFails(Download download, string expectedMessagePart = null, Type expectedExceptionType = null)
private void AssertInitializationFails(Download download, string expectedMessagePart = null)
{
var sd = ConfigXmlBuilder.create()
.WithDownload(download)
.ToServiceDescriptor(true);
ExceptionHelper.AssertFails(expectedMessagePart, expectedExceptionType ?? typeof(InvalidDataException), () => _ = GetSingleEntry(sd));
Assert.That(() => GetSingleEntry(sd), Throws.TypeOf<InvalidDataException>().With.Message.StartsWith(expectedMessagePart));
}
}
}

View File

@ -1,4 +1,7 @@
using NUnit.Framework;
using System;
using System.Security.Principal;
using System.ServiceProcess;
using NUnit.Framework;
using winsw;
using winswTests.Util;
@ -12,7 +15,7 @@ namespace winswTests
{
string expectedVersion = WrapperService.Version.ToString();
string cliOut = CLITestHelper.CLITest(new[] { "version" });
StringAssert.Contains(expectedVersion, cliOut, "Expected that version contains " + expectedVersion);
Assert.That(cliOut, Does.Contain(expectedVersion));
}
[Test]
@ -21,14 +24,14 @@ namespace winswTests
string expectedVersion = WrapperService.Version.ToString();
string cliOut = CLITestHelper.CLITest(new[] { "help" });
StringAssert.Contains(expectedVersion, cliOut, "Expected that help contains " + expectedVersion);
StringAssert.Contains("start", cliOut, "Expected that help refers start command");
StringAssert.Contains("help", cliOut, "Expected that help refers help command");
StringAssert.Contains("version", cliOut, "Expected that help refers version command");
Assert.That(cliOut, Does.Contain(expectedVersion));
Assert.That(cliOut, Does.Contain("start"));
Assert.That(cliOut, Does.Contain("help"));
Assert.That(cliOut, Does.Contain("version"));
// TODO: check all commands after the migration of ccommands to enum
// Extra options
StringAssert.Contains("/redirect", cliOut, "Expected that help message refers the redirect message");
Assert.That(cliOut, Does.Contain("/redirect"));
}
[Test]
@ -36,12 +39,11 @@ namespace winswTests
{
const string commandName = "nonExistentCommand";
string expectedMessage = "Unknown command: " + commandName;
CLITestResult res = CLITestHelper.CLIErrorTest(new[] { commandName });
CLITestResult result = CLITestHelper.CLIErrorTest(new[] { commandName });
Assert.True(res.HasException, "Expected an exception due to the wrong command");
StringAssert.Contains(expectedMessage, res.Out, "Expected the message about unknown command");
// ReSharper disable once PossibleNullReferenceException
StringAssert.Contains(expectedMessage, res.Exception.Message, "Expected the message about unknown command");
Assert.That(result.HasException, Is.True);
Assert.That(result.Out, Does.Contain(expectedMessage));
Assert.That(result.Exception.Message, Does.Contain(expectedMessage));
}
/// <summary>
@ -51,7 +53,7 @@ namespace winswTests
public void ShouldNotPrintLogsForStatusCommand()
{
string cliOut = CLITestHelper.CLITest(new[] { "status" });
StringAssert.AreEqualIgnoringCase("NonExistent\r\n", cliOut);
Assert.That(cliOut, Is.EqualTo("NonExistent" + Environment.NewLine).IgnoreCase);
}
}
}

View File

@ -70,7 +70,7 @@ $@"<service>
</service>";
_extendedServiceDescriptor = ServiceDescriptor.FromXML(seedXml);
Assert.Throws<ArgumentException>(() => _ = _extendedServiceDescriptor.StartMode);
Assert.That(() => _extendedServiceDescriptor.StartMode, Throws.ArgumentException);
}
[Test]
@ -109,7 +109,7 @@ $@"<service>
[Test]
public void VerifyServiceLogonRight()
{
Assert.That(_extendedServiceDescriptor.AllowServiceAcountLogonRight, Is.EqualTo(true));
Assert.That(_extendedServiceDescriptor.AllowServiceAcountLogonRight, Is.True);
}
[Test]
@ -142,7 +142,7 @@ $@"<service>
[Test]
public void StopParentProcessFirstIsFalseByDefault()
{
Assert.False(_extendedServiceDescriptor.StopParentProcessFirst);
Assert.That(_extendedServiceDescriptor.StopParentProcessFirst, Is.False);
}
[Test]
@ -153,7 +153,7 @@ $@"<service>
+ "</service>";
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
Assert.True(serviceDescriptor.StopParentProcessFirst);
Assert.That(serviceDescriptor.StopParentProcessFirst, Is.True);
}
[Test]
@ -197,7 +197,7 @@ $@"<service>
+ "</service>";
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
Assert.That(serviceDescriptor.OutFileDisabled, Is.EqualTo(true));
Assert.That(serviceDescriptor.OutFileDisabled, Is.True);
}
[Test]
@ -208,7 +208,7 @@ $@"<service>
+ "</service>";
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
Assert.That(serviceDescriptor.ErrFileDisabled, Is.EqualTo(true));
Assert.That(serviceDescriptor.ErrFileDisabled, Is.True);
}
[Test]
@ -248,7 +248,7 @@ $@"<service>
serviceDescriptor.BaseName = "service";
var logHandler = serviceDescriptor.LogHandler as SizeBasedRollingLogAppender;
Assert.NotNull(logHandler);
Assert.That(logHandler, Is.Not.Null);
Assert.That(logHandler.SizeTheshold, Is.EqualTo(112 * 1024));
Assert.That(logHandler.FilesToKeep, Is.EqualTo(113));
}
@ -268,7 +268,7 @@ $@"<service>
serviceDescriptor.BaseName = "service";
var logHandler = serviceDescriptor.LogHandler as TimeBasedRollingLogAppender;
Assert.NotNull(logHandler);
Assert.That(logHandler, Is.Not.Null);
Assert.That(logHandler.Period, Is.EqualTo(7));
Assert.That(logHandler.Pattern, Is.EqualTo("log pattern"));
}
@ -289,7 +289,7 @@ $@"<service>
serviceDescriptor.BaseName = "service";
var logHandler = serviceDescriptor.LogHandler as RollingSizeTimeLogAppender;
Assert.NotNull(logHandler);
Assert.That(logHandler, Is.Not.Null);
Assert.That(logHandler.SizeTheshold, Is.EqualTo(10240 * 1024));
Assert.That(logHandler.FilePattern, Is.EqualTo("yyyy-MM-dd"));
Assert.That(logHandler.AutoRollAtTime, Is.EqualTo((TimeSpan?)new TimeSpan(0, 0, 0)));
@ -307,7 +307,7 @@ $@"<service>
+ "</serviceaccount>"
+ "</service>";
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
Assert.That(serviceDescriptor.AllowServiceAcountLogonRight, Is.EqualTo(false));
Assert.That(serviceDescriptor.AllowServiceAcountLogonRight, Is.False);
}
[Test]
@ -321,7 +321,7 @@ $@"<service>
+ "</serviceaccount>"
+ "</service>";
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
Assert.That(serviceDescriptor.AllowServiceAcountLogonRight, Is.EqualTo(false));
Assert.That(serviceDescriptor.AllowServiceAcountLogonRight, Is.False);
}
[Test]

View File

@ -1,5 +1,6 @@
using System;
using System.IO;
using NUnit.Framework;
using winsw;
namespace winswTests.Util
@ -9,10 +10,13 @@ namespace winswTests.Util
/// </summary>
public static class CLITestHelper
{
private const string SeedXml =
@"<service>
<id>service.exe</id>
<name>Service</name>
public const string Id = "WinSW.Tests";
public const string Name = "WinSW Test Service";
private static readonly string SeedXml =
$@"<service>
<id>{Id}</id>
<name>{Name}</name>
<description>The service.</description>
<executable>node.exe</executable>
<arguments>My Arguments</arguments>
@ -21,66 +25,79 @@ namespace winswTests.Util
<logpath>C:\winsw\logs</logpath>
</service>";
private static readonly ServiceDescriptor DefaultServiceDescriptor = ServiceDescriptor.FromXML(SeedXml);
public static readonly ServiceDescriptor DefaultServiceDescriptor = ServiceDescriptor.FromXML(SeedXml);
/// <summary>
/// Runs a simle test, which returns the output CLI
/// </summary>
/// <param name="args">CLI arguments to be passed</param>
/// <param name="arguments">CLI arguments to be passed</param>
/// <param name="descriptor">Optional Service descriptor (will be used for initializationpurposes)</param>
/// <returns>STDOUT if there's no exceptions</returns>
/// <exception cref="Exception">Command failure</exception>
public static string CLITest(string[] args, ServiceDescriptor descriptor = null)
public static string CLITest(string[] arguments, ServiceDescriptor descriptor = null)
{
using StringWriter sw = new StringWriter();
TextWriter tmp = Console.Out;
Console.SetOut(sw);
WrapperService.Run(args, descriptor ?? DefaultServiceDescriptor);
Console.SetOut(tmp);
Console.Write(sw.ToString());
return sw.ToString();
TextWriter tmpOut = Console.Out;
TextWriter tmpErr = Console.Error;
using StringWriter swOut = new StringWriter();
using StringWriter swErr = new StringWriter();
Console.SetOut(swOut);
Console.SetError(swErr);
try
{
WrapperService.Run(arguments, descriptor ?? DefaultServiceDescriptor);
}
finally
{
Console.SetOut(tmpOut);
Console.SetError(tmpErr);
}
Assert.That(swErr.GetStringBuilder().Length, Is.Zero);
Console.Write(swOut.ToString());
return swOut.ToString();
}
/// <summary>
/// Runs a simle test, which returns the output CLI
/// </summary>
/// <param name="args">CLI arguments to be passed</param>
/// <param name="arguments">CLI arguments to be passed</param>
/// <param name="descriptor">Optional Service descriptor (will be used for initializationpurposes)</param>
/// <returns>Test results</returns>
public static CLITestResult CLIErrorTest(string[] args, ServiceDescriptor descriptor = null)
public static CLITestResult CLIErrorTest(string[] arguments, ServiceDescriptor descriptor = null)
{
StringWriter swOut, swErr;
Exception testEx = null;
TextWriter tmpOut = Console.Out;
TextWriter tmpErr = Console.Error;
using (swOut = new StringWriter())
using (swErr = new StringWriter())
using StringWriter swOut = new StringWriter();
using StringWriter swErr = new StringWriter();
Console.SetOut(swOut);
Console.SetError(swErr);
try
{
try
{
Console.SetOut(swOut);
Console.SetError(swErr);
WrapperService.Run(args, descriptor ?? DefaultServiceDescriptor);
}
catch (Exception ex)
{
testEx = ex;
}
finally
{
Console.SetOut(tmpOut);
Console.SetError(tmpErr);
Console.WriteLine("\n>>> Output: ");
Console.Write(swOut.ToString());
Console.WriteLine("\n>>> Error: ");
Console.Write(swErr.ToString());
if (testEx != null)
{
Console.WriteLine("\n>>> Exception: ");
Console.WriteLine(testEx);
}
}
WrapperService.Run(arguments, descriptor ?? DefaultServiceDescriptor);
}
catch (Exception ex)
{
testEx = ex;
}
finally
{
Console.SetOut(tmpOut);
Console.SetError(tmpErr);
}
Console.WriteLine("\n>>> Output: ");
Console.Write(swOut.ToString());
Console.WriteLine("\n>>> Error: ");
Console.Write(swErr.ToString());
if (testEx != null)
{
Console.WriteLine("\n>>> Exception: ");
Console.WriteLine(testEx);
}
return new CLITestResult(swOut.ToString(), swErr.ToString(), testEx);
@ -92,18 +109,18 @@ namespace winswTests.Util
/// </summary>
public class CLITestResult
{
public string Out { get; private set; }
public string Out { get; }
public string Err { get; private set; }
public string Error { get; }
public Exception Exception { get; private set; }
public Exception Exception { get; }
public bool HasException => Exception != null;
public CLITestResult(string output, string err, Exception exception = null)
public CLITestResult(string output, string error, Exception exception = null)
{
Out = output;
Err = err;
Error = error;
Exception = exception;
}
}

View File

@ -126,27 +126,37 @@ namespace winswTests.Util
public ConfigXmlBuilder WithDownload(Download download)
{
StringBuilder str = new StringBuilder();
str.AppendFormat("<download from=\"{0}\" to=\"{1}\" failOnError=\"{2}\"", new object[] { download.From, download.To, download.FailOnError });
StringBuilder xml = new StringBuilder();
xml.Append($"<download from=\"{download.From}\" to=\"{download.To}\" failOnError=\"{download.FailOnError}\"");
// Authentication
if (download.Auth != Download.AuthType.none)
{
str.AppendFormat(" auth=\"{0}\"", download.Auth);
xml.Append($" auth=\"{download.Auth}\"");
if (download.Auth == Download.AuthType.basic)
{
str.AppendFormat(" user=\"{0}\" password=\"{1}\"", new object[] { download.Username, download.Password });
string username = download.Username;
if (username != null)
{
xml.Append($" user=\"{username}\"");
}
string password = download.Password;
if (password != null)
{
xml.Append($" password=\"{password}\"");
}
}
if (download.UnsecureAuth)
{
str.AppendFormat(" unsecureAuth=\"true\"");
xml.Append(" unsecureAuth=\"true\"");
}
}
str.Append("/>");
xml.Append("/>");
return WithRawEntry(str.ToString());
return WithRawEntry(xml.ToString());
}
public ConfigXmlBuilder WithDelayedAutoStart()

View File

@ -1,14 +0,0 @@
using System;
using NUnit.Framework;
namespace winswTests.Util
{
class ExceptionHelper
{
public static void AssertFails(string expectedMessagePart, Type expectedExceptionType, TestDelegate body)
{
Exception exception = Assert.Throws(expectedExceptionType ?? typeof(Exception), body);
StringAssert.Contains(expectedMessagePart, exception.Message);
}
}
}

View File

@ -11,28 +11,28 @@ namespace winswTests.Util
// TODO: convert to Extension attributes once the .NET dependency is upgraded
// BTW there is a way to get them working in .NET2, but KISS
public static void AssertPropertyIsDefault(ServiceDescriptor d, string property)
public static void AssertPropertyIsDefault(ServiceDescriptor desc, string property)
{
PropertyInfo actualProperty = typeof(ServiceDescriptor).GetProperty(property);
Assert.IsNotNull(actualProperty, "Cannot find property " + property + " in the service descriptor" + d);
PropertyInfo defaultProperty = typeof(DefaultWinSWSettings).GetProperty(property);
Assert.IsNotNull(defaultProperty, "Cannot find property " + property + " in the default settings");
Assert.That(actualProperty, Is.Not.Null);
Assert.AreEqual(defaultProperty.GetValue(ServiceDescriptor.Defaults, null), actualProperty.GetValue(d, null),
"Value of property " + property + " does not equal to the default one");
PropertyInfo defaultProperty = typeof(DefaultWinSWSettings).GetProperty(property);
Assert.That(defaultProperty, Is.Not.Null);
Assert.That(actualProperty.GetValue(desc, null), Is.EqualTo(defaultProperty.GetValue(ServiceDescriptor.Defaults, null)));
}
public static void AssertPropertyIsDefault(ServiceDescriptor d, List<string> properties)
public static void AssertPropertyIsDefault(ServiceDescriptor desc, List<string> properties)
{
foreach (var prop in properties)
{
AssertPropertyIsDefault(d, prop);
AssertPropertyIsDefault(desc, prop);
}
}
public static void AssertAllOptionalPropertiesAreDefault(ServiceDescriptor d)
public static void AssertAllOptionalPropertiesAreDefault(ServiceDescriptor desc)
{
AssertPropertyIsDefault(d, AllOptionalProperties);
AssertPropertyIsDefault(desc, AllOptionalProperties);
}
private static List<string> AllProperties

View File

@ -8,12 +8,20 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="1.2.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="log4net" Version="2.0.8" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.16.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
<PackageReference Include="System.ServiceProcess.ServiceController" Version="4.7.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp3.1'">
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" />
<Reference Include="System.ServiceProcess" />