Skip to content

Commit 2b6c040

Browse files
committed
Make tests successfully run concurrently across multiple target frameworks
1 parent 74402f8 commit 2b6c040

File tree

8 files changed

+172
-135
lines changed

8 files changed

+172
-135
lines changed

test/Serilog.Settings.Configuration.Tests/PublishSingleFileTests.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
using System.Diagnostics;
22
using System.Text;
33
using CliWrap;
4+
using CliWrap.Exceptions;
45
using FluentAssertions;
56
using FluentAssertions.Execution;
7+
using Serilog.Settings.Configuration.Tests.Support;
68
using Xunit.Abstractions;
79

810
namespace Serilog.Settings.Configuration.Tests;
@@ -122,7 +124,7 @@ public async Task RunTestApp_ConsoleAndThread(PublishMode publishMode, string st
122124

123125
if (result.ExitCode != 0)
124126
{
125-
throw new Exception($"An unexpected exception has occurred while running {command}{Environment.NewLine}{stdErr}".Trim());
127+
throw new CommandExecutionException(command, result.ExitCode, $"An unexpected exception has occurred while running {command}{Environment.NewLine}{stdErr}".Trim());
126128
}
127129

128130
return (stdOut, stdErr);

test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@
1818

1919
<ItemGroup>
2020
<PackageReference Include="CliWrap" Version="3.6.0" />
21-
<PackageReference Include="DistributedLock.FileSystem" Version="1.0.1" />
2221
<PackageReference Include="FluentAssertions" Version="6.10.0" />
2322
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
2423
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
2524
<PackageReference Include="NuGet.Frameworks" Version="6.5.0" />
25+
<PackageReference Include="Polly" Version="7.2.3" />
2626
<PackageReference Include="Serilog.Expressions" Version="3.3.0" />
2727
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5" />
2828
<PackageReference Include="xunit" Version="2.4.2" />
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace Serilog.Settings.Configuration.Tests;
2+
3+
static class DirectoryInfoExtensions
4+
{
5+
public static DirectoryInfo SubDirectory(this DirectoryInfo directory, params string[] paths)
6+
=> new(Path.GetFullPath(Path.Combine(paths.Prepend(directory.FullName).ToArray())));
7+
8+
public static FileInfo File(this DirectoryInfo directory, params string[] paths)
9+
=> new(Path.GetFullPath(Path.Combine(paths.Prepend(directory.FullName).ToArray())));
10+
}

test/Serilog.Settings.Configuration.Tests/PublishMode.cs renamed to test/Serilog.Settings.Configuration.Tests/Support/PublishMode.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace Serilog.Settings.Configuration.Tests;
1+
namespace Serilog.Settings.Configuration.Tests.Support;
22

33
/// <summary>
44
/// The possible application publish modes for the TestApp.

test/Serilog.Settings.Configuration.Tests/PublishModeExtensions.cs renamed to test/Serilog.Settings.Configuration.Tests/Support/PublishModeExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
using System.Runtime.Versioning;
33
using NuGet.Frameworks;
44

5-
namespace Serilog.Settings.Configuration.Tests;
5+
namespace Serilog.Settings.Configuration.Tests.Support;
66

77
public static class PublishModeExtensions
88
{
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
using System.Runtime.CompilerServices;
2+
using System.Runtime.InteropServices;
3+
using System.Text;
4+
using CliWrap;
5+
using CliWrap.Exceptions;
6+
using FluentAssertions;
7+
using Polly;
8+
using Xunit.Abstractions;
9+
using Xunit.Sdk;
10+
using static Serilog.Settings.Configuration.Tests.Support.PublishModeExtensions;
11+
12+
namespace Serilog.Settings.Configuration.Tests.Support;
13+
14+
public class TestApp : IAsyncLifetime
15+
{
16+
readonly IMessageSink _messageSink;
17+
readonly DirectoryInfo _workingDirectory;
18+
readonly Dictionary<PublishMode, FileInfo> _executables;
19+
20+
public TestApp(IMessageSink messageSink)
21+
{
22+
_messageSink = messageSink;
23+
_workingDirectory = GetDirectory("test", $"TestApp-{TargetFramework}");
24+
_workingDirectory.Create();
25+
foreach (var file in GetDirectory("test", "TestApp").EnumerateFiles())
26+
{
27+
file.CopyTo(_workingDirectory.File(file.Name).FullName, overwrite: true);
28+
}
29+
_executables = new Dictionary<PublishMode, FileInfo>();
30+
}
31+
32+
async Task IAsyncLifetime.InitializeAsync()
33+
{
34+
// Retry 3 times because pack / restore / publish may try to access the same files across different target frameworks and fail with System.IO.IOException:
35+
// The process cannot access the file [Serilog.Settings.Configuration.deps.json or Serilog.Settings.Configuration.dll] because it is being used by another process.
36+
var retryPolicy = Policy.Handle<CommandExecutionException>().RetryAsync(3);
37+
await retryPolicy.ExecuteAsync(CreateTestAppAsync);
38+
}
39+
40+
Task IAsyncLifetime.DisposeAsync()
41+
{
42+
_workingDirectory.Delete(recursive: true);
43+
return Task.CompletedTask;
44+
}
45+
46+
public string GetExecutablePath(PublishMode publishMode) => _executables[publishMode].FullName;
47+
48+
async Task CreateTestAppAsync()
49+
{
50+
await PackAsync();
51+
await RestoreAsync();
52+
53+
var publishDirectory = _workingDirectory.SubDirectory("publish");
54+
var fodyWeaversXml = _workingDirectory.File("FodyWeavers.xml");
55+
56+
foreach (var publishMode in GetPublishModes())
57+
{
58+
var outputDirectory = publishDirectory.SubDirectory(publishMode.ToString());
59+
60+
File.WriteAllText(fodyWeaversXml.FullName, publishMode == PublishMode.SingleFile && IsDesktop ? "<Weavers><Costura/></Weavers>" : "<Weavers/>");
61+
62+
var publishArgs = new[] {
63+
"publish",
64+
"--no-restore",
65+
"--configuration", "Release",
66+
"--output", outputDirectory.FullName,
67+
$"-p:TargetFramework={TargetFramework}"
68+
};
69+
var publishSingleFile = $"-p:PublishSingleFile={publishMode is PublishMode.SingleFile or PublishMode.SelfContained}";
70+
var selfContained = $"-p:SelfContained={publishMode is PublishMode.SelfContained}";
71+
await RunDotnetAsync(_workingDirectory, IsDesktop ? publishArgs : publishArgs.Append(publishSingleFile).Append(selfContained).ToArray());
72+
73+
var executableFileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "TestApp.exe" : "TestApp";
74+
var executableFile = new FileInfo(Path.Combine(outputDirectory.FullName, executableFileName));
75+
executableFile.Exists.Should().BeTrue();
76+
var dlls = executableFile.Directory!.EnumerateFiles("*.dll");
77+
if (publishMode == PublishMode.Standard)
78+
{
79+
dlls.Should().NotBeEmpty(because: $"the test app was _not_ published as single-file ({publishMode})");
80+
}
81+
else
82+
{
83+
dlls.Should().BeEmpty(because: $"the test app was published as single-file ({publishMode})");
84+
executableFile.Directory.EnumerateFiles().Should().ContainSingle().Which.FullName.Should().Be(executableFile.FullName);
85+
}
86+
_executables[publishMode] = executableFile;
87+
}
88+
}
89+
90+
async Task PackAsync()
91+
{
92+
var projectFile = GetFile("src", "Serilog.Settings.Configuration", "Serilog.Settings.Configuration.csproj");
93+
var packArgs = new[] {
94+
"pack", projectFile.FullName,
95+
"--configuration", "Release",
96+
"--output", _workingDirectory.FullName,
97+
"-p:Version=0.0.0-IntegrationTest.0",
98+
};
99+
await RunDotnetAsync(_workingDirectory, packArgs);
100+
}
101+
102+
async Task RestoreAsync()
103+
{
104+
var packagesDirectory = _workingDirectory.SubDirectory("packages");
105+
var restoreArgs = new[] {
106+
"restore",
107+
"--packages", packagesDirectory.FullName,
108+
"--source", ".",
109+
"--source", "https://api.nuget.org/v3/index.json",
110+
"-p:Configuration=Release",
111+
$"-p:TargetFramework={TargetFramework}"
112+
};
113+
await RunDotnetAsync(_workingDirectory, restoreArgs);
114+
}
115+
116+
async Task RunDotnetAsync(DirectoryInfo workingDirectory, params string[] arguments)
117+
{
118+
_messageSink.OnMessage(new DiagnosticMessage($"cd {workingDirectory}"));
119+
_messageSink.OnMessage(new DiagnosticMessage($"dotnet {string.Join(" ", arguments)}"));
120+
var outBuilder = new StringBuilder();
121+
var errBuilder = new StringBuilder();
122+
var command = Cli.Wrap("dotnet")
123+
.WithValidation(CommandResultValidation.None)
124+
.WithWorkingDirectory(workingDirectory.FullName)
125+
.WithArguments(arguments)
126+
.WithStandardOutputPipe(PipeTarget.ToDelegate(line =>
127+
{
128+
outBuilder.AppendLine(line);
129+
_messageSink.OnMessage(new DiagnosticMessage($"==> out: {line}"));
130+
}))
131+
.WithStandardErrorPipe(PipeTarget.ToDelegate(line =>
132+
{
133+
errBuilder.AppendLine(line);
134+
_messageSink.OnMessage(new DiagnosticMessage($"==> err: {line}"));
135+
}));
136+
137+
var result = await command.ExecuteAsync();
138+
if (result.ExitCode != 0)
139+
{
140+
throw new CommandExecutionException(command, result.ExitCode, $"An unexpected exception has occurred while running {command}{Environment.NewLine}{errBuilder}{outBuilder}".Trim());
141+
}
142+
}
143+
144+
static DirectoryInfo GetDirectory(params string[] paths) => new(GetFullPath(paths));
145+
146+
static FileInfo GetFile(params string[] paths) => new(GetFullPath(paths));
147+
148+
static string GetFullPath(params string[] paths) => Path.GetFullPath(Path.Combine(new[] { GetThisDirectory(), "..", "..", ".." }.Concat(paths).ToArray()));
149+
150+
static string GetThisDirectory([CallerFilePath] string path = "") => Path.GetDirectoryName(path)!;
151+
}

test/Serilog.Settings.Configuration.Tests/TestApp.cs

Lines changed: 0 additions & 130 deletions
This file was deleted.

test/TestApp/TestApp.csproj

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,13 @@
1111
<UseCurrentRuntimeIdentifier>true</UseCurrentRuntimeIdentifier>
1212
</PropertyGroup>
1313

14-
<ItemGroup>
14+
<ItemGroup Condition="$(Configuration) == 'Debug'">
1515
<ProjectReference Include="..\..\src\Serilog.Settings.Configuration\Serilog.Settings.Configuration.csproj" />
1616
</ItemGroup>
17+
18+
<ItemGroup Condition="$(Configuration) == 'Release'">
19+
<PackageReference Include="Serilog.Settings.Configuration" Version="[0.0.0-IntegrationTest.0]" />
20+
</ItemGroup>
1721

1822
<ItemGroup>
1923
<PackageReference Include="Costura.Fody" Version="5.7.0" PrivateAssets="all" />

0 commit comments

Comments
 (0)