From 6bed0b7206c1553d14a58dd54d2c3e38b802904e Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Thu, 12 Jul 2018 11:56:33 -0700 Subject: [PATCH] Add Microsoft.AspNetCore.Server.IntegrationTesting.IIS package (#1037) --- IISIntegration.NoV1.sln | 15 + IISIntegration.sln | 15 + .../ApplicationDeployerFactory.cs | 44 + .../DotNetCommands.cs | 73 ++ .../Http.config | 1034 +++++++++++++++++ .../IISApplication.cs | 3 +- .../IISDeployer.cs | 11 +- .../IISExpressDeployer.cs | 436 +++++++ ...tCore.Server.IntegrationTesting.IIS.csproj | 21 + .../TestUriHelper.cs | 44 +- .../WebConfigHelpers.cs | 50 + .../Inprocess/LoggingTests.cs | 3 +- .../Utilities/FunctionalTestsBase.cs | 10 +- .../Utilities/Helpers.cs | 35 - .../Utilities/IISTestSiteFixture.cs | 13 +- .../IIS.FunctionalTests.csproj | 3 +- .../IISExpress.FunctionalTests.csproj | 3 +- .../InProcess/StartupTests.cs | 3 +- 18 files changed, 1713 insertions(+), 103 deletions(-) create mode 100644 src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/ApplicationDeployerFactory.cs create mode 100644 src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/DotNetCommands.cs create mode 100644 src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/Http.config rename {test/Common.FunctionalTests/Utilities => src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS}/IISApplication.cs (99%) rename {test/Common.FunctionalTests/Utilities => src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS}/IISDeployer.cs (87%) create mode 100644 src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISExpressDeployer.cs create mode 100644 src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/Microsoft.AspNetCore.Server.IntegrationTesting.IIS.csproj rename test/Common.FunctionalTests/Utilities/TestIISUriHelper.cs => src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/TestUriHelper.cs (52%) create mode 100644 src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/WebConfigHelpers.cs diff --git a/IISIntegration.NoV1.sln b/IISIntegration.NoV1.sln index 04bda53889..b9b82faf5a 100644 --- a/IISIntegration.NoV1.sln +++ b/IISIntegration.NoV1.sln @@ -111,6 +111,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RequestHandlerLib", "src\As EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IIS.FunctionalTests", "test\IIS.FunctionalTests\IIS.FunctionalTests.csproj", "{D182103F-8405-4647-B158-C36F598657EF}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.IntegrationTesting.IIS", "src\Microsoft.AspNetCore.Server.IntegrationTesting.IIS\Microsoft.AspNetCore.Server.IntegrationTesting.IIS.csproj", "{34135ED7-313D-4E68-860C-D6B51AA28523}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -367,6 +369,18 @@ Global {D182103F-8405-4647-B158-C36F598657EF}.Release|x64.Build.0 = Release|Any CPU {D182103F-8405-4647-B158-C36F598657EF}.Release|x86.ActiveCfg = Release|Any CPU {D182103F-8405-4647-B158-C36F598657EF}.Release|x86.Build.0 = Release|Any CPU + {34135ED7-313D-4E68-860C-D6B51AA28523}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {34135ED7-313D-4E68-860C-D6B51AA28523}.Debug|Any CPU.Build.0 = Debug|Any CPU + {34135ED7-313D-4E68-860C-D6B51AA28523}.Debug|x64.ActiveCfg = Debug|Any CPU + {34135ED7-313D-4E68-860C-D6B51AA28523}.Debug|x64.Build.0 = Debug|Any CPU + {34135ED7-313D-4E68-860C-D6B51AA28523}.Debug|x86.ActiveCfg = Debug|Any CPU + {34135ED7-313D-4E68-860C-D6B51AA28523}.Debug|x86.Build.0 = Debug|Any CPU + {34135ED7-313D-4E68-860C-D6B51AA28523}.Release|Any CPU.ActiveCfg = Release|Any CPU + {34135ED7-313D-4E68-860C-D6B51AA28523}.Release|Any CPU.Build.0 = Release|Any CPU + {34135ED7-313D-4E68-860C-D6B51AA28523}.Release|x64.ActiveCfg = Release|Any CPU + {34135ED7-313D-4E68-860C-D6B51AA28523}.Release|x64.Build.0 = Release|Any CPU + {34135ED7-313D-4E68-860C-D6B51AA28523}.Release|x86.ActiveCfg = Release|Any CPU + {34135ED7-313D-4E68-860C-D6B51AA28523}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -396,6 +410,7 @@ Global {CAC1267B-8778-4257-AAC6-CAF481723B01} = {EF30B533-D715-421A-92B7-92FEF460AC9C} {1533E271-F61B-441B-8B74-59FB61DF0552} = {06CA2C2B-83B0-4D83-905A-E0C74790009E} {D182103F-8405-4647-B158-C36F598657EF} = {EF30B533-D715-421A-92B7-92FEF460AC9C} + {34135ED7-313D-4E68-860C-D6B51AA28523} = {04B1EDB6-E967-4D25-89B9-E6F8304038CD} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DB4F868D-E1AE-4FD7-9333-69FA15B268C5} diff --git a/IISIntegration.sln b/IISIntegration.sln index d3e7ca4de0..c21a81482f 100644 --- a/IISIntegration.sln +++ b/IISIntegration.sln @@ -118,6 +118,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RequestHandlerLib", "src\As EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IIS.FunctionalTests", "test\IIS.FunctionalTests\IIS.FunctionalTests.csproj", "{1F0C8D9B-F47B-41F3-9FC9-6954B6DC7712}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.IntegrationTesting.IIS", "src\Microsoft.AspNetCore.Server.IntegrationTesting.IIS\Microsoft.AspNetCore.Server.IntegrationTesting.IIS.csproj", "{CE4FB142-91FB-4B34-BC96-A31120EF4009}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -394,6 +396,18 @@ Global {1F0C8D9B-F47B-41F3-9FC9-6954B6DC7712}.Release|x64.Build.0 = Release|Any CPU {1F0C8D9B-F47B-41F3-9FC9-6954B6DC7712}.Release|x86.ActiveCfg = Release|Any CPU {1F0C8D9B-F47B-41F3-9FC9-6954B6DC7712}.Release|x86.Build.0 = Release|Any CPU + {CE4FB142-91FB-4B34-BC96-A31120EF4009}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CE4FB142-91FB-4B34-BC96-A31120EF4009}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CE4FB142-91FB-4B34-BC96-A31120EF4009}.Debug|x64.ActiveCfg = Debug|Any CPU + {CE4FB142-91FB-4B34-BC96-A31120EF4009}.Debug|x64.Build.0 = Debug|Any CPU + {CE4FB142-91FB-4B34-BC96-A31120EF4009}.Debug|x86.ActiveCfg = Debug|Any CPU + {CE4FB142-91FB-4B34-BC96-A31120EF4009}.Debug|x86.Build.0 = Debug|Any CPU + {CE4FB142-91FB-4B34-BC96-A31120EF4009}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CE4FB142-91FB-4B34-BC96-A31120EF4009}.Release|Any CPU.Build.0 = Release|Any CPU + {CE4FB142-91FB-4B34-BC96-A31120EF4009}.Release|x64.ActiveCfg = Release|Any CPU + {CE4FB142-91FB-4B34-BC96-A31120EF4009}.Release|x64.Build.0 = Release|Any CPU + {CE4FB142-91FB-4B34-BC96-A31120EF4009}.Release|x86.ActiveCfg = Release|Any CPU + {CE4FB142-91FB-4B34-BC96-A31120EF4009}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -426,6 +440,7 @@ Global {CAC1267B-8778-4257-AAC6-CAF481723B01} = {EF30B533-D715-421A-92B7-92FEF460AC9C} {1533E271-F61B-441B-8B74-59FB61DF0552} = {06CA2C2B-83B0-4D83-905A-E0C74790009E} {1F0C8D9B-F47B-41F3-9FC9-6954B6DC7712} = {EF30B533-D715-421A-92B7-92FEF460AC9C} + {CE4FB142-91FB-4B34-BC96-A31120EF4009} = {04B1EDB6-E967-4D25-89B9-E6F8304038CD} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DB4F868D-E1AE-4FD7-9333-69FA15B268C5} diff --git a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/ApplicationDeployerFactory.cs b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/ApplicationDeployerFactory.cs new file mode 100644 index 0000000000..aaec90971e --- /dev/null +++ b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/ApplicationDeployerFactory.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 System; +using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Server.IntegrationTesting +{ + /// + /// Factory to create an appropriate deployer based on . + /// + public class IISApplicationDeployerFactory + { + /// + /// Creates a deployer instance based on settings in . + /// + /// + /// + /// + public static ApplicationDeployer Create(DeploymentParameters deploymentParameters, ILoggerFactory loggerFactory) + { + if (deploymentParameters == null) + { + throw new ArgumentNullException(nameof(deploymentParameters)); + } + + if (loggerFactory == null) + { + throw new ArgumentNullException(nameof(loggerFactory)); + } + + switch (deploymentParameters.ServerType) + { + case ServerType.IISExpress: + return new IIS.IISExpressDeployer(deploymentParameters, loggerFactory); + case ServerType.IIS: + return new IISDeployer(deploymentParameters, loggerFactory); + default: + return ApplicationDeployerFactory.Create(deploymentParameters, loggerFactory); + } + } + } +} diff --git a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/DotNetCommands.cs b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/DotNetCommands.cs new file mode 100644 index 0000000000..aaa7ade405 --- /dev/null +++ b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/DotNetCommands.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; +using System.IO; +using System.Runtime.InteropServices; + +namespace Microsoft.AspNetCore.Server.IntegrationTesting +{ + // Copied from hosting + // TODO: make public while removing IISExpressDeployer + public static class DotNetCommands + { + private const string _dotnetFolderName = ".dotnet"; + + internal static string DotNetHome { get; } = GetDotNetHome(); + + // Compare to https://github.com/aspnet/BuildTools/blob/314c98e4533217a841ff9767bb38e144eb6c93e4/tools/KoreBuild.Console/Commands/CommandContext.cs#L76 + private static string GetDotNetHome() + { + var dotnetHome = Environment.GetEnvironmentVariable("DOTNET_HOME"); + var userProfile = Environment.GetEnvironmentVariable("USERPROFILE"); + var home = Environment.GetEnvironmentVariable("HOME"); + + var result = Path.Combine(Directory.GetCurrentDirectory(), _dotnetFolderName); + if (!string.IsNullOrEmpty(dotnetHome)) + { + result = dotnetHome; + } + else if (!string.IsNullOrEmpty(userProfile)) + { + result = Path.Combine(userProfile, _dotnetFolderName); + } + else if (!string.IsNullOrEmpty(home)) + { + result = home; + } + + return result; + } + + internal static string GetDotNetInstallDir(RuntimeArchitecture arch) + { + var dotnetDir = DotNetHome; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + dotnetDir = Path.Combine(dotnetDir, arch.ToString()); + } + + return dotnetDir; + } + + public static string GetDotNetExecutable(RuntimeArchitecture arch) + { + var dotnetDir = GetDotNetInstallDir(arch); + + var dotnetFile = "dotnet"; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + dotnetFile += ".exe"; + } + + return Path.Combine(dotnetDir, dotnetFile); + } + + internal static bool IsRunningX86OnX64(RuntimeArchitecture arch) + { + return (RuntimeInformation.OSArchitecture == Architecture.X64 || RuntimeInformation.OSArchitecture == Architecture.Arm64) + && arch == RuntimeArchitecture.x86; + } + } +} diff --git a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/Http.config b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/Http.config new file mode 100644 index 0000000000..4508dea843 --- /dev/null +++ b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/Http.config @@ -0,0 +1,1034 @@ + + + + + + + + +
+
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+
+ + +
+
+
+
+
+
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Common.FunctionalTests/Utilities/IISApplication.cs b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISApplication.cs similarity index 99% rename from test/Common.FunctionalTests/Utilities/IISApplication.cs rename to src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISApplication.cs index fbde500177..3fafd7c8f9 100644 --- a/test/Common.FunctionalTests/Utilities/IISApplication.cs +++ b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISApplication.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Runtime.InteropServices; using System.Threading.Tasks; using System.Xml.Linq; -using Microsoft.Extensions.CommandLineUtils; using Microsoft.Extensions.Logging; using Microsoft.Web.Administration; @@ -72,7 +71,7 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting if (_deploymentParameters.ApplicationType == ApplicationType.Portable) { - ModifyAspNetCoreSectionInWebConfig("processPath", DotNetMuxer.MuxerPathOrDefault()); + ModifyAspNetCoreSectionInWebConfig("processPath", DotNetCommands.GetDotNetExecutable(_deploymentParameters.RuntimeArchitecture)); } _serverManager.CommitChanges(); diff --git a/test/Common.FunctionalTests/Utilities/IISDeployer.cs b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployer.cs similarity index 87% rename from test/Common.FunctionalTests/Utilities/IISDeployer.cs rename to src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployer.cs index 8104a47a1c..6df58dd736 100644 --- a/test/Common.FunctionalTests/Utilities/IISDeployer.cs +++ b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployer.cs @@ -1,16 +1,15 @@ // 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 System.Linq; using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; -using Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests; +using Microsoft.AspNetCore.Server.IntegrationTesting.Common; using Microsoft.Extensions.Logging; -namespace Microsoft.AspNetCore.Server.IntegrationTesting +namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS { /// /// Deployer for IIS. @@ -57,7 +56,7 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting _application = new IISApplication(DeploymentParameters, Logger); - // For now, only support using published output + // For now, only support using published output DeploymentParameters.PublishApplicationBeforeDeployment = true; if (DeploymentParameters.PublishApplicationBeforeDeployment) { @@ -65,9 +64,9 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting contentRoot = DeploymentParameters.PublishedApplicationRootPath; } - Helpers.AddDebugLogToWebConfig(contentRoot, Path.Combine(contentRoot, $"{_application.WebSiteName}.txt")); + WebConfigHelpers.AddDebugLogToWebConfig(contentRoot, Path.Combine(contentRoot, $"{_application.WebSiteName}.txt")); - var uri = TestIISUriHelper.BuildTestUri(ServerType.IIS, DeploymentParameters.ApplicationBaseUriHint); + var uri = TestUriHelper.BuildTestUri(ServerType.IIS, DeploymentParameters.ApplicationBaseUriHint); // To prevent modifying the IIS setup concurrently. await _application.StartIIS(uri, contentRoot); diff --git a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISExpressDeployer.cs b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISExpressDeployer.cs new file mode 100644 index 0000000000..0cd67cc132 --- /dev/null +++ b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISExpressDeployer.cs @@ -0,0 +1,436 @@ +// 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.Globalization; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using System.Xml.Linq; +using Microsoft.AspNetCore.Server.IntegrationTesting.Common; +using Microsoft.AspNetCore.Testing; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS +{ + /// + /// Deployment helper for IISExpress. + /// + public class IISExpressDeployer : ApplicationDeployer + { + private const string IISExpressRunningMessage = "IIS Express is running."; + private const string FailedToInitializeBindingsMessage = "Failed to initialize site bindings"; + private const string UnableToStartIISExpressMessage = "Unable to start iisexpress."; + private const int MaximumAttempts = 5; + + private static readonly Regex UrlDetectorRegex = new Regex(@"^\s*Successfully registered URL ""(?[^""]+)"" for site.*$"); + + private Process _hostProcess; + + public IISExpressDeployer(DeploymentParameters deploymentParameters, ILoggerFactory loggerFactory) + : base(deploymentParameters, loggerFactory) + { + } + + public override async Task DeployAsync() + { + using (Logger.BeginScope("Deployment")) + { + // Start timer + StartTimer(); + + // For an unpublished application the dllroot points pre-built dlls like projectdir/bin/debug/net461/ + // and contentRoot points to the project directory so you get things like static assets. + // For a published app both point to the publish directory. + var dllRoot = CheckIfPublishIsRequired(); + var contentRoot = string.Empty; + if (DeploymentParameters.PublishApplicationBeforeDeployment) + { + DotnetPublish(); + contentRoot = DeploymentParameters.PublishedApplicationRootPath; + dllRoot = contentRoot; + } + else + { + // Core+Standalone always publishes. This must be Clr+Standalone or Core+Portable. + // Update processPath and arguments for our current scenario + contentRoot = DeploymentParameters.ApplicationPath; + + var executableExtension = DeploymentParameters.ApplicationType == ApplicationType.Portable ? ".dll" : ".exe"; + var entryPoint = Path.Combine(dllRoot, DeploymentParameters.ApplicationName + executableExtension); + + var executableName = string.Empty; + var executableArgs = string.Empty; + + if (DeploymentParameters.RuntimeFlavor == RuntimeFlavor.CoreClr && DeploymentParameters.ApplicationType == ApplicationType.Portable) + { + executableName = GetDotNetExeForArchitecture(); + executableArgs = entryPoint; + } + else + { + executableName = entryPoint; + } + + Logger.LogInformation("Executing: {exe} {args}", executableName, executableArgs); + DeploymentParameters.EnvironmentVariables["LAUNCHER_PATH"] = executableName; + DeploymentParameters.EnvironmentVariables["LAUNCHER_ARGS"] = executableArgs; + + // CurrentDirectory will point to bin/{config}/{tfm}, but the config and static files aren't copied, point to the app base instead. + Logger.LogInformation("ContentRoot: {path}", DeploymentParameters.ApplicationPath); + DeploymentParameters.EnvironmentVariables["ASPNETCORE_CONTENTROOT"] = DeploymentParameters.ApplicationPath; + } + + var testUri = TestUriHelper.BuildTestUri(ServerType.IISExpress, DeploymentParameters.ApplicationBaseUriHint); + + // Launch the host process. + var (actualUri, hostExitToken) = await StartIISExpressAsync(testUri, contentRoot, dllRoot); + + Logger.LogInformation("Application ready at URL: {appUrl}", actualUri); + + // Right now this works only for urls like http://localhost:5001/. Does not work for http://localhost:5001/subpath. + return new DeploymentResult( + LoggerFactory, + DeploymentParameters, + applicationBaseUri: actualUri.ToString(), + contentRoot: contentRoot, + hostShutdownToken: hostExitToken); + } + } + + private string CheckIfPublishIsRequired() + { + var targetFramework = DeploymentParameters.TargetFramework; + + // IISIntegration uses this layout + var dllRoot = Path.Combine(DeploymentParameters.ApplicationPath, "bin", DeploymentParameters.RuntimeArchitecture.ToString(), + DeploymentParameters.Configuration, targetFramework); + + if (!Directory.Exists(dllRoot)) + { + // Most repos use this layout + dllRoot = Path.Combine(DeploymentParameters.ApplicationPath, "bin", DeploymentParameters.Configuration, targetFramework); + + if (!Directory.Exists(dllRoot)) + { + // The bits we need weren't pre-compiled, compile on publish + DeploymentParameters.PublishApplicationBeforeDeployment = true; + } + else if (DeploymentParameters.RuntimeFlavor == RuntimeFlavor.Clr + && DeploymentParameters.RuntimeArchitecture == RuntimeArchitecture.x86) + { + // x64 is the default. Publish to rebuild for the right bitness + DeploymentParameters.PublishApplicationBeforeDeployment = true; + } + } + + if (DeploymentParameters.RuntimeFlavor == RuntimeFlavor.CoreClr + && DeploymentParameters.ApplicationType == ApplicationType.Standalone) + { + // Publish is always required to get the correct standalone files in the output directory + DeploymentParameters.PublishApplicationBeforeDeployment = true; + } + + return dllRoot; + } + + private async Task<(Uri url, CancellationToken hostExitToken)> StartIISExpressAsync(Uri uri, string contentRoot, string dllRoot) + { + using (Logger.BeginScope("StartIISExpress")) + { + var port = uri.Port; + if (port == 0) + { + port = (uri.Scheme == "https") ? TestPortHelper.GetNextSSLPort() : TestPortHelper.GetNextPort(); + } + + Logger.LogInformation("Attempting to start IIS Express on port: {port}", port); + PrepareConfig(contentRoot, dllRoot, port); + + var parameters = string.IsNullOrEmpty(DeploymentParameters.ServerConfigLocation) ? + string.Format("/port:{0} /path:\"{1}\" /trace:error /systray:false", uri.Port, contentRoot) : + string.Format("/site:{0} /config:{1} /trace:error /systray:false", DeploymentParameters.SiteName, DeploymentParameters.ServerConfigLocation); + + var iisExpressPath = GetIISExpressPath(); + + for (var attempt = 0; attempt < MaximumAttempts; attempt++) + { + Logger.LogInformation("Executing command : {iisExpress} {parameters}", iisExpressPath, parameters); + + var startInfo = new ProcessStartInfo + { + FileName = iisExpressPath, + Arguments = parameters, + UseShellExecute = false, + CreateNoWindow = true, + RedirectStandardError = true, + RedirectStandardOutput = true + }; + + AddEnvironmentVariablesToProcess(startInfo, DeploymentParameters.EnvironmentVariables); + + Uri url = null; + var started = new TaskCompletionSource(); + + var process = new Process() { StartInfo = startInfo }; + process.OutputDataReceived += (sender, dataArgs) => + { + if (string.Equals(dataArgs.Data, UnableToStartIISExpressMessage)) + { + // We completely failed to start and we don't really know why + started.TrySetException(new InvalidOperationException("Failed to start IIS Express")); + } + else if (string.Equals(dataArgs.Data, FailedToInitializeBindingsMessage)) + { + started.TrySetResult(false); + } + else if (string.Equals(dataArgs.Data, IISExpressRunningMessage)) + { + started.TrySetResult(true); + } + else if (!string.IsNullOrEmpty(dataArgs.Data)) + { + var m = UrlDetectorRegex.Match(dataArgs.Data); + if (m.Success) + { + url = new Uri(m.Groups["url"].Value); + } + } + }; + + process.EnableRaisingEvents = true; + var hostExitTokenSource = new CancellationTokenSource(); + process.Exited += (sender, e) => + { + Logger.LogInformation("iisexpress Process {pid} shut down", process.Id); + + // If TrySetResult was called above, this will just silently fail to set the new state, which is what we want + started.TrySetException(new Exception($"Command exited unexpectedly with exit code: {process.ExitCode}")); + + TriggerHostShutdown(hostExitTokenSource); + }; + process.StartAndCaptureOutAndErrToLogger("iisexpress", Logger); + Logger.LogInformation("iisexpress Process {pid} started", process.Id); + + if (process.HasExited) + { + Logger.LogError("Host process {processName} {pid} exited with code {exitCode} or failed to start.", startInfo.FileName, process.Id, process.ExitCode); + throw new Exception("Failed to start host"); + } + + // Wait for the app to start + // The timeout here is large, because we don't know how long the test could need + // We cover a lot of error cases above, but I want to make sure we eventually give up and don't hang the build + // just in case we missed one -anurse + if (!await started.Task.TimeoutAfter(TimeSpan.FromMinutes(10))) + { + Logger.LogInformation("iisexpress Process {pid} failed to bind to port {port}, trying again", process.Id, port); + + // Wait for the process to exit and try again + process.WaitForExit(30 * 1000); + await Task.Delay(1000); // Wait a second to make sure the socket is completely cleaned up + } + else + { + _hostProcess = process; + Logger.LogInformation("Started iisexpress successfully. Process Id : {processId}, Port: {port}", _hostProcess.Id, port); + return (url: url, hostExitToken: hostExitTokenSource.Token); + } + } + + var message = $"Failed to initialize IIS Express after {MaximumAttempts} attempts to select a port"; + Logger.LogError(message); + throw new TimeoutException(message); + } + } + + private void PrepareConfig(string contentRoot, string dllRoot, int port) + { + // Config is required. If not present then fall back to one we carry with us. + if (string.IsNullOrEmpty(DeploymentParameters.ServerConfigTemplateContent)) + { + using (var stream = GetType().Assembly.GetManifestResourceStream("Microsoft.AspNetCore.Server.IntegrationTesting.IIS.Http.config")) + using (var reader = new StreamReader(stream)) + { + DeploymentParameters.ServerConfigTemplateContent = reader.ReadToEnd(); + } + } + + var serverConfig = DeploymentParameters.ServerConfigTemplateContent; + + // Pass on the applicationhost.config to iis express. With this don't need to pass in the /path /port switches as they are in the applicationHost.config + // We take a copy of the original specified applicationHost.Config to prevent modifying the one in the repo. + serverConfig = ModifyANCMPathInConfig(replaceFlag: "[ANCMPath]", dllName: "aspnetcore.dll", serverConfig, dllRoot); + serverConfig = ModifyANCMPathInConfig(replaceFlag: "[ANCMV2Path]", dllName: "aspnetcorev2.dll", serverConfig, dllRoot); + + serverConfig = ReplacePlaceholder(serverConfig, "[PORT]", port.ToString(CultureInfo.InvariantCulture)); + serverConfig = ReplacePlaceholder(serverConfig, "[ApplicationPhysicalPath]", contentRoot); + + if (DeploymentParameters.PublishApplicationBeforeDeployment) + { + // For published apps, prefer the content in the web.config, but update it. + ModifyAspNetCoreSectionInWebConfig(key: "hostingModel", + value: DeploymentParameters.HostingModel == HostingModel.InProcess ? "inprocess" : ""); + ModifyHandlerSectionInWebConfig(key: "modules", value: DeploymentParameters.AncmVersion.ToString()); + ModifyDotNetExePathInWebConfig(); + serverConfig = RemoveRedundantElements(serverConfig); + } + else + { + // The elements normally in the web.config are in the applicationhost.config for unpublished apps. + serverConfig = ReplacePlaceholder(serverConfig, "[HostingModel]", DeploymentParameters.HostingModel.ToString()); + serverConfig = ReplacePlaceholder(serverConfig, "[AspNetCoreModule]", DeploymentParameters.AncmVersion.ToString()); + } + + DeploymentParameters.ServerConfigLocation = Path.GetTempFileName(); + Logger.LogDebug("Saving Config to {configPath}", DeploymentParameters.ServerConfigLocation); + + File.WriteAllText(DeploymentParameters.ServerConfigLocation, serverConfig); + } + + private string ReplacePlaceholder(string content, string field, string value) + { + if (content.Contains(field)) + { + content = content.Replace(field, value); + Logger.LogDebug("Writing {field} '{value}' to config", field, value); + } + return content; + } + + private string ModifyANCMPathInConfig(string replaceFlag, string dllName, string serverConfig, string dllRoot) + { + if (serverConfig.Contains(replaceFlag)) + { + var arch = DeploymentParameters.RuntimeArchitecture == RuntimeArchitecture.x64 ? $@"x64\{dllName}" : $@"x86\{dllName}"; + var ancmFile = Path.Combine(dllRoot, arch); + if (!File.Exists(Environment.ExpandEnvironmentVariables(ancmFile))) + { + ancmFile = Path.Combine(dllRoot, dllName); + if (!File.Exists(Environment.ExpandEnvironmentVariables(ancmFile))) + { + throw new FileNotFoundException("AspNetCoreModule could not be found.", ancmFile); + } + } + + Logger.LogDebug($"Writing '{replaceFlag}' '{ancmFile}' to config"); + return serverConfig.Replace(replaceFlag, ancmFile); + } + return serverConfig; + } + + private string GetIISExpressPath() + { + var programFiles = "Program Files"; + if (DotNetCommands.IsRunningX86OnX64(DeploymentParameters.RuntimeArchitecture)) + { + programFiles = "Program Files (x86)"; + } + + // Get path to program files + var iisExpressPath = Path.Combine(Environment.GetEnvironmentVariable("SystemDrive") + "\\", programFiles, "IIS Express", "iisexpress.exe"); + + if (!File.Exists(iisExpressPath)) + { + throw new Exception("Unable to find IISExpress on the machine: " + iisExpressPath); + } + + return iisExpressPath; + } + + public override void Dispose() + { + using (Logger.BeginScope("Dispose")) + { + ShutDownIfAnyHostProcess(_hostProcess); + + if (!string.IsNullOrEmpty(DeploymentParameters.ServerConfigLocation) + && File.Exists(DeploymentParameters.ServerConfigLocation)) + { + // Delete the temp applicationHostConfig that we created. + Logger.LogDebug("Deleting applicationHost.config file from {configLocation}", DeploymentParameters.ServerConfigLocation); + try + { + File.Delete(DeploymentParameters.ServerConfigLocation); + } + catch (Exception exception) + { + // Ignore delete failures - just write a log. + Logger.LogWarning("Failed to delete '{config}'. Exception : {exception}", DeploymentParameters.ServerConfigLocation, exception.Message); + } + } + + if (DeploymentParameters.PublishApplicationBeforeDeployment) + { + CleanPublishedOutput(); + } + + InvokeUserApplicationCleanup(); + + StopTimer(); + } + + // If by this point, the host process is still running (somehow), throw an error. + // A test failure is better than a silent hang and unknown failure later on + if (_hostProcess != null && !_hostProcess.HasExited) + { + throw new Exception($"iisexpress Process {_hostProcess.Id} failed to shutdown"); + } + } + + private void ModifyDotNetExePathInWebConfig() + { + // We assume the x64 dotnet.exe is on the path so we need to provide an absolute path for x86 scenarios. + // Only do it for scenarios that rely on dotnet.exe (Core, portable, etc.). + if (DeploymentParameters.RuntimeFlavor == RuntimeFlavor.CoreClr + && DeploymentParameters.ApplicationType == ApplicationType.Portable + && DotNetCommands.IsRunningX86OnX64(DeploymentParameters.RuntimeArchitecture)) + { + var executableName = DotNetCommands.GetDotNetExecutable(DeploymentParameters.RuntimeArchitecture); + if (!File.Exists(executableName)) + { + throw new Exception($"Unable to find '{executableName}'.'"); + } + ModifyAspNetCoreSectionInWebConfig("processPath", executableName); + } + } + + // Transforms the web.config file to set attributes like hostingModel="inprocess" element + private void ModifyAspNetCoreSectionInWebConfig(string key, string value) + { + var webConfigFile = Path.Combine(DeploymentParameters.PublishedApplicationRootPath, "web.config"); + var config = XDocument.Load(webConfigFile); + var element = config.Descendants("aspNetCore").FirstOrDefault(); + element.SetAttributeValue(key, value); + config.Save(webConfigFile); + } + + private void ModifyHandlerSectionInWebConfig(string key, string value) + { + var webConfigFile = Path.Combine(DeploymentParameters.PublishedApplicationRootPath, "web.config"); + var config = XDocument.Load(webConfigFile); + var element = config.Descendants("handlers").FirstOrDefault().Descendants("add").FirstOrDefault(); + element.SetAttributeValue(key, value); + config.Save(webConfigFile); + } + + // These elements are duplicated in the web.config if you publish. Remove them from the host.config. + private string RemoveRedundantElements(string serverConfig) + { + var hostConfig = XDocument.Parse(serverConfig); + + var coreElement = hostConfig.Descendants("aspNetCore").FirstOrDefault(); + coreElement?.Remove(); + + var handlersElement = hostConfig.Descendants("handlers").First(); + var handlerElement = handlersElement.Descendants("add") + .Where(x => x.Attribute("name").Value == "aspNetCore").FirstOrDefault(); + handlerElement?.Remove(); + + return hostConfig.ToString(); + } + } +} diff --git a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/Microsoft.AspNetCore.Server.IntegrationTesting.IIS.csproj b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/Microsoft.AspNetCore.Server.IntegrationTesting.IIS.csproj new file mode 100644 index 0000000000..0891a315dc --- /dev/null +++ b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/Microsoft.AspNetCore.Server.IntegrationTesting.IIS.csproj @@ -0,0 +1,21 @@ + + + + netstandard2.0 + Microsoft.AspNetCore.Server.IntegrationTesting.IIS + Provides support for integration testing using IIS based servers. + $(NoWarn);CS1591 + true + aspnetcore;iis + + + + + + + + + + + + diff --git a/test/Common.FunctionalTests/Utilities/TestIISUriHelper.cs b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/TestUriHelper.cs similarity index 52% rename from test/Common.FunctionalTests/Utilities/TestIISUriHelper.cs rename to src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/TestUriHelper.cs index b8c37ddd93..fe1b351ba4 100644 --- a/test/Common.FunctionalTests/Utilities/TestIISUriHelper.cs +++ b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/TestUriHelper.cs @@ -2,26 +2,20 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Net; -using System.Net.Sockets; -namespace Microsoft.AspNetCore.Server.IntegrationTesting +namespace Microsoft.AspNetCore.Server.IntegrationTesting.Common { - // Copied from Hosting for now https://github.com/aspnet/Hosting/blob/970bc8a30d66dd6894f8f662e5fdab9e68d57777/src/Microsoft.AspNetCore.Server.IntegrationTesting/Common/TestUriHelper.cs - internal static class TestIISUriHelper + internal static class TestUriHelper { - internal static Uri BuildTestUri(ServerType serverType) - { - return BuildTestUri(serverType, hint: null); - } - internal static Uri BuildTestUri(ServerType serverType, string hint) { // Assume status messages are enabled for Kestrel and disabled for all other servers. - return BuildTestUri(serverType, hint, statusMessagesEnabled: serverType == ServerType.Kestrel); + var statusMessagesEnabled = (serverType == ServerType.Kestrel); + + return BuildTestUri(serverType, Uri.UriSchemeHttp, hint, statusMessagesEnabled); } - internal static Uri BuildTestUri(ServerType serverType, string hint, bool statusMessagesEnabled) + internal static Uri BuildTestUri(ServerType serverType, string scheme, string hint, bool statusMessagesEnabled) { if (string.IsNullOrEmpty(hint)) { @@ -32,7 +26,7 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting // once and never released. Binding to dynamic port "0" on "localhost" (both IPv4 and IPv6) is not // supported, so the port is only bound on "127.0.0.1" (IPv4). If a test explicitly requires IPv6, // it should provide a hint URL with "localhost" (IPv4 and IPv6) or "[::1]" (IPv6-only). - return new UriBuilder("http", "127.0.0.1", 0).Uri; + return new UriBuilder(scheme, "127.0.0.1", 0).Uri; } else { @@ -40,7 +34,7 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting // from which to scrape the assigned port, so the less reliable GetNextPort() must be used. The // port is bound on "localhost" (both IPv4 and IPv6), since this is supported when using a specific // (non-zero) port. - return new UriBuilder("http", "localhost", GetNextPort()).Uri; + return new UriBuilder(scheme, "localhost", TestPortHelper.GetNextPort()).Uri; } } else @@ -51,7 +45,7 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting // Only a few tests use this codepath, so it's fine to use the less reliable GetNextPort() for simplicity. // The tests using this codepath will be reviewed to see if they can be changed to directly bind to dynamic // port "0" on "127.0.0.1" and scrape the assigned port from the status message (the default codepath). - return new UriBuilder(uriHint) { Port = GetNextPort() }.Uri; + return new UriBuilder(uriHint) { Port = TestPortHelper.GetNextPort() }.Uri; } else { @@ -60,25 +54,5 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting } } } - - // Copied from https://github.com/aspnet/KestrelHttpServer/blob/47f1db20e063c2da75d9d89653fad4eafe24446c/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/AddressRegistrationTests.cs#L508 - // - // This method is an attempt to safely get a free port from the OS. Most of the time, - // when binding to dynamic port "0" the OS increments the assigned port, so it's safe - // to re-use the assigned port in another process. However, occasionally the OS will reuse - // a recently assigned port instead of incrementing, which causes flaky tests with AddressInUse - // exceptions. This method should only be used when the application itself cannot use - // dynamic port "0" (e.g. IISExpress). Most functional tests using raw Kestrel - // (with status messages enabled) should directly bind to dynamic port "0" and scrape - // the assigned port from the status message, which should be 100% reliable since the port - // is bound once and never released. - internal static int GetNextPort() - { - using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - { - socket.Bind(new IPEndPoint(IPAddress.Loopback, 0)); - return ((IPEndPoint)socket.LocalEndPoint).Port; - } - } } } diff --git a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/WebConfigHelpers.cs b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/WebConfigHelpers.cs new file mode 100644 index 0000000000..e5c13225b5 --- /dev/null +++ b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/WebConfigHelpers.cs @@ -0,0 +1,50 @@ +// 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 System.Linq; +using System.Xml.Linq; +using Microsoft.AspNetCore.Server.IntegrationTesting; +using Microsoft.AspNetCore.Testing; + +namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS +{ + public class WebConfigHelpers + { + public static void AddDebugLogToWebConfig(string contentRoot, string filename) + { + var path = Path.Combine(contentRoot, "web.config"); + var webconfig = XDocument.Load(path); + var xElement = webconfig.Descendants("aspNetCore").Single(); + + var element = xElement.Descendants("handlerSettings").SingleOrDefault(); + if (element == null) + { + element = new XElement("handlerSettings"); + xElement.Add(element); + } + + CreateOrSetElement(element, "debugLevel", "4"); + + CreateOrSetElement(element, "debugFile", Path.Combine(contentRoot, filename)); + + webconfig.Save(path); + } + + private static void CreateOrSetElement(XElement rootElement, string name, string value) + { + if (rootElement.Descendants() + .Attributes() + .Where(attribute => attribute.Value == name) + .Any()) + { + return; + } + var element = new XElement("handlerSetting"); + element.SetAttributeValue("name", name); + element.SetAttributeValue("value", value); + rootElement.Add(element); + } + } +} diff --git a/test/Common.FunctionalTests/Inprocess/LoggingTests.cs b/test/Common.FunctionalTests/Inprocess/LoggingTests.cs index 1ac8c9e1d8..6b4b2278a4 100644 --- a/test/Common.FunctionalTests/Inprocess/LoggingTests.cs +++ b/test/Common.FunctionalTests/Inprocess/LoggingTests.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities; using Microsoft.AspNetCore.Server.IntegrationTesting; +using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Microsoft.AspNetCore.Testing.xunit; using Microsoft.Extensions.Logging; using Xunit; @@ -104,7 +105,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests deploymentParameters.EnvironmentVariables["ASPNETCORE_MODULE_DEBUG_FILE"] = firstTempFile; var deploymentResult = await DeployAsync(deploymentParameters); - Helpers.AddDebugLogToWebConfig(deploymentParameters.PublishedApplicationRootPath, secondTempFile); + WebConfigHelpers.AddDebugLogToWebConfig(deploymentParameters.PublishedApplicationRootPath, secondTempFile); var response = await deploymentResult.RetryingHttpClient.GetAsync("/"); diff --git a/test/Common.FunctionalTests/Utilities/FunctionalTestsBase.cs b/test/Common.FunctionalTests/Utilities/FunctionalTestsBase.cs index c2a9c3682a..4827fcaecc 100644 --- a/test/Common.FunctionalTests/Utilities/FunctionalTestsBase.cs +++ b/test/Common.FunctionalTests/Utilities/FunctionalTestsBase.cs @@ -25,17 +25,13 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting parameters.EnvironmentVariables[DebugEnvironmentVariable] = "4"; } - if (parameters.ServerType == ServerType.IIS) - { - // Currently hosting throws if the Servertype = IIS. - _deployer = new IISDeployer(parameters, LoggerFactory); - } - else if (parameters.ServerType == ServerType.IISExpress) + if (parameters.ServerType == ServerType.IISExpress) { parameters.ServerConfigTemplateContent = parameters.ServerConfigTemplateContent ?? File.ReadAllText("IISExpress.config"); - _deployer = new IISExpressDeployer(parameters, LoggerFactory); } + _deployer = IISApplicationDeployerFactory.Create(parameters, LoggerFactory); + var result = await _deployer.DeployAsync(); return new IISDeploymentResult(result, Logger); diff --git a/test/Common.FunctionalTests/Utilities/Helpers.cs b/test/Common.FunctionalTests/Utilities/Helpers.cs index 8ab128e15f..e68b3c8b67 100644 --- a/test/Common.FunctionalTests/Utilities/Helpers.cs +++ b/test/Common.FunctionalTests/Utilities/Helpers.cs @@ -52,41 +52,6 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests config.Save(webConfigFile); } - public static void AddDebugLogToWebConfig(string contentRoot, string filename) - { - var path = Path.Combine(contentRoot, "web.config"); - var webconfig = XDocument.Load(path); - var xElement = webconfig.Descendants("aspNetCore").Single(); - - var element = xElement.Descendants("handlerSettings").SingleOrDefault(); - if (element == null) - { - element = new XElement("handlerSettings"); - xElement.Add(element); - } - - CreateOrSetElement(element, "debugLevel", "4"); - - CreateOrSetElement(element, "debugFile", Path.Combine(contentRoot, filename)); - - webconfig.Save(path); - } - - private static void CreateOrSetElement(XElement rootElement, string name, string value) - { - if (rootElement.Descendants() - .Attributes() - .Where(attribute => attribute.Value == name) - .Any()) - { - return; - } - var element = new XElement("handlerSetting"); - element.SetAttributeValue("name", name); - element.SetAttributeValue("value", value); - rootElement.Add(element); - } - // Defaults to inprocess specific deployment parameters public static DeploymentParameters GetBaseDeploymentParameters(string site = null, HostingModel hostingModel = HostingModel.InProcess, bool publish = false) { diff --git a/test/Common.FunctionalTests/Utilities/IISTestSiteFixture.cs b/test/Common.FunctionalTests/Utilities/IISTestSiteFixture.cs index 1a60e85246..d8fd3342ed 100644 --- a/test/Common.FunctionalTests/Utilities/IISTestSiteFixture.cs +++ b/test/Common.FunctionalTests/Utilities/IISTestSiteFixture.cs @@ -3,12 +3,10 @@ using System; using System.Collections.Generic; -using System.IO; using System.Net.Http; using System.Threading; using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Testing; namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests @@ -36,15 +34,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests _forwardingProvider = new ForwardingProvider(); var loggerFactory = logging.CreateLoggerFactory(null, nameof(IISTestSiteFixture)); loggerFactory.AddProvider(_forwardingProvider); - if (deploymentParameters.ServerType == ServerType.IIS) - { - // Currently hosting throws if the Servertype = IIS. - _deployer = new IISDeployer(deploymentParameters, loggerFactory); - } - else if (deploymentParameters.ServerType == ServerType.IISExpress) - { - _deployer = new IISExpressDeployer(deploymentParameters, loggerFactory); - } + + _deployer = IISApplicationDeployerFactory.Create(deploymentParameters, loggerFactory); DeploymentResult = _deployer.DeployAsync().Result; Client = DeploymentResult.HttpClient; diff --git a/test/IIS.FunctionalTests/IIS.FunctionalTests.csproj b/test/IIS.FunctionalTests/IIS.FunctionalTests.csproj index cc02e6d69e..bbe5ffe049 100644 --- a/test/IIS.FunctionalTests/IIS.FunctionalTests.csproj +++ b/test/IIS.FunctionalTests/IIS.FunctionalTests.csproj @@ -12,6 +12,7 @@ + False @@ -32,10 +33,8 @@ - - diff --git a/test/IISExpress.FunctionalTests/IISExpress.FunctionalTests.csproj b/test/IISExpress.FunctionalTests/IISExpress.FunctionalTests.csproj index 718053f101..c2e7b815f9 100644 --- a/test/IISExpress.FunctionalTests/IISExpress.FunctionalTests.csproj +++ b/test/IISExpress.FunctionalTests/IISExpress.FunctionalTests.csproj @@ -11,6 +11,7 @@ + False @@ -31,10 +32,8 @@ - - diff --git a/test/IISExpress.FunctionalTests/InProcess/StartupTests.cs b/test/IISExpress.FunctionalTests/InProcess/StartupTests.cs index 5fbc821d29..4de6ceba3d 100644 --- a/test/IISExpress.FunctionalTests/InProcess/StartupTests.cs +++ b/test/IISExpress.FunctionalTests/InProcess/StartupTests.cs @@ -9,7 +9,6 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities; using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Testing.xunit; -using Microsoft.Extensions.CommandLineUtils; using Xunit; using Xunit.Abstractions; @@ -17,7 +16,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests { public class StartupTests : IISFunctionalTestBase { - private readonly string _dotnetLocation = DotNetMuxer.MuxerPathOrDefault(); + private readonly string _dotnetLocation = DotNetCommands.GetDotNetExecutable(RuntimeArchitecture.x64); public StartupTests(ITestOutputHelper output) : base(output) {