From 10b0e69aab4ad4665d731c85b2ed65f217b00b71 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 10 Nov 2025 13:52:45 +0000 Subject: [PATCH 01/62] Fix light mode text contrast in Assertions Library page (#3752) * Initial plan * Fix light mode text visibility in Assertions Library page Co-authored-by: thomhurst <30480171+thomhurst@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: thomhurst <30480171+thomhurst@users.noreply.github.com> --- .../AssertionsLibrary/styles.module.css | 127 +++++++++++++++--- 1 file changed, 107 insertions(+), 20 deletions(-) diff --git a/docs/src/components/AssertionsLibrary/styles.module.css b/docs/src/components/AssertionsLibrary/styles.module.css index 984944d2bf..4830c0fd02 100644 --- a/docs/src/components/AssertionsLibrary/styles.module.css +++ b/docs/src/components/AssertionsLibrary/styles.module.css @@ -22,10 +22,20 @@ } .header p { - color: #cbd5e1; + color: #64748b; font-size: 1.1rem; } +/* Light mode specific styles */ +[data-theme='light'] .header p { + color: #64748b; +} + +/* Dark mode specific styles */ +[data-theme='dark'] .header p { + color: #cbd5e1; +} + .controls { margin-bottom: 2rem; } @@ -39,23 +49,36 @@ max-width: 600px; padding: 0.875rem 1.25rem; font-size: 1rem; - background: rgba(20, 184, 166, 0.1); + background: rgba(20, 184, 166, 0.05); border: 2px solid rgba(20, 184, 166, 0.2); border-radius: 0.75rem; - color: #f1f5f9; + color: #1e293b; transition: all 0.2s ease; display: block; margin: 0 auto; } +[data-theme='dark'] .searchInput { + background: rgba(20, 184, 166, 0.1); + color: #f1f5f9; +} + .searchInput:focus { outline: none; border-color: #14b8a6; - background: rgba(20, 184, 166, 0.15); + background: rgba(20, 184, 166, 0.08); box-shadow: 0 0 0 3px rgba(20, 184, 166, 0.1); } +[data-theme='dark'] .searchInput:focus { + background: rgba(20, 184, 166, 0.15); +} + .searchInput::placeholder { + color: #64748b; +} + +[data-theme='dark'] .searchInput::placeholder { color: #94a3b8; } @@ -68,39 +91,57 @@ .categoryButton { padding: 0.5rem 1rem; - background: rgba(20, 184, 166, 0.1); + background: rgba(20, 184, 166, 0.05); border: 1px solid rgba(20, 184, 166, 0.2); border-radius: 0.5rem; - color: #cbd5e1; + color: #334155; font-weight: 500; cursor: pointer; transition: all 0.2s ease; } +[data-theme='dark'] .categoryButton { + background: rgba(20, 184, 166, 0.1); + color: #cbd5e1; +} + .categoryButton:hover { - background: rgba(20, 184, 166, 0.15); + background: rgba(20, 184, 166, 0.1); border-color: rgba(20, 184, 166, 0.3); + color: #1e293b; +} + +[data-theme='dark'] .categoryButton:hover { + background: rgba(20, 184, 166, 0.15); color: #f1f5f9; } .categoryButton.active { background: #14b8a6; border-color: #14b8a6; - color: #0f172a; + color: #ffffff; font-weight: 600; } +[data-theme='dark'] .categoryButton.active { + color: #0f172a; +} + .results { margin-top: 2rem; } .resultCount { - color: #94a3b8; + color: #64748b; font-size: 0.95rem; margin-bottom: 1.5rem; text-align: center; } +[data-theme='dark'] .resultCount { + color: #94a3b8; +} + .assertionsList { display: grid; grid-template-columns: repeat(auto-fill, minmax(450px, 1fr)); @@ -108,13 +149,18 @@ } .assertionCard { - background: linear-gradient(135deg, rgba(20, 184, 166, 0.05) 0%, rgba(14, 165, 233, 0.05) 100%); - border: 1px solid rgba(20, 184, 166, 0.2); + background: linear-gradient(135deg, rgba(20, 184, 166, 0.03) 0%, rgba(14, 165, 233, 0.03) 100%); + border: 1px solid rgba(20, 184, 166, 0.15); border-radius: 0.75rem; padding: 1.5rem; transition: all 0.3s ease; } +[data-theme='dark'] .assertionCard { + background: linear-gradient(135deg, rgba(20, 184, 166, 0.05) 0%, rgba(14, 165, 233, 0.05) 100%); + border: 1px solid rgba(20, 184, 166, 0.2); +} + .assertionCard:hover { transform: translateY(-2px); border-color: #14b8a6; @@ -131,44 +177,65 @@ .assertionName { font-size: 1.25rem; font-weight: 600; - color: #f1f5f9; + color: #0f172a; margin: 0; } +[data-theme='dark'] .assertionName { + color: #f1f5f9; +} + .assertionCategory { font-size: 0.75rem; padding: 0.25rem 0.75rem; - background: rgba(20, 184, 166, 0.2); + background: rgba(20, 184, 166, 0.1); border: 1px solid rgba(20, 184, 166, 0.3); border-radius: 1rem; - color: #5eead4; + color: #0d9488; font-weight: 500; } +[data-theme='dark'] .assertionCategory { + background: rgba(20, 184, 166, 0.2); + color: #5eead4; +} + .assertionDescription { - color: #cbd5e1; + color: #475569; margin-bottom: 1rem; font-size: 0.95rem; line-height: 1.5; } +[data-theme='dark'] .assertionDescription { + color: #cbd5e1; +} + .assertionSyntax { - background: rgba(15, 23, 42, 0.8); + background: rgba(241, 245, 249, 0.8); border: 1px solid rgba(20, 184, 166, 0.2); border-radius: 0.5rem; padding: 0.75rem; margin-bottom: 0.75rem; } +[data-theme='dark'] .assertionSyntax { + background: rgba(15, 23, 42, 0.8); +} + .assertionSyntax code { - color: #a5f3fc; + color: #0c4a6e; font-family: 'Fira Code', 'Courier New', monospace; font-size: 0.875rem; word-break: break-all; } +[data-theme='dark'] .assertionSyntax code { + color: #a5f3fc; +} + .assertionExample { - background: rgba(168, 85, 247, 0.1); + background: rgba(168, 85, 247, 0.05); border: 1px solid rgba(168, 85, 247, 0.2); border-radius: 0.5rem; padding: 0.75rem; @@ -177,24 +244,40 @@ gap: 0.5rem; } +[data-theme='dark'] .assertionExample { + background: rgba(168, 85, 247, 0.1); +} + .exampleLabel { - color: #a855f7; + color: #7c3aed; font-size: 0.8rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; } +[data-theme='dark'] .exampleLabel { + color: #a855f7; +} + .assertionExample code { - color: #e9d5ff; + color: #5b21b6; font-family: 'Fira Code', 'Courier New', monospace; font-size: 0.875rem; word-break: break-all; } +[data-theme='dark'] .assertionExample code { + color: #e9d5ff; +} + .noResults { text-align: center; padding: 3rem 1rem; + color: #64748b; +} + +[data-theme='dark'] .noResults { color: #94a3b8; } @@ -205,6 +288,10 @@ .noResults p:first-child { font-size: 1.1rem; font-weight: 600; + color: #475569; +} + +[data-theme='dark'] .noResults p:first-child { color: #cbd5e1; } From 6f9cc41415877954f9a281d2aa68a51f6ec453b9 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Mon, 10 Nov 2025 14:06:40 +0000 Subject: [PATCH 02/62] chore(deps): update tunit to 1.0.48 (#3753) Co-authored-by: Renovate Bot --- Directory.Packages.props | 6 +++--- .../TUnit.AspNet.FSharp/TestProject/TestProject.fsproj | 4 ++-- .../content/TUnit.AspNet/TestProject/TestProject.csproj | 2 +- .../ExampleNamespace.TestProject.csproj | 2 +- .../content/TUnit.Aspire.Test/ExampleNamespace.csproj | 2 +- TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj | 4 ++-- TUnit.Templates/content/TUnit.Playwright/TestProject.csproj | 2 +- TUnit.Templates/content/TUnit.VB/TestProject.vbproj | 2 +- TUnit.Templates/content/TUnit/TestProject.csproj | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 03deab996a..758cc43694 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -83,9 +83,9 @@ - - - + + + diff --git a/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj b/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj index d53e0c80df..32cd4bc7fd 100644 --- a/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj +++ b/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj @@ -10,8 +10,8 @@ - - + + diff --git a/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj b/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj index c505fbc8e2..4044669304 100644 --- a/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj +++ b/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj @@ -9,7 +9,7 @@ - + diff --git a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.TestProject/ExampleNamespace.TestProject.csproj b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.TestProject/ExampleNamespace.TestProject.csproj index 7d5ff24998..ca45c3bdf2 100644 --- a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.TestProject/ExampleNamespace.TestProject.csproj +++ b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.TestProject/ExampleNamespace.TestProject.csproj @@ -11,7 +11,7 @@ - + diff --git a/TUnit.Templates/content/TUnit.Aspire.Test/ExampleNamespace.csproj b/TUnit.Templates/content/TUnit.Aspire.Test/ExampleNamespace.csproj index ee944b3b32..0fe11b279b 100644 --- a/TUnit.Templates/content/TUnit.Aspire.Test/ExampleNamespace.csproj +++ b/TUnit.Templates/content/TUnit.Aspire.Test/ExampleNamespace.csproj @@ -10,7 +10,7 @@ - + diff --git a/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj b/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj index 4a15915970..4c79c3bfce 100644 --- a/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj +++ b/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj @@ -10,8 +10,8 @@ - - + + diff --git a/TUnit.Templates/content/TUnit.Playwright/TestProject.csproj b/TUnit.Templates/content/TUnit.Playwright/TestProject.csproj index 4e44cc95cc..25055774d9 100644 --- a/TUnit.Templates/content/TUnit.Playwright/TestProject.csproj +++ b/TUnit.Templates/content/TUnit.Playwright/TestProject.csproj @@ -8,7 +8,7 @@ - + diff --git a/TUnit.Templates/content/TUnit.VB/TestProject.vbproj b/TUnit.Templates/content/TUnit.VB/TestProject.vbproj index 4b0f8442c3..ff5e5de922 100644 --- a/TUnit.Templates/content/TUnit.VB/TestProject.vbproj +++ b/TUnit.Templates/content/TUnit.VB/TestProject.vbproj @@ -8,6 +8,6 @@ - + diff --git a/TUnit.Templates/content/TUnit/TestProject.csproj b/TUnit.Templates/content/TUnit/TestProject.csproj index 24c8206e7f..c375f81c6f 100644 --- a/TUnit.Templates/content/TUnit/TestProject.csproj +++ b/TUnit.Templates/content/TUnit/TestProject.csproj @@ -8,7 +8,7 @@ - + \ No newline at end of file From 44536fc6ed8f16825849c372525f30711b225f8b Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Mon, 10 Nov 2025 22:01:19 +0000 Subject: [PATCH 03/62] chore(deps): update dependency microsoft.playwright to 1.56.0 (#3754) Co-authored-by: Renovate Bot --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 758cc43694..c7dd52c5a3 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -35,7 +35,7 @@ - + From 1ac30f56c741871a1ec098311148cf311159796c Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 00:57:42 +0000 Subject: [PATCH 04/62] chore(deps): update dependency verify to 31.5.2 (#3755) Co-authored-by: Renovate Bot --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index c7dd52c5a3..ea8f66bc1c 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -81,7 +81,7 @@ - + From 9e00f2124e242cc2c734357f6cee041060c7f7eb Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 02:03:18 +0000 Subject: [PATCH 05/62] chore(deps): update dependency verify.tunit to 31.5.2 (#3758) Co-authored-by: Renovate Bot --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index ea8f66bc1c..0a2f8108e5 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -86,7 +86,7 @@ - + From 853bfd332918b7212109e978d4885a1feacd4a19 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 02:04:10 +0000 Subject: [PATCH 06/62] chore(deps): update dependency verify.nunit to 31.5.2 (#3756) Co-authored-by: Renovate Bot --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 0a2f8108e5..4d095ed310 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -82,7 +82,7 @@ - + From e7907c7e396caed5b26351f66d491b588eff434c Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 05:03:45 +0000 Subject: [PATCH 07/62] chore(deps): update dependency verify to 31.5.3 (#3759) Co-authored-by: Renovate Bot --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 4d095ed310..7dfb8af6f3 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -81,7 +81,7 @@ - + From cfc0218a79a4c883dcb5344ebcfce96bd1167edf Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 05:27:41 +0000 Subject: [PATCH 08/62] chore(deps): update dependency verify.nunit to 31.5.3 (#3760) Co-authored-by: Renovate Bot --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 7dfb8af6f3..970ccb50dd 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -82,7 +82,7 @@ - + From f38afbe06085004cd4f3150dd7e09dca64697c65 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 05:29:46 +0000 Subject: [PATCH 09/62] chore(deps): update dependency verify.tunit to 31.5.3 (#3761) Co-authored-by: Renovate Bot --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 970ccb50dd..a2d894b8be 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -86,7 +86,7 @@ - + From 7b7e5c31227b82134f25e91e2bf1e11252bd2e62 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 08:28:00 +0000 Subject: [PATCH 10/62] Update README.md (#3762) Co-authored-by: thomhurst <30480171_thomhurst@users.noreply.github.com> --- README.md | 72 +++++++++++++++++++++++++++---------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index d982094d67..e1a3a1f772 100644 --- a/README.md +++ b/README.md @@ -373,7 +373,7 @@ dotnet add package TUnit --prerelease ``` BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat) -AMD EPYC 7763 3.24GHz, 1 CPU, 4 logical and 2 physical cores +AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores .NET SDK 10.0.100-rc.2.25502.107 [Host] : .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 Job-GVKUBM : .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 @@ -383,10 +383,10 @@ Runtime=.NET 10.0 ``` | Method | Version | Mean | Error | StdDev | Median | |------------- |-------- |--------:|---------:|---------:|--------:| -| Build_TUnit | 1.0.30 | 1.828 s | 0.0354 s | 0.0460 s | 1.831 s | -| Build_NUnit | 4.4.0 | 1.633 s | 0.0202 s | 0.0179 s | 1.633 s | -| Build_MSTest | 4.0.1 | 1.717 s | 0.0166 s | 0.0147 s | 1.716 s | -| Build_xUnit3 | 3.2.0 | 1.635 s | 0.0214 s | 0.0201 s | 1.634 s | +| Build_TUnit | 1.0.48 | 1.798 s | 0.0345 s | 0.0424 s | 1.785 s | +| Build_NUnit | 4.4.0 | 1.575 s | 0.0169 s | 0.0158 s | 1.573 s | +| Build_MSTest | 4.0.1 | 1.659 s | 0.0150 s | 0.0140 s | 1.658 s | +| Build_xUnit3 | 3.2.0 | 1.579 s | 0.0182 s | 0.0170 s | 1.575 s | ### Scenario: Tests running asynchronous operations and async/await patterns @@ -402,13 +402,13 @@ AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores Runtime=.NET 10.0 ``` -| Method | Version | Mean | Error | StdDev | Median | -|---------- |-------- |---------:|--------:|--------:|---------:| -| TUnit | 1.0.30 | 541.8 ms | 3.38 ms | 3.16 ms | 542.0 ms | -| NUnit | 4.4.0 | 654.1 ms | 6.35 ms | 5.63 ms | 654.0 ms | -| MSTest | 4.0.1 | 625.8 ms | 5.42 ms | 4.53 ms | 625.4 ms | -| xUnit3 | 3.2.0 | 717.9 ms | 8.60 ms | 8.04 ms | 718.7 ms | -| TUnit_AOT | 1.0.30 | 123.0 ms | 0.32 ms | 0.30 ms | 122.9 ms | +| Method | Version | Mean | Error | StdDev | Median | +|---------- |-------- |---------:|---------:|---------:|---------:| +| TUnit | 1.0.48 | 562.4 ms | 4.18 ms | 3.91 ms | 561.0 ms | +| NUnit | 4.4.0 | 679.2 ms | 7.24 ms | 6.41 ms | 679.6 ms | +| MSTest | 4.0.1 | 647.7 ms | 8.63 ms | 7.65 ms | 647.8 ms | +| xUnit3 | 3.2.0 | 744.4 ms | 11.90 ms | 10.55 ms | 741.5 ms | +| TUnit_AOT | 1.0.48 | 127.6 ms | 0.45 ms | 0.42 ms | 127.6 ms | ### Scenario: Parameterized tests with multiple test cases using data attributes @@ -426,11 +426,11 @@ Runtime=.NET 10.0 ``` | Method | Version | Mean | Error | StdDev | Median | |---------- |-------- |----------:|----------:|----------:|----------:| -| TUnit | 1.0.30 | 501.16 ms | 4.557 ms | 3.805 ms | 503.78 ms | -| NUnit | 4.4.0 | 580.97 ms | 11.588 ms | 18.042 ms | 582.44 ms | -| MSTest | 4.0.1 | 587.95 ms | 9.871 ms | 8.751 ms | 588.34 ms | -| xUnit3 | 3.2.0 | 612.51 ms | 10.806 ms | 9.579 ms | 608.64 ms | -| TUnit_AOT | 1.0.30 | 26.02 ms | 0.355 ms | 0.332 ms | 25.95 ms | +| TUnit | 1.0.48 | 476.97 ms | 5.430 ms | 5.080 ms | 478.26 ms | +| NUnit | 4.4.0 | 537.80 ms | 6.692 ms | 6.260 ms | 537.55 ms | +| MSTest | 4.0.1 | 496.84 ms | 9.188 ms | 8.145 ms | 496.37 ms | +| xUnit3 | 3.2.0 | 584.15 ms | 10.733 ms | 10.039 ms | 582.13 ms | +| TUnit_AOT | 1.0.48 | 24.65 ms | 0.177 ms | 0.157 ms | 24.68 ms | ### Scenario: Tests executing massively parallel workloads with CPU-bound, I/O-bound, and mixed operations @@ -446,13 +446,13 @@ AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores Runtime=.NET 10.0 ``` -| Method | Version | Mean | Error | StdDev | Median | -|---------- |-------- |-----------:|--------:|--------:|-----------:| -| TUnit | 1.0.30 | 572.4 ms | 6.95 ms | 5.80 ms | 572.9 ms | -| NUnit | 4.4.0 | 1,170.5 ms | 8.04 ms | 7.13 ms | 1,170.7 ms | -| MSTest | 4.0.1 | 2,953.6 ms | 8.62 ms | 7.64 ms | 2,952.1 ms | -| xUnit3 | 3.2.0 | 3,043.7 ms | 6.39 ms | 5.98 ms | 3,044.4 ms | -| TUnit_AOT | 1.0.30 | 130.7 ms | 0.65 ms | 0.58 ms | 130.8 ms | +| Method | Version | Mean | Error | StdDev | Median | +|---------- |-------- |-----------:|---------:|---------:|-----------:| +| TUnit | 1.0.48 | 578.1 ms | 5.79 ms | 5.13 ms | 577.3 ms | +| NUnit | 4.4.0 | 1,220.5 ms | 7.43 ms | 6.20 ms | 1,220.9 ms | +| MSTest | 4.0.1 | 3,005.3 ms | 13.91 ms | 12.33 ms | 3,003.4 ms | +| xUnit3 | 3.2.0 | 3,096.0 ms | 11.11 ms | 10.40 ms | 3,094.6 ms | +| TUnit_AOT | 1.0.48 | 130.6 ms | 0.39 ms | 0.36 ms | 130.7 ms | ### Scenario: Tests with complex parameter combinations creating 25-125 test variations @@ -468,13 +468,13 @@ AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores Runtime=.NET 10.0 ``` -| Method | Version | Mean | Error | StdDev | Median | -|---------- |-------- |------------:|----------:|----------:|------------:| -| TUnit | 1.0.30 | 554.83 ms | 6.074 ms | 5.682 ms | 554.19 ms | -| NUnit | 4.4.0 | 1,573.36 ms | 8.701 ms | 8.139 ms | 1,571.59 ms | -| MSTest | 4.0.1 | 1,526.75 ms | 10.786 ms | 10.089 ms | 1,526.09 ms | -| xUnit3 | 3.2.0 | 1,619.58 ms | 10.993 ms | 10.283 ms | 1,619.62 ms | -| TUnit_AOT | 1.0.30 | 79.80 ms | 0.468 ms | 0.437 ms | 79.72 ms | +| Method | Version | Mean | Error | StdDev | Median | +|---------- |-------- |------------:|---------:|---------:|------------:| +| TUnit | 1.0.48 | 544.97 ms | 5.561 ms | 4.930 ms | 544.99 ms | +| NUnit | 4.4.0 | 1,540.60 ms | 5.644 ms | 4.713 ms | 1,540.91 ms | +| MSTest | 4.0.1 | 1,499.17 ms | 4.590 ms | 3.833 ms | 1,499.42 ms | +| xUnit3 | 3.2.0 | 1,591.72 ms | 6.560 ms | 6.136 ms | 1,592.55 ms | +| TUnit_AOT | 1.0.48 | 79.41 ms | 0.252 ms | 0.236 ms | 79.48 ms | ### Scenario: Large-scale parameterized tests with 100+ test cases testing framework scalability @@ -492,11 +492,11 @@ Runtime=.NET 10.0 ``` | Method | Version | Mean | Error | StdDev | Median | |---------- |-------- |----------:|----------:|----------:|----------:| -| TUnit | 1.0.30 | 494.79 ms | 3.391 ms | 3.172 ms | 494.06 ms | -| NUnit | 4.4.0 | 569.05 ms | 10.739 ms | 10.547 ms | 568.53 ms | -| MSTest | 4.0.1 | 483.48 ms | 8.017 ms | 6.694 ms | 483.72 ms | -| xUnit3 | 3.2.0 | 570.20 ms | 5.614 ms | 5.252 ms | 569.69 ms | -| TUnit_AOT | 1.0.30 | 41.52 ms | 0.956 ms | 2.819 ms | 41.47 ms | +| TUnit | 1.0.48 | 498.22 ms | 7.188 ms | 6.723 ms | 496.45 ms | +| NUnit | 4.4.0 | 584.65 ms | 11.669 ms | 11.461 ms | 582.38 ms | +| MSTest | 4.0.1 | 580.53 ms | 11.233 ms | 15.747 ms | 583.58 ms | +| xUnit3 | 3.2.0 | 587.89 ms | 8.750 ms | 7.757 ms | 586.15 ms | +| TUnit_AOT | 1.0.48 | 46.75 ms | 1.442 ms | 4.253 ms | 47.33 ms | From c06c1c504bfd8bfc9fc5572ace67df1e94ab3f9f Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 10:37:17 +0000 Subject: [PATCH 11/62] chore(deps): update dependency microsoft.net.test.sdk to 18.0.1 (#3763) Co-authored-by: Renovate Bot --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index a2d894b8be..708a143c31 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -34,7 +34,7 @@ - + From 438f1351e2495651a3fbe9313a0931a84d0c3e30 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 11:56:26 +0000 Subject: [PATCH 12/62] chore(deps): update dependency microsoft.templateengine.authoring.cli to v9.0.307 (#3764) Co-authored-by: Renovate Bot --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index db56292769..756f58fa94 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "microsoft.templateengine.authoring.cli": { - "version": "9.0.306", + "version": "9.0.307", "commands": [ "dotnet-template-authoring" ], From a0430c883779373bb2239f56726f99c2c60bfdce Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 12:04:24 +0000 Subject: [PATCH 13/62] chore(deps): update dependency microsoft.templateengine.authoring.templateverifier to 9.0.307 (#3765) Co-authored-by: Renovate Bot --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 708a143c31..0d33c6f937 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -36,7 +36,7 @@ - + From 1111b69ac031480e6e73cf400a8a6643e5808835 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 12:37:28 +0000 Subject: [PATCH 14/62] chore(deps): update dependency microsoft.entityframeworkcore to 9.0.11 (#3768) Co-authored-by: Renovate Bot --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 0d33c6f937..abf0504cd2 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -32,7 +32,7 @@ - + From 49cc1cc5b65866b8b1424ee9458d2c167077b7bb Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 13:20:56 +0000 Subject: [PATCH 15/62] chore(deps): update dependency microsoft.extensions.dependencyinjection to 9.0.11 (#3769) Co-authored-by: Renovate Bot --- Directory.Packages.props | 2 +- TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index abf0504cd2..ef239f3413 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -33,7 +33,7 @@ - + diff --git a/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj b/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj index 4c79c3bfce..15c1ab3f33 100644 --- a/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj +++ b/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj @@ -9,7 +9,7 @@ - + From e08d946502a50d2fe48ade0ac4036847bfda83cf Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 13:43:15 +0000 Subject: [PATCH 16/62] chore(deps): update dependency fsharp.core to 10.0.100 (#3772) Co-authored-by: Renovate Bot --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index ef239f3413..d50a2342d7 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -13,7 +13,7 @@ - + From 518b2b91f732224704bcd79527d0629f2d277cac Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 14:07:03 +0000 Subject: [PATCH 17/62] chore(deps): update dependency system.commandline to 2.0.0 (#3773) Co-authored-by: Renovate Bot --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index d50a2342d7..d518f1b541 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -73,7 +73,7 @@ - + From 976073cef6a61fd6b7eb2fc49ee82974ba108b4a Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 14:34:28 +0000 Subject: [PATCH 18/62] chore(deps): update microsoft.testing to 2.0.2 (#3775) Co-authored-by: Renovate Bot --- Directory.Packages.props | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index d518f1b541..65530e93bc 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -38,11 +38,11 @@ - - - - - + + + + + From 48d4c11273073c0f7d329564b753fd32fbf42170 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 14:35:14 +0000 Subject: [PATCH 19/62] chore(deps): update microsoft.aspnetcore to 9.0.11 (#3774) Co-authored-by: Renovate Bot --- Directory.Packages.props | 6 +++--- .../TUnit.AspNet.FSharp/TestProject/TestProject.fsproj | 2 +- .../content/TUnit.AspNet/TestProject/TestProject.csproj | 2 +- TUnit.Templates/content/TUnit.AspNet/WebApp/WebApp.csproj | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 65530e93bc..41c2f0a13b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -16,9 +16,9 @@ - - - + + + diff --git a/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj b/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj index 32cd4bc7fd..f1ba496a75 100644 --- a/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj +++ b/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj @@ -9,7 +9,7 @@ - + diff --git a/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj b/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj index 4044669304..287051cd44 100644 --- a/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj +++ b/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj @@ -8,7 +8,7 @@ - + diff --git a/TUnit.Templates/content/TUnit.AspNet/WebApp/WebApp.csproj b/TUnit.Templates/content/TUnit.AspNet/WebApp/WebApp.csproj index 18e1256391..d77de3aded 100644 --- a/TUnit.Templates/content/TUnit.AspNet/WebApp/WebApp.csproj +++ b/TUnit.Templates/content/TUnit.AspNet/WebApp/WebApp.csproj @@ -7,7 +7,7 @@ - + From d4b40104857c8040a3e8488b6f037c04491bd343 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 15:34:07 +0000 Subject: [PATCH 20/62] chore(deps): update dependency dotnet-sdk to v9.0.307 (#3777) Co-authored-by: Renovate Bot --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index 095c4095fd..a245810e44 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.306", + "version": "9.0.307", "rollForward": "latestMajor", "allowPrerelease": true }, From 3f584f816769a1b6a8b640244e89432ff31d4466 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 16:07:18 +0000 Subject: [PATCH 21/62] chore(deps): update mstest to 4.0.2 (#3778) Co-authored-by: Renovate Bot --- Directory.Packages.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 41c2f0a13b..d8e5e4c8a8 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -48,9 +48,9 @@ - - - + + + From 14f0ad8e10858e27640a11b3e8650617ddda7c5f Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 16:37:59 +0000 Subject: [PATCH 22/62] chore(deps): update dependency dotnet-sdk to v10 (#3780) Co-authored-by: Renovate Bot --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index a245810e44..67b0c9d008 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.307", + "version": "10.0.100", "rollForward": "latestMajor", "allowPrerelease": true }, From c8295621b4924531f44c3d2f88f24c97eb5aba36 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 16:38:40 +0000 Subject: [PATCH 23/62] chore(deps): update aspire to v13 (#3779) Co-authored-by: Renovate Bot --- .../ExampleNamespace.AppHost.csproj | 6 +++--- .../ExampleNamespace.TestProject.csproj | 2 +- .../ExampleNamespace.WebApp/ExampleNamespace.WebApp.csproj | 2 +- .../content/TUnit.Aspire.Test/ExampleNamespace.csproj | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.AppHost/ExampleNamespace.AppHost.csproj b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.AppHost/ExampleNamespace.AppHost.csproj index 31973813fd..a712d7c89c 100644 --- a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.AppHost/ExampleNamespace.AppHost.csproj +++ b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.AppHost/ExampleNamespace.AppHost.csproj @@ -1,6 +1,6 @@  - + Exe @@ -12,8 +12,8 @@ - - + + diff --git a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.TestProject/ExampleNamespace.TestProject.csproj b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.TestProject/ExampleNamespace.TestProject.csproj index ca45c3bdf2..3075c21f6c 100644 --- a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.TestProject/ExampleNamespace.TestProject.csproj +++ b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.TestProject/ExampleNamespace.TestProject.csproj @@ -10,7 +10,7 @@ - + diff --git a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.WebApp/ExampleNamespace.WebApp.csproj b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.WebApp/ExampleNamespace.WebApp.csproj index cbafa2ced1..55567ede91 100644 --- a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.WebApp/ExampleNamespace.WebApp.csproj +++ b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.WebApp/ExampleNamespace.WebApp.csproj @@ -7,7 +7,7 @@ - + diff --git a/TUnit.Templates/content/TUnit.Aspire.Test/ExampleNamespace.csproj b/TUnit.Templates/content/TUnit.Aspire.Test/ExampleNamespace.csproj index 0fe11b279b..7da16ebe3d 100644 --- a/TUnit.Templates/content/TUnit.Aspire.Test/ExampleNamespace.csproj +++ b/TUnit.Templates/content/TUnit.Aspire.Test/ExampleNamespace.csproj @@ -9,7 +9,7 @@ - + From 35220874153f6d6f8f6dc1bbe6e8c5c1c3646088 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 17:20:40 +0000 Subject: [PATCH 24/62] feat(tests): add All_Satisfy_WithMapper tests and update documentation --- TUnit.Assertions.Tests/SatisfiesTests.cs | 41 ++++++++++++++++++++++++ docs/docs/assertions/collections.md | 13 ++++---- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/TUnit.Assertions.Tests/SatisfiesTests.cs b/TUnit.Assertions.Tests/SatisfiesTests.cs index 671c126638..c108f636cf 100644 --- a/TUnit.Assertions.Tests/SatisfiesTests.cs +++ b/TUnit.Assertions.Tests/SatisfiesTests.cs @@ -232,6 +232,41 @@ await Assert.That(models).All().Satisfy(model => model!.Value, item => item.Cont """); } + // Tests for issue #3766: Demonstrates the correct usage of .All().Satisfy() + // The mapper overload allows checking properties of collection items + [Test] + public async Task All_Satisfy_WithMapper_Good() + { + var users = new[] + { + new User { Name = "Alice", Age = 25 }, + new User { Name = "Bob", Age = 30 } + }; + + await Assert.That(users) + .All() + .Satisfy(u => u.Age, age => age.IsGreaterThan(18)); + } + + [Test] + public async Task All_Satisfy_WithMapper_Throws() + { + var users = new[] + { + new User { Name = "Alice", Age = 25 }, + new User { Name = "Bob", Age = 15 } + }; + + await Assert.That(async () => + await Assert.That(users) + .All() + .Satisfy(u => u.Age, age => age.IsGreaterThan(18)) + ).Throws().WithMessageMatching(""" + *to satisfy* + *index 1* + """); + } + [Test] public async Task Satisfies_With_Member_As_Final_Statement() { @@ -291,6 +326,12 @@ await Assert.That(list).Satisfies( """); } + public class User + { + public string Name { get; init; } = string.Empty; + public int Age { get; init; } + } + public class MyModelWithId { public int Id { get; init; } diff --git a/docs/docs/assertions/collections.md b/docs/docs/assertions/collections.md index 44312bbbe2..d332624600 100644 --- a/docs/docs/assertions/collections.md +++ b/docs/docs/assertions/collections.md @@ -290,11 +290,11 @@ public async Task All_Items_Match() #### With Satisfy -Chain additional assertions on all items: +Chain additional assertions on all items by mapping to a property: ```csharp [Test] -public async Task All_Satisfy() +public async Task All_Satisfy_With_Property() { var users = new[] { @@ -302,15 +302,14 @@ public async Task All_Satisfy() new User { Name = "Bob", Age = 30 } }; + // Use the mapper overload to access item properties await Assert.That(users) .All() - .Satisfy(u => Assert.That(u.Age).IsGreaterThan(18)); + .Satisfy(u => u.Age, age => age.IsGreaterThan(18)); } ``` -#### With Mapper - -Map items before asserting: +You can also map to any expression: ```csharp [Test] @@ -326,7 +325,7 @@ public async Task All_Satisfy_With_Mapper() .All() .Satisfy( u => u.Name, - name => Assert.That(name).IsNotEmpty() + name => name.IsNotEmpty() ); } ``` From 85822f5603769507ece7a51d943386de8626e4fb Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 17:28:08 +0000 Subject: [PATCH 25/62] chore(deps): update dependency microsoft.templateengine.authoring.cli to v10 (#3782) Co-authored-by: Renovate Bot --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 756f58fa94..d86752265b 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "microsoft.templateengine.authoring.cli": { - "version": "9.0.307", + "version": "10.0.100", "commands": [ "dotnet-template-authoring" ], From 79a7095588da50853fc817bf13c4e4dce931362b Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 18:34:33 +0000 Subject: [PATCH 26/62] chore(deps): update dependency microsoft.templateengine.authoring.templateverifier to v10 (#3783) Co-authored-by: Renovate Bot --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index d8e5e4c8a8..80b11dc212 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -36,7 +36,7 @@ - + From 8c8f1722c741ae8f6dfa5244dd6814fda0ade10f Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 19:12:13 +0000 Subject: [PATCH 27/62] feat(tests): update All_Satisfy tests to use new assertions --- docs/docs/assertions/collections.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/docs/assertions/collections.md b/docs/docs/assertions/collections.md index d332624600..0f9badb06b 100644 --- a/docs/docs/assertions/collections.md +++ b/docs/docs/assertions/collections.md @@ -290,7 +290,7 @@ public async Task All_Items_Match() #### With Satisfy -Chain additional assertions on all items by mapping to a property: +The single parameter overload will match T from IEnumerable - Giving you the relevant assertions for that type. ```csharp [Test] @@ -305,11 +305,11 @@ public async Task All_Satisfy_With_Property() // Use the mapper overload to access item properties await Assert.That(users) .All() - .Satisfy(u => u.Age, age => age.IsGreaterThan(18)); + .Satisfy(user => user.IsNotNull()); } ``` -You can also map to any expression: +You can also map to other types by accessing properties an such - And then assert on those specific values: ```csharp [Test] @@ -324,8 +324,8 @@ public async Task All_Satisfy_With_Mapper() await Assert.That(users) .All() .Satisfy( - u => u.Name, - name => name.IsNotEmpty() + u => u.Age, + age => age.IsGreaterThan(18) ); } ``` From fa72406d8cff774dcaccaa1defca98dfe205d479 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 20:02:41 +0000 Subject: [PATCH 28/62] feat(polyfills): add support for embedded Polyfill attributes to prevent type conflicts (#3787) * feat(polyfills): add support for embedded Polyfill attributes to prevent type conflicts * feat(props): add PolyUseEmbeddedAttribute to Roslyn.props for embedded attribute support fix(docs): update documentation to use code formatting for IEnumerable in collections.md * chore(targets): remove PolyUseEmbeddedAttribute from TUnit.Core.targets --- Polyfill.targets | 4 ++++ Roslyn.props | 4 ++++ TUnit.Core/TUnit.Core.props | 2 ++ docs/docs/assertions/collections.md | 2 +- docs/docs/getting-started/installation.md | 5 +++++ 5 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Polyfill.targets b/Polyfill.targets index e4c70c753d..27f0006759 100644 --- a/Polyfill.targets +++ b/Polyfill.targets @@ -1,4 +1,8 @@ + + true + + all diff --git a/Roslyn.props b/Roslyn.props index fc06388c70..24cdff7c9e 100644 --- a/Roslyn.props +++ b/Roslyn.props @@ -1,5 +1,9 @@ + + true + + diff --git a/TUnit.Core/TUnit.Core.props b/TUnit.Core/TUnit.Core.props index b9533b105d..4837fcb702 100644 --- a/TUnit.Core/TUnit.Core.props +++ b/TUnit.Core/TUnit.Core.props @@ -36,6 +36,8 @@ true + + true diff --git a/docs/docs/assertions/collections.md b/docs/docs/assertions/collections.md index 0f9badb06b..52f72d5d27 100644 --- a/docs/docs/assertions/collections.md +++ b/docs/docs/assertions/collections.md @@ -290,7 +290,7 @@ public async Task All_Items_Match() #### With Satisfy -The single parameter overload will match T from IEnumerable - Giving you the relevant assertions for that type. +The single parameter overload will match T from `IEnumerable` - Giving you the relevant assertions for that type. ```csharp [Test] diff --git a/docs/docs/getting-started/installation.md b/docs/docs/getting-started/installation.md index 38d5ca5b1d..31ccb0421d 100644 --- a/docs/docs/getting-started/installation.md +++ b/docs/docs/getting-started/installation.md @@ -123,3 +123,8 @@ TUnit is fully compatible with NuGet Central Package Management. When CPM is ena If you prefer to manage the Polyfill version yourself, you can: - Add `` to your `Directory.Packages.props`, OR - Disable automatic injection with `false` and add it manually + +### Embedded Polyfill Attributes +TUnit automatically sets `true` to ensure that Polyfill types are embedded in each project. This prevents type conflicts when using `InternalsVisibleTo` or when multiple projects in your solution reference Polyfill. Each project gets its own isolated copy of the polyfill types, following the [recommended Polyfill consuming pattern](https://github.com/SimonCropp/Polyfill/blob/main/consuming.md#recommended-consuming-pattern). + +You can override this behavior by setting `false` in your project file if needed. From 60e22b45d48ba0c35b09d33095869355f5c04eb4 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 20:06:24 +0000 Subject: [PATCH 29/62] chore(deps): update microsoft.extensions to v10 (#3785) Co-authored-by: Renovate Bot --- Directory.Packages.props | 2 +- .../ExampleNamespace.ServiceDefaults.csproj | 4 ++-- TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 80b11dc212..d5240f97bb 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -33,7 +33,7 @@ - + diff --git a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.ServiceDefaults/ExampleNamespace.ServiceDefaults.csproj b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.ServiceDefaults/ExampleNamespace.ServiceDefaults.csproj index 233173d757..8fd483346a 100644 --- a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.ServiceDefaults/ExampleNamespace.ServiceDefaults.csproj +++ b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.ServiceDefaults/ExampleNamespace.ServiceDefaults.csproj @@ -10,8 +10,8 @@ - - + + diff --git a/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj b/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj index 15c1ab3f33..87db30ee23 100644 --- a/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj +++ b/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj @@ -9,7 +9,7 @@ - + From a79f104795ed8f25935b0aecbef1714d61bdffce Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 22:13:49 +0000 Subject: [PATCH 30/62] feat(tests): fix code fixer for xUnit migration code fixer .All().Satisfy() (#3786) * feat(tests): fix code fixer for xUnit migration code fixer .All().Satisfy() * feat(tests): update xUnit assertion conversion for All() and improve lambda handling * Add test output for TUnit.Assertions.Analyzers.CodeFixers.Tests with multiple failures --- .../Verifiers/CSharpCodeFixVerifier`2.cs | 24 +- .../XUnitAssertionCodeFixProviderTests.cs | 69 +++++ .../XUnitAssertionCodeFixProvider.cs | 264 +++++++++++++++++- 3 files changed, 347 insertions(+), 10 deletions(-) diff --git a/TUnit.Assertions.Analyzers.CodeFixers.Tests/Verifiers/CSharpCodeFixVerifier`2.cs b/TUnit.Assertions.Analyzers.CodeFixers.Tests/Verifiers/CSharpCodeFixVerifier`2.cs index 6ad9ff152f..2c8ee11dc2 100644 --- a/TUnit.Assertions.Analyzers.CodeFixers.Tests/Verifiers/CSharpCodeFixVerifier`2.cs +++ b/TUnit.Assertions.Analyzers.CodeFixers.Tests/Verifiers/CSharpCodeFixVerifier`2.cs @@ -4,7 +4,9 @@ using Microsoft.CodeAnalysis.CSharp.Testing; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Testing; +using TUnit.Assertions; using TUnit.Assertions.Analyzers.CodeFixers.Tests.Extensions; +using TUnit.Core; namespace TUnit.Assertions.Analyzers.CodeFixers.Tests.Verifiers; @@ -41,12 +43,19 @@ public static async Task VerifyAnalyzerAsync( params DiagnosticResult[] expected ) { + var referenceAssemblies = GetReferenceAssemblies(); + + // Only add xunit package for XUnitAssertionAnalyzer + if (typeof(TAnalyzer).Name == "XUnitAssertionAnalyzer") + { + referenceAssemblies = referenceAssemblies.AddPackages([new PackageIdentity("xunit.v3.assert", "3.2.0")]); + } + var test = new Test { TestCode = source.NormalizeLineEndings(), CodeActionValidationMode = CodeActionValidationMode.SemanticStructure, - ReferenceAssemblies = GetReferenceAssemblies() - .AddPackages([new PackageIdentity("xunit.v3.assert", "2.0.0")]), + ReferenceAssemblies = referenceAssemblies, TestState = { AdditionalReferences = @@ -76,12 +85,19 @@ public static async Task VerifyCodeFixAsync( [StringSyntax("c#-test")] string fixedSource ) { + var referenceAssemblies = GetReferenceAssemblies(); + + // Only add xunit package for XUnitAssertionAnalyzer + if (typeof(TAnalyzer).Name == "XUnitAssertionAnalyzer") + { + referenceAssemblies = referenceAssemblies.AddPackages([new PackageIdentity("xunit.v3.assert", "3.2.0")]); + } + var test = new Test { TestCode = source.NormalizeLineEndings(), FixedCode = fixedSource.NormalizeLineEndings(), - ReferenceAssemblies = GetReferenceAssemblies() - .AddPackages([new PackageIdentity("xunit.v3.assert", "2.0.0")]), + ReferenceAssemblies = referenceAssemblies, TestState = { AdditionalReferences = diff --git a/TUnit.Assertions.Analyzers.CodeFixers.Tests/XUnitAssertionCodeFixProviderTests.cs b/TUnit.Assertions.Analyzers.CodeFixers.Tests/XUnitAssertionCodeFixProviderTests.cs index 4359388627..4914f8aae6 100644 --- a/TUnit.Assertions.Analyzers.CodeFixers.Tests/XUnitAssertionCodeFixProviderTests.cs +++ b/TUnit.Assertions.Analyzers.CodeFixers.Tests/XUnitAssertionCodeFixProviderTests.cs @@ -150,4 +150,73 @@ public void MyTest() """ ); } + + [Test] + public async Task Xunit_All_Converts_To_AssertMultiple_WithForeach() + { + await Verifier + .VerifyCodeFixAsync( + """ + using System.Threading.Tasks; + + public class MyClass + { + public void MyTest() + { + var users = new[] + { + new User { Name = "Alice", Age = 25 }, + new User { Name = "Bob", Age = 30 } + }; + + {|#0:Xunit.Assert.All(users, user => + { + {|#1:Xunit.Assert.NotNull(user.Name)|}; + {|#2:Xunit.Assert.True(user.Age > 18)|}; + })|}; + } + } + + public class User + { + public string Name { get; init; } + public int Age { get; init; } + } + """, + [ + Verifier.Diagnostic(Rules.XUnitAssertion).WithLocation(0), + Verifier.Diagnostic(Rules.XUnitAssertion).WithLocation(1), + Verifier.Diagnostic(Rules.XUnitAssertion).WithLocation(2) + ], + """ + using System.Threading.Tasks; + + public class MyClass + { + public async Task MyTest() + { + var users = new[] + { + new User { Name = "Alice", Age = 25 }, + new User { Name = "Bob", Age = 30 } + }; + using (Assert.Multiple()) + { + foreach (var user in users) + { + await Assert.That(user.Name).IsNotNull(); + await Assert.That(user.Age > 18).IsTrue(); + } + } + } + } + + public class User + { + public string Name { get; init; } + public int Age { get; init; } + } + """ + ); + } } diff --git a/TUnit.Assertions.Analyzers.CodeFixers/XUnitAssertionCodeFixProvider.cs b/TUnit.Assertions.Analyzers.CodeFixers/XUnitAssertionCodeFixProvider.cs index c4f6adaea6..8afa092b20 100644 --- a/TUnit.Assertions.Analyzers.CodeFixers/XUnitAssertionCodeFixProvider.cs +++ b/TUnit.Assertions.Analyzers.CodeFixers/XUnitAssertionCodeFixProvider.cs @@ -5,6 +5,7 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Formatting; using TUnit.Assertions.Analyzers.Extensions; namespace TUnit.Assertions.Analyzers.CodeFixers; @@ -64,7 +65,57 @@ private static async Task ConvertAssertionAsync(CodeFixContext context var genericArgs = GetGenericArguments(memberAccessExpressionSyntax.Name); - var newExpression = await GetNewExpression(context, memberAccessExpressionSyntax, methodName, actual, expected, genericArgs, expressionSyntax.ArgumentList.Arguments); + // Special handling for Assert.All - returns a statement instead of an expression + if (methodName == "All") + { + var newStatement = ConvertAllToStatement(expected, actual); + if (newStatement != null) + { + // Find the parent expression statement and containing method + var parentStatement = expressionSyntax.FirstAncestorOrSelf(); + var methodDeclaration = expressionSyntax.FirstAncestorOrSelf(); + + if (parentStatement != null) + { + // Format the statement using Roslyn's formatter with annotations + newStatement = newStatement.WithAdditionalAnnotations(Formatter.Annotation); + + // Replace the statement + var newRoot = compilationUnit.ReplaceNode(parentStatement, newStatement); + + // Find the method declaration in the new tree if it needs to be modified + if (methodDeclaration != null && !methodDeclaration.Modifiers.Any(SyntaxKind.AsyncKeyword)) + { + // Find the method in the new tree + var newMethodDeclaration = newRoot.DescendantNodes() + .OfType() + .FirstOrDefault(m => m.Identifier.ValueText == methodDeclaration.Identifier.ValueText); + + if (newMethodDeclaration != null) + { + var asyncModifier = SyntaxFactory.Token(SyntaxKind.AsyncKeyword).WithTrailingTrivia(SyntaxFactory.Space); + var newModifiers = newMethodDeclaration.Modifiers.Add(asyncModifier); + var updatedMethodDeclaration = newMethodDeclaration.WithModifiers(newModifiers); + + // Update return type to Task if it's void + if (newMethodDeclaration.ReturnType.ToString() == "void") + { + updatedMethodDeclaration = updatedMethodDeclaration.WithReturnType( + SyntaxFactory.IdentifierName("Task").WithLeadingTrivia(newMethodDeclaration.ReturnType.GetLeadingTrivia()).WithTrailingTrivia(SyntaxFactory.Space)); + } + + newRoot = newRoot.ReplaceNode(newMethodDeclaration, updatedMethodDeclaration); + } + } + + // Format the entire document + var formattedRoot = Formatter.Format(newRoot, Formatter.Annotation, document.Project.Solution.Workspace); + return document.WithSyntaxRoot(formattedRoot); + } + } + } + + var newExpression = await GetNewExpression(context, expressionSyntax, memberAccessExpressionSyntax, methodName, actual, expected, genericArgs, expressionSyntax.ArgumentList.Arguments); if (newExpression != null) { @@ -75,12 +126,16 @@ private static async Task ConvertAssertionAsync(CodeFixContext context } private static async Task GetNewExpression(CodeFixContext context, + InvocationExpressionSyntax expressionSyntax, MemberAccessExpressionSyntax memberAccessExpressionSyntax, string method, ArgumentSyntax? actual, ArgumentSyntax? expected, string genericArgs, SeparatedSyntaxList argumentListArguments) { var isGeneric = !string.IsNullOrEmpty(genericArgs); + // Check if we're inside a .Satisfy() or .Satisfies() lambda + var (isInSatisfy, parameterName) = IsInsideSatisfyLambda(expressionSyntax); + return method switch { "Equal" => await IsEqualTo(context, argumentListArguments, actual, expected), @@ -95,13 +150,21 @@ private static async Task ConvertAssertionAsync(CodeFixContext context "EndsWith" => SyntaxFactory.ParseExpression($"Assert.That({actual}).EndsWith({expected})"), - "NotNull" => SyntaxFactory.ParseExpression($"Assert.That({actual}).IsNotNull()"), + "NotNull" => isInSatisfy && parameterName != null + ? SyntaxFactory.ParseExpression($"{actual}.IsNotNull()") + : SyntaxFactory.ParseExpression($"Assert.That({actual}).IsNotNull()"), - "Null" => SyntaxFactory.ParseExpression($"Assert.That({actual}).IsNull()"), + "Null" => isInSatisfy && parameterName != null + ? SyntaxFactory.ParseExpression($"{actual}.IsNull()") + : SyntaxFactory.ParseExpression($"Assert.That({actual}).IsNull()"), - "True" => SyntaxFactory.ParseExpression($"Assert.That({actual}).IsTrue()"), + "True" => isInSatisfy && parameterName != null + ? SyntaxFactory.ParseExpression($"{actual}.IsTrue()") + : SyntaxFactory.ParseExpression($"Assert.That({actual}).IsTrue()"), - "False" => SyntaxFactory.ParseExpression($"Assert.That({actual}).IsFalse()"), + "False" => isInSatisfy && parameterName != null + ? SyntaxFactory.ParseExpression($"{actual}.IsFalse()") + : SyntaxFactory.ParseExpression($"Assert.That({actual}).IsFalse()"), "Same" => SyntaxFactory.ParseExpression($"Assert.That({actual}).IsSameReferenceAs({expected})"), @@ -123,7 +186,7 @@ private static async Task ConvertAssertionAsync(CodeFixContext context ? SyntaxFactory.ParseExpression($"Assert.That({actual}).IsNotAssignableFrom<{genericArgs}>()") : SyntaxFactory.ParseExpression($"Assert.That({actual}).IsNotAssignableFrom({expected})"), - "All" => SyntaxFactory.ParseExpression($"Assert.That({actual}).All().Satisfy({expected})"), + // "All" is handled separately in ConvertAssertionAsync "Single" => SyntaxFactory.ParseExpression($"Assert.That({actual}).HasSingleItem()"), @@ -278,4 +341,193 @@ public static string GetGenericArguments(ExpressionSyntax expressionSyntax) return string.Empty; } + + private static (bool isInSatisfy, string? parameterName) IsInsideSatisfyLambda(SyntaxNode node) + { + var current = node.Parent; + + while (current != null) + { + // Check if we're in a lambda expression + if (current is SimpleLambdaExpressionSyntax simpleLambda) + { + // Check if the lambda is an argument to a .Satisfy() or .Satisfies() call + if (current.Parent is ArgumentSyntax argument && + argument.Parent is ArgumentListSyntax argumentList && + argumentList.Parent is InvocationExpressionSyntax invocation && + invocation.Expression is MemberAccessExpressionSyntax memberAccess) + { + var methodName = memberAccess.Name.Identifier.ValueText; + if (methodName is "Satisfy" or "Satisfies") + { + return (true, simpleLambda.Parameter.Identifier.ValueText); + } + } + } + else if (current is ParenthesizedLambdaExpressionSyntax parenLambda) + { + // Check if the lambda is an argument to a .Satisfy() or .Satisfies() call + if (current.Parent is ArgumentSyntax argument && + argument.Parent is ArgumentListSyntax argumentList && + argumentList.Parent is InvocationExpressionSyntax invocation && + invocation.Expression is MemberAccessExpressionSyntax memberAccess) + { + var methodName = memberAccess.Name.Identifier.ValueText; + if (methodName is "Satisfy" or "Satisfies") + { + // For parenthesized lambda, get the first parameter + var firstParam = parenLambda.ParameterList.Parameters.FirstOrDefault(); + return (true, firstParam?.Identifier.ValueText); + } + } + } + + current = current.Parent; + } + + return (false, null); + } + + private static StatementSyntax? ConvertAllToStatement(ArgumentSyntax? collection, ArgumentSyntax? lambda) + { + if (lambda?.Expression is not LambdaExpressionSyntax lambdaExpression) + { + return null; + } + + // Extract lambda parameter name + string? paramName = lambdaExpression switch + { + SimpleLambdaExpressionSyntax simple => simple.Parameter.Identifier.ValueText, + ParenthesizedLambdaExpressionSyntax paren => paren.ParameterList.Parameters.FirstOrDefault()?.Identifier.ValueText, + _ => null + }; + + if (paramName == null) + { + return null; + } + + // Get the lambda body + var lambdaBody = lambdaExpression switch + { + SimpleLambdaExpressionSyntax simple => simple.Body, + ParenthesizedLambdaExpressionSyntax paren => paren.Body, + _ => null + }; + + if (lambdaBody == null) + { + return null; + } + + // Convert xUnit assertions in the lambda body to TUnit assertions + var convertedStatements = ConvertLambdaBodyToTUnitAssertions(lambdaBody); + + if (convertedStatements == null || convertedStatements.Count == 0) + { + return null; + } + + // Build the foreach statement with converted assertions + var foreachBlock = SyntaxFactory.Block(convertedStatements); + var foreachStatement = SyntaxFactory.ForEachStatement( + SyntaxFactory.IdentifierName("var"), + SyntaxFactory.Identifier(paramName), + collection!.Expression, + foreachBlock + ); + + // Build the using statement with the foreach inside + var usingBlock = SyntaxFactory.Block(foreachStatement); + var usingStatement = SyntaxFactory.UsingStatement( + declaration: null, + expression: SyntaxFactory.InvocationExpression( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.IdentifierName("Assert"), + SyntaxFactory.IdentifierName("Multiple") + ), + SyntaxFactory.ArgumentList() + ), + statement: usingBlock + ).NormalizeWhitespace(); + + return usingStatement; + } + + private static List? ConvertLambdaBodyToTUnitAssertions(SyntaxNode lambdaBody) + { + var statements = new List(); + + // Extract statements from the lambda body + var bodyStatements = lambdaBody switch + { + BlockSyntax block => block.Statements, + ExpressionSyntax expr => SyntaxFactory.SingletonList( + SyntaxFactory.ExpressionStatement(expr) + ), + _ => default + }; + + if (bodyStatements == default) + { + return null; + } + + foreach (var statement in bodyStatements) + { + // Find xUnit assertion invocations in this statement + var invocations = statement.DescendantNodes().OfType() + .Where(inv => inv.Expression is MemberAccessExpressionSyntax memberAccess && + memberAccess.Expression.ToString().Contains("Xunit.Assert")) + .ToList(); + + if (invocations.Count == 0) + { + // Not an assertion statement - keep it as is + statements.Add(statement); + continue; + } + + // Convert each xUnit assertion + foreach (var invocation in invocations) + { + if (invocation.Expression is not MemberAccessExpressionSyntax memberAccess) + { + continue; + } + + var methodName = memberAccess.Name.Identifier.ValueText; + var args = invocation.ArgumentList.Arguments; + + // Convert to TUnit assertion + ExpressionSyntax? tunitAssertion = methodName switch + { + "NotNull" when args.Count >= 1 => + SyntaxFactory.ParseExpression($"Assert.That({args[0]}).IsNotNull()"), + "Null" when args.Count >= 1 => + SyntaxFactory.ParseExpression($"Assert.That({args[0]}).IsNull()"), + "True" when args.Count >= 1 => + SyntaxFactory.ParseExpression($"Assert.That({args[0]}).IsTrue()"), + "False" when args.Count >= 1 => + SyntaxFactory.ParseExpression($"Assert.That({args[0]}).IsFalse()"), + "Equal" when args.Count >= 2 => + SyntaxFactory.ParseExpression($"Assert.That({args[1]}).IsEqualTo({args[0]})"), + "NotEqual" when args.Count >= 2 => + SyntaxFactory.ParseExpression($"Assert.That({args[1]}).IsNotEqualTo({args[0]})"), + _ => null + }; + + if (tunitAssertion != null) + { + // Make it an await expression statement + var awaitExpr = SyntaxFactory.AwaitExpression(tunitAssertion); + statements.Add(SyntaxFactory.ExpressionStatement(awaitExpr)); + } + } + } + + return statements.Count > 0 ? statements : null; + } } From 18ae01ef5953166fb7b709778952dc2ccc88b048 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 22:34:20 +0000 Subject: [PATCH 31/62] chore(deps): update dependency nuget.protocol to v7 (#3788) Co-authored-by: Renovate Bot --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index d5240f97bb..be8ceaf8ac 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -52,7 +52,7 @@ - + From 312ab9872772896ad8f5132594a24cbbe638a7c0 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 22:40:21 +0000 Subject: [PATCH 32/62] feat(benchmarks): add summary generation for runtime and build benchmarks in PR body --- .github/scripts/process-benchmarks.js | 115 +++++++++++++++++-------- .github/workflows/speed-comparison.yml | 30 +++++++ 2 files changed, 108 insertions(+), 37 deletions(-) diff --git a/.github/scripts/process-benchmarks.js b/.github/scripts/process-benchmarks.js index 1a58bb5282..f46ff58c51 100644 --- a/.github/scripts/process-benchmarks.js +++ b/.github/scripts/process-benchmarks.js @@ -212,50 +212,14 @@ These benchmarks were automatically generated on **${timestamp}** from the lates **Environment:** ${environmentInfo.os || 'Ubuntu Latest'} • ${environmentInfo.sdk || '.NET 10'} ::: -## šŸŽÆ Executive Summary - -TUnit demonstrates significant performance advantages across all testing scenarios: - -
- -### Average Performance vs Other Frameworks - -- **${avgSpeedups.vsXUnit}x faster** than xUnit v3 -- **${avgSpeedups.vsNUnit}x faster** than NUnit -- **${avgSpeedups.vsMSTest}x faster** than MSTest - -
- ---- - ## šŸš€ Runtime Performance `; // Add runtime results Object.entries(categories.runtime).forEach(([testClass, data]) => { - const comparison = comparisons[testClass]; - mainPage += `\n### ${testClass}\n\n`; - if (comparison && comparison.aotSpeedup) { - mainPage += `:::tip Native AOT Performance\n`; - mainPage += `TUnit with Native AOT compilation is **${comparison.aotSpeedup}x faster** than regular JIT!\n`; - mainPage += `:::\n\n`; - } - - // Add speedup badges - if (comparison) { - const badges = []; - if (comparison.vsXUnit) badges.push(`**${comparison.vsXUnit}x faster** than xUnit`); - if (comparison.vsNUnit) badges.push(`**${comparison.vsNUnit}x faster** than NUnit`); - if (comparison.vsMSTest) badges.push(`**${comparison.vsMSTest}x faster** than MSTest`); - - if (badges.length > 0) { - mainPage += `**Performance:** ${badges.join(' • ')}\n\n`; - } - } - // Add table mainPage += `| Framework | Version | Mean | Median | StdDev |\n`; mainPage += `|-----------|---------|------|--------|--------|\n`; @@ -267,6 +231,34 @@ Object.entries(categories.runtime).forEach(([testClass, data]) => { }); mainPage += '\n'; + + // Add Mermaid bar chart + mainPage += `
\nšŸ“Š Visual Comparison\n\n`; + mainPage += '```mermaid\n%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#4CAF50", "primaryTextColor": "#fff", "primaryBorderColor": "#2E7D32", "lineColor": "#2E7D32", "secondaryColor": "#FFC107", "tertiaryColor": "#2196F3"}}}%%\nxychart-beta\n'; + mainPage += ` title "${testClass} - Mean Execution Time (Lower is Better)"\n`; + mainPage += ' x-axis ['; + + // Add framework names for x-axis + const chartData = data.map(row => { + const name = row.Method.includes('TUnit_AOT') ? 'TUnit (AOT)' : row.Method; + const meanValue = parseMeanValue(row.Mean); + return { name, value: meanValue }; + }); + + mainPage += chartData.map(d => `"${d.name}"`).join(', '); + mainPage += ']\n'; + mainPage += ' y-axis "Time (ms)" 0 --> '; + + // Set y-axis max to 120% of the highest value for better visualization + const maxValue = Math.max(...chartData.map(d => d.value)); + mainPage += Math.ceil(maxValue * 1.2); + mainPage += '\n'; + + mainPage += ' bar ['; + mainPage += chartData.map(d => d.value.toFixed(2)).join(', '); + mainPage += ']\n'; + mainPage += '```\n\n'; + mainPage += `
\n\n`; }); // Add build time results @@ -285,6 +277,34 @@ if (Object.keys(categories.build).length > 0) { }); mainPage += '\n'; + + // Add Mermaid bar chart for build performance + mainPage += `
\nšŸ“Š Visual Comparison\n\n`; + mainPage += '```mermaid\n%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#4CAF50", "primaryTextColor": "#fff", "primaryBorderColor": "#2E7D32", "lineColor": "#2E7D32", "secondaryColor": "#FFC107", "tertiaryColor": "#2196F3"}}}%%\nxychart-beta\n'; + mainPage += ` title "Build Time Comparison - Mean Compilation Time (Lower is Better)"\n`; + mainPage += ' x-axis ['; + + // Add framework names for x-axis + const chartData = data.map(row => { + const name = row.Method; + const meanValue = parseMeanValue(row.Mean); + return { name, value: meanValue }; + }); + + mainPage += chartData.map(d => `"${d.name}"`).join(', '); + mainPage += ']\n'; + mainPage += ' y-axis "Time (ms)" 0 --> '; + + // Set y-axis max to 120% of the highest value for better visualization + const maxValue = Math.max(...chartData.map(d => d.value)); + mainPage += Math.ceil(maxValue * 1.2); + mainPage += '\n'; + + mainPage += ' bar ['; + mainPage += chartData.map(d => d.value.toFixed(2)).join(', '); + mainPage += ']\n'; + mainPage += '```\n\n'; + mainPage += `
\n\n`; }); } @@ -400,9 +420,30 @@ fs.writeFileSync( ); console.log(` āœ“ Updated ${historicalFile} (${historical.length} data points)`); +// Generate benchmark summary for PR body +const benchmarkSummary = { + runtime: Object.keys(categories.runtime), + build: Object.keys(categories.build), + timestamp: timestamp, + environment: `${environmentInfo.os || 'Ubuntu Latest'} • ${environmentInfo.sdk || '.NET 10'}` +}; + +fs.writeFileSync( + path.join(STATIC_DIR, 'summary.json'), + JSON.stringify(benchmarkSummary, null, 2) +); +console.log(` āœ“ Created ${STATIC_DIR}/summary.json`); + console.log('\nāœ… Benchmark processing complete!\n'); console.log(`Summary:`); console.log(` - Runtime categories: ${stats.runtimeCategories}`); console.log(` - Build categories: ${stats.buildCategories}`); console.log(` - Total benchmarks: ${stats.totalBenchmarks}`); -console.log(` - Output files: 3 (markdown + 2 JSON files)`); +console.log(` - Output files: 4 (markdown + 3 JSON files)`); +console.log(`\nšŸ“Š Benchmarks produced:`); +console.log(`\nRuntime Benchmarks:`); +Object.keys(categories.runtime).forEach(cat => console.log(` - ${cat}`)); +if (Object.keys(categories.build).length > 0) { + console.log(`\nBuild Benchmarks:`); + Object.keys(categories.build).forEach(cat => console.log(` - ${cat}`)); +} diff --git a/.github/workflows/speed-comparison.yml b/.github/workflows/speed-comparison.yml index 316ddff2d3..d28d08f107 100644 --- a/.github/workflows/speed-comparison.yml +++ b/.github/workflows/speed-comparison.yml @@ -160,6 +160,27 @@ jobs: run: | node .github/scripts/process-benchmarks.js + - name: Generate Benchmark List + id: benchmark_list + run: | + # Read the summary.json file and format it for the PR body + SUMMARY=$(cat docs/static/benchmarks/summary.json) + RUNTIME_BENCHMARKS=$(echo "$SUMMARY" | grep -A 100 '"runtime"' | grep -o '"[^"]*Tests"' | sed 's/"//g' | sed 's/^/- /' | tr '\n' '|') + BUILD_BENCHMARKS=$(echo "$SUMMARY" | grep -A 100 '"build"' | grep -o '"[^"]*"' | sed 's/"//g' | grep -v 'runtime\|build\|timestamp\|environment' | sed 's/^/- /' | tr '\n' '|') + + # Replace | with newlines for proper formatting + RUNTIME_LIST=$(echo "$RUNTIME_BENCHMARKS" | sed 's/|/\n/g') + BUILD_LIST=$(echo "$BUILD_BENCHMARKS" | sed 's/|/\n/g') + + # Set output with proper escaping + echo "runtime_benchmarks<> $GITHUB_OUTPUT + echo "$RUNTIME_LIST" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + echo "build_benchmarks<> $GITHUB_OUTPUT + echo "$BUILD_LIST" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + - name: Check for Changes id: check_changes run: | @@ -187,10 +208,19 @@ jobs: This PR updates the benchmark documentation with the latest results from the Speed Comparison workflow. + ### Benchmarks Produced + + #### Runtime Benchmarks + ${{ steps.benchmark_list.outputs.runtime_benchmarks }} + + #### Build Benchmarks + ${{ steps.benchmark_list.outputs.build_benchmarks }} + ### Changes - Updated benchmark data in `docs/static/benchmarks/latest.json` - Updated historical trends in `docs/static/benchmarks/historical.json` - Regenerated benchmark documentation in `docs/docs/benchmarks/index.md` + - Updated benchmark summary in `docs/static/benchmarks/summary.json` ### Workflow Run - **Run ID**: ${{ github.run_id }} From 44ac0b0062035994d15bd9a420999a2feaa69a43 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 22:42:49 +0000 Subject: [PATCH 33/62] fix: update GitHub token to use ADMIN_TOKEN for benchmark workflows --- .github/workflows/speed-comparison.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/speed-comparison.yml b/.github/workflows/speed-comparison.yml index d28d08f107..67e5136da5 100644 --- a/.github/workflows/speed-comparison.yml +++ b/.github/workflows/speed-comparison.yml @@ -135,7 +135,7 @@ jobs: - uses: actions/checkout@v5 with: fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ secrets.ADMIN_TOKEN }} - name: Download All Runtime Benchmark Artifacts uses: actions/download-artifact@v6 @@ -198,7 +198,7 @@ jobs: id: create_pr uses: peter-evans/create-pull-request@v7 with: - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ secrets.ADMIN_TOKEN }} commit-message: 'chore: update benchmark results' branch: automated-benchmarks-update delete-branch: true @@ -236,10 +236,12 @@ jobs: documentation draft: false - - name: Enable Auto-Merge + - name: Merge PR Immediately if: steps.check_changes.outputs.has_changes == 'true' && steps.create_pr.outputs.pull-request-number != '' env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.ADMIN_TOKEN }} run: | - gh pr merge ${{ steps.create_pr.outputs.pull-request-number }} --auto --squash --delete-branch + # Wait a moment for PR to be fully created + sleep 5 + gh pr merge ${{ steps.create_pr.outputs.pull-request-number }} --squash --delete-branch --admin From 3f9790f32aa313ce5ac4e615f7b6b5d6e9513e97 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 22:52:12 +0000 Subject: [PATCH 34/62] chore(deps): update tunit to 1.0.78 (#3789) Co-authored-by: Renovate Bot --- Directory.Packages.props | 6 +++--- .../TUnit.AspNet.FSharp/TestProject/TestProject.fsproj | 4 ++-- .../content/TUnit.AspNet/TestProject/TestProject.csproj | 2 +- .../ExampleNamespace.TestProject.csproj | 2 +- .../content/TUnit.Aspire.Test/ExampleNamespace.csproj | 2 +- TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj | 4 ++-- TUnit.Templates/content/TUnit.Playwright/TestProject.csproj | 2 +- TUnit.Templates/content/TUnit.VB/TestProject.vbproj | 2 +- TUnit.Templates/content/TUnit/TestProject.csproj | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index be8ceaf8ac..3f2141dd84 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -83,9 +83,9 @@ - - - + + + diff --git a/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj b/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj index f1ba496a75..0bb4259733 100644 --- a/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj +++ b/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj @@ -10,8 +10,8 @@ - - + + diff --git a/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj b/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj index 287051cd44..45f7c0723a 100644 --- a/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj +++ b/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj @@ -9,7 +9,7 @@ - + diff --git a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.TestProject/ExampleNamespace.TestProject.csproj b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.TestProject/ExampleNamespace.TestProject.csproj index 3075c21f6c..29ac371ea2 100644 --- a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.TestProject/ExampleNamespace.TestProject.csproj +++ b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.TestProject/ExampleNamespace.TestProject.csproj @@ -11,7 +11,7 @@ - + diff --git a/TUnit.Templates/content/TUnit.Aspire.Test/ExampleNamespace.csproj b/TUnit.Templates/content/TUnit.Aspire.Test/ExampleNamespace.csproj index 7da16ebe3d..6acb29b07d 100644 --- a/TUnit.Templates/content/TUnit.Aspire.Test/ExampleNamespace.csproj +++ b/TUnit.Templates/content/TUnit.Aspire.Test/ExampleNamespace.csproj @@ -10,7 +10,7 @@ - + diff --git a/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj b/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj index 87db30ee23..d1f6d6dfed 100644 --- a/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj +++ b/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj @@ -10,8 +10,8 @@ - - + + diff --git a/TUnit.Templates/content/TUnit.Playwright/TestProject.csproj b/TUnit.Templates/content/TUnit.Playwright/TestProject.csproj index 25055774d9..5f8bef5d2a 100644 --- a/TUnit.Templates/content/TUnit.Playwright/TestProject.csproj +++ b/TUnit.Templates/content/TUnit.Playwright/TestProject.csproj @@ -8,7 +8,7 @@ - +
diff --git a/TUnit.Templates/content/TUnit.VB/TestProject.vbproj b/TUnit.Templates/content/TUnit.VB/TestProject.vbproj index ff5e5de922..142edd7810 100644 --- a/TUnit.Templates/content/TUnit.VB/TestProject.vbproj +++ b/TUnit.Templates/content/TUnit.VB/TestProject.vbproj @@ -8,6 +8,6 @@ - +
diff --git a/TUnit.Templates/content/TUnit/TestProject.csproj b/TUnit.Templates/content/TUnit/TestProject.csproj index c375f81c6f..948ae0ad1f 100644 --- a/TUnit.Templates/content/TUnit/TestProject.csproj +++ b/TUnit.Templates/content/TUnit/TestProject.csproj @@ -8,7 +8,7 @@ - +
\ No newline at end of file From 81e211a631f05d7b37d8077034b78b006698b798 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 22:58:29 +0000 Subject: [PATCH 35/62] chore: update benchmark results (#3757) Co-authored-by: thomhurst <30480171+thomhurst@users.noreply.github.com> --- docs/docs/benchmarks/index.md | 82 ++++++++++--------- docs/static/benchmarks/historical.json | 10 +++ docs/static/benchmarks/latest.json | 108 ++++++++++++------------- docs/static/benchmarks/summary.json | 10 +++ 4 files changed, 118 insertions(+), 92 deletions(-) create mode 100644 docs/static/benchmarks/summary.json diff --git a/docs/docs/benchmarks/index.md b/docs/docs/benchmarks/index.md index 2c5b59a725..b0bb83b7cc 100644 --- a/docs/docs/benchmarks/index.md +++ b/docs/docs/benchmarks/index.md @@ -7,45 +7,37 @@ sidebar_position: 1 # Performance Benchmarks :::info Last Updated -These benchmarks were automatically generated on **2025-11-10** from the latest CI run. +These benchmarks were automatically generated on **2025-11-11** from the latest CI run. -**Environment:** Ubuntu Latest • .NET SDK 10.0.100-rc.2.25502.107 +**Environment:** Ubuntu Latest • .NET SDK 10.0.100 ::: -## šŸŽÆ Executive Summary - -TUnit demonstrates significant performance advantages across all testing scenarios: - -
- -### Average Performance vs Other Frameworks - -- **1.2x faster** than xUnit v3 -- **1.2x faster** than NUnit -- **1.0x faster** than MSTest - -
- ---- - ## šŸš€ Runtime Performance ### results -:::tip Native AOT Performance -TUnit with Native AOT compilation is **11.22x faster** than regular JIT! -::: - -**Performance:** **1.19x faster** than xUnit • **1.17x faster** than NUnit • **1.00x faster** than MSTest - | Framework | Version | Mean | Median | StdDev | |-----------|---------|------|--------|--------| -| šŸ† **TUnit** | 1.0.39 | 495.43 ms | 495.43 ms | 2.042 ms | -| NUnit | 4.4.0 | 578.77 ms | 580.83 ms | 7.718 ms | -| MSTest | 4.0.1 | 495.62 ms | 496.03 ms | 6.128 ms | -| xUnit3 | 3.2.0 | 591.61 ms | 591.28 ms | 9.984 ms | -| šŸ† **TUnit (AOT)** | 1.0.39 | 44.15 ms | 44.00 ms | 3.244 ms | +| šŸ† **TUnit** | 1.0.78 | 580.4 ms | 578.3 ms | 7.38 ms | +| NUnit | 4.4.0 | 1,211.6 ms | 1,209.8 ms | 9.40 ms | +| MSTest | 4.0.2 | 2,995.4 ms | 2,996.6 ms | 7.68 ms | +| xUnit3 | 3.2.0 | 3,086.8 ms | 3,086.9 ms | 11.28 ms | +| šŸ† **TUnit (AOT)** | 1.0.78 | 130.8 ms | 130.8 ms | 0.47 ms | + +
+šŸ“Š Visual Comparison + +```mermaid +%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#4CAF50", "primaryTextColor": "#fff", "primaryBorderColor": "#2E7D32", "lineColor": "#2E7D32", "secondaryColor": "#FFC107", "tertiaryColor": "#2196F3"}}}%% +xychart-beta + title "results - Mean Execution Time (Lower is Better)" + x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit (AOT)"] + y-axis "Time (ms)" 0 --> 697 + bar [580.40, 1.00, 2.00, 3.00, 130.80] +``` + +
--- @@ -56,10 +48,24 @@ Compilation time comparison across frameworks: | Framework | Version | Mean | Median | StdDev | |-----------|---------|------|--------|--------| -| šŸ† **TUnit** | 1.0.39 | 1.788 s | 1.789 s | 0.0352 s | -| Build_NUnit | 4.4.0 | 1.570 s | 1.571 s | 0.0178 s | -| Build_MSTest | 4.0.1 | 1.657 s | 1.661 s | 0.0240 s | -| Build_xUnit3 | 3.2.0 | 1.577 s | 1.575 s | 0.0136 s | +| šŸ† **TUnit** | 1.0.78 | 1.791 s | 1.788 s | 0.0257 s | +| Build_NUnit | 4.4.0 | 1.560 s | 1.565 s | 0.0213 s | +| Build_MSTest | 4.0.2 | 1.652 s | 1.656 s | 0.0123 s | +| Build_xUnit3 | 3.2.0 | 1.546 s | 1.551 s | 0.0203 s | + +
+šŸ“Š Visual Comparison + +```mermaid +%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#4CAF50", "primaryTextColor": "#fff", "primaryBorderColor": "#2E7D32", "lineColor": "#2E7D32", "secondaryColor": "#FFC107", "tertiaryColor": "#2196F3"}}}%% +xychart-beta + title "Build Time Comparison - Mean Compilation Time (Lower is Better)" + x-axis ["Build_TUnit", "Build_NUnit", "Build_MSTest", "Build_xUnit3"] + y-axis "Time (ms)" 0 --> 3 + bar [1.79, 1.56, 1.65, 1.55] +``` + +
--- @@ -70,10 +76,10 @@ These benchmarks compare TUnit against the most popular .NET testing frameworks: | Framework | Version Tested | |-----------|----------------| -| **TUnit** | 1.0.39 | +| **TUnit** | 1.0.78 | | **xUnit v3** | 3.2.0 | | **NUnit** | 4.4.0 | -| **MSTest** | 4.0.1 | +| **MSTest** | 4.0.2 | ### Test Scenarios @@ -88,8 +94,8 @@ The benchmarks measure real-world testing patterns: ### Environment - **OS**: Ubuntu Latest (GitHub Actions) -- **Runtime**: .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 -- **SDK**: .NET SDK 10.0.100-rc.2.25502.107 +- **Runtime**: .NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3 +- **SDK**: .NET SDK 10.0.100 - **Hardware**: GitHub Actions Standard Runner (Ubuntu) - **Tool**: BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat) @@ -112,4 +118,4 @@ These benchmarks run automatically daily via [GitHub Actions](https://github.com Each benchmark runs multiple iterations with statistical analysis to ensure accuracy. Results may vary based on hardware and test characteristics. ::: -*Last generated: 2025-11-10T00:29:09.704Z* +*Last generated: 2025-11-11T22:58:18.735Z* diff --git a/docs/static/benchmarks/historical.json b/docs/static/benchmarks/historical.json index 9912b13410..f224d6dfaf 100644 --- a/docs/static/benchmarks/historical.json +++ b/docs/static/benchmarks/historical.json @@ -38,5 +38,15 @@ "count": 1 }, "environment": "Ubuntu" + }, + { + "date": "2025-11-11", + "averageSpeedups": { + "vsXUnit": "0.0", + "vsNUnit": "0.0", + "vsMSTest": "0.0", + "count": 1 + }, + "environment": "Ubuntu" } ] \ No newline at end of file diff --git a/docs/static/benchmarks/latest.json b/docs/static/benchmarks/latest.json index 145489d3e1..076f04121d 100644 --- a/docs/static/benchmarks/latest.json +++ b/docs/static/benchmarks/latest.json @@ -1,51 +1,51 @@ { - "timestamp": "2025-11-10T00:29:09.704Z", + "timestamp": "2025-11-11T22:58:18.735Z", "environment": { "benchmarkDotNetVersion": "BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat)", - "sdk": ".NET SDK 10.0.100-rc.2.25502.107", - "host": ".NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3" + "sdk": ".NET SDK 10.0.100", + "host": ".NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3" }, "categories": { "results": [ { "Method": "TUnit", - "Version": "1.0.39", - "Mean": "495.43 ms", - "Error": "2.303 ms", - "StdDev": "2.042 ms", - "Median": "495.43 ms" + "Version": "1.0.78", + "Mean": "580.4 ms", + "Error": "8.32 ms", + "StdDev": "7.38 ms", + "Median": "578.3 ms" }, { "Method": "NUnit", "Version": "4.4.0", - "Mean": "578.77 ms", - "Error": "9.242 ms", - "StdDev": "7.718 ms", - "Median": "580.83 ms" + "Mean": "1,211.6 ms", + "Error": "10.05 ms", + "StdDev": "9.40 ms", + "Median": "1,209.8 ms" }, { "Method": "MSTest", - "Version": "4.0.1", - "Mean": "495.62 ms", - "Error": "7.338 ms", - "StdDev": "6.128 ms", - "Median": "496.03 ms" + "Version": "4.0.2", + "Mean": "2,995.4 ms", + "Error": "8.66 ms", + "StdDev": "7.68 ms", + "Median": "2,996.6 ms" }, { "Method": "xUnit3", "Version": "3.2.0", - "Mean": "591.61 ms", - "Error": "10.673 ms", - "StdDev": "9.984 ms", - "Median": "591.28 ms" + "Mean": "3,086.8 ms", + "Error": "12.06 ms", + "StdDev": "11.28 ms", + "Median": "3,086.9 ms" }, { "Method": "TUnit_AOT", - "Version": "1.0.39", - "Mean": "44.15 ms", - "Error": "1.112 ms", - "StdDev": "3.244 ms", - "Median": "44.00 ms" + "Version": "1.0.78", + "Mean": "130.8 ms", + "Error": "0.50 ms", + "StdDev": "0.47 ms", + "Median": "130.8 ms" } ] }, @@ -53,58 +53,58 @@ "BuildTime": [ { "Method": "Build_TUnit", - "Version": "1.0.39", - "Mean": "1.788 s", - "Error": "0.0343 s", - "StdDev": "0.0352 s", - "Median": "1.789 s" + "Version": "1.0.78", + "Mean": "1.791 s", + "Error": "0.0290 s", + "StdDev": "0.0257 s", + "Median": "1.788 s" }, { "Method": "Build_NUnit", "Version": "4.4.0", - "Mean": "1.570 s", - "Error": "0.0201 s", - "StdDev": "0.0178 s", - "Median": "1.571 s" + "Mean": "1.560 s", + "Error": "0.0240 s", + "StdDev": "0.0213 s", + "Median": "1.565 s" }, { "Method": "Build_MSTest", - "Version": "4.0.1", - "Mean": "1.657 s", - "Error": "0.0256 s", - "StdDev": "0.0240 s", - "Median": "1.661 s" + "Version": "4.0.2", + "Mean": "1.652 s", + "Error": "0.0132 s", + "StdDev": "0.0123 s", + "Median": "1.656 s" }, { "Method": "Build_xUnit3", "Version": "3.2.0", - "Mean": "1.577 s", - "Error": "0.0154 s", - "StdDev": "0.0136 s", - "Median": "1.575 s" + "Mean": "1.546 s", + "Error": "0.0217 s", + "StdDev": "0.0203 s", + "Median": "1.551 s" } ] }, "comparisons": { "results": { - "tunitMean": 495.43, - "tunitAOTMean": 44.15, - "vsXUnit": "1.19", - "vsNUnit": "1.17", - "vsMSTest": "1.00", - "aotSpeedup": "11.22" + "tunitMean": 580.4, + "tunitAOTMean": 130.8, + "vsXUnit": "0.01", + "vsNUnit": "0.00", + "vsMSTest": "0.00", + "aotSpeedup": "4.44" } }, "averageSpeedups": { - "vsXUnit": "1.2", - "vsNUnit": "1.2", - "vsMSTest": "1.0", + "vsXUnit": "0.0", + "vsNUnit": "0.0", + "vsMSTest": "0.0", "count": 1 }, "stats": { "runtimeCategories": 1, "buildCategories": 1, "totalBenchmarks": 2, - "lastUpdated": "2025-11-10T00:29:09.702Z" + "lastUpdated": "2025-11-11T22:58:18.734Z" } } \ No newline at end of file diff --git a/docs/static/benchmarks/summary.json b/docs/static/benchmarks/summary.json new file mode 100644 index 0000000000..d91d4c1bd0 --- /dev/null +++ b/docs/static/benchmarks/summary.json @@ -0,0 +1,10 @@ +{ + "runtime": [ + "results" + ], + "build": [ + "BuildTime" + ], + "timestamp": "2025-11-11", + "environment": "Ubuntu Latest • .NET SDK 10.0.100" +} \ No newline at end of file From 9bbc55262566a640f6086bfa370f2b152268ef4e Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 23:29:46 +0000 Subject: [PATCH 36/62] feat(tests): add SetupTeardownTests for comprehensive resource management in unit tests --- .github/workflows/speed-comparison.yml | 2 +- .../UnifiedTests/SetupTeardownTests.cs | 380 ++++++++++++++++++ 2 files changed, 381 insertions(+), 1 deletion(-) create mode 100644 tools/speed-comparison/UnifiedTests/SetupTeardownTests.cs diff --git a/.github/workflows/speed-comparison.yml b/.github/workflows/speed-comparison.yml index 67e5136da5..510e5d4f3e 100644 --- a/.github/workflows/speed-comparison.yml +++ b/.github/workflows/speed-comparison.yml @@ -45,7 +45,7 @@ jobs: environment: ${{ github.ref == 'refs/heads/main' && 'Production' || 'Pull Requests' }} strategy: matrix: - class: [DataDrivenTests, AsyncTests, ScaleTests, MatrixTests, MassiveParallelTests] + class: [DataDrivenTests, AsyncTests, ScaleTests, MatrixTests, MassiveParallelTests, SetupTeardownTests] fail-fast: false runs-on: ubuntu-latest concurrency: diff --git a/tools/speed-comparison/UnifiedTests/SetupTeardownTests.cs b/tools/speed-comparison/UnifiedTests/SetupTeardownTests.cs new file mode 100644 index 0000000000..613d9a842a --- /dev/null +++ b/tools/speed-comparison/UnifiedTests/SetupTeardownTests.cs @@ -0,0 +1,380 @@ +using System.Text; +using System.Threading.Tasks; + +namespace UnifiedTests; + +[TestClass] +#if XUNIT3 +public class SetupTeardownTests : IDisposable +#else +public class SetupTeardownTests +#endif +{ + // Simulated expensive state + private byte[] _databaseConnection; + private List _tempFiles; + private HttpClient _httpClient; + private StringBuilder _logBuilder; + +#if TUNIT + [Before(Test)] + public async Task Setup() +#elif XUNIT3 + public SetupTeardownTests() +#elif NUNIT + [SetUp] + public async Task Setup() +#elif MSTEST + [TestInitialize] + public async Task Setup() +#else + public async Task Setup() +#endif + { +#if XUNIT3 + SetupCore().GetAwaiter().GetResult(); + } + + private async Task SetupCore() + { +#endif + // Simulate expensive database connection initialization + _databaseConnection = new byte[1024 * 100]; // 100KB allocation + for (int i = 0; i < _databaseConnection.Length; i++) + { + _databaseConnection[i] = (byte)(i % 256); + } + + // Simulate file system setup + _tempFiles = []; + await Task.Delay(5); // Simulate async I/O + + // Simulate HTTP client initialization + _httpClient = new HttpClient + { + Timeout = TimeSpan.FromSeconds(30) + }; + + // Simulate logging infrastructure + _logBuilder = new StringBuilder(1000); + _logBuilder.AppendLine($"Test started at {DateTime.UtcNow}"); + + // Simulate loading configuration + await Task.Delay(5); + } + +#if TUNIT + [After(Test)] + public async Task Cleanup() +#elif XUNIT3 + public void Dispose() +#elif NUNIT + [TearDown] + public async Task Cleanup() +#elif MSTEST + [TestCleanup] + public async Task Cleanup() +#else + public async Task Cleanup() +#endif + { +#if XUNIT3 + CleanupCore().GetAwaiter().GetResult(); + } + + private async Task CleanupCore() + { +#endif + // Simulate database connection cleanup + if (_databaseConnection != null) + { + Array.Clear(_databaseConnection, 0, _databaseConnection.Length); + _databaseConnection = null!; + } + + // Simulate file cleanup + if (_tempFiles != null) + { + await Task.Delay(5); // Simulate async I/O + _tempFiles.Clear(); + _tempFiles = null!; + } + + // Cleanup HTTP client + _httpClient?.Dispose(); + _httpClient = null!; + + // Finalize logging + if (_logBuilder != null) + { + _logBuilder.AppendLine($"Test completed at {DateTime.UtcNow}"); + _logBuilder.Clear(); + _logBuilder = null!; + } + + await Task.Delay(5); // Simulate async cleanup + } + + [Test] + public void DatabaseOperationTest() + { + // Simulate database query + var sum = 0; + for (int i = 0; i < 1000; i++) + { + sum += _databaseConnection[i]; + } + _logBuilder.AppendLine($"Database operation result: {sum}"); + } + + [Test] + public async Task AsyncDatabaseOperationTest() + { + // Simulate async database operation + await Task.Delay(10); + var sum = 0; + for (int i = 0; i < 1000; i++) + { + sum += _databaseConnection[i]; + } + _logBuilder.AppendLine($"Async database operation result: {sum}"); + } + + [Test] + public void FileSystemOperationTest() + { + // Simulate file operations + for (int i = 0; i < 10; i++) + { + _tempFiles.Add($"temp_file_{i}.txt"); + } + _logBuilder.AppendLine($"Created {_tempFiles.Count} temp files"); + } + + [Test] + public async Task AsyncFileSystemOperationTest() + { + // Simulate async file operations + await Task.Delay(10); + for (int i = 0; i < 10; i++) + { + _tempFiles.Add($"async_temp_file_{i}.txt"); + } + _logBuilder.AppendLine($"Created {_tempFiles.Count} temp files asynchronously"); + } + + [Test] + public void HttpClientOperationTest() + { + // Simulate HTTP client usage + var timeout = _httpClient.Timeout; + _logBuilder.AppendLine($"HTTP client timeout: {timeout.TotalSeconds}s"); + } + + [Test] + public async Task AsyncHttpClientOperationTest() + { + // Simulate async HTTP operation + await Task.Delay(10); + var timeout = _httpClient.Timeout; + _logBuilder.AppendLine($"Async HTTP client timeout: {timeout.TotalSeconds}s"); + } + + [Test] + public void LoggingOperationTest() + { + // Simulate logging + for (int i = 0; i < 50; i++) + { + _logBuilder.AppendLine($"Log entry {i}"); + } + } + + [Test] + public async Task AsyncLoggingOperationTest() + { + // Simulate async logging + await Task.Delay(10); + for (int i = 0; i < 50; i++) + { + _logBuilder.AppendLine($"Async log entry {i}"); + } + } + + [Test] + public void MemoryIntensiveOperationTest() + { + // Simulate memory-intensive operation + var tempBuffer = new byte[1024 * 50]; // 50KB + for (int i = 0; i < tempBuffer.Length; i++) + { + tempBuffer[i] = _databaseConnection[i % _databaseConnection.Length]; + } + var sum = tempBuffer.Sum(b => (int)b); + _logBuilder.AppendLine($"Memory operation result: {sum}"); + } + + [Test] + public async Task AsyncMemoryIntensiveOperationTest() + { + // Simulate async memory-intensive operation + await Task.Delay(10); + var tempBuffer = new byte[1024 * 50]; // 50KB + for (int i = 0; i < tempBuffer.Length; i++) + { + tempBuffer[i] = _databaseConnection[i % _databaseConnection.Length]; + } + var sum = tempBuffer.Sum(b => (int)b); + _logBuilder.AppendLine($"Async memory operation result: {sum}"); + } + + [Test] + public void ComputationTest() + { + // Simulate computation + var result = 0; + for (int i = 0; i < 10000; i++) + { + result += i * i; + } + _logBuilder.AppendLine($"Computation result: {result}"); + } + + [Test] + public async Task AsyncComputationTest() + { + // Simulate async computation + await Task.Delay(10); + var result = 0; + for (int i = 0; i < 10000; i++) + { + result += i * i; + } + _logBuilder.AppendLine($"Async computation result: {result}"); + } + + [Test] + public void StringManipulationTest() + { + // Simulate string operations + var sb = new StringBuilder(); + for (int i = 0; i < 100; i++) + { + sb.Append($"Item {i}, "); + } + _logBuilder.AppendLine($"String length: {sb.Length}"); + } + + [Test] + public async Task AsyncStringManipulationTest() + { + // Simulate async string operations + await Task.Delay(10); + var sb = new StringBuilder(); + for (int i = 0; i < 100; i++) + { + sb.Append($"Item {i}, "); + } + _logBuilder.AppendLine($"Async string length: {sb.Length}"); + } + + [Test] + public void CollectionOperationTest() + { + // Simulate collection operations + var numbers = Enumerable.Range(0, 1000).ToList(); + var filtered = numbers.Where(n => n % 2 == 0).ToList(); + _logBuilder.AppendLine($"Filtered count: {filtered.Count}"); + } + + [Test] + public async Task AsyncCollectionOperationTest() + { + // Simulate async collection operations + await Task.Delay(10); + var numbers = Enumerable.Range(0, 1000).ToList(); + var filtered = numbers.Where(n => n % 2 == 0).ToList(); + _logBuilder.AppendLine($"Async filtered count: {filtered.Count}"); + } + + [Test] + public void DateTimeOperationTest() + { + // Simulate datetime operations + var start = DateTime.UtcNow; + var timestamps = new List(); + for (int i = 0; i < 100; i++) + { + timestamps.Add(start.AddSeconds(i)); + } + _logBuilder.AppendLine($"Timestamps created: {timestamps.Count}"); + } + + [Test] + public async Task AsyncDateTimeOperationTest() + { + // Simulate async datetime operations + await Task.Delay(10); + var start = DateTime.UtcNow; + var timestamps = new List(); + for (int i = 0; i < 100; i++) + { + timestamps.Add(start.AddSeconds(i)); + } + _logBuilder.AppendLine($"Async timestamps created: {timestamps.Count}"); + } + + [Test] + public void DictionaryOperationTest() + { + // Simulate dictionary operations + var dict = new Dictionary(); + for (int i = 0; i < 100; i++) + { + dict[$"key_{i}"] = i * 2; + } + _logBuilder.AppendLine($"Dictionary size: {dict.Count}"); + } + + [Test] + public async Task AsyncDictionaryOperationTest() + { + // Simulate async dictionary operations + await Task.Delay(10); + var dict = new Dictionary(); + for (int i = 0; i < 100; i++) + { + dict[$"key_{i}"] = i * 2; + } + _logBuilder.AppendLine($"Async dictionary size: {dict.Count}"); + } + + [Test] + public void JsonOperationTest() + { + // Simulate JSON serialization + var data = new + { + Id = 123, + Name = "Test Data", + Values = Enumerable.Range(0, 50).ToArray() + }; + var json = System.Text.Json.JsonSerializer.Serialize(data); + _logBuilder.AppendLine($"JSON length: {json.Length}"); + } + + [Test] + public async Task AsyncJsonOperationTest() + { + // Simulate async JSON operations + await Task.Delay(10); + var data = new + { + Id = 123, + Name = "Test Data", + Values = Enumerable.Range(0, 50).ToArray() + }; + var json = System.Text.Json.JsonSerializer.Serialize(data); + _logBuilder.AppendLine($"Async JSON length: {json.Length}"); + } +} From 092bd471667cb73b655d8c09e80405e8f364f033 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Tue, 11 Nov 2025 23:57:18 +0000 Subject: [PATCH 37/62] Add performance benchmarks documentation for TUnit --- .github/scripts/docs/docs/benchmarks/index.md | 69 ++ .github/scripts/process-benchmarks.js | 153 +-- docs/docs/benchmarks/index.md | 117 ++- docs/docusaurus.config.ts | 5 + docs/package.json | 1 + docs/static/benchmarks/historical.json | 44 +- docs/static/benchmarks/latest.json | 194 +++- docs/static/benchmarks/summary.json | 6 +- docs/yarn.lock | 962 +++++++++++++++++- 9 files changed, 1328 insertions(+), 223 deletions(-) create mode 100644 .github/scripts/docs/docs/benchmarks/index.md diff --git a/.github/scripts/docs/docs/benchmarks/index.md b/.github/scripts/docs/docs/benchmarks/index.md new file mode 100644 index 0000000000..8bfca449e7 --- /dev/null +++ b/.github/scripts/docs/docs/benchmarks/index.md @@ -0,0 +1,69 @@ +--- +title: Performance Benchmarks +description: Real-world performance comparisons between TUnit and other .NET testing frameworks +sidebar_position: 1 +--- + +# Performance Benchmarks + +:::info Last Updated +These benchmarks were automatically generated on **2025-11-11** from the latest CI run. + +**Environment:** Ubuntu Latest • .NET 10 +::: + +## šŸš€ Runtime Performance + + +--- + +## šŸ“Š Methodology + +These benchmarks compare TUnit against the most popular .NET testing frameworks: + +| Framework | Version Tested | +|-----------|----------------| +| **TUnit** | latest | +| **xUnit v3** | latest | +| **NUnit** | latest | +| **MSTest** | latest | + +### Test Scenarios + +The benchmarks measure real-world testing patterns: + +- **DataDrivenTests**: Parameterized tests with multiple data sources +- **AsyncTests**: Realistic async/await patterns with I/O simulation +- **ScaleTests**: Large test suites (150+ tests) measuring scalability +- **MatrixTests**: Combinatorial test generation and execution +- **MassiveParallelTests**: Parallel execution stress tests +- **SetupTeardownTests**: Expensive test fixtures with setup/teardown overhead + +### Environment + +- **OS**: Ubuntu Latest (GitHub Actions) +- **Runtime**: .NET 10 +- **SDK**: .NET 10 SDK +- **Hardware**: GitHub Actions Standard Runner (Ubuntu) +- **Tool**: BenchmarkDotNet + +### Why These Numbers Matter + +- **No Mocking**: All tests use realistic patterns, not artificial micro-benchmarks +- **Equivalent Logic**: Each framework implements identical test scenarios +- **Warm-Up Excluded**: Measurements exclude JIT warm-up overhead +- **Statistical Rigor**: Multiple iterations with outlier detection + +### Source Code + +All benchmark source code is available in the [`tools/speed-comparison`](https://github.com/thomhurst/TUnit/tree/main/tools/speed-comparison) directory. + +--- + +:::note Continuous Benchmarking +These benchmarks run automatically daily via [GitHub Actions](https://github.com/thomhurst/TUnit/actions/workflows/speed-comparison.yml). + +Each benchmark runs multiple iterations with statistical analysis to ensure accuracy. Results may vary based on hardware and test characteristics. +::: + +*Last generated: 2025-11-11T23:48:04.871Z* diff --git a/.github/scripts/process-benchmarks.js b/.github/scripts/process-benchmarks.js index f46ff58c51..a0d1109e64 100644 --- a/.github/scripts/process-benchmarks.js +++ b/.github/scripts/process-benchmarks.js @@ -79,8 +79,10 @@ function extractEnvironmentInfo(content) { } function parseMeanValue(meanStr) { - // Parse "352.5 ms" -> 352.5 - const match = meanStr.match(/[\d.]+/); + // Parse "352.5 ms" or "1,211.6 ms" -> 352.5 or 1211.6 + // Remove commas, then extract number + const cleaned = meanStr.replace(/,/g, ''); + const match = cleaned.match(/[\d.]+/); return match ? parseFloat(match[0]) : 0; } @@ -136,62 +138,7 @@ const stats = { lastUpdated: new Date().toISOString() }; -console.log('\nšŸ“ˆ Calculating performance comparisons...'); - -function calculateComparisons() { - const comparisons = {}; - - Object.entries(categories.runtime).forEach(([category, data]) => { - const tunit = data.find(d => d.Method === 'TUnit'); - const tunitAOT = data.find(d => d.Method === 'TUnit_AOT'); - const xunit = data.find(d => d.Method === 'xUnit3'); - const nunit = data.find(d => d.Method === 'NUnit'); - const mstest = data.find(d => d.Method === 'MSTest'); - - if (tunit) { - const tunitMean = parseMeanValue(tunit.Mean); - comparisons[category] = { - tunitMean, - tunitAOTMean: tunitAOT ? parseMeanValue(tunitAOT.Mean) : null, - vsXUnit: xunit ? (parseMeanValue(xunit.Mean) / tunitMean).toFixed(2) : null, - vsNUnit: nunit ? (parseMeanValue(nunit.Mean) / tunitMean).toFixed(2) : null, - vsMSTest: mstest ? (parseMeanValue(mstest.Mean) / tunitMean).toFixed(2) : null, - aotSpeedup: tunitAOT ? (tunitMean / parseMeanValue(tunitAOT.Mean)).toFixed(2) : null - }; - } - }); - - return comparisons; -} - -const comparisons = calculateComparisons(); - -// Calculate average speedups -const avgSpeedups = { - vsXUnit: 0, - vsNUnit: 0, - vsMSTest: 0, - count: 0 -}; - -Object.values(comparisons).forEach(comp => { - if (comp.vsXUnit) { - avgSpeedups.vsXUnit += parseFloat(comp.vsXUnit); - avgSpeedups.count++; - } - if (comp.vsNUnit) avgSpeedups.vsNUnit += parseFloat(comp.vsNUnit); - if (comp.vsMSTest) avgSpeedups.vsMSTest += parseFloat(comp.vsMSTest); -}); - -if (avgSpeedups.count > 0) { - avgSpeedups.vsXUnit = (avgSpeedups.vsXUnit / avgSpeedups.count).toFixed(1); - avgSpeedups.vsNUnit = (avgSpeedups.vsNUnit / avgSpeedups.count).toFixed(1); - avgSpeedups.vsMSTest = (avgSpeedups.vsMSTest / avgSpeedups.count).toFixed(1); -} - -console.log(` Average speedup vs xUnit: ${avgSpeedups.vsXUnit}x`); -console.log(` Average speedup vs NUnit: ${avgSpeedups.vsNUnit}x`); -console.log(` Average speedup vs MSTest: ${avgSpeedups.vsMSTest}x`); +console.log('\nšŸ“Š Preparing benchmark data...'); // Generate main benchmarks page console.log('\nšŸ“ Generating documentation...'); @@ -225,40 +172,30 @@ Object.entries(categories.runtime).forEach(([testClass, data]) => { mainPage += `|-----------|---------|------|--------|--------|\n`; data.forEach(row => { - const emoji = row.Method.includes('TUnit') ? 'šŸ† ' : ''; const name = row.Method.includes('TUnit_AOT') ? '**TUnit (AOT)**' : row.Method.includes('TUnit') ? '**TUnit**' : row.Method; - mainPage += `| ${emoji}${name} | ${row.Version || 'N/A'} | ${row.Mean} | ${row.Median || 'N/A'} | ${row.StdDev || 'N/A'} |\n`; + mainPage += `| ${name} | ${row.Version || 'N/A'} | ${row.Mean} | ${row.Median || 'N/A'} | ${row.StdDev || 'N/A'} |\n`; }); mainPage += '\n'; - // Add Mermaid bar chart - mainPage += `
\nšŸ“Š Visual Comparison\n\n`; - mainPage += '```mermaid\n%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#4CAF50", "primaryTextColor": "#fff", "primaryBorderColor": "#2E7D32", "lineColor": "#2E7D32", "secondaryColor": "#FFC107", "tertiaryColor": "#2196F3"}}}%%\nxychart-beta\n'; - mainPage += ` title "${testClass} - Mean Execution Time (Lower is Better)"\n`; - mainPage += ' x-axis ['; - - // Add framework names for x-axis - const chartData = data.map(row => { - const name = row.Method.includes('TUnit_AOT') ? 'TUnit (AOT)' : row.Method; - const meanValue = parseMeanValue(row.Mean); - return { name, value: meanValue }; - }); + // Add Mermaid chart + mainPage += `\`\`\`mermaid\n`; + mainPage += `%%{init: {'theme':'base'}}%%\n`; + mainPage += `xychart-beta\n`; + mainPage += ` title "${testClass} Performance Comparison"\n`; - mainPage += chartData.map(d => `"${d.name}"`).join(', '); - mainPage += ']\n'; - mainPage += ' y-axis "Time (ms)" 0 --> '; + // Detect time unit from the data + const sampleMean = data[0]?.Mean || ''; + const timeUnit = sampleMean.includes(' s') ? 's' : 'ms'; - // Set y-axis max to 120% of the highest value for better visualization - const maxValue = Math.max(...chartData.map(d => d.value)); - mainPage += Math.ceil(maxValue * 1.2); - mainPage += '\n'; + // Find max value for y-axis scaling + const maxValue = Math.max(...data.map(d => parseMeanValue(d.Mean))); + const yMax = Math.ceil(maxValue * 1.2); // 20% padding - mainPage += ' bar ['; - mainPage += chartData.map(d => d.value.toFixed(2)).join(', '); - mainPage += ']\n'; - mainPage += '```\n\n'; - mainPage += `
\n\n`; + mainPage += ` x-axis [${data.map(d => `"${d.Method}"`).join(', ')}]\n`; + mainPage += ` y-axis "Time (${timeUnit})" 0 --> ${yMax}\n`; + mainPage += ` bar [${data.map(d => parseMeanValue(d.Mean)).join(', ')}]\n`; + mainPage += `\`\`\`\n\n`; }); // Add build time results @@ -271,40 +208,30 @@ if (Object.keys(categories.build).length > 0) { mainPage += `|-----------|---------|------|--------|--------|\n`; data.forEach(row => { - const emoji = row.Method.includes('TUnit') ? 'šŸ† ' : ''; const name = row.Method.includes('TUnit') ? '**TUnit**' : row.Method; - mainPage += `| ${emoji}${name} | ${row.Version || 'N/A'} | ${row.Mean} | ${row.Median || 'N/A'} | ${row.StdDev || 'N/A'} |\n`; + mainPage += `| ${name} | ${row.Version || 'N/A'} | ${row.Mean} | ${row.Median || 'N/A'} | ${row.StdDev || 'N/A'} |\n`; }); mainPage += '\n'; - // Add Mermaid bar chart for build performance - mainPage += `
\nšŸ“Š Visual Comparison\n\n`; - mainPage += '```mermaid\n%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#4CAF50", "primaryTextColor": "#fff", "primaryBorderColor": "#2E7D32", "lineColor": "#2E7D32", "secondaryColor": "#FFC107", "tertiaryColor": "#2196F3"}}}%%\nxychart-beta\n'; - mainPage += ` title "Build Time Comparison - Mean Compilation Time (Lower is Better)"\n`; - mainPage += ' x-axis ['; - - // Add framework names for x-axis - const chartData = data.map(row => { - const name = row.Method; - const meanValue = parseMeanValue(row.Mean); - return { name, value: meanValue }; - }); + // Add Mermaid chart for build performance + mainPage += `\`\`\`mermaid\n`; + mainPage += `%%{init: {'theme':'base'}}%%\n`; + mainPage += `xychart-beta\n`; + mainPage += ` title "Build Time Comparison"\n`; - mainPage += chartData.map(d => `"${d.name}"`).join(', '); - mainPage += ']\n'; - mainPage += ' y-axis "Time (ms)" 0 --> '; + // Detect time unit from the data + const sampleMean = data[0]?.Mean || ''; + const timeUnit = sampleMean.includes(' s') ? 's' : 'ms'; - // Set y-axis max to 120% of the highest value for better visualization - const maxValue = Math.max(...chartData.map(d => d.value)); - mainPage += Math.ceil(maxValue * 1.2); - mainPage += '\n'; + // Find max value for y-axis scaling + const maxValue = Math.max(...data.map(d => parseMeanValue(d.Mean))); + const yMax = Math.ceil(maxValue * 1.2); // 20% padding - mainPage += ' bar ['; - mainPage += chartData.map(d => d.value.toFixed(2)).join(', '); - mainPage += ']\n'; - mainPage += '```\n\n'; - mainPage += `
\n\n`; + mainPage += ` x-axis [${data.map(d => `"${d.Method}"`).join(', ')}]\n`; + mainPage += ` y-axis "Time (${timeUnit})" 0 --> ${yMax}\n`; + mainPage += ` bar [${data.map(d => parseMeanValue(d.Mean)).join(', ')}]\n`; + mainPage += `\`\`\`\n\n`; }); } @@ -337,9 +264,10 @@ The benchmarks measure real-world testing patterns: - **DataDrivenTests**: Parameterized tests with multiple data sources - **AsyncTests**: Realistic async/await patterns with I/O simulation -- **ScaleTests**: Large test suites (1000+ tests) measuring scalability +- **ScaleTests**: Large test suites (150+ tests) measuring scalability - **MatrixTests**: Combinatorial test generation and execution - **MassiveParallelTests**: Parallel execution stress tests +- **SetupTeardownTests**: Expensive test fixtures with setup/teardown overhead ### Environment @@ -380,8 +308,6 @@ const benchmarkData = { environment: environmentInfo, categories: categories.runtime, build: categories.build, - comparisons, - averageSpeedups: avgSpeedups, stats }; @@ -407,7 +333,6 @@ if (fs.existsSync(historicalFile)) { // Add new data point historical.push({ date: new Date().toISOString().split('T')[0], - averageSpeedups: avgSpeedups, environment: environmentInfo.os || 'Ubuntu' }); diff --git a/docs/docs/benchmarks/index.md b/docs/docs/benchmarks/index.md index b0bb83b7cc..26b2a27508 100644 --- a/docs/docs/benchmarks/index.md +++ b/docs/docs/benchmarks/index.md @@ -15,29 +15,104 @@ These benchmarks were automatically generated on **2025-11-11** from the latest ## šŸš€ Runtime Performance -### results +### AsyncTests | Framework | Version | Mean | Median | StdDev | |-----------|---------|------|--------|--------| -| šŸ† **TUnit** | 1.0.78 | 580.4 ms | 578.3 ms | 7.38 ms | +| **TUnit** | 1.0.78 | 515.2 ms | 514.4 ms | 5.97 ms | +| NUnit | 4.4.0 | 653.0 ms | 651.9 ms | 9.14 ms | +| MSTest | 4.0.2 | 625.0 ms | 626.1 ms | 5.44 ms | +| xUnit3 | 3.2.0 | 705.5 ms | 703.8 ms | 8.57 ms | +| **TUnit (AOT)** | 1.0.78 | 123.0 ms | 123.0 ms | 0.27 ms | + +```mermaid +%%{init: {'theme':'base'}}%% +xychart-beta + title "AsyncTests Performance Comparison" + x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"] + y-axis "Time (ms)" 0 --> 847 + bar [515.2, 653, 625, 705.5, 123] +``` + + +### DataDrivenTests + +| Framework | Version | Mean | Median | StdDev | +|-----------|---------|------|--------|--------| +| **TUnit** | 1.0.78 | 470.12 ms | 468.90 ms | 6.656 ms | +| NUnit | 4.4.0 | 517.39 ms | 515.90 ms | 5.243 ms | +| MSTest | 4.0.2 | 469.53 ms | 468.35 ms | 8.490 ms | +| xUnit3 | 3.2.0 | 559.23 ms | 561.96 ms | 6.806 ms | +| **TUnit (AOT)** | 1.0.78 | 23.21 ms | 23.24 ms | 0.216 ms | + +```mermaid +%%{init: {'theme':'base'}}%% +xychart-beta + title "DataDrivenTests Performance Comparison" + x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"] + y-axis "Time (ms)" 0 --> 672 + bar [470.12, 517.39, 469.53, 559.23, 23.21] +``` + + +### MassiveParallelTests + +| Framework | Version | Mean | Median | StdDev | +|-----------|---------|------|--------|--------| +| **TUnit** | 1.0.78 | 580.4 ms | 578.3 ms | 7.38 ms | | NUnit | 4.4.0 | 1,211.6 ms | 1,209.8 ms | 9.40 ms | | MSTest | 4.0.2 | 2,995.4 ms | 2,996.6 ms | 7.68 ms | | xUnit3 | 3.2.0 | 3,086.8 ms | 3,086.9 ms | 11.28 ms | -| šŸ† **TUnit (AOT)** | 1.0.78 | 130.8 ms | 130.8 ms | 0.47 ms | +| **TUnit (AOT)** | 1.0.78 | 130.8 ms | 130.8 ms | 0.47 ms | -
-šŸ“Š Visual Comparison +```mermaid +%%{init: {'theme':'base'}}%% +xychart-beta + title "MassiveParallelTests Performance Comparison" + x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"] + y-axis "Time (ms)" 0 --> 3705 + bar [580.4, 1211.6, 2995.4, 3086.8, 130.8] +``` + + +### MatrixTests + +| Framework | Version | Mean | Median | StdDev | +|-----------|---------|------|--------|--------| +| **TUnit** | 1.0.78 | 556.38 ms | 555.90 ms | 7.361 ms | +| NUnit | 4.4.0 | 1,545.79 ms | 1,548.36 ms | 14.829 ms | +| MSTest | 4.0.2 | 1,510.34 ms | 1,509.61 ms | 12.203 ms | +| xUnit3 | 3.2.0 | 1,595.19 ms | 1,596.19 ms | 9.486 ms | +| **TUnit (AOT)** | 1.0.78 | 78.81 ms | 78.75 ms | 0.441 ms | ```mermaid -%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#4CAF50", "primaryTextColor": "#fff", "primaryBorderColor": "#2E7D32", "lineColor": "#2E7D32", "secondaryColor": "#FFC107", "tertiaryColor": "#2196F3"}}}%% +%%{init: {'theme':'base'}}%% xychart-beta - title "results - Mean Execution Time (Lower is Better)" - x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit (AOT)"] - y-axis "Time (ms)" 0 --> 697 - bar [580.40, 1.00, 2.00, 3.00, 130.80] + title "MatrixTests Performance Comparison" + x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"] + y-axis "Time (ms)" 0 --> 1915 + bar [556.38, 1545.79, 1510.34, 1595.19, 78.81] ``` -
+ +### ScaleTests + +| Framework | Version | Mean | Median | StdDev | +|-----------|---------|------|--------|--------| +| **TUnit** | 1.0.78 | 488.96 ms | 489.51 ms | 3.711 ms | +| NUnit | 4.4.0 | 564.21 ms | 564.16 ms | 7.073 ms | +| MSTest | 4.0.2 | 496.03 ms | 494.58 ms | 7.211 ms | +| xUnit3 | 3.2.0 | 578.53 ms | 581.03 ms | 10.227 ms | +| **TUnit (AOT)** | 1.0.78 | 45.46 ms | 45.85 ms | 4.083 ms | + +```mermaid +%%{init: {'theme':'base'}}%% +xychart-beta + title "ScaleTests Performance Comparison" + x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"] + y-axis "Time (ms)" 0 --> 695 + bar [488.96, 564.21, 496.03, 578.53, 45.46] +``` --- @@ -48,25 +123,20 @@ Compilation time comparison across frameworks: | Framework | Version | Mean | Median | StdDev | |-----------|---------|------|--------|--------| -| šŸ† **TUnit** | 1.0.78 | 1.791 s | 1.788 s | 0.0257 s | +| **TUnit** | 1.0.78 | 1.791 s | 1.788 s | 0.0257 s | | Build_NUnit | 4.4.0 | 1.560 s | 1.565 s | 0.0213 s | | Build_MSTest | 4.0.2 | 1.652 s | 1.656 s | 0.0123 s | | Build_xUnit3 | 3.2.0 | 1.546 s | 1.551 s | 0.0203 s | -
-šŸ“Š Visual Comparison - ```mermaid -%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#4CAF50", "primaryTextColor": "#fff", "primaryBorderColor": "#2E7D32", "lineColor": "#2E7D32", "secondaryColor": "#FFC107", "tertiaryColor": "#2196F3"}}}%% +%%{init: {'theme':'base'}}%% xychart-beta - title "Build Time Comparison - Mean Compilation Time (Lower is Better)" + title "Build Time Comparison" x-axis ["Build_TUnit", "Build_NUnit", "Build_MSTest", "Build_xUnit3"] - y-axis "Time (ms)" 0 --> 3 - bar [1.79, 1.56, 1.65, 1.55] + y-axis "Time (s)" 0 --> 3 + bar [1.791, 1.56, 1.652, 1.546] ``` -
- --- @@ -87,9 +157,10 @@ The benchmarks measure real-world testing patterns: - **DataDrivenTests**: Parameterized tests with multiple data sources - **AsyncTests**: Realistic async/await patterns with I/O simulation -- **ScaleTests**: Large test suites (1000+ tests) measuring scalability +- **ScaleTests**: Large test suites (150+ tests) measuring scalability - **MatrixTests**: Combinatorial test generation and execution - **MassiveParallelTests**: Parallel execution stress tests +- **SetupTeardownTests**: Expensive test fixtures with setup/teardown overhead ### Environment @@ -118,4 +189,4 @@ These benchmarks run automatically daily via [GitHub Actions](https://github.com Each benchmark runs multiple iterations with statistical analysis to ensure accuracy. Results may vary based on hardware and test characteristics. ::: -*Last generated: 2025-11-11T22:58:18.735Z* +*Last generated: 2025-11-11T23:54:26.122Z* diff --git a/docs/docusaurus.config.ts b/docs/docusaurus.config.ts index 0b75b8f24b..d9b66cd3c6 100644 --- a/docs/docusaurus.config.ts +++ b/docs/docusaurus.config.ts @@ -55,6 +55,11 @@ const config: Config = { ], ], + markdown: { + mermaid: true, + }, + themes: ['@docusaurus/theme-mermaid'], + themeConfig: { // Replace with your project's social card algolia: { diff --git a/docs/package.json b/docs/package.json index 15e14f9e45..4aab3e1654 100644 --- a/docs/package.json +++ b/docs/package.json @@ -17,6 +17,7 @@ "dependencies": { "@docusaurus/core": "3.9.2", "@docusaurus/preset-classic": "3.9.2", + "@docusaurus/theme-mermaid": "^3.9.2", "@mdx-js/react": "^3.0.0", "clsx": "^2.1.1", "prism-react-renderer": "^2.4.0", diff --git a/docs/static/benchmarks/historical.json b/docs/static/benchmarks/historical.json index f224d6dfaf..463b63da63 100644 --- a/docs/static/benchmarks/historical.json +++ b/docs/static/benchmarks/historical.json @@ -1,52 +1,10 @@ [ { - "date": "2025-11-07", - "averageSpeedups": { - "vsXUnit": "1.3", - "vsNUnit": "1.2", - "vsMSTest": "1.3", - "count": 1 - }, - "environment": "Ubuntu" - }, - { - "date": "2025-11-08", - "averageSpeedups": { - "vsXUnit": "1.2", - "vsNUnit": "1.1", - "vsMSTest": "1.0", - "count": 1 - }, - "environment": "Ubuntu" - }, - { - "date": "2025-11-09", - "averageSpeedups": { - "vsXUnit": "0.0", - "vsNUnit": "0.0", - "vsMSTest": "0.0", - "count": 1 - }, - "environment": "Ubuntu" - }, - { - "date": "2025-11-10", - "averageSpeedups": { - "vsXUnit": "1.2", - "vsNUnit": "1.2", - "vsMSTest": "1.0", - "count": 1 - }, + "date": "2025-11-11", "environment": "Ubuntu" }, { "date": "2025-11-11", - "averageSpeedups": { - "vsXUnit": "0.0", - "vsNUnit": "0.0", - "vsMSTest": "0.0", - "count": 1 - }, "environment": "Ubuntu" } ] \ No newline at end of file diff --git a/docs/static/benchmarks/latest.json b/docs/static/benchmarks/latest.json index 076f04121d..9012f7c5d8 100644 --- a/docs/static/benchmarks/latest.json +++ b/docs/static/benchmarks/latest.json @@ -1,12 +1,96 @@ { - "timestamp": "2025-11-11T22:58:18.735Z", + "timestamp": "2025-11-11T23:54:26.123Z", "environment": { "benchmarkDotNetVersion": "BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat)", "sdk": ".NET SDK 10.0.100", "host": ".NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3" }, "categories": { - "results": [ + "AsyncTests": [ + { + "Method": "TUnit", + "Version": "1.0.78", + "Mean": "515.2 ms", + "Error": "6.38 ms", + "StdDev": "5.97 ms", + "Median": "514.4 ms" + }, + { + "Method": "NUnit", + "Version": "4.4.0", + "Mean": "653.0 ms", + "Error": "10.31 ms", + "StdDev": "9.14 ms", + "Median": "651.9 ms" + }, + { + "Method": "MSTest", + "Version": "4.0.2", + "Mean": "625.0 ms", + "Error": "6.14 ms", + "StdDev": "5.44 ms", + "Median": "626.1 ms" + }, + { + "Method": "xUnit3", + "Version": "3.2.0", + "Mean": "705.5 ms", + "Error": "9.67 ms", + "StdDev": "8.57 ms", + "Median": "703.8 ms" + }, + { + "Method": "TUnit_AOT", + "Version": "1.0.78", + "Mean": "123.0 ms", + "Error": "0.32 ms", + "StdDev": "0.27 ms", + "Median": "123.0 ms" + } + ], + "DataDrivenTests": [ + { + "Method": "TUnit", + "Version": "1.0.78", + "Mean": "470.12 ms", + "Error": "7.116 ms", + "StdDev": "6.656 ms", + "Median": "468.90 ms" + }, + { + "Method": "NUnit", + "Version": "4.4.0", + "Mean": "517.39 ms", + "Error": "5.915 ms", + "StdDev": "5.243 ms", + "Median": "515.90 ms" + }, + { + "Method": "MSTest", + "Version": "4.0.2", + "Mean": "469.53 ms", + "Error": "9.076 ms", + "StdDev": "8.490 ms", + "Median": "468.35 ms" + }, + { + "Method": "xUnit3", + "Version": "3.2.0", + "Mean": "559.23 ms", + "Error": "7.276 ms", + "StdDev": "6.806 ms", + "Median": "561.96 ms" + }, + { + "Method": "TUnit_AOT", + "Version": "1.0.78", + "Mean": "23.21 ms", + "Error": "0.231 ms", + "StdDev": "0.216 ms", + "Median": "23.24 ms" + } + ], + "MassiveParallelTests": [ { "Method": "TUnit", "Version": "1.0.78", @@ -47,6 +131,90 @@ "StdDev": "0.47 ms", "Median": "130.8 ms" } + ], + "MatrixTests": [ + { + "Method": "TUnit", + "Version": "1.0.78", + "Mean": "556.38 ms", + "Error": "7.869 ms", + "StdDev": "7.361 ms", + "Median": "555.90 ms" + }, + { + "Method": "NUnit", + "Version": "4.4.0", + "Mean": "1,545.79 ms", + "Error": "15.854 ms", + "StdDev": "14.829 ms", + "Median": "1,548.36 ms" + }, + { + "Method": "MSTest", + "Version": "4.0.2", + "Mean": "1,510.34 ms", + "Error": "13.046 ms", + "StdDev": "12.203 ms", + "Median": "1,509.61 ms" + }, + { + "Method": "xUnit3", + "Version": "3.2.0", + "Mean": "1,595.19 ms", + "Error": "10.701 ms", + "StdDev": "9.486 ms", + "Median": "1,596.19 ms" + }, + { + "Method": "TUnit_AOT", + "Version": "1.0.78", + "Mean": "78.81 ms", + "Error": "0.471 ms", + "StdDev": "0.441 ms", + "Median": "78.75 ms" + } + ], + "ScaleTests": [ + { + "Method": "TUnit", + "Version": "1.0.78", + "Mean": "488.96 ms", + "Error": "4.187 ms", + "StdDev": "3.711 ms", + "Median": "489.51 ms" + }, + { + "Method": "NUnit", + "Version": "4.4.0", + "Mean": "564.21 ms", + "Error": "7.978 ms", + "StdDev": "7.073 ms", + "Median": "564.16 ms" + }, + { + "Method": "MSTest", + "Version": "4.0.2", + "Mean": "496.03 ms", + "Error": "8.635 ms", + "StdDev": "7.211 ms", + "Median": "494.58 ms" + }, + { + "Method": "xUnit3", + "Version": "3.2.0", + "Mean": "578.53 ms", + "Error": "11.537 ms", + "StdDev": "10.227 ms", + "Median": "581.03 ms" + }, + { + "Method": "TUnit_AOT", + "Version": "1.0.78", + "Mean": "45.46 ms", + "Error": "1.385 ms", + "StdDev": "4.083 ms", + "Median": "45.85 ms" + } ] }, "build": { @@ -85,26 +253,10 @@ } ] }, - "comparisons": { - "results": { - "tunitMean": 580.4, - "tunitAOTMean": 130.8, - "vsXUnit": "0.01", - "vsNUnit": "0.00", - "vsMSTest": "0.00", - "aotSpeedup": "4.44" - } - }, - "averageSpeedups": { - "vsXUnit": "0.0", - "vsNUnit": "0.0", - "vsMSTest": "0.0", - "count": 1 - }, "stats": { - "runtimeCategories": 1, + "runtimeCategories": 5, "buildCategories": 1, - "totalBenchmarks": 2, - "lastUpdated": "2025-11-11T22:58:18.734Z" + "totalBenchmarks": 6, + "lastUpdated": "2025-11-11T23:54:26.121Z" } } \ No newline at end of file diff --git a/docs/static/benchmarks/summary.json b/docs/static/benchmarks/summary.json index d91d4c1bd0..13ed3fdef0 100644 --- a/docs/static/benchmarks/summary.json +++ b/docs/static/benchmarks/summary.json @@ -1,6 +1,10 @@ { "runtime": [ - "results" + "AsyncTests", + "DataDrivenTests", + "MassiveParallelTests", + "MatrixTests", + "ScaleTests" ], "build": [ "BuildTime" diff --git a/docs/yarn.lock b/docs/yarn.lock index e7b9f20ebd..884c7aca68 100644 --- a/docs/yarn.lock +++ b/docs/yarn.lock @@ -231,6 +231,19 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" +"@antfu/install-pkg@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz" + integrity sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ== + dependencies: + package-manager-detector "^1.3.0" + tinyexec "^1.0.1" + +"@antfu/utils@^9.2.0": + version "9.3.0" + resolved "https://registry.npmjs.org/@antfu/utils/-/utils-9.3.0.tgz" + integrity sha512-9hFT4RauhcUzqOE4f1+frMKLZrgNog5b06I7VmZQV1BkvwvqrbC8EBZf3L1eEL2AKb6rNKjER0sEvJiSP1FXEA== + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0", "@babel/code-frame@^7.26.2": version "7.26.2" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz" @@ -1137,6 +1150,43 @@ "@babel/helper-string-parser" "^7.25.9" "@babel/helper-validator-identifier" "^7.25.9" +"@braintree/sanitize-url@^7.1.1": + version "7.1.1" + resolved "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.1.tgz" + integrity sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw== + +"@chevrotain/cst-dts-gen@11.0.3": + version "11.0.3" + resolved "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz" + integrity sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ== + dependencies: + "@chevrotain/gast" "11.0.3" + "@chevrotain/types" "11.0.3" + lodash-es "4.17.21" + +"@chevrotain/gast@11.0.3": + version "11.0.3" + resolved "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz" + integrity sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q== + dependencies: + "@chevrotain/types" "11.0.3" + lodash-es "4.17.21" + +"@chevrotain/regexp-to-ast@11.0.3": + version "11.0.3" + resolved "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz" + integrity sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA== + +"@chevrotain/types@11.0.3": + version "11.0.3" + resolved "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz" + integrity sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ== + +"@chevrotain/utils@11.0.3": + version "11.0.3" + resolved "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz" + integrity sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ== + "@colors/colors@1.5.0": version "1.5.0" resolved "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz" @@ -1873,6 +1923,19 @@ tslib "^2.6.0" utility-types "^3.10.0" +"@docusaurus/theme-mermaid@^3.9.2": + version "3.9.2" + resolved "https://registry.npmjs.org/@docusaurus/theme-mermaid/-/theme-mermaid-3.9.2.tgz" + integrity sha512-5vhShRDq/ntLzdInsQkTdoKWSzw8d1jB17sNPYhA/KvYYFXfuVEGHLM6nrf8MFbV8TruAHDG21Fn3W4lO8GaDw== + dependencies: + "@docusaurus/core" "3.9.2" + "@docusaurus/module-type-aliases" "3.9.2" + "@docusaurus/theme-common" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" + mermaid ">=11.6.0" + tslib "^2.6.0" + "@docusaurus/theme-search-algolia@3.9.2": version "3.9.2" resolved "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.9.2.tgz" @@ -1985,6 +2048,25 @@ dependencies: "@hapi/hoek" "^9.0.0" +"@iconify/types@^2.0.0": + version "2.0.0" + resolved "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz" + integrity sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg== + +"@iconify/utils@^3.0.1": + version "3.0.2" + resolved "https://registry.npmjs.org/@iconify/utils/-/utils-3.0.2.tgz" + integrity sha512-EfJS0rLfVuRuJRn4psJHtK2A9TqVnkxPpHY6lYHiB9+8eSuudsxbwMiavocG45ujOo6FJ+CIRlRnlOGinzkaGQ== + dependencies: + "@antfu/install-pkg" "^1.1.0" + "@antfu/utils" "^9.2.0" + "@iconify/types" "^2.0.0" + debug "^4.4.1" + globals "^15.15.0" + kolorist "^1.8.0" + local-pkg "^1.1.1" + mlly "^1.7.4" + "@jest/schemas@^29.6.3": version "29.6.3" resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz" @@ -2129,6 +2211,13 @@ dependencies: "@types/mdx" "^2.0.0" +"@mermaid-js/parser@^0.6.3": + version "0.6.3" + resolved "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.6.3.tgz" + integrity sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA== + dependencies: + langium "3.3.1" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" @@ -2382,6 +2471,216 @@ dependencies: "@types/node" "*" +"@types/d3-array@*": + version "3.2.2" + resolved "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz" + integrity sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw== + +"@types/d3-axis@*": + version "3.0.6" + resolved "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz" + integrity sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw== + dependencies: + "@types/d3-selection" "*" + +"@types/d3-brush@*": + version "3.0.6" + resolved "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz" + integrity sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A== + dependencies: + "@types/d3-selection" "*" + +"@types/d3-chord@*": + version "3.0.6" + resolved "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz" + integrity sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg== + +"@types/d3-color@*": + version "3.1.3" + resolved "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz" + integrity sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A== + +"@types/d3-contour@*": + version "3.0.6" + resolved "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz" + integrity sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg== + dependencies: + "@types/d3-array" "*" + "@types/geojson" "*" + +"@types/d3-delaunay@*": + version "6.0.4" + resolved "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz" + integrity sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw== + +"@types/d3-dispatch@*": + version "3.0.7" + resolved "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz" + integrity sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA== + +"@types/d3-drag@*": + version "3.0.7" + resolved "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz" + integrity sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ== + dependencies: + "@types/d3-selection" "*" + +"@types/d3-dsv@*": + version "3.0.7" + resolved "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz" + integrity sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g== + +"@types/d3-ease@*": + version "3.0.2" + resolved "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz" + integrity sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA== + +"@types/d3-fetch@*": + version "3.0.7" + resolved "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz" + integrity sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA== + dependencies: + "@types/d3-dsv" "*" + +"@types/d3-force@*": + version "3.0.10" + resolved "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz" + integrity sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw== + +"@types/d3-format@*": + version "3.0.4" + resolved "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz" + integrity sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g== + +"@types/d3-geo@*": + version "3.1.0" + resolved "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz" + integrity sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ== + dependencies: + "@types/geojson" "*" + +"@types/d3-hierarchy@*": + version "3.1.7" + resolved "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz" + integrity sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg== + +"@types/d3-interpolate@*": + version "3.0.4" + resolved "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz" + integrity sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA== + dependencies: + "@types/d3-color" "*" + +"@types/d3-path@*": + version "3.1.1" + resolved "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz" + integrity sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg== + +"@types/d3-polygon@*": + version "3.0.2" + resolved "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz" + integrity sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA== + +"@types/d3-quadtree@*": + version "3.0.6" + resolved "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz" + integrity sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg== + +"@types/d3-random@*": + version "3.0.3" + resolved "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz" + integrity sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ== + +"@types/d3-scale-chromatic@*": + version "3.1.0" + resolved "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz" + integrity sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ== + +"@types/d3-scale@*": + version "4.0.9" + resolved "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz" + integrity sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw== + dependencies: + "@types/d3-time" "*" + +"@types/d3-selection@*": + version "3.0.11" + resolved "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz" + integrity sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w== + +"@types/d3-shape@*": + version "3.1.7" + resolved "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz" + integrity sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg== + dependencies: + "@types/d3-path" "*" + +"@types/d3-time-format@*": + version "4.0.3" + resolved "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz" + integrity sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg== + +"@types/d3-time@*": + version "3.0.4" + resolved "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz" + integrity sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g== + +"@types/d3-timer@*": + version "3.0.2" + resolved "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz" + integrity sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw== + +"@types/d3-transition@*": + version "3.0.9" + resolved "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz" + integrity sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg== + dependencies: + "@types/d3-selection" "*" + +"@types/d3-zoom@*": + version "3.0.8" + resolved "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz" + integrity sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw== + dependencies: + "@types/d3-interpolate" "*" + "@types/d3-selection" "*" + +"@types/d3@^7.4.3": + version "7.4.3" + resolved "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz" + integrity sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww== + dependencies: + "@types/d3-array" "*" + "@types/d3-axis" "*" + "@types/d3-brush" "*" + "@types/d3-chord" "*" + "@types/d3-color" "*" + "@types/d3-contour" "*" + "@types/d3-delaunay" "*" + "@types/d3-dispatch" "*" + "@types/d3-drag" "*" + "@types/d3-dsv" "*" + "@types/d3-ease" "*" + "@types/d3-fetch" "*" + "@types/d3-force" "*" + "@types/d3-format" "*" + "@types/d3-geo" "*" + "@types/d3-hierarchy" "*" + "@types/d3-interpolate" "*" + "@types/d3-path" "*" + "@types/d3-polygon" "*" + "@types/d3-quadtree" "*" + "@types/d3-random" "*" + "@types/d3-scale" "*" + "@types/d3-scale-chromatic" "*" + "@types/d3-selection" "*" + "@types/d3-shape" "*" + "@types/d3-time" "*" + "@types/d3-time-format" "*" + "@types/d3-timer" "*" + "@types/d3-transition" "*" + "@types/d3-zoom" "*" + "@types/debug@^4.0.0": version "4.1.12" resolved "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz" @@ -2437,6 +2736,11 @@ "@types/qs" "*" "@types/serve-static" "*" +"@types/geojson@*": + version "7946.0.16" + resolved "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz" + integrity sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg== + "@types/gtag.js@^0.0.12": version "0.0.12" resolved "https://registry.npmjs.org/@types/gtag.js/-/gtag.js-0.0.12.tgz" @@ -2632,6 +2936,11 @@ dependencies: "@types/node" "*" +"@types/trusted-types@^2.0.7": + version "2.0.7" + resolved "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz" + integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== + "@types/unist@*", "@types/unist@^3.0.0": version "3.0.2" resolved "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz" @@ -2815,10 +3124,10 @@ acorn-walk@^8.0.0: resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz" integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== -"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.0.0, acorn@^8.0.4, acorn@^8.14.0, acorn@^8.8.2: - version "8.14.0" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz" - integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== +"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.0.0, acorn@^8.0.4, acorn@^8.14.0, acorn@^8.15.0, acorn@^8.8.2: + version "8.15.0" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz" + integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== address@^1.0.1: version "1.2.2" @@ -3307,6 +3616,25 @@ cheerio@1.0.0-rc.12: parse5 "^7.0.0" parse5-htmlparser2-tree-adapter "^7.0.0" +chevrotain-allstar@~0.3.0: + version "0.3.1" + resolved "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz" + integrity sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw== + dependencies: + lodash-es "^4.17.21" + +chevrotain@^11.0.0, chevrotain@~11.0.3: + version "11.0.3" + resolved "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz" + integrity sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw== + dependencies: + "@chevrotain/cst-dts-gen" "11.0.3" + "@chevrotain/gast" "11.0.3" + "@chevrotain/regexp-to-ast" "11.0.3" + "@chevrotain/types" "11.0.3" + "@chevrotain/utils" "11.0.3" + lodash-es "4.17.21" + chokidar@^3.5.3, chokidar@^3.6.0: version "3.6.0" resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz" @@ -3434,6 +3762,11 @@ commander@^8.3.0: resolved "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz" integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== +commander@7: + version "7.2.0" + resolved "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + common-path-prefix@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz" @@ -3464,6 +3797,16 @@ concat-map@0.0.1: resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +confbox@^0.1.8: + version "0.1.8" + resolved "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz" + integrity sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w== + +confbox@^0.2.2: + version "0.2.2" + resolved "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz" + integrity sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ== + config-chain@^1.1.11: version "1.1.13" resolved "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz" @@ -3559,6 +3902,20 @@ core-util-is@~1.0.0: resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== +cose-base@^1.0.0: + version "1.0.3" + resolved "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz" + integrity sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg== + dependencies: + layout-base "^1.0.0" + +cose-base@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz" + integrity sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g== + dependencies: + layout-base "^2.0.0" + cosmiconfig@^8.1.3, cosmiconfig@^8.3.5: version "8.3.6" resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz" @@ -3764,17 +4121,320 @@ csstype@^3.0.2: resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== +cytoscape-cose-bilkent@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz" + integrity sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ== + dependencies: + cose-base "^1.0.0" + +cytoscape-fcose@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz" + integrity sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ== + dependencies: + cose-base "^2.2.0" + +cytoscape@^3.2.0, cytoscape@^3.29.3: + version "3.33.1" + resolved "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz" + integrity sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ== + +d3-array@^3.2.0, "d3-array@2 - 3", "d3-array@2.10.0 - 3", "d3-array@2.5.0 - 3", d3-array@3: + version "3.2.4" + resolved "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz" + integrity sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg== + dependencies: + internmap "1 - 2" + +"d3-array@1 - 2": + version "2.12.1" + resolved "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz" + integrity sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ== + dependencies: + internmap "^1.0.0" + +d3-axis@3: + version "3.0.0" + resolved "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz" + integrity sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw== + +d3-brush@3: + version "3.0.0" + resolved "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz" + integrity sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ== + dependencies: + d3-dispatch "1 - 3" + d3-drag "2 - 3" + d3-interpolate "1 - 3" + d3-selection "3" + d3-transition "3" + +d3-chord@3: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz" + integrity sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g== + dependencies: + d3-path "1 - 3" + +"d3-color@1 - 3", d3-color@3: + version "3.1.0" + resolved "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz" + integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA== + +d3-contour@4: + version "4.0.2" + resolved "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz" + integrity sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA== + dependencies: + d3-array "^3.2.0" + +d3-delaunay@6: + version "6.0.4" + resolved "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz" + integrity sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A== + dependencies: + delaunator "5" + +"d3-dispatch@1 - 3", d3-dispatch@3: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz" + integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg== + +"d3-drag@2 - 3", d3-drag@3: + version "3.0.0" + resolved "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz" + integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg== + dependencies: + d3-dispatch "1 - 3" + d3-selection "3" + +"d3-dsv@1 - 3", d3-dsv@3: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz" + integrity sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q== + dependencies: + commander "7" + iconv-lite "0.6" + rw "1" + +"d3-ease@1 - 3", d3-ease@3: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz" + integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w== + +d3-fetch@3: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz" + integrity sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw== + dependencies: + d3-dsv "1 - 3" + +d3-force@3: + version "3.0.0" + resolved "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz" + integrity sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg== + dependencies: + d3-dispatch "1 - 3" + d3-quadtree "1 - 3" + d3-timer "1 - 3" + +"d3-format@1 - 3", d3-format@3: + version "3.1.0" + resolved "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz" + integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA== + +d3-geo@3: + version "3.1.1" + resolved "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz" + integrity sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q== + dependencies: + d3-array "2.5.0 - 3" + +d3-hierarchy@3: + version "3.1.2" + resolved "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz" + integrity sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA== + +"d3-interpolate@1 - 3", "d3-interpolate@1.2.0 - 3", d3-interpolate@3: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz" + integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g== + dependencies: + d3-color "1 - 3" + +d3-path@^3.1.0, "d3-path@1 - 3", d3-path@3: + version "3.1.0" + resolved "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz" + integrity sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ== + +d3-path@1: + version "1.0.9" + resolved "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz" + integrity sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg== + +d3-polygon@3: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz" + integrity sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg== + +"d3-quadtree@1 - 3", d3-quadtree@3: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz" + integrity sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw== + +d3-random@3: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz" + integrity sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ== + +d3-sankey@^0.12.3: + version "0.12.3" + resolved "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz" + integrity sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ== + dependencies: + d3-array "1 - 2" + d3-shape "^1.2.0" + +d3-scale-chromatic@3: + version "3.1.0" + resolved "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz" + integrity sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ== + dependencies: + d3-color "1 - 3" + d3-interpolate "1 - 3" + +d3-scale@4: + version "4.0.2" + resolved "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz" + integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ== + dependencies: + d3-array "2.10.0 - 3" + d3-format "1 - 3" + d3-interpolate "1.2.0 - 3" + d3-time "2.1.1 - 3" + d3-time-format "2 - 4" + +"d3-selection@2 - 3", d3-selection@3: + version "3.0.0" + resolved "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz" + integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ== + +d3-shape@^1.2.0: + version "1.3.7" + resolved "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz" + integrity sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw== + dependencies: + d3-path "1" + +d3-shape@3: + version "3.2.0" + resolved "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz" + integrity sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA== + dependencies: + d3-path "^3.1.0" + +"d3-time-format@2 - 4", d3-time-format@4: + version "4.1.0" + resolved "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz" + integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg== + dependencies: + d3-time "1 - 3" + +"d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@3: + version "3.1.0" + resolved "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz" + integrity sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q== + dependencies: + d3-array "2 - 3" + +"d3-timer@1 - 3", d3-timer@3: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz" + integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA== + +"d3-transition@2 - 3", d3-transition@3: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz" + integrity sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w== + dependencies: + d3-color "1 - 3" + d3-dispatch "1 - 3" + d3-ease "1 - 3" + d3-interpolate "1 - 3" + d3-timer "1 - 3" + +d3-zoom@3: + version "3.0.0" + resolved "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz" + integrity sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw== + dependencies: + d3-dispatch "1 - 3" + d3-drag "2 - 3" + d3-interpolate "1 - 3" + d3-selection "2 - 3" + d3-transition "2 - 3" + +d3@^7.9.0: + version "7.9.0" + resolved "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz" + integrity sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA== + dependencies: + d3-array "3" + d3-axis "3" + d3-brush "3" + d3-chord "3" + d3-color "3" + d3-contour "4" + d3-delaunay "6" + d3-dispatch "3" + d3-drag "3" + d3-dsv "3" + d3-ease "3" + d3-fetch "3" + d3-force "3" + d3-format "3" + d3-geo "3" + d3-hierarchy "3" + d3-interpolate "3" + d3-path "3" + d3-polygon "3" + d3-quadtree "3" + d3-random "3" + d3-scale "4" + d3-scale-chromatic "3" + d3-selection "3" + d3-shape "3" + d3-time "3" + d3-time-format "4" + d3-timer "3" + d3-transition "3" + d3-zoom "3" + +dagre-d3-es@7.0.13: + version "7.0.13" + resolved "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.13.tgz" + integrity sha512-efEhnxpSuwpYOKRm/L5KbqoZmNNukHa/Flty4Wp62JRvgH2ojwVgPgdYyr4twpieZnyRDdIH7PY2mopX26+j2Q== + dependencies: + d3 "^7.9.0" + lodash-es "^4.17.21" + +dayjs@^1.11.18: + version "1.11.19" + resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz" + integrity sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw== + debounce@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz" integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== -debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@4: - version "4.3.4" - resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== +debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.4.1, debug@4: + version "4.4.3" + resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== dependencies: - ms "2.1.2" + ms "^2.1.3" debug@2.6.9: version "2.6.9" @@ -3853,6 +4513,13 @@ define-properties@^1.2.1: has-property-descriptors "^1.0.0" object-keys "^1.1.1" +delaunator@5: + version "5.0.1" + resolved "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz" + integrity sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw== + dependencies: + robust-predicates "^3.0.2" + depd@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" @@ -3951,6 +4618,13 @@ domhandler@^5.0.2, domhandler@^5.0.3: dependencies: domelementtype "^2.3.0" +dompurify@^3.2.5: + version "3.3.0" + resolved "https://registry.npmjs.org/dompurify/-/dompurify-3.3.0.tgz" + integrity sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ== + optionalDependencies: + "@types/trusted-types" "^2.0.7" + domutils@^2.5.2, domutils@^2.8.0: version "2.8.0" resolved "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz" @@ -4284,6 +4958,11 @@ express@^4.21.2: utils-merge "1.0.1" vary "~1.1.2" +exsolve@^1.0.7: + version "1.0.8" + resolved "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz" + integrity sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA== + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz" @@ -4512,6 +5191,11 @@ globals@^11.1.0: resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== +globals@^15.15.0: + version "15.15.0" + resolved "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz" + integrity sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg== + globby@^11.1.0: version "11.1.0" resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" @@ -4586,6 +5270,11 @@ gzip-size@^6.0.0: dependencies: duplexer "^0.1.2" +hachure-fill@^0.5.2: + version "0.5.2" + resolved "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz" + integrity sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg== + handle-thing@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz" @@ -4926,6 +5615,13 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" +iconv-lite@0.6: + version "0.6.3" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + icss-utils@^5.0.0, icss-utils@^5.1.0: version "5.1.0" resolved "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz" @@ -5004,6 +5700,16 @@ inline-style-parser@0.2.2: resolved "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.2.tgz" integrity sha512-EcKzdTHVe8wFVOGEYXiW9WmJXPjqi1T+234YpJr98RiFYKHV3cdy1+3mkTE+KHTHxFFLH51SfaGOoUdW+v7ViQ== +internmap@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz" + integrity sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw== + +"internmap@1 - 2": + version "2.0.3" + resolved "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz" + integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg== + invariant@^2.2.4: version "2.2.4" resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz" @@ -5336,6 +6042,13 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" +katex@^0.16.22: + version "0.16.25" + resolved "https://registry.npmjs.org/katex/-/katex-0.16.25.tgz" + integrity sha512-woHRUZ/iF23GBP1dkDQMh1QBad9dmr8/PAwNA54VrSOVYgI12MAcE14TqnDdQOdzyEonGzMepYnqBMYdsoAr8Q== + dependencies: + commander "^8.3.0" + keyv@^4.5.3: version "4.5.4" resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz" @@ -5343,6 +6056,11 @@ keyv@^4.5.3: dependencies: json-buffer "3.0.1" +khroma@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz" + integrity sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw== + kind-of@^6.0.0, kind-of@^6.0.2: version "6.0.3" resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" @@ -5353,6 +6071,22 @@ kleur@^3.0.3: resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +kolorist@^1.8.0: + version "1.8.0" + resolved "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz" + integrity sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ== + +langium@3.3.1: + version "3.3.1" + resolved "https://registry.npmjs.org/langium/-/langium-3.3.1.tgz" + integrity sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w== + dependencies: + chevrotain "~11.0.3" + chevrotain-allstar "~0.3.0" + vscode-languageserver "~9.0.1" + vscode-languageserver-textdocument "~1.0.11" + vscode-uri "~3.0.8" + latest-version@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz" @@ -5368,6 +6102,16 @@ launch-editor@^2.6.1: picocolors "^1.1.1" shell-quote "^1.8.3" +layout-base@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz" + integrity sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg== + +layout-base@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz" + integrity sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg== + leven@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" @@ -5397,6 +6141,15 @@ loader-utils@^2.0.0: emojis-list "^3.0.0" json5 "^2.1.2" +local-pkg@^1.1.1: + version "1.1.2" + resolved "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.2.tgz" + integrity sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A== + dependencies: + mlly "^1.7.4" + pkg-types "^2.3.0" + quansync "^0.2.11" + locate-path@^7.1.0: version "7.2.0" resolved "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz" @@ -5404,6 +6157,11 @@ locate-path@^7.1.0: dependencies: p-locate "^6.0.0" +lodash-es@^4.17.21, lodash-es@4.17.21: + version "4.17.21" + resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz" + integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== + lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz" @@ -5479,7 +6237,7 @@ markdown-table@^3.0.0: resolved "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz" integrity sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw== -marked@^16.3.0: +marked@^16.2.1, marked@^16.3.0: version "16.3.0" resolved "https://registry.npmjs.org/marked/-/marked-16.3.0.tgz" integrity sha512-K3UxuKu6l6bmA5FUwYho8CfJBlsUWAooKtdGgMcERSpF7gcBUrCGsLH7wDaaNOzwq18JzSUDyoEb/YsrqMac3w== @@ -5743,6 +6501,32 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== +mermaid@>=11.6.0: + version "11.12.1" + resolved "https://registry.npmjs.org/mermaid/-/mermaid-11.12.1.tgz" + integrity sha512-UlIZrRariB11TY1RtTgUWp65tphtBv4CSq7vyS2ZZ2TgoMjs2nloq+wFqxiwcxlhHUvs7DPGgMjs2aeQxz5h9g== + dependencies: + "@braintree/sanitize-url" "^7.1.1" + "@iconify/utils" "^3.0.1" + "@mermaid-js/parser" "^0.6.3" + "@types/d3" "^7.4.3" + cytoscape "^3.29.3" + cytoscape-cose-bilkent "^4.1.0" + cytoscape-fcose "^2.2.0" + d3 "^7.9.0" + d3-sankey "^0.12.3" + dagre-d3-es "7.0.13" + dayjs "^1.11.18" + dompurify "^3.2.5" + katex "^0.16.22" + khroma "^2.1.0" + lodash-es "^4.17.21" + marked "^16.2.1" + roughjs "^4.6.6" + stylis "^4.3.6" + ts-dedent "^2.2.0" + uuid "^11.1.0" + methods@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" @@ -6272,26 +7056,31 @@ minimist@^1.2.0: resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== +mlly@^1.7.4: + version "1.8.0" + resolved "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz" + integrity sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g== + dependencies: + acorn "^8.15.0" + pathe "^2.0.3" + pkg-types "^1.3.1" + ufo "^1.6.1" + mrmime@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz" integrity sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw== +ms@^2.1.3, ms@2.1.3: + version "2.1.3" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + ms@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== -ms@2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@2.1.3: - version "2.1.3" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - multicast-dns@^7.2.5: version "7.2.5" resolved "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz" @@ -6523,6 +7312,11 @@ package-json@^8.1.0: registry-url "^6.0.0" semver "^7.3.7" +package-manager-detector@^1.3.0: + version "1.5.0" + resolved "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.5.0.tgz" + integrity sha512-uBj69dVlYe/+wxj8JOpr97XfsxH/eumMt6HqjNTmJDf/6NO9s+0uxeOneIz3AsPt2m6y9PqzDzd3ATcU17MNfw== + param-case@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz" @@ -6595,6 +7389,11 @@ pascal-case@^3.1.2: no-case "^3.0.4" tslib "^2.0.3" +path-data-parser@^0.1.0, path-data-parser@0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz" + integrity sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w== + path-exists@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz" @@ -6637,6 +7436,11 @@ path-type@^4.0.0: resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pathe@^2.0.1, pathe@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz" + integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== + periscopic@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz" @@ -6663,6 +7467,37 @@ pkg-dir@^7.0.0: dependencies: find-up "^6.3.0" +pkg-types@^1.3.1: + version "1.3.1" + resolved "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz" + integrity sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ== + dependencies: + confbox "^0.1.8" + mlly "^1.7.4" + pathe "^2.0.1" + +pkg-types@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz" + integrity sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig== + dependencies: + confbox "^0.2.2" + exsolve "^1.0.7" + pathe "^2.0.3" + +points-on-curve@^0.2.0, points-on-curve@0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz" + integrity sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A== + +points-on-path@^0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz" + integrity sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g== + dependencies: + path-data-parser "0.1.0" + points-on-curve "0.2.0" + postcss-attribute-case-insensitive@^7.0.1: version "7.0.1" resolved "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-7.0.1.tgz" @@ -7305,6 +8140,11 @@ qs@6.13.0: dependencies: side-channel "^1.0.6" +quansync@^0.2.11: + version "0.2.11" + resolved "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz" + integrity sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" @@ -7702,6 +8542,21 @@ reusify@^1.0.4: resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +robust-predicates@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz" + integrity sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg== + +roughjs@^4.6.6: + version "4.6.6" + resolved "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz" + integrity sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ== + dependencies: + hachure-fill "^0.5.2" + path-data-parser "^0.1.0" + points-on-curve "^0.2.0" + points-on-path "^0.2.1" + rtlcss@^4.1.0: version "4.1.1" resolved "https://registry.npmjs.org/rtlcss/-/rtlcss-4.1.1.tgz" @@ -7724,6 +8579,11 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +rw@1: + version "1.3.3" + resolved "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz" + integrity sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ== + safe-buffer@^5.1.0, safe-buffer@>=5.1.0, safe-buffer@~5.2.0, safe-buffer@5.2.1: version "5.2.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" @@ -7739,7 +8599,7 @@ safe-buffer@5.1.2: resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -"safer-buffer@>= 2.1.2 < 3": +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -8227,6 +9087,11 @@ stylehacks@^6.1.1: browserslist "^4.23.0" postcss-selector-parser "^6.0.16" +stylis@^4.3.6: + version "4.3.6" + resolved "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz" + integrity sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ== + supports-color@^7.1.0: version "7.2.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" @@ -8323,6 +9188,11 @@ tiny-warning@^1.0.0: resolved "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== +tinyexec@^1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz" + integrity sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg== + tinypool@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz" @@ -8360,6 +9230,11 @@ trough@^2.0.0: resolved "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz" integrity sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw== +ts-dedent@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz" + integrity sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ== + tslib@^2, tslib@^2.0.0, tslib@^2.0.3, tslib@^2.6.0, tslib@2: version "2.6.2" resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz" @@ -8400,6 +9275,11 @@ typescript@>=4.9.5, typescript@~5.9.0: resolved "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz" integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== +ufo@^1.6.1: + version "1.6.1" + resolved "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz" + integrity sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA== + undici-types@~5.26.4: version "5.26.5" resolved "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz" @@ -8585,6 +9465,11 @@ utils-merge@1.0.1: resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== +uuid@^11.1.0: + version "11.1.0" + resolved "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz" + integrity sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A== + uuid@^8.3.2: version "8.3.2" resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" @@ -8625,6 +9510,41 @@ vfile@^6.0.0, vfile@^6.0.1: unist-util-stringify-position "^4.0.0" vfile-message "^4.0.0" +vscode-jsonrpc@8.2.0: + version "8.2.0" + resolved "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz" + integrity sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA== + +vscode-languageserver-protocol@3.17.5: + version "3.17.5" + resolved "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz" + integrity sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg== + dependencies: + vscode-jsonrpc "8.2.0" + vscode-languageserver-types "3.17.5" + +vscode-languageserver-textdocument@~1.0.11: + version "1.0.12" + resolved "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz" + integrity sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA== + +vscode-languageserver-types@3.17.5: + version "3.17.5" + resolved "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz" + integrity sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg== + +vscode-languageserver@~9.0.1: + version "9.0.1" + resolved "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz" + integrity sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g== + dependencies: + vscode-languageserver-protocol "3.17.5" + +vscode-uri@~3.0.8: + version "3.0.8" + resolved "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz" + integrity sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw== + watchpack@^2.4.1: version "2.4.2" resolved "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz" From c265d8c74053fcfa4d1245cb826f8695e597552c Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 12 Nov 2025 00:03:47 +0000 Subject: [PATCH 38/62] chore(deps): update dependency polyfill to v9 (#3791) Co-authored-by: Renovate Bot --- TUnit.Core/TUnit.Core.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TUnit.Core/TUnit.Core.targets b/TUnit.Core/TUnit.Core.targets index 6dd52bbb6b..f822718364 100644 --- a/TUnit.Core/TUnit.Core.targets +++ b/TUnit.Core/TUnit.Core.targets @@ -8,7 +8,7 @@ - <_TUnitPolyfillVersion>8.9.1 + <_TUnitPolyfillVersion>9.0.0 <_TUnitNeedsPolyfill Condition="'$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'netstandard2.1' or '$(TargetFrameworkIdentifier)' == '.NETFramework'">true From f9451b5d6be4b14eb029a35886fa6beb7b981d29 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 12 Nov 2025 00:04:12 +0000 Subject: [PATCH 39/62] chore: update benchmark results (#3793) --- docs/docs/benchmarks/index.md | 112 ++--------- docs/static/benchmarks/historical.json | 4 + docs/static/benchmarks/latest.json | 250 ++++--------------------- docs/static/benchmarks/summary.json | 8 +- 4 files changed, 63 insertions(+), 311 deletions(-) diff --git a/docs/docs/benchmarks/index.md b/docs/docs/benchmarks/index.md index 26b2a27508..b10c0e9d1f 100644 --- a/docs/docs/benchmarks/index.md +++ b/docs/docs/benchmarks/index.md @@ -7,7 +7,7 @@ sidebar_position: 1 # Performance Benchmarks :::info Last Updated -These benchmarks were automatically generated on **2025-11-11** from the latest CI run. +These benchmarks were automatically generated on **2025-11-12** from the latest CI run. **Environment:** Ubuntu Latest • .NET SDK 10.0.100 ::: @@ -15,103 +15,23 @@ These benchmarks were automatically generated on **2025-11-11** from the latest ## šŸš€ Runtime Performance -### AsyncTests +### results | Framework | Version | Mean | Median | StdDev | |-----------|---------|------|--------|--------| -| **TUnit** | 1.0.78 | 515.2 ms | 514.4 ms | 5.97 ms | -| NUnit | 4.4.0 | 653.0 ms | 651.9 ms | 9.14 ms | -| MSTest | 4.0.2 | 625.0 ms | 626.1 ms | 5.44 ms | -| xUnit3 | 3.2.0 | 705.5 ms | 703.8 ms | 8.57 ms | -| **TUnit (AOT)** | 1.0.78 | 123.0 ms | 123.0 ms | 0.27 ms | +| **TUnit** | 1.0.78 | 495.66 ms | 496.75 ms | 3.288 ms | +| NUnit | 4.4.0 | 533.59 ms | 534.97 ms | 10.858 ms | +| MSTest | 4.0.2 | 505.82 ms | 502.05 ms | 13.674 ms | +| xUnit3 | 3.2.0 | 583.29 ms | 580.92 ms | 10.011 ms | +| **TUnit (AOT)** | 1.0.78 | 25.84 ms | 25.90 ms | 0.241 ms | ```mermaid %%{init: {'theme':'base'}}%% xychart-beta - title "AsyncTests Performance Comparison" + title "results Performance Comparison" x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"] - y-axis "Time (ms)" 0 --> 847 - bar [515.2, 653, 625, 705.5, 123] -``` - - -### DataDrivenTests - -| Framework | Version | Mean | Median | StdDev | -|-----------|---------|------|--------|--------| -| **TUnit** | 1.0.78 | 470.12 ms | 468.90 ms | 6.656 ms | -| NUnit | 4.4.0 | 517.39 ms | 515.90 ms | 5.243 ms | -| MSTest | 4.0.2 | 469.53 ms | 468.35 ms | 8.490 ms | -| xUnit3 | 3.2.0 | 559.23 ms | 561.96 ms | 6.806 ms | -| **TUnit (AOT)** | 1.0.78 | 23.21 ms | 23.24 ms | 0.216 ms | - -```mermaid -%%{init: {'theme':'base'}}%% -xychart-beta - title "DataDrivenTests Performance Comparison" - x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"] - y-axis "Time (ms)" 0 --> 672 - bar [470.12, 517.39, 469.53, 559.23, 23.21] -``` - - -### MassiveParallelTests - -| Framework | Version | Mean | Median | StdDev | -|-----------|---------|------|--------|--------| -| **TUnit** | 1.0.78 | 580.4 ms | 578.3 ms | 7.38 ms | -| NUnit | 4.4.0 | 1,211.6 ms | 1,209.8 ms | 9.40 ms | -| MSTest | 4.0.2 | 2,995.4 ms | 2,996.6 ms | 7.68 ms | -| xUnit3 | 3.2.0 | 3,086.8 ms | 3,086.9 ms | 11.28 ms | -| **TUnit (AOT)** | 1.0.78 | 130.8 ms | 130.8 ms | 0.47 ms | - -```mermaid -%%{init: {'theme':'base'}}%% -xychart-beta - title "MassiveParallelTests Performance Comparison" - x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"] - y-axis "Time (ms)" 0 --> 3705 - bar [580.4, 1211.6, 2995.4, 3086.8, 130.8] -``` - - -### MatrixTests - -| Framework | Version | Mean | Median | StdDev | -|-----------|---------|------|--------|--------| -| **TUnit** | 1.0.78 | 556.38 ms | 555.90 ms | 7.361 ms | -| NUnit | 4.4.0 | 1,545.79 ms | 1,548.36 ms | 14.829 ms | -| MSTest | 4.0.2 | 1,510.34 ms | 1,509.61 ms | 12.203 ms | -| xUnit3 | 3.2.0 | 1,595.19 ms | 1,596.19 ms | 9.486 ms | -| **TUnit (AOT)** | 1.0.78 | 78.81 ms | 78.75 ms | 0.441 ms | - -```mermaid -%%{init: {'theme':'base'}}%% -xychart-beta - title "MatrixTests Performance Comparison" - x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"] - y-axis "Time (ms)" 0 --> 1915 - bar [556.38, 1545.79, 1510.34, 1595.19, 78.81] -``` - - -### ScaleTests - -| Framework | Version | Mean | Median | StdDev | -|-----------|---------|------|--------|--------| -| **TUnit** | 1.0.78 | 488.96 ms | 489.51 ms | 3.711 ms | -| NUnit | 4.4.0 | 564.21 ms | 564.16 ms | 7.073 ms | -| MSTest | 4.0.2 | 496.03 ms | 494.58 ms | 7.211 ms | -| xUnit3 | 3.2.0 | 578.53 ms | 581.03 ms | 10.227 ms | -| **TUnit (AOT)** | 1.0.78 | 45.46 ms | 45.85 ms | 4.083 ms | - -```mermaid -%%{init: {'theme':'base'}}%% -xychart-beta - title "ScaleTests Performance Comparison" - x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"] - y-axis "Time (ms)" 0 --> 695 - bar [488.96, 564.21, 496.03, 578.53, 45.46] + y-axis "Time (ms)" 0 --> 700 + bar [495.66, 533.59, 505.82, 583.29, 25.84] ``` @@ -123,10 +43,10 @@ Compilation time comparison across frameworks: | Framework | Version | Mean | Median | StdDev | |-----------|---------|------|--------|--------| -| **TUnit** | 1.0.78 | 1.791 s | 1.788 s | 0.0257 s | -| Build_NUnit | 4.4.0 | 1.560 s | 1.565 s | 0.0213 s | -| Build_MSTest | 4.0.2 | 1.652 s | 1.656 s | 0.0123 s | -| Build_xUnit3 | 3.2.0 | 1.546 s | 1.551 s | 0.0203 s | +| **TUnit** | 1.0.78 | 2.025 s | 2.022 s | 0.0200 s | +| Build_NUnit | 4.4.0 | 1.630 s | 1.634 s | 0.0153 s | +| Build_MSTest | 4.0.2 | 1.712 s | 1.714 s | 0.0165 s | +| Build_xUnit3 | 3.2.0 | 1.600 s | 1.600 s | 0.0100 s | ```mermaid %%{init: {'theme':'base'}}%% @@ -134,7 +54,7 @@ xychart-beta title "Build Time Comparison" x-axis ["Build_TUnit", "Build_NUnit", "Build_MSTest", "Build_xUnit3"] y-axis "Time (s)" 0 --> 3 - bar [1.791, 1.56, 1.652, 1.546] + bar [2.025, 1.63, 1.712, 1.6] ``` @@ -189,4 +109,4 @@ These benchmarks run automatically daily via [GitHub Actions](https://github.com Each benchmark runs multiple iterations with statistical analysis to ensure accuracy. Results may vary based on hardware and test characteristics. ::: -*Last generated: 2025-11-11T23:54:26.122Z* +*Last generated: 2025-11-12T00:04:00.609Z* diff --git a/docs/static/benchmarks/historical.json b/docs/static/benchmarks/historical.json index 463b63da63..eb5613d754 100644 --- a/docs/static/benchmarks/historical.json +++ b/docs/static/benchmarks/historical.json @@ -6,5 +6,9 @@ { "date": "2025-11-11", "environment": "Ubuntu" + }, + { + "date": "2025-11-12", + "environment": "Ubuntu" } ] \ No newline at end of file diff --git a/docs/static/benchmarks/latest.json b/docs/static/benchmarks/latest.json index 9012f7c5d8..5c50198c2b 100644 --- a/docs/static/benchmarks/latest.json +++ b/docs/static/benchmarks/latest.json @@ -1,219 +1,51 @@ { - "timestamp": "2025-11-11T23:54:26.123Z", + "timestamp": "2025-11-12T00:04:00.609Z", "environment": { "benchmarkDotNetVersion": "BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat)", "sdk": ".NET SDK 10.0.100", "host": ".NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3" }, "categories": { - "AsyncTests": [ + "results": [ { "Method": "TUnit", "Version": "1.0.78", - "Mean": "515.2 ms", - "Error": "6.38 ms", - "StdDev": "5.97 ms", - "Median": "514.4 ms" + "Mean": "495.66 ms", + "Error": "3.515 ms", + "StdDev": "3.288 ms", + "Median": "496.75 ms" }, { "Method": "NUnit", "Version": "4.4.0", - "Mean": "653.0 ms", - "Error": "10.31 ms", - "StdDev": "9.14 ms", - "Median": "651.9 ms" + "Mean": "533.59 ms", + "Error": "10.573 ms", + "StdDev": "10.858 ms", + "Median": "534.97 ms" }, { "Method": "MSTest", "Version": "4.0.2", - "Mean": "625.0 ms", - "Error": "6.14 ms", - "StdDev": "5.44 ms", - "Median": "626.1 ms" + "Mean": "505.82 ms", + "Error": "9.990 ms", + "StdDev": "13.674 ms", + "Median": "502.05 ms" }, { "Method": "xUnit3", "Version": "3.2.0", - "Mean": "705.5 ms", - "Error": "9.67 ms", - "StdDev": "8.57 ms", - "Median": "703.8 ms" + "Mean": "583.29 ms", + "Error": "10.193 ms", + "StdDev": "10.011 ms", + "Median": "580.92 ms" }, { "Method": "TUnit_AOT", "Version": "1.0.78", - "Mean": "123.0 ms", - "Error": "0.32 ms", - "StdDev": "0.27 ms", - "Median": "123.0 ms" - } - ], - "DataDrivenTests": [ - { - "Method": "TUnit", - "Version": "1.0.78", - "Mean": "470.12 ms", - "Error": "7.116 ms", - "StdDev": "6.656 ms", - "Median": "468.90 ms" - }, - { - "Method": "NUnit", - "Version": "4.4.0", - "Mean": "517.39 ms", - "Error": "5.915 ms", - "StdDev": "5.243 ms", - "Median": "515.90 ms" - }, - { - "Method": "MSTest", - "Version": "4.0.2", - "Mean": "469.53 ms", - "Error": "9.076 ms", - "StdDev": "8.490 ms", - "Median": "468.35 ms" - }, - { - "Method": "xUnit3", - "Version": "3.2.0", - "Mean": "559.23 ms", - "Error": "7.276 ms", - "StdDev": "6.806 ms", - "Median": "561.96 ms" - }, - { - "Method": "TUnit_AOT", - "Version": "1.0.78", - "Mean": "23.21 ms", - "Error": "0.231 ms", - "StdDev": "0.216 ms", - "Median": "23.24 ms" - } - ], - "MassiveParallelTests": [ - { - "Method": "TUnit", - "Version": "1.0.78", - "Mean": "580.4 ms", - "Error": "8.32 ms", - "StdDev": "7.38 ms", - "Median": "578.3 ms" - }, - { - "Method": "NUnit", - "Version": "4.4.0", - "Mean": "1,211.6 ms", - "Error": "10.05 ms", - "StdDev": "9.40 ms", - "Median": "1,209.8 ms" - }, - { - "Method": "MSTest", - "Version": "4.0.2", - "Mean": "2,995.4 ms", - "Error": "8.66 ms", - "StdDev": "7.68 ms", - "Median": "2,996.6 ms" - }, - { - "Method": "xUnit3", - "Version": "3.2.0", - "Mean": "3,086.8 ms", - "Error": "12.06 ms", - "StdDev": "11.28 ms", - "Median": "3,086.9 ms" - }, - { - "Method": "TUnit_AOT", - "Version": "1.0.78", - "Mean": "130.8 ms", - "Error": "0.50 ms", - "StdDev": "0.47 ms", - "Median": "130.8 ms" - } - ], - "MatrixTests": [ - { - "Method": "TUnit", - "Version": "1.0.78", - "Mean": "556.38 ms", - "Error": "7.869 ms", - "StdDev": "7.361 ms", - "Median": "555.90 ms" - }, - { - "Method": "NUnit", - "Version": "4.4.0", - "Mean": "1,545.79 ms", - "Error": "15.854 ms", - "StdDev": "14.829 ms", - "Median": "1,548.36 ms" - }, - { - "Method": "MSTest", - "Version": "4.0.2", - "Mean": "1,510.34 ms", - "Error": "13.046 ms", - "StdDev": "12.203 ms", - "Median": "1,509.61 ms" - }, - { - "Method": "xUnit3", - "Version": "3.2.0", - "Mean": "1,595.19 ms", - "Error": "10.701 ms", - "StdDev": "9.486 ms", - "Median": "1,596.19 ms" - }, - { - "Method": "TUnit_AOT", - "Version": "1.0.78", - "Mean": "78.81 ms", - "Error": "0.471 ms", - "StdDev": "0.441 ms", - "Median": "78.75 ms" - } - ], - "ScaleTests": [ - { - "Method": "TUnit", - "Version": "1.0.78", - "Mean": "488.96 ms", - "Error": "4.187 ms", - "StdDev": "3.711 ms", - "Median": "489.51 ms" - }, - { - "Method": "NUnit", - "Version": "4.4.0", - "Mean": "564.21 ms", - "Error": "7.978 ms", - "StdDev": "7.073 ms", - "Median": "564.16 ms" - }, - { - "Method": "MSTest", - "Version": "4.0.2", - "Mean": "496.03 ms", - "Error": "8.635 ms", - "StdDev": "7.211 ms", - "Median": "494.58 ms" - }, - { - "Method": "xUnit3", - "Version": "3.2.0", - "Mean": "578.53 ms", - "Error": "11.537 ms", - "StdDev": "10.227 ms", - "Median": "581.03 ms" - }, - { - "Method": "TUnit_AOT", - "Version": "1.0.78", - "Mean": "45.46 ms", - "Error": "1.385 ms", - "StdDev": "4.083 ms", - "Median": "45.85 ms" + "Mean": "25.84 ms", + "Error": "0.272 ms", + "StdDev": "0.241 ms", + "Median": "25.90 ms" } ] }, @@ -222,41 +54,41 @@ { "Method": "Build_TUnit", "Version": "1.0.78", - "Mean": "1.791 s", - "Error": "0.0290 s", - "StdDev": "0.0257 s", - "Median": "1.788 s" + "Mean": "2.025 s", + "Error": "0.0225 s", + "StdDev": "0.0200 s", + "Median": "2.022 s" }, { "Method": "Build_NUnit", "Version": "4.4.0", - "Mean": "1.560 s", - "Error": "0.0240 s", - "StdDev": "0.0213 s", - "Median": "1.565 s" + "Mean": "1.630 s", + "Error": "0.0183 s", + "StdDev": "0.0153 s", + "Median": "1.634 s" }, { "Method": "Build_MSTest", "Version": "4.0.2", - "Mean": "1.652 s", - "Error": "0.0132 s", - "StdDev": "0.0123 s", - "Median": "1.656 s" + "Mean": "1.712 s", + "Error": "0.0177 s", + "StdDev": "0.0165 s", + "Median": "1.714 s" }, { "Method": "Build_xUnit3", "Version": "3.2.0", - "Mean": "1.546 s", - "Error": "0.0217 s", - "StdDev": "0.0203 s", - "Median": "1.551 s" + "Mean": "1.600 s", + "Error": "0.0120 s", + "StdDev": "0.0100 s", + "Median": "1.600 s" } ] }, "stats": { - "runtimeCategories": 5, + "runtimeCategories": 1, "buildCategories": 1, - "totalBenchmarks": 6, - "lastUpdated": "2025-11-11T23:54:26.121Z" + "totalBenchmarks": 2, + "lastUpdated": "2025-11-12T00:04:00.608Z" } } \ No newline at end of file diff --git a/docs/static/benchmarks/summary.json b/docs/static/benchmarks/summary.json index 13ed3fdef0..f3ba5050a2 100644 --- a/docs/static/benchmarks/summary.json +++ b/docs/static/benchmarks/summary.json @@ -1,14 +1,10 @@ { "runtime": [ - "AsyncTests", - "DataDrivenTests", - "MassiveParallelTests", - "MatrixTests", - "ScaleTests" + "results" ], "build": [ "BuildTime" ], - "timestamp": "2025-11-11", + "timestamp": "2025-11-12", "environment": "Ubuntu Latest • .NET SDK 10.0.100" } \ No newline at end of file From 05f248967e30517ab75877f7fb23a970c2b3702c Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 12 Nov 2025 00:30:13 +0000 Subject: [PATCH 40/62] chore: update benchmark results (#3795) --- docs/docs/benchmarks/index.md | 26 ++++----- docs/static/benchmarks/historical.json | 4 ++ docs/static/benchmarks/latest.json | 76 +++++++++++++------------- 3 files changed, 55 insertions(+), 51 deletions(-) diff --git a/docs/docs/benchmarks/index.md b/docs/docs/benchmarks/index.md index b10c0e9d1f..1ee894a3c2 100644 --- a/docs/docs/benchmarks/index.md +++ b/docs/docs/benchmarks/index.md @@ -19,19 +19,19 @@ These benchmarks were automatically generated on **2025-11-12** from the latest | Framework | Version | Mean | Median | StdDev | |-----------|---------|------|--------|--------| -| **TUnit** | 1.0.78 | 495.66 ms | 496.75 ms | 3.288 ms | -| NUnit | 4.4.0 | 533.59 ms | 534.97 ms | 10.858 ms | -| MSTest | 4.0.2 | 505.82 ms | 502.05 ms | 13.674 ms | -| xUnit3 | 3.2.0 | 583.29 ms | 580.92 ms | 10.011 ms | -| **TUnit (AOT)** | 1.0.78 | 25.84 ms | 25.90 ms | 0.241 ms | +| **TUnit** | 1.0.78 | 559.5 ms | 559.1 ms | 4.23 ms | +| NUnit | 4.4.0 | 673.9 ms | 674.9 ms | 14.13 ms | +| MSTest | 4.0.2 | 637.8 ms | 634.8 ms | 7.82 ms | +| xUnit3 | 3.2.0 | 723.7 ms | 721.6 ms | 13.31 ms | +| **TUnit (AOT)** | 1.0.78 | 123.2 ms | 122.9 ms | 0.66 ms | ```mermaid %%{init: {'theme':'base'}}%% xychart-beta title "results Performance Comparison" x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"] - y-axis "Time (ms)" 0 --> 700 - bar [495.66, 533.59, 505.82, 583.29, 25.84] + y-axis "Time (ms)" 0 --> 869 + bar [559.5, 673.9, 637.8, 723.7, 123.2] ``` @@ -43,10 +43,10 @@ Compilation time comparison across frameworks: | Framework | Version | Mean | Median | StdDev | |-----------|---------|------|--------|--------| -| **TUnit** | 1.0.78 | 2.025 s | 2.022 s | 0.0200 s | -| Build_NUnit | 4.4.0 | 1.630 s | 1.634 s | 0.0153 s | -| Build_MSTest | 4.0.2 | 1.712 s | 1.714 s | 0.0165 s | -| Build_xUnit3 | 3.2.0 | 1.600 s | 1.600 s | 0.0100 s | +| **TUnit** | 1.0.78 | 1.993 s | 1.991 s | 0.0148 s | +| Build_NUnit | 4.4.0 | 1.602 s | 1.602 s | 0.0126 s | +| Build_MSTest | 4.0.2 | 1.677 s | 1.678 s | 0.0063 s | +| Build_xUnit3 | 3.2.0 | 1.586 s | 1.590 s | 0.0126 s | ```mermaid %%{init: {'theme':'base'}}%% @@ -54,7 +54,7 @@ xychart-beta title "Build Time Comparison" x-axis ["Build_TUnit", "Build_NUnit", "Build_MSTest", "Build_xUnit3"] y-axis "Time (s)" 0 --> 3 - bar [2.025, 1.63, 1.712, 1.6] + bar [1.993, 1.602, 1.677, 1.586] ``` @@ -109,4 +109,4 @@ These benchmarks run automatically daily via [GitHub Actions](https://github.com Each benchmark runs multiple iterations with statistical analysis to ensure accuracy. Results may vary based on hardware and test characteristics. ::: -*Last generated: 2025-11-12T00:04:00.609Z* +*Last generated: 2025-11-12T00:30:03.764Z* diff --git a/docs/static/benchmarks/historical.json b/docs/static/benchmarks/historical.json index eb5613d754..1cd8258e39 100644 --- a/docs/static/benchmarks/historical.json +++ b/docs/static/benchmarks/historical.json @@ -7,6 +7,10 @@ "date": "2025-11-11", "environment": "Ubuntu" }, + { + "date": "2025-11-12", + "environment": "Ubuntu" + }, { "date": "2025-11-12", "environment": "Ubuntu" diff --git a/docs/static/benchmarks/latest.json b/docs/static/benchmarks/latest.json index 5c50198c2b..52755f0611 100644 --- a/docs/static/benchmarks/latest.json +++ b/docs/static/benchmarks/latest.json @@ -1,5 +1,5 @@ { - "timestamp": "2025-11-12T00:04:00.609Z", + "timestamp": "2025-11-12T00:30:03.765Z", "environment": { "benchmarkDotNetVersion": "BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat)", "sdk": ".NET SDK 10.0.100", @@ -10,42 +10,42 @@ { "Method": "TUnit", "Version": "1.0.78", - "Mean": "495.66 ms", - "Error": "3.515 ms", - "StdDev": "3.288 ms", - "Median": "496.75 ms" + "Mean": "559.5 ms", + "Error": "4.77 ms", + "StdDev": "4.23 ms", + "Median": "559.1 ms" }, { "Method": "NUnit", "Version": "4.4.0", - "Mean": "533.59 ms", - "Error": "10.573 ms", - "StdDev": "10.858 ms", - "Median": "534.97 ms" + "Mean": "673.9 ms", + "Error": "12.71 ms", + "StdDev": "14.13 ms", + "Median": "674.9 ms" }, { "Method": "MSTest", "Version": "4.0.2", - "Mean": "505.82 ms", - "Error": "9.990 ms", - "StdDev": "13.674 ms", - "Median": "502.05 ms" + "Mean": "637.8 ms", + "Error": "9.37 ms", + "StdDev": "7.82 ms", + "Median": "634.8 ms" }, { "Method": "xUnit3", "Version": "3.2.0", - "Mean": "583.29 ms", - "Error": "10.193 ms", - "StdDev": "10.011 ms", - "Median": "580.92 ms" + "Mean": "723.7 ms", + "Error": "14.23 ms", + "StdDev": "13.31 ms", + "Median": "721.6 ms" }, { "Method": "TUnit_AOT", "Version": "1.0.78", - "Mean": "25.84 ms", - "Error": "0.272 ms", - "StdDev": "0.241 ms", - "Median": "25.90 ms" + "Mean": "123.2 ms", + "Error": "0.71 ms", + "StdDev": "0.66 ms", + "Median": "122.9 ms" } ] }, @@ -54,34 +54,34 @@ { "Method": "Build_TUnit", "Version": "1.0.78", - "Mean": "2.025 s", - "Error": "0.0225 s", - "StdDev": "0.0200 s", - "Median": "2.022 s" + "Mean": "1.993 s", + "Error": "0.0167 s", + "StdDev": "0.0148 s", + "Median": "1.991 s" }, { "Method": "Build_NUnit", "Version": "4.4.0", - "Mean": "1.630 s", - "Error": "0.0183 s", - "StdDev": "0.0153 s", - "Median": "1.634 s" + "Mean": "1.602 s", + "Error": "0.0135 s", + "StdDev": "0.0126 s", + "Median": "1.602 s" }, { "Method": "Build_MSTest", "Version": "4.0.2", - "Mean": "1.712 s", - "Error": "0.0177 s", - "StdDev": "0.0165 s", - "Median": "1.714 s" + "Mean": "1.677 s", + "Error": "0.0081 s", + "StdDev": "0.0063 s", + "Median": "1.678 s" }, { "Method": "Build_xUnit3", "Version": "3.2.0", - "Mean": "1.600 s", - "Error": "0.0120 s", - "StdDev": "0.0100 s", - "Median": "1.600 s" + "Mean": "1.586 s", + "Error": "0.0135 s", + "StdDev": "0.0126 s", + "Median": "1.590 s" } ] }, @@ -89,6 +89,6 @@ "runtimeCategories": 1, "buildCategories": 1, "totalBenchmarks": 2, - "lastUpdated": "2025-11-12T00:04:00.608Z" + "lastUpdated": "2025-11-12T00:30:03.764Z" } } \ No newline at end of file From 0d0fcdec8319e2135cc36d666a934f1e93b70b63 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 12 Nov 2025 00:30:39 +0000 Subject: [PATCH 41/62] feat: enhance benchmark documentation generation and styling --- .github/scripts/process-benchmarks.js | 279 ++++++++++++++++++-------- docs/sidebars.ts | 5 +- docs/src/css/benchmark-charts.css | 69 +++++++ docs/src/css/custom.css | 3 + 4 files changed, 276 insertions(+), 80 deletions(-) create mode 100644 docs/src/css/benchmark-charts.css diff --git a/.github/scripts/process-benchmarks.js b/.github/scripts/process-benchmarks.js index a0d1109e64..ecfda6f301 100644 --- a/.github/scripts/process-benchmarks.js +++ b/.github/scripts/process-benchmarks.js @@ -139,112 +139,233 @@ const stats = { }; console.log('\nšŸ“Š Preparing benchmark data...'); - -// Generate main benchmarks page console.log('\nšŸ“ Generating documentation...'); const timestamp = new Date().toISOString().split('T')[0]; +const sampleData = Object.values(categories.runtime)[0] || []; +const frameworks = { + tunit: sampleData.find(d => d.Method === 'TUnit')?.Version || 'latest', + xunit: sampleData.find(d => d.Method === 'xUnit3')?.Version || 'latest', + nunit: sampleData.find(d => d.Method === 'NUnit')?.Version || 'latest', + mstest: sampleData.find(d => d.Method === 'MSTest')?.Version || 'latest' +}; -let mainPage = `--- -title: Performance Benchmarks -description: Real-world performance comparisons between TUnit and other .NET testing frameworks -sidebar_position: 1 +// Generate individual benchmark pages for each runtime category +Object.entries(categories.runtime).forEach(([testClass, data]) => { + const benchmarkPage = `--- +title: ${testClass} +description: Performance benchmark results for ${testClass} +sidebar_position: ${Object.keys(categories.runtime).indexOf(testClass) + 2} --- -# Performance Benchmarks +# ${testClass} Benchmark :::info Last Updated -These benchmarks were automatically generated on **${timestamp}** from the latest CI run. +This benchmark was automatically generated on **${timestamp}** from the latest CI run. **Environment:** ${environmentInfo.os || 'Ubuntu Latest'} • ${environmentInfo.sdk || '.NET 10'} ::: -## šŸš€ Runtime Performance +## šŸ“Š Results + +| Framework | Version | Mean | Median | StdDev | +|-----------|---------|------|--------|--------| +${data.map(row => { + const name = row.Method.includes('TUnit_AOT') ? '**TUnit (AOT)**' : row.Method.includes('TUnit') ? '**TUnit**' : row.Method; + return `| ${name} | ${row.Version || 'N/A'} | ${row.Mean} | ${row.Median || 'N/A'} | ${row.StdDev || 'N/A'} |`; +}).join('\n')} + +## šŸ“ˆ Visual Comparison + +\`\`\`mermaid +%%{init: { + 'theme':'base', + 'themeVariables': { + 'primaryColor': '#10b981', + 'primaryTextColor': '#fff', + 'primaryBorderColor': '#059669', + 'lineColor': '#d1d5db', + 'secondaryColor': '#3b82f6', + 'tertiaryColor': '#f59e0b', + 'background': '#ffffff', + 'mainBkg': '#10b981', + 'secondBkg': '#ef4444', + 'tertiaryBkg': '#f59e0b' + } +}}%% +xychart-beta + title "${testClass} Performance Comparison" + x-axis [${data.map(d => `"${d.Method}"`).join(', ')}] + y-axis "Time (${data[0]?.Mean.includes(' s') ? 's' : 'ms'})" 0 --> ${Math.ceil(Math.max(...data.map(d => parseMeanValue(d.Mean))) * 1.2)} + bar [${data.map(d => parseMeanValue(d.Mean)).join(', ')}] +\`\`\` + +## šŸŽÆ Key Insights + +${(() => { + const tunitResult = data.find(d => d.Method === 'TUnit'); + const tunitAotResult = data.find(d => d.Method === 'TUnit_AOT'); + const otherResults = data.filter(d => !d.Method.includes('TUnit')); + + if (!tunitResult) return '- TUnit data not available'; + + const tunitMean = parseMeanValue(tunitResult.Mean); + const insights = []; + + otherResults.forEach(other => { + const otherMean = parseMeanValue(other.Mean); + const speedup = (otherMean / tunitMean).toFixed(2); + if (speedup > 1) { + insights.push(`- **${speedup}x faster** than ${other.Method} (${other.Version})`); + } + }); + if (tunitAotResult) { + const aotMean = parseMeanValue(tunitAotResult.Mean); + const aotSpeedup = (tunitMean / aotMean).toFixed(2); + insights.push(`- **${aotSpeedup}x faster** with Native AOT compilation`); + } + + return insights.join('\n'); +})()} + +--- + +:::note Methodology +View the [benchmarks overview](/docs/benchmarks) for methodology details and environment information. +::: + +*Last generated: ${new Date().toISOString()}* `; -// Add runtime results -Object.entries(categories.runtime).forEach(([testClass, data]) => { - mainPage += `\n### ${testClass}\n\n`; + fs.writeFileSync(path.join(OUTPUT_DIR, `${testClass}.md`), benchmarkPage); + console.log(` āœ“ Created ${OUTPUT_DIR}/${testClass}.md`); + + // Generate individual JSON file for each benchmark + const benchmarkJson = { + timestamp: new Date().toISOString(), + category: testClass, + environment: environmentInfo, + results: data + }; + + fs.writeFileSync( + path.join(STATIC_DIR, `${testClass}.json`), + JSON.stringify(benchmarkJson, null, 2) + ); + console.log(` āœ“ Created ${STATIC_DIR}/${testClass}.json`); +}); - // Add table - mainPage += `| Framework | Version | Mean | Median | StdDev |\n`; - mainPage += `|-----------|---------|------|--------|--------|\n`; +// Generate build benchmark page if available +if (Object.keys(categories.build).length > 0) { + Object.entries(categories.build).forEach(([testClass, data]) => { + const benchmarkPage = `--- +title: Build Performance +description: Compilation time benchmark results +sidebar_position: ${Object.keys(categories.runtime).length + 2} +--- - data.forEach(row => { - const name = row.Method.includes('TUnit_AOT') ? '**TUnit (AOT)**' : row.Method.includes('TUnit') ? '**TUnit**' : row.Method; - mainPage += `| ${name} | ${row.Version || 'N/A'} | ${row.Mean} | ${row.Median || 'N/A'} | ${row.StdDev || 'N/A'} |\n`; - }); +# Build Performance Benchmark - mainPage += '\n'; +:::info Last Updated +This benchmark was automatically generated on **${timestamp}** from the latest CI run. - // Add Mermaid chart - mainPage += `\`\`\`mermaid\n`; - mainPage += `%%{init: {'theme':'base'}}%%\n`; - mainPage += `xychart-beta\n`; - mainPage += ` title "${testClass} Performance Comparison"\n`; +**Environment:** ${environmentInfo.os || 'Ubuntu Latest'} • ${environmentInfo.sdk || '.NET 10'} +::: - // Detect time unit from the data - const sampleMean = data[0]?.Mean || ''; - const timeUnit = sampleMean.includes(' s') ? 's' : 'ms'; +## šŸ“Š Results + +Compilation time comparison across frameworks: + +| Framework | Version | Mean | Median | StdDev | +|-----------|---------|------|--------|--------| +${data.map(row => { + const name = row.Method.includes('TUnit') ? '**TUnit**' : row.Method; + return `| ${name} | ${row.Version || 'N/A'} | ${row.Mean} | ${row.Median || 'N/A'} | ${row.StdDev || 'N/A'} |`; +}).join('\n')} + +## šŸ“ˆ Visual Comparison + +\`\`\`mermaid +%%{init: { + 'theme':'base', + 'themeVariables': { + 'primaryColor': '#10b981', + 'primaryTextColor': '#fff', + 'primaryBorderColor': '#059669', + 'lineColor': '#d1d5db', + 'secondaryColor': '#3b82f6', + 'tertiaryColor': '#f59e0b', + 'background': '#ffffff', + 'mainBkg': '#10b981', + 'secondBkg': '#ef4444', + 'tertiaryBkg': '#f59e0b' + } +}}%% +xychart-beta + title "Build Time Comparison" + x-axis [${data.map(d => `"${d.Method}"`).join(', ')}] + y-axis "Time (${data[0]?.Mean.includes(' s') ? 's' : 'ms'})" 0 --> ${Math.ceil(Math.max(...data.map(d => parseMeanValue(d.Mean))) * 1.2)} + bar [${data.map(d => parseMeanValue(d.Mean)).join(', ')}] +\`\`\` - // Find max value for y-axis scaling - const maxValue = Math.max(...data.map(d => parseMeanValue(d.Mean))); - const yMax = Math.ceil(maxValue * 1.2); // 20% padding +--- - mainPage += ` x-axis [${data.map(d => `"${d.Method}"`).join(', ')}]\n`; - mainPage += ` y-axis "Time (${timeUnit})" 0 --> ${yMax}\n`; - mainPage += ` bar [${data.map(d => parseMeanValue(d.Mean)).join(', ')}]\n`; - mainPage += `\`\`\`\n\n`; -}); +:::note Methodology +View the [benchmarks overview](/docs/benchmarks) for methodology details and environment information. +::: -// Add build time results -if (Object.keys(categories.build).length > 0) { - mainPage += `\n---\n\n## šŸ”Ø Build Performance\n\n`; - mainPage += `Compilation time comparison across frameworks:\n\n`; +*Last generated: ${new Date().toISOString()}* +`; - Object.entries(categories.build).forEach(([testClass, data]) => { - mainPage += `| Framework | Version | Mean | Median | StdDev |\n`; - mainPage += `|-----------|---------|------|--------|--------|\n`; - - data.forEach(row => { - const name = row.Method.includes('TUnit') ? '**TUnit**' : row.Method; - mainPage += `| ${name} | ${row.Version || 'N/A'} | ${row.Mean} | ${row.Median || 'N/A'} | ${row.StdDev || 'N/A'} |\n`; - }); - - mainPage += '\n'; - - // Add Mermaid chart for build performance - mainPage += `\`\`\`mermaid\n`; - mainPage += `%%{init: {'theme':'base'}}%%\n`; - mainPage += `xychart-beta\n`; - mainPage += ` title "Build Time Comparison"\n`; - - // Detect time unit from the data - const sampleMean = data[0]?.Mean || ''; - const timeUnit = sampleMean.includes(' s') ? 's' : 'ms'; - - // Find max value for y-axis scaling - const maxValue = Math.max(...data.map(d => parseMeanValue(d.Mean))); - const yMax = Math.ceil(maxValue * 1.2); // 20% padding - - mainPage += ` x-axis [${data.map(d => `"${d.Method}"`).join(', ')}]\n`; - mainPage += ` y-axis "Time (${timeUnit})" 0 --> ${yMax}\n`; - mainPage += ` bar [${data.map(d => parseMeanValue(d.Mean)).join(', ')}]\n`; - mainPage += `\`\`\`\n\n`; + fs.writeFileSync(path.join(OUTPUT_DIR, 'BuildTime.md'), benchmarkPage); + console.log(` āœ“ Created ${OUTPUT_DIR}/BuildTime.md`); + + // Generate build benchmark JSON + const buildJson = { + timestamp: new Date().toISOString(), + category: 'BuildTime', + environment: environmentInfo, + results: data + }; + + fs.writeFileSync( + path.join(STATIC_DIR, 'BuildTime.json'), + JSON.stringify(buildJson, null, 2) + ); + console.log(` āœ“ Created ${STATIC_DIR}/BuildTime.json`); }); } -// Add methodology section -const sampleData = Object.values(categories.runtime)[0] || []; -const frameworks = { - tunit: sampleData.find(d => d.Method === 'TUnit')?.Version || 'latest', - xunit: sampleData.find(d => d.Method === 'xUnit3')?.Version || 'latest', - nunit: sampleData.find(d => d.Method === 'NUnit')?.Version || 'latest', - mstest: sampleData.find(d => d.Method === 'MSTest')?.Version || 'latest' -}; +// Generate index/overview page +const indexPage = `--- +title: Performance Benchmarks +description: Real-world performance comparisons between TUnit and other .NET testing frameworks +sidebar_position: 1 +--- + +# Performance Benchmarks + +:::info Last Updated +These benchmarks were automatically generated on **${timestamp}** from the latest CI run. + +**Environment:** ${environmentInfo.os || 'Ubuntu Latest'} • ${environmentInfo.sdk || '.NET 10'} +::: + +## šŸš€ Runtime Benchmarks + +Click on any benchmark to view detailed results: + +${Object.keys(categories.runtime).map(testClass => + `- [${testClass}](${testClass}) - Detailed performance analysis` +).join('\n')} + +${Object.keys(categories.build).length > 0 ? ` +## šŸ”Ø Build Benchmarks + +- [Build Performance](BuildTime) - Compilation time comparison +` : ''} -mainPage += ` --- ## šŸ“Š Methodology @@ -299,7 +420,7 @@ Each benchmark runs multiple iterations with statistical analysis to ensure accu *Last generated: ${new Date().toISOString()}* `; -fs.writeFileSync(path.join(OUTPUT_DIR, 'index.md'), mainPage); +fs.writeFileSync(path.join(OUTPUT_DIR, 'index.md'), indexPage); console.log(` āœ“ Created ${OUTPUT_DIR}/index.md`); // Generate JSON for interactive components diff --git a/docs/sidebars.ts b/docs/sidebars.ts index fd63fab784..b53b165699 100644 --- a/docs/sidebars.ts +++ b/docs/sidebars.ts @@ -291,7 +291,10 @@ const sidebars: SidebarsConfig = { collapsed: false, items: [ 'benchmarks/index', - 'benchmarks/methodology', + { + type: 'autogenerated', + dirName: 'benchmarks', + }, ], }, ], diff --git a/docs/src/css/benchmark-charts.css b/docs/src/css/benchmark-charts.css new file mode 100644 index 0000000000..cf992fae08 --- /dev/null +++ b/docs/src/css/benchmark-charts.css @@ -0,0 +1,69 @@ +/* Enhanced styling for Mermaid benchmark charts */ + +/* Override Mermaid xychart bar colors for better visibility */ +.markdown article .mermaid .xychart .plot > g > g > rect:nth-child(1) { + fill: #10b981 !important; /* TUnit - Emerald Green */ + opacity: 0.9; +} + +.markdown article .mermaid .xychart .plot > g > g > rect:nth-child(2) { + fill: #ef4444 !important; /* NUnit - Red */ + opacity: 0.85; +} + +.markdown article .mermaid .xychart .plot > g > g > rect:nth-child(3) { + fill: #f59e0b !important; /* MSTest - Amber */ + opacity: 0.85; +} + +.markdown article .mermaid .xychart .plot > g > g > rect:nth-child(4) { + fill: #8b5cf6 !important; /* xUnit - Purple */ + opacity: 0.85; +} + +.markdown article .mermaid .xychart .plot > g > g > rect:nth-child(5) { + fill: #06b6d4 !important; /* TUnit AOT - Cyan */ + opacity: 0.9; +} + +/* Enhance chart readability */ +.markdown article .mermaid .xychart text { + fill: var(--ifm-font-color-base) !important; + font-family: var(--ifm-font-family-base); +} + +.markdown article .mermaid .xychart .title { + font-size: 16px; + font-weight: 600; +} + +/* Grid lines for better readability */ +.markdown article .mermaid .xychart line { + stroke: var(--ifm-color-emphasis-300); + stroke-opacity: 0.5; +} + +/* Axis styling */ +.markdown article .mermaid .xychart .xAxis line, +.markdown article .mermaid .xychart .yAxis line { + stroke: var(--ifm-color-emphasis-500); +} + +/* Dark mode adjustments */ +[data-theme='dark'] .markdown article .mermaid .xychart text { + fill: var(--ifm-font-color-base) !important; +} + +[data-theme='dark'] .markdown article .mermaid .xychart line { + stroke: var(--ifm-color-emphasis-500); +} + +/* Add subtle hover effects */ +.markdown article .mermaid .xychart .plot > g > g > rect { + transition: opacity 0.2s ease; +} + +.markdown article .mermaid .xychart .plot > g > g > rect:hover { + opacity: 1 !important; + filter: brightness(1.1); +} diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css index 7d4220ef57..912280f65d 100644 --- a/docs/src/css/custom.css +++ b/docs/src/css/custom.css @@ -4,6 +4,9 @@ * work well for content-centric websites. */ +/* Import benchmark chart styling */ +@import url('./benchmark-charts.css'); + /* You can override the default Infima variables here. */ :root { --ifm-color-primary: #14b8a6; From ec75f2f93f02d8fe83596ea54679c0912f1a0776 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 12 Nov 2025 00:34:20 +0000 Subject: [PATCH 42/62] Update templates to use .NET 10 (#3792) * Update templates to use .NET 10 * chore: update target frameworks to .NET 10 only --- TUnit.Engine.Tests/TUnit.Engine.Tests.csproj | 2 +- .../TUnit.Example.Asp.Net.TestProject.csproj | 2 +- TUnit.Example.Asp.Net/TUnit.Example.Asp.Net.csproj | 2 +- .../TUnit.AspNet/TUnit.AspNet/TUnit.AspNet.csproj | 2 +- .../TUnit.AspNet/WebApp/WebApp.csproj | 2 +- .../TUnit.Aspire.Starter.ApiService.csproj | 2 +- .../TUnit.Aspire.Starter.AppHost.csproj | 2 +- .../TUnit.Aspire.Starter.ServiceDefaults.csproj | 2 +- .../TUnit.Aspire.Starter.TestProject.csproj | 2 +- .../TUnit.Aspire.Starter.WebApp.csproj | 2 +- .../TUnit.Aspire.Test/TUnit.Aspire.Test.csproj | 2 +- .../TUnit.AspNet.FSharp/TUnit.AspNet.FSharp.fsproj | 2 +- .../TUnit.AspNet.FSharp/WebApp/WebApp.fsproj | 2 +- TUnit.Templates.Tests/TUnit.Templates.Tests.csproj | 2 +- TUnit.Templates/TUnit.Templates.csproj | 2 +- .../content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj | 2 +- .../content/TUnit.AspNet.FSharp/WebApp/WebApp.fsproj | 2 +- .../content/TUnit.AspNet/TestProject/TestProject.csproj | 2 +- TUnit.Templates/content/TUnit.AspNet/WebApp/WebApp.csproj | 2 +- .../ExampleNamespace.ApiService.csproj | 2 +- .../ExampleNamespace.AppHost/ExampleNamespace.AppHost.csproj | 2 +- .../ExampleNamespace.ServiceDefaults.csproj | 2 +- .../ExampleNamespace.TestProject.csproj | 2 +- .../ExampleNamespace.WebApp/ExampleNamespace.WebApp.csproj | 2 +- .../content/TUnit.Aspire.Test/ExampleNamespace.csproj | 2 +- docs/docs/execution/engine-modes.md | 2 +- 26 files changed, 26 insertions(+), 26 deletions(-) diff --git a/TUnit.Engine.Tests/TUnit.Engine.Tests.csproj b/TUnit.Engine.Tests/TUnit.Engine.Tests.csproj index 4349b07740..99a1694787 100644 --- a/TUnit.Engine.Tests/TUnit.Engine.Tests.csproj +++ b/TUnit.Engine.Tests/TUnit.Engine.Tests.csproj @@ -3,7 +3,7 @@ - net9.0 + net10.0 diff --git a/TUnit.Example.Asp.Net.TestProject/TUnit.Example.Asp.Net.TestProject.csproj b/TUnit.Example.Asp.Net.TestProject/TUnit.Example.Asp.Net.TestProject.csproj index 4106a2cedb..abc7a1390f 100644 --- a/TUnit.Example.Asp.Net.TestProject/TUnit.Example.Asp.Net.TestProject.csproj +++ b/TUnit.Example.Asp.Net.TestProject/TUnit.Example.Asp.Net.TestProject.csproj @@ -3,7 +3,7 @@ - net9.0;net10.0 + net10.0 diff --git a/TUnit.Example.Asp.Net/TUnit.Example.Asp.Net.csproj b/TUnit.Example.Asp.Net/TUnit.Example.Asp.Net.csproj index 69d9f6d009..549f17e876 100644 --- a/TUnit.Example.Asp.Net/TUnit.Example.Asp.Net.csproj +++ b/TUnit.Example.Asp.Net/TUnit.Example.Asp.Net.csproj @@ -1,7 +1,7 @@ - net9.0;net10.0 + net10.0 enable enable false diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.AspNet._.verified/TUnit.AspNet/TUnit.AspNet/TUnit.AspNet.csproj b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.AspNet._.verified/TUnit.AspNet/TUnit.AspNet/TUnit.AspNet.csproj index 8967721da8..eb10888b9e 100644 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.AspNet._.verified/TUnit.AspNet/TUnit.AspNet/TUnit.AspNet.csproj +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.AspNet._.verified/TUnit.AspNet/TUnit.AspNet/TUnit.AspNet.csproj @@ -4,7 +4,7 @@ enable enable Exe - net9.0 + net10.0 diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.AspNet._.verified/TUnit.AspNet/WebApp/WebApp.csproj b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.AspNet._.verified/TUnit.AspNet/WebApp/WebApp.csproj index 0fb72c4b20..a6a96dcf18 100644 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.AspNet._.verified/TUnit.AspNet/WebApp/WebApp.csproj +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.AspNet._.verified/TUnit.AspNet/WebApp/WebApp.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 enable enable diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Aspire.Starter._.verified/TUnit.Aspire.Starter/TUnit.Aspire.Starter.ApiService/TUnit.Aspire.Starter.ApiService.csproj b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Aspire.Starter._.verified/TUnit.Aspire.Starter/TUnit.Aspire.Starter.ApiService/TUnit.Aspire.Starter.ApiService.csproj index 0cdcf8be70..1ad71b0e28 100644 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Aspire.Starter._.verified/TUnit.Aspire.Starter/TUnit.Aspire.Starter.ApiService/TUnit.Aspire.Starter.ApiService.csproj +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Aspire.Starter._.verified/TUnit.Aspire.Starter/TUnit.Aspire.Starter.ApiService/TUnit.Aspire.Starter.ApiService.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 enable enable diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Aspire.Starter._.verified/TUnit.Aspire.Starter/TUnit.Aspire.Starter.AppHost/TUnit.Aspire.Starter.AppHost.csproj b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Aspire.Starter._.verified/TUnit.Aspire.Starter/TUnit.Aspire.Starter.AppHost/TUnit.Aspire.Starter.AppHost.csproj index 18012ac870..3cbe117a5a 100644 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Aspire.Starter._.verified/TUnit.Aspire.Starter/TUnit.Aspire.Starter.AppHost/TUnit.Aspire.Starter.AppHost.csproj +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Aspire.Starter._.verified/TUnit.Aspire.Starter/TUnit.Aspire.Starter.AppHost/TUnit.Aspire.Starter.AppHost.csproj @@ -4,7 +4,7 @@ Exe - net9.0 + net10.0 enable enable true diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Aspire.Starter._.verified/TUnit.Aspire.Starter/TUnit.Aspire.Starter.ServiceDefaults/TUnit.Aspire.Starter.ServiceDefaults.csproj b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Aspire.Starter._.verified/TUnit.Aspire.Starter/TUnit.Aspire.Starter.ServiceDefaults/TUnit.Aspire.Starter.ServiceDefaults.csproj index 6b09c19d12..f0593381b8 100644 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Aspire.Starter._.verified/TUnit.Aspire.Starter/TUnit.Aspire.Starter.ServiceDefaults/TUnit.Aspire.Starter.ServiceDefaults.csproj +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Aspire.Starter._.verified/TUnit.Aspire.Starter/TUnit.Aspire.Starter.ServiceDefaults/TUnit.Aspire.Starter.ServiceDefaults.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 enable enable true diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Aspire.Starter._.verified/TUnit.Aspire.Starter/TUnit.Aspire.Starter.TestProject/TUnit.Aspire.Starter.TestProject.csproj b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Aspire.Starter._.verified/TUnit.Aspire.Starter/TUnit.Aspire.Starter.TestProject/TUnit.Aspire.Starter.TestProject.csproj index 2356fbfa25..241b9eb367 100644 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Aspire.Starter._.verified/TUnit.Aspire.Starter/TUnit.Aspire.Starter.TestProject/TUnit.Aspire.Starter.TestProject.csproj +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Aspire.Starter._.verified/TUnit.Aspire.Starter/TUnit.Aspire.Starter.TestProject/TUnit.Aspire.Starter.TestProject.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 enable enable false diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Aspire.Starter._.verified/TUnit.Aspire.Starter/TUnit.Aspire.Starter.WebApp/TUnit.Aspire.Starter.WebApp.csproj b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Aspire.Starter._.verified/TUnit.Aspire.Starter/TUnit.Aspire.Starter.WebApp/TUnit.Aspire.Starter.WebApp.csproj index ddb4c3bf05..d871db6526 100644 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Aspire.Starter._.verified/TUnit.Aspire.Starter/TUnit.Aspire.Starter.WebApp/TUnit.Aspire.Starter.WebApp.csproj +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Aspire.Starter._.verified/TUnit.Aspire.Starter/TUnit.Aspire.Starter.WebApp/TUnit.Aspire.Starter.WebApp.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 enable enable diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Aspire.Test._.verified/TUnit.Aspire.Test/TUnit.Aspire.Test.csproj b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Aspire.Test._.verified/TUnit.Aspire.Test/TUnit.Aspire.Test.csproj index 3606790ad9..3646299f7b 100644 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Aspire.Test._.verified/TUnit.Aspire.Test/TUnit.Aspire.Test.csproj +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit.Aspire.Test._.verified/TUnit.Aspire.Test/TUnit.Aspire.Test.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 enable enable false diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithFSharp.TUnit.AspNet.FSharp._.verified/TUnit.AspNet.FSharp/TUnit.AspNet.FSharp/TUnit.AspNet.FSharp.fsproj b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithFSharp.TUnit.AspNet.FSharp._.verified/TUnit.AspNet.FSharp/TUnit.AspNet.FSharp/TUnit.AspNet.FSharp.fsproj index be7d8a2c0d..30cbfc590b 100644 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithFSharp.TUnit.AspNet.FSharp._.verified/TUnit.AspNet.FSharp/TUnit.AspNet.FSharp/TUnit.AspNet.FSharp.fsproj +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithFSharp.TUnit.AspNet.FSharp._.verified/TUnit.AspNet.FSharp/TUnit.AspNet.FSharp/TUnit.AspNet.FSharp.fsproj @@ -5,7 +5,7 @@ enable enable Exe - net9.0 + net10.0 diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithFSharp.TUnit.AspNet.FSharp._.verified/TUnit.AspNet.FSharp/WebApp/WebApp.fsproj b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithFSharp.TUnit.AspNet.FSharp._.verified/TUnit.AspNet.FSharp/WebApp/WebApp.fsproj index 4018f042a6..5393f5177d 100644 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithFSharp.TUnit.AspNet.FSharp._.verified/TUnit.AspNet.FSharp/WebApp/WebApp.fsproj +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithFSharp.TUnit.AspNet.FSharp._.verified/TUnit.AspNet.FSharp/WebApp/WebApp.fsproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 diff --git a/TUnit.Templates.Tests/TUnit.Templates.Tests.csproj b/TUnit.Templates.Tests/TUnit.Templates.Tests.csproj index bfa9211dda..2868a34ab1 100644 --- a/TUnit.Templates.Tests/TUnit.Templates.Tests.csproj +++ b/TUnit.Templates.Tests/TUnit.Templates.Tests.csproj @@ -3,7 +3,7 @@ - net9.0;net10.0 + net10.0 diff --git a/TUnit.Templates/TUnit.Templates.csproj b/TUnit.Templates/TUnit.Templates.csproj index f5fea84539..97c688cbf6 100644 --- a/TUnit.Templates/TUnit.Templates.csproj +++ b/TUnit.Templates/TUnit.Templates.csproj @@ -10,7 +10,7 @@ https://www.github.com/thomhurst/TUnit Template - net9.0 + net10.0 true false content diff --git a/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj b/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj index 0bb4259733..5bced25117 100644 --- a/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj +++ b/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj @@ -5,7 +5,7 @@ enable enable Exe - net9.0 + net10.0 diff --git a/TUnit.Templates/content/TUnit.AspNet.FSharp/WebApp/WebApp.fsproj b/TUnit.Templates/content/TUnit.AspNet.FSharp/WebApp/WebApp.fsproj index b42e1e3d59..e010c79135 100644 --- a/TUnit.Templates/content/TUnit.AspNet.FSharp/WebApp/WebApp.fsproj +++ b/TUnit.Templates/content/TUnit.AspNet.FSharp/WebApp/WebApp.fsproj @@ -1,7 +1,7 @@ - net9.0 + net10.0 diff --git a/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj b/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj index 45f7c0723a..2a0c73c2bf 100644 --- a/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj +++ b/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj @@ -4,7 +4,7 @@ enable enable Exe - net9.0 + net10.0 diff --git a/TUnit.Templates/content/TUnit.AspNet/WebApp/WebApp.csproj b/TUnit.Templates/content/TUnit.AspNet/WebApp/WebApp.csproj index d77de3aded..687096f753 100644 --- a/TUnit.Templates/content/TUnit.AspNet/WebApp/WebApp.csproj +++ b/TUnit.Templates/content/TUnit.AspNet/WebApp/WebApp.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 enable enable diff --git a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.ApiService/ExampleNamespace.ApiService.csproj b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.ApiService/ExampleNamespace.ApiService.csproj index b48bfef63c..e3c1bc99c1 100644 --- a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.ApiService/ExampleNamespace.ApiService.csproj +++ b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.ApiService/ExampleNamespace.ApiService.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 enable enable diff --git a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.AppHost/ExampleNamespace.AppHost.csproj b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.AppHost/ExampleNamespace.AppHost.csproj index a712d7c89c..44bd877374 100644 --- a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.AppHost/ExampleNamespace.AppHost.csproj +++ b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.AppHost/ExampleNamespace.AppHost.csproj @@ -4,7 +4,7 @@ Exe - net9.0 + net10.0 enable enable true diff --git a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.ServiceDefaults/ExampleNamespace.ServiceDefaults.csproj b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.ServiceDefaults/ExampleNamespace.ServiceDefaults.csproj index 8fd483346a..0eaff9455a 100644 --- a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.ServiceDefaults/ExampleNamespace.ServiceDefaults.csproj +++ b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.ServiceDefaults/ExampleNamespace.ServiceDefaults.csproj @@ -1,7 +1,7 @@ - net9.0 + net10.0 enable enable true diff --git a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.TestProject/ExampleNamespace.TestProject.csproj b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.TestProject/ExampleNamespace.TestProject.csproj index 29ac371ea2..a92efa13aa 100644 --- a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.TestProject/ExampleNamespace.TestProject.csproj +++ b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.TestProject/ExampleNamespace.TestProject.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 enable enable false diff --git a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.WebApp/ExampleNamespace.WebApp.csproj b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.WebApp/ExampleNamespace.WebApp.csproj index 55567ede91..3d403305bb 100644 --- a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.WebApp/ExampleNamespace.WebApp.csproj +++ b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.WebApp/ExampleNamespace.WebApp.csproj @@ -1,7 +1,7 @@ - net9.0 + net10.0 enable enable diff --git a/TUnit.Templates/content/TUnit.Aspire.Test/ExampleNamespace.csproj b/TUnit.Templates/content/TUnit.Aspire.Test/ExampleNamespace.csproj index 6acb29b07d..c73e63b202 100644 --- a/TUnit.Templates/content/TUnit.Aspire.Test/ExampleNamespace.csproj +++ b/TUnit.Templates/content/TUnit.Aspire.Test/ExampleNamespace.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 enable enable false diff --git a/docs/docs/execution/engine-modes.md b/docs/docs/execution/engine-modes.md index 4a6eefbc0b..aa45293abd 100644 --- a/docs/docs/execution/engine-modes.md +++ b/docs/docs/execution/engine-modes.md @@ -107,7 +107,7 @@ Add this MSBuild property to your test project file (`.csproj`): ```xml - net9.0 + net10.0 false From 3fadbda29ab088c0af8323de6827bac4e26511cd Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 12 Nov 2025 00:36:27 +0000 Subject: [PATCH 43/62] chore(deps): update dependency polyfill to 9.0.1 (#3794) Co-authored-by: Renovate Bot --- TUnit.Core/TUnit.Core.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TUnit.Core/TUnit.Core.targets b/TUnit.Core/TUnit.Core.targets index f822718364..f27fabe053 100644 --- a/TUnit.Core/TUnit.Core.targets +++ b/TUnit.Core/TUnit.Core.targets @@ -8,7 +8,7 @@ - <_TUnitPolyfillVersion>9.0.0 + <_TUnitPolyfillVersion>9.0.1 <_TUnitNeedsPolyfill Condition="'$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'netstandard2.1' or '$(TargetFrameworkIdentifier)' == '.NETFramework'">true From 6895443489bb2d324c2f661ca1c65e63f37ae1ec Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 12 Nov 2025 00:44:22 +0000 Subject: [PATCH 44/62] chore: update benchmark results (#3796) --- docs/docs/benchmarks/BuildTime.md | 57 +++++++++++++++++++ docs/docs/benchmarks/index.md | 46 ++-------------- docs/docs/benchmarks/results.md | 63 +++++++++++++++++++++ docs/static/benchmarks/BuildTime.json | 43 +++++++++++++++ docs/static/benchmarks/historical.json | 4 ++ docs/static/benchmarks/latest.json | 76 +++++++++++++------------- docs/static/benchmarks/results.json | 51 +++++++++++++++++ 7 files changed, 262 insertions(+), 78 deletions(-) create mode 100644 docs/docs/benchmarks/BuildTime.md create mode 100644 docs/docs/benchmarks/results.md create mode 100644 docs/static/benchmarks/BuildTime.json create mode 100644 docs/static/benchmarks/results.json diff --git a/docs/docs/benchmarks/BuildTime.md b/docs/docs/benchmarks/BuildTime.md new file mode 100644 index 0000000000..4a20e0cb3f --- /dev/null +++ b/docs/docs/benchmarks/BuildTime.md @@ -0,0 +1,57 @@ +--- +title: Build Performance +description: Compilation time benchmark results +sidebar_position: 3 +--- + +# Build Performance Benchmark + +:::info Last Updated +This benchmark was automatically generated on **2025-11-12** from the latest CI run. + +**Environment:** Ubuntu Latest • .NET SDK 10.0.100 +::: + +## šŸ“Š Results + +Compilation time comparison across frameworks: + +| Framework | Version | Mean | Median | StdDev | +|-----------|---------|------|--------|--------| +| **TUnit** | 1.0.78 | 2.061 s | 2.060 s | 0.0202 s | +| Build_NUnit | 4.4.0 | 1.645 s | 1.645 s | 0.0147 s | +| Build_MSTest | 4.0.2 | 1.712 s | 1.710 s | 0.0132 s | +| Build_xUnit3 | 3.2.0 | 1.615 s | 1.619 s | 0.0149 s | + +## šŸ“ˆ Visual Comparison + +```mermaid +%%{init: { + 'theme':'base', + 'themeVariables': { + 'primaryColor': '#10b981', + 'primaryTextColor': '#fff', + 'primaryBorderColor': '#059669', + 'lineColor': '#d1d5db', + 'secondaryColor': '#3b82f6', + 'tertiaryColor': '#f59e0b', + 'background': '#ffffff', + 'mainBkg': '#10b981', + 'secondBkg': '#ef4444', + 'tertiaryBkg': '#f59e0b' + } +}}%% +xychart-beta + title "Build Time Comparison" + x-axis ["Build_TUnit", "Build_NUnit", "Build_MSTest", "Build_xUnit3"] + y-axis "Time (s)" 0 --> 3 + bar [2.061, 1.645, 1.712, 1.615] +``` + +--- + +:::note Methodology +View the [benchmarks overview](/docs/benchmarks) for methodology details and environment information. +::: + +*Last generated: 2025-11-12T00:44:12.201Z* diff --git a/docs/docs/benchmarks/index.md b/docs/docs/benchmarks/index.md index 1ee894a3c2..4795f4d795 100644 --- a/docs/docs/benchmarks/index.md +++ b/docs/docs/benchmarks/index.md @@ -12,50 +12,16 @@ These benchmarks were automatically generated on **2025-11-12** from the latest **Environment:** Ubuntu Latest • .NET SDK 10.0.100 ::: -## šŸš€ Runtime Performance +## šŸš€ Runtime Benchmarks +Click on any benchmark to view detailed results: -### results +- [results](results) - Detailed performance analysis -| Framework | Version | Mean | Median | StdDev | -|-----------|---------|------|--------|--------| -| **TUnit** | 1.0.78 | 559.5 ms | 559.1 ms | 4.23 ms | -| NUnit | 4.4.0 | 673.9 ms | 674.9 ms | 14.13 ms | -| MSTest | 4.0.2 | 637.8 ms | 634.8 ms | 7.82 ms | -| xUnit3 | 3.2.0 | 723.7 ms | 721.6 ms | 13.31 ms | -| **TUnit (AOT)** | 1.0.78 | 123.2 ms | 122.9 ms | 0.66 ms | -```mermaid -%%{init: {'theme':'base'}}%% -xychart-beta - title "results Performance Comparison" - x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"] - y-axis "Time (ms)" 0 --> 869 - bar [559.5, 673.9, 637.8, 723.7, 123.2] -``` +## šŸ”Ø Build Benchmarks - ---- - -## šŸ”Ø Build Performance - -Compilation time comparison across frameworks: - -| Framework | Version | Mean | Median | StdDev | -|-----------|---------|------|--------|--------| -| **TUnit** | 1.0.78 | 1.993 s | 1.991 s | 0.0148 s | -| Build_NUnit | 4.4.0 | 1.602 s | 1.602 s | 0.0126 s | -| Build_MSTest | 4.0.2 | 1.677 s | 1.678 s | 0.0063 s | -| Build_xUnit3 | 3.2.0 | 1.586 s | 1.590 s | 0.0126 s | - -```mermaid -%%{init: {'theme':'base'}}%% -xychart-beta - title "Build Time Comparison" - x-axis ["Build_TUnit", "Build_NUnit", "Build_MSTest", "Build_xUnit3"] - y-axis "Time (s)" 0 --> 3 - bar [1.993, 1.602, 1.677, 1.586] -``` +- [Build Performance](BuildTime) - Compilation time comparison --- @@ -109,4 +75,4 @@ These benchmarks run automatically daily via [GitHub Actions](https://github.com Each benchmark runs multiple iterations with statistical analysis to ensure accuracy. Results may vary based on hardware and test characteristics. ::: -*Last generated: 2025-11-12T00:30:03.764Z* +*Last generated: 2025-11-12T00:44:12.201Z* diff --git a/docs/docs/benchmarks/results.md b/docs/docs/benchmarks/results.md new file mode 100644 index 0000000000..7fa319d416 --- /dev/null +++ b/docs/docs/benchmarks/results.md @@ -0,0 +1,63 @@ +--- +title: results +description: Performance benchmark results for results +sidebar_position: 2 +--- + +# results Benchmark + +:::info Last Updated +This benchmark was automatically generated on **2025-11-12** from the latest CI run. + +**Environment:** Ubuntu Latest • .NET SDK 10.0.100 +::: + +## šŸ“Š Results + +| Framework | Version | Mean | Median | StdDev | +|-----------|---------|------|--------|--------| +| **TUnit** | 1.0.78 | 569.0 ms | 568.5 ms | 3.62 ms | +| NUnit | 4.4.0 | 691.2 ms | 691.0 ms | 5.19 ms | +| MSTest | 4.0.2 | 658.5 ms | 655.6 ms | 5.84 ms | +| xUnit3 | 3.2.0 | 738.2 ms | 735.9 ms | 7.49 ms | +| **TUnit (AOT)** | 1.0.78 | 124.2 ms | 124.4 ms | 0.42 ms | + +## šŸ“ˆ Visual Comparison + +```mermaid +%%{init: { + 'theme':'base', + 'themeVariables': { + 'primaryColor': '#10b981', + 'primaryTextColor': '#fff', + 'primaryBorderColor': '#059669', + 'lineColor': '#d1d5db', + 'secondaryColor': '#3b82f6', + 'tertiaryColor': '#f59e0b', + 'background': '#ffffff', + 'mainBkg': '#10b981', + 'secondBkg': '#ef4444', + 'tertiaryBkg': '#f59e0b' + } +}}%% +xychart-beta + title "results Performance Comparison" + x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"] + y-axis "Time (ms)" 0 --> 886 + bar [569, 691.2, 658.5, 738.2, 124.2] +``` + +## šŸŽÆ Key Insights + +- **1.21x faster** than NUnit (4.4.0) +- **1.16x faster** than MSTest (4.0.2) +- **1.30x faster** than xUnit3 (3.2.0) +- **4.58x faster** with Native AOT compilation + +--- + +:::note Methodology +View the [benchmarks overview](/docs/benchmarks) for methodology details and environment information. +::: + +*Last generated: 2025-11-12T00:44:12.201Z* diff --git a/docs/static/benchmarks/BuildTime.json b/docs/static/benchmarks/BuildTime.json new file mode 100644 index 0000000000..2f4f507e3d --- /dev/null +++ b/docs/static/benchmarks/BuildTime.json @@ -0,0 +1,43 @@ +{ + "timestamp": "2025-11-12T00:44:12.201Z", + "category": "BuildTime", + "environment": { + "benchmarkDotNetVersion": "BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat)", + "sdk": ".NET SDK 10.0.100", + "host": ".NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3" + }, + "results": [ + { + "Method": "Build_TUnit", + "Version": "1.0.78", + "Mean": "2.061 s", + "Error": "0.0216 s", + "StdDev": "0.0202 s", + "Median": "2.060 s" + }, + { + "Method": "Build_NUnit", + "Version": "4.4.0", + "Mean": "1.645 s", + "Error": "0.0165 s", + "StdDev": "0.0147 s", + "Median": "1.645 s" + }, + { + "Method": "Build_MSTest", + "Version": "4.0.2", + "Mean": "1.712 s", + "Error": "0.0149 s", + "StdDev": "0.0132 s", + "Median": "1.710 s" + }, + { + "Method": "Build_xUnit3", + "Version": "3.2.0", + "Mean": "1.615 s", + "Error": "0.0160 s", + "StdDev": "0.0149 s", + "Median": "1.619 s" + } + ] +} \ No newline at end of file diff --git a/docs/static/benchmarks/historical.json b/docs/static/benchmarks/historical.json index 1cd8258e39..f0a01a895b 100644 --- a/docs/static/benchmarks/historical.json +++ b/docs/static/benchmarks/historical.json @@ -11,6 +11,10 @@ "date": "2025-11-12", "environment": "Ubuntu" }, + { + "date": "2025-11-12", + "environment": "Ubuntu" + }, { "date": "2025-11-12", "environment": "Ubuntu" diff --git a/docs/static/benchmarks/latest.json b/docs/static/benchmarks/latest.json index 52755f0611..0599437f16 100644 --- a/docs/static/benchmarks/latest.json +++ b/docs/static/benchmarks/latest.json @@ -1,5 +1,5 @@ { - "timestamp": "2025-11-12T00:30:03.765Z", + "timestamp": "2025-11-12T00:44:12.201Z", "environment": { "benchmarkDotNetVersion": "BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat)", "sdk": ".NET SDK 10.0.100", @@ -10,42 +10,42 @@ { "Method": "TUnit", "Version": "1.0.78", - "Mean": "559.5 ms", - "Error": "4.77 ms", - "StdDev": "4.23 ms", - "Median": "559.1 ms" + "Mean": "569.0 ms", + "Error": "3.87 ms", + "StdDev": "3.62 ms", + "Median": "568.5 ms" }, { "Method": "NUnit", "Version": "4.4.0", - "Mean": "673.9 ms", - "Error": "12.71 ms", - "StdDev": "14.13 ms", - "Median": "674.9 ms" + "Mean": "691.2 ms", + "Error": "5.85 ms", + "StdDev": "5.19 ms", + "Median": "691.0 ms" }, { "Method": "MSTest", "Version": "4.0.2", - "Mean": "637.8 ms", - "Error": "9.37 ms", - "StdDev": "7.82 ms", - "Median": "634.8 ms" + "Mean": "658.5 ms", + "Error": "6.59 ms", + "StdDev": "5.84 ms", + "Median": "655.6 ms" }, { "Method": "xUnit3", "Version": "3.2.0", - "Mean": "723.7 ms", - "Error": "14.23 ms", - "StdDev": "13.31 ms", - "Median": "721.6 ms" + "Mean": "738.2 ms", + "Error": "8.45 ms", + "StdDev": "7.49 ms", + "Median": "735.9 ms" }, { "Method": "TUnit_AOT", "Version": "1.0.78", - "Mean": "123.2 ms", - "Error": "0.71 ms", - "StdDev": "0.66 ms", - "Median": "122.9 ms" + "Mean": "124.2 ms", + "Error": "0.45 ms", + "StdDev": "0.42 ms", + "Median": "124.4 ms" } ] }, @@ -54,34 +54,34 @@ { "Method": "Build_TUnit", "Version": "1.0.78", - "Mean": "1.993 s", - "Error": "0.0167 s", - "StdDev": "0.0148 s", - "Median": "1.991 s" + "Mean": "2.061 s", + "Error": "0.0216 s", + "StdDev": "0.0202 s", + "Median": "2.060 s" }, { "Method": "Build_NUnit", "Version": "4.4.0", - "Mean": "1.602 s", - "Error": "0.0135 s", - "StdDev": "0.0126 s", - "Median": "1.602 s" + "Mean": "1.645 s", + "Error": "0.0165 s", + "StdDev": "0.0147 s", + "Median": "1.645 s" }, { "Method": "Build_MSTest", "Version": "4.0.2", - "Mean": "1.677 s", - "Error": "0.0081 s", - "StdDev": "0.0063 s", - "Median": "1.678 s" + "Mean": "1.712 s", + "Error": "0.0149 s", + "StdDev": "0.0132 s", + "Median": "1.710 s" }, { "Method": "Build_xUnit3", "Version": "3.2.0", - "Mean": "1.586 s", - "Error": "0.0135 s", - "StdDev": "0.0126 s", - "Median": "1.590 s" + "Mean": "1.615 s", + "Error": "0.0160 s", + "StdDev": "0.0149 s", + "Median": "1.619 s" } ] }, @@ -89,6 +89,6 @@ "runtimeCategories": 1, "buildCategories": 1, "totalBenchmarks": 2, - "lastUpdated": "2025-11-12T00:30:03.764Z" + "lastUpdated": "2025-11-12T00:44:12.200Z" } } \ No newline at end of file diff --git a/docs/static/benchmarks/results.json b/docs/static/benchmarks/results.json new file mode 100644 index 0000000000..5c66be3364 --- /dev/null +++ b/docs/static/benchmarks/results.json @@ -0,0 +1,51 @@ +{ + "timestamp": "2025-11-12T00:44:12.201Z", + "category": "results", + "environment": { + "benchmarkDotNetVersion": "BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat)", + "sdk": ".NET SDK 10.0.100", + "host": ".NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3" + }, + "results": [ + { + "Method": "TUnit", + "Version": "1.0.78", + "Mean": "569.0 ms", + "Error": "3.87 ms", + "StdDev": "3.62 ms", + "Median": "568.5 ms" + }, + { + "Method": "NUnit", + "Version": "4.4.0", + "Mean": "691.2 ms", + "Error": "5.85 ms", + "StdDev": "5.19 ms", + "Median": "691.0 ms" + }, + { + "Method": "MSTest", + "Version": "4.0.2", + "Mean": "658.5 ms", + "Error": "6.59 ms", + "StdDev": "5.84 ms", + "Median": "655.6 ms" + }, + { + "Method": "xUnit3", + "Version": "3.2.0", + "Mean": "738.2 ms", + "Error": "8.45 ms", + "StdDev": "7.49 ms", + "Median": "735.9 ms" + }, + { + "Method": "TUnit_AOT", + "Version": "1.0.78", + "Mean": "124.2 ms", + "Error": "0.45 ms", + "StdDev": "0.42 ms", + "Median": "124.4 ms" + } + ] +} \ No newline at end of file From f04908138d8328fe887f856af41ee25b840c494c Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 12 Nov 2025 00:49:24 +0000 Subject: [PATCH 45/62] chore(deps): update microsoft.aspnetcore to v10 (#3784) Co-authored-by: Renovate Bot --- Directory.Packages.props | 6 +++--- .../TUnit.AspNet.FSharp/TestProject/TestProject.fsproj | 2 +- .../content/TUnit.AspNet/TestProject/TestProject.csproj | 2 +- TUnit.Templates/content/TUnit.AspNet/WebApp/WebApp.csproj | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 3f2141dd84..418b178cf2 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -16,9 +16,9 @@ - - - + + + diff --git a/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj b/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj index 5bced25117..04588b29e1 100644 --- a/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj +++ b/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj @@ -9,7 +9,7 @@ - + diff --git a/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj b/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj index 2a0c73c2bf..f47a1018ad 100644 --- a/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj +++ b/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj @@ -8,7 +8,7 @@ - + diff --git a/TUnit.Templates/content/TUnit.AspNet/WebApp/WebApp.csproj b/TUnit.Templates/content/TUnit.AspNet/WebApp/WebApp.csproj index 687096f753..d1fadfb3ce 100644 --- a/TUnit.Templates/content/TUnit.AspNet/WebApp/WebApp.csproj +++ b/TUnit.Templates/content/TUnit.AspNet/WebApp/WebApp.csproj @@ -7,7 +7,7 @@ - + From 5fff6ab9e736e0c06686b1acce17950d9f8025bf Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 12 Nov 2025 00:50:00 +0000 Subject: [PATCH 46/62] chore(deps): update dependency microsoft.entityframeworkcore to v10 (#3781) Co-authored-by: Renovate Bot --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 418b178cf2..60ebed43a2 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -32,7 +32,7 @@ - + From a2cff693e19b3ad19867d687567d841c71d95f69 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 12 Nov 2025 17:47:00 +0000 Subject: [PATCH 47/62] Optimize polyfill with embedded options (#3805) * chore(deps): update dependency polyfill to v9 * chore: enable embedded polyfill option for specific target frameworks * chore: replace FilePolyfill usage with direct File methods and clean up polyfill references * chore: update .NET version from net9.0 to net10.0 across multiple modules --------- Co-authored-by: Renovate Bot --- .github/actions/execute-pipeline/action.yml | 2 +- Directory.Build.props | 14 +++++++++++++- Directory.Packages.props | 2 +- Polyfill.targets | 12 ++---------- Roslyn.props | 9 +-------- .../TestsBase.cs | 8 ++++---- .../GenericTypeResolverTests.cs | 2 +- TUnit.Core.SourceGenerator.Tests/TestsBase.cs | 8 ++++---- .../UnifiedReflectionFreeTests.cs | 2 +- TUnit.Core.SourceGenerator.Tests/Verify.cs | 6 +++--- .../Attributes/SkipNetFrameworkAttribute.cs | 2 +- TUnit.Engine.Tests/InvokableTestBase.cs | 2 +- TUnit.Pipeline/Modules/Abstract/TestBaseModule.cs | 2 +- .../Modules/PublishNugetTesterAOTModule.cs | 4 ++-- TUnit.Pipeline/Modules/RunAspNetTestsModule.cs | 2 +- TUnit.Pipeline/Modules/RunEngineTestsModule.cs | 2 +- TUnit.Pipeline/Modules/RunTemplateTestsModule.cs | 2 +- TUnit.Pipeline/Modules/TestNugetPackageModule.cs | 2 +- TUnit.PublicAPI/Tests.cs | 4 ++-- TUnit.PublicAPI/Verify.cs | 6 +++--- .../Polyfills/ModuleInitializerAttribute.cs | 5 ----- TUnit.TestProject/AfterTestAttributeTests.cs | 2 +- .../AfterTests/TestDiscoveryAfterTests.cs | 2 +- .../AfterTests/TestSessionAfterTests.cs | 2 +- .../BeforeTests/TestDiscoveryBeforeTests.cs | 2 +- .../BeforeTests/TestSessionBeforeTests.cs | 2 +- 26 files changed, 50 insertions(+), 58 deletions(-) delete mode 100644 TUnit.TestProject.Library/Polyfills/ModuleInitializerAttribute.cs diff --git a/.github/actions/execute-pipeline/action.yml b/.github/actions/execute-pipeline/action.yml index 55e4893bc4..50c32f7793 100644 --- a/.github/actions/execute-pipeline/action.yml +++ b/.github/actions/execute-pipeline/action.yml @@ -23,7 +23,7 @@ inputs: netversion: description: 'Net version' required: false - default: 'net9.0' + default: 'net10.0' runs: using: "composite" diff --git a/Directory.Build.props b/Directory.Build.props index a351596691..3a10242347 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -69,4 +69,16 @@ - + + true + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + \ No newline at end of file diff --git a/Directory.Packages.props b/Directory.Packages.props index 60ebed43a2..f771af1524 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -59,7 +59,7 @@ - + diff --git a/Polyfill.targets b/Polyfill.targets index 27f0006759..ca88149295 100644 --- a/Polyfill.targets +++ b/Polyfill.targets @@ -1,14 +1,6 @@ - - true - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - + + diff --git a/Roslyn.props b/Roslyn.props index 24cdff7c9e..9a3940c423 100644 --- a/Roslyn.props +++ b/Roslyn.props @@ -1,16 +1,9 @@ - - - true - + - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/TUnit.Assertions.SourceGenerator.Tests/TestsBase.cs b/TUnit.Assertions.SourceGenerator.Tests/TestsBase.cs index eddc6c2fda..10df29a364 100644 --- a/TUnit.Assertions.SourceGenerator.Tests/TestsBase.cs +++ b/TUnit.Assertions.SourceGenerator.Tests/TestsBase.cs @@ -37,7 +37,7 @@ public Task RunTest(string inputFile, Func assertions) public async Task RunTest(string inputFile, RunTestOptions runTestOptions, Func assertions) { #if NET - var source = await FilePolyfill.ReadAllTextAsync(inputFile); + var source = await File.ReadAllTextAsync(inputFile); #else var source = File.ReadAllText(inputFile); #endif @@ -80,7 +80,7 @@ namespace System.Diagnostics.CodeAnalysis; public class UnconditionalSuppressMessageAttribute : Attribute; """, #if NET - ..await Task.WhenAll(runTestOptions.AdditionalFiles.Select(x => FilePolyfill.ReadAllTextAsync(x))), + ..await Task.WhenAll(runTestOptions.AdditionalFiles.Select(x => File.ReadAllTextAsync(x))), #else ..runTestOptions.AdditionalFiles.Select(x => File.ReadAllText(x)), #endif @@ -160,8 +160,8 @@ Have you added required references and additional files? verifyTask = verifyTask.OnVerifyMismatch(async (pair, message, verify) => { - var received = await FilePolyfill.ReadAllTextAsync(pair.ReceivedPath); - var verified = await FilePolyfill.ReadAllTextAsync(pair.VerifiedPath); + var received = await File.ReadAllTextAsync(pair.ReceivedPath); + var verified = await File.ReadAllTextAsync(pair.VerifiedPath); // Better diff message since original one is too large await Assert.That(Scrub(received)).IsEqualTo(Scrub(verified)); diff --git a/TUnit.Core.SourceGenerator.Tests/GenericTypeResolverTests.cs b/TUnit.Core.SourceGenerator.Tests/GenericTypeResolverTests.cs index c074eb0d5f..40b105d8fe 100644 --- a/TUnit.Core.SourceGenerator.Tests/GenericTypeResolverTests.cs +++ b/TUnit.Core.SourceGenerator.Tests/GenericTypeResolverTests.cs @@ -148,7 +148,7 @@ public void TestMethod() private async Task RunTestWithInlineSource(string source) { var tempFile = Path.GetTempFileName() + ".cs"; - await FilePolyfill.WriteAllTextAsync(tempFile, source); + await File.WriteAllTextAsync(tempFile, source); try { diff --git a/TUnit.Core.SourceGenerator.Tests/TestsBase.cs b/TUnit.Core.SourceGenerator.Tests/TestsBase.cs index 161a01beed..98e003ad13 100644 --- a/TUnit.Core.SourceGenerator.Tests/TestsBase.cs +++ b/TUnit.Core.SourceGenerator.Tests/TestsBase.cs @@ -43,7 +43,7 @@ public Task RunTest(string inputFile, Func assertions) public async Task RunTest(string inputFile, RunTestOptions runTestOptions, Func assertions) { #if NET - var source = await FilePolyfill.ReadAllTextAsync(inputFile); + var source = await File.ReadAllTextAsync(inputFile); #else var source = File.ReadAllText(inputFile); #endif @@ -86,7 +86,7 @@ namespace System.Diagnostics.CodeAnalysis; public class UnconditionalSuppressMessageAttribute : Attribute; """, #if NET - ..await Task.WhenAll(runTestOptions.AdditionalFiles.Select(x => FilePolyfill.ReadAllTextAsync(x))), + ..await Task.WhenAll(runTestOptions.AdditionalFiles.Select(x => File.ReadAllTextAsync(x))), #else ..runTestOptions.AdditionalFiles.Select(x => File.ReadAllText(x)), #endif @@ -172,8 +172,8 @@ Have you added required references and additional files? { verifyTask = verifyTask.OnVerifyMismatch(async (pair, message, verify) => { - var received = await FilePolyfill.ReadAllTextAsync(pair.ReceivedPath); - var verified = await FilePolyfill.ReadAllTextAsync(pair.VerifiedPath); + var received = await File.ReadAllTextAsync(pair.ReceivedPath); + var verified = await File.ReadAllTextAsync(pair.VerifiedPath); // Better diff message since original one is too large await Assert.That(Scrub(received)).IsEqualTo(Scrub(verified)); diff --git a/TUnit.Core.SourceGenerator.Tests/UnifiedReflectionFreeTests.cs b/TUnit.Core.SourceGenerator.Tests/UnifiedReflectionFreeTests.cs index 3a039c557d..6182181767 100644 --- a/TUnit.Core.SourceGenerator.Tests/UnifiedReflectionFreeTests.cs +++ b/TUnit.Core.SourceGenerator.Tests/UnifiedReflectionFreeTests.cs @@ -95,7 +95,7 @@ public void TestWithConfiguration() private async Task RunTestWithInlineSource(string source) { var tempFile = Path.GetTempFileName() + ".cs"; - await FilePolyfill.WriteAllTextAsync(tempFile, source); + await File.WriteAllTextAsync(tempFile, source); try { diff --git a/TUnit.Core.SourceGenerator.Tests/Verify.cs b/TUnit.Core.SourceGenerator.Tests/Verify.cs index d1db167879..d15b2ac87a 100644 --- a/TUnit.Core.SourceGenerator.Tests/Verify.cs +++ b/TUnit.Core.SourceGenerator.Tests/Verify.cs @@ -157,15 +157,15 @@ public async Task ToTask() if (!File.Exists(_verifiedPath)) { - await FilePolyfill.WriteAllTextAsync(_receivedPath, NormalizeNewline(final)); + await File.WriteAllTextAsync(_receivedPath, NormalizeNewline(final)); throw new InvalidOperationException($"No verified file found for '{name}'."); } - var approved = await FilePolyfill.ReadAllTextAsync(_verifiedPath); + var approved = await File.ReadAllTextAsync(_verifiedPath); if (!string.Equals(NormalizeNewline(final), NormalizeNewline(approved), StringComparison.Ordinal)) { - await FilePolyfill.WriteAllTextAsync(_receivedPath, NormalizeNewline(final)); + await File.WriteAllTextAsync(_receivedPath, NormalizeNewline(final)); if (_onVerifyMismatch != null) { diff --git a/TUnit.Engine.Tests/Attributes/SkipNetFrameworkAttribute.cs b/TUnit.Engine.Tests/Attributes/SkipNetFrameworkAttribute.cs index 8ad1fb4e2b..b8205c3ad6 100644 --- a/TUnit.Engine.Tests/Attributes/SkipNetFrameworkAttribute.cs +++ b/TUnit.Engine.Tests/Attributes/SkipNetFrameworkAttribute.cs @@ -2,7 +2,7 @@ public class SkipNetFrameworkAttribute(string reason) : SkipAttribute(reason) { - private static readonly string NetVersion = Environment.GetEnvironmentVariable("NET_VERSION") ?? "net9.0"; + private static readonly string NetVersion = Environment.GetEnvironmentVariable("NET_VERSION") ?? "net10.0"; public override Task ShouldSkip(TestRegisteredContext testRegisteredContext) { diff --git a/TUnit.Engine.Tests/InvokableTestBase.cs b/TUnit.Engine.Tests/InvokableTestBase.cs index bc20a0e4a1..1e264449bc 100644 --- a/TUnit.Engine.Tests/InvokableTestBase.cs +++ b/TUnit.Engine.Tests/InvokableTestBase.cs @@ -22,7 +22,7 @@ public static IEnumerable GetTestModes() } } - private static readonly string GetEnvironmentVariable = Environment.GetEnvironmentVariable("NET_VERSION") ?? "net9.0"; + private static readonly string GetEnvironmentVariable = Environment.GetEnvironmentVariable("NET_VERSION") ?? "net10.0"; public static bool IsNetFramework => GetEnvironmentVariable.StartsWith("net4"); diff --git a/TUnit.Pipeline/Modules/Abstract/TestBaseModule.cs b/TUnit.Pipeline/Modules/Abstract/TestBaseModule.cs index 0943197e35..481e4167a3 100644 --- a/TUnit.Pipeline/Modules/Abstract/TestBaseModule.cs +++ b/TUnit.Pipeline/Modules/Abstract/TestBaseModule.cs @@ -14,7 +14,7 @@ protected virtual IEnumerable TestableFrameworks { get { - yield return "net9.0"; + yield return "net10.0"; yield return "net8.0"; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) diff --git a/TUnit.Pipeline/Modules/PublishNugetTesterAOTModule.cs b/TUnit.Pipeline/Modules/PublishNugetTesterAOTModule.cs index 5bbb404429..b15f0e8a03 100644 --- a/TUnit.Pipeline/Modules/PublishNugetTesterAOTModule.cs +++ b/TUnit.Pipeline/Modules/PublishNugetTesterAOTModule.cs @@ -32,8 +32,8 @@ protected override Task ShouldSkip(IPipelineContext context) .FindFile(x => x.Name == "TUnit.NugetTester.csproj") .AssertExists(); - // Test AOT publishing for net8.0 and net9.0 - foreach (var framework in new[] { "net8.0", "net9.0" }) + // Test AOT publishing + foreach (var framework in new[] { "net8.0", "net9.0", "net10.0" }) { var result = await SubModule($"AOT-{framework}", async () => { diff --git a/TUnit.Pipeline/Modules/RunAspNetTestsModule.cs b/TUnit.Pipeline/Modules/RunAspNetTestsModule.cs index ad5ed5103e..e6c974434a 100644 --- a/TUnit.Pipeline/Modules/RunAspNetTestsModule.cs +++ b/TUnit.Pipeline/Modules/RunAspNetTestsModule.cs @@ -22,7 +22,7 @@ public class RunAspNetTestsModule : Module Project = project.Name, NoBuild = true, Configuration = Configuration.Release, - Framework = "net9.0", + Framework = "net10.0", WorkingDirectory = project.Folder!, Arguments = ["--ignore-exit-code", "8", "--hangdump", "--hangdump-filename", "hangdump.aspnet-tests.dmp", "--hangdump-timeout", "5m"], EnvironmentVariables = new Dictionary diff --git a/TUnit.Pipeline/Modules/RunEngineTestsModule.cs b/TUnit.Pipeline/Modules/RunEngineTestsModule.cs index 162ccaf06e..49a70177aa 100644 --- a/TUnit.Pipeline/Modules/RunEngineTestsModule.cs +++ b/TUnit.Pipeline/Modules/RunEngineTestsModule.cs @@ -33,7 +33,7 @@ public class RunEngineTestsModule : Module Project = project.Name, NoBuild = true, Configuration = Configuration.Release, - Framework = "net9.0", + Framework = "net10.0", WorkingDirectory = project.Folder!, Arguments = [ "--hangdump", "--hangdump-filename", $"hangdump.{Environment.OSVersion.Platform}.engine-tests.dmp", "--hangdump-timeout", "30m", diff --git a/TUnit.Pipeline/Modules/RunTemplateTestsModule.cs b/TUnit.Pipeline/Modules/RunTemplateTestsModule.cs index ad6f2384bd..6199c64f72 100644 --- a/TUnit.Pipeline/Modules/RunTemplateTestsModule.cs +++ b/TUnit.Pipeline/Modules/RunTemplateTestsModule.cs @@ -22,7 +22,7 @@ public class RunTemplateTestsModule : Module WorkingDirectory = project.Folder!, NoBuild = true, Configuration = Configuration.Release, - Framework = "net9.0", + Framework = "net10.0", Arguments = ["--", "--hangdump", "--hangdump-filename", "hangdump.template-tests.dmp", "--hangdump-timeout", "5m"], EnvironmentVariables = new Dictionary { diff --git a/TUnit.Pipeline/Modules/TestNugetPackageModule.cs b/TUnit.Pipeline/Modules/TestNugetPackageModule.cs index 6ace295308..e9045d5c6e 100644 --- a/TUnit.Pipeline/Modules/TestNugetPackageModule.cs +++ b/TUnit.Pipeline/Modules/TestNugetPackageModule.cs @@ -37,7 +37,7 @@ protected override IEnumerable TestableFrameworks { get { - yield return "net9.0"; + yield return "net10.0"; yield return "net8.0"; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) diff --git a/TUnit.PublicAPI/Tests.cs b/TUnit.PublicAPI/Tests.cs index 0aa7f2dff9..67830f80f0 100644 --- a/TUnit.PublicAPI/Tests.cs +++ b/TUnit.PublicAPI/Tests.cs @@ -51,8 +51,8 @@ await VerifyTUnit.Verify(publicApi) .ScrubFilePaths() .OnVerifyMismatch(async (pair, message, verify) => { - var received = await FilePolyfill.ReadAllTextAsync(pair.ReceivedPath); - var verified = await FilePolyfill.ReadAllTextAsync(pair.VerifiedPath); + var received = await File.ReadAllTextAsync(pair.ReceivedPath); + var verified = await File.ReadAllTextAsync(pair.VerifiedPath); // Better diff message since original one is too large await Assert.That(Scrub(received)).IsEqualTo(Scrub(verified)); diff --git a/TUnit.PublicAPI/Verify.cs b/TUnit.PublicAPI/Verify.cs index d057db9a0b..5059fc603c 100644 --- a/TUnit.PublicAPI/Verify.cs +++ b/TUnit.PublicAPI/Verify.cs @@ -129,15 +129,15 @@ public async Task ToTask() if (!File.Exists(_verifiedPath)) { - await FilePolyfill.WriteAllTextAsync(_receivedPath, NormalizeNewline(final)); + await File.WriteAllTextAsync(_receivedPath, NormalizeNewline(final)); throw new InvalidOperationException($"No verified file found for '{name}'."); } - var approved = await FilePolyfill.ReadAllTextAsync(_verifiedPath); + var approved = await File.ReadAllTextAsync(_verifiedPath); if (!string.Equals(NormalizeNewline(final), NormalizeNewline(approved), StringComparison.Ordinal)) { - await FilePolyfill.WriteAllTextAsync(_receivedPath, NormalizeNewline(final)); + await File.WriteAllTextAsync(_receivedPath, NormalizeNewline(final)); if (_onVerifyMismatch != null) { diff --git a/TUnit.TestProject.Library/Polyfills/ModuleInitializerAttribute.cs b/TUnit.TestProject.Library/Polyfills/ModuleInitializerAttribute.cs deleted file mode 100644 index 49cce8f4f0..0000000000 --- a/TUnit.TestProject.Library/Polyfills/ModuleInitializerAttribute.cs +++ /dev/null @@ -1,5 +0,0 @@ -// ReSharper disable once CheckNamespace -namespace System.Runtime.CompilerServices; - -// ReSharper disable once RedundantExtendsListEntry -sealed class ModuleInitializerAttribute : Attribute; diff --git a/TUnit.TestProject/AfterTestAttributeTests.cs b/TUnit.TestProject/AfterTestAttributeTests.cs index 47e9022801..4aba172d1d 100644 --- a/TUnit.TestProject/AfterTestAttributeTests.cs +++ b/TUnit.TestProject/AfterTestAttributeTests.cs @@ -19,7 +19,7 @@ public async ValueTask OnTestEnd(TestContext testContext) { Console.WriteLine(@"Writing file inside WriteFileAfterTestAttribute!"); - await FilePolyfill.WriteAllTextAsync(Filename, "Foo!"); + await File.WriteAllTextAsync(Filename, "Foo!"); } public int Order => 0; diff --git a/TUnit.TestProject/AfterTests/TestDiscoveryAfterTests.cs b/TUnit.TestProject/AfterTests/TestDiscoveryAfterTests.cs index 9a7b82e62a..1adac76180 100644 --- a/TUnit.TestProject/AfterTests/TestDiscoveryAfterTests.cs +++ b/TUnit.TestProject/AfterTests/TestDiscoveryAfterTests.cs @@ -11,7 +11,7 @@ public static async Task AfterTestDiscovery(TestDiscoveryContext context) [AfterEvery(TestDiscovery)] public static async Task AfterEveryTestDiscovery(TestDiscoveryContext context) { - await FilePolyfill.WriteAllTextAsync($"TestDiscoveryAfterTests{Guid.NewGuid():N}.txt", $"{context.AllTests.Count()} tests found"); + await File.WriteAllTextAsync($"TestDiscoveryAfterTests{Guid.NewGuid():N}.txt", $"{context.AllTests.Count()} tests found"); var test = context.AllTests.FirstOrDefault(x => x.Metadata.TestDetails.TestName == nameof(TestDiscoveryAfterTests.EnsureAfterEveryTestDiscoveryHit)); diff --git a/TUnit.TestProject/AfterTests/TestSessionAfterTests.cs b/TUnit.TestProject/AfterTests/TestSessionAfterTests.cs index 384c5f3ba2..34dc0788fb 100644 --- a/TUnit.TestProject/AfterTests/TestSessionAfterTests.cs +++ b/TUnit.TestProject/AfterTests/TestSessionAfterTests.cs @@ -11,7 +11,7 @@ public static async Task AfterTestSession(TestSessionContext context) [AfterEvery(TestSession)] public static async Task AfterEveryTestSession(TestSessionContext context) { - await FilePolyfill.WriteAllTextAsync($"TestSessionAfterTests{Guid.NewGuid():N}.txt", $"{context.AllTests.Count()} tests in session"); + await File.WriteAllTextAsync($"TestSessionAfterTests{Guid.NewGuid():N}.txt", $"{context.AllTests.Count()} tests in session"); var test = context.AllTests.FirstOrDefault(x => x.Metadata.TestDetails.TestName == nameof(TestSessionAfterTests.PepareForAfterSession)); diff --git a/TUnit.TestProject/BeforeTests/TestDiscoveryBeforeTests.cs b/TUnit.TestProject/BeforeTests/TestDiscoveryBeforeTests.cs index da3412f221..73b5060e00 100644 --- a/TUnit.TestProject/BeforeTests/TestDiscoveryBeforeTests.cs +++ b/TUnit.TestProject/BeforeTests/TestDiscoveryBeforeTests.cs @@ -11,7 +11,7 @@ public static async Task BeforeTestDiscovery(BeforeTestDiscoveryContext context) [BeforeEvery(TestDiscovery)] public static async Task BeforeEveryTestDiscovery(BeforeTestDiscoveryContext context) { - await FilePolyfill.WriteAllTextAsync($"TestDiscoveryBeforeTests{Guid.NewGuid():N}.txt", $"Blah!"); + await File.WriteAllTextAsync($"TestDiscoveryBeforeTests{Guid.NewGuid():N}.txt", $"Blah!"); } } diff --git a/TUnit.TestProject/BeforeTests/TestSessionBeforeTests.cs b/TUnit.TestProject/BeforeTests/TestSessionBeforeTests.cs index 8c443ef421..8baf92eadd 100644 --- a/TUnit.TestProject/BeforeTests/TestSessionBeforeTests.cs +++ b/TUnit.TestProject/BeforeTests/TestSessionBeforeTests.cs @@ -11,7 +11,7 @@ public static async Task BeforeTestSession(TestSessionContext context) [BeforeEvery(TestSession)] public static async Task BeforeEveryTestSession(TestSessionContext context) { - await FilePolyfill.WriteAllTextAsync($"TestSessionBeforeTests{Guid.NewGuid():N}.txt", $"{context.AllTests.Count()} tests in session"); + await File.WriteAllTextAsync($"TestSessionBeforeTests{Guid.NewGuid():N}.txt", $"{context.AllTests.Count()} tests in session"); var test = context.AllTests.FirstOrDefault(x => x.Metadata.TestDetails.TestName == nameof(TestSessionBeforeTests.EnsureBeforeEveryTestSessionHit)); From ca3f17ac6e1b48a4216e44d4bda74d17d0d01b9b Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 12 Nov 2025 18:15:59 +0000 Subject: [PATCH 48/62] docs: fix DI attribute example (#3802) --- docs/docs/test-lifecycle/property-injection.md | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/docs/docs/test-lifecycle/property-injection.md b/docs/docs/test-lifecycle/property-injection.md index 58470f0fa9..022af12955 100644 --- a/docs/docs/test-lifecycle/property-injection.md +++ b/docs/docs/test-lifecycle/property-injection.md @@ -8,11 +8,11 @@ The required keyword keeps your code clean and correct. If a property isn't pass ## AOT-Compatible Property Attributes Supported attributes for properties in AOT mode: -- **Argument** - Compile-time constant values -- **MethodDataSource** - Static method data sources -- **ClassDataSource** - Static class-based data sources +- **MethodDataSource** - Data sources via calling a method +- **ClassDataSource** - A data source that injects in T - **DataSourceGeneratorAttribute** - Source-generated data (first item only) -- **ClassDataSource** - Dependency injection with service provider + +For dependency injection with service providers, inherit from `DependencyInjectionDataSourceAttribute` to create custom attributes. See [Dependency Injection](./dependency-injection.md) documentation for details. The AOT system generates strongly-typed property setters at compile time, eliminating reflection overhead and ensuring Native AOT compatibility. @@ -55,10 +55,6 @@ namespace MyTestProject; public class PropertySetterTests { - // Compile-time constant injection - [Arguments("1")] - public required string Property1 { get; init; } - // Static method data source injection [MethodDataSource(nameof(GetMethodData))] public required string Property2 { get; init; } @@ -83,7 +79,7 @@ public class PropertySetterTests [DataSourceGeneratorTests.AutoFixtureGenerator] public required string Property7 { get; init; } - // Service provider dependency injection + // Async initialization example (IAsyncInitializer) [ClassDataSource] public required AsyncPropertyExample AsyncService { get; init; } @@ -91,7 +87,6 @@ public class PropertySetterTests public async Task Test() { // All properties are automatically initialized before this test runs - await Assert.That(Property1).IsEqualTo("1"); await Assert.That(Property2).IsNotNull(); await Assert.That(Property3).IsNotNull(); await Assert.That(AsyncService.IsInitialized).IsTrue(); From 0f8dacc53a391b1910bd8c2d96df49f93f0a19cb Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 12 Nov 2025 18:21:30 +0000 Subject: [PATCH 49/62] docs: fix formatting error causing build failure (#3807) --- docs/docs/test-lifecycle/property-injection.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/test-lifecycle/property-injection.md b/docs/docs/test-lifecycle/property-injection.md index 022af12955..e770ab1fc1 100644 --- a/docs/docs/test-lifecycle/property-injection.md +++ b/docs/docs/test-lifecycle/property-injection.md @@ -9,7 +9,7 @@ The required keyword keeps your code clean and correct. If a property isn't pass Supported attributes for properties in AOT mode: - **MethodDataSource** - Data sources via calling a method -- **ClassDataSource** - A data source that injects in T +- **`ClassDataSource`** - A data source that injects in T - **DataSourceGeneratorAttribute** - Source-generated data (first item only) For dependency injection with service providers, inherit from `DependencyInjectionDataSourceAttribute` to create custom attributes. See [Dependency Injection](./dependency-injection.md) documentation for details. From f81f092429c7fc9fd3572719a1ef88ef649e7397 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 12 Nov 2025 18:58:38 +0000 Subject: [PATCH 50/62] chore(deps): update verify to 31.6.0 (#3808) Co-authored-by: Renovate Bot --- Directory.Packages.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index f771af1524..d4a959ae77 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -81,12 +81,12 @@ - - + + - + From 6c23dc34f3577834374e7597407f81a282d302bf Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 12 Nov 2025 18:58:56 +0000 Subject: [PATCH 51/62] chore(deps): update opentelemetry to 1.14.0 (#3806) Co-authored-by: Renovate Bot --- .../ExampleNamespace.ServiceDefaults.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.ServiceDefaults/ExampleNamespace.ServiceDefaults.csproj b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.ServiceDefaults/ExampleNamespace.ServiceDefaults.csproj index 0eaff9455a..6e350de0e8 100644 --- a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.ServiceDefaults/ExampleNamespace.ServiceDefaults.csproj +++ b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.ServiceDefaults/ExampleNamespace.ServiceDefaults.csproj @@ -12,8 +12,8 @@ - - + + From 2cd5b0b557ab2dfbefe1d965272076a99fa5d5d8 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 12 Nov 2025 19:06:20 +0000 Subject: [PATCH 52/62] +semver:minor - Fix ClassDataSource nested property injection not respecting shared types (#3809) * Add test containers and tests for bug #3803: ensure single instance per test session * Fix ClassDataSource nested property injection not respecting shared types --- .../Attributes/TestData/ClassDataSources.cs | 25 +--- .../Bugs/3803/TestRabbitContainer.cs | 51 +++++++ .../Bugs/3803/TestSqlContainer.cs | 51 +++++++ TUnit.TestProject/Bugs/3803/Tests.cs | 135 ++++++++++++++++++ .../Bugs/3803/WebApplicationFactory.cs | 61 ++++++++ 5 files changed, 301 insertions(+), 22 deletions(-) create mode 100644 TUnit.TestProject/Bugs/3803/TestRabbitContainer.cs create mode 100644 TUnit.TestProject/Bugs/3803/TestSqlContainer.cs create mode 100644 TUnit.TestProject/Bugs/3803/Tests.cs create mode 100644 TUnit.TestProject/Bugs/3803/WebApplicationFactory.cs diff --git a/TUnit.Core/Attributes/TestData/ClassDataSources.cs b/TUnit.Core/Attributes/TestData/ClassDataSources.cs index a933e860b4..efad3790cd 100644 --- a/TUnit.Core/Attributes/TestData/ClassDataSources.cs +++ b/TUnit.Core/Attributes/TestData/ClassDataSources.cs @@ -73,24 +73,21 @@ private string GetKey(int index, SharedType[] sharedTypes, string[] keys) private static object Create([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type, DataGeneratorMetadata dataGeneratorMetadata) { - return CreateWithNestedDependencies(type, dataGeneratorMetadata, recursionDepth: 0); + return Create(type, dataGeneratorMetadata, recursionDepth: 0); } private const int MaxRecursionDepth = 10; - [UnconditionalSuppressMessage("Trimming", "IL2072:Target parameter argument does not satisfy 'DynamicallyAccessedMembersAttribute' requirements", - Justification = "PropertyType from PropertyInjectionMetadata has the required DynamicallyAccessedMembers annotations")] - private static object CreateWithNestedDependencies([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type, DataGeneratorMetadata dataGeneratorMetadata, int recursionDepth) + private static object Create([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type, DataGeneratorMetadata dataGeneratorMetadata, int recursionDepth) { if (recursionDepth >= MaxRecursionDepth) { throw new InvalidOperationException($"Maximum recursion depth ({MaxRecursionDepth}) exceeded when creating nested ClassDataSource dependencies. This may indicate a circular dependency."); } - object instance; try { - instance = Activator.CreateInstance(type)!; + return Activator.CreateInstance(type)!; } catch (TargetInvocationException targetInvocationException) { @@ -101,21 +98,5 @@ private static object CreateWithNestedDependencies([DynamicallyAccessedMembers(D throw; } - - // Populate nested ClassDataSource properties recursively - var propertySource = PropertySourceRegistry.GetSource(type); - if (propertySource?.ShouldInitialize == true) - { - var propertyMetadata = propertySource.GetPropertyMetadata(); - foreach (var metadata in propertyMetadata) - { - // Recursively create the property value using CreateWithNestedDependencies - // This will handle nested ClassDataSource properties - var propertyValue = CreateWithNestedDependencies(metadata.PropertyType, dataGeneratorMetadata, recursionDepth + 1); - metadata.SetProperty(instance, propertyValue); - } - } - - return instance; } } diff --git a/TUnit.TestProject/Bugs/3803/TestRabbitContainer.cs b/TUnit.TestProject/Bugs/3803/TestRabbitContainer.cs new file mode 100644 index 0000000000..58c1a14622 --- /dev/null +++ b/TUnit.TestProject/Bugs/3803/TestRabbitContainer.cs @@ -0,0 +1,51 @@ +using TUnit.Core.Interfaces; + +namespace TUnit.TestProject.Bugs._3803; + +/// +/// Simulates a RabbitMQ test container. +/// This class should be instantiated only once per test session when marked as SharedType.PerTestSession. +/// +public class TestRabbitContainer : IAsyncInitializer, IAsyncDisposable +{ + private static int _instanceCount = 0; + private static int _initializeCount = 0; + private static int _disposeCount = 0; + + public static int InstanceCount => _instanceCount; + public static int InitializeCount => _initializeCount; + public static int DisposeCount => _disposeCount; + + public int InstanceId { get; } + public bool IsInitialized { get; private set; } + public bool IsDisposed { get; private set; } + + public TestRabbitContainer() + { + InstanceId = Interlocked.Increment(ref _instanceCount); + Console.WriteLine($"[TestRabbitContainer] Constructor called - Instance #{InstanceId}"); + } + + public Task InitializeAsync() + { + Interlocked.Increment(ref _initializeCount); + IsInitialized = true; + Console.WriteLine($"[TestRabbitContainer] InitializeAsync called - Instance #{InstanceId}"); + return Task.CompletedTask; + } + + public ValueTask DisposeAsync() + { + Interlocked.Increment(ref _disposeCount); + IsDisposed = true; + Console.WriteLine($"[TestRabbitContainer] DisposeAsync called - Instance #{InstanceId}"); + return default; + } + + public static void ResetCounters() + { + _instanceCount = 0; + _initializeCount = 0; + _disposeCount = 0; + } +} diff --git a/TUnit.TestProject/Bugs/3803/TestSqlContainer.cs b/TUnit.TestProject/Bugs/3803/TestSqlContainer.cs new file mode 100644 index 0000000000..57c6f317db --- /dev/null +++ b/TUnit.TestProject/Bugs/3803/TestSqlContainer.cs @@ -0,0 +1,51 @@ +using TUnit.Core.Interfaces; + +namespace TUnit.TestProject.Bugs._3803; + +/// +/// Simulates a SQL test container. +/// This class should be instantiated only once per test session when marked as SharedType.PerTestSession. +/// +public class TestSqlContainer : IAsyncInitializer, IAsyncDisposable +{ + private static int _instanceCount = 0; + private static int _initializeCount = 0; + private static int _disposeCount = 0; + + public static int InstanceCount => _instanceCount; + public static int InitializeCount => _initializeCount; + public static int DisposeCount => _disposeCount; + + public int InstanceId { get; } + public bool IsInitialized { get; private set; } + public bool IsDisposed { get; private set; } + + public TestSqlContainer() + { + InstanceId = Interlocked.Increment(ref _instanceCount); + Console.WriteLine($"[TestSqlContainer] Constructor called - Instance #{InstanceId}"); + } + + public Task InitializeAsync() + { + Interlocked.Increment(ref _initializeCount); + IsInitialized = true; + Console.WriteLine($"[TestSqlContainer] InitializeAsync called - Instance #{InstanceId}"); + return Task.CompletedTask; + } + + public ValueTask DisposeAsync() + { + Interlocked.Increment(ref _disposeCount); + IsDisposed = true; + Console.WriteLine($"[TestSqlContainer] DisposeAsync called - Instance #{InstanceId}"); + return default; + } + + public static void ResetCounters() + { + _instanceCount = 0; + _initializeCount = 0; + _disposeCount = 0; + } +} diff --git a/TUnit.TestProject/Bugs/3803/Tests.cs b/TUnit.TestProject/Bugs/3803/Tests.cs new file mode 100644 index 0000000000..556c173d52 --- /dev/null +++ b/TUnit.TestProject/Bugs/3803/Tests.cs @@ -0,0 +1,135 @@ +using TUnit.TestProject.Attributes; + +namespace TUnit.TestProject.Bugs._3803; + +/// +/// Bug #3803: Nested dependencies with SharedType.PerTestSession are instantiated multiple times +/// +/// Expected behavior: +/// - TestRabbitContainer should be instantiated ONCE per test session (InstanceCount == 1) +/// - TestSqlContainer should be instantiated ONCE per test session (InstanceCount == 1) +/// - All WebApplicationFactory instances should receive the SAME container instances +/// +/// Actual behavior (BUG): +/// - Containers are instantiated multiple times (once per test or once per factory) +/// - Each WebApplicationFactory receives DIFFERENT container instances +/// + +[NotInParallel] +[EngineTest(ExpectedResult.Pass)] +public class Bug3803_TestClass1 +{ + [ClassDataSource(Shared = SharedType.PerTestSession)] + public required WebApplicationFactory Factory { get; init; } + + [Test] + public async Task Test1_VerifyContainersAreShared() + { + Console.WriteLine($"[Bug3803_TestClass1.Test1] Factory Instance: #{Factory.InstanceId}"); + Console.WriteLine($"[Bug3803_TestClass1.Test1] RabbitContainer Instance: #{Factory.RabbitContainer.InstanceId}"); + Console.WriteLine($"[Bug3803_TestClass1.Test1] SqlContainer Instance: #{Factory.SqlContainer.InstanceId}"); + + // Verify containers are initialized + await Assert.That(Factory.RabbitContainer.IsInitialized).IsTrue(); + await Assert.That(Factory.SqlContainer.IsInitialized).IsTrue(); + + // BUG VERIFICATION: These should ALWAYS be 1 if SharedType.PerTestSession works correctly + await Assert.That(TestRabbitContainer.InstanceCount).IsEqualTo(1); + await Assert.That(TestSqlContainer.InstanceCount).IsEqualTo(1); + + // All instances should have ID = 1 (first and only instance) + await Assert.That(Factory.RabbitContainer.InstanceId).IsEqualTo(1); + await Assert.That(Factory.SqlContainer.InstanceId).IsEqualTo(1); + } + + [Test] + public async Task Test2_VerifyContainersAreStillShared() + { + Console.WriteLine($"[Bug3803_TestClass1.Test2] Factory Instance: #{Factory.InstanceId}"); + Console.WriteLine($"[Bug3803_TestClass1.Test2] RabbitContainer Instance: #{Factory.RabbitContainer.InstanceId}"); + Console.WriteLine($"[Bug3803_TestClass1.Test2] SqlContainer Instance: #{Factory.SqlContainer.InstanceId}"); + + // Same assertions as Test1 - containers should still be the same instances + await Assert.That(TestRabbitContainer.InstanceCount).IsEqualTo(1); + await Assert.That(TestSqlContainer.InstanceCount).IsEqualTo(1); + await Assert.That(Factory.RabbitContainer.InstanceId).IsEqualTo(1); + await Assert.That(Factory.SqlContainer.InstanceId).IsEqualTo(1); + } + + [Test] + public async Task Test3_VerifyInitializationCalledOnce() + { + Console.WriteLine($"[Bug3803_TestClass1.Test3] RabbitContainer InitializeCount: {TestRabbitContainer.InitializeCount}"); + Console.WriteLine($"[Bug3803_TestClass1.Test3] SqlContainer InitializeCount: {TestSqlContainer.InitializeCount}"); + + // Initialize should be called only once per container + await Assert.That(TestRabbitContainer.InitializeCount).IsEqualTo(1); + await Assert.That(TestSqlContainer.InitializeCount).IsEqualTo(1); + } +} + +[NotInParallel] +[EngineTest(ExpectedResult.Pass)] +public class Bug3803_TestClass2 +{ + [ClassDataSource(Shared = SharedType.PerTestSession)] + public required WebApplicationFactory Factory { get; init; } + + [Test] + public async Task Test1_DifferentClassShouldGetSameContainers() + { + Console.WriteLine($"[Bug3803_TestClass2.Test1] Factory Instance: #{Factory.InstanceId}"); + Console.WriteLine($"[Bug3803_TestClass2.Test1] RabbitContainer Instance: #{Factory.RabbitContainer.InstanceId}"); + Console.WriteLine($"[Bug3803_TestClass2.Test1] SqlContainer Instance: #{Factory.SqlContainer.InstanceId}"); + + // Even in a different test class, we should get the SAME container instances + await Assert.That(TestRabbitContainer.InstanceCount).IsEqualTo(1); + await Assert.That(TestSqlContainer.InstanceCount).IsEqualTo(1); + + // Should be the same instance (ID = 1) + await Assert.That(Factory.RabbitContainer.InstanceId).IsEqualTo(1); + await Assert.That(Factory.SqlContainer.InstanceId).IsEqualTo(1); + } + + [Test] + public async Task Test2_VerifyContainersAreInitialized() + { + await Assert.That(Factory.RabbitContainer.IsInitialized).IsTrue(); + await Assert.That(Factory.SqlContainer.IsInitialized).IsTrue(); + } +} + +[NotInParallel] +[EngineTest(ExpectedResult.Pass)] +public class Bug3803_TestClass3 +{ + [ClassDataSource(Shared = SharedType.PerTestSession)] + public required WebApplicationFactory Factory { get; init; } + + [Test] + public async Task Test1_ThirdClassAlsoGetsSameContainers() + { + Console.WriteLine($"[Bug3803_TestClass3.Test1] Factory Instance: #{Factory.InstanceId}"); + Console.WriteLine($"[Bug3803_TestClass3.Test1] RabbitContainer Instance: #{Factory.RabbitContainer.InstanceId}"); + Console.WriteLine($"[Bug3803_TestClass3.Test1] SqlContainer Instance: #{Factory.SqlContainer.InstanceId}"); + + // Still the same instances + await Assert.That(TestRabbitContainer.InstanceCount).IsEqualTo(1); + await Assert.That(TestSqlContainer.InstanceCount).IsEqualTo(1); + await Assert.That(Factory.RabbitContainer.InstanceId).IsEqualTo(1); + await Assert.That(Factory.SqlContainer.InstanceId).IsEqualTo(1); + } + + [Test] + public async Task Test2_FinalVerification() + { + Console.WriteLine($"[Bug3803_TestClass3.Test2] Final verification"); + Console.WriteLine($" Total RabbitContainer instances: {TestRabbitContainer.InstanceCount}"); + Console.WriteLine($" Total SqlContainer instances: {TestSqlContainer.InstanceCount}"); + Console.WriteLine($" Total WebApplicationFactory instances: {WebApplicationFactory.InstanceCount}"); + + // Final assertion: containers should have been instantiated exactly once + await Assert.That(TestRabbitContainer.InstanceCount).IsEqualTo(1); + await Assert.That(TestSqlContainer.InstanceCount).IsEqualTo(1); + } +} diff --git a/TUnit.TestProject/Bugs/3803/WebApplicationFactory.cs b/TUnit.TestProject/Bugs/3803/WebApplicationFactory.cs new file mode 100644 index 0000000000..3172f4d792 --- /dev/null +++ b/TUnit.TestProject/Bugs/3803/WebApplicationFactory.cs @@ -0,0 +1,61 @@ +using TUnit.Core.Interfaces; + +namespace TUnit.TestProject.Bugs._3803; + +/// +/// Simulates a WebApplicationFactory that depends on test containers. +/// The containers should be shared (SharedType.PerTestSession), meaning: +/// - Each container should be instantiated only ONCE per test session +/// - All instances of WebApplicationFactory should receive the SAME container instances +/// +public class WebApplicationFactory : IAsyncInitializer, IAsyncDisposable +{ + private static int _instanceCount = 0; + private static int _initializeCount = 0; + private static int _disposeCount = 0; + + public static int InstanceCount => _instanceCount; + public static int InitializeCount => _initializeCount; + public static int DisposeCount => _disposeCount; + + public int InstanceId { get; } + public bool IsInitialized { get; private set; } + public bool IsDisposed { get; private set; } + + [ClassDataSource(Shared = SharedType.PerTestSession)] + public required TestRabbitContainer RabbitContainer { get; init; } + + [ClassDataSource(Shared = SharedType.PerTestSession)] + public required TestSqlContainer SqlContainer { get; init; } + + public WebApplicationFactory() + { + InstanceId = Interlocked.Increment(ref _instanceCount); + Console.WriteLine($"[WebApplicationFactory] Constructor called - Instance #{InstanceId}"); + } + + public Task InitializeAsync() + { + Interlocked.Increment(ref _initializeCount); + IsInitialized = true; + Console.WriteLine($"[WebApplicationFactory] InitializeAsync called - Instance #{InstanceId}"); + Console.WriteLine($" -> RabbitContainer Instance: #{RabbitContainer.InstanceId}"); + Console.WriteLine($" -> SqlContainer Instance: #{SqlContainer.InstanceId}"); + return Task.CompletedTask; + } + + public ValueTask DisposeAsync() + { + Interlocked.Increment(ref _disposeCount); + IsDisposed = true; + Console.WriteLine($"[WebApplicationFactory] DisposeAsync called - Instance #{InstanceId}"); + return default; + } + + public static void ResetCounters() + { + _instanceCount = 0; + _initializeCount = 0; + _disposeCount = 0; + } +} From 6f5720e91cd764604827401225809000eee7667a Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 12 Nov 2025 20:05:40 +0000 Subject: [PATCH 53/62] chore(deps): update tunit to 1.1.0 (#3799) Co-authored-by: Renovate Bot --- Directory.Packages.props | 6 +++--- .../TUnit.AspNet.FSharp/TestProject/TestProject.fsproj | 4 ++-- .../content/TUnit.AspNet/TestProject/TestProject.csproj | 2 +- .../ExampleNamespace.TestProject.csproj | 2 +- .../content/TUnit.Aspire.Test/ExampleNamespace.csproj | 2 +- TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj | 4 ++-- TUnit.Templates/content/TUnit.Playwright/TestProject.csproj | 2 +- TUnit.Templates/content/TUnit.VB/TestProject.vbproj | 2 +- TUnit.Templates/content/TUnit/TestProject.csproj | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index d4a959ae77..7fd5f5e6e3 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -83,9 +83,9 @@ - - - + + + diff --git a/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj b/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj index 04588b29e1..466f212a0f 100644 --- a/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj +++ b/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj @@ -10,8 +10,8 @@ - - + + diff --git a/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj b/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj index f47a1018ad..3b4dea1e34 100644 --- a/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj +++ b/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj @@ -9,7 +9,7 @@ - + diff --git a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.TestProject/ExampleNamespace.TestProject.csproj b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.TestProject/ExampleNamespace.TestProject.csproj index a92efa13aa..2d85bf717d 100644 --- a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.TestProject/ExampleNamespace.TestProject.csproj +++ b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.TestProject/ExampleNamespace.TestProject.csproj @@ -11,7 +11,7 @@ - + diff --git a/TUnit.Templates/content/TUnit.Aspire.Test/ExampleNamespace.csproj b/TUnit.Templates/content/TUnit.Aspire.Test/ExampleNamespace.csproj index c73e63b202..87faa21d22 100644 --- a/TUnit.Templates/content/TUnit.Aspire.Test/ExampleNamespace.csproj +++ b/TUnit.Templates/content/TUnit.Aspire.Test/ExampleNamespace.csproj @@ -10,7 +10,7 @@ - + diff --git a/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj b/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj index d1f6d6dfed..adafbe6af9 100644 --- a/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj +++ b/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj @@ -10,8 +10,8 @@ - - + + diff --git a/TUnit.Templates/content/TUnit.Playwright/TestProject.csproj b/TUnit.Templates/content/TUnit.Playwright/TestProject.csproj index 5f8bef5d2a..662edbd9f1 100644 --- a/TUnit.Templates/content/TUnit.Playwright/TestProject.csproj +++ b/TUnit.Templates/content/TUnit.Playwright/TestProject.csproj @@ -8,7 +8,7 @@ - + diff --git a/TUnit.Templates/content/TUnit.VB/TestProject.vbproj b/TUnit.Templates/content/TUnit.VB/TestProject.vbproj index 142edd7810..6242ab0689 100644 --- a/TUnit.Templates/content/TUnit.VB/TestProject.vbproj +++ b/TUnit.Templates/content/TUnit.VB/TestProject.vbproj @@ -8,6 +8,6 @@ - + diff --git a/TUnit.Templates/content/TUnit/TestProject.csproj b/TUnit.Templates/content/TUnit/TestProject.csproj index 948ae0ad1f..f0173684fd 100644 --- a/TUnit.Templates/content/TUnit/TestProject.csproj +++ b/TUnit.Templates/content/TUnit/TestProject.csproj @@ -8,7 +8,7 @@ - + \ No newline at end of file From 05ea4b74a0262c0c70dd686b19da79d03ee78f79 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 12 Nov 2025 20:28:43 +0000 Subject: [PATCH 54/62] chore: update benchmark results (#3810) --- docs/docs/benchmarks/BuildTime.md | 12 ++-- docs/docs/benchmarks/index.md | 6 +- docs/docs/benchmarks/results.md | 24 ++++---- docs/static/benchmarks/BuildTime.json | 36 +++++------ docs/static/benchmarks/historical.json | 4 ++ docs/static/benchmarks/latest.json | 82 +++++++++++++------------- docs/static/benchmarks/results.json | 48 +++++++-------- 7 files changed, 108 insertions(+), 104 deletions(-) diff --git a/docs/docs/benchmarks/BuildTime.md b/docs/docs/benchmarks/BuildTime.md index 4a20e0cb3f..ee37330fc4 100644 --- a/docs/docs/benchmarks/BuildTime.md +++ b/docs/docs/benchmarks/BuildTime.md @@ -18,10 +18,10 @@ Compilation time comparison across frameworks: | Framework | Version | Mean | Median | StdDev | |-----------|---------|------|--------|--------| -| **TUnit** | 1.0.78 | 2.061 s | 2.060 s | 0.0202 s | -| Build_NUnit | 4.4.0 | 1.645 s | 1.645 s | 0.0147 s | -| Build_MSTest | 4.0.2 | 1.712 s | 1.710 s | 0.0132 s | -| Build_xUnit3 | 3.2.0 | 1.615 s | 1.619 s | 0.0149 s | +| **TUnit** | 1.1.0 | 2.033 s | 2.032 s | 0.0274 s | +| Build_NUnit | 4.4.0 | 1.626 s | 1.628 s | 0.0146 s | +| Build_MSTest | 4.0.2 | 1.700 s | 1.694 s | 0.0153 s | +| Build_xUnit3 | 3.2.0 | 1.607 s | 1.602 s | 0.0149 s | ## šŸ“ˆ Visual Comparison @@ -45,7 +45,7 @@ xychart-beta title "Build Time Comparison" x-axis ["Build_TUnit", "Build_NUnit", "Build_MSTest", "Build_xUnit3"] y-axis "Time (s)" 0 --> 3 - bar [2.061, 1.645, 1.712, 1.615] + bar [2.033, 1.626, 1.7, 1.607] ``` --- @@ -54,4 +54,4 @@ xychart-beta View the [benchmarks overview](/docs/benchmarks) for methodology details and environment information. ::: -*Last generated: 2025-11-12T00:44:12.201Z* +*Last generated: 2025-11-12T20:28:34.075Z* diff --git a/docs/docs/benchmarks/index.md b/docs/docs/benchmarks/index.md index 4795f4d795..338ba92eec 100644 --- a/docs/docs/benchmarks/index.md +++ b/docs/docs/benchmarks/index.md @@ -32,7 +32,7 @@ These benchmarks compare TUnit against the most popular .NET testing frameworks: | Framework | Version Tested | |-----------|----------------| -| **TUnit** | 1.0.78 | +| **TUnit** | 1.1.0 | | **xUnit v3** | 3.2.0 | | **NUnit** | 4.4.0 | | **MSTest** | 4.0.2 | @@ -51,7 +51,7 @@ The benchmarks measure real-world testing patterns: ### Environment - **OS**: Ubuntu Latest (GitHub Actions) -- **Runtime**: .NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3 +- **Runtime**: .NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v4 - **SDK**: .NET SDK 10.0.100 - **Hardware**: GitHub Actions Standard Runner (Ubuntu) - **Tool**: BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat) @@ -75,4 +75,4 @@ These benchmarks run automatically daily via [GitHub Actions](https://github.com Each benchmark runs multiple iterations with statistical analysis to ensure accuracy. Results may vary based on hardware and test characteristics. ::: -*Last generated: 2025-11-12T00:44:12.201Z* +*Last generated: 2025-11-12T20:28:34.076Z* diff --git a/docs/docs/benchmarks/results.md b/docs/docs/benchmarks/results.md index 7fa319d416..e2bf8fe4ba 100644 --- a/docs/docs/benchmarks/results.md +++ b/docs/docs/benchmarks/results.md @@ -16,11 +16,11 @@ This benchmark was automatically generated on **2025-11-12** from the latest CI | Framework | Version | Mean | Median | StdDev | |-----------|---------|------|--------|--------| -| **TUnit** | 1.0.78 | 569.0 ms | 568.5 ms | 3.62 ms | -| NUnit | 4.4.0 | 691.2 ms | 691.0 ms | 5.19 ms | -| MSTest | 4.0.2 | 658.5 ms | 655.6 ms | 5.84 ms | -| xUnit3 | 3.2.0 | 738.2 ms | 735.9 ms | 7.49 ms | -| **TUnit (AOT)** | 1.0.78 | 124.2 ms | 124.4 ms | 0.42 ms | +| **TUnit** | 1.1.0 | 536.11 ms | 532.73 ms | 6.770 ms | +| NUnit | 4.4.0 | 1,556.59 ms | 1,554.77 ms | 10.335 ms | +| MSTest | 4.0.2 | 1,526.36 ms | 1,525.39 ms | 3.795 ms | +| xUnit3 | 3.2.0 | 1,603.59 ms | 1,604.07 ms | 8.271 ms | +| **TUnit (AOT)** | 1.1.0 | 77.73 ms | 77.78 ms | 0.214 ms | ## šŸ“ˆ Visual Comparison @@ -43,16 +43,16 @@ This benchmark was automatically generated on **2025-11-12** from the latest CI xychart-beta title "results Performance Comparison" x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"] - y-axis "Time (ms)" 0 --> 886 - bar [569, 691.2, 658.5, 738.2, 124.2] + y-axis "Time (ms)" 0 --> 1925 + bar [536.11, 1556.59, 1526.36, 1603.59, 77.73] ``` ## šŸŽÆ Key Insights -- **1.21x faster** than NUnit (4.4.0) -- **1.16x faster** than MSTest (4.0.2) -- **1.30x faster** than xUnit3 (3.2.0) -- **4.58x faster** with Native AOT compilation +- **2.90x faster** than NUnit (4.4.0) +- **2.85x faster** than MSTest (4.0.2) +- **2.99x faster** than xUnit3 (3.2.0) +- **6.90x faster** with Native AOT compilation --- @@ -60,4 +60,4 @@ xychart-beta View the [benchmarks overview](/docs/benchmarks) for methodology details and environment information. ::: -*Last generated: 2025-11-12T00:44:12.201Z* +*Last generated: 2025-11-12T20:28:34.075Z* diff --git a/docs/static/benchmarks/BuildTime.json b/docs/static/benchmarks/BuildTime.json index 2f4f507e3d..edfd5b22ed 100644 --- a/docs/static/benchmarks/BuildTime.json +++ b/docs/static/benchmarks/BuildTime.json @@ -1,43 +1,43 @@ { - "timestamp": "2025-11-12T00:44:12.201Z", + "timestamp": "2025-11-12T20:28:34.075Z", "category": "BuildTime", "environment": { "benchmarkDotNetVersion": "BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat)", "sdk": ".NET SDK 10.0.100", - "host": ".NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3" + "host": ".NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v4" }, "results": [ { "Method": "Build_TUnit", - "Version": "1.0.78", - "Mean": "2.061 s", - "Error": "0.0216 s", - "StdDev": "0.0202 s", - "Median": "2.060 s" + "Version": "1.1.0", + "Mean": "2.033 s", + "Error": "0.0293 s", + "StdDev": "0.0274 s", + "Median": "2.032 s" }, { "Method": "Build_NUnit", "Version": "4.4.0", - "Mean": "1.645 s", - "Error": "0.0165 s", - "StdDev": "0.0147 s", - "Median": "1.645 s" + "Mean": "1.626 s", + "Error": "0.0156 s", + "StdDev": "0.0146 s", + "Median": "1.628 s" }, { "Method": "Build_MSTest", "Version": "4.0.2", - "Mean": "1.712 s", - "Error": "0.0149 s", - "StdDev": "0.0132 s", - "Median": "1.710 s" + "Mean": "1.700 s", + "Error": "0.0183 s", + "StdDev": "0.0153 s", + "Median": "1.694 s" }, { "Method": "Build_xUnit3", "Version": "3.2.0", - "Mean": "1.615 s", - "Error": "0.0160 s", + "Mean": "1.607 s", + "Error": "0.0159 s", "StdDev": "0.0149 s", - "Median": "1.619 s" + "Median": "1.602 s" } ] } \ No newline at end of file diff --git a/docs/static/benchmarks/historical.json b/docs/static/benchmarks/historical.json index f0a01a895b..487d1aabe8 100644 --- a/docs/static/benchmarks/historical.json +++ b/docs/static/benchmarks/historical.json @@ -15,6 +15,10 @@ "date": "2025-11-12", "environment": "Ubuntu" }, + { + "date": "2025-11-12", + "environment": "Ubuntu" + }, { "date": "2025-11-12", "environment": "Ubuntu" diff --git a/docs/static/benchmarks/latest.json b/docs/static/benchmarks/latest.json index 0599437f16..aa530b7849 100644 --- a/docs/static/benchmarks/latest.json +++ b/docs/static/benchmarks/latest.json @@ -1,51 +1,51 @@ { - "timestamp": "2025-11-12T00:44:12.201Z", + "timestamp": "2025-11-12T20:28:34.076Z", "environment": { "benchmarkDotNetVersion": "BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat)", "sdk": ".NET SDK 10.0.100", - "host": ".NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3" + "host": ".NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v4" }, "categories": { "results": [ { "Method": "TUnit", - "Version": "1.0.78", - "Mean": "569.0 ms", - "Error": "3.87 ms", - "StdDev": "3.62 ms", - "Median": "568.5 ms" + "Version": "1.1.0", + "Mean": "536.11 ms", + "Error": "7.237 ms", + "StdDev": "6.770 ms", + "Median": "532.73 ms" }, { "Method": "NUnit", "Version": "4.4.0", - "Mean": "691.2 ms", - "Error": "5.85 ms", - "StdDev": "5.19 ms", - "Median": "691.0 ms" + "Mean": "1,556.59 ms", + "Error": "11.049 ms", + "StdDev": "10.335 ms", + "Median": "1,554.77 ms" }, { "Method": "MSTest", "Version": "4.0.2", - "Mean": "658.5 ms", - "Error": "6.59 ms", - "StdDev": "5.84 ms", - "Median": "655.6 ms" + "Mean": "1,526.36 ms", + "Error": "4.282 ms", + "StdDev": "3.795 ms", + "Median": "1,525.39 ms" }, { "Method": "xUnit3", "Version": "3.2.0", - "Mean": "738.2 ms", - "Error": "8.45 ms", - "StdDev": "7.49 ms", - "Median": "735.9 ms" + "Mean": "1,603.59 ms", + "Error": "9.330 ms", + "StdDev": "8.271 ms", + "Median": "1,604.07 ms" }, { "Method": "TUnit_AOT", - "Version": "1.0.78", - "Mean": "124.2 ms", - "Error": "0.45 ms", - "StdDev": "0.42 ms", - "Median": "124.4 ms" + "Version": "1.1.0", + "Mean": "77.73 ms", + "Error": "0.242 ms", + "StdDev": "0.214 ms", + "Median": "77.78 ms" } ] }, @@ -53,35 +53,35 @@ "BuildTime": [ { "Method": "Build_TUnit", - "Version": "1.0.78", - "Mean": "2.061 s", - "Error": "0.0216 s", - "StdDev": "0.0202 s", - "Median": "2.060 s" + "Version": "1.1.0", + "Mean": "2.033 s", + "Error": "0.0293 s", + "StdDev": "0.0274 s", + "Median": "2.032 s" }, { "Method": "Build_NUnit", "Version": "4.4.0", - "Mean": "1.645 s", - "Error": "0.0165 s", - "StdDev": "0.0147 s", - "Median": "1.645 s" + "Mean": "1.626 s", + "Error": "0.0156 s", + "StdDev": "0.0146 s", + "Median": "1.628 s" }, { "Method": "Build_MSTest", "Version": "4.0.2", - "Mean": "1.712 s", - "Error": "0.0149 s", - "StdDev": "0.0132 s", - "Median": "1.710 s" + "Mean": "1.700 s", + "Error": "0.0183 s", + "StdDev": "0.0153 s", + "Median": "1.694 s" }, { "Method": "Build_xUnit3", "Version": "3.2.0", - "Mean": "1.615 s", - "Error": "0.0160 s", + "Mean": "1.607 s", + "Error": "0.0159 s", "StdDev": "0.0149 s", - "Median": "1.619 s" + "Median": "1.602 s" } ] }, @@ -89,6 +89,6 @@ "runtimeCategories": 1, "buildCategories": 1, "totalBenchmarks": 2, - "lastUpdated": "2025-11-12T00:44:12.200Z" + "lastUpdated": "2025-11-12T20:28:34.074Z" } } \ No newline at end of file diff --git a/docs/static/benchmarks/results.json b/docs/static/benchmarks/results.json index 5c66be3364..2d973e76cc 100644 --- a/docs/static/benchmarks/results.json +++ b/docs/static/benchmarks/results.json @@ -1,51 +1,51 @@ { - "timestamp": "2025-11-12T00:44:12.201Z", + "timestamp": "2025-11-12T20:28:34.075Z", "category": "results", "environment": { "benchmarkDotNetVersion": "BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat)", "sdk": ".NET SDK 10.0.100", - "host": ".NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3" + "host": ".NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v4" }, "results": [ { "Method": "TUnit", - "Version": "1.0.78", - "Mean": "569.0 ms", - "Error": "3.87 ms", - "StdDev": "3.62 ms", - "Median": "568.5 ms" + "Version": "1.1.0", + "Mean": "536.11 ms", + "Error": "7.237 ms", + "StdDev": "6.770 ms", + "Median": "532.73 ms" }, { "Method": "NUnit", "Version": "4.4.0", - "Mean": "691.2 ms", - "Error": "5.85 ms", - "StdDev": "5.19 ms", - "Median": "691.0 ms" + "Mean": "1,556.59 ms", + "Error": "11.049 ms", + "StdDev": "10.335 ms", + "Median": "1,554.77 ms" }, { "Method": "MSTest", "Version": "4.0.2", - "Mean": "658.5 ms", - "Error": "6.59 ms", - "StdDev": "5.84 ms", - "Median": "655.6 ms" + "Mean": "1,526.36 ms", + "Error": "4.282 ms", + "StdDev": "3.795 ms", + "Median": "1,525.39 ms" }, { "Method": "xUnit3", "Version": "3.2.0", - "Mean": "738.2 ms", - "Error": "8.45 ms", - "StdDev": "7.49 ms", - "Median": "735.9 ms" + "Mean": "1,603.59 ms", + "Error": "9.330 ms", + "StdDev": "8.271 ms", + "Median": "1,604.07 ms" }, { "Method": "TUnit_AOT", - "Version": "1.0.78", - "Mean": "124.2 ms", - "Error": "0.45 ms", - "StdDev": "0.42 ms", - "Median": "124.4 ms" + "Version": "1.1.0", + "Mean": "77.73 ms", + "Error": "0.242 ms", + "StdDev": "0.214 ms", + "Median": "77.78 ms" } ] } \ No newline at end of file From b133921f4d5fa6caec75c4a87d932b8bc3a0e2a2 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 12 Nov 2025 20:47:37 +0000 Subject: [PATCH 55/62] chore: update benchmark theme colors and enhance artifact uploads --- .github/scripts/process-benchmarks.js | 66 +++++++++++++----- .github/workflows/speed-comparison.yml | 94 +++++++++++++++++++++++++- 2 files changed, 141 insertions(+), 19 deletions(-) diff --git a/.github/scripts/process-benchmarks.js b/.github/scripts/process-benchmarks.js index ecfda6f301..94d908de42 100644 --- a/.github/scripts/process-benchmarks.js +++ b/.github/scripts/process-benchmarks.js @@ -181,16 +181,31 @@ ${data.map(row => { %%{init: { 'theme':'base', 'themeVariables': { - 'primaryColor': '#10b981', - 'primaryTextColor': '#fff', - 'primaryBorderColor': '#059669', - 'lineColor': '#d1d5db', - 'secondaryColor': '#3b82f6', - 'tertiaryColor': '#f59e0b', + 'primaryColor': '#2563eb', + 'primaryTextColor': '#ffffff', + 'primaryBorderColor': '#1e40af', + 'lineColor': '#6b7280', + 'secondaryColor': '#7c3aed', + 'tertiaryColor': '#dc2626', 'background': '#ffffff', - 'mainBkg': '#10b981', - 'secondBkg': '#ef4444', - 'tertiaryBkg': '#f59e0b' + 'mainBkg': '#2563eb', + 'secondBkg': '#7c3aed', + 'tertiaryBkg': '#dc2626', + 'clusterBkg': '#f3f4f6', + 'edgeLabelBackground': '#ffffff', + 'tertiaryTextColor': '#1f2937', + 'pie1': '#2563eb', + 'pie2': '#7c3aed', + 'pie3': '#dc2626', + 'pie4': '#f59e0b', + 'pie5': '#10b981', + 'pie6': '#06b6d4', + 'pie7': '#ec4899', + 'pie8': '#6366f1', + 'pie9': '#84cc16', + 'pie10': '#f97316', + 'pie11': '#14b8a6', + 'pie12': '#a855f7' } }}%% xychart-beta @@ -290,16 +305,31 @@ ${data.map(row => { %%{init: { 'theme':'base', 'themeVariables': { - 'primaryColor': '#10b981', - 'primaryTextColor': '#fff', - 'primaryBorderColor': '#059669', - 'lineColor': '#d1d5db', - 'secondaryColor': '#3b82f6', - 'tertiaryColor': '#f59e0b', + 'primaryColor': '#2563eb', + 'primaryTextColor': '#ffffff', + 'primaryBorderColor': '#1e40af', + 'lineColor': '#6b7280', + 'secondaryColor': '#7c3aed', + 'tertiaryColor': '#dc2626', 'background': '#ffffff', - 'mainBkg': '#10b981', - 'secondBkg': '#ef4444', - 'tertiaryBkg': '#f59e0b' + 'mainBkg': '#2563eb', + 'secondBkg': '#7c3aed', + 'tertiaryBkg': '#dc2626', + 'clusterBkg': '#f3f4f6', + 'edgeLabelBackground': '#ffffff', + 'tertiaryTextColor': '#1f2937', + 'pie1': '#2563eb', + 'pie2': '#7c3aed', + 'pie3': '#dc2626', + 'pie4': '#f59e0b', + 'pie5': '#10b981', + 'pie6': '#06b6d4', + 'pie7': '#ec4899', + 'pie8': '#6366f1', + 'pie9': '#84cc16', + 'pie10': '#f97316', + 'pie11': '#14b8a6', + 'pie12': '#a855f7' } }}%% xychart-beta diff --git a/.github/workflows/speed-comparison.yml b/.github/workflows/speed-comparison.yml index 510e5d4f3e..0573d3292e 100644 --- a/.github/workflows/speed-comparison.yml +++ b/.github/workflows/speed-comparison.yml @@ -123,7 +123,7 @@ jobs: path: | **/BenchmarkDotNet.Artifacts/** - aggregate-and-commit-results: + process-and-upload-benchmarks: needs: [run-time-benchmarks, build-time-benchmarks] runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' @@ -160,6 +160,88 @@ jobs: run: | node .github/scripts/process-benchmarks.js + - name: Upload Individual Runtime Benchmarks + uses: actions/upload-artifact@v5 + if: always() + with: + name: benchmark-DataDrivenTests + path: | + docs/docs/benchmarks/DataDrivenTests.md + docs/static/benchmarks/DataDrivenTests.json + retention-days: 90 + + - name: Upload Individual Runtime Benchmarks + uses: actions/upload-artifact@v5 + if: always() + with: + name: benchmark-AsyncTests + path: | + docs/docs/benchmarks/AsyncTests.md + docs/static/benchmarks/AsyncTests.json + retention-days: 90 + + - name: Upload Individual Runtime Benchmarks + uses: actions/upload-artifact@v5 + if: always() + with: + name: benchmark-ScaleTests + path: | + docs/docs/benchmarks/ScaleTests.md + docs/static/benchmarks/ScaleTests.json + retention-days: 90 + + - name: Upload Individual Runtime Benchmarks + uses: actions/upload-artifact@v5 + if: always() + with: + name: benchmark-MatrixTests + path: | + docs/docs/benchmarks/MatrixTests.md + docs/static/benchmarks/MatrixTests.json + retention-days: 90 + + - name: Upload Individual Runtime Benchmarks + uses: actions/upload-artifact@v5 + if: always() + with: + name: benchmark-MassiveParallelTests + path: | + docs/docs/benchmarks/MassiveParallelTests.md + docs/static/benchmarks/MassiveParallelTests.json + retention-days: 90 + + - name: Upload Individual Runtime Benchmarks + uses: actions/upload-artifact@v5 + if: always() + with: + name: benchmark-SetupTeardownTests + path: | + docs/docs/benchmarks/SetupTeardownTests.md + docs/static/benchmarks/SetupTeardownTests.json + retention-days: 90 + + - name: Upload Build Benchmark + uses: actions/upload-artifact@v5 + if: always() + with: + name: benchmark-BuildTime + path: | + docs/docs/benchmarks/BuildTime.md + docs/static/benchmarks/BuildTime.json + retention-days: 90 + + - name: Upload Summary Files + uses: actions/upload-artifact@v5 + if: always() + with: + name: benchmark-summary + path: | + docs/docs/benchmarks/index.md + docs/static/benchmarks/latest.json + docs/static/benchmarks/summary.json + docs/static/benchmarks/historical.json + retention-days: 90 + - name: Generate Benchmark List id: benchmark_list run: | @@ -210,6 +292,16 @@ jobs: ### Benchmarks Produced + Individual benchmark artifacts are available for download: + - `benchmark-DataDrivenTests` + - `benchmark-AsyncTests` + - `benchmark-ScaleTests` + - `benchmark-MatrixTests` + - `benchmark-MassiveParallelTests` + - `benchmark-SetupTeardownTests` + - `benchmark-BuildTime` + - `benchmark-summary` (aggregated overview) + #### Runtime Benchmarks ${{ steps.benchmark_list.outputs.runtime_benchmarks }} From 6a996ab3e5e29b297497364d224a750bdc26694d Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 12 Nov 2025 20:56:08 +0000 Subject: [PATCH 56/62] chore: update benchmark results (#3811) --- docs/docs/benchmarks/BuildTime.md | 45 ++++++++++----- docs/docs/benchmarks/index.md | 4 +- docs/docs/benchmarks/results.md | 57 ++++++++++++------- docs/static/benchmarks/BuildTime.json | 36 ++++++------ docs/static/benchmarks/historical.json | 4 ++ docs/static/benchmarks/latest.json | 78 +++++++++++++------------- docs/static/benchmarks/results.json | 44 +++++++-------- 7 files changed, 151 insertions(+), 117 deletions(-) diff --git a/docs/docs/benchmarks/BuildTime.md b/docs/docs/benchmarks/BuildTime.md index ee37330fc4..e09a32c0ba 100644 --- a/docs/docs/benchmarks/BuildTime.md +++ b/docs/docs/benchmarks/BuildTime.md @@ -18,10 +18,10 @@ Compilation time comparison across frameworks: | Framework | Version | Mean | Median | StdDev | |-----------|---------|------|--------|--------| -| **TUnit** | 1.1.0 | 2.033 s | 2.032 s | 0.0274 s | -| Build_NUnit | 4.4.0 | 1.626 s | 1.628 s | 0.0146 s | -| Build_MSTest | 4.0.2 | 1.700 s | 1.694 s | 0.0153 s | -| Build_xUnit3 | 3.2.0 | 1.607 s | 1.602 s | 0.0149 s | +| **TUnit** | 1.1.0 | 2.009 s | 2.007 s | 0.0253 s | +| Build_NUnit | 4.4.0 | 1.606 s | 1.603 s | 0.0159 s | +| Build_MSTest | 4.0.2 | 1.683 s | 1.679 s | 0.0145 s | +| Build_xUnit3 | 3.2.0 | 1.589 s | 1.593 s | 0.0206 s | ## šŸ“ˆ Visual Comparison @@ -29,23 +29,38 @@ Compilation time comparison across frameworks: %%{init: { 'theme':'base', 'themeVariables': { - 'primaryColor': '#10b981', - 'primaryTextColor': '#fff', - 'primaryBorderColor': '#059669', - 'lineColor': '#d1d5db', - 'secondaryColor': '#3b82f6', - 'tertiaryColor': '#f59e0b', + 'primaryColor': '#2563eb', + 'primaryTextColor': '#ffffff', + 'primaryBorderColor': '#1e40af', + 'lineColor': '#6b7280', + 'secondaryColor': '#7c3aed', + 'tertiaryColor': '#dc2626', 'background': '#ffffff', - 'mainBkg': '#10b981', - 'secondBkg': '#ef4444', - 'tertiaryBkg': '#f59e0b' + 'mainBkg': '#2563eb', + 'secondBkg': '#7c3aed', + 'tertiaryBkg': '#dc2626', + 'clusterBkg': '#f3f4f6', + 'edgeLabelBackground': '#ffffff', + 'tertiaryTextColor': '#1f2937', + 'pie1': '#2563eb', + 'pie2': '#7c3aed', + 'pie3': '#dc2626', + 'pie4': '#f59e0b', + 'pie5': '#10b981', + 'pie6': '#06b6d4', + 'pie7': '#ec4899', + 'pie8': '#6366f1', + 'pie9': '#84cc16', + 'pie10': '#f97316', + 'pie11': '#14b8a6', + 'pie12': '#a855f7' } }}%% xychart-beta title "Build Time Comparison" x-axis ["Build_TUnit", "Build_NUnit", "Build_MSTest", "Build_xUnit3"] y-axis "Time (s)" 0 --> 3 - bar [2.033, 1.626, 1.7, 1.607] + bar [2.009, 1.606, 1.683, 1.589] ``` --- @@ -54,4 +69,4 @@ xychart-beta View the [benchmarks overview](/docs/benchmarks) for methodology details and environment information. ::: -*Last generated: 2025-11-12T20:28:34.075Z* +*Last generated: 2025-11-12T20:55:56.395Z* diff --git a/docs/docs/benchmarks/index.md b/docs/docs/benchmarks/index.md index 338ba92eec..7e6443468d 100644 --- a/docs/docs/benchmarks/index.md +++ b/docs/docs/benchmarks/index.md @@ -51,7 +51,7 @@ The benchmarks measure real-world testing patterns: ### Environment - **OS**: Ubuntu Latest (GitHub Actions) -- **Runtime**: .NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v4 +- **Runtime**: .NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3 - **SDK**: .NET SDK 10.0.100 - **Hardware**: GitHub Actions Standard Runner (Ubuntu) - **Tool**: BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat) @@ -75,4 +75,4 @@ These benchmarks run automatically daily via [GitHub Actions](https://github.com Each benchmark runs multiple iterations with statistical analysis to ensure accuracy. Results may vary based on hardware and test characteristics. ::: -*Last generated: 2025-11-12T20:28:34.076Z* +*Last generated: 2025-11-12T20:55:56.396Z* diff --git a/docs/docs/benchmarks/results.md b/docs/docs/benchmarks/results.md index e2bf8fe4ba..cee00e4417 100644 --- a/docs/docs/benchmarks/results.md +++ b/docs/docs/benchmarks/results.md @@ -16,11 +16,11 @@ This benchmark was automatically generated on **2025-11-12** from the latest CI | Framework | Version | Mean | Median | StdDev | |-----------|---------|------|--------|--------| -| **TUnit** | 1.1.0 | 536.11 ms | 532.73 ms | 6.770 ms | -| NUnit | 4.4.0 | 1,556.59 ms | 1,554.77 ms | 10.335 ms | -| MSTest | 4.0.2 | 1,526.36 ms | 1,525.39 ms | 3.795 ms | -| xUnit3 | 3.2.0 | 1,603.59 ms | 1,604.07 ms | 8.271 ms | -| **TUnit (AOT)** | 1.1.0 | 77.73 ms | 77.78 ms | 0.214 ms | +| **TUnit** | 1.1.0 | 564.8 ms | 564.8 ms | 5.34 ms | +| NUnit | 4.4.0 | 1,156.4 ms | 1,156.4 ms | 5.00 ms | +| MSTest | 4.0.2 | 1,132.6 ms | 1,130.6 ms | 9.92 ms | +| xUnit3 | 3.2.0 | 1,200.7 ms | 1,200.4 ms | 5.74 ms | +| **TUnit (AOT)** | 1.1.0 | NA | NA | NA | ## šŸ“ˆ Visual Comparison @@ -28,31 +28,46 @@ This benchmark was automatically generated on **2025-11-12** from the latest CI %%{init: { 'theme':'base', 'themeVariables': { - 'primaryColor': '#10b981', - 'primaryTextColor': '#fff', - 'primaryBorderColor': '#059669', - 'lineColor': '#d1d5db', - 'secondaryColor': '#3b82f6', - 'tertiaryColor': '#f59e0b', + 'primaryColor': '#2563eb', + 'primaryTextColor': '#ffffff', + 'primaryBorderColor': '#1e40af', + 'lineColor': '#6b7280', + 'secondaryColor': '#7c3aed', + 'tertiaryColor': '#dc2626', 'background': '#ffffff', - 'mainBkg': '#10b981', - 'secondBkg': '#ef4444', - 'tertiaryBkg': '#f59e0b' + 'mainBkg': '#2563eb', + 'secondBkg': '#7c3aed', + 'tertiaryBkg': '#dc2626', + 'clusterBkg': '#f3f4f6', + 'edgeLabelBackground': '#ffffff', + 'tertiaryTextColor': '#1f2937', + 'pie1': '#2563eb', + 'pie2': '#7c3aed', + 'pie3': '#dc2626', + 'pie4': '#f59e0b', + 'pie5': '#10b981', + 'pie6': '#06b6d4', + 'pie7': '#ec4899', + 'pie8': '#6366f1', + 'pie9': '#84cc16', + 'pie10': '#f97316', + 'pie11': '#14b8a6', + 'pie12': '#a855f7' } }}%% xychart-beta title "results Performance Comparison" x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"] - y-axis "Time (ms)" 0 --> 1925 - bar [536.11, 1556.59, 1526.36, 1603.59, 77.73] + y-axis "Time (ms)" 0 --> 1441 + bar [564.8, 1156.4, 1132.6, 1200.7, 0] ``` ## šŸŽÆ Key Insights -- **2.90x faster** than NUnit (4.4.0) -- **2.85x faster** than MSTest (4.0.2) -- **2.99x faster** than xUnit3 (3.2.0) -- **6.90x faster** with Native AOT compilation +- **2.05x faster** than NUnit (4.4.0) +- **2.01x faster** than MSTest (4.0.2) +- **2.13x faster** than xUnit3 (3.2.0) +- **Infinityx faster** with Native AOT compilation --- @@ -60,4 +75,4 @@ xychart-beta View the [benchmarks overview](/docs/benchmarks) for methodology details and environment information. ::: -*Last generated: 2025-11-12T20:28:34.075Z* +*Last generated: 2025-11-12T20:55:56.395Z* diff --git a/docs/static/benchmarks/BuildTime.json b/docs/static/benchmarks/BuildTime.json index edfd5b22ed..71633e333c 100644 --- a/docs/static/benchmarks/BuildTime.json +++ b/docs/static/benchmarks/BuildTime.json @@ -1,43 +1,43 @@ { - "timestamp": "2025-11-12T20:28:34.075Z", + "timestamp": "2025-11-12T20:55:56.396Z", "category": "BuildTime", "environment": { "benchmarkDotNetVersion": "BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat)", "sdk": ".NET SDK 10.0.100", - "host": ".NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v4" + "host": ".NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3" }, "results": [ { "Method": "Build_TUnit", "Version": "1.1.0", - "Mean": "2.033 s", - "Error": "0.0293 s", - "StdDev": "0.0274 s", - "Median": "2.032 s" + "Mean": "2.009 s", + "Error": "0.0285 s", + "StdDev": "0.0253 s", + "Median": "2.007 s" }, { "Method": "Build_NUnit", "Version": "4.4.0", - "Mean": "1.626 s", - "Error": "0.0156 s", - "StdDev": "0.0146 s", - "Median": "1.628 s" + "Mean": "1.606 s", + "Error": "0.0170 s", + "StdDev": "0.0159 s", + "Median": "1.603 s" }, { "Method": "Build_MSTest", "Version": "4.0.2", - "Mean": "1.700 s", - "Error": "0.0183 s", - "StdDev": "0.0153 s", - "Median": "1.694 s" + "Mean": "1.683 s", + "Error": "0.0155 s", + "StdDev": "0.0145 s", + "Median": "1.679 s" }, { "Method": "Build_xUnit3", "Version": "3.2.0", - "Mean": "1.607 s", - "Error": "0.0159 s", - "StdDev": "0.0149 s", - "Median": "1.602 s" + "Mean": "1.589 s", + "Error": "0.0220 s", + "StdDev": "0.0206 s", + "Median": "1.593 s" } ] } \ No newline at end of file diff --git a/docs/static/benchmarks/historical.json b/docs/static/benchmarks/historical.json index 487d1aabe8..0f9034eea2 100644 --- a/docs/static/benchmarks/historical.json +++ b/docs/static/benchmarks/historical.json @@ -19,6 +19,10 @@ "date": "2025-11-12", "environment": "Ubuntu" }, + { + "date": "2025-11-12", + "environment": "Ubuntu" + }, { "date": "2025-11-12", "environment": "Ubuntu" diff --git a/docs/static/benchmarks/latest.json b/docs/static/benchmarks/latest.json index aa530b7849..bf1bb9c64a 100644 --- a/docs/static/benchmarks/latest.json +++ b/docs/static/benchmarks/latest.json @@ -1,51 +1,51 @@ { - "timestamp": "2025-11-12T20:28:34.076Z", + "timestamp": "2025-11-12T20:55:56.396Z", "environment": { "benchmarkDotNetVersion": "BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat)", "sdk": ".NET SDK 10.0.100", - "host": ".NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v4" + "host": ".NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3" }, "categories": { "results": [ { "Method": "TUnit", "Version": "1.1.0", - "Mean": "536.11 ms", - "Error": "7.237 ms", - "StdDev": "6.770 ms", - "Median": "532.73 ms" + "Mean": "564.8 ms", + "Error": "5.71 ms", + "StdDev": "5.34 ms", + "Median": "564.8 ms" }, { "Method": "NUnit", "Version": "4.4.0", - "Mean": "1,556.59 ms", - "Error": "11.049 ms", - "StdDev": "10.335 ms", - "Median": "1,554.77 ms" + "Mean": "1,156.4 ms", + "Error": "5.99 ms", + "StdDev": "5.00 ms", + "Median": "1,156.4 ms" }, { "Method": "MSTest", "Version": "4.0.2", - "Mean": "1,526.36 ms", - "Error": "4.282 ms", - "StdDev": "3.795 ms", - "Median": "1,525.39 ms" + "Mean": "1,132.6 ms", + "Error": "10.60 ms", + "StdDev": "9.92 ms", + "Median": "1,130.6 ms" }, { "Method": "xUnit3", "Version": "3.2.0", - "Mean": "1,603.59 ms", - "Error": "9.330 ms", - "StdDev": "8.271 ms", - "Median": "1,604.07 ms" + "Mean": "1,200.7 ms", + "Error": "6.87 ms", + "StdDev": "5.74 ms", + "Median": "1,200.4 ms" }, { "Method": "TUnit_AOT", "Version": "1.1.0", - "Mean": "77.73 ms", - "Error": "0.242 ms", - "StdDev": "0.214 ms", - "Median": "77.78 ms" + "Mean": "NA", + "Error": "NA", + "StdDev": "NA", + "Median": "NA" } ] }, @@ -54,34 +54,34 @@ { "Method": "Build_TUnit", "Version": "1.1.0", - "Mean": "2.033 s", - "Error": "0.0293 s", - "StdDev": "0.0274 s", - "Median": "2.032 s" + "Mean": "2.009 s", + "Error": "0.0285 s", + "StdDev": "0.0253 s", + "Median": "2.007 s" }, { "Method": "Build_NUnit", "Version": "4.4.0", - "Mean": "1.626 s", - "Error": "0.0156 s", - "StdDev": "0.0146 s", - "Median": "1.628 s" + "Mean": "1.606 s", + "Error": "0.0170 s", + "StdDev": "0.0159 s", + "Median": "1.603 s" }, { "Method": "Build_MSTest", "Version": "4.0.2", - "Mean": "1.700 s", - "Error": "0.0183 s", - "StdDev": "0.0153 s", - "Median": "1.694 s" + "Mean": "1.683 s", + "Error": "0.0155 s", + "StdDev": "0.0145 s", + "Median": "1.679 s" }, { "Method": "Build_xUnit3", "Version": "3.2.0", - "Mean": "1.607 s", - "Error": "0.0159 s", - "StdDev": "0.0149 s", - "Median": "1.602 s" + "Mean": "1.589 s", + "Error": "0.0220 s", + "StdDev": "0.0206 s", + "Median": "1.593 s" } ] }, @@ -89,6 +89,6 @@ "runtimeCategories": 1, "buildCategories": 1, "totalBenchmarks": 2, - "lastUpdated": "2025-11-12T20:28:34.074Z" + "lastUpdated": "2025-11-12T20:55:56.394Z" } } \ No newline at end of file diff --git a/docs/static/benchmarks/results.json b/docs/static/benchmarks/results.json index 2d973e76cc..f371056295 100644 --- a/docs/static/benchmarks/results.json +++ b/docs/static/benchmarks/results.json @@ -1,51 +1,51 @@ { - "timestamp": "2025-11-12T20:28:34.075Z", + "timestamp": "2025-11-12T20:55:56.395Z", "category": "results", "environment": { "benchmarkDotNetVersion": "BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat)", "sdk": ".NET SDK 10.0.100", - "host": ".NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v4" + "host": ".NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3" }, "results": [ { "Method": "TUnit", "Version": "1.1.0", - "Mean": "536.11 ms", - "Error": "7.237 ms", - "StdDev": "6.770 ms", - "Median": "532.73 ms" + "Mean": "564.8 ms", + "Error": "5.71 ms", + "StdDev": "5.34 ms", + "Median": "564.8 ms" }, { "Method": "NUnit", "Version": "4.4.0", - "Mean": "1,556.59 ms", - "Error": "11.049 ms", - "StdDev": "10.335 ms", - "Median": "1,554.77 ms" + "Mean": "1,156.4 ms", + "Error": "5.99 ms", + "StdDev": "5.00 ms", + "Median": "1,156.4 ms" }, { "Method": "MSTest", "Version": "4.0.2", - "Mean": "1,526.36 ms", - "Error": "4.282 ms", - "StdDev": "3.795 ms", - "Median": "1,525.39 ms" + "Mean": "1,132.6 ms", + "Error": "10.60 ms", + "StdDev": "9.92 ms", + "Median": "1,130.6 ms" }, { "Method": "xUnit3", "Version": "3.2.0", - "Mean": "1,603.59 ms", - "Error": "9.330 ms", - "StdDev": "8.271 ms", - "Median": "1,604.07 ms" + "Mean": "1,200.7 ms", + "Error": "6.87 ms", + "StdDev": "5.74 ms", + "Median": "1,200.4 ms" }, { "Method": "TUnit_AOT", "Version": "1.1.0", - "Mean": "77.73 ms", - "Error": "0.242 ms", - "StdDev": "0.214 ms", - "Median": "77.78 ms" + "Mean": "NA", + "Error": "NA", + "StdDev": "NA", + "Median": "NA" } ] } \ No newline at end of file From ad89be1dee61d8927764aeea816801f19a39ff0d Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 12 Nov 2025 21:06:39 +0000 Subject: [PATCH 57/62] chore: enhance benchmark processing with improved category extraction and validation --- .github/scripts/process-benchmarks.js | 62 +++++++++++--------------- .github/workflows/speed-comparison.yml | 4 +- 2 files changed, 29 insertions(+), 37 deletions(-) diff --git a/.github/scripts/process-benchmarks.js b/.github/scripts/process-benchmarks.js index 94d908de42..44ebc84537 100644 --- a/.github/scripts/process-benchmarks.js +++ b/.github/scripts/process-benchmarks.js @@ -97,6 +97,10 @@ let environmentInfo = {}; console.log('šŸ“Š Processing runtime benchmarks...'); const runtimeFiles = findMarkdownFiles(RUNTIME_DIR); console.log(` Found ${runtimeFiles.length} runtime benchmark files`); +if (runtimeFiles.length > 0) { + console.log(' Sample paths:'); + runtimeFiles.slice(0, 2).forEach(f => console.log(` ${f}`)); +} runtimeFiles.forEach(file => { const content = fs.readFileSync(file, 'utf8'); @@ -107,12 +111,17 @@ runtimeFiles.forEach(file => { } if (data) { - // Extract test category from path - const match = file.match(/run_time_([A-Za-z]+Tests)/); - const category = match ? match[1] : path.basename(path.dirname(file)); - - categories.runtime[category] = data; - console.log(` āœ“ Processed ${category}: ${data.length} frameworks`); + // Extract test category from artifact directory path + // Path structure: benchmark-results/runtime/ubuntu_markdown_run_time_/.../*.md + const match = file.match(/ubuntu_markdown_run_time_([A-Za-z]+Tests)/); + const category = match ? match[1] : null; + + if (category) { + categories.runtime[category] = data; + console.log(` āœ“ Processed ${category}: ${data.length} frameworks`); + } else { + console.warn(` āš ļø Could not extract category from file path: ${file}`); + } } }); @@ -217,32 +226,7 @@ xychart-beta ## šŸŽÆ Key Insights -${(() => { - const tunitResult = data.find(d => d.Method === 'TUnit'); - const tunitAotResult = data.find(d => d.Method === 'TUnit_AOT'); - const otherResults = data.filter(d => !d.Method.includes('TUnit')); - - if (!tunitResult) return '- TUnit data not available'; - - const tunitMean = parseMeanValue(tunitResult.Mean); - const insights = []; - - otherResults.forEach(other => { - const otherMean = parseMeanValue(other.Mean); - const speedup = (otherMean / tunitMean).toFixed(2); - if (speedup > 1) { - insights.push(`- **${speedup}x faster** than ${other.Method} (${other.Version})`); - } - }); - - if (tunitAotResult) { - const aotMean = parseMeanValue(tunitAotResult.Mean); - const aotSpeedup = (tunitMean / aotMean).toFixed(2); - insights.push(`- **${aotSpeedup}x faster** with Native AOT compilation`); - } - - return insights.join('\n'); -})()} +This benchmark compares TUnit's performance against ${data.filter(d => !d.Method.includes('TUnit')).map(d => d.Method).join(', ')} using identical test scenarios. --- @@ -515,11 +499,19 @@ console.log(`Summary:`); console.log(` - Runtime categories: ${stats.runtimeCategories}`); console.log(` - Build categories: ${stats.buildCategories}`); console.log(` - Total benchmarks: ${stats.totalBenchmarks}`); -console.log(` - Output files: 4 (markdown + 3 JSON files)`); +console.log(` - Markdown pages generated: ${stats.runtimeCategories + stats.buildCategories + 1}`); +console.log(` - JSON files generated: ${stats.runtimeCategories + stats.buildCategories + 3}`); console.log(`\nšŸ“Š Benchmarks produced:`); -console.log(`\nRuntime Benchmarks:`); +console.log(`\nRuntime Benchmarks (${Object.keys(categories.runtime).length}):`); Object.keys(categories.runtime).forEach(cat => console.log(` - ${cat}`)); if (Object.keys(categories.build).length > 0) { - console.log(`\nBuild Benchmarks:`); + console.log(`\nBuild Benchmarks (${Object.keys(categories.build).length}):`); Object.keys(categories.build).forEach(cat => console.log(` - ${cat}`)); } + +// Validation warning +if (Object.keys(categories.runtime).length === 0) { + console.warn('\nāš ļø WARNING: No runtime benchmark categories were found!'); + console.warn('This likely means the artifact directory structure is not as expected.'); + console.warn('Expected structure: benchmark-results/runtime/ubuntu_markdown_run_time_/'); +} diff --git a/.github/workflows/speed-comparison.yml b/.github/workflows/speed-comparison.yml index 0573d3292e..7a551189bb 100644 --- a/.github/workflows/speed-comparison.yml +++ b/.github/workflows/speed-comparison.yml @@ -142,14 +142,14 @@ jobs: with: path: benchmark-results/runtime/ pattern: ubuntu_markdown_run_time_* - merge-multiple: true + merge-multiple: false - name: Download Build Time Benchmark Artifacts uses: actions/download-artifact@v6 with: path: benchmark-results/build/ pattern: ubuntu_markdown_build_time - merge-multiple: true + merge-multiple: false - name: Setup Node.js uses: actions/setup-node@v6 From cf054f0ff2850ed5c9968a57f35f72cf0db0177d Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 12 Nov 2025 21:13:34 +0000 Subject: [PATCH 58/62] chore: update benchmark results (#3812) --- docs/docs/benchmarks/AsyncTests.md | 75 +++++ docs/docs/benchmarks/BuildTime.md | 14 +- docs/docs/benchmarks/DataDrivenTests.md | 75 +++++ docs/docs/benchmarks/MassiveParallelTests.md | 75 +++++ docs/docs/benchmarks/MatrixTests.md | 75 +++++ docs/docs/benchmarks/ScaleTests.md | 75 +++++ docs/docs/benchmarks/SetupTeardownTests.md | 75 +++++ docs/docs/benchmarks/index.md | 9 +- docs/static/benchmarks/AsyncTests.json | 51 ++++ docs/static/benchmarks/BuildTime.json | 34 +-- docs/static/benchmarks/DataDrivenTests.json | 51 ++++ .../benchmarks/MassiveParallelTests.json | 51 ++++ docs/static/benchmarks/MatrixTests.json | 51 ++++ docs/static/benchmarks/ScaleTests.json | 51 ++++ .../static/benchmarks/SetupTeardownTests.json | 51 ++++ docs/static/benchmarks/historical.json | 4 + docs/static/benchmarks/latest.json | 284 +++++++++++++++--- docs/static/benchmarks/summary.json | 7 +- 18 files changed, 1044 insertions(+), 64 deletions(-) create mode 100644 docs/docs/benchmarks/AsyncTests.md create mode 100644 docs/docs/benchmarks/DataDrivenTests.md create mode 100644 docs/docs/benchmarks/MassiveParallelTests.md create mode 100644 docs/docs/benchmarks/MatrixTests.md create mode 100644 docs/docs/benchmarks/ScaleTests.md create mode 100644 docs/docs/benchmarks/SetupTeardownTests.md create mode 100644 docs/static/benchmarks/AsyncTests.json create mode 100644 docs/static/benchmarks/DataDrivenTests.json create mode 100644 docs/static/benchmarks/MassiveParallelTests.json create mode 100644 docs/static/benchmarks/MatrixTests.json create mode 100644 docs/static/benchmarks/ScaleTests.json create mode 100644 docs/static/benchmarks/SetupTeardownTests.json diff --git a/docs/docs/benchmarks/AsyncTests.md b/docs/docs/benchmarks/AsyncTests.md new file mode 100644 index 0000000000..311679bb5a --- /dev/null +++ b/docs/docs/benchmarks/AsyncTests.md @@ -0,0 +1,75 @@ +--- +title: AsyncTests +description: Performance benchmark results for AsyncTests +sidebar_position: 2 +--- + +# AsyncTests Benchmark + +:::info Last Updated +This benchmark was automatically generated on **2025-11-12** from the latest CI run. + +**Environment:** Ubuntu Latest • .NET SDK 10.0.100 +::: + +## šŸ“Š Results + +| Framework | Version | Mean | Median | StdDev | +|-----------|---------|------|--------|--------| +| **TUnit** | 1.1.0 | 558.9 ms | 557.2 ms | 4.11 ms | +| NUnit | 4.4.0 | 650.2 ms | 649.0 ms | 9.00 ms | +| MSTest | 4.0.2 | 615.0 ms | 614.8 ms | 5.89 ms | +| xUnit3 | 3.2.0 | 702.4 ms | 702.4 ms | 7.09 ms | +| **TUnit (AOT)** | 1.1.0 | 123.8 ms | 123.9 ms | 0.46 ms | + +## šŸ“ˆ Visual Comparison + +```mermaid +%%{init: { + 'theme':'base', + 'themeVariables': { + 'primaryColor': '#2563eb', + 'primaryTextColor': '#ffffff', + 'primaryBorderColor': '#1e40af', + 'lineColor': '#6b7280', + 'secondaryColor': '#7c3aed', + 'tertiaryColor': '#dc2626', + 'background': '#ffffff', + 'mainBkg': '#2563eb', + 'secondBkg': '#7c3aed', + 'tertiaryBkg': '#dc2626', + 'clusterBkg': '#f3f4f6', + 'edgeLabelBackground': '#ffffff', + 'tertiaryTextColor': '#1f2937', + 'pie1': '#2563eb', + 'pie2': '#7c3aed', + 'pie3': '#dc2626', + 'pie4': '#f59e0b', + 'pie5': '#10b981', + 'pie6': '#06b6d4', + 'pie7': '#ec4899', + 'pie8': '#6366f1', + 'pie9': '#84cc16', + 'pie10': '#f97316', + 'pie11': '#14b8a6', + 'pie12': '#a855f7' + } +}}%% +xychart-beta + title "AsyncTests Performance Comparison" + x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"] + y-axis "Time (ms)" 0 --> 843 + bar [558.9, 650.2, 615, 702.4, 123.8] +``` + +## šŸŽÆ Key Insights + +This benchmark compares TUnit's performance against NUnit, MSTest, xUnit3 using identical test scenarios. + +--- + +:::note Methodology +View the [benchmarks overview](/docs/benchmarks) for methodology details and environment information. +::: + +*Last generated: 2025-11-12T21:13:20.516Z* diff --git a/docs/docs/benchmarks/BuildTime.md b/docs/docs/benchmarks/BuildTime.md index e09a32c0ba..bb58397fad 100644 --- a/docs/docs/benchmarks/BuildTime.md +++ b/docs/docs/benchmarks/BuildTime.md @@ -1,7 +1,7 @@ --- title: Build Performance description: Compilation time benchmark results -sidebar_position: 3 +sidebar_position: 8 --- # Build Performance Benchmark @@ -18,10 +18,10 @@ Compilation time comparison across frameworks: | Framework | Version | Mean | Median | StdDev | |-----------|---------|------|--------|--------| -| **TUnit** | 1.1.0 | 2.009 s | 2.007 s | 0.0253 s | -| Build_NUnit | 4.4.0 | 1.606 s | 1.603 s | 0.0159 s | -| Build_MSTest | 4.0.2 | 1.683 s | 1.679 s | 0.0145 s | -| Build_xUnit3 | 3.2.0 | 1.589 s | 1.593 s | 0.0206 s | +| **TUnit** | 1.1.0 | 2.033 s | 2.032 s | 0.0296 s | +| Build_NUnit | 4.4.0 | 1.622 s | 1.615 s | 0.0216 s | +| Build_MSTest | 4.0.2 | 1.705 s | 1.701 s | 0.0231 s | +| Build_xUnit3 | 3.2.0 | 1.611 s | 1.613 s | 0.0248 s | ## šŸ“ˆ Visual Comparison @@ -60,7 +60,7 @@ xychart-beta title "Build Time Comparison" x-axis ["Build_TUnit", "Build_NUnit", "Build_MSTest", "Build_xUnit3"] y-axis "Time (s)" 0 --> 3 - bar [2.009, 1.606, 1.683, 1.589] + bar [2.033, 1.622, 1.705, 1.611] ``` --- @@ -69,4 +69,4 @@ xychart-beta View the [benchmarks overview](/docs/benchmarks) for methodology details and environment information. ::: -*Last generated: 2025-11-12T20:55:56.395Z* +*Last generated: 2025-11-12T21:13:20.518Z* diff --git a/docs/docs/benchmarks/DataDrivenTests.md b/docs/docs/benchmarks/DataDrivenTests.md new file mode 100644 index 0000000000..539d7eaa2f --- /dev/null +++ b/docs/docs/benchmarks/DataDrivenTests.md @@ -0,0 +1,75 @@ +--- +title: DataDrivenTests +description: Performance benchmark results for DataDrivenTests +sidebar_position: 3 +--- + +# DataDrivenTests Benchmark + +:::info Last Updated +This benchmark was automatically generated on **2025-11-12** from the latest CI run. + +**Environment:** Ubuntu Latest • .NET SDK 10.0.100 +::: + +## šŸ“Š Results + +| Framework | Version | Mean | Median | StdDev | +|-----------|---------|------|--------|--------| +| **TUnit** | 1.1.0 | 510.37 ms | 511.05 ms | 7.125 ms | +| NUnit | 4.4.0 | 583.06 ms | 583.47 ms | 17.581 ms | +| MSTest | 4.0.2 | 594.23 ms | 591.72 ms | 18.226 ms | +| xUnit3 | 3.2.0 | 604.34 ms | 605.85 ms | 7.447 ms | +| **TUnit (AOT)** | 1.1.0 | 25.55 ms | 25.62 ms | 0.241 ms | + +## šŸ“ˆ Visual Comparison + +```mermaid +%%{init: { + 'theme':'base', + 'themeVariables': { + 'primaryColor': '#2563eb', + 'primaryTextColor': '#ffffff', + 'primaryBorderColor': '#1e40af', + 'lineColor': '#6b7280', + 'secondaryColor': '#7c3aed', + 'tertiaryColor': '#dc2626', + 'background': '#ffffff', + 'mainBkg': '#2563eb', + 'secondBkg': '#7c3aed', + 'tertiaryBkg': '#dc2626', + 'clusterBkg': '#f3f4f6', + 'edgeLabelBackground': '#ffffff', + 'tertiaryTextColor': '#1f2937', + 'pie1': '#2563eb', + 'pie2': '#7c3aed', + 'pie3': '#dc2626', + 'pie4': '#f59e0b', + 'pie5': '#10b981', + 'pie6': '#06b6d4', + 'pie7': '#ec4899', + 'pie8': '#6366f1', + 'pie9': '#84cc16', + 'pie10': '#f97316', + 'pie11': '#14b8a6', + 'pie12': '#a855f7' + } +}}%% +xychart-beta + title "DataDrivenTests Performance Comparison" + x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"] + y-axis "Time (ms)" 0 --> 726 + bar [510.37, 583.06, 594.23, 604.34, 25.55] +``` + +## šŸŽÆ Key Insights + +This benchmark compares TUnit's performance against NUnit, MSTest, xUnit3 using identical test scenarios. + +--- + +:::note Methodology +View the [benchmarks overview](/docs/benchmarks) for methodology details and environment information. +::: + +*Last generated: 2025-11-12T21:13:20.516Z* diff --git a/docs/docs/benchmarks/MassiveParallelTests.md b/docs/docs/benchmarks/MassiveParallelTests.md new file mode 100644 index 0000000000..6f2c8a816f --- /dev/null +++ b/docs/docs/benchmarks/MassiveParallelTests.md @@ -0,0 +1,75 @@ +--- +title: MassiveParallelTests +description: Performance benchmark results for MassiveParallelTests +sidebar_position: 4 +--- + +# MassiveParallelTests Benchmark + +:::info Last Updated +This benchmark was automatically generated on **2025-11-12** from the latest CI run. + +**Environment:** Ubuntu Latest • .NET SDK 10.0.100 +::: + +## šŸ“Š Results + +| Framework | Version | Mean | Median | StdDev | +|-----------|---------|------|--------|--------| +| **TUnit** | 1.1.0 | 625.5 ms | 626.8 ms | 7.61 ms | +| NUnit | 4.4.0 | 1,213.0 ms | 1,211.9 ms | 12.39 ms | +| MSTest | 4.0.2 | 3,010.8 ms | 3,010.4 ms | 12.63 ms | +| xUnit3 | 3.2.0 | 3,099.6 ms | 3,101.6 ms | 15.59 ms | +| **TUnit (AOT)** | 1.1.0 | 132.3 ms | 132.3 ms | 0.36 ms | + +## šŸ“ˆ Visual Comparison + +```mermaid +%%{init: { + 'theme':'base', + 'themeVariables': { + 'primaryColor': '#2563eb', + 'primaryTextColor': '#ffffff', + 'primaryBorderColor': '#1e40af', + 'lineColor': '#6b7280', + 'secondaryColor': '#7c3aed', + 'tertiaryColor': '#dc2626', + 'background': '#ffffff', + 'mainBkg': '#2563eb', + 'secondBkg': '#7c3aed', + 'tertiaryBkg': '#dc2626', + 'clusterBkg': '#f3f4f6', + 'edgeLabelBackground': '#ffffff', + 'tertiaryTextColor': '#1f2937', + 'pie1': '#2563eb', + 'pie2': '#7c3aed', + 'pie3': '#dc2626', + 'pie4': '#f59e0b', + 'pie5': '#10b981', + 'pie6': '#06b6d4', + 'pie7': '#ec4899', + 'pie8': '#6366f1', + 'pie9': '#84cc16', + 'pie10': '#f97316', + 'pie11': '#14b8a6', + 'pie12': '#a855f7' + } +}}%% +xychart-beta + title "MassiveParallelTests Performance Comparison" + x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"] + y-axis "Time (ms)" 0 --> 3720 + bar [625.5, 1213, 3010.8, 3099.6, 132.3] +``` + +## šŸŽÆ Key Insights + +This benchmark compares TUnit's performance against NUnit, MSTest, xUnit3 using identical test scenarios. + +--- + +:::note Methodology +View the [benchmarks overview](/docs/benchmarks) for methodology details and environment information. +::: + +*Last generated: 2025-11-12T21:13:20.517Z* diff --git a/docs/docs/benchmarks/MatrixTests.md b/docs/docs/benchmarks/MatrixTests.md new file mode 100644 index 0000000000..d683a99d99 --- /dev/null +++ b/docs/docs/benchmarks/MatrixTests.md @@ -0,0 +1,75 @@ +--- +title: MatrixTests +description: Performance benchmark results for MatrixTests +sidebar_position: 5 +--- + +# MatrixTests Benchmark + +:::info Last Updated +This benchmark was automatically generated on **2025-11-12** from the latest CI run. + +**Environment:** Ubuntu Latest • .NET SDK 10.0.100 +::: + +## šŸ“Š Results + +| Framework | Version | Mean | Median | StdDev | +|-----------|---------|------|--------|--------| +| **TUnit** | 1.1.0 | 557.69 ms | 556.60 ms | 4.221 ms | +| NUnit | 4.4.0 | 1,539.35 ms | 1,541.87 ms | 11.514 ms | +| MSTest | 4.0.2 | 1,496.25 ms | 1,496.55 ms | 5.576 ms | +| xUnit3 | 3.2.0 | 1,586.79 ms | 1,586.67 ms | 7.339 ms | +| **TUnit (AOT)** | 1.1.0 | 79.15 ms | 79.11 ms | 0.381 ms | + +## šŸ“ˆ Visual Comparison + +```mermaid +%%{init: { + 'theme':'base', + 'themeVariables': { + 'primaryColor': '#2563eb', + 'primaryTextColor': '#ffffff', + 'primaryBorderColor': '#1e40af', + 'lineColor': '#6b7280', + 'secondaryColor': '#7c3aed', + 'tertiaryColor': '#dc2626', + 'background': '#ffffff', + 'mainBkg': '#2563eb', + 'secondBkg': '#7c3aed', + 'tertiaryBkg': '#dc2626', + 'clusterBkg': '#f3f4f6', + 'edgeLabelBackground': '#ffffff', + 'tertiaryTextColor': '#1f2937', + 'pie1': '#2563eb', + 'pie2': '#7c3aed', + 'pie3': '#dc2626', + 'pie4': '#f59e0b', + 'pie5': '#10b981', + 'pie6': '#06b6d4', + 'pie7': '#ec4899', + 'pie8': '#6366f1', + 'pie9': '#84cc16', + 'pie10': '#f97316', + 'pie11': '#14b8a6', + 'pie12': '#a855f7' + } +}}%% +xychart-beta + title "MatrixTests Performance Comparison" + x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"] + y-axis "Time (ms)" 0 --> 1905 + bar [557.69, 1539.35, 1496.25, 1586.79, 79.15] +``` + +## šŸŽÆ Key Insights + +This benchmark compares TUnit's performance against NUnit, MSTest, xUnit3 using identical test scenarios. + +--- + +:::note Methodology +View the [benchmarks overview](/docs/benchmarks) for methodology details and environment information. +::: + +*Last generated: 2025-11-12T21:13:20.517Z* diff --git a/docs/docs/benchmarks/ScaleTests.md b/docs/docs/benchmarks/ScaleTests.md new file mode 100644 index 0000000000..3de683a15b --- /dev/null +++ b/docs/docs/benchmarks/ScaleTests.md @@ -0,0 +1,75 @@ +--- +title: ScaleTests +description: Performance benchmark results for ScaleTests +sidebar_position: 6 +--- + +# ScaleTests Benchmark + +:::info Last Updated +This benchmark was automatically generated on **2025-11-12** from the latest CI run. + +**Environment:** Ubuntu Latest • .NET SDK 10.0.100 +::: + +## šŸ“Š Results + +| Framework | Version | Mean | Median | StdDev | +|-----------|---------|------|--------|--------| +| **TUnit** | 1.1.0 | 514.19 ms | 512.52 ms | 6.486 ms | +| NUnit | 4.4.0 | 552.95 ms | 553.62 ms | 7.860 ms | +| MSTest | 4.0.2 | 474.67 ms | 475.13 ms | 8.594 ms | +| xUnit3 | 3.2.0 | 562.71 ms | 561.53 ms | 6.028 ms | +| **TUnit (AOT)** | 1.1.0 | 44.36 ms | 45.13 ms | 3.857 ms | + +## šŸ“ˆ Visual Comparison + +```mermaid +%%{init: { + 'theme':'base', + 'themeVariables': { + 'primaryColor': '#2563eb', + 'primaryTextColor': '#ffffff', + 'primaryBorderColor': '#1e40af', + 'lineColor': '#6b7280', + 'secondaryColor': '#7c3aed', + 'tertiaryColor': '#dc2626', + 'background': '#ffffff', + 'mainBkg': '#2563eb', + 'secondBkg': '#7c3aed', + 'tertiaryBkg': '#dc2626', + 'clusterBkg': '#f3f4f6', + 'edgeLabelBackground': '#ffffff', + 'tertiaryTextColor': '#1f2937', + 'pie1': '#2563eb', + 'pie2': '#7c3aed', + 'pie3': '#dc2626', + 'pie4': '#f59e0b', + 'pie5': '#10b981', + 'pie6': '#06b6d4', + 'pie7': '#ec4899', + 'pie8': '#6366f1', + 'pie9': '#84cc16', + 'pie10': '#f97316', + 'pie11': '#14b8a6', + 'pie12': '#a855f7' + } +}}%% +xychart-beta + title "ScaleTests Performance Comparison" + x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"] + y-axis "Time (ms)" 0 --> 676 + bar [514.19, 552.95, 474.67, 562.71, 44.36] +``` + +## šŸŽÆ Key Insights + +This benchmark compares TUnit's performance against NUnit, MSTest, xUnit3 using identical test scenarios. + +--- + +:::note Methodology +View the [benchmarks overview](/docs/benchmarks) for methodology details and environment information. +::: + +*Last generated: 2025-11-12T21:13:20.517Z* diff --git a/docs/docs/benchmarks/SetupTeardownTests.md b/docs/docs/benchmarks/SetupTeardownTests.md new file mode 100644 index 0000000000..4452a73eb9 --- /dev/null +++ b/docs/docs/benchmarks/SetupTeardownTests.md @@ -0,0 +1,75 @@ +--- +title: SetupTeardownTests +description: Performance benchmark results for SetupTeardownTests +sidebar_position: 7 +--- + +# SetupTeardownTests Benchmark + +:::info Last Updated +This benchmark was automatically generated on **2025-11-12** from the latest CI run. + +**Environment:** Ubuntu Latest • .NET SDK 10.0.100 +::: + +## šŸ“Š Results + +| Framework | Version | Mean | Median | StdDev | +|-----------|---------|------|--------|--------| +| **TUnit** | 1.1.0 | 583.4 ms | 583.7 ms | 5.16 ms | +| NUnit | 4.4.0 | 1,198.4 ms | 1,195.0 ms | 15.35 ms | +| MSTest | 4.0.2 | 1,162.5 ms | 1,161.5 ms | 9.11 ms | +| xUnit3 | 3.2.0 | 1,243.3 ms | 1,241.9 ms | 9.80 ms | +| **TUnit (AOT)** | 1.1.0 | NA | NA | NA | + +## šŸ“ˆ Visual Comparison + +```mermaid +%%{init: { + 'theme':'base', + 'themeVariables': { + 'primaryColor': '#2563eb', + 'primaryTextColor': '#ffffff', + 'primaryBorderColor': '#1e40af', + 'lineColor': '#6b7280', + 'secondaryColor': '#7c3aed', + 'tertiaryColor': '#dc2626', + 'background': '#ffffff', + 'mainBkg': '#2563eb', + 'secondBkg': '#7c3aed', + 'tertiaryBkg': '#dc2626', + 'clusterBkg': '#f3f4f6', + 'edgeLabelBackground': '#ffffff', + 'tertiaryTextColor': '#1f2937', + 'pie1': '#2563eb', + 'pie2': '#7c3aed', + 'pie3': '#dc2626', + 'pie4': '#f59e0b', + 'pie5': '#10b981', + 'pie6': '#06b6d4', + 'pie7': '#ec4899', + 'pie8': '#6366f1', + 'pie9': '#84cc16', + 'pie10': '#f97316', + 'pie11': '#14b8a6', + 'pie12': '#a855f7' + } +}}%% +xychart-beta + title "SetupTeardownTests Performance Comparison" + x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"] + y-axis "Time (ms)" 0 --> 1492 + bar [583.4, 1198.4, 1162.5, 1243.3, 0] +``` + +## šŸŽÆ Key Insights + +This benchmark compares TUnit's performance against NUnit, MSTest, xUnit3 using identical test scenarios. + +--- + +:::note Methodology +View the [benchmarks overview](/docs/benchmarks) for methodology details and environment information. +::: + +*Last generated: 2025-11-12T21:13:20.517Z* diff --git a/docs/docs/benchmarks/index.md b/docs/docs/benchmarks/index.md index 7e6443468d..c320ea6144 100644 --- a/docs/docs/benchmarks/index.md +++ b/docs/docs/benchmarks/index.md @@ -16,7 +16,12 @@ These benchmarks were automatically generated on **2025-11-12** from the latest Click on any benchmark to view detailed results: -- [results](results) - Detailed performance analysis +- [AsyncTests](AsyncTests) - Detailed performance analysis +- [DataDrivenTests](DataDrivenTests) - Detailed performance analysis +- [MassiveParallelTests](MassiveParallelTests) - Detailed performance analysis +- [MatrixTests](MatrixTests) - Detailed performance analysis +- [ScaleTests](ScaleTests) - Detailed performance analysis +- [SetupTeardownTests](SetupTeardownTests) - Detailed performance analysis ## šŸ”Ø Build Benchmarks @@ -75,4 +80,4 @@ These benchmarks run automatically daily via [GitHub Actions](https://github.com Each benchmark runs multiple iterations with statistical analysis to ensure accuracy. Results may vary based on hardware and test characteristics. ::: -*Last generated: 2025-11-12T20:55:56.396Z* +*Last generated: 2025-11-12T21:13:20.518Z* diff --git a/docs/static/benchmarks/AsyncTests.json b/docs/static/benchmarks/AsyncTests.json new file mode 100644 index 0000000000..f0c2840116 --- /dev/null +++ b/docs/static/benchmarks/AsyncTests.json @@ -0,0 +1,51 @@ +{ + "timestamp": "2025-11-12T21:13:20.516Z", + "category": "AsyncTests", + "environment": { + "benchmarkDotNetVersion": "BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat)", + "sdk": ".NET SDK 10.0.100", + "host": ".NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3" + }, + "results": [ + { + "Method": "TUnit", + "Version": "1.1.0", + "Mean": "558.9 ms", + "Error": "4.39 ms", + "StdDev": "4.11 ms", + "Median": "557.2 ms" + }, + { + "Method": "NUnit", + "Version": "4.4.0", + "Mean": "650.2 ms", + "Error": "10.15 ms", + "StdDev": "9.00 ms", + "Median": "649.0 ms" + }, + { + "Method": "MSTest", + "Version": "4.0.2", + "Mean": "615.0 ms", + "Error": "6.64 ms", + "StdDev": "5.89 ms", + "Median": "614.8 ms" + }, + { + "Method": "xUnit3", + "Version": "3.2.0", + "Mean": "702.4 ms", + "Error": "8.00 ms", + "StdDev": "7.09 ms", + "Median": "702.4 ms" + }, + { + "Method": "TUnit_AOT", + "Version": "1.1.0", + "Mean": "123.8 ms", + "Error": "0.49 ms", + "StdDev": "0.46 ms", + "Median": "123.9 ms" + } + ] +} \ No newline at end of file diff --git a/docs/static/benchmarks/BuildTime.json b/docs/static/benchmarks/BuildTime.json index 71633e333c..f64bd7f717 100644 --- a/docs/static/benchmarks/BuildTime.json +++ b/docs/static/benchmarks/BuildTime.json @@ -1,5 +1,5 @@ { - "timestamp": "2025-11-12T20:55:56.396Z", + "timestamp": "2025-11-12T21:13:20.518Z", "category": "BuildTime", "environment": { "benchmarkDotNetVersion": "BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat)", @@ -10,34 +10,34 @@ { "Method": "Build_TUnit", "Version": "1.1.0", - "Mean": "2.009 s", - "Error": "0.0285 s", - "StdDev": "0.0253 s", - "Median": "2.007 s" + "Mean": "2.033 s", + "Error": "0.0333 s", + "StdDev": "0.0296 s", + "Median": "2.032 s" }, { "Method": "Build_NUnit", "Version": "4.4.0", - "Mean": "1.606 s", - "Error": "0.0170 s", - "StdDev": "0.0159 s", - "Median": "1.603 s" + "Mean": "1.622 s", + "Error": "0.0231 s", + "StdDev": "0.0216 s", + "Median": "1.615 s" }, { "Method": "Build_MSTest", "Version": "4.0.2", - "Mean": "1.683 s", - "Error": "0.0155 s", - "StdDev": "0.0145 s", - "Median": "1.679 s" + "Mean": "1.705 s", + "Error": "0.0247 s", + "StdDev": "0.0231 s", + "Median": "1.701 s" }, { "Method": "Build_xUnit3", "Version": "3.2.0", - "Mean": "1.589 s", - "Error": "0.0220 s", - "StdDev": "0.0206 s", - "Median": "1.593 s" + "Mean": "1.611 s", + "Error": "0.0265 s", + "StdDev": "0.0248 s", + "Median": "1.613 s" } ] } \ No newline at end of file diff --git a/docs/static/benchmarks/DataDrivenTests.json b/docs/static/benchmarks/DataDrivenTests.json new file mode 100644 index 0000000000..03fc62272a --- /dev/null +++ b/docs/static/benchmarks/DataDrivenTests.json @@ -0,0 +1,51 @@ +{ + "timestamp": "2025-11-12T21:13:20.517Z", + "category": "DataDrivenTests", + "environment": { + "benchmarkDotNetVersion": "BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat)", + "sdk": ".NET SDK 10.0.100", + "host": ".NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3" + }, + "results": [ + { + "Method": "TUnit", + "Version": "1.1.0", + "Mean": "510.37 ms", + "Error": "7.617 ms", + "StdDev": "7.125 ms", + "Median": "511.05 ms" + }, + { + "Method": "NUnit", + "Version": "4.4.0", + "Mean": "583.06 ms", + "Error": "11.292 ms", + "StdDev": "17.581 ms", + "Median": "583.47 ms" + }, + { + "Method": "MSTest", + "Version": "4.0.2", + "Mean": "594.23 ms", + "Error": "11.707 ms", + "StdDev": "18.226 ms", + "Median": "591.72 ms" + }, + { + "Method": "xUnit3", + "Version": "3.2.0", + "Mean": "604.34 ms", + "Error": "8.401 ms", + "StdDev": "7.447 ms", + "Median": "605.85 ms" + }, + { + "Method": "TUnit_AOT", + "Version": "1.1.0", + "Mean": "25.55 ms", + "Error": "0.258 ms", + "StdDev": "0.241 ms", + "Median": "25.62 ms" + } + ] +} \ No newline at end of file diff --git a/docs/static/benchmarks/MassiveParallelTests.json b/docs/static/benchmarks/MassiveParallelTests.json new file mode 100644 index 0000000000..c60cb4d2d9 --- /dev/null +++ b/docs/static/benchmarks/MassiveParallelTests.json @@ -0,0 +1,51 @@ +{ + "timestamp": "2025-11-12T21:13:20.517Z", + "category": "MassiveParallelTests", + "environment": { + "benchmarkDotNetVersion": "BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat)", + "sdk": ".NET SDK 10.0.100", + "host": ".NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3" + }, + "results": [ + { + "Method": "TUnit", + "Version": "1.1.0", + "Mean": "625.5 ms", + "Error": "8.58 ms", + "StdDev": "7.61 ms", + "Median": "626.8 ms" + }, + { + "Method": "NUnit", + "Version": "4.4.0", + "Mean": "1,213.0 ms", + "Error": "13.24 ms", + "StdDev": "12.39 ms", + "Median": "1,211.9 ms" + }, + { + "Method": "MSTest", + "Version": "4.0.2", + "Mean": "3,010.8 ms", + "Error": "13.51 ms", + "StdDev": "12.63 ms", + "Median": "3,010.4 ms" + }, + { + "Method": "xUnit3", + "Version": "3.2.0", + "Mean": "3,099.6 ms", + "Error": "17.58 ms", + "StdDev": "15.59 ms", + "Median": "3,101.6 ms" + }, + { + "Method": "TUnit_AOT", + "Version": "1.1.0", + "Mean": "132.3 ms", + "Error": "0.47 ms", + "StdDev": "0.36 ms", + "Median": "132.3 ms" + } + ] +} \ No newline at end of file diff --git a/docs/static/benchmarks/MatrixTests.json b/docs/static/benchmarks/MatrixTests.json new file mode 100644 index 0000000000..fd462c3e66 --- /dev/null +++ b/docs/static/benchmarks/MatrixTests.json @@ -0,0 +1,51 @@ +{ + "timestamp": "2025-11-12T21:13:20.517Z", + "category": "MatrixTests", + "environment": { + "benchmarkDotNetVersion": "BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat)", + "sdk": ".NET SDK 10.0.100", + "host": ".NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3" + }, + "results": [ + { + "Method": "TUnit", + "Version": "1.1.0", + "Mean": "557.69 ms", + "Error": "4.512 ms", + "StdDev": "4.221 ms", + "Median": "556.60 ms" + }, + { + "Method": "NUnit", + "Version": "4.4.0", + "Mean": "1,539.35 ms", + "Error": "12.309 ms", + "StdDev": "11.514 ms", + "Median": "1,541.87 ms" + }, + { + "Method": "MSTest", + "Version": "4.0.2", + "Mean": "1,496.25 ms", + "Error": "7.142 ms", + "StdDev": "5.576 ms", + "Median": "1,496.55 ms" + }, + { + "Method": "xUnit3", + "Version": "3.2.0", + "Mean": "1,586.79 ms", + "Error": "7.846 ms", + "StdDev": "7.339 ms", + "Median": "1,586.67 ms" + }, + { + "Method": "TUnit_AOT", + "Version": "1.1.0", + "Mean": "79.15 ms", + "Error": "0.408 ms", + "StdDev": "0.381 ms", + "Median": "79.11 ms" + } + ] +} \ No newline at end of file diff --git a/docs/static/benchmarks/ScaleTests.json b/docs/static/benchmarks/ScaleTests.json new file mode 100644 index 0000000000..47799a37bb --- /dev/null +++ b/docs/static/benchmarks/ScaleTests.json @@ -0,0 +1,51 @@ +{ + "timestamp": "2025-11-12T21:13:20.517Z", + "category": "ScaleTests", + "environment": { + "benchmarkDotNetVersion": "BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat)", + "sdk": ".NET SDK 10.0.100", + "host": ".NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3" + }, + "results": [ + { + "Method": "TUnit", + "Version": "1.1.0", + "Mean": "514.19 ms", + "Error": "6.934 ms", + "StdDev": "6.486 ms", + "Median": "512.52 ms" + }, + { + "Method": "NUnit", + "Version": "4.4.0", + "Mean": "552.95 ms", + "Error": "8.867 ms", + "StdDev": "7.860 ms", + "Median": "553.62 ms" + }, + { + "Method": "MSTest", + "Version": "4.0.2", + "Mean": "474.67 ms", + "Error": "9.187 ms", + "StdDev": "8.594 ms", + "Median": "475.13 ms" + }, + { + "Method": "xUnit3", + "Version": "3.2.0", + "Mean": "562.71 ms", + "Error": "6.445 ms", + "StdDev": "6.028 ms", + "Median": "561.53 ms" + }, + { + "Method": "TUnit_AOT", + "Version": "1.1.0", + "Mean": "44.36 ms", + "Error": "1.308 ms", + "StdDev": "3.857 ms", + "Median": "45.13 ms" + } + ] +} \ No newline at end of file diff --git a/docs/static/benchmarks/SetupTeardownTests.json b/docs/static/benchmarks/SetupTeardownTests.json new file mode 100644 index 0000000000..b432996a90 --- /dev/null +++ b/docs/static/benchmarks/SetupTeardownTests.json @@ -0,0 +1,51 @@ +{ + "timestamp": "2025-11-12T21:13:20.518Z", + "category": "SetupTeardownTests", + "environment": { + "benchmarkDotNetVersion": "BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat)", + "sdk": ".NET SDK 10.0.100", + "host": ".NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3" + }, + "results": [ + { + "Method": "TUnit", + "Version": "1.1.0", + "Mean": "583.4 ms", + "Error": "6.18 ms", + "StdDev": "5.16 ms", + "Median": "583.7 ms" + }, + { + "Method": "NUnit", + "Version": "4.4.0", + "Mean": "1,198.4 ms", + "Error": "16.41 ms", + "StdDev": "15.35 ms", + "Median": "1,195.0 ms" + }, + { + "Method": "MSTest", + "Version": "4.0.2", + "Mean": "1,162.5 ms", + "Error": "10.28 ms", + "StdDev": "9.11 ms", + "Median": "1,161.5 ms" + }, + { + "Method": "xUnit3", + "Version": "3.2.0", + "Mean": "1,243.3 ms", + "Error": "10.48 ms", + "StdDev": "9.80 ms", + "Median": "1,241.9 ms" + }, + { + "Method": "TUnit_AOT", + "Version": "1.1.0", + "Mean": "NA", + "Error": "NA", + "StdDev": "NA", + "Median": "NA" + } + ] +} \ No newline at end of file diff --git a/docs/static/benchmarks/historical.json b/docs/static/benchmarks/historical.json index 0f9034eea2..10ef4375d6 100644 --- a/docs/static/benchmarks/historical.json +++ b/docs/static/benchmarks/historical.json @@ -23,6 +23,10 @@ "date": "2025-11-12", "environment": "Ubuntu" }, + { + "date": "2025-11-12", + "environment": "Ubuntu" + }, { "date": "2025-11-12", "environment": "Ubuntu" diff --git a/docs/static/benchmarks/latest.json b/docs/static/benchmarks/latest.json index bf1bb9c64a..8b4f513b5e 100644 --- a/docs/static/benchmarks/latest.json +++ b/docs/static/benchmarks/latest.json @@ -1,43 +1,253 @@ { - "timestamp": "2025-11-12T20:55:56.396Z", + "timestamp": "2025-11-12T21:13:20.518Z", "environment": { "benchmarkDotNetVersion": "BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat)", "sdk": ".NET SDK 10.0.100", "host": ".NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v3" }, "categories": { - "results": [ + "AsyncTests": [ { "Method": "TUnit", "Version": "1.1.0", - "Mean": "564.8 ms", - "Error": "5.71 ms", - "StdDev": "5.34 ms", - "Median": "564.8 ms" + "Mean": "558.9 ms", + "Error": "4.39 ms", + "StdDev": "4.11 ms", + "Median": "557.2 ms" }, { "Method": "NUnit", "Version": "4.4.0", - "Mean": "1,156.4 ms", - "Error": "5.99 ms", - "StdDev": "5.00 ms", - "Median": "1,156.4 ms" + "Mean": "650.2 ms", + "Error": "10.15 ms", + "StdDev": "9.00 ms", + "Median": "649.0 ms" }, { "Method": "MSTest", "Version": "4.0.2", - "Mean": "1,132.6 ms", - "Error": "10.60 ms", - "StdDev": "9.92 ms", - "Median": "1,130.6 ms" + "Mean": "615.0 ms", + "Error": "6.64 ms", + "StdDev": "5.89 ms", + "Median": "614.8 ms" }, { "Method": "xUnit3", "Version": "3.2.0", - "Mean": "1,200.7 ms", - "Error": "6.87 ms", - "StdDev": "5.74 ms", - "Median": "1,200.4 ms" + "Mean": "702.4 ms", + "Error": "8.00 ms", + "StdDev": "7.09 ms", + "Median": "702.4 ms" + }, + { + "Method": "TUnit_AOT", + "Version": "1.1.0", + "Mean": "123.8 ms", + "Error": "0.49 ms", + "StdDev": "0.46 ms", + "Median": "123.9 ms" + } + ], + "DataDrivenTests": [ + { + "Method": "TUnit", + "Version": "1.1.0", + "Mean": "510.37 ms", + "Error": "7.617 ms", + "StdDev": "7.125 ms", + "Median": "511.05 ms" + }, + { + "Method": "NUnit", + "Version": "4.4.0", + "Mean": "583.06 ms", + "Error": "11.292 ms", + "StdDev": "17.581 ms", + "Median": "583.47 ms" + }, + { + "Method": "MSTest", + "Version": "4.0.2", + "Mean": "594.23 ms", + "Error": "11.707 ms", + "StdDev": "18.226 ms", + "Median": "591.72 ms" + }, + { + "Method": "xUnit3", + "Version": "3.2.0", + "Mean": "604.34 ms", + "Error": "8.401 ms", + "StdDev": "7.447 ms", + "Median": "605.85 ms" + }, + { + "Method": "TUnit_AOT", + "Version": "1.1.0", + "Mean": "25.55 ms", + "Error": "0.258 ms", + "StdDev": "0.241 ms", + "Median": "25.62 ms" + } + ], + "MassiveParallelTests": [ + { + "Method": "TUnit", + "Version": "1.1.0", + "Mean": "625.5 ms", + "Error": "8.58 ms", + "StdDev": "7.61 ms", + "Median": "626.8 ms" + }, + { + "Method": "NUnit", + "Version": "4.4.0", + "Mean": "1,213.0 ms", + "Error": "13.24 ms", + "StdDev": "12.39 ms", + "Median": "1,211.9 ms" + }, + { + "Method": "MSTest", + "Version": "4.0.2", + "Mean": "3,010.8 ms", + "Error": "13.51 ms", + "StdDev": "12.63 ms", + "Median": "3,010.4 ms" + }, + { + "Method": "xUnit3", + "Version": "3.2.0", + "Mean": "3,099.6 ms", + "Error": "17.58 ms", + "StdDev": "15.59 ms", + "Median": "3,101.6 ms" + }, + { + "Method": "TUnit_AOT", + "Version": "1.1.0", + "Mean": "132.3 ms", + "Error": "0.47 ms", + "StdDev": "0.36 ms", + "Median": "132.3 ms" + } + ], + "MatrixTests": [ + { + "Method": "TUnit", + "Version": "1.1.0", + "Mean": "557.69 ms", + "Error": "4.512 ms", + "StdDev": "4.221 ms", + "Median": "556.60 ms" + }, + { + "Method": "NUnit", + "Version": "4.4.0", + "Mean": "1,539.35 ms", + "Error": "12.309 ms", + "StdDev": "11.514 ms", + "Median": "1,541.87 ms" + }, + { + "Method": "MSTest", + "Version": "4.0.2", + "Mean": "1,496.25 ms", + "Error": "7.142 ms", + "StdDev": "5.576 ms", + "Median": "1,496.55 ms" + }, + { + "Method": "xUnit3", + "Version": "3.2.0", + "Mean": "1,586.79 ms", + "Error": "7.846 ms", + "StdDev": "7.339 ms", + "Median": "1,586.67 ms" + }, + { + "Method": "TUnit_AOT", + "Version": "1.1.0", + "Mean": "79.15 ms", + "Error": "0.408 ms", + "StdDev": "0.381 ms", + "Median": "79.11 ms" + } + ], + "ScaleTests": [ + { + "Method": "TUnit", + "Version": "1.1.0", + "Mean": "514.19 ms", + "Error": "6.934 ms", + "StdDev": "6.486 ms", + "Median": "512.52 ms" + }, + { + "Method": "NUnit", + "Version": "4.4.0", + "Mean": "552.95 ms", + "Error": "8.867 ms", + "StdDev": "7.860 ms", + "Median": "553.62 ms" + }, + { + "Method": "MSTest", + "Version": "4.0.2", + "Mean": "474.67 ms", + "Error": "9.187 ms", + "StdDev": "8.594 ms", + "Median": "475.13 ms" + }, + { + "Method": "xUnit3", + "Version": "3.2.0", + "Mean": "562.71 ms", + "Error": "6.445 ms", + "StdDev": "6.028 ms", + "Median": "561.53 ms" + }, + { + "Method": "TUnit_AOT", + "Version": "1.1.0", + "Mean": "44.36 ms", + "Error": "1.308 ms", + "StdDev": "3.857 ms", + "Median": "45.13 ms" + } + ], + "SetupTeardownTests": [ + { + "Method": "TUnit", + "Version": "1.1.0", + "Mean": "583.4 ms", + "Error": "6.18 ms", + "StdDev": "5.16 ms", + "Median": "583.7 ms" + }, + { + "Method": "NUnit", + "Version": "4.4.0", + "Mean": "1,198.4 ms", + "Error": "16.41 ms", + "StdDev": "15.35 ms", + "Median": "1,195.0 ms" + }, + { + "Method": "MSTest", + "Version": "4.0.2", + "Mean": "1,162.5 ms", + "Error": "10.28 ms", + "StdDev": "9.11 ms", + "Median": "1,161.5 ms" + }, + { + "Method": "xUnit3", + "Version": "3.2.0", + "Mean": "1,243.3 ms", + "Error": "10.48 ms", + "StdDev": "9.80 ms", + "Median": "1,241.9 ms" }, { "Method": "TUnit_AOT", @@ -54,41 +264,41 @@ { "Method": "Build_TUnit", "Version": "1.1.0", - "Mean": "2.009 s", - "Error": "0.0285 s", - "StdDev": "0.0253 s", - "Median": "2.007 s" + "Mean": "2.033 s", + "Error": "0.0333 s", + "StdDev": "0.0296 s", + "Median": "2.032 s" }, { "Method": "Build_NUnit", "Version": "4.4.0", - "Mean": "1.606 s", - "Error": "0.0170 s", - "StdDev": "0.0159 s", - "Median": "1.603 s" + "Mean": "1.622 s", + "Error": "0.0231 s", + "StdDev": "0.0216 s", + "Median": "1.615 s" }, { "Method": "Build_MSTest", "Version": "4.0.2", - "Mean": "1.683 s", - "Error": "0.0155 s", - "StdDev": "0.0145 s", - "Median": "1.679 s" + "Mean": "1.705 s", + "Error": "0.0247 s", + "StdDev": "0.0231 s", + "Median": "1.701 s" }, { "Method": "Build_xUnit3", "Version": "3.2.0", - "Mean": "1.589 s", - "Error": "0.0220 s", - "StdDev": "0.0206 s", - "Median": "1.593 s" + "Mean": "1.611 s", + "Error": "0.0265 s", + "StdDev": "0.0248 s", + "Median": "1.613 s" } ] }, "stats": { - "runtimeCategories": 1, + "runtimeCategories": 6, "buildCategories": 1, - "totalBenchmarks": 2, - "lastUpdated": "2025-11-12T20:55:56.394Z" + "totalBenchmarks": 7, + "lastUpdated": "2025-11-12T21:13:20.516Z" } } \ No newline at end of file diff --git a/docs/static/benchmarks/summary.json b/docs/static/benchmarks/summary.json index f3ba5050a2..dbf147f580 100644 --- a/docs/static/benchmarks/summary.json +++ b/docs/static/benchmarks/summary.json @@ -1,6 +1,11 @@ { "runtime": [ - "results" + "AsyncTests", + "DataDrivenTests", + "MassiveParallelTests", + "MatrixTests", + "ScaleTests", + "SetupTeardownTests" ], "build": [ "BuildTime" From f7a684c5fa1f497c486f4a4237c2bb25c735d01f Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 12 Nov 2025 21:20:12 +0000 Subject: [PATCH 59/62] Delete docs/docs/benchmarks/results.md --- docs/docs/benchmarks/results.md | 78 --------------------------------- 1 file changed, 78 deletions(-) delete mode 100644 docs/docs/benchmarks/results.md diff --git a/docs/docs/benchmarks/results.md b/docs/docs/benchmarks/results.md deleted file mode 100644 index cee00e4417..0000000000 --- a/docs/docs/benchmarks/results.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -title: results -description: Performance benchmark results for results -sidebar_position: 2 ---- - -# results Benchmark - -:::info Last Updated -This benchmark was automatically generated on **2025-11-12** from the latest CI run. - -**Environment:** Ubuntu Latest • .NET SDK 10.0.100 -::: - -## šŸ“Š Results - -| Framework | Version | Mean | Median | StdDev | -|-----------|---------|------|--------|--------| -| **TUnit** | 1.1.0 | 564.8 ms | 564.8 ms | 5.34 ms | -| NUnit | 4.4.0 | 1,156.4 ms | 1,156.4 ms | 5.00 ms | -| MSTest | 4.0.2 | 1,132.6 ms | 1,130.6 ms | 9.92 ms | -| xUnit3 | 3.2.0 | 1,200.7 ms | 1,200.4 ms | 5.74 ms | -| **TUnit (AOT)** | 1.1.0 | NA | NA | NA | - -## šŸ“ˆ Visual Comparison - -```mermaid -%%{init: { - 'theme':'base', - 'themeVariables': { - 'primaryColor': '#2563eb', - 'primaryTextColor': '#ffffff', - 'primaryBorderColor': '#1e40af', - 'lineColor': '#6b7280', - 'secondaryColor': '#7c3aed', - 'tertiaryColor': '#dc2626', - 'background': '#ffffff', - 'mainBkg': '#2563eb', - 'secondBkg': '#7c3aed', - 'tertiaryBkg': '#dc2626', - 'clusterBkg': '#f3f4f6', - 'edgeLabelBackground': '#ffffff', - 'tertiaryTextColor': '#1f2937', - 'pie1': '#2563eb', - 'pie2': '#7c3aed', - 'pie3': '#dc2626', - 'pie4': '#f59e0b', - 'pie5': '#10b981', - 'pie6': '#06b6d4', - 'pie7': '#ec4899', - 'pie8': '#6366f1', - 'pie9': '#84cc16', - 'pie10': '#f97316', - 'pie11': '#14b8a6', - 'pie12': '#a855f7' - } -}}%% -xychart-beta - title "results Performance Comparison" - x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"] - y-axis "Time (ms)" 0 --> 1441 - bar [564.8, 1156.4, 1132.6, 1200.7, 0] -``` - -## šŸŽÆ Key Insights - -- **2.05x faster** than NUnit (4.4.0) -- **2.01x faster** than MSTest (4.0.2) -- **2.13x faster** than xUnit3 (3.2.0) -- **Infinityx faster** with Native AOT compilation - ---- - -:::note Methodology -View the [benchmarks overview](/docs/benchmarks) for methodology details and environment information. -::: - -*Last generated: 2025-11-12T20:55:56.395Z* From 52370ef00827da13b33eabb9521e8d29f81c4d59 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 12 Nov 2025 21:22:17 +0000 Subject: [PATCH 60/62] Remove benchmarks index from sidebar Removed benchmarks index from Performance & Benchmarks category. --- docs/sidebars.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/sidebars.ts b/docs/sidebars.ts index b53b165699..5e446d9f36 100644 --- a/docs/sidebars.ts +++ b/docs/sidebars.ts @@ -290,7 +290,6 @@ const sidebars: SidebarsConfig = { label: 'Performance & Benchmarks', collapsed: false, items: [ - 'benchmarks/index', { type: 'autogenerated', dirName: 'benchmarks', From 4728a0f7bbe6fc278672302d157a4ed87282ab8a Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 12 Nov 2025 22:32:43 +0000 Subject: [PATCH 61/62] chore(deps): update verify to 31.6.1 (#3815) Co-authored-by: Renovate Bot --- Directory.Packages.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 7fd5f5e6e3..acd52ae7d7 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -81,12 +81,12 @@ - - + + - + From 0d9047113a97fb125a80c62a819b6fd300cabf30 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 12 Nov 2025 22:49:54 +0000 Subject: [PATCH 62/62] feat: override tests via new keyword in derived classes (#3816) --- .../Generators/TestMetadataGenerator.cs | 74 ++++++++++++++++++- .../Bugs/3813/BaseServiceTests.cs | 16 ++++ .../Bugs/3813/ImplementationATests.cs | 12 +++ 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 TUnit.TestProject/Bugs/3813/BaseServiceTests.cs create mode 100644 TUnit.TestProject/Bugs/3813/ImplementationATests.cs diff --git a/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs b/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs index 5826707b2d..0f232f71d3 100644 --- a/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs +++ b/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs @@ -2341,11 +2341,83 @@ private static string GetDefaultValueString(IParameterSymbol parameter) } + private static bool IsMethodHiding(IMethodSymbol derivedMethod, IMethodSymbol baseMethod) + { + // Must have same name + if (derivedMethod.Name != baseMethod.Name) + { + return false; + } + + // Must NOT be an override (overrides are different from hiding) + if (derivedMethod.IsOverride) + { + return false; + } + + // Must have matching parameters + if (!ParametersMatch(derivedMethod.Parameters, baseMethod.Parameters)) + { + return false; + } + + // Derived method's containing type must be derived from base method's containing type + var derivedType = derivedMethod.ContainingType; + var baseType = baseMethod.ContainingType; + + // Can't hide yourself + if (SymbolEqualityComparer.Default.Equals(derivedType.OriginalDefinition, baseType.OriginalDefinition)) + { + return false; + } + + // Check if derived type inherits from base type + var current = derivedType.BaseType; + while (current is not null && current.SpecialType != SpecialType.System_Object) + { + if (SymbolEqualityComparer.Default.Equals(current.OriginalDefinition, baseType.OriginalDefinition)) + { + return true; + } + current = current.BaseType; + } + + return false; + } + private static List CollectInheritedTestMethods(INamedTypeSymbol derivedClass) { - return derivedClass.GetMembersIncludingBase().OfType() + var allTestMethods = derivedClass.GetMembersIncludingBase() + .OfType() .Where(m => m.GetAttributes().Any(attr => attr.IsTestAttribute())) .ToList(); + + // Find methods declared directly on the derived class + var derivedClassMethods = allTestMethods + .Where(m => SymbolEqualityComparer.Default.Equals(m.ContainingType.OriginalDefinition, derivedClass.OriginalDefinition)) + .ToList(); + + // Filter out base methods that are hidden by derived class methods or declared directly on derived class + var result = new List(); + foreach (var method in allTestMethods) + { + // Skip methods declared directly on derived class + // (they're handled by regular test registration) + if (SymbolEqualityComparer.Default.Equals(method.ContainingType.OriginalDefinition, derivedClass.OriginalDefinition)) + { + continue; + } + + // Check if this base method is hidden by any derived class method + var isHidden = derivedClassMethods.Any(derived => IsMethodHiding(derived, method)); + + if (!isHidden) + { + result.Add(method); + } + } + + return result; } private static IMethodSymbol? FindConcreteMethodImplementation(INamedTypeSymbol derivedClass, IMethodSymbol baseMethod) diff --git a/TUnit.TestProject/Bugs/3813/BaseServiceTests.cs b/TUnit.TestProject/Bugs/3813/BaseServiceTests.cs new file mode 100644 index 0000000000..f435689105 --- /dev/null +++ b/TUnit.TestProject/Bugs/3813/BaseServiceTests.cs @@ -0,0 +1,16 @@ +namespace TUnit.TestProject.Bugs._3813; + +public abstract class BaseServiceTests +{ + [Test] + public async Task BasicFeature() + { + await Task.CompletedTask; + } + + [Test] + public async Task AdvancedFeature() + { + await Task.CompletedTask; + } +} diff --git a/TUnit.TestProject/Bugs/3813/ImplementationATests.cs b/TUnit.TestProject/Bugs/3813/ImplementationATests.cs new file mode 100644 index 0000000000..75f2c52d8d --- /dev/null +++ b/TUnit.TestProject/Bugs/3813/ImplementationATests.cs @@ -0,0 +1,12 @@ +namespace TUnit.TestProject.Bugs._3813; + +[InheritsTests] +public class ImplementationATests : BaseServiceTests +{ + [Test] + [Skip("Implementation A does not support advanced feature")] + public new Task AdvancedFeature() + { + return Task.CompletedTask; + } +}