diff --git a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployer.cs b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployer.cs index 5a8519926c..76ce9a3800 100644 --- a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployer.cs +++ b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployer.cs @@ -357,10 +357,27 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS throw new InvalidOperationException("Site not stopped yet"); } - if (appPool.WorkerProcesses != null && appPool.WorkerProcesses.Any(wp => wp.State == WorkerProcessState.Running || - wp.State == WorkerProcessState.Stopping)) + try { - throw new InvalidOperationException("WorkerProcess not stopped yet"); + if (appPool.WorkerProcesses != null && + appPool.WorkerProcesses.Any(wp => + wp.State == WorkerProcessState.Running || + wp.State == WorkerProcessState.Stopping)) + { + throw new InvalidOperationException("WorkerProcess not stopped yet"); + } + + } + // If WAS was stopped for some reason appPool.WorkerProcesses + // would throw UnauthorizedAccessException. + // check if it's the case and continue shutting down deployer + catch (UnauthorizedAccessException) + { + var serviceController = new ServiceController("was"); + if (serviceController.Status != ServiceControllerStatus.Stopped) + { + throw; + } } if (!HostProcess.HasExited) diff --git a/test/Common.FunctionalTests/ClientCertificateFixture.cs b/test/Common.FunctionalTests/ClientCertificateFixture.cs index f1498d0b05..4979865e8b 100644 --- a/test/Common.FunctionalTests/ClientCertificateFixture.cs +++ b/test/Common.FunctionalTests/ClientCertificateFixture.cs @@ -8,7 +8,7 @@ using Xunit; namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests { - public class ClientCertificateFixture : IDisposable + public class ClientCertificateFixture : IDisposable { private X509Certificate2 _certificate; diff --git a/test/Common.FunctionalTests/Utilities/EventLogHelpers.cs b/test/Common.FunctionalTests/Utilities/EventLogHelpers.cs index aa3453fe9b..2d7de2a8f1 100644 --- a/test/Common.FunctionalTests/Utilities/EventLogHelpers.cs +++ b/test/Common.FunctionalTests/Utilities/EventLogHelpers.cs @@ -20,7 +20,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests var entries = GetEntries(deploymentResult); AssertSingleEntry(expectedRegexMatchString, entries); } - + public static void VerifyEventLogEvents(IISDeploymentResult deploymentResult, params string[] expectedRegexMatchString) { Assert.True(deploymentResult.HostProcess.HasExited); @@ -35,7 +35,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests entries.Remove(matchedEntry); } } - + Assert.True(0 == entries.Count, $"Some entries were not matched by any regex {FormatEntries(entries)}"); } @@ -77,7 +77,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests { continue; } - + // ReplacementStings == EventData collection in EventLog // This is unaffected if event providers are not registered correctly if (eventLogEntry.Source == AncmVersionToMatch(deploymentResult) && @@ -96,12 +96,28 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests (deploymentResult.DeploymentParameters.AncmVersion == AncmVersion.AspNetCoreModuleV2 ? " V2" : ""); } - + public static string Started(IISDeploymentResult deploymentResult) + { + if (deploymentResult.DeploymentParameters.HostingModel == HostingModel.InProcess) + { + return InProcessStarted(deploymentResult); + } + else + { + return OutOfProcessStarted(deploymentResult); + } + } + public static string InProcessStarted(IISDeploymentResult deploymentResult) { return $"Application '{EscapedContentRoot(deploymentResult)}' started the coreclr in-process successfully"; } + public static string OutOfProcessStarted(IISDeploymentResult deploymentResult) + { + return $"Application '/LM/W3SVC/1/ROOT' started process '\\d+' successfully and process '\\d+' is listening on port '\\d+'."; + } + public static string InProcessFailedToStart(IISDeploymentResult deploymentResult, string reason) { return $"Application '/LM/W3SVC/1/ROOT' with physical root '{EscapedContentRoot(deploymentResult)}' failed to load clr and managed application. {reason}"; diff --git a/test/Common.FunctionalTests/Utilities/Helpers.cs b/test/Common.FunctionalTests/Utilities/Helpers.cs index 176805cbad..cb6779210a 100644 --- a/test/Common.FunctionalTests/Utilities/Helpers.cs +++ b/test/Common.FunctionalTests/Utilities/Helpers.cs @@ -111,10 +111,10 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests return response; } - public static void AssertWorkerProcessStop(this IISDeploymentResult deploymentResult) + public static void AssertWorkerProcessStop(this IISDeploymentResult deploymentResult, int? timeout = null) { var hostProcess = deploymentResult.HostProcess; - Assert.True(hostProcess.WaitForExit((int)TimeoutExtensions.DefaultTimeoutValue.TotalMilliseconds)); + Assert.True(hostProcess.WaitForExit(timeout ?? (int)TimeoutExtensions.DefaultTimeoutValue.TotalMilliseconds)); if (deploymentResult.DeploymentParameters.ServerType == ServerType.IISExpress) { diff --git a/test/Common.FunctionalTests/Utilities/IISCapability.cs b/test/Common.FunctionalTests/Utilities/IISCapability.cs index e96127ea9f..9550eac3e5 100644 --- a/test/Common.FunctionalTests/Utilities/IISCapability.cs +++ b/test/Common.FunctionalTests/Utilities/IISCapability.cs @@ -13,6 +13,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests WindowsAuthentication = 2, PoolEnvironmentVariables = 4, ShutdownToken = 8, - DynamicCompression = 16 + DynamicCompression = 16, + ApplicationInitialization = 32 } } diff --git a/test/IIS.FunctionalTests/RequiresIISAttribute.cs b/test/IIS.FunctionalTests/RequiresIISAttribute.cs index dc374293b8..507de60279 100644 --- a/test/IIS.FunctionalTests/RequiresIISAttribute.cs +++ b/test/IIS.FunctionalTests/RequiresIISAttribute.cs @@ -22,6 +22,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests private static readonly bool _windowsAuthAvailable; private static readonly bool _poolEnvironmentVariablesAvailable; private static readonly bool _dynamicCompressionAvailable; + private static readonly bool _applicationInitializationModule; static RequiresIISAttribute() { @@ -84,6 +85,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests _dynamicCompressionAvailable = File.Exists(Path.Combine(Environment.SystemDirectory, "inetsrv", "compdyn.dll")); + _applicationInitializationModule = File.Exists(Path.Combine(Environment.SystemDirectory, "inetsrv", "warmup.dll")); + var iisRegistryKey = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\InetStp", writable: false); if (iisRegistryKey == null) { @@ -145,6 +148,15 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests SkipReason += "The machine does not have IIS dynamic compression installed."; } } + + if (capabilities.HasFlag(IISCapability.ApplicationInitialization)) + { + IsMet &= _dynamicCompressionAvailable; + if (!_dynamicCompressionAvailable) + { + SkipReason += "The machine does not have IIS ApplicationInitialization installed."; + } + } } public bool IsMet { get; } diff --git a/test/IIS.FunctionalTests/ServicesTests.cs b/test/IIS.FunctionalTests/ServicesTests.cs new file mode 100644 index 0000000000..0fe723eae9 --- /dev/null +++ b/test/IIS.FunctionalTests/ServicesTests.cs @@ -0,0 +1,73 @@ +// 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.ServiceProcess; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities; +using Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests; +using Microsoft.AspNetCore.Server.IntegrationTesting; +using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; +using Microsoft.AspNetCore.Testing; +using Microsoft.AspNetCore.Testing.xunit; +using Xunit; + +namespace IIS.FunctionalTests +{ + [Collection(PublishedSitesCollection.Name)] + public class ApplicationInitializationTests : IISFunctionalTestBase + { + private readonly PublishedSitesFixture _fixture; + + public ApplicationInitializationTests(PublishedSitesFixture fixture) + { + _fixture = fixture; + } + + [ConditionalTheory] + [RequiresIIS(IISCapability.ApplicationInitialization)] + [InlineData(HostingModel.InProcess)] + [InlineData(HostingModel.OutOfProcess)] + public async Task ApplicationInitializationInitializedInProc(HostingModel hostingModel) + { + var baseDeploymentParameters = _fixture.GetBaseDeploymentParameters(hostingModel); + EnableAppInitialization(baseDeploymentParameters); + + var result = await DeployAsync(baseDeploymentParameters); + + // Allow IIS a bit of time to complete starting before we start checking + await Task.Delay(100); + // There is always a race between which Init request arrives first + // retry couple times to see if we ever get the one comming from ApplicationInitialization module + await result.HttpClient.RetryRequestAsync("/ApplicationInitialization", async message => await message.Content.ReadAsStringAsync() == "True"); + + StopServer(); + EventLogHelpers.VerifyEventLogEvent(result, EventLogHelpers.Started(result)); + } + + private static void EnableAppInitialization(IISDeploymentParameters baseDeploymentParameters) + { + baseDeploymentParameters.ServerConfigActionList.Add( + (config, _) => { + config + .RequiredElement("configSections") + .GetOrAdd("sectionGroup", "name", "system.webServer") + .GetOrAdd("section", "name", "applicationInitialization") + .SetAttributeValue("overrideModeDefault", "Allow"); + + config + .RequiredElement("system.applicationHost") + .RequiredElement("sites") + .RequiredElement("site") + .RequiredElement("application") + .SetAttributeValue("preloadEnabled", true); + + config + .RequiredElement("system.webServer") + .GetOrAdd("applicationInitialization") + .GetOrAdd("add", "initializationPage", "/ApplicationInitialization?IISInit=true"); + }); + + baseDeploymentParameters.EnableModule("ApplicationInitializationModule", "%IIS_BIN%\\warmup.dll"); + } + } +} diff --git a/test/WebSites/shared/SharedStartup/Startup.shared.cs b/test/WebSites/shared/SharedStartup/Startup.shared.cs index 1db25d5905..bf9e3bb7fd 100644 --- a/test/WebSites/shared/SharedStartup/Startup.shared.cs +++ b/test/WebSites/shared/SharedStartup/Startup.shared.cs @@ -71,5 +71,16 @@ namespace TestSite { await context.Response.WriteAsync(_waitingRequestCount.ToString()); } + + private static bool _applicationInitializationCalled; + + public async Task ApplicationInitialization(HttpContext context) + { + if (context.Request.Query["IISInit"] == "true") + { + _applicationInitializationCalled = true; + } + await context.Response.WriteAsync(_applicationInitializationCalled.ToString()); + } } }