Skip to content

Commit 932f97b

Browse files
committed
Update SolutionDiscoveryTests.cs
1 parent 69cc38c commit 932f97b

1 file changed

Lines changed: 87 additions & 17 deletions

File tree

src/Verify.Tests/SolutionDiscoveryTests.cs

Lines changed: 87 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -294,23 +294,83 @@ public async Task ExplicitSolutionName_OverridesDiscovery()
294294
Assert.Equal(explicitSolutionName, solutionName);
295295
}
296296

297-
static string CreateMinimalCsprojContent()
297+
[Fact]
298+
public async Task RebuildFromAnotherSolution_UpdatesMetadata()
299+
{
300+
using var directory = new TempDirectory();
301+
var tempDir = directory.Path;
302+
303+
// Create directory structure
304+
var projectDir = Path.Combine(tempDir, "TestProject");
305+
Directory.CreateDirectory(projectDir);
306+
307+
// Two solution directories the same project can be built from
308+
var firstSolutionDir = Path.Combine(tempDir, "First") + Path.DirectorySeparatorChar;
309+
var secondSolutionDir = Path.Combine(tempDir, "Second") + Path.DirectorySeparatorChar;
310+
Directory.CreateDirectory(firstSolutionDir);
311+
Directory.CreateDirectory(secondSolutionDir);
312+
313+
// Create .csproj file. It does not reference Verify.csproj: SolutionDir is a global
314+
// property, so it would flow into that build too, and this repo derives its strong name
315+
// key path from SolutionDir. Only the imported props are needed here anyway.
316+
var csprojPath = Path.Combine(projectDir, "TestProject.csproj");
317+
await File.WriteAllTextAsync(csprojPath, CreateMinimalCsprojContent(referenceVerify: false));
318+
319+
// Build against the first solution
320+
var (success, output) = await BuildProject(csprojPath, firstSolutionDir, "FirstSolution");
321+
Assert.True(success, $"Build failed: {output}");
322+
323+
var assemblyPath = GetAssemblyPath(projectDir);
324+
var (solutionDir, solutionName) = LoadAssemblyAndGetMetadata(assemblyPath);
325+
326+
Assert.Equal(firstSolutionDir, solutionDir);
327+
Assert.Equal("FirstSolution", solutionName);
328+
329+
// Rebuild the same intermediate directory against the second solution. No file the
330+
// up-to-date check can see has changed, so only the attributes cache can stop
331+
// WriteVerifyAttributes being skipped and the baked in metadata going stale.
332+
(success, output) = await BuildProject(csprojPath, secondSolutionDir, "SecondSolution");
333+
Assert.True(success, $"Build failed: {output}");
334+
335+
(solutionDir, solutionName) = LoadAssemblyAndGetMetadata(assemblyPath);
336+
337+
Assert.Equal(secondSolutionDir, solutionDir);
338+
Assert.Equal("SecondSolution", solutionName);
339+
Assert.DoesNotContain(skipMessage, output);
340+
341+
// Build against the second solution again. The values are unchanged, so the target has
342+
// to go back to being skipped rather than regenerating on every build.
343+
(success, output) = await BuildProject(csprojPath, secondSolutionDir, "SecondSolution");
344+
Assert.True(success, $"Build failed: {output}");
345+
Assert.Contains(skipMessage, output);
346+
}
347+
348+
// MSBuild message, in the language BuildProject pins the build to
349+
const string skipMessage = "Skipping target \"WriteVerifyAttributes\" because all output files are up-to-date";
350+
351+
static string CreateMinimalCsprojContent(bool referenceVerify = true)
298352
{
299353
// Get the path to Verify.csproj and Verify.props relative to test project
300354
var verifyProjectPath = Path.Combine(ProjectFiles.SolutionDirectory, "Verify", "Verify.csproj");
301355

302356
var verifyPropsPath = Path.Combine(ProjectFiles.SolutionDirectory, "Verify", "buildTransitive", "Verify.props");
303357

358+
var reference = referenceVerify
359+
? $"""
360+
<ItemGroup>
361+
<ProjectReference Include="{verifyProjectPath}" />
362+
</ItemGroup>
363+
"""
364+
: "";
365+
304366
return $"""
305367
<Project Sdk="Microsoft.NET.Sdk">
306368
<PropertyGroup>
307369
<TargetFramework>net10.0</TargetFramework>
308370
<OutputType>Library</OutputType>
309371
<AssemblyName>TestProject</AssemblyName>
310372
</PropertyGroup>
311-
<ItemGroup>
312-
<ProjectReference Include="{verifyProjectPath}" />
313-
</ItemGroup>
373+
{reference}
314374
<Import Project="{verifyPropsPath}" />
315375
</Project>
316376
""";
@@ -341,28 +401,38 @@ static string CreateMinimalSlnContent() =>
341401

342402
static async Task<(bool success, string output)> BuildProject(string csprojPath, string? solutionDir = null, string? solutionName = null)
343403
{
344-
var args = $"build \"{csprojPath}\" --configuration Release --verbosity normal";
345-
346-
if (solutionDir != null)
347-
{
348-
args += $" \"/p:SolutionDir={solutionDir}\"";
349-
}
350-
351-
if (solutionName != null)
352-
{
353-
args += $" \"/p:SolutionName={solutionName}\"";
354-
}
355-
356404
var startInfo = new ProcessStartInfo
357405
{
358406
FileName = "dotnet",
359-
Arguments = args,
360407
RedirectStandardOutput = true,
361408
RedirectStandardError = true,
362409
UseShellExecute = false,
363410
CreateNoWindow = true
364411
};
365412

413+
// MSBuild localizes its messages, and the assertions above match the English text
414+
startInfo.Environment["DOTNET_CLI_UI_LANGUAGE"] = "en";
415+
416+
// ArgumentList quotes each value, so a SolutionDir ending in a separator is not
417+
// mangled by that separator escaping the closing quote
418+
var arguments = startInfo.ArgumentList;
419+
arguments.Add("build");
420+
arguments.Add(csprojPath);
421+
arguments.Add("--configuration");
422+
arguments.Add("Release");
423+
arguments.Add("--verbosity");
424+
arguments.Add("normal");
425+
426+
if (solutionDir != null)
427+
{
428+
arguments.Add($"/p:SolutionDir={solutionDir}");
429+
}
430+
431+
if (solutionName != null)
432+
{
433+
arguments.Add($"/p:SolutionName={solutionName}");
434+
}
435+
366436
using var process = Process.Start(startInfo)!;
367437

368438
var outputTask = process.StandardOutput.ReadToEndAsync();

0 commit comments

Comments
 (0)