Skip to content

Commit c18ed00

Browse files
authored
Experimental: Dynamically Register Tests (#2185)
* DynamicTest.cs * GetDynamicTests * Source generator * Working * Mark experimental * Docs * Update desc * Update snaps * Fix test * Update snaps
1 parent f0aba1d commit c18ed00

34 files changed

Lines changed: 1312 additions & 33 deletions

TUnit.Core.SourceGenerator.Roslyn47/TUnit.Core.SourceGenerator.Roslyn47.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@
1515

1616
<ItemGroup>
1717
<Compile Include="..\TUnit.Core.SourceGenerator\**\*.cs" Exclude="..\TUnit.Core.SourceGenerator\obj\**\*.cs" />
18+
<Compile Update="..\TUnit.Core.SourceGenerator\CodeGenerators\Helpers\DynamicTestSourceDataModelRetriever.cs">
19+
<Link>CodeGenerators\Helpers\DynamicTestSourceDataModelRetriever.cs</Link>
20+
</Compile>
21+
<Compile Update="..\TUnit.Core.SourceGenerator\Models\DynamicTestSourceDataModel.cs">
22+
<Link>Models\DynamicTestSourceDataModel.cs</Link>
23+
</Compile>
1824
</ItemGroup>
1925

2026
</Project>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
[
2+
// <auto-generated/>
3+
#pragma warning disable
4+
using global::System.Linq;
5+
using global::System.Reflection;
6+
using global::TUnit.Core;
7+
using global::TUnit.Core.Extensions;
8+
9+
namespace TUnit.SourceGenerated;
10+
11+
[global::System.Diagnostics.StackTraceHidden]
12+
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
13+
file partial class Basic : global::TUnit.Core.Interfaces.SourceGenerator.IDynamicTestSource
14+
{
15+
[global::System.Runtime.CompilerServices.ModuleInitializer]
16+
public static void Initialise()
17+
{
18+
global::TUnit.Core.SourceRegistrar.RegisterDynamic(new Basic());
19+
}
20+
public global::System.Collections.Generic.IReadOnlyList<DynamicTest> CollectDynamicTests(string sessionId)
21+
{
22+
try
23+
{
24+
var context = new global::TUnit.Core.DynamicTestBuilderContext(@"", 40);
25+
new global::TUnit.TestProject.DynamicTests.Basic().BuildTests(context);
26+
return context.Tests;
27+
}
28+
catch (global::System.Exception exception)
29+
{
30+
return
31+
[
32+
new FailedDynamicTest<global::TUnit.TestProject.DynamicTests.Basic>
33+
{
34+
TestId = @"global::TUnit.TestProject.DynamicTests.BasicBuildTests",
35+
MethodName = $"BuildTests",
36+
Exception = new TUnit.Core.Exceptions.TestFailedInitializationException("Basic.BuildTests failed to initialize", exception),
37+
TestFilePath = @"",
38+
TestLineNumber = 40,
39+
}
40+
];
41+
}
42+
}
43+
}
44+
45+
]
Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
[
2+
// <auto-generated/>
3+
#pragma warning disable
4+
using global::System.Linq;
5+
using global::System.Reflection;
6+
using global::TUnit.Core;
7+
using global::TUnit.Core.Extensions;
8+
9+
namespace TUnit.SourceGenerated;
10+
11+
[global::System.Diagnostics.StackTraceHidden]
12+
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
13+
file partial class BasicTests : global::TUnit.Core.Interfaces.SourceGenerator.ITestSource
14+
{
15+
[global::System.Runtime.CompilerServices.ModuleInitializer]
16+
public static void Initialise()
17+
{
18+
global::TUnit.Core.SourceRegistrar.Register(new BasicTests());
19+
}
20+
public global::System.Collections.Generic.IReadOnlyList<TestMetadata> CollectTests(string sessionId)
21+
{
22+
return Tests0(sessionId);
23+
}
24+
private global::System.Collections.Generic.List<TestMetadata> Tests0(string sessionId)
25+
{
26+
global::System.Collections.Generic.List<TestMetadata> nodes = [];
27+
var classDataIndex = 0;
28+
var testMethodDataIndex = 0;
29+
try
30+
{
31+
var testInformation = new global::TUnit.Core.SourceGeneratedMethodInformation
32+
{
33+
Type = typeof(global::TUnit.TestProject.BasicTests),
34+
Name = "SynchronousTest",
35+
GenericTypeCount = 0,
36+
ReturnType = typeof(void),
37+
Attributes =
38+
[
39+
new global::TUnit.Core.TestAttribute()
40+
],
41+
Parameters = [],
42+
Class = global::TUnit.Core.SourceGeneratedClassInformation.GetOrAdd("global::TUnit.TestProject.BasicTests", () => new global::TUnit.Core.SourceGeneratedClassInformation
43+
{
44+
Type = typeof(global::TUnit.TestProject.BasicTests),
45+
Assembly = global::TUnit.Core.SourceGeneratedAssemblyInformation.GetOrAdd("BasicTests", () => new global::TUnit.Core.SourceGeneratedAssemblyInformation
46+
{
47+
Name = "BasicTests",
48+
Attributes = [],
49+
}),
50+
Name = "BasicTests",
51+
Namespace = "TUnit.TestProject",
52+
Attributes = [],
53+
Parameters = [],
54+
Properties = [],
55+
}),
56+
};
57+
58+
var testBuilderContext = new global::TUnit.Core.TestBuilderContext();
59+
var testBuilderContextAccessor = new global::TUnit.Core.TestBuilderContextAccessor(testBuilderContext);
60+
global::TUnit.TestProject.BasicTests? classInstance = null;
61+
object?[]? classInstanceArguments = null;
62+
classInstanceArguments = [];
63+
64+
var resettableClassFactoryDelegate = () => new ResettableLazy<global::TUnit.TestProject.BasicTests>(() =>
65+
new global::TUnit.TestProject.BasicTests()
66+
, sessionId, testBuilderContext);
67+
var resettableClassFactory = resettableClassFactoryDelegate();
68+
69+
70+
nodes.Add(new TestMetadata<global::TUnit.TestProject.BasicTests>
71+
{
72+
TestId = $"TUnit.TestProject.BasicTests.SynchronousTest:0",
73+
TestClassArguments = [],
74+
TestMethodArguments = [],
75+
TestClassProperties = [],
76+
CurrentRepeatAttempt = 0,
77+
RepeatLimit = 0,
78+
ResettableClassFactory = resettableClassFactory,
79+
TestMethodFactory = (classInstance, cancellationToken) => AsyncConvert.Convert(() => classInstance.SynchronousTest()),
80+
TestFilePath = @"",
81+
TestLineNumber = 5,
82+
TestMethod = testInformation,
83+
TestBuilderContext = testBuilderContext,
84+
});
85+
resettableClassFactory = resettableClassFactoryDelegate();
86+
testBuilderContext = new();
87+
testBuilderContextAccessor.Current = testBuilderContext;
88+
}
89+
catch (global::System.Exception exception)
90+
{
91+
nodes.Add(new FailedTestMetadata<global::TUnit.TestProject.BasicTests>
92+
{
93+
TestId = $"TUnit.TestProject.BasicTests.SynchronousTest:0",
94+
MethodName = $"SynchronousTest",
95+
Exception = new TUnit.Core.Exceptions.TestFailedInitializationException("BasicTests.SynchronousTest failed to initialize", exception),
96+
TestFilePath = @"",
97+
TestLineNumber = 5,
98+
});
99+
}
100+
return nodes;
101+
}
102+
}
103+
104+
105+
// <auto-generated/>
106+
#pragma warning disable
107+
using global::System.Linq;
108+
using global::System.Reflection;
109+
using global::TUnit.Core;
110+
using global::TUnit.Core.Extensions;
111+
112+
namespace TUnit.SourceGenerated;
113+
114+
[global::System.Diagnostics.StackTraceHidden]
115+
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
116+
file partial class BasicTests : global::TUnit.Core.Interfaces.SourceGenerator.ITestSource
117+
{
118+
[global::System.Runtime.CompilerServices.ModuleInitializer]
119+
public static void Initialise()
120+
{
121+
global::TUnit.Core.SourceRegistrar.Register(new BasicTests());
122+
}
123+
public global::System.Collections.Generic.IReadOnlyList<TestMetadata> CollectTests(string sessionId)
124+
{
125+
return Tests0(sessionId);
126+
}
127+
private global::System.Collections.Generic.List<TestMetadata> Tests0(string sessionId)
128+
{
129+
global::System.Collections.Generic.List<TestMetadata> nodes = [];
130+
var classDataIndex = 0;
131+
var testMethodDataIndex = 0;
132+
try
133+
{
134+
var testInformation = new global::TUnit.Core.SourceGeneratedMethodInformation
135+
{
136+
Type = typeof(global::TUnit.TestProject.BasicTests),
137+
Name = "AsynchronousTest",
138+
GenericTypeCount = 0,
139+
ReturnType = typeof(global::System.Threading.Tasks.Task),
140+
Attributes =
141+
[
142+
new global::TUnit.Core.TestAttribute()
143+
],
144+
Parameters = [],
145+
Class = global::TUnit.Core.SourceGeneratedClassInformation.GetOrAdd("global::TUnit.TestProject.BasicTests", () => new global::TUnit.Core.SourceGeneratedClassInformation
146+
{
147+
Type = typeof(global::TUnit.TestProject.BasicTests),
148+
Assembly = global::TUnit.Core.SourceGeneratedAssemblyInformation.GetOrAdd("BasicTests", () => new global::TUnit.Core.SourceGeneratedAssemblyInformation
149+
{
150+
Name = "BasicTests",
151+
Attributes = [],
152+
}),
153+
Name = "BasicTests",
154+
Namespace = "TUnit.TestProject",
155+
Attributes = [],
156+
Parameters = [],
157+
Properties = [],
158+
}),
159+
};
160+
161+
var testBuilderContext = new global::TUnit.Core.TestBuilderContext();
162+
var testBuilderContextAccessor = new global::TUnit.Core.TestBuilderContextAccessor(testBuilderContext);
163+
global::TUnit.TestProject.BasicTests? classInstance = null;
164+
object?[]? classInstanceArguments = null;
165+
classInstanceArguments = [];
166+
167+
var resettableClassFactoryDelegate = () => new ResettableLazy<global::TUnit.TestProject.BasicTests>(() =>
168+
new global::TUnit.TestProject.BasicTests()
169+
, sessionId, testBuilderContext);
170+
var resettableClassFactory = resettableClassFactoryDelegate();
171+
172+
173+
nodes.Add(new TestMetadata<global::TUnit.TestProject.BasicTests>
174+
{
175+
TestId = $"TUnit.TestProject.BasicTests.AsynchronousTest:0",
176+
TestClassArguments = [],
177+
TestMethodArguments = [],
178+
TestClassProperties = [],
179+
CurrentRepeatAttempt = 0,
180+
RepeatLimit = 0,
181+
ResettableClassFactory = resettableClassFactory,
182+
TestMethodFactory = (classInstance, cancellationToken) => AsyncConvert.Convert(() => classInstance.AsynchronousTest()),
183+
TestFilePath = @"",
184+
TestLineNumber = 11,
185+
TestMethod = testInformation,
186+
TestBuilderContext = testBuilderContext,
187+
});
188+
resettableClassFactory = resettableClassFactoryDelegate();
189+
testBuilderContext = new();
190+
testBuilderContextAccessor.Current = testBuilderContext;
191+
}
192+
catch (global::System.Exception exception)
193+
{
194+
nodes.Add(new FailedTestMetadata<global::TUnit.TestProject.BasicTests>
195+
{
196+
TestId = $"TUnit.TestProject.BasicTests.AsynchronousTest:0",
197+
MethodName = $"AsynchronousTest",
198+
Exception = new TUnit.Core.Exceptions.TestFailedInitializationException("BasicTests.AsynchronousTest failed to initialize", exception),
199+
TestFilePath = @"",
200+
TestLineNumber = 11,
201+
});
202+
}
203+
return nodes;
204+
}
205+
}
206+
207+
208+
// <auto-generated/>
209+
#pragma warning disable
210+
using global::System.Linq;
211+
using global::System.Reflection;
212+
using global::TUnit.Core;
213+
using global::TUnit.Core.Extensions;
214+
215+
namespace TUnit.SourceGenerated;
216+
217+
[global::System.Diagnostics.StackTraceHidden]
218+
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
219+
file partial class BasicTests : global::TUnit.Core.Interfaces.SourceGenerator.ITestSource
220+
{
221+
[global::System.Runtime.CompilerServices.ModuleInitializer]
222+
public static void Initialise()
223+
{
224+
global::TUnit.Core.SourceRegistrar.Register(new BasicTests());
225+
}
226+
public global::System.Collections.Generic.IReadOnlyList<TestMetadata> CollectTests(string sessionId)
227+
{
228+
return Tests0(sessionId);
229+
}
230+
private global::System.Collections.Generic.List<TestMetadata> Tests0(string sessionId)
231+
{
232+
global::System.Collections.Generic.List<TestMetadata> nodes = [];
233+
var classDataIndex = 0;
234+
var testMethodDataIndex = 0;
235+
try
236+
{
237+
var testInformation = new global::TUnit.Core.SourceGeneratedMethodInformation
238+
{
239+
Type = typeof(global::TUnit.TestProject.BasicTests),
240+
Name = "ValueTaskAsynchronousTest",
241+
GenericTypeCount = 0,
242+
ReturnType = typeof(global::System.Threading.Tasks.ValueTask),
243+
Attributes =
244+
[
245+
new global::TUnit.Core.TestAttribute()
246+
],
247+
Parameters = [],
248+
Class = global::TUnit.Core.SourceGeneratedClassInformation.GetOrAdd("global::TUnit.TestProject.BasicTests", () => new global::TUnit.Core.SourceGeneratedClassInformation
249+
{
250+
Type = typeof(global::TUnit.TestProject.BasicTests),
251+
Assembly = global::TUnit.Core.SourceGeneratedAssemblyInformation.GetOrAdd("BasicTests", () => new global::TUnit.Core.SourceGeneratedAssemblyInformation
252+
{
253+
Name = "BasicTests",
254+
Attributes = [],
255+
}),
256+
Name = "BasicTests",
257+
Namespace = "TUnit.TestProject",
258+
Attributes = [],
259+
Parameters = [],
260+
Properties = [],
261+
}),
262+
};
263+
264+
var testBuilderContext = new global::TUnit.Core.TestBuilderContext();
265+
var testBuilderContextAccessor = new global::TUnit.Core.TestBuilderContextAccessor(testBuilderContext);
266+
global::TUnit.TestProject.BasicTests? classInstance = null;
267+
object?[]? classInstanceArguments = null;
268+
classInstanceArguments = [];
269+
270+
var resettableClassFactoryDelegate = () => new ResettableLazy<global::TUnit.TestProject.BasicTests>(() =>
271+
new global::TUnit.TestProject.BasicTests()
272+
, sessionId, testBuilderContext);
273+
var resettableClassFactory = resettableClassFactoryDelegate();
274+
275+
276+
nodes.Add(new TestMetadata<global::TUnit.TestProject.BasicTests>
277+
{
278+
TestId = $"TUnit.TestProject.BasicTests.ValueTaskAsynchronousTest:0",
279+
TestClassArguments = [],
280+
TestMethodArguments = [],
281+
TestClassProperties = [],
282+
CurrentRepeatAttempt = 0,
283+
RepeatLimit = 0,
284+
ResettableClassFactory = resettableClassFactory,
285+
TestMethodFactory = (classInstance, cancellationToken) => AsyncConvert.Convert(() => classInstance.ValueTaskAsynchronousTest()),
286+
TestFilePath = @"",
287+
TestLineNumber = 17,
288+
TestMethod = testInformation,
289+
TestBuilderContext = testBuilderContext,
290+
});
291+
resettableClassFactory = resettableClassFactoryDelegate();
292+
testBuilderContext = new();
293+
testBuilderContextAccessor.Current = testBuilderContext;
294+
}
295+
catch (global::System.Exception exception)
296+
{
297+
nodes.Add(new FailedTestMetadata<global::TUnit.TestProject.BasicTests>
298+
{
299+
TestId = $"TUnit.TestProject.BasicTests.ValueTaskAsynchronousTest:0",
300+
MethodName = $"ValueTaskAsynchronousTest",
301+
Exception = new TUnit.Core.Exceptions.TestFailedInitializationException("BasicTests.ValueTaskAsynchronousTest failed to initialize", exception),
302+
TestFilePath = @"",
303+
TestLineNumber = 17,
304+
});
305+
}
306+
return nodes;
307+
}
308+
}
309+
310+
]
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using TUnit.Core.SourceGenerator.CodeGenerators;
2+
3+
namespace TUnit.Core.SourceGenerator.Tests.Dynamic;
4+
5+
internal class Basic : TestsBase<DynamicTestsGenerator>
6+
{
7+
[Test]
8+
public Task Test() => RunTest(Path.Combine(Git.RootDirectory.FullName,
9+
"TUnit.TestProject",
10+
"DynamicTests",
11+
"Basic.cs"),
12+
async generatedFiles =>
13+
{
14+
await Assert.That(generatedFiles.Length).IsEqualTo(1);
15+
});
16+
}

0 commit comments

Comments
 (0)