Skip to content

Commit 92d37fc

Browse files
committed
Run tests on the TestApp when published as self-contained
This is when the default DependencyContext is actually null. Also run all the tests on .NET 6 which is a Long Term Support (LTS) release.
1 parent 9a28018 commit 92d37fc

File tree

6 files changed

+123
-54
lines changed

6 files changed

+123
-54
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
namespace Serilog.Settings.Configuration.Tests;
2+
3+
/// <summary>
4+
/// The possible application publish modes for the TestApp.
5+
/// See also the <a href="https://learn.microsoft.com/en-us/dotnet/core/deploying/">.NET application publishing overview</a> documentation.
6+
/// </summary>
7+
public enum PublishMode
8+
{
9+
/// <summary>
10+
/// Standard app publish, all dlls and related files are copied along the main executable.
11+
/// </summary>
12+
Standard,
13+
14+
/// <summary>
15+
/// Publish a single file as a framework-dependent binary.
16+
/// </summary>
17+
/// <remarks>On .NET Framework, <a href="https://github.com/Fody/Costura">Costura</a> is used to publish as a single file.</remarks>
18+
SingleFile,
19+
20+
/// <summary>
21+
/// Publish a single file as a self contained binary, i.e. including the .NET libraries and target runtime.
22+
/// </summary>
23+
/// <remarks>This mode is ignored on .NET Framework as it doesn't make sense.</remarks>
24+
SelfContained,
25+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System.Reflection;
2+
using System.Runtime.Versioning;
3+
using NuGet.Frameworks;
4+
5+
namespace Serilog.Settings.Configuration.Tests;
6+
7+
public static class PublishModeExtensions
8+
{
9+
static PublishModeExtensions()
10+
{
11+
var targetFrameworkAttribute = typeof(TestApp).Assembly.GetCustomAttribute<TargetFrameworkAttribute>();
12+
if (targetFrameworkAttribute == null)
13+
{
14+
throw new Exception($"Assembly {typeof(TestApp).Assembly} does not have a {nameof(TargetFrameworkAttribute)}");
15+
}
16+
17+
var framework = NuGetFramework.Parse(targetFrameworkAttribute.FrameworkName);
18+
19+
TargetFramework = framework.GetShortFolderName();
20+
IsDesktop = framework.IsDesktop();
21+
}
22+
23+
public static bool IsDesktop { get; }
24+
25+
public static string TargetFramework { get; }
26+
27+
public static IEnumerable<PublishMode> GetPublishModes()
28+
{
29+
return IsDesktop ? new[] { PublishMode.Standard, PublishMode.SingleFile } : Enum.GetValues(typeof(PublishMode)).Cast<PublishMode>();
30+
}
31+
}

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

Lines changed: 49 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,19 @@ public void Dispose()
2626
}
2727

2828
[Theory]
29-
[CombinatorialData]
30-
public async Task RunTestApp_NoUsingAndNoAssembly(bool singleFile)
29+
[ClassData(typeof(PublishModeTheoryData))]
30+
public async Task RunTestApp_NoUsingAndNoAssembly(PublishMode publishMode)
3131
{
32-
var (isSingleFile, stdOut, stdErr) = await RunTestAppAsync(singleFile);
32+
var (isSingleFile, stdOut, stdErr) = await RunTestAppAsync(publishMode);
3333
stdOut.Should().Be(isSingleFile ? "Expected exception" : "(Main thread) [Information] Expected success");
3434
stdErr.Should().BeEmpty();
3535
}
3636

3737
[Theory]
38-
[CombinatorialData]
39-
public async Task RunTestApp_UsingConsole(bool singleFile)
38+
[ClassData(typeof(PublishModeTheoryData))]
39+
public async Task RunTestApp_UsingConsole(PublishMode publishMode)
4040
{
41-
var (isSingleFile, stdOut, stdErr) = await RunTestAppAsync(singleFile, "--using-console");
41+
var (isSingleFile, stdOut, stdErr) = await RunTestAppAsync(publishMode, "--using-console");
4242
stdOut.Should().Be(isSingleFile ? "() [Information] Expected success" : "(Main thread) [Information] Expected success");
4343
if (isSingleFile)
4444
stdErr.Should().Contain("Unable to find a method called WithThreadName");
@@ -47,10 +47,10 @@ public async Task RunTestApp_UsingConsole(bool singleFile)
4747
}
4848

4949
[Theory]
50-
[CombinatorialData]
51-
public async Task RunTestApp_UsingThread(bool singleFile)
50+
[ClassData(typeof(PublishModeTheoryData))]
51+
public async Task RunTestApp_UsingThread(PublishMode publishMode)
5252
{
53-
var (isSingleFile, stdOut, stdErr) = await RunTestAppAsync(singleFile, "--using-thread");
53+
var (isSingleFile, stdOut, stdErr) = await RunTestAppAsync(publishMode, "--using-thread");
5454
stdOut.Should().Be(isSingleFile ? "" : "(Main thread) [Information] Expected success");
5555
if (isSingleFile)
5656
stdErr.Should().Contain("Unable to find a method called Console");
@@ -59,48 +59,48 @@ public async Task RunTestApp_UsingThread(bool singleFile)
5959
}
6060

6161
[Theory]
62-
[CombinatorialData]
63-
public async Task RunTestApp_AssemblyThread(bool singleFile)
62+
[ClassData(typeof(PublishModeTheoryData))]
63+
public async Task RunTestApp_AssemblyThread(PublishMode publishMode)
6464
{
65-
var (_, stdOut, stdErr) = await RunTestAppAsync(singleFile, "--assembly-thread");
65+
var (_, stdOut, stdErr) = await RunTestAppAsync(publishMode, "--assembly-thread");
6666
stdOut.Should().BeEmpty();
6767
stdErr.Should().Contain("Unable to find a method called Console");
6868
}
6969

7070
[Theory]
71-
[CombinatorialData]
72-
public async Task RunTestApp_AssemblyConsole(bool singleFile)
71+
[ClassData(typeof(PublishModeTheoryData))]
72+
public async Task RunTestApp_AssemblyConsole(PublishMode publishMode)
7373
{
74-
var (_, stdOut, stdErr) = await RunTestAppAsync(singleFile, "--assembly-console");
74+
var (_, stdOut, stdErr) = await RunTestAppAsync(publishMode, "--assembly-console");
7575
stdOut.Should().Be("() [Information] Expected success");
7676
stdErr.Should().Contain("Unable to find a method called WithThreadName");
7777
}
7878

7979
[Theory]
80-
[CombinatorialData]
81-
public async Task RunTestApp_ConsoleAndThread(bool singleFile, [CombinatorialValues("using", "assembly")] string strategy)
80+
[ClassData(typeof(PublishModeAndStrategyTheoryData))]
81+
public async Task RunTestApp_ConsoleAndThread(PublishMode publishMode, string strategy)
8282
{
83-
var (_, stdOut, stdErr) = await RunTestAppAsync(singleFile, $"--{strategy}-console", $"--{strategy}-thread");
83+
var (_, stdOut, stdErr) = await RunTestAppAsync(publishMode, $"--{strategy}-console", $"--{strategy}-thread");
8484
stdOut.Should().Be("(Main thread) [Information] Expected success");
8585
stdErr.Should().BeEmpty();
8686
}
8787

88-
async Task<(bool IsSingleFile, string StdOut, string StdErr)> RunTestAppAsync(bool singleFile, params string[] args)
88+
async Task<(bool IsSingleFile, string StdOut, string StdErr)> RunTestAppAsync(PublishMode publishMode, params string[] args)
8989
{
9090
// Determine whether the app is a _true_ single file, i.e. not a .NET Core 3.x version which
9191
// [extracts bundled files to disk][1] and thus can find dlls.
9292
// [1]: https://github.com/dotnet/designs/blob/main/accepted/2020/single-file/extract.md
93-
var (isSingleFile, _) = await RunTestAppInternalAsync(singleFile, "is-single-file");
94-
var (stdOut, stdErr) = await RunTestAppInternalAsync(singleFile, args);
93+
var (isSingleFile, _) = await RunTestAppInternalAsync(publishMode, "is-single-file");
94+
var (stdOut, stdErr) = await RunTestAppInternalAsync(publishMode, args);
9595
return (bool.Parse(isSingleFile), stdOut, stdErr);
9696
}
9797

98-
async Task<(string StdOut, string StdErr)> RunTestAppInternalAsync(bool singleExe, params string[] args)
98+
async Task<(string StdOut, string StdErr)> RunTestAppInternalAsync(PublishMode publishMode, params string[] args)
9999
{
100100
var stdOutBuilder = new StringBuilder();
101101
var stdErrBuilder = new StringBuilder();
102102

103-
var command = Cli.Wrap(singleExe ? _testApp.SingleFileExe.FullName : _testApp.StandardExe.FullName)
103+
var command = Cli.Wrap(_testApp.GetExecutablePath(publishMode))
104104
.WithArguments(args)
105105
.WithValidation(CommandResultValidation.None)
106106
.WithStandardOutputPipe(PipeTarget.ToStringBuilder(stdOutBuilder))
@@ -122,9 +122,34 @@ public async Task RunTestApp_ConsoleAndThread(bool singleFile, [CombinatorialVal
122122

123123
if (result.ExitCode != 0)
124124
{
125-
throw new Exception($"An unexpected exception has occurred while running {command}. {stdErr}".Trim());
125+
throw new Exception($"An unexpected exception has occurred while running {command}{Environment.NewLine}{stdErr}".Trim());
126126
}
127127

128128
return (stdOut, stdErr);
129129
}
130+
131+
class PublishModeTheoryData : TheoryData<PublishMode>
132+
{
133+
public PublishModeTheoryData()
134+
{
135+
foreach (var publishMode in PublishModeExtensions.GetPublishModes())
136+
{
137+
Add(publishMode);
138+
}
139+
}
140+
}
141+
142+
class PublishModeAndStrategyTheoryData : TheoryData<PublishMode, string>
143+
{
144+
public PublishModeAndStrategyTheoryData()
145+
{
146+
foreach (var publishMode in PublishModeExtensions.GetPublishModes())
147+
{
148+
foreach (var strategy in new[] { "using", "assembly" })
149+
{
150+
Add(publishMode, strategy);
151+
}
152+
}
153+
}
154+
}
130155
}

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT'">net48</TargetFrameworks>
5-
<TargetFrameworks>$(TargetFrameworks);net7.0;netcoreapp3.1</TargetFrameworks>
5+
<TargetFrameworks>$(TargetFrameworks);net7.0;net6.0;netcoreapp3.1</TargetFrameworks>
66
</PropertyGroup>
77

88
<ItemGroup>
@@ -24,7 +24,6 @@
2424
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
2525
<PackageReference Include="NuGet.Frameworks" Version="6.5.0" />
2626
<PackageReference Include="Serilog.Expressions" Version="3.3.0" />
27-
<PackageReference Include="Xunit.Combinatorial" Version="1.5.25" />
2827
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5" />
2928
<PackageReference Include="xunit" Version="2.4.2" />
3029
<PackageReference Include="Shouldly" Version="4.1.0" />

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

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
1-
using System.Reflection;
21
using System.Runtime.CompilerServices;
32
using System.Runtime.InteropServices;
4-
using System.Runtime.Versioning;
53
using System.Xml.Linq;
64
using System.Xml.XPath;
75
using CliWrap;
86
using FluentAssertions;
97
using Medallion.Threading;
108
using Medallion.Threading.FileSystem;
11-
using NuGet.Frameworks;
129
using Xunit.Abstractions;
1310
using Xunit.Sdk;
11+
using static Serilog.Settings.Configuration.Tests.PublishModeExtensions;
1412

1513
namespace Serilog.Settings.Configuration.Tests;
1614

@@ -19,6 +17,7 @@ public class TestApp : IAsyncLifetime
1917
readonly IMessageSink _messageSink;
2018
readonly DirectoryInfo _workingDirectory;
2119
readonly List<DirectoryInfo> _directoriesToCleanup;
20+
readonly Dictionary<PublishMode, FileInfo> _executables;
2221
readonly IDistributedLock _lock;
2322
IDistributedSynchronizationHandle? _lockHandle;
2423

@@ -27,51 +26,43 @@ public TestApp(IMessageSink messageSink)
2726
_messageSink = messageSink;
2827
_workingDirectory = GetDirectory("test", "TestApp");
2928
_directoriesToCleanup = new List<DirectoryInfo>();
29+
_executables = new Dictionary<PublishMode, FileInfo>();
3030
_lock = new FileDistributedLock(new FileInfo(Path.Combine(_workingDirectory.FullName, "dotnet-restore.lock")));
3131
}
3232

3333
public async Task InitializeAsync()
3434
{
3535
_lockHandle = await _lock.AcquireAsync();
3636

37-
var targetFrameworkAttribute = typeof(TestApp).Assembly.GetCustomAttribute<TargetFrameworkAttribute>();
38-
if (targetFrameworkAttribute == null)
37+
foreach (var publishMode in GetPublishModes())
3938
{
40-
throw new Exception($"Assembly {typeof(TestApp).Assembly} does not have a {nameof(TargetFrameworkAttribute)}");
41-
}
42-
43-
var targetFramework = NuGetFramework.Parse(targetFrameworkAttribute.FrameworkName);
44-
foreach (var singleFile in new[] { true, false })
45-
{
46-
var framework = targetFramework.GetShortFolderName();
47-
var isDesktop = targetFramework.IsDesktop();
48-
49-
var outputDirectory = new DirectoryInfo(Path.Combine(_workingDirectory.FullName, framework, singleFile ? "publish-single-file" : "publish-standard"));
39+
var outputDirectory = new DirectoryInfo(Path.Combine(_workingDirectory.FullName, TargetFramework, publishMode.ToString()));
5040
_directoriesToCleanup.Add(outputDirectory.Parent!);
5141

52-
var restoreArgs = new[] { "restore", "--no-dependencies", $"-p:TargetFrameworks={string.Join("%3B", GetProjectTargetFrameworks().Append(framework).Distinct())}" };
42+
var restoreArgs = new[] { "restore", $"-p:TargetFrameworks={string.Join("%3B", GetProjectTargetFrameworks().Append(TargetFramework).Distinct())}" };
5343
await RunDotnetAsync(_workingDirectory, restoreArgs);
5444

55-
File.WriteAllText(Path.Combine(_workingDirectory.FullName, "FodyWeavers.xml"), singleFile && isDesktop ? "<Weavers><Costura/></Weavers>" : "<Weavers/>");
45+
File.WriteAllText(Path.Combine(_workingDirectory.FullName, "FodyWeavers.xml"), publishMode == PublishMode.SingleFile && IsDesktop ? "<Weavers><Costura/></Weavers>" : "<Weavers/>");
5646

57-
var args = new[] { "publish", "--no-restore", "--configuration", "Release", "--output", outputDirectory.FullName, $"-p:TargetFramework={framework}" };
58-
await RunDotnetAsync(_workingDirectory, isDesktop ? args : args.Append($"-p:PublishSingleFile={singleFile}").ToArray());
47+
var args = new[] { "publish", "--no-restore", "--configuration", "Release", "--output", outputDirectory.FullName, $"-p:TargetFramework={TargetFramework}" };
48+
var publishSingleFile = $"-p:PublishSingleFile={publishMode is PublishMode.SingleFile or PublishMode.SelfContained}";
49+
var selfContained = $"-p:SelfContained={publishMode is PublishMode.SelfContained}";
50+
await RunDotnetAsync(_workingDirectory, IsDesktop ? args : args.Append(publishSingleFile).Append(selfContained).ToArray());
5951

6052
var executableFileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "TestApp.exe" : "TestApp";
6153
var executableFile = new FileInfo(Path.Combine(outputDirectory.FullName, executableFileName));
6254
executableFile.Exists.Should().BeTrue();
6355
var dlls = executableFile.Directory!.EnumerateFiles("*.dll");
64-
if (singleFile)
56+
if (publishMode == PublishMode.Standard)
6557
{
66-
dlls.Should().BeEmpty(because: "the test app was published as single-file");
67-
executableFile.Directory.EnumerateFiles().Should().ContainSingle().Which.FullName.Should().Be(executableFile.FullName);
68-
SingleFileExe = executableFile;
58+
dlls.Should().NotBeEmpty(because: $"the test app was _not_ published as single-file ({publishMode})");
6959
}
7060
else
7161
{
72-
dlls.Should().NotBeEmpty(because: "the test app was _not_ published as single-file");
73-
StandardExe = executableFile;
62+
dlls.Should().BeEmpty(because: $"the test app was published as single-file ({publishMode})");
63+
executableFile.Directory.EnumerateFiles().Should().ContainSingle().Which.FullName.Should().Be(executableFile.FullName);
7464
}
65+
_executables[publishMode] = executableFile;
7566
}
7667
}
7768

@@ -90,8 +81,7 @@ public async Task DisposeAsync()
9081
}
9182
}
9283

93-
public FileInfo SingleFileExe { get; private set; } = null!;
94-
public FileInfo StandardExe { get; private set; } = null!;
84+
public string GetExecutablePath(PublishMode publishMode) => _executables[publishMode].FullName;
9585

9686
async Task RunDotnetAsync(DirectoryInfo workingDirectory, params string[] arguments)
9787
{

test/TestApp/TestApp.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
<GenerateSupportedRuntime>false</GenerateSupportedRuntime>
99
<PublishReferencesDocumentationFiles>false</PublishReferencesDocumentationFiles>
1010
<AllowedReferenceRelatedFileExtensions>none</AllowedReferenceRelatedFileExtensions>
11-
<SelfContained>false</SelfContained>
1211
<UseCurrentRuntimeIdentifier>true</UseCurrentRuntimeIdentifier>
1312
</PropertyGroup>
1413

0 commit comments

Comments
 (0)