Skip to content

Commit 1f62155

Browse files
committed
- Generate relative contentRoot paths in test manifests
- Fixed the public API declarations - React to changes in the WebApplicationFactory - Use solution-relative contentRoot path, when the path from metadata either is not available or doesn't exist. - If none of the paths exist, use the AppContext.BaseDirectory instead. - Fix tests with outdated expectations
1 parent a61a717 commit 1f62155

File tree

6 files changed

+84
-12
lines changed

6 files changed

+84
-12
lines changed

src/Identity/test/Identity.FunctionalTests/Infrastructure/ServerFactory.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ protected override IHost CreateHost(IHostBuilder builder)
6363
return result;
6464
}
6565

66-
protected override TestServer CreateServer(IWebHostBuilder builder)
66+
protected override ITestServer CreateServer(IWebHostBuilder builder)
6767
{
6868
var result = base.CreateServer(builder);
6969
EnsureDatabaseCreated(result.Host.Services);

src/Mvc/Mvc.Testing.Tasks/src/GenerateMvcTestManifestTask.cs

+46-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System;
45
using System.Collections.Generic;
56
using System.IO;
7+
using System.Linq;
68
using System.Runtime.Serialization.Json;
79
using System.Text;
810
using Microsoft.Build.Framework;
@@ -33,11 +35,14 @@ public override bool Execute()
3335
{
3436
using var fileStream = File.Create(ManifestPath);
3537
var output = new Dictionary<string, string>();
38+
var manifestDirectory = Path.GetDirectoryName(ManifestPath);
39+
3640
foreach (var project in Projects)
3741
{
3842
var contentRoot = project.GetMetadata("ContentRoot");
3943
var assemblyName = project.GetMetadata("Identity");
40-
output[assemblyName] = contentRoot;
44+
var relativeContentRoot = GetRelativePath(manifestDirectory, contentRoot);
45+
output[assemblyName] = relativeContentRoot;
4146
}
4247

4348
var serializer = new DataContractJsonSerializer(typeof(Dictionary<string, string>), new DataContractJsonSerializerSettings
@@ -49,4 +54,44 @@ public override bool Execute()
4954

5055
return !Log.HasLoggedErrors;
5156
}
57+
58+
private static string GetRelativePath(string? relativeTo, string path)
59+
{
60+
if (string.IsNullOrEmpty(relativeTo))
61+
{
62+
return path;
63+
}
64+
65+
// Ensure the paths are absolute
66+
string absoluteRelativeTo = Path.GetFullPath(relativeTo);
67+
string absolutePath = Path.GetFullPath(path);
68+
69+
// Split the paths into their components
70+
string[] relativeToParts = absoluteRelativeTo.TrimEnd(Path.DirectorySeparatorChar).Split(Path.DirectorySeparatorChar);
71+
string[] pathParts = absolutePath.TrimEnd(Path.DirectorySeparatorChar).Split(Path.DirectorySeparatorChar);
72+
73+
// Find the common base path length
74+
int commonLength = 0;
75+
while (commonLength < relativeToParts.Length && commonLength < pathParts.Length &&
76+
string.Equals(relativeToParts[commonLength], pathParts[commonLength], StringComparison.OrdinalIgnoreCase))
77+
{
78+
commonLength++;
79+
}
80+
81+
// Calculate the number of directories to go up from the relativeTo path
82+
int upDirectories = relativeToParts.Length - commonLength;
83+
84+
// Build the relative path
85+
string relativePath = string.Join(Path.DirectorySeparatorChar.ToString(), new string[upDirectories].Select(_ => "..").ToArray());
86+
if (commonLength < pathParts.Length)
87+
{
88+
if (relativePath.Length > 0)
89+
{
90+
relativePath += Path.DirectorySeparatorChar;
91+
}
92+
relativePath += string.Join(Path.DirectorySeparatorChar.ToString(), pathParts.Skip(commonLength));
93+
}
94+
95+
return relativePath;
96+
}
5297
}

src/Mvc/Mvc.Testing/src/PublicAPI.Shipped.txt

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateDefaul
1616
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateDefaultClient(System.Uri! baseAddress, params System.Net.Http.DelegatingHandler![]! handlers) -> System.Net.Http.HttpClient!
1717
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.Dispose() -> void
1818
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.Factories.get -> System.Collections.Generic.IReadOnlyList<Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint!>!>!
19+
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.Server.get -> Microsoft.AspNetCore.TestHost.TestServer!
1920
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.WebApplicationFactory() -> void
2021
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.WithWebHostBuilder(System.Action<Microsoft.AspNetCore.Hosting.IWebHostBuilder!>! configuration) -> Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint!>!
2122
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions
@@ -40,6 +41,7 @@ virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.Conf
4041
virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.ConfigureWebHost(Microsoft.AspNetCore.Hosting.IWebHostBuilder! builder) -> void
4142
virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateHost(Microsoft.Extensions.Hosting.IHostBuilder! builder) -> Microsoft.Extensions.Hosting.IHost!
4243
virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateHostBuilder() -> Microsoft.Extensions.Hosting.IHostBuilder?
44+
virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateServer(Microsoft.AspNetCore.Hosting.IWebHostBuilder! builder) -> Microsoft.AspNetCore.TestHost.TestServer!
4345
virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateWebHostBuilder() -> Microsoft.AspNetCore.Hosting.IWebHostBuilder?
4446
virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.Dispose(bool disposing) -> void
4547
virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.DisposeAsync() -> System.Threading.Tasks.ValueTask
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#nullable enable
22
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.Initialize() -> void
3+
*REMOVED*Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.Server.get -> Microsoft.AspNetCore.TestHost.TestServer!
34
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.Server.get -> Microsoft.AspNetCore.TestHost.TestServer?
45
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.TestServer.get -> Microsoft.AspNetCore.TestHost.ITestServer?
6+
*REMOVED*virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateServer(Microsoft.AspNetCore.Hosting.IWebHostBuilder! builder) -> Microsoft.AspNetCore.TestHost.TestServer!
57
virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateServer(Microsoft.AspNetCore.Hosting.IWebHostBuilder! builder) -> Microsoft.AspNetCore.TestHost.ITestServer!
8+

src/Mvc/Mvc.Testing/src/WebApplicationFactory.cs

+13-7
Original file line numberDiff line numberDiff line change
@@ -246,17 +246,23 @@ private void SetContentRoot(IWebHostBuilder builder)
246246
return;
247247
}
248248

249-
var fromFile = File.Exists("MvcTestingAppManifest.json");
250-
var contentRoot = fromFile ? GetContentRootFromFile("MvcTestingAppManifest.json") : GetContentRootFromAssembly();
251-
252-
if (contentRoot != null)
249+
string? contentRoot = null;
250+
if (File.Exists("MvcTestingAppManifest.json"))
253251
{
254-
builder.UseContentRoot(contentRoot);
252+
var manifestContentRoot = GetContentRootFromFile("MvcTestingAppManifest.json");
253+
if (manifestContentRoot is not null && Directory.Exists(manifestContentRoot))
254+
{
255+
contentRoot = manifestContentRoot;
256+
}
255257
}
256-
else
258+
259+
contentRoot ??= GetContentRootFromAssembly();
260+
if (contentRoot is null || !Directory.Exists(contentRoot))
257261
{
258-
builder.UseSolutionRelativeContentRoot(typeof(TEntryPoint).Assembly.GetName().Name!);
262+
contentRoot = AppContext.BaseDirectory;
259263
}
264+
265+
builder.UseContentRoot(contentRoot);
260266
}
261267

262268
private static string? GetContentRootFromFile(string file)

src/Mvc/test/Mvc.FunctionalTests/TestingInfrastructureInheritanceTests.cs

+19-3
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,13 @@ public void TestingInfrastructure_WebHost_WithWebHostBuilderRespectsCustomizatio
2727
Assert.Equal(new[] { "ConfigureWebHost", "Customization", "FurtherCustomization" }, factory.ConfigureWebHostCalled.ToArray());
2828
Assert.True(factory.CreateServerCalled);
2929
Assert.True(factory.CreateWebHostBuilderCalled);
30-
// GetTestAssemblies is not called when reading content roots from MvcAppManifest
31-
Assert.False(factory.GetTestAssembliesCalled);
30+
31+
// When run locally on the developer machine, the test manifest will be generated,
32+
// and the content root will be read from the manifest file.
33+
// However, when run in CI, the relative path in the metadata will not exist,
34+
// and the content root lookup logic will probe the test assemblies too.
35+
//Assert.False(factory.GetTestAssembliesCalled);
36+
3237
Assert.True(factory.CreateHostBuilderCalled);
3338
Assert.False(factory.CreateHostCalled);
3439
}
@@ -45,7 +50,13 @@ public void TestingInfrastructure_GenericHost_WithWithHostBuilderRespectsCustomi
4550

4651
// Assert
4752
Assert.Equal(new[] { "ConfigureWebHost", "Customization", "FurtherCustomization" }, factory.ConfigureWebHostCalled.ToArray());
48-
Assert.False(factory.GetTestAssembliesCalled);
53+
54+
// When run locally on the developer machine, the test manifest will be generated,
55+
// and the content root will be read from the manifest file.
56+
// However, when run in CI, the relative path in the metadata will not exist,
57+
// and the content root lookup logic will probe the test assemblies too.
58+
//Assert.False(factory.GetTestAssembliesCalled);
59+
4960
Assert.True(factory.CreateHostBuilderCalled);
5061
Assert.True(factory.CreateHostCalled);
5162
Assert.False(factory.CreateServerCalled);
@@ -128,7 +139,12 @@ public void Dispose()
128139

129140
private class CustomizedFactory<TEntryPoint> : WebApplicationFactory<TEntryPoint> where TEntryPoint : class
130141
{
142+
// GetTestAssemblies is not called when reading content roots from MvcAppManifest,
143+
// and the content root specified in the manifest is a path that exists.
144+
// Otherwise, the WebApplicationFactory will try to look for the referenced assemblies which have
145+
// `WebApplicationFactoryContentRootAttribute` applied to them, to extract the content root path from that metadata.
131146
public bool GetTestAssembliesCalled { get; private set; }
147+
132148
public bool CreateWebHostBuilderCalled { get; private set; }
133149
public bool CreateHostBuilderCalled { get; private set; }
134150
public bool CreateServerCalled { get; private set; }

0 commit comments

Comments
 (0)