diff --git a/test/Microsoft.AspNetCore.AzureAppServices.FunctionalTests/Microsoft.AspNetCore.AzureAppServices.FunctionalTests.csproj b/test/Microsoft.AspNetCore.AzureAppServices.FunctionalTests/Microsoft.AspNetCore.AzureAppServices.FunctionalTests.csproj
index e3c7a4a800..c6005436d4 100644
--- a/test/Microsoft.AspNetCore.AzureAppServices.FunctionalTests/Microsoft.AspNetCore.AzureAppServices.FunctionalTests.csproj
+++ b/test/Microsoft.AspNetCore.AzureAppServices.FunctionalTests/Microsoft.AspNetCore.AzureAppServices.FunctionalTests.csproj
@@ -9,12 +9,12 @@
-
-
+
+
-
+
diff --git a/test/Microsoft.AspNetCore.AzureAppServices.FunctionalTests/PathUtilities.cs b/test/Microsoft.AspNetCore.AzureAppServices.FunctionalTests/PathUtilities.cs
new file mode 100644
index 0000000000..5d276e3ae7
--- /dev/null
+++ b/test/Microsoft.AspNetCore.AzureAppServices.FunctionalTests/PathUtilities.cs
@@ -0,0 +1,40 @@
+// 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.IO;
+using System.Linq;
+
+namespace Microsoft.AspNetCore.AzureAppServices.FunctionalTests
+{
+ public class PathUtilities
+ {
+ public static string[] GetStoreModules(string dotnetPath)
+ {
+ var dotnetHome = Path.GetDirectoryName(dotnetPath);
+ return new DirectoryInfo(Path.Combine(dotnetHome, "store", "x64", "netcoreapp2.0"))
+ .GetDirectories()
+ .Select(d => d.Name)
+ .ToArray();
+ }
+
+ public static string[] GetSharedRuntimeAssemblies(string dotnetPath)
+ {
+ var dotnetHome = Path.GetDirectoryName(dotnetPath);
+ return new DirectoryInfo(Path.Combine(dotnetHome, "shared", "Microsoft.NETCore.App"))
+ .GetDirectories()
+ .Single()
+ .GetFiles("*.dll")
+ .Select(f => f.Name)
+ .ToArray();
+ }
+
+ public static string GetBundledAspNetCoreVersion(string dotnetPath)
+ {
+ var dotnetHome = Path.GetDirectoryName(dotnetPath);
+ return new DirectoryInfo(Path.Combine(dotnetHome, "store", "x64", "netcoreapp2.0", "microsoft.aspnetcore"))
+ .GetDirectories()
+ .Single()
+ .Name;
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.AzureAppServices.FunctionalTests/TemplateFunctionalTests.cs b/test/Microsoft.AspNetCore.AzureAppServices.FunctionalTests/TemplateFunctionalTests.cs
index c59bb69dec..679886103c 100644
--- a/test/Microsoft.AspNetCore.AzureAppServices.FunctionalTests/TemplateFunctionalTests.cs
+++ b/test/Microsoft.AspNetCore.AzureAppServices.FunctionalTests/TemplateFunctionalTests.cs
@@ -7,8 +7,10 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
+using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml.Linq;
+using Newtonsoft.Json;
using Xunit;
using Xunit.Abstractions;
@@ -17,6 +19,10 @@ namespace Microsoft.AspNetCore.AzureAppServices.FunctionalTests
[Collection("Azure")]
public class TemplateFunctionalTests
{
+ private const string RuntimeInformationMiddlewareType = "Microsoft.AspNetCore.AzureAppServices.FunctionalTests.RuntimeInformationMiddleware";
+
+ private const string RuntimeInformationMiddlewareFile = "Templates\\RuntimeInformationMiddleware.cs";
+
readonly AzureFixture _fixture;
private readonly ITestOutputHelper _outputHelper;
@@ -43,6 +49,8 @@ namespace Microsoft.AspNetCore.AzureAppServices.FunctionalTests
await dotnet.ExecuteAndAssertAsync("new " + template);
+ InjectMiddlware(testDirectory, RuntimeInformationMiddlewareType, RuntimeInformationMiddlewareFile);
+
await site.BuildPublishProfileAsync(testDirectory.FullName);
await dotnet.ExecuteAndAssertAsync("publish /p:PublishProfile=Profile");
@@ -52,6 +60,12 @@ namespace Microsoft.AspNetCore.AzureAppServices.FunctionalTests
var getResult = await httpClient.GetAsync("/");
getResult.EnsureSuccessStatusCode();
Assert.Contains(expected, await getResult.Content.ReadAsStringAsync());
+
+ getResult = await httpClient.GetAsync("/runtimeInfo");
+ getResult.EnsureSuccessStatusCode();
+
+ var runtimeInfo = JsonConvert.DeserializeObject(await getResult.Content.ReadAsStringAsync());
+ ValidateRuntimeInfo(runtimeInfo, dotnet.Command);
}
}
}
@@ -80,6 +94,8 @@ namespace Microsoft.AspNetCore.AzureAppServices.FunctionalTests
await dotnet.ExecuteAndAssertAsync("new " + template);
+ InjectMiddlware(testDirectory, RuntimeInformationMiddlewareType, RuntimeInformationMiddlewareFile);
+
FixAspNetCoreVersion(testDirectory, dotnet.Command);
await dotnet.ExecuteAndAssertAsync("restore");
@@ -93,23 +109,58 @@ namespace Microsoft.AspNetCore.AzureAppServices.FunctionalTests
var getResult = await httpClient.GetAsync("/");
getResult.EnsureSuccessStatusCode();
Assert.Contains(expected, await getResult.Content.ReadAsStringAsync());
+
+ getResult = await httpClient.GetAsync("/runtimeInfo");
+ getResult.EnsureSuccessStatusCode();
+
+ var runtimeInfo = JsonConvert.DeserializeObject(await getResult.Content.ReadAsStringAsync());
+ ValidateRuntimeInfo(runtimeInfo, dotnet.Command);
}
}
}
+ private void ValidateRuntimeInfo(RuntimeInfo runtimeInfo, string dotnetPath)
+ {
+ var storeModules = PathUtilities.GetStoreModules(dotnetPath);
+
+ var runtimeModules = PathUtilities.GetSharedRuntimeAssemblies(dotnetPath);
+
+ foreach (var runtimeInfoModule in runtimeInfo.Modules)
+ {
+ if (storeModules.Any(f => runtimeInfoModule.ModuleName.StartsWith(f, StringComparison.InvariantCultureIgnoreCase)))
+ {
+ Assert.Contains("store\\x86\\netcoreapp2.0\\", runtimeInfoModule.FileName);
+ }
+
+ // Native modules would prefer to be loaded from windows folder, skip them
+ if (runtimeModules.Any(f => runtimeInfoModule.ModuleName.StartsWith(f, StringComparison.InvariantCultureIgnoreCase)) &&
+ runtimeInfoModule.FileName.IndexOf("windows\\system32", StringComparison.InvariantCultureIgnoreCase) == -1)
+ {
+ Assert.Contains("shared\\Microsoft.NETCore.App\\", runtimeInfoModule.FileName);
+ }
+ }
+ }
+
+ private static void InjectMiddlware(DirectoryInfo projectRoot, string typeName, string fileName)
+ {
+ // Copy implementation file to project directory
+ var implementationFile = Path.Combine(Directory.GetCurrentDirectory(), fileName);
+ var destinationImplementationFile = Path.Combine(projectRoot.FullName, Path.GetFileName(fileName));
+ File.Copy(implementationFile, destinationImplementationFile, true);
+
+ // Register middleware in Startup.cs/Configure
+ var startupFile = Path.Combine(projectRoot.FullName, "Startup.cs");
+ var startupText = File.ReadAllText(startupFile);
+ startupText = Regex.Replace(startupText, "public void Configure\\([^{]+{", match => match.Value + $" app.UseMiddleware<{typeName}>();");
+ File.WriteAllText(startupFile, startupText);
+ }
+
private static void FixAspNetCoreVersion(DirectoryInfo testDirectory, string dotnetPath)
{
// TODO: Temporary workaround for broken templates in latest CLI
// Detect what version of aspnet core was shipped with this CLI installation
- var aspnetCoreVersion =
- new DirectoryInfo(
- Path.Combine(
- Path.GetDirectoryName(dotnetPath),
- "store", "x64", "netcoreapp2.0", "microsoft.aspnetcore"))
- .GetDirectories()
- .Single()
- .Name;
+ var aspnetCoreVersion = PathUtilities.GetBundledAspNetCoreVersion(dotnetPath);
var csproj = testDirectory.GetFiles("*.csproj").Single().FullName;
var projectContents = XDocument.Load(csproj);
@@ -149,14 +200,14 @@ namespace Microsoft.AspNetCore.AzureAppServices.FunctionalTests
private TestCommand DotNet(TestLogger logger, DirectoryInfo workingDirectory, string sufix)
{
- return new TestCommand(GetDotnetPath(sufix))
+ return new TestCommand(GetDotNetPath(sufix))
{
Logger = logger,
WorkingDirectory = workingDirectory.FullName
};
}
- private static string GetDotnetPath(string sufix)
+ private static string GetDotNetPath(string sufix)
{
var current = new DirectoryInfo(Directory.GetCurrentDirectory());
while (current != null)
diff --git a/test/Microsoft.AspNetCore.AzureAppServices.FunctionalTests/Templates/RuntimeInformationMiddleware.cs b/test/Microsoft.AspNetCore.AzureAppServices.FunctionalTests/Templates/RuntimeInformationMiddleware.cs
new file mode 100644
index 0000000000..2850f0a77f
--- /dev/null
+++ b/test/Microsoft.AspNetCore.AzureAppServices.FunctionalTests/Templates/RuntimeInformationMiddleware.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.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Newtonsoft.Json;
+
+namespace Microsoft.AspNetCore.AzureAppServices.FunctionalTests
+{
+ public class ModuleInfo
+ {
+ public string FileName { get; set; }
+ public string ModuleName { get; set; }
+ public string Version { get; set; }
+ public string InformationalVersion { get; set; }
+ }
+
+ public class RuntimeInfo
+ {
+ public IDictionary Environment { get; set; }
+ public IList Modules { get; set; }
+ }
+
+ class RuntimeInformationMiddleware
+ {
+ private readonly RequestDelegate _next;
+
+ public RuntimeInformationMiddleware(RequestDelegate next)
+ {
+ _next = next;
+ }
+
+ public async Task Invoke(HttpContext context)
+ {
+ if (context.Request.Path == "/runtimeInfo")
+ {
+ await context.Response.WriteAsync(
+ JsonConvert.SerializeObject(
+ new RuntimeInfo
+ {
+ Environment = Environment.GetEnvironmentVariables(),
+ Modules = Process.GetCurrentProcess().Modules.OfType().Select(m =>
+ {
+ Assembly assembly = null;
+ try
+ {
+ assembly = Assembly.Load(Path.GetFileNameWithoutExtension(m.ModuleName));
+ }
+ catch { }
+
+ return new ModuleInfo
+ {
+ FileName = m.FileName,
+ ModuleName = m.ModuleName,
+ Version = assembly?.GetName().Version.ToString(),
+ InformationalVersion = assembly?.GetCustomAttribute()?.InformationalVersion
+ };
+ }).ToList()
+ }));
+ return;
+ }
+ // Call the next delegate/middleware in the pipeline
+ await _next(context);
+ }
+ }
+}