From 6d0e5f77ee0308686209c6760a9f92317197344f Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Sat, 25 Apr 2026 12:47:25 +0100 Subject: [PATCH 1/3] perf(build): trim test TFMs and skip viewer dump by default (#5646) Cuts cold-build time on TUnit.Dev.slnx by reducing per-project work that contributors don't need. - TestProject.props now defaults to net10.0 only. Three projects that snapshot output across consumer TFMs (Core.SourceGenerator.Tests, Assertions.SourceGenerator.Tests, PublicAPI) keep the legacy net472;net8.0;net9.0;net10.0 set explicitly. - EmitCompilerGeneratedFiles is now opt-in via -p:EmitCompilerGeneratedFiles=true. The Compile Remove for SourceGeneratedViewer\ stays unconditional so stale folders from prior opt-in builds don't poison the implicit C# glob. - Library.props gates GenerateDocumentationFile on IsPacking/GeneratePackageOnBuild/ContinuousIntegrationBuild so local incremental dev builds skip XML doc emission. - CI workflow's AOT publish step gets --no-restore (restore already ran earlier in the job). - Playground project removed (was a scratch project, not used by any test or build target, and triggered VS load failures because of missing TargetFramework/OutputType). - CONTRIBUTING.md gains a "Building TUnit Locally" section pointing contributors at TUnit.Dev.slnx, MSBUILDUSESERVER, and the new EmitCompilerGeneratedFiles opt-in. --- .github/CONTRIBUTING.md | 32 +++++++++++ .github/workflows/dotnet.yml | 2 +- Library.props | 4 +- Playground/GenericTestExample.cs | 29 ---------- Playground/Playground.csproj | 31 ---------- Playground/Program.cs | 56 ------------------- Playground/TestConstructorDemo.cs | 40 ------------- ...it.Assertions.SourceGenerator.Tests.csproj | 5 ++ .../TUnit.Core.SourceGenerator.Tests.csproj | 5 ++ TUnit.PublicAPI/TUnit.PublicAPI.csproj | 7 ++- TUnit.slnx | 1 - TestProject.props | 33 +++++++---- 12 files changed, 74 insertions(+), 171 deletions(-) delete mode 100644 Playground/GenericTestExample.cs delete mode 100644 Playground/Playground.csproj delete mode 100644 Playground/Program.cs delete mode 100644 Playground/TestConstructorDemo.cs diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index e2fd24a4a7..848a4c3333 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -104,6 +104,38 @@ The relevant files are at `docs > docs > tutorials-[basics|extras|assertions]` If want to provide sample code for complicated or useful different test suite set-ups, that's also very welcome, as this can help other users get started a lot quicker! +## Building TUnit Locally + +TUnit ships several solution files. Pick the right one for your task — the +full `TUnit.slnx` is large enough that a cold build can take 20+ minutes, +which is rarely what contributors actually need: + +| Solution | Projects | When to use | +| ---------------- | -------- | -------------------------------------------------------------------------------------------------------------- | +| `TUnit.Dev.slnx` | 44 | **Recommended for contributors.** Drops the per-Roslyn-version generator variants and example projects. Open this in Visual Studio / Rider for day-to-day work. | +| `TUnit.CI.slnx` | 73 | What CI builds. Use only when you're verifying CI-specific changes. | +| `TUnit.slnx` | 96 | Full shipping graph including all per-Roslyn-version analyzer/generator variants. Slow; rarely needed locally. | + +### Faster repeat builds + +Set `MSBUILDUSESERVER=1` in your shell to keep the MSBuild process alive +between invocations — significant speedup on warm/incremental builds. + +```bash +export MSBUILDUSESERVER=1 # bash/zsh +$env:MSBUILDUSESERVER = '1' # PowerShell +``` + +### Inspecting generated source + +Source-generated files are not written to disk by default (saves significant +disk I/O on every build). To dump them under each test project's +`SourceGeneratedViewer\` folder, build with: + +```bash +dotnet build -p:EmitCompilerGeneratedFiles=true +``` + ### Code Contributions When contributing code to TUnit, please keep these important requirements in mind: diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 49bc76207f..0901e12900 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -136,7 +136,7 @@ jobs: - name: Publish AOT shell: bash run: >- - dotnet publish TUnit.TestProject/TUnit.TestProject.csproj -c Release + dotnet publish TUnit.TestProject/TUnit.TestProject.csproj -c Release --no-restore --use-current-runtime -p:Aot=true -o TESTPROJECT_AOT --framework net10.0 "-p:Version=${{ steps.gitversion.outputs.semVer }}" "-p:AssemblyVersion=${{ steps.gitversion.outputs.assemblySemVer }}" diff --git a/Library.props b/Library.props index 89f2601f4c..c5a6e3b88c 100644 --- a/Library.props +++ b/Library.props @@ -4,7 +4,9 @@ true - true + + true $(NoWarn);CS1591;CS1570;CS1572;CS1573;CS1574;CS1584;CS1587;CS1658;CS1734;CS0419 diff --git a/Playground/GenericTestExample.cs b/Playground/GenericTestExample.cs deleted file mode 100644 index bfe03ef821..0000000000 --- a/Playground/GenericTestExample.cs +++ /dev/null @@ -1,29 +0,0 @@ -using TUnit.Core; - -namespace Playground; - -public abstract class GenericTestExample -{ - [Test] - public void GenericTest() - { - // This is a test in a generic class - Console.WriteLine($"Running test with type: {typeof(T).Name}"); - } - - [Test] - [Arguments(5)] - [Arguments("hello")] - public void GenericTestWithArguments(TArg value) - { - // This is a generic method with arguments - types should be inferred from Arguments - Console.WriteLine($"Running test with value: {value} of type: {typeof(TArg).Name}"); - } -} - -// Test instantiation with different types -[InheritsTests] -public class IntGenericTests : GenericTestExample { } - -[InheritsTests] -public class StringGenericTests : GenericTestExample { } diff --git a/Playground/Playground.csproj b/Playground/Playground.csproj deleted file mode 100644 index 87298026d4..0000000000 --- a/Playground/Playground.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - - true - - - - - - - - - - - - - - - - - - - - - - - - false - - - diff --git a/Playground/Program.cs b/Playground/Program.cs deleted file mode 100644 index 16d26bfbea..0000000000 --- a/Playground/Program.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Testcontainers.PostgreSql; -using Testcontainers.Redis; -using TUnit.Core; -using TUnit.Engine.Extensions; -using Xunit; - -namespace Playground; - -public static class Program -{ -#pragma warning disable TUnit0034 - public static async Task Main(string[] args) -#pragma warning restore TUnit0034 - { - var builder = await Microsoft.Testing.Platform.Builder.TestApplication.CreateBuilderAsync(args); - - builder.AddTUnit(); - - using var app = await builder.BuildAsync(); - - return await app.RunAsync(); - } -} - -public class Tests -{ - [Fact] - public void Test() - { - var one = "1"; - - Xunit.Assert.Equal("1", one); - } -} - -public class Hooks -{ - public static PostgreSqlContainer PostgreSqlContainer { get; } = new PostgreSqlBuilder().Build(); - public static RedisContainer RedisContainer { get; } = new RedisBuilder().Build(); - - [Before(HookType.Assembly)] - public static async Task Before() - { - await PostgreSqlContainer.StartAsync(); - await RedisContainer.StartAsync(); - } - - [After(HookType.Assembly)] - public static async Task After() - { - await PostgreSqlContainer.StopAsync(); - await PostgreSqlContainer.DisposeAsync(); - await RedisContainer.StopAsync(); - await RedisContainer.DisposeAsync(); - } -} diff --git a/Playground/TestConstructorDemo.cs b/Playground/TestConstructorDemo.cs deleted file mode 100644 index 9556339e95..0000000000 --- a/Playground/TestConstructorDemo.cs +++ /dev/null @@ -1,40 +0,0 @@ -using TUnit.Core; - -namespace Playground; - -// This class should trigger TUnit0052 warning about multiple constructors -public class MultipleConstructorsDemo -{ - public MultipleConstructorsDemo() - { - } - - public MultipleConstructorsDemo(string value) - { - } - - [Test] - public void ExampleTest() - { - // This should trigger warning TUnit0052 - } -} - -// This class should work fine with TestConstructor -public class TestConstructorDemo -{ - public TestConstructorDemo() - { - } - - [TestConstructor] - public TestConstructorDemo(string value) - { - } - - [Test] - public void ExampleTestWithConstructor() - { - // This should use the marked constructor without warnings - } -} \ No newline at end of file diff --git a/TUnit.Assertions.SourceGenerator.Tests/TUnit.Assertions.SourceGenerator.Tests.csproj b/TUnit.Assertions.SourceGenerator.Tests/TUnit.Assertions.SourceGenerator.Tests.csproj index c2a0c85500..38cec4121f 100644 --- a/TUnit.Assertions.SourceGenerator.Tests/TUnit.Assertions.SourceGenerator.Tests.csproj +++ b/TUnit.Assertions.SourceGenerator.Tests/TUnit.Assertions.SourceGenerator.Tests.csproj @@ -1,5 +1,10 @@ + + + net472;net8.0;net9.0;net10.0 + + diff --git a/TUnit.Core.SourceGenerator.Tests/TUnit.Core.SourceGenerator.Tests.csproj b/TUnit.Core.SourceGenerator.Tests/TUnit.Core.SourceGenerator.Tests.csproj index b870ac5405..b06f5148e8 100644 --- a/TUnit.Core.SourceGenerator.Tests/TUnit.Core.SourceGenerator.Tests.csproj +++ b/TUnit.Core.SourceGenerator.Tests/TUnit.Core.SourceGenerator.Tests.csproj @@ -1,5 +1,10 @@ + + + net472;net8.0;net9.0;net10.0 + + diff --git a/TUnit.PublicAPI/TUnit.PublicAPI.csproj b/TUnit.PublicAPI/TUnit.PublicAPI.csproj index b1aa4e0f2e..93fea6c669 100644 --- a/TUnit.PublicAPI/TUnit.PublicAPI.csproj +++ b/TUnit.PublicAPI/TUnit.PublicAPI.csproj @@ -1,7 +1,12 @@  + + + net472;net8.0;net9.0;net10.0 + + - + Release diff --git a/TUnit.slnx b/TUnit.slnx index 3a13bcae2a..e5eae1e0f4 100644 --- a/TUnit.slnx +++ b/TUnit.slnx @@ -101,7 +101,6 @@ - diff --git a/TestProject.props b/TestProject.props index b9b42a2bc7..d6c6860e3d 100644 --- a/TestProject.props +++ b/TestProject.props @@ -9,7 +9,11 @@ Condition="'$(MSBuildProjectExtension)' == '.fsproj'" /> - net472;net8.0;net9.0;net10.0 + + net10.0 Exe @@ -25,24 +29,31 @@ - - - - - - - + - true - SourceGeneratedViewer + false + SourceGeneratedViewer + + + + + + + + + + + From 9be994c85a21748440a8fb63e3a90b2de52bdfcc Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Sat, 25 Apr 2026 12:56:20 +0100 Subject: [PATCH 2/3] fix(build): clean stale SourceGeneratedViewer dirs on opt-out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR review feedback: when a contributor switches from -p:EmitCompilerGeneratedFiles=true back to the default (off), the viewer folder previously stayed on disk forever — accumulating stale generator output. The unconditional already prevents compile errors, but the files keep growing. Drop the EmitCompilerGeneratedFiles==true guard from the CleanSourceGeneratedViewer target. The folder gets cleaned whenever it exists on a local build: - opt-in: fresh per-build dump (unchanged behavior) - opt-out: one-time cleanup of stale dirs from a prior opt-in run - CI: skipped (ephemeral runners — nothing to clean) --- TestProject.props | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/TestProject.props b/TestProject.props index d6c6860e3d..269d2b50a5 100644 --- a/TestProject.props +++ b/TestProject.props @@ -46,8 +46,12 @@ + + Condition="'$(ContinuousIntegrationBuild)' != 'true' AND Exists('$(ProjectDir)SourceGeneratedViewer')"> From 813774e2fc433cba957d6ff188267cdc1bafac1c Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Sat, 25 Apr 2026 13:08:05 +0100 Subject: [PATCH 3/3] revert: drop --no-restore from AOT publish step MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The earlier `dotnet restore TUnit.CI.slnx` is RID-agnostic, but `dotnet publish --use-current-runtime` needs RID-specific assets (e.g. osx-arm64, linux-x64) in project.assets.json. Without --no-restore, publish does its own RID-aware restore. With it, the publish fails on macOS / non-x64 runners with NETSDK1047. The publish step still gets a warm NuGet cache from the prior restore — it just has to do the RID-specific resolve work itself. --- .github/workflows/dotnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 0901e12900..49bc76207f 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -136,7 +136,7 @@ jobs: - name: Publish AOT shell: bash run: >- - dotnet publish TUnit.TestProject/TUnit.TestProject.csproj -c Release --no-restore + dotnet publish TUnit.TestProject/TUnit.TestProject.csproj -c Release --use-current-runtime -p:Aot=true -o TESTPROJECT_AOT --framework net10.0 "-p:Version=${{ steps.gitversion.outputs.semVer }}" "-p:AssemblyVersion=${{ steps.gitversion.outputs.assemblySemVer }}"