From 1ee27971a5af7c729d79008c63436e5de233da87 Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Tue, 8 Nov 2016 09:58:45 -0800 Subject: [PATCH] Add test for SIGTERM functionality (#878) --- Hosting.sln | 33 +++++- .../Deployers/SelfHostDeployer.cs | 26 ++--- .../project.json | 1 + .../xunit/TestProjectHelpers.cs | 32 ++++++ ...t.AspNetCore.Hosting.FunctionalTests.xproj | 17 ++++ .../ShutdownTests.cs | 94 ++++++++++++++++++ .../project.json | 34 +++++++ .../.Program.cs.swp | Bin 0 -> 12288 bytes ...crosoft.AspNetCore.Hosting.TestSites.xproj | 17 ++++ .../Program.cs | 44 ++++++++ .../StartupShutdown.cs | 37 +++++++ .../project.json | 23 +++++ 12 files changed, 343 insertions(+), 15 deletions(-) create mode 100644 src/Microsoft.AspNetCore.Server.IntegrationTesting/xunit/TestProjectHelpers.cs create mode 100644 test/Microsoft.AspNetCore.Hosting.FunctionalTests/Microsoft.AspNetCore.Hosting.FunctionalTests.xproj create mode 100644 test/Microsoft.AspNetCore.Hosting.FunctionalTests/ShutdownTests.cs create mode 100644 test/Microsoft.AspNetCore.Hosting.FunctionalTests/project.json create mode 100644 test/Microsoft.AspNetCore.Hosting.TestSites/.Program.cs.swp create mode 100644 test/Microsoft.AspNetCore.Hosting.TestSites/Microsoft.AspNetCore.Hosting.TestSites.xproj create mode 100644 test/Microsoft.AspNetCore.Hosting.TestSites/Program.cs create mode 100644 test/Microsoft.AspNetCore.Hosting.TestSites/StartupShutdown.cs create mode 100644 test/Microsoft.AspNetCore.Hosting.TestSites/project.json diff --git a/Hosting.sln b/Hosting.sln index e00cee06..96a30a67 100644 --- a/Hosting.sln +++ b/Hosting.sln @@ -1,7 +1,6 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.24720.0 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E0497F39-AFFB-4819-A116-E39E361915AB}" EndProject @@ -32,6 +31,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SampleStartups", "samples\S EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.Hosting.WindowsServices", "src\Microsoft.AspNetCore.Hosting.WindowsServices\Microsoft.AspNetCore.Hosting.WindowsServices.xproj", "{03148731-EA95-40A2-BAE8-A12315EA1748}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.Hosting.FunctionalTests", "test\Microsoft.AspNetCore.Hosting.FunctionalTests\Microsoft.AspNetCore.Hosting.FunctionalTests.xproj", "{FC578F4E-171C-4F82-B301-3ABF6318D082}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.Hosting.TestSites", "test\Microsoft.AspNetCore.Hosting.TestSites\Microsoft.AspNetCore.Hosting.TestSites.xproj", "{542D4600-B232-4B17-A08C-E31EBFA0D74E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -142,6 +145,30 @@ Global {03148731-EA95-40A2-BAE8-A12315EA1748}.Release|Mixed Platforms.Build.0 = Release|Any CPU {03148731-EA95-40A2-BAE8-A12315EA1748}.Release|x86.ActiveCfg = Release|Any CPU {03148731-EA95-40A2-BAE8-A12315EA1748}.Release|x86.Build.0 = Release|Any CPU + {FC578F4E-171C-4F82-B301-3ABF6318D082}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FC578F4E-171C-4F82-B301-3ABF6318D082}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FC578F4E-171C-4F82-B301-3ABF6318D082}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {FC578F4E-171C-4F82-B301-3ABF6318D082}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {FC578F4E-171C-4F82-B301-3ABF6318D082}.Debug|x86.ActiveCfg = Debug|Any CPU + {FC578F4E-171C-4F82-B301-3ABF6318D082}.Debug|x86.Build.0 = Debug|Any CPU + {FC578F4E-171C-4F82-B301-3ABF6318D082}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FC578F4E-171C-4F82-B301-3ABF6318D082}.Release|Any CPU.Build.0 = Release|Any CPU + {FC578F4E-171C-4F82-B301-3ABF6318D082}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {FC578F4E-171C-4F82-B301-3ABF6318D082}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {FC578F4E-171C-4F82-B301-3ABF6318D082}.Release|x86.ActiveCfg = Release|Any CPU + {FC578F4E-171C-4F82-B301-3ABF6318D082}.Release|x86.Build.0 = Release|Any CPU + {542D4600-B232-4B17-A08C-E31EBFA0D74E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {542D4600-B232-4B17-A08C-E31EBFA0D74E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {542D4600-B232-4B17-A08C-E31EBFA0D74E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {542D4600-B232-4B17-A08C-E31EBFA0D74E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {542D4600-B232-4B17-A08C-E31EBFA0D74E}.Debug|x86.ActiveCfg = Debug|Any CPU + {542D4600-B232-4B17-A08C-E31EBFA0D74E}.Debug|x86.Build.0 = Debug|Any CPU + {542D4600-B232-4B17-A08C-E31EBFA0D74E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {542D4600-B232-4B17-A08C-E31EBFA0D74E}.Release|Any CPU.Build.0 = Release|Any CPU + {542D4600-B232-4B17-A08C-E31EBFA0D74E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {542D4600-B232-4B17-A08C-E31EBFA0D74E}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {542D4600-B232-4B17-A08C-E31EBFA0D74E}.Release|x86.ActiveCfg = Release|Any CPU + {542D4600-B232-4B17-A08C-E31EBFA0D74E}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -156,5 +183,7 @@ Global {3DA89347-6731-4366-80C4-548F24E8607B} = {E0497F39-AFFB-4819-A116-E39E361915AB} {485B6745-7648-400A-A969-F68FCF194E46} = {9C7520A0-F2EB-411C-8BB2-80B39C937217} {03148731-EA95-40A2-BAE8-A12315EA1748} = {E0497F39-AFFB-4819-A116-E39E361915AB} + {FC578F4E-171C-4F82-B301-3ABF6318D082} = {FEB39027-9158-4DE2-997F-7ADAEF8188D0} + {542D4600-B232-4B17-A08C-E31EBFA0D74E} = {FEB39027-9158-4DE2-997F-7ADAEF8188D0} EndGlobalSection EndGlobal diff --git a/src/Microsoft.AspNetCore.Server.IntegrationTesting/Deployers/SelfHostDeployer.cs b/src/Microsoft.AspNetCore.Server.IntegrationTesting/Deployers/SelfHostDeployer.cs index 7148c6fb..d7764c02 100644 --- a/src/Microsoft.AspNetCore.Server.IntegrationTesting/Deployers/SelfHostDeployer.cs +++ b/src/Microsoft.AspNetCore.Server.IntegrationTesting/Deployers/SelfHostDeployer.cs @@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting /// public class SelfHostDeployer : ApplicationDeployer { - private Process _hostProcess; + public Process HostProcess { get; private set; } public SelfHostDeployer(DeploymentParameters deploymentParameters, ILogger logger) : base(deploymentParameters, logger) @@ -103,32 +103,32 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting AddEnvironmentVariablesToProcess(startInfo, DeploymentParameters.EnvironmentVariables); - _hostProcess = new Process() { StartInfo = startInfo }; - _hostProcess.ErrorDataReceived += (sender, dataArgs) => { Logger.LogError(dataArgs.Data ?? string.Empty); }; - _hostProcess.OutputDataReceived += (sender, dataArgs) => { Logger.LogInformation(dataArgs.Data ?? string.Empty); }; - _hostProcess.EnableRaisingEvents = true; + HostProcess = new Process() { StartInfo = startInfo }; + HostProcess.ErrorDataReceived += (sender, dataArgs) => { Logger.LogError(dataArgs.Data ?? string.Empty); }; + HostProcess.OutputDataReceived += (sender, dataArgs) => { Logger.LogInformation(dataArgs.Data ?? string.Empty); }; + HostProcess.EnableRaisingEvents = true; var hostExitTokenSource = new CancellationTokenSource(); - _hostProcess.Exited += (sender, e) => + HostProcess.Exited += (sender, e) => { TriggerHostShutdown(hostExitTokenSource); }; - _hostProcess.Start(); - _hostProcess.BeginErrorReadLine(); - _hostProcess.BeginOutputReadLine(); + HostProcess.Start(); + HostProcess.BeginErrorReadLine(); + HostProcess.BeginOutputReadLine(); - if (_hostProcess.HasExited) + if (HostProcess.HasExited) { - Logger.LogError("Host process {processName} exited with code {exitCode} or failed to start.", startInfo.FileName, _hostProcess.ExitCode); + Logger.LogError("Host process {processName} exited with code {exitCode} or failed to start.", startInfo.FileName, HostProcess.ExitCode); throw new Exception("Failed to start host"); } - Logger.LogInformation("Started {fileName}. Process Id : {processId}", startInfo.FileName, _hostProcess.Id); + Logger.LogInformation("Started {fileName}. Process Id : {processId}", startInfo.FileName, HostProcess.Id); return hostExitTokenSource.Token; } public override void Dispose() { - ShutDownIfAnyHostProcess(_hostProcess); + ShutDownIfAnyHostProcess(HostProcess); if (DeploymentParameters.PublishApplicationBeforeDeployment) { diff --git a/src/Microsoft.AspNetCore.Server.IntegrationTesting/project.json b/src/Microsoft.AspNetCore.Server.IntegrationTesting/project.json index 98b6d395..a7e2cb5e 100644 --- a/src/Microsoft.AspNetCore.Server.IntegrationTesting/project.json +++ b/src/Microsoft.AspNetCore.Server.IntegrationTesting/project.json @@ -32,6 +32,7 @@ "type": "build", "version": "1.1.0-*" }, + "Microsoft.Extensions.PlatformAbstractions": "1.1.0-*", "NETStandard.Library": "1.6.1-*" }, "frameworks": { diff --git a/src/Microsoft.AspNetCore.Server.IntegrationTesting/xunit/TestProjectHelpers.cs b/src/Microsoft.AspNetCore.Server.IntegrationTesting/xunit/TestProjectHelpers.cs new file mode 100644 index 00000000..76a3dc6d --- /dev/null +++ b/src/Microsoft.AspNetCore.Server.IntegrationTesting/xunit/TestProjectHelpers.cs @@ -0,0 +1,32 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using Microsoft.Extensions.PlatformAbstractions; + +namespace Microsoft.AspNetCore.Server.IntegrationTesting.xunit +{ + public class TestProjectHelpers + { + public static string GetProjectRoot() + { + var applicationBasePath = PlatformServices.Default.Application.ApplicationBasePath; + + var directoryInfo = new DirectoryInfo(applicationBasePath); + do + { + var projectFileInfo = new FileInfo(Path.Combine(directoryInfo.FullName, "project.json")); + if (projectFileInfo.Exists) + { + return projectFileInfo.DirectoryName; + } + + directoryInfo = directoryInfo.Parent; + } + while (directoryInfo.Parent != null); + + throw new Exception($"Project root could not be found using {applicationBasePath}"); + } + } + } diff --git a/test/Microsoft.AspNetCore.Hosting.FunctionalTests/Microsoft.AspNetCore.Hosting.FunctionalTests.xproj b/test/Microsoft.AspNetCore.Hosting.FunctionalTests/Microsoft.AspNetCore.Hosting.FunctionalTests.xproj new file mode 100644 index 00000000..3e17cac8 --- /dev/null +++ b/test/Microsoft.AspNetCore.Hosting.FunctionalTests/Microsoft.AspNetCore.Hosting.FunctionalTests.xproj @@ -0,0 +1,17 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + fc578f4e-171c-4f82-b301-3abf6318d082 + .\obj + .\bin\ + + + 2.0 + + + \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Hosting.FunctionalTests/ShutdownTests.cs b/test/Microsoft.AspNetCore.Hosting.FunctionalTests/ShutdownTests.cs new file mode 100644 index 00000000..c794cbc9 --- /dev/null +++ b/test/Microsoft.AspNetCore.Hosting.FunctionalTests/ShutdownTests.cs @@ -0,0 +1,94 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using System.IO; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Server.IntegrationTesting; +using Microsoft.AspNetCore.Server.IntegrationTesting.xunit; +using Microsoft.AspNetCore.Testing.xunit; +using Microsoft.Extensions.Logging; +using Xunit; +using Xunit.Sdk; + +namespace Microsoft.AspNetCore.Hosting.FunctionalTests +{ + public class ShutdownTests + { + [ConditionalFact] + [OSSkipCondition(OperatingSystems.Windows)] + [OSSkipCondition(OperatingSystems.MacOSX)] + public void ShutdownTest() + { + var logger = new LoggerFactory() + .AddConsole() + .CreateLogger(nameof(ShutdownTest)); + + string applicationPath = Path.Combine(TestProjectHelpers.GetProjectRoot(), "..", + "Microsoft.AspNetCore.Hosting.TestSites"); + + var deploymentParameters = new DeploymentParameters( + applicationPath, + ServerType.Kestrel, + RuntimeFlavor.CoreClr, + RuntimeArchitecture.x64) + { + EnvironmentName = "Shutdown", + TargetFramework = "netcoreapp1.1", + ApplicationType = ApplicationType.Portable, + PublishApplicationBeforeDeployment = true + }; + + using (var deployer = new SelfHostDeployer(deploymentParameters, logger)) + { + deployer.Deploy(); + + // Wait for application to start + System.Threading.Thread.Sleep(1000); + + string output = string.Empty; + deployer.HostProcess.OutputDataReceived += (sender, args) => output += args.Data + '\n'; + + SendSIGINT(deployer.HostProcess.Id); + + WaitForExitOrKill(deployer.HostProcess); + + output = output.Trim('\n'); + + Assert.Equal(output, "Application is shutting down...\n" + + "Stopping firing\n" + + "Stopping end\n" + + "Stopped firing\n" + + "Stopped end"); + } + } + + + private static void SendSIGINT(int processId) + { + var startInfo = new ProcessStartInfo + { + FileName = "kill", + Arguments = processId.ToString(), + RedirectStandardOutput = true, + UseShellExecute = false + }; + + var process = Process.Start(startInfo); + WaitForExitOrKill(process); + } + + private static void WaitForExitOrKill(Process process) + { + process.WaitForExit(1000); + if (!process.HasExited) + { + process.Kill(); + } + + Assert.Equal(0, process.ExitCode); + } + } +} diff --git a/test/Microsoft.AspNetCore.Hosting.FunctionalTests/project.json b/test/Microsoft.AspNetCore.Hosting.FunctionalTests/project.json new file mode 100644 index 00000000..0abc059b --- /dev/null +++ b/test/Microsoft.AspNetCore.Hosting.FunctionalTests/project.json @@ -0,0 +1,34 @@ +{ + "buildOptions": { + "warningsAsErrors": true, + "keyFile": "../../tools/Key.snk", + "copyToOutput": { + "include": [ + "testroot/**/*" + ] + } + }, + "publishOptions": { + "include": [ + "testroot/**/*" + ] + }, + "dependencies": { + "dotnet-test-xunit": "2.2.0-*", + "Microsoft.AspNetCore.Server.IntegrationTesting": "0.2.0-*", + "Microsoft.AspNetCore.Hosting": "1.1.0-*", + "Microsoft.Extensions.Logging.Console": "1.1.0-*", + "xunit": "2.2.0-*" + }, + "frameworks": { + "netcoreapp1.1": { + "dependencies": { + "Microsoft.NETCore.App": { + "version": "1.1.0-*", + "type": "platform" + } + } + } + }, + "testRunner": "xunit" +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Hosting.TestSites/.Program.cs.swp b/test/Microsoft.AspNetCore.Hosting.TestSites/.Program.cs.swp new file mode 100644 index 0000000000000000000000000000000000000000..ef1c7881b16b651bf2b0d03796b39adca14116ad GIT binary patch literal 12288 zcmeI2O>fjj7{`a+NDEXT^@5Nd5)NL8%!U`0mH^VS4J8r@h)n_rRh8GCYz(^|Yi1l0 zrL<}fh;z^707v)=y;TBwg?@s51P+}0AKQ!BE(u&g&q)6r+w=H&`OVBKt4w!hWqyH9 z$&&)@OCetGK6L%j#ESU#sSsIs+iVW}z|O6-o6fs=>hfb>Z)^N>Y1q7|txM8g(<$qk z3rS?O)vHdnY__OeTN@=S3%LY~P6E5;6{CA59LUIS>z)Ug02A1bK<-wjzu6;B96vVW zO{OLf(}DfV+gbB-Ccp%k025#WOn?b60Vco%K0yL5+a-QMU7uC89j*FZ|Ldz;?w9}* zU;<2l2`~XBzyz286JP>NfC(^xkB~qZ3-Mq=h~#r5kN^JJvQQYSmg1&vwZM||57MTd_p@Ai|3)^!q7$A!k7S!(OlsSP3C zAK?8aJ6L=`4(?nqf6=+Dm1RgWbcs%f>A7=yIdly5Cokf0L>w zmbR#;+$`Otvy`ek!{@c7C-uf(4fx#&f0x)yTNN~lH%gl-ho~P~ON&}(onk8JG|iV+ zrTf}-edViI!%1OzHBU$K@B{0iDF%^vH&6PpGV;9d#|*u(T(+v1(L4(#KkU5mR=-?U%gN+fa(yp*b-?tP&NxAMddO;+F584tJC_-H-21N_@;Ny7&e0(uH)GqEz8s9 z*!ccSU})Oev`!7=(wb3Wj3~qBb+EY&r&5dUV&w>>am(?8>)18TCS6J*cu+CrDHbTX zHAOAdjIJ6vN-N6v5OiG5NT7H#BHf0gBrV>_jJ}19OGax)tJ)C00lZ&~<8DwaMd{ + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 542d4600-b232-4b17-a08c-e31ebfa0d74e + .\obj + .\bin\ + + + 2.0 + + + \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Hosting.TestSites/Program.cs b/test/Microsoft.AspNetCore.Hosting.TestSites/Program.cs new file mode 100644 index 00000000..33332152 --- /dev/null +++ b/test/Microsoft.AspNetCore.Hosting.TestSites/Program.cs @@ -0,0 +1,44 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using System.Threading; +using Microsoft.AspNetCore.Hosting.Server; +using Microsoft.AspNetCore.Http.Features; + +namespace ServerComparison.TestSites +{ + public static class Program + { + public static void Main(string[] args) + { + var config = new ConfigurationBuilder() + .AddCommandLine(args) + .Build(); + + var builder = new WebHostBuilder() + .UseServer(new NoopServer()) + .UseConfiguration(config) + .UseStartup("Microsoft.AspNetCore.Hosting.TestSites"); + + var host = builder.Build(); + + host.Run(); + } + } + + public class NoopServer : IServer + { + public void Dispose() + { + } + + public IFeatureCollection Features { get; } = new FeatureCollection(); + + public void Start(IHttpApplication application) + { + } + } +} + diff --git a/test/Microsoft.AspNetCore.Hosting.TestSites/StartupShutdown.cs b/test/Microsoft.AspNetCore.Hosting.TestSites/StartupShutdown.cs new file mode 100644 index 00000000..b8a5b8b7 --- /dev/null +++ b/test/Microsoft.AspNetCore.Hosting.TestSites/StartupShutdown.cs @@ -0,0 +1,37 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Hosting.TestSites +{ + public class StartupShutdown + { + public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory, IApplicationLifetime lifetime) + { + lifetime.ApplicationStopping.Register(() => + { + Console.WriteLine("Stopping firing"); + System.Threading.Thread.Sleep(200); + Console.WriteLine("Stopping end"); + }); + lifetime.ApplicationStopped.Register(() => + { + Console.WriteLine("Stopped firing"); + System.Threading.Thread.Sleep(200); + Console.WriteLine("Stopped end"); + }); + + + loggerFactory.AddConsole(minLevel: LogLevel.Warning); + + app.Run(context => + { + return context.Response.WriteAsync("Hello World"); + }); + } + } +} diff --git a/test/Microsoft.AspNetCore.Hosting.TestSites/project.json b/test/Microsoft.AspNetCore.Hosting.TestSites/project.json new file mode 100644 index 00000000..5dc04cc3 --- /dev/null +++ b/test/Microsoft.AspNetCore.Hosting.TestSites/project.json @@ -0,0 +1,23 @@ +{ + "version": "1.1.0-*", + "dependencies": { + "Microsoft.AspNetCore.Hosting": "1.1.0-*", + "Microsoft.Extensions.Configuration": "1.1.0-*", + "Microsoft.Extensions.Configuration.CommandLine": "1.1.0-*", + "Microsoft.Extensions.Logging.Console": "1.1.0-*" + }, + "buildOptions": { + "emitEntryPoint": true + }, + "frameworks": { + "net451": {}, + "netcoreapp1.1": { + "dependencies": { + "Microsoft.NETCore.App": { + "version": "1.1.0-*", + "type": "platform" + } + } + } + } +}