diff --git a/Directory.Packages.props b/Directory.Packages.props
index 4c480ad7b4..a83b526eda 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -81,9 +81,9 @@
-
-
-
+
+
+
diff --git a/TUnit.Core.SourceGenerator.Tests/AssemblyAfterTests.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/AssemblyAfterTests.Test.verified.txt
index f4d052aceb..ec6b3bd938 100644
--- a/TUnit.Core.SourceGenerator.Tests/AssemblyAfterTests.Test.verified.txt
+++ b/TUnit.Core.SourceGenerator.Tests/AssemblyAfterTests.Test.verified.txt
@@ -22,7 +22,7 @@ internal static class AssemblyBase1_AfterAll1_After_Assembly_GUIDInitializer
public static void Initialize()
{
var TestsBase`1_assembly = typeof(global::TUnit.TestProject.AfterTests.AssemblyBase1).Assembly;
- global::TUnit.Core.Sources.AfterAssemblyHooks.GetOrAdd(TestsBase`1_assembly, _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.AfterAssemblyHooks.GetOrAdd(TestsBase`1_assembly, static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.AfterAssemblyHooks[TestsBase`1_assembly].Add(
new AfterAssemblyHookMethod
{
@@ -97,7 +97,7 @@ internal static class AssemblyBase1_AfterEach1_After_Test_GUIDInitializer
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.AssemblyBase1), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.AssemblyBase1), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.AfterTestHooks[typeof(global::TUnit.TestProject.AfterTests.AssemblyBase1)].Add(
new InstanceHookMethod
{
@@ -173,7 +173,7 @@ internal static class AssemblyBase2_AfterAll2_After_Assembly_GUIDInitializer
public static void Initialize()
{
var TestsBase`1_assembly = typeof(global::TUnit.TestProject.AfterTests.AssemblyBase2).Assembly;
- global::TUnit.Core.Sources.AfterAssemblyHooks.GetOrAdd(TestsBase`1_assembly, _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.AfterAssemblyHooks.GetOrAdd(TestsBase`1_assembly, static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.AfterAssemblyHooks[TestsBase`1_assembly].Add(
new AfterAssemblyHookMethod
{
@@ -248,7 +248,7 @@ internal static class AssemblyBase2_AfterEach2_After_Test_GUIDInitializer
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.AssemblyBase2), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.AssemblyBase2), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.AfterTestHooks[typeof(global::TUnit.TestProject.AfterTests.AssemblyBase2)].Add(
new InstanceHookMethod
{
@@ -324,7 +324,7 @@ internal static class AssemblyBase3_AfterAll3_After_Assembly_GUIDInitializer
public static void Initialize()
{
var TestsBase`1_assembly = typeof(global::TUnit.TestProject.AfterTests.AssemblyBase3).Assembly;
- global::TUnit.Core.Sources.AfterAssemblyHooks.GetOrAdd(TestsBase`1_assembly, _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.AfterAssemblyHooks.GetOrAdd(TestsBase`1_assembly, static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.AfterAssemblyHooks[TestsBase`1_assembly].Add(
new AfterAssemblyHookMethod
{
@@ -399,7 +399,7 @@ internal static class AssemblyBase3_AfterEach3_After_Test_GUIDInitializer
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.AssemblyBase3), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.AssemblyBase3), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.AfterTestHooks[typeof(global::TUnit.TestProject.AfterTests.AssemblyBase3)].Add(
new InstanceHookMethod
{
@@ -475,7 +475,7 @@ internal static class AssemblyCleanupTests_AfterAllCleanUp_After_Assembly_GUIDIn
public static void Initialize()
{
var TestsBase`1_assembly = typeof(global::TUnit.TestProject.AfterTests.AssemblyCleanupTests).Assembly;
- global::TUnit.Core.Sources.AfterAssemblyHooks.GetOrAdd(TestsBase`1_assembly, _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.AfterAssemblyHooks.GetOrAdd(TestsBase`1_assembly, static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.AfterAssemblyHooks[TestsBase`1_assembly].Add(
new AfterAssemblyHookMethod
{
@@ -551,7 +551,7 @@ internal static class AssemblyCleanupTests_AfterAllCleanUpWithContext_After_Asse
public static void Initialize()
{
var TestsBase`1_assembly = typeof(global::TUnit.TestProject.AfterTests.AssemblyCleanupTests).Assembly;
- global::TUnit.Core.Sources.AfterAssemblyHooks.GetOrAdd(TestsBase`1_assembly, _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.AfterAssemblyHooks.GetOrAdd(TestsBase`1_assembly, static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.AfterAssemblyHooks[TestsBase`1_assembly].Add(
new AfterAssemblyHookMethod
{
@@ -636,7 +636,7 @@ internal static class AssemblyCleanupTests_AfterAllCleanUp2_After_Assembly_GUIDI
public static void Initialize()
{
var TestsBase`1_assembly = typeof(global::TUnit.TestProject.AfterTests.AssemblyCleanupTests).Assembly;
- global::TUnit.Core.Sources.AfterAssemblyHooks.GetOrAdd(TestsBase`1_assembly, _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.AfterAssemblyHooks.GetOrAdd(TestsBase`1_assembly, static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.AfterAssemblyHooks[TestsBase`1_assembly].Add(
new AfterAssemblyHookMethod
{
@@ -712,7 +712,7 @@ internal static class AssemblyCleanupTests_AfterAllCleanUpWithContextAndToken_Af
public static void Initialize()
{
var TestsBase`1_assembly = typeof(global::TUnit.TestProject.AfterTests.AssemblyCleanupTests).Assembly;
- global::TUnit.Core.Sources.AfterAssemblyHooks.GetOrAdd(TestsBase`1_assembly, _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.AfterAssemblyHooks.GetOrAdd(TestsBase`1_assembly, static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.AfterAssemblyHooks[TestsBase`1_assembly].Add(
new AfterAssemblyHookMethod
{
@@ -803,7 +803,7 @@ internal static class AssemblyCleanupTests_Cleanup_After_Test_GUIDInitializer
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.AssemblyCleanupTests), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.AssemblyCleanupTests), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.AfterTestHooks[typeof(global::TUnit.TestProject.AfterTests.AssemblyCleanupTests)].Add(
new InstanceHookMethod
{
@@ -878,7 +878,7 @@ internal static class AssemblyCleanupTests_Cleanup_After_Test_GUIDInitializer
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.AssemblyCleanupTests), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.AssemblyCleanupTests), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.AfterTestHooks[typeof(global::TUnit.TestProject.AfterTests.AssemblyCleanupTests)].Add(
new InstanceHookMethod
{
@@ -962,7 +962,7 @@ internal static class AssemblyCleanupTests_CleanupWithContext_After_Test_GUIDIni
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.AssemblyCleanupTests), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.AssemblyCleanupTests), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.AfterTestHooks[typeof(global::TUnit.TestProject.AfterTests.AssemblyCleanupTests)].Add(
new InstanceHookMethod
{
@@ -1046,7 +1046,7 @@ internal static class AssemblyCleanupTests_CleanupWithContext_After_Test_GUIDIni
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.AssemblyCleanupTests), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.AssemblyCleanupTests), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.AfterTestHooks[typeof(global::TUnit.TestProject.AfterTests.AssemblyCleanupTests)].Add(
new InstanceHookMethod
{
diff --git a/TUnit.Core.SourceGenerator.Tests/AssemblyBeforeTests.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/AssemblyBeforeTests.Test.verified.txt
index 6341fa3557..592bf7f927 100644
--- a/TUnit.Core.SourceGenerator.Tests/AssemblyBeforeTests.Test.verified.txt
+++ b/TUnit.Core.SourceGenerator.Tests/AssemblyBeforeTests.Test.verified.txt
@@ -22,7 +22,7 @@ internal static class AssemblyBase1_BeforeAll1_Before_Assembly_GUIDInitializer
public static void Initialize()
{
var TestsBase`1_assembly = typeof(global::TUnit.TestProject.BeforeTests.AssemblyBase1).Assembly;
- global::TUnit.Core.Sources.BeforeAssemblyHooks.GetOrAdd(TestsBase`1_assembly, _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.BeforeAssemblyHooks.GetOrAdd(TestsBase`1_assembly, static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.BeforeAssemblyHooks[TestsBase`1_assembly].Add(
new BeforeAssemblyHookMethod
{
@@ -97,7 +97,7 @@ internal static class AssemblyBase1_BeforeEach1_Before_Test_GUIDInitializer
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.AssemblyBase1), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.AssemblyBase1), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.BeforeTestHooks[typeof(global::TUnit.TestProject.BeforeTests.AssemblyBase1)].Add(
new InstanceHookMethod
{
@@ -173,7 +173,7 @@ internal static class AssemblyBase2_BeforeAll2_Before_Assembly_GUIDInitializer
public static void Initialize()
{
var TestsBase`1_assembly = typeof(global::TUnit.TestProject.BeforeTests.AssemblyBase2).Assembly;
- global::TUnit.Core.Sources.BeforeAssemblyHooks.GetOrAdd(TestsBase`1_assembly, _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.BeforeAssemblyHooks.GetOrAdd(TestsBase`1_assembly, static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.BeforeAssemblyHooks[TestsBase`1_assembly].Add(
new BeforeAssemblyHookMethod
{
@@ -248,7 +248,7 @@ internal static class AssemblyBase2_BeforeEach2_Before_Test_GUIDInitializer
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.AssemblyBase2), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.AssemblyBase2), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.BeforeTestHooks[typeof(global::TUnit.TestProject.BeforeTests.AssemblyBase2)].Add(
new InstanceHookMethod
{
@@ -324,7 +324,7 @@ internal static class AssemblyBase3_BeforeAll3_Before_Assembly_GUIDInitializer
public static void Initialize()
{
var TestsBase`1_assembly = typeof(global::TUnit.TestProject.BeforeTests.AssemblyBase3).Assembly;
- global::TUnit.Core.Sources.BeforeAssemblyHooks.GetOrAdd(TestsBase`1_assembly, _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.BeforeAssemblyHooks.GetOrAdd(TestsBase`1_assembly, static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.BeforeAssemblyHooks[TestsBase`1_assembly].Add(
new BeforeAssemblyHookMethod
{
@@ -399,7 +399,7 @@ internal static class AssemblyBase3_BeforeEach3_Before_Test_GUIDInitializer
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.AssemblyBase3), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.AssemblyBase3), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.BeforeTestHooks[typeof(global::TUnit.TestProject.BeforeTests.AssemblyBase3)].Add(
new InstanceHookMethod
{
@@ -475,7 +475,7 @@ internal static class AssemblySetupTests_BeforeAllSetUp_Before_Assembly_GUIDInit
public static void Initialize()
{
var TestsBase`1_assembly = typeof(global::TUnit.TestProject.BeforeTests.AssemblySetupTests).Assembly;
- global::TUnit.Core.Sources.BeforeAssemblyHooks.GetOrAdd(TestsBase`1_assembly, _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.BeforeAssemblyHooks.GetOrAdd(TestsBase`1_assembly, static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.BeforeAssemblyHooks[TestsBase`1_assembly].Add(
new BeforeAssemblyHookMethod
{
@@ -551,7 +551,7 @@ internal static class AssemblySetupTests_BeforeAllSetUpWithContext_Before_Assemb
public static void Initialize()
{
var TestsBase`1_assembly = typeof(global::TUnit.TestProject.BeforeTests.AssemblySetupTests).Assembly;
- global::TUnit.Core.Sources.BeforeAssemblyHooks.GetOrAdd(TestsBase`1_assembly, _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.BeforeAssemblyHooks.GetOrAdd(TestsBase`1_assembly, static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.BeforeAssemblyHooks[TestsBase`1_assembly].Add(
new BeforeAssemblyHookMethod
{
@@ -636,7 +636,7 @@ internal static class AssemblySetupTests_BeforeAllSetUp2_Before_Assembly_GUIDIni
public static void Initialize()
{
var TestsBase`1_assembly = typeof(global::TUnit.TestProject.BeforeTests.AssemblySetupTests).Assembly;
- global::TUnit.Core.Sources.BeforeAssemblyHooks.GetOrAdd(TestsBase`1_assembly, _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.BeforeAssemblyHooks.GetOrAdd(TestsBase`1_assembly, static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.BeforeAssemblyHooks[TestsBase`1_assembly].Add(
new BeforeAssemblyHookMethod
{
@@ -712,7 +712,7 @@ internal static class AssemblySetupTests_BeforeAllSetUpWithContext_Before_Assemb
public static void Initialize()
{
var TestsBase`1_assembly = typeof(global::TUnit.TestProject.BeforeTests.AssemblySetupTests).Assembly;
- global::TUnit.Core.Sources.BeforeAssemblyHooks.GetOrAdd(TestsBase`1_assembly, _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.BeforeAssemblyHooks.GetOrAdd(TestsBase`1_assembly, static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.BeforeAssemblyHooks[TestsBase`1_assembly].Add(
new BeforeAssemblyHookMethod
{
@@ -803,7 +803,7 @@ internal static class AssemblySetupTests_Setup_Before_Test_GUIDInitializer
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.AssemblySetupTests), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.AssemblySetupTests), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.BeforeTestHooks[typeof(global::TUnit.TestProject.BeforeTests.AssemblySetupTests)].Add(
new InstanceHookMethod
{
@@ -878,7 +878,7 @@ internal static class AssemblySetupTests_Setup_Before_Test_GUIDInitializer
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.AssemblySetupTests), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.AssemblySetupTests), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.BeforeTestHooks[typeof(global::TUnit.TestProject.BeforeTests.AssemblySetupTests)].Add(
new InstanceHookMethod
{
@@ -962,7 +962,7 @@ internal static class AssemblySetupTests_SetupWithContext_Before_Test_GUIDInitia
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.AssemblySetupTests), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.AssemblySetupTests), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.BeforeTestHooks[typeof(global::TUnit.TestProject.BeforeTests.AssemblySetupTests)].Add(
new InstanceHookMethod
{
@@ -1046,7 +1046,7 @@ internal static class AssemblySetupTests_SetupWithContext_Before_Test_GUIDInitia
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.AssemblySetupTests), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.AssemblySetupTests), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.BeforeTestHooks[typeof(global::TUnit.TestProject.BeforeTests.AssemblySetupTests)].Add(
new InstanceHookMethod
{
diff --git a/TUnit.Core.SourceGenerator.Tests/GlobalStaticAfterEachTests.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/GlobalStaticAfterEachTests.Test.verified.txt
index ade513497d..eb67b6c9cf 100644
--- a/TUnit.Core.SourceGenerator.Tests/GlobalStaticAfterEachTests.Test.verified.txt
+++ b/TUnit.Core.SourceGenerator.Tests/GlobalStaticAfterEachTests.Test.verified.txt
@@ -21,7 +21,7 @@ internal static class GlobalBase1_AfterEach1_After_Test_GUIDInitializer
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.GlobalBase1), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.GlobalBase1), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.AfterTestHooks[typeof(global::TUnit.TestProject.AfterTests.GlobalBase1)].Add(
new InstanceHookMethod
{
@@ -96,7 +96,7 @@ internal static class GlobalBase2_AfterEach2_After_Test_GUIDInitializer
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.GlobalBase2), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.GlobalBase2), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.AfterTestHooks[typeof(global::TUnit.TestProject.AfterTests.GlobalBase2)].Add(
new InstanceHookMethod
{
@@ -171,7 +171,7 @@ internal static class GlobalBase3_AfterEach3_After_Test_GUIDInitializer
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.GlobalBase3), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.GlobalBase3), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.AfterTestHooks[typeof(global::TUnit.TestProject.AfterTests.GlobalBase3)].Add(
new InstanceHookMethod
{
@@ -246,7 +246,7 @@ internal static class GlobalCleanUpTests_CleanUp_After_Test_GUIDInitializer
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.GlobalCleanUpTests), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.GlobalCleanUpTests), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.AfterTestHooks[typeof(global::TUnit.TestProject.AfterTests.GlobalCleanUpTests)].Add(
new InstanceHookMethod
{
@@ -321,7 +321,7 @@ internal static class GlobalCleanUpTests_CleanUp_After_Test_GUIDInitializer
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.GlobalCleanUpTests), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.GlobalCleanUpTests), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.AfterTestHooks[typeof(global::TUnit.TestProject.AfterTests.GlobalCleanUpTests)].Add(
new InstanceHookMethod
{
@@ -405,7 +405,7 @@ internal static class GlobalCleanUpTests_CleanUpWithContext_After_Test_GUIDIniti
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.GlobalCleanUpTests), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.GlobalCleanUpTests), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.AfterTestHooks[typeof(global::TUnit.TestProject.AfterTests.GlobalCleanUpTests)].Add(
new InstanceHookMethod
{
@@ -489,7 +489,7 @@ internal static class GlobalCleanUpTests_CleanUpWithContext_After_Test_GUIDIniti
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.GlobalCleanUpTests), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.AfterTests.GlobalCleanUpTests), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.AfterTestHooks[typeof(global::TUnit.TestProject.AfterTests.GlobalCleanUpTests)].Add(
new InstanceHookMethod
{
diff --git a/TUnit.Core.SourceGenerator.Tests/GlobalStaticBeforeEachTests.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/GlobalStaticBeforeEachTests.Test.verified.txt
index 0941f23f3b..b4f8390141 100644
--- a/TUnit.Core.SourceGenerator.Tests/GlobalStaticBeforeEachTests.Test.verified.txt
+++ b/TUnit.Core.SourceGenerator.Tests/GlobalStaticBeforeEachTests.Test.verified.txt
@@ -21,7 +21,7 @@ internal static class GlobalBase1_BeforeEach1_Before_Test_GUIDInitializer
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.GlobalBase1), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.GlobalBase1), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.BeforeTestHooks[typeof(global::TUnit.TestProject.BeforeTests.GlobalBase1)].Add(
new InstanceHookMethod
{
@@ -96,7 +96,7 @@ internal static class GlobalBase2_BeforeEach2_Before_Test_GUIDInitializer
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.GlobalBase2), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.GlobalBase2), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.BeforeTestHooks[typeof(global::TUnit.TestProject.BeforeTests.GlobalBase2)].Add(
new InstanceHookMethod
{
@@ -171,7 +171,7 @@ internal static class GlobalBase3_BeforeEach3_Before_Test_GUIDInitializer
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.GlobalBase3), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.GlobalBase3), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.BeforeTestHooks[typeof(global::TUnit.TestProject.BeforeTests.GlobalBase3)].Add(
new InstanceHookMethod
{
@@ -246,7 +246,7 @@ internal static class GlobalSetUpTests_SetUp_Before_Test_GUIDInitializer
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.GlobalSetUpTests), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.GlobalSetUpTests), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.BeforeTestHooks[typeof(global::TUnit.TestProject.BeforeTests.GlobalSetUpTests)].Add(
new InstanceHookMethod
{
@@ -321,7 +321,7 @@ internal static class GlobalSetUpTests_SetUp_Before_Test_GUIDInitializer
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.GlobalSetUpTests), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.GlobalSetUpTests), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.BeforeTestHooks[typeof(global::TUnit.TestProject.BeforeTests.GlobalSetUpTests)].Add(
new InstanceHookMethod
{
@@ -405,7 +405,7 @@ internal static class GlobalSetUpTests_SetUpWithContext_Before_Test_GUIDInitiali
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.GlobalSetUpTests), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.GlobalSetUpTests), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.BeforeTestHooks[typeof(global::TUnit.TestProject.BeforeTests.GlobalSetUpTests)].Add(
new InstanceHookMethod
{
@@ -489,7 +489,7 @@ internal static class GlobalSetUpTests_SetUpWithContext_Before_Test_GUIDInitiali
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.GlobalSetUpTests), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.BeforeTests.GlobalSetUpTests), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.BeforeTestHooks[typeof(global::TUnit.TestProject.BeforeTests.GlobalSetUpTests)].Add(
new InstanceHookMethod
{
diff --git a/TUnit.Core.SourceGenerator.Tests/HooksTests.DisposableFieldTests.verified.txt b/TUnit.Core.SourceGenerator.Tests/HooksTests.DisposableFieldTests.verified.txt
index dc6db06f66..e920c70d68 100644
--- a/TUnit.Core.SourceGenerator.Tests/HooksTests.DisposableFieldTests.verified.txt
+++ b/TUnit.Core.SourceGenerator.Tests/HooksTests.DisposableFieldTests.verified.txt
@@ -21,7 +21,7 @@ internal static class DisposableFieldTests_Setup_Before_Test_GUIDInitializer
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.DisposableFieldTests), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.BeforeTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.DisposableFieldTests), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.BeforeTestHooks[typeof(global::TUnit.TestProject.DisposableFieldTests)].Add(
new InstanceHookMethod
{
@@ -96,7 +96,7 @@ internal static class DisposableFieldTests_Blah_After_Test_GUIDInitializer
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
- global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.DisposableFieldTests), _ => new global::System.Collections.Concurrent.ConcurrentBag());
+ global::TUnit.Core.Sources.AfterTestHooks.GetOrAdd(typeof(global::TUnit.TestProject.DisposableFieldTests), static _ => new global::System.Collections.Concurrent.ConcurrentBag());
global::TUnit.Core.Sources.AfterTestHooks[typeof(global::TUnit.TestProject.DisposableFieldTests)].Add(
new InstanceHookMethod
{
diff --git a/TUnit.Core.SourceGenerator/CodeGenerators/Formatting/TypedConstantFormatter.cs b/TUnit.Core.SourceGenerator/CodeGenerators/Formatting/TypedConstantFormatter.cs
index 173a21d1a2..02acfca105 100644
--- a/TUnit.Core.SourceGenerator/CodeGenerators/Formatting/TypedConstantFormatter.cs
+++ b/TUnit.Core.SourceGenerator/CodeGenerators/Formatting/TypedConstantFormatter.cs
@@ -1,4 +1,5 @@
using System.Globalization;
+using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using TUnit.Core.SourceGenerator.Extensions;
@@ -386,10 +387,46 @@ private bool AreValuesEqual(object? enumValue, object? providedValue)
private static string EscapeForTestId(string str)
{
- return str.Replace("\\", "\\\\")
- .Replace("\r", "\\r")
- .Replace("\n", "\\n")
- .Replace("\t", "\\t")
- .Replace("\"", "\\\"");
+ var needsEscape = false;
+ foreach (var c in str)
+ {
+ if (c == '\\' || c == '\r' || c == '\n' || c == '\t' || c == '"')
+ {
+ needsEscape = true;
+ break;
+ }
+ }
+
+ if (!needsEscape)
+ {
+ return str;
+ }
+
+ var builder = new StringBuilder(str.Length + 10);
+ foreach (var c in str)
+ {
+ switch (c)
+ {
+ case '\\':
+ builder.Append("\\\\");
+ break;
+ case '\r':
+ builder.Append("\\r");
+ break;
+ case '\n':
+ builder.Append("\\n");
+ break;
+ case '\t':
+ builder.Append("\\t");
+ break;
+ case '"':
+ builder.Append("\\\"");
+ break;
+ default:
+ builder.Append(c);
+ break;
+ }
+ }
+ return builder.ToString();
}
}
diff --git a/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/DataSourceAttributeHelper.cs b/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/DataSourceAttributeHelper.cs
index 40deca4e17..935fec6b69 100644
--- a/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/DataSourceAttributeHelper.cs
+++ b/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/DataSourceAttributeHelper.cs
@@ -1,5 +1,5 @@
using Microsoft.CodeAnalysis;
-using TUnit.Core.SourceGenerator.Extensions;
+using TUnit.Core.SourceGenerator.Helpers;
namespace TUnit.Core.SourceGenerator.CodeGenerators.Helpers;
@@ -12,10 +12,10 @@ public static bool IsDataSourceAttribute(INamedTypeSymbol? attributeClass)
return false;
}
- // Check if the attribute implements IDataSourceAttribute
- return attributeClass.AllInterfaces.Any(i => i.GloballyQualified() == "global::TUnit.Core.IDataSourceAttribute");
+ // Check if the attribute implements IDataSourceAttribute (using cache)
+ return InterfaceCache.ImplementsInterface(attributeClass, "global::TUnit.Core.IDataSourceAttribute");
}
-
+
public static bool IsTypedDataSourceAttribute(INamedTypeSymbol? attributeClass)
{
if (attributeClass == null)
@@ -23,12 +23,10 @@ public static bool IsTypedDataSourceAttribute(INamedTypeSymbol? attributeClass)
return false;
}
- // Check if the attribute implements ITypedDataSourceAttribute
- return attributeClass.AllInterfaces.Any(i =>
- i.IsGenericType &&
- i.ConstructedFrom.GloballyQualified() == "global::TUnit.Core.ITypedDataSourceAttribute`1");
+ // Check if the attribute implements ITypedDataSourceAttribute (using cache)
+ return InterfaceCache.ImplementsGenericInterface(attributeClass, "global::TUnit.Core.ITypedDataSourceAttribute`1");
}
-
+
public static ITypeSymbol? GetTypedDataSourceType(INamedTypeSymbol? attributeClass)
{
if (attributeClass == null)
@@ -36,10 +34,8 @@ public static bool IsTypedDataSourceAttribute(INamedTypeSymbol? attributeClass)
return null;
}
- var typedInterface = attributeClass.AllInterfaces
- .FirstOrDefault(i => i.IsGenericType &&
- i.ConstructedFrom.GloballyQualified() == "global::TUnit.Core.ITypedDataSourceAttribute`1");
-
+ var typedInterface = InterfaceCache.GetGenericInterface(attributeClass, "global::TUnit.Core.ITypedDataSourceAttribute`1");
+
return typedInterface?.TypeArguments.FirstOrDefault();
}
}
\ No newline at end of file
diff --git a/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/InstanceFactoryGenerator.cs b/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/InstanceFactoryGenerator.cs
index 1fe8a151a5..38fb99fadc 100644
--- a/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/InstanceFactoryGenerator.cs
+++ b/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/InstanceFactoryGenerator.cs
@@ -86,32 +86,32 @@ public static void GenerateInstanceFactory(CodeWriter writer, ITypeSymbol typeSy
private static IMethodSymbol? GetPrimaryConstructor(ITypeSymbol typeSymbol)
{
+ // Materialize constructors once to avoid multiple enumerations
var constructors = typeSymbol.GetMembers()
.OfType()
.Where(m => m.MethodKind == MethodKind.Constructor && !m.IsStatic)
- .ToList();
+ .ToArray();
// First, check for constructors marked with [TestConstructor]
- var testConstructorMarked = constructors
- .Where(c => c.GetAttributes().Any(a =>
- a.AttributeClass?.ToDisplayString() == WellKnownFullyQualifiedClassNames.TestConstructorAttribute.WithoutGlobalPrefix))
- .ToList();
-
- if (testConstructorMarked.Count > 0)
+ foreach (var constructor in constructors)
{
- return testConstructorMarked[0];
+ if (constructor.GetAttributes().Any(a =>
+ a.AttributeClass?.ToDisplayString() == WellKnownFullyQualifiedClassNames.TestConstructorAttribute.WithoutGlobalPrefix))
+ {
+ return constructor;
+ }
}
// If no [TestConstructor] found, use existing logic
- constructors = constructors.OrderByDescending(c => c.Parameters.Length).ToList();
+ var orderedConstructors = constructors.OrderByDescending(c => c.Parameters.Length).ToArray();
- if (constructors.Count == 1)
+ if (orderedConstructors.Length == 1)
{
- return constructors[0];
+ return orderedConstructors[0];
}
- var publicConstructors = constructors.Where(c => c.DeclaredAccessibility == Accessibility.Public).ToList();
- return publicConstructors.Count == 1 ? publicConstructors[0] : publicConstructors.FirstOrDefault();
+ var publicConstructors = orderedConstructors.Where(c => c.DeclaredAccessibility == Accessibility.Public).ToArray();
+ return publicConstructors.Length == 1 ? publicConstructors[0] : publicConstructors.FirstOrDefault();
}
private static void GenerateTypedConstructorCall(CodeWriter writer, string className, IMethodSymbol constructor, TestMethodMetadata? testMethod)
diff --git a/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/TypedDataSourceOptimizer.cs b/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/TypedDataSourceOptimizer.cs
index c571175423..48f1c2a16b 100644
--- a/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/TypedDataSourceOptimizer.cs
+++ b/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/TypedDataSourceOptimizer.cs
@@ -10,11 +10,8 @@ internal static class TypedDataSourceOptimizer
///
public static bool CanOptimizeTypedDataSource(AttributeData dataSourceAttribute, IMethodSymbol testMethod)
{
- if (!dataSourceAttribute.IsTypedDataSourceAttribute())
- {
- return false;
- }
-
+ // GetTypedDataSourceType already checks if it's a typed data source (returns null if not)
+ // This avoids enumerating AllInterfaces twice
var dataSourceType = dataSourceAttribute.GetTypedDataSourceType();
if (dataSourceType == null)
{
diff --git a/TUnit.Core.SourceGenerator/CodeWriter.cs b/TUnit.Core.SourceGenerator/CodeWriter.cs
index bf392288f5..a6efffc07c 100644
--- a/TUnit.Core.SourceGenerator/CodeWriter.cs
+++ b/TUnit.Core.SourceGenerator/CodeWriter.cs
@@ -1,3 +1,4 @@
+using System.Collections.Concurrent;
using System.Text;
namespace TUnit.Core.SourceGenerator;
@@ -13,10 +14,18 @@ public class CodeWriter : ICodeWriter
internal int _indentLevel; // Keep old name for compatibility
private bool _isNewLine = true;
+ private static readonly ConcurrentDictionary<(string, int), string> _indentCache = new();
+
public CodeWriter(string indentString = " ", bool includeHeader = true)
{
_indentString = indentString;
+ for (var i = 0; i <= 10; i++)
+ {
+ var key = (_indentString, i);
+ _indentCache.TryAdd(key, string.Concat(Enumerable.Repeat(_indentString, i)));
+ }
+
if (includeHeader)
{
_builder.AppendLine("// ");
@@ -26,7 +35,7 @@ public CodeWriter(string indentString = " ", bool includeHeader = true)
}
else
{
- _isNewLine = true; // Fix: Always start at new line state for proper indentation
+ _isNewLine = true;
}
}
@@ -39,6 +48,15 @@ public ICodeWriter SetIndentLevel(int level)
return this;
}
+ ///
+ /// Gets the cached indentation string for the specified level, building it if necessary.
+ ///
+ private string GetIndentation(int level)
+ {
+ var key = (_indentString, level);
+ return _indentCache.GetOrAdd(key, static k => string.Concat(Enumerable.Repeat(k.Item1, k.Item2)));
+ }
+
///
/// Appends text to the current line, applying indentation if at the start of a new line.
///
@@ -51,7 +69,7 @@ public ICodeWriter Append(string text)
if (_isNewLine)
{
- _builder.Append(string.Concat(Enumerable.Repeat(_indentString, _indentLevel)));
+ _builder.Append(GetIndentation(_indentLevel));
_isNewLine = false;
}
_builder.Append(text);
diff --git a/TUnit.Core.SourceGenerator/Extensions/AttributeDataExtensions.cs b/TUnit.Core.SourceGenerator/Extensions/AttributeDataExtensions.cs
index ef50b74657..e13ab939d7 100644
--- a/TUnit.Core.SourceGenerator/Extensions/AttributeDataExtensions.cs
+++ b/TUnit.Core.SourceGenerator/Extensions/AttributeDataExtensions.cs
@@ -45,21 +45,21 @@ public static bool IsTypedDataSourceAttribute(this AttributeData? attributeData)
public static bool IsNonGlobalHook(this AttributeData attributeData, Compilation compilation)
{
- return SymbolEqualityComparer.Default.Equals(attributeData.AttributeClass,
- compilation.GetTypeByMetadataName(WellKnownFullyQualifiedClassNames.BeforeAttribute
- .WithoutGlobalPrefix))
- || SymbolEqualityComparer.Default.Equals(attributeData.AttributeClass,
- compilation.GetTypeByMetadataName(WellKnownFullyQualifiedClassNames.AfterAttribute
- .WithoutGlobalPrefix));
+ // Cache type symbols to avoid repeated GetTypeByMetadataName calls
+ var beforeAttribute = compilation.GetTypeByMetadataName(WellKnownFullyQualifiedClassNames.BeforeAttribute.WithoutGlobalPrefix);
+ var afterAttribute = compilation.GetTypeByMetadataName(WellKnownFullyQualifiedClassNames.AfterAttribute.WithoutGlobalPrefix);
+
+ return SymbolEqualityComparer.Default.Equals(attributeData.AttributeClass, beforeAttribute)
+ || SymbolEqualityComparer.Default.Equals(attributeData.AttributeClass, afterAttribute);
}
public static bool IsGlobalHook(this AttributeData attributeData, Compilation compilation)
{
- return SymbolEqualityComparer.Default.Equals(attributeData.AttributeClass,
- compilation.GetTypeByMetadataName(WellKnownFullyQualifiedClassNames.BeforeEveryAttribute
- .WithoutGlobalPrefix))
- || SymbolEqualityComparer.Default.Equals(attributeData.AttributeClass,
- compilation.GetTypeByMetadataName(WellKnownFullyQualifiedClassNames.AfterEveryAttribute
- .WithoutGlobalPrefix));
+ // Cache type symbols to avoid repeated GetTypeByMetadataName calls
+ var beforeEveryAttribute = compilation.GetTypeByMetadataName(WellKnownFullyQualifiedClassNames.BeforeEveryAttribute.WithoutGlobalPrefix);
+ var afterEveryAttribute = compilation.GetTypeByMetadataName(WellKnownFullyQualifiedClassNames.AfterEveryAttribute.WithoutGlobalPrefix);
+
+ return SymbolEqualityComparer.Default.Equals(attributeData.AttributeClass, beforeEveryAttribute)
+ || SymbolEqualityComparer.Default.Equals(attributeData.AttributeClass, afterEveryAttribute);
}
}
diff --git a/TUnit.Core.SourceGenerator/Extensions/StringExtensions.cs b/TUnit.Core.SourceGenerator/Extensions/StringExtensions.cs
index e47921ea91..3eee610ddc 100644
--- a/TUnit.Core.SourceGenerator/Extensions/StringExtensions.cs
+++ b/TUnit.Core.SourceGenerator/Extensions/StringExtensions.cs
@@ -1,13 +1,12 @@
-using System.Text.RegularExpressions;
-
-namespace TUnit.Core.SourceGenerator.Extensions;
+namespace TUnit.Core.SourceGenerator.Extensions;
public static class StringExtensions
{
public static string ReplaceFirstOccurrence(this string source, string find, string replace)
{
- var regex = new Regex(Regex.Escape(find));
- return regex.Replace(source, replace, 1);
+ var place = source.IndexOf(find, StringComparison.Ordinal);
+
+ return place == -1 ? source : source.Remove(place, find.Length).Insert(place, replace);
}
public static string ReplaceLastOccurrence(this string source, string find, string replace)
diff --git a/TUnit.Core.SourceGenerator/Extensions/TypeExtensions.cs b/TUnit.Core.SourceGenerator/Extensions/TypeExtensions.cs
index 58a62157e6..d4b2266807 100644
--- a/TUnit.Core.SourceGenerator/Extensions/TypeExtensions.cs
+++ b/TUnit.Core.SourceGenerator/Extensions/TypeExtensions.cs
@@ -35,32 +35,53 @@ public static string GetMetadataName(this Type type)
public static IEnumerable GetMembersIncludingBase(this ITypeSymbol namedTypeSymbol, bool reverse = true)
{
- var list = new List();
-
- var symbol = namedTypeSymbol;
-
- while (symbol is not null)
+ if (!reverse)
{
- if (symbol is IErrorTypeSymbol)
+ // Forward traversal - yield directly without allocations
+ var symbol = namedTypeSymbol;
+ while (symbol is not null && symbol.SpecialType != SpecialType.System_Object)
{
- throw new Exception($"ErrorTypeSymbol for {symbol.Name} - Have you added any missing file sources to the compilation?");
+ if (symbol is IErrorTypeSymbol)
+ {
+ throw new Exception($"ErrorTypeSymbol for {symbol.Name} - Have you added any missing file sources to the compilation?");
+ }
+
+ foreach (var member in symbol.GetMembers())
+ {
+ yield return member;
+ }
+
+ symbol = symbol.BaseType;
}
- if (symbol.SpecialType == SpecialType.System_Object)
+ yield break;
+ }
+
+ // Reverse traversal - collect hierarchy, then yield from base to derived
+ // Use stack to collect types (base to derived), then iterate members in forward order
+ var typeStack = new Stack();
+ var current = namedTypeSymbol;
+
+ while (current is not null && current.SpecialType != SpecialType.System_Object)
+ {
+ if (current is IErrorTypeSymbol)
{
- break;
+ throw new Exception($"ErrorTypeSymbol for {current.Name} - Have you added any missing file sources to the compilation?");
}
- list.AddRange(reverse ? symbol.GetMembers().Reverse() : symbol.GetMembers());
- symbol = symbol.BaseType;
+ typeStack.Push(current);
+ current = current.BaseType;
}
- if (reverse)
+ // Yield members from base to derived
+ while (typeStack.Count > 0)
{
- list.Reverse();
+ var type = typeStack.Pop();
+ foreach (var member in type.GetMembers())
+ {
+ yield return member;
+ }
}
-
- return list;
}
public static IEnumerable GetSelfAndBaseTypes(this INamedTypeSymbol namedTypeSymbol)
@@ -109,9 +130,12 @@ public static bool IsIEnumerable(this ITypeSymbol namedTypeSymbol, Compilation c
? [(INamedTypeSymbol) namedTypeSymbol, .. namedTypeSymbol.AllInterfaces]
: namedTypeSymbol.AllInterfaces.AsEnumerable();
+ // Cache the special type lookup to avoid repeated calls
+ var enumerableT = compilation.GetSpecialType(SpecialType.System_Collections_Generic_IEnumerable_T);
+
foreach (var enumerable in interfaces
.Where(x => x.IsGenericType)
- .Where(x => SymbolEqualityComparer.Default.Equals(x.OriginalDefinition, compilation.GetSpecialType(SpecialType.System_Collections_Generic_IEnumerable_T))))
+ .Where(x => SymbolEqualityComparer.Default.Equals(x.OriginalDefinition, enumerableT)))
{
innerType = enumerable.TypeArguments[0];
return true;
diff --git a/TUnit.Core.SourceGenerator/Generators/HookMetadataGenerator.cs b/TUnit.Core.SourceGenerator/Generators/HookMetadataGenerator.cs
index e380fd36ce..c49e6dd639 100644
--- a/TUnit.Core.SourceGenerator/Generators/HookMetadataGenerator.cs
+++ b/TUnit.Core.SourceGenerator/Generators/HookMetadataGenerator.cs
@@ -272,7 +272,7 @@ private static void GenerateHookRegistration(CodeWriter writer, HookMethodMetada
private static void GenerateInstanceHookRegistration(CodeWriter writer, string dictionaryName, string typeDisplay, HookMethodMetadata hook)
{
var hookType = GetConcreteHookType(dictionaryName, true);
- writer.AppendLine($"global::TUnit.Core.Sources.{dictionaryName}.GetOrAdd(typeof({typeDisplay}), _ => new global::System.Collections.Concurrent.ConcurrentBag());");
+ writer.AppendLine($"global::TUnit.Core.Sources.{dictionaryName}.GetOrAdd(typeof({typeDisplay}), static _ => new global::System.Collections.Concurrent.ConcurrentBag());");
writer.AppendLine($"global::TUnit.Core.Sources.{dictionaryName}[typeof({typeDisplay})].Add(");
writer.Indent();
GenerateHookObject(writer, hook, true);
@@ -283,7 +283,7 @@ private static void GenerateInstanceHookRegistration(CodeWriter writer, string d
private static void GenerateTypeHookRegistration(CodeWriter writer, string dictionaryName, string typeDisplay, HookMethodMetadata hook)
{
var hookType = GetConcreteHookType(dictionaryName, false);
- writer.AppendLine($"global::TUnit.Core.Sources.{dictionaryName}.GetOrAdd(typeof({typeDisplay}), _ => new global::System.Collections.Concurrent.ConcurrentBag());");
+ writer.AppendLine($"global::TUnit.Core.Sources.{dictionaryName}.GetOrAdd(typeof({typeDisplay}), static _ => new global::System.Collections.Concurrent.ConcurrentBag());");
writer.AppendLine($"global::TUnit.Core.Sources.{dictionaryName}[typeof({typeDisplay})].Add(");
writer.Indent();
GenerateHookObject(writer, hook, false);
@@ -295,7 +295,7 @@ private static void GenerateAssemblyHookRegistration(CodeWriter writer, string d
{
var assemblyVar = assemblyVarName + "_assembly";
var hookType = GetConcreteHookType(dictionaryName, false);
- writer.AppendLine($"global::TUnit.Core.Sources.{dictionaryName}.GetOrAdd({assemblyVar}, _ => new global::System.Collections.Concurrent.ConcurrentBag());");
+ writer.AppendLine($"global::TUnit.Core.Sources.{dictionaryName}.GetOrAdd({assemblyVar}, static _ => new global::System.Collections.Concurrent.ConcurrentBag());");
writer.AppendLine($"global::TUnit.Core.Sources.{dictionaryName}[{assemblyVar}].Add(");
writer.Indent();
GenerateHookObject(writer, hook, false);
diff --git a/TUnit.Core.SourceGenerator/Generators/PropertyInjectionSourceGenerator.cs b/TUnit.Core.SourceGenerator/Generators/PropertyInjectionSourceGenerator.cs
index 23084bee35..c25c168c84 100644
--- a/TUnit.Core.SourceGenerator/Generators/PropertyInjectionSourceGenerator.cs
+++ b/TUnit.Core.SourceGenerator/Generators/PropertyInjectionSourceGenerator.cs
@@ -457,3 +457,20 @@ internal sealed class PropertyWithDataSourceAttribute
public required IPropertySymbol Property { get; init; }
public required AttributeData DataSourceAttribute { get; init; }
}
+
+internal sealed class ClassWithDataSourcePropertiesComparer : IEqualityComparer
+{
+ public bool Equals(ClassWithDataSourceProperties? x, ClassWithDataSourceProperties? y)
+ {
+ if (ReferenceEquals(x, y)) return true;
+ if (x is null || y is null) return false;
+
+ // Compare based on the class symbol - this handles partial classes correctly
+ return SymbolEqualityComparer.Default.Equals(x.ClassSymbol, y.ClassSymbol);
+ }
+
+ public int GetHashCode(ClassWithDataSourceProperties obj)
+ {
+ return SymbolEqualityComparer.Default.GetHashCode(obj.ClassSymbol);
+ }
+}
diff --git a/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs b/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs
index c620f96855..46bd3dd1ec 100644
--- a/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs
+++ b/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs
@@ -9,6 +9,7 @@
using TUnit.Core.SourceGenerator.CodeGenerators.Helpers;
using TUnit.Core.SourceGenerator.CodeGenerators.Writers;
using TUnit.Core.SourceGenerator.Extensions;
+using TUnit.Core.SourceGenerator.Helpers;
using TUnit.Core.SourceGenerator.Models;
namespace TUnit.Core.SourceGenerator.Generators;
@@ -1273,17 +1274,8 @@ private static void GeneratePropertyDataSourceFactory(CodeWriter writer, IProper
private static bool IsAsyncEnumerable(ITypeSymbol type)
{
- // Check if the type itself is an IAsyncEnumerable
- if (type is INamedTypeSymbol { IsGenericType: true } namedType &&
- namedType.OriginalDefinition.ToDisplayString() == "System.Collections.Generic.IAsyncEnumerable")
- {
- return true;
- }
-
- // Check if the type implements IAsyncEnumerable
- return type.AllInterfaces.Any(i =>
- i.IsGenericType &&
- i.OriginalDefinition.ToDisplayString() == "System.Collections.Generic.IAsyncEnumerable");
+ // Use cached interface check
+ return InterfaceCache.IsAsyncEnumerable(type);
}
private static bool IsTask(ITypeSymbol type)
@@ -1295,14 +1287,8 @@ private static bool IsTask(ITypeSymbol type)
private static bool IsEnumerable(ITypeSymbol type)
{
- if (type.SpecialType == SpecialType.System_String)
- {
- return false;
- }
-
- return type.AllInterfaces.Any(i =>
- i.OriginalDefinition.ToDisplayString() == "System.Collections.IEnumerable" ||
- (i.IsGenericType && i.OriginalDefinition.ToDisplayString() == "System.Collections.Generic.IEnumerable"));
+ // Use cached interface check (already handles string exclusion)
+ return InterfaceCache.IsEnumerable(type);
}
private static void WriteTypedConstant(CodeWriter writer, TypedConstant constant)
@@ -2421,6 +2407,32 @@ private static string GenerateTypeReference(INamedTypeSymbol typeSymbol, bool is
return $"typeof({fullyQualifiedName})";
}
+ private static string BuildTypeKey(IEnumerable types)
+ {
+ var typesList = types as IList ?? types.ToArray();
+ if (typesList.Count == 0)
+ {
+ return string.Empty;
+ }
+
+ var formattedTypes = new string[typesList.Count];
+ for (var i = 0; i < typesList.Count; i++)
+ {
+ formattedTypes[i] = typesList[i].ToDisplayString(DisplayFormats.FullyQualifiedGenericWithoutGlobalPrefix);
+ }
+ return string.Join(",", formattedTypes);
+ }
+
+ ///
+ /// Generates code that resolves the type name at runtime (FullName ?? Name).
+ /// Caches ToDisplayString result to avoid calling it twice.
+ ///
+ private static string FormatTypeForRuntimeName(ITypeSymbol type)
+ {
+ var typeString = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+ return $"(typeof({typeString}).FullName ?? typeof({typeString}).Name)";
+ }
+
private static void GenerateGenericTypeInfo(CodeWriter writer, INamedTypeSymbol typeSymbol)
{
writer.AppendLine("GenericTypeInfo = new global::TUnit.Core.GenericTypeInfo");
@@ -2657,7 +2669,7 @@ private static void GenerateGenericTestWithConcreteTypes(
Array.Copy(classTypes, 0, combinedTypes, 0, classTypes.Length);
Array.Copy(methodTypes, 0, combinedTypes, classTypes.Length, methodTypes.Length);
- var typeKey = string.Join(",", combinedTypes.Select(t => t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat).Replace("global::", "")));
+ var typeKey = BuildTypeKey(combinedTypes);
// Skip if we've already processed this type combination
if (!processedTypeCombinations.Add(typeKey))
@@ -2678,7 +2690,7 @@ private static void GenerateGenericTestWithConcreteTypes(
}
// Generate a concrete instantiation for this type combination
- writer.AppendLine($"[{string.Join(" + \",\" + ", combinedTypes.Select(t => $"(typeof({t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}).FullName ?? typeof({t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}).Name)"))}] = ");
+ writer.AppendLine($"[{string.Join(" + \",\" + ", combinedTypes.Select(FormatTypeForRuntimeName))}] = ");
GenerateConcreteTestMetadata(writer, compilation, testMethod, className, combinedTypes, classAttr);
writer.AppendLine(",");
}
@@ -2715,7 +2727,7 @@ private static void GenerateGenericTestWithConcreteTypes(
if (inferredTypes is { Length: > 0 })
{
- var typeKey = string.Join(",", inferredTypes.Select(t => t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat).Replace("global::", "")));
+ var typeKey = BuildTypeKey(inferredTypes);
// Skip if we've already processed this type combination
if (!processedTypeCombinations.Add(typeKey))
@@ -2742,7 +2754,7 @@ private static void GenerateGenericTestWithConcreteTypes(
}
// Generate a concrete instantiation for this type combination
- writer.AppendLine($"[{string.Join(" + \",\" + ", inferredTypes.Select(t => $"(typeof({t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}).FullName ?? typeof({t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}).Name)"))}] = ");
+ writer.AppendLine($"[{string.Join(" + \",\" + ", inferredTypes.Select(FormatTypeForRuntimeName))}] = ");
GenerateConcreteTestMetadata(writer, compilation, testMethod, className, inferredTypes, argAttr);
writer.AppendLine(",");
}
@@ -2758,7 +2770,7 @@ private static void GenerateGenericTestWithConcreteTypes(
var inferredTypes = InferClassTypesFromMethodArguments(testMethod.TypeSymbol, testMethod.MethodSymbol, methodArgAttr, compilation);
if (inferredTypes is { Length: > 0 })
{
- var typeKey = string.Join(",", inferredTypes.Select(t => t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat).Replace("global::", "")));
+ var typeKey = BuildTypeKey(inferredTypes);
// Skip if we've already processed this type combination
if (!processedTypeCombinations.Add(typeKey))
@@ -2778,7 +2790,7 @@ private static void GenerateGenericTestWithConcreteTypes(
}
// Generate a concrete instantiation for this type combination
- writer.AppendLine($"[{string.Join(" + \",\" + ", inferredTypes.Select(t => $"(typeof({t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}).FullName ?? typeof({t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}).Name)"))}] = ");
+ writer.AppendLine($"[{string.Join(" + \",\" + ", inferredTypes.Select(FormatTypeForRuntimeName))}] = ");
GenerateConcreteTestMetadata(writer, compilation, testMethod, className, inferredTypes, methodArgAttr);
writer.AppendLine(",");
}
@@ -2795,7 +2807,7 @@ private static void GenerateGenericTestWithConcreteTypes(
var inferredTypes = InferTypesFromDataSourceAttribute(testMethod.MethodSymbol, dataSourceAttr);
if (inferredTypes is { Length: > 0 })
{
- var typeKey = string.Join(",", inferredTypes.Select(t => t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat).Replace("global::", "")));
+ var typeKey = BuildTypeKey(inferredTypes);
// Skip if we've already processed this type combination
if (!processedTypeCombinations.Add(typeKey))
@@ -2822,7 +2834,7 @@ private static void GenerateGenericTestWithConcreteTypes(
}
// Generate a concrete instantiation for this type combination
- writer.AppendLine($"[{string.Join(" + \",\" + ", inferredTypes.Select(t => $"(typeof({t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}).FullName ?? typeof({t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}).Name)"))}] = ");
+ writer.AppendLine($"[{string.Join(" + \",\" + ", inferredTypes.Select(FormatTypeForRuntimeName))}] = ");
GenerateConcreteTestMetadata(writer, compilation, testMethod, className, inferredTypes, dataSourceAttr);
writer.AppendLine(",");
}
@@ -2834,7 +2846,7 @@ private static void GenerateGenericTestWithConcreteTypes(
var inferredTypes = InferTypesFromTypeInferringAttributes(testMethod.MethodSymbol);
if (inferredTypes is { Length: > 0 })
{
- var typeKey = string.Join(",", inferredTypes.Select(t => t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat).Replace("global::", "")));
+ var typeKey = BuildTypeKey(inferredTypes);
// Skip if we've already processed this type combination
if (processedTypeCombinations.Add(typeKey))
@@ -2843,7 +2855,7 @@ private static void GenerateGenericTestWithConcreteTypes(
if (ValidateTypeConstraints(testMethod.MethodSymbol, inferredTypes))
{
// Generate a concrete instantiation for this type combination
- writer.AppendLine($"[{string.Join(" + \",\" + ", inferredTypes.Select(t => $"(typeof({t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}).FullName ?? typeof({t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}).Name)"))}] = ");
+ writer.AppendLine($"[{string.Join(" + \",\" + ", inferredTypes.Select(FormatTypeForRuntimeName))}] = ");
GenerateConcreteTestMetadata(writer, compilation, testMethod, className, inferredTypes);
writer.AppendLine(",");
}
@@ -2864,7 +2876,7 @@ private static void GenerateGenericTestWithConcreteTypes(
var inferredTypes = InferClassTypesFromMethodDataSource(compilation, testMethod, mdsAttr);
if (inferredTypes is { Length: > 0 })
{
- var typeKey = string.Join(",", inferredTypes.Select(t => t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat).Replace("global::", "")));
+ var typeKey = BuildTypeKey(inferredTypes);
// Skip if we've already processed this type combination
if (processedTypeCombinations.Add(typeKey))
@@ -2873,7 +2885,7 @@ private static void GenerateGenericTestWithConcreteTypes(
if (ValidateClassTypeConstraints(testMethod.TypeSymbol, inferredTypes))
{
// Generate a concrete instantiation for this type combination
- writer.AppendLine($"[{string.Join(" + \",\" + ", inferredTypes.Select(t => $"(typeof({t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}).FullName ?? typeof({t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}).Name)"))}] = ");
+ writer.AppendLine($"[{string.Join(" + \",\" + ", inferredTypes.Select(FormatTypeForRuntimeName))}] = ");
GenerateConcreteTestMetadata(writer, compilation, testMethod, className, inferredTypes);
writer.AppendLine(",");
}
@@ -2888,7 +2900,7 @@ private static void GenerateGenericTestWithConcreteTypes(
var typedDataSourceInferredTypes = InferTypesFromTypedDataSourceForClass(testMethod.TypeSymbol, testMethod.MethodSymbol);
if (typedDataSourceInferredTypes is { Length: > 0 })
{
- var typeKey = string.Join(",", typedDataSourceInferredTypes.Select(t => t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat).Replace("global::", "")));
+ var typeKey = BuildTypeKey(typedDataSourceInferredTypes);
// Skip if we've already processed this type combination
if (processedTypeCombinations.Add(typeKey))
@@ -2897,7 +2909,7 @@ private static void GenerateGenericTestWithConcreteTypes(
if (ValidateClassTypeConstraints(testMethod.TypeSymbol, typedDataSourceInferredTypes))
{
// Generate a concrete instantiation for this type combination
- writer.AppendLine($"[{string.Join(" + \",\" + ", typedDataSourceInferredTypes.Select(t => $"(typeof({t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}).FullName ?? typeof({t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}).Name)"))}] = ");
+ writer.AppendLine($"[{string.Join(" + \",\" + ", typedDataSourceInferredTypes.Select(FormatTypeForRuntimeName))}] = ");
GenerateConcreteTestMetadata(writer, compilation, testMethod, className, typedDataSourceInferredTypes);
writer.AppendLine(",");
}
@@ -2918,7 +2930,7 @@ private static void GenerateGenericTestWithConcreteTypes(
var inferredTypes = InferTypesFromMethodDataSource(compilation, testMethod, mdsAttr);
if (inferredTypes is { Length: > 0 })
{
- var typeKey = string.Join(",", inferredTypes.Select(t => t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat).Replace("global::", "")));
+ var typeKey = BuildTypeKey(inferredTypes);
// Skip if we've already processed this type combination
if (processedTypeCombinations.Add(typeKey))
@@ -2927,7 +2939,7 @@ private static void GenerateGenericTestWithConcreteTypes(
if (ValidateTypeConstraints(testMethod.MethodSymbol, inferredTypes))
{
// Generate a concrete instantiation for this type combination
- writer.AppendLine($"[{string.Join(" + \",\" + ", inferredTypes.Select(t => $"(typeof({t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}).FullName ?? typeof({t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}).Name)"))}] = ");
+ writer.AppendLine($"[{string.Join(" + \",\" + ", inferredTypes.Select(FormatTypeForRuntimeName))}] = ");
GenerateConcreteTestMetadata(writer, compilation, testMethod, className, inferredTypes);
writer.AppendLine(",");
}
@@ -2965,7 +2977,7 @@ private static void GenerateGenericTestWithConcreteTypes(
{
// Combine class types and method types
var combinedTypes = classInferredTypes.Concat(methodInferredTypes).ToArray();
- var typeKey = string.Join(",", combinedTypes.Select(t => t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat).Replace("global::", "")));
+ var typeKey = BuildTypeKey(combinedTypes);
// Skip if we've already processed this type combination
if (processedTypeCombinations.Add(typeKey))
@@ -2975,7 +2987,7 @@ private static void GenerateGenericTestWithConcreteTypes(
ValidateTypeConstraints(testMethod.MethodSymbol, methodInferredTypes))
{
// Generate a concrete instantiation for this type combination
- writer.AppendLine($"[{string.Join(" + \",\" + ", combinedTypes.Select(t => $"(typeof({t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}).FullName ?? typeof({t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}).Name)"))}] = ");
+ writer.AppendLine($"[{string.Join(" + \",\" + ", combinedTypes.Select(FormatTypeForRuntimeName))}] = ");
GenerateConcreteTestMetadata(writer, compilation, testMethod, className, combinedTypes, argAttr);
writer.AppendLine(",");
}
@@ -2986,7 +2998,7 @@ private static void GenerateGenericTestWithConcreteTypes(
else
{
// For non-generic methods, just use class types
- var typeKey = string.Join(",", classInferredTypes.Select(t => t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat).Replace("global::", "")));
+ var typeKey = BuildTypeKey(classInferredTypes);
// Skip if we've already processed this type combination
if (processedTypeCombinations.Add(typeKey))
@@ -2995,7 +3007,7 @@ private static void GenerateGenericTestWithConcreteTypes(
if (ValidateClassTypeConstraints(testMethod.TypeSymbol, classInferredTypes))
{
// Generate a concrete instantiation for this type combination
- writer.AppendLine($"[{string.Join(" + \",\" + ", classInferredTypes.Select(t => $"(typeof({t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}).FullName ?? typeof({t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}).Name)"))}] = ");
+ writer.AppendLine($"[{string.Join(" + \",\" + ", classInferredTypes.Select(FormatTypeForRuntimeName))}] = ");
GenerateConcreteTestMetadata(writer, compilation, testMethod, className, classInferredTypes, argAttr);
writer.AppendLine(",");
}
@@ -3111,7 +3123,7 @@ private static void GenerateGenericTestWithConcreteTypes(
if (typeArgs.Count > 0)
{
var inferredTypes = typeArgs.ToArray();
- var typeKey = string.Join(",", inferredTypes.Select(t => t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat).Replace("global::", "")));
+ var typeKey = BuildTypeKey(inferredTypes);
// Skip if we've already processed this type combination
if (processedTypeCombinations.Add(typeKey))
@@ -3121,7 +3133,7 @@ private static void GenerateGenericTestWithConcreteTypes(
{
// Generate a concrete instantiation for this type combination
// Use the same key format as runtime: FullName ?? Name
- writer.AppendLine($"[{string.Join(" + \",\" + ", inferredTypes.Select(t => $"(typeof({t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}).FullName ?? typeof({t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}).Name)"))}] = ");
+ writer.AppendLine($"[{string.Join(" + \",\" + ", inferredTypes.Select(FormatTypeForRuntimeName))}] = ");
GenerateConcreteTestMetadata(writer, compilation, testMethod, className, inferredTypes);
writer.AppendLine(",");
}
diff --git a/TUnit.Core.SourceGenerator/Helpers/InterfaceCache.cs b/TUnit.Core.SourceGenerator/Helpers/InterfaceCache.cs
new file mode 100644
index 0000000000..0064250efa
--- /dev/null
+++ b/TUnit.Core.SourceGenerator/Helpers/InterfaceCache.cs
@@ -0,0 +1,99 @@
+using System.Collections.Concurrent;
+using Microsoft.CodeAnalysis;
+using TUnit.Core.SourceGenerator.Extensions;
+
+namespace TUnit.Core.SourceGenerator.Helpers;
+
+///
+/// Caches interface implementation checks to avoid repeated AllInterfaces traversals
+///
+internal static class InterfaceCache
+{
+ private static readonly ConcurrentDictionary<(ITypeSymbol Type, string InterfaceName), bool> _implementsCache = new(TypeStringTupleComparer.Default);
+ private static readonly ConcurrentDictionary<(ITypeSymbol Type, string GenericInterfacePattern), INamedTypeSymbol?> _genericInterfaceCache = new(TypeStringTupleComparer.Default);
+
+ ///
+ /// Checks if a type implements a specific interface
+ ///
+ public static bool ImplementsInterface(ITypeSymbol type, string fullyQualifiedInterfaceName)
+ {
+ return _implementsCache.GetOrAdd((type, fullyQualifiedInterfaceName), key =>
+ key.Type.AllInterfaces.Any(i => i.GloballyQualified() == key.InterfaceName));
+ }
+
+ ///
+ /// Checks if a type implements a generic interface and returns the matching interface symbol
+ ///
+ public static INamedTypeSymbol? GetGenericInterface(ITypeSymbol type, string fullyQualifiedGenericPattern)
+ {
+ return _genericInterfaceCache.GetOrAdd((type, fullyQualifiedGenericPattern), key =>
+ key.Type.AllInterfaces.FirstOrDefault(i =>
+ i.IsGenericType &&
+ i.ConstructedFrom.GloballyQualified() == key.GenericInterfacePattern));
+ }
+
+ ///
+ /// Checks if a type implements a generic interface
+ ///
+ public static bool ImplementsGenericInterface(ITypeSymbol type, string fullyQualifiedGenericPattern)
+ {
+ return GetGenericInterface(type, fullyQualifiedGenericPattern) != null;
+ }
+
+ ///
+ /// Checks if a type implements IAsyncEnumerable<T>
+ ///
+ public static bool IsAsyncEnumerable(ITypeSymbol type)
+ {
+ return _implementsCache.GetOrAdd((type, "System.Collections.Generic.IAsyncEnumerable"), key =>
+ {
+ // Check if the type itself is an IAsyncEnumerable
+ if (key.Type is INamedTypeSymbol { IsGenericType: true } namedType &&
+ namedType.OriginalDefinition.ToDisplayString() == "System.Collections.Generic.IAsyncEnumerable")
+ {
+ return true;
+ }
+
+ // Check if the type implements IAsyncEnumerable
+ return key.Type.AllInterfaces.Any(i =>
+ i.IsGenericType &&
+ i.OriginalDefinition.ToDisplayString() == "System.Collections.Generic.IAsyncEnumerable");
+ });
+ }
+
+ ///
+ /// Checks if a type implements IEnumerable (excluding string)
+ ///
+ public static bool IsEnumerable(ITypeSymbol type)
+ {
+ if (type.SpecialType == SpecialType.System_String)
+ {
+ return false;
+ }
+
+ return _implementsCache.GetOrAdd((type, "System.Collections.IEnumerable"), key =>
+ key.Type.AllInterfaces.Any(i =>
+ i.OriginalDefinition.ToDisplayString() == "System.Collections.IEnumerable" ||
+ (i.IsGenericType && i.OriginalDefinition.ToDisplayString() == "System.Collections.Generic.IEnumerable")));
+ }
+}
+
+internal sealed class TypeStringTupleComparer : IEqualityComparer<(ITypeSymbol Type, string Name)>
+{
+ public static readonly TypeStringTupleComparer Default = new();
+
+ private TypeStringTupleComparer() { }
+
+ public bool Equals((ITypeSymbol Type, string Name) x, (ITypeSymbol Type, string Name) y)
+ {
+ return Microsoft.CodeAnalysis.SymbolEqualityComparer.Default.Equals(x.Type, y.Type) && x.Name == y.Name;
+ }
+
+ public int GetHashCode((ITypeSymbol Type, string Name) obj)
+ {
+ unchecked
+ {
+ return (Microsoft.CodeAnalysis.SymbolEqualityComparer.Default.GetHashCode(obj.Type) * 397) ^ obj.Name.GetHashCode();
+ }
+ }
+}
diff --git a/TUnit.Core.SourceGenerator/Models/TypeWithDataSourceProperties.cs b/TUnit.Core.SourceGenerator/Models/TypeWithDataSourceProperties.cs
index b95834a9f4..9b52e5e691 100644
--- a/TUnit.Core.SourceGenerator/Models/TypeWithDataSourceProperties.cs
+++ b/TUnit.Core.SourceGenerator/Models/TypeWithDataSourceProperties.cs
@@ -7,3 +7,17 @@ public struct TypeWithDataSourceProperties
public INamedTypeSymbol TypeSymbol { get; init; }
public List Properties { get; init; }
}
+
+public sealed class TypeWithDataSourcePropertiesComparer : IEqualityComparer
+{
+ public bool Equals(TypeWithDataSourceProperties x, TypeWithDataSourceProperties y)
+ {
+ // Compare based on the type symbol - this handles partial classes correctly
+ return SymbolEqualityComparer.Default.Equals(x.TypeSymbol, y.TypeSymbol);
+ }
+
+ public int GetHashCode(TypeWithDataSourceProperties obj)
+ {
+ return SymbolEqualityComparer.Default.GetHashCode(obj.TypeSymbol);
+ }
+}
diff --git a/TUnit.Core/AotCompatibility/GenericTestRegistry.cs b/TUnit.Core/AotCompatibility/GenericTestRegistry.cs
index 0afcdd7588..3e5a8f1b74 100644
--- a/TUnit.Core/AotCompatibility/GenericTestRegistry.cs
+++ b/TUnit.Core/AotCompatibility/GenericTestRegistry.cs
@@ -23,7 +23,7 @@ public static void RegisterGenericMethod(Type declaringType, string methodName,
_compiledMethods[key] = compiledMethod;
// Track registered combinations
- var combinations = _registeredCombinations.GetOrAdd(declaringType, _ => new HashSet(new TypeArrayComparer()));
+ var combinations = _registeredCombinations.GetOrAdd(declaringType, static _ => new HashSet(new TypeArrayComparer()));
combinations.Add(typeArguments);
}
diff --git a/TUnit.Core/Attributes/TestData/ClassDataSources.cs b/TUnit.Core/Attributes/TestData/ClassDataSources.cs
index 24536b466a..f4bcc3dcb2 100644
--- a/TUnit.Core/Attributes/TestData/ClassDataSources.cs
+++ b/TUnit.Core/Attributes/TestData/ClassDataSources.cs
@@ -15,7 +15,7 @@ private ClassDataSources()
public static ClassDataSources Get(string sessionId)
{
- return SourcesPerSession.GetOrAdd(sessionId, _ => new ClassDataSources());
+ return SourcesPerSession.GetOrAdd(sessionId, static _ => new ClassDataSources());
}
public (T, SharedType, string) GetItemForIndexAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] T>(int index, Type testClassType, SharedType[] sharedTypes, string[] keys, DataGeneratorMetadata dataGeneratorMetadata) where T : new()
diff --git a/TUnit.Core/Attributes/TestData/MethodDataSourceAttribute.cs b/TUnit.Core/Attributes/TestData/MethodDataSourceAttribute.cs
index 7eda317f4f..6c41636817 100644
--- a/TUnit.Core/Attributes/TestData/MethodDataSourceAttribute.cs
+++ b/TUnit.Core/Attributes/TestData/MethodDataSourceAttribute.cs
@@ -157,11 +157,11 @@ public MethodDataSourceAttribute(
hasAnyItems = true;
yield return async () =>
{
- var paramTypes = dataGeneratorMetadata.TestInformation?.Parameters.Select(p => p.Type).ToArray();
+ var paramTypes = dataGeneratorMetadata.TestInformation?.Parameters.Select(static p => p.Type).ToArray();
return await Task.FromResult