Increase code coverage

pull/531/head
NextTurn 2019-12-09 00:00:00 +08:00
parent 70d83c045c
commit 94668c9f77
No known key found for this signature in database
GPG Key ID: 17A0D50ADDE1A0C4
9 changed files with 332 additions and 62 deletions

View File

@ -1044,7 +1044,7 @@ namespace winsw
appenders.ToArray());
}
private static unsafe bool IsProcessElevated()
internal static unsafe bool IsProcessElevated()
{
IntPtr process = Kernel32.GetCurrentProcess();
if (!Advapi32.OpenProcessToken(process, TokenAccessLevels.Read, out IntPtr token))

View File

@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("winswTests")]

View File

@ -12,7 +12,7 @@ namespace winswTests.Configuration
/// The test uses a relative path to example files, which is based on the current project structure.
/// </summary>
[TestFixture]
class ExamplesTest
public class ExamplesTest
{
[Test]
public void AllOptionsConfigShouldDeclareDefaults()
@ -40,10 +40,21 @@ namespace winswTests.Configuration
ServiceDescriptorAssert.AssertAllOptionalPropertiesAreDefault(desc);
}
private ServiceDescriptor Load(string exampleName)
private static ServiceDescriptor Load(string exampleName)
{
var directory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string path = Path.GetFullPath($@"{directory}\..\..\..\..\..\..\examples\sample-{exampleName}.xml");
string directory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
while (true)
{
if (File.Exists(Path.Combine(directory, ".gitignore")))
{
break;
}
directory = Path.GetDirectoryName(directory);
Assert.That(directory, Is.Not.Null);
}
string path = Path.Combine(directory, $@"examples\sample-{exampleName}.xml");
Assert.That(path, Does.Exist);
XmlDocument dom = new XmlDocument();

View File

@ -1,8 +1,4 @@
using System.IO;
using System.Net;
#if VNEXT
using System.Threading.Tasks;
#endif
using NUnit.Framework;
using winsw;
using winswTests.Util;
@ -10,61 +6,11 @@ using winswTests.Util;
namespace winswTests
{
[TestFixture]
class DownloadTest
public class DownloadConfigTests
{
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()
{

View File

@ -0,0 +1,231 @@
#if VNEXT
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using NUnit.Framework;
using winsw;
using winswTests.Util;
namespace winswTests
{
[TestFixture]
public class DownloadTests
{
private readonly HttpListener globalListener = new HttpListener();
private readonly byte[] contents = { 0x57, 0x69, 0x6e, 0x53, 0x57 };
private string globalPrefix;
[OneTimeSetUp]
public void SetUp()
{
TcpListener tcpListener = new TcpListener(IPAddress.Loopback, 0);
tcpListener.Start();
int port = ((IPEndPoint)tcpListener.LocalEndpoint).Port;
string prefix = $"http://localhost:{port}/";
this.globalListener.Prefixes.Add(prefix);
this.globalPrefix = prefix;
{
tcpListener.Stop();
this.globalListener.Start();
}
}
[OneTimeTearDown]
public void TearDown()
{
this.globalListener.Stop();
this.globalListener.Close();
}
private async Task TestClientServerAsync(Func<string, string, Task> client, Action<HttpListenerContext> server, AuthenticationSchemes authenticationSchemes = AuthenticationSchemes.Anonymous, [CallerMemberName] string path = null)
{
HttpListener listener = new HttpListener();
string prefix = $"{this.globalPrefix}{path}/";
listener.Prefixes.Add(prefix);
listener.AuthenticationSchemes = authenticationSchemes;
listener.Start();
Task serverTask = null;
try
{
serverTask = ListenAsync();
string dest = Path.GetTempFileName();
try
{
await client(prefix, dest);
}
finally
{
File.Delete(dest);
}
}
finally
{
listener.Stop();
listener.Close();
if (serverTask != null)
{
await serverTask;
}
}
async Task ListenAsync()
{
HttpListenerContext context = await listener.GetContextAsync();
try
{
server(context);
}
catch
{
context.Response.Abort();
}
}
}
[Test]
public async Task TestHttpAsync()
{
await this.TestClientServerAsync(
async (source, dest) =>
{
await new Download(source, dest).PerformAsync();
Assert.That(File.ReadAllBytes(dest), Is.EqualTo(this.contents));
},
context =>
{
context.Response.OutputStream.Write(this.contents, 0, this.contents.Length);
context.Response.Close();
});
}
[Test]
public async Task TestHttp_NoAuthAsync()
{
await this.TestClientServerAsync(
async (source, dest) =>
{
await new Download(source, dest, false, Download.AuthType.none).PerformAsync();
Assert.That(File.ReadAllBytes(dest), Is.EqualTo(this.contents));
},
context =>
{
if (((WebHeaderCollection)context.Request.Headers)[HttpRequestHeader.Authorization] != null)
{
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
context.Response.Close();
}
context.Response.OutputStream.Write(this.contents, 0, this.contents.Length);
context.Response.Close();
});
}
[Test]
public async Task TestHttp_BasicAuthAsync()
{
const string username = nameof(username);
const string password = nameof(password);
await this.TestClientServerAsync(
async (source, dest) =>
{
await new Download(source, dest, false, Download.AuthType.basic, username, password, true).PerformAsync();
Assert.That(File.ReadAllBytes(dest), Is.EqualTo(this.contents));
},
context =>
{
HttpListenerBasicIdentity identity = (HttpListenerBasicIdentity)context.User.Identity;
if (identity.Name != username || identity.Password != password)
{
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
context.Response.Close();
}
context.Response.OutputStream.Write(this.contents, 0, this.contents.Length);
context.Response.Close();
},
AuthenticationSchemes.Basic);
}
[Test]
public async Task TestHttp_IfModifiedSince_ModifiedAsync()
{
DateTime lastModified = DateTime.Now.TrimToSeconds();
DateTime prevModified = lastModified.AddDays(-1);
await this.TestClientServerAsync(
async (source, dest) =>
{
File.WriteAllBytes(dest, this.contents);
File.SetLastWriteTime(dest, prevModified);
await new Download(source, dest).PerformAsync();
Assert.That(File.GetLastWriteTime(dest), Is.EqualTo(lastModified));
Assert.That(File.ReadAllBytes(dest), Is.Not.EqualTo(this.contents));
},
context =>
{
string ifModifiedSince = ((WebHeaderCollection)context.Request.Headers)[HttpRequestHeader.IfModifiedSince];
if (ifModifiedSince != null && DateTime.Parse(ifModifiedSince) >= lastModified)
{
context.Response.StatusCode = (int)HttpStatusCode.NotModified;
}
context.Response.Headers[HttpResponseHeader.LastModified] = lastModified.ToUniversalTime().ToString("r");
context.Response.Close();
});
}
[Test]
public async Task TestHttp_IfModifiedSince_NotModifiedAsync()
{
DateTime lastModified = DateTime.Now.TrimToSeconds();
await this.TestClientServerAsync(
async (source, dest) =>
{
File.WriteAllBytes(dest, this.contents);
File.SetLastWriteTime(dest, lastModified);
await new Download(source, dest).PerformAsync();
Assert.That(File.GetLastWriteTime(dest), Is.EqualTo(lastModified));
Assert.That(File.ReadAllBytes(dest), Is.EqualTo(this.contents));
},
context =>
{
string ifModifiedSince = ((WebHeaderCollection)context.Request.Headers)[HttpRequestHeader.IfModifiedSince];
if (ifModifiedSince != null && DateTime.Parse(ifModifiedSince) >= lastModified)
{
context.Response.StatusCode = (int)HttpStatusCode.NotModified;
}
context.Response.Headers[HttpResponseHeader.LastModified] = lastModified.ToUniversalTime().ToString("r");
context.Response.Close();
});
}
[Test]
public async Task TestHttp_NotFound_ThrowsAsync()
{
await this.TestClientServerAsync(
async (source, dest) =>
{
WebException exception = await AsyncAssert.ThrowsAsync<WebException>(
async () => await new Download(source, dest).PerformAsync());
Assert.That(exception.Status, Is.EqualTo(WebExceptionStatus.ProtocolError));
},
context =>
{
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
context.Response.Close();
});
}
}
}
#endif

View File

@ -1,5 +1,4 @@
using System;
using System.Security.Principal;
using System.ServiceProcess;
using NUnit.Framework;
using winsw;
@ -8,8 +7,31 @@ using winswTests.Util;
namespace winswTests
{
[TestFixture]
class MainTest
public class MainTest
{
[Test]
public void TestInstall()
{
TestHelper.RequireProcessElevated();
try
{
_ = CLITestHelper.CLITest(new[] { "install" });
using ServiceController controller = new ServiceController(CLITestHelper.Id);
Assert.That(controller.DisplayName, Is.EqualTo(CLITestHelper.Name));
Assert.That(controller.CanStop, Is.False);
Assert.That(controller.CanShutdown, Is.False);
Assert.That(controller.CanPauseAndContinue, Is.False);
Assert.That(controller.Status, Is.EqualTo(ServiceControllerStatus.Stopped));
Assert.That(controller.ServiceType, Is.EqualTo(ServiceType.Win32OwnProcess));
}
finally
{
_ = CLITestHelper.CLITest(new[] { "uninstall" });
}
}
[Test]
public void PrintVersion()
{

View File

@ -0,0 +1,29 @@
#if VNEXT
using System;
using System.Threading.Tasks;
using NUnit.Framework;
using NUnit.Framework.Constraints;
namespace winswTests.Util
{
internal static class AsyncAssert
{
internal static async Task<TActual> ThrowsAsync<TActual>(AsyncTestDelegate code)
where TActual : Exception
{
Exception caught = null;
try
{
await code();
}
catch (Exception e)
{
caught = e;
}
Assert.That(caught, new ExceptionTypeConstraint(typeof(TActual)));
return (TActual)caught;
}
}
}
#endif

View File

@ -0,0 +1,12 @@
#if VNEXT
using System;
namespace winswTests.Util
{
internal static class DateTimeExtensions
{
internal static DateTime TrimToSeconds(this DateTime dateTime) =>
dateTime.AddTicks(-(dateTime.Ticks % TimeSpan.TicksPerSecond));
}
}
#endif

View File

@ -0,0 +1,16 @@
using NUnit.Framework;
using winsw;
namespace winswTests.Util
{
internal static class TestHelper
{
internal static void RequireProcessElevated()
{
if (!WrapperService.IsProcessElevated())
{
Assert.Ignore();
}
}
}
}