Skip to content

Commit 977c757

Browse files
majochapsfinakiT-Gro
authored
xUnit tests - improve assembly fixture (#18381)
* xUnit - move custom initialization from test framework constructor to executor * Update tests/FSharp.Test.Utilities/XunitHelpers.fs Co-authored-by: Tomas Grosup <[email protected]> --------- Co-authored-by: Petr <[email protected]> Co-authored-by: Tomas Grosup <[email protected]>
1 parent 4dc458c commit 977c757

File tree

1 file changed

+39
-52
lines changed

1 file changed

+39
-52
lines changed

tests/FSharp.Test.Utilities/XunitHelpers.fs

Lines changed: 39 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -21,29 +21,7 @@ open OpenTelemetry.Trace
2121
[<AttributeUsage(AttributeTargets.Class ||| AttributeTargets.Method, AllowMultiple = false)>]
2222
type RunTestCasesInSequenceAttribute() = inherit Attribute()
2323

24-
#if !XUNIT_EXTRAS
25-
/// Installs console support for parallel test runs and conditionally enables optional xUnit customizations.
26-
type FSharpXunitFramework(sink: IMessageSink) =
27-
inherit XunitTestFramework(sink)
28-
do
29-
// Because xUnit v2 lacks assembly fixture, the next best place to ensure things get called
30-
// right at the start of the test run is here in the constructor.
31-
// This gets executed once per test assembly.
32-
logConfig initialConfig
33-
log "FSharpXunitFramework installing TestConsole redirection"
34-
TestConsole.install()
35-
#if !NETCOREAPP
36-
AssemblyResolver.addResolver ()
37-
#endif
38-
39-
interface IDisposable with
40-
member _.Dispose() =
41-
match Environment.GetEnvironmentVariable("FSHARP_RETAIN_TESTBUILDS") with
42-
| null -> cleanUpTemporaryDirectoryOfThisTestRun ()
43-
| _ -> ()
44-
base.Dispose()
45-
46-
#else
24+
#if XUNIT_EXTRAS
4725

4826
// To use xUnit means to customize it. The following abomination adds 2 features:
4927
// - Capturing full console output individually for each test case, viewable in Test Explorer as test stdout.
@@ -57,7 +35,7 @@ type ConsoleCapturingTestRunner(test, messageBus, testClass, constructorArgument
5735
override this.InvokeTestAsync (aggregator: ExceptionAggregator) =
5836
task {
5937
use capture = new TestConsole.ExecutionCapture()
60-
use _ = Activity.start test.DisplayName [ ]
38+
use _ = Activity.startNoTags test.DisplayName
6139
let! executionTime = this.BaseInvokeTestMethodAsync aggregator
6240
let output =
6341
seq {
@@ -142,46 +120,55 @@ type CustomTheoryTestCase =
142120
base.Initialize()
143121
testCase.TestMethod <- TestCaseCustomizations.rewriteTestMethod testCase
144122

123+
#endif
124+
145125
/// `XunitTestFramework` providing parallel console support and conditionally enabling optional xUnit customizations.
146126
type FSharpXunitFramework(sink: IMessageSink) =
147127
inherit XunitTestFramework(sink)
148-
149-
let traceProvider =
150-
Sdk.CreateTracerProviderBuilder()
151-
.AddSource(ActivityNames.FscSourceName)
152-
.SetResourceBuilder(
153-
ResourceBuilder.CreateDefault().AddService(serviceName="F#", serviceVersion = "1.0.0"))
154-
.AddOtlpExporter()
155-
.Build()
156-
157-
interface IDisposable with
158-
member _.Dispose() =
159-
cleanUpTemporaryDirectoryOfThisTestRun ()
160-
traceProvider.ForceFlush() |> ignore
161-
traceProvider.Dispose()
162-
base.Dispose()
163128

164-
// Group test run under single activity, to make traces more readable.
165-
// Otherwise this overriden method is not necessary and can be removed.
166-
override this.CreateExecutor (assemblyName) =
129+
override this.CreateExecutor (assemblyName) =
167130
{ new XunitTestFrameworkExecutor(assemblyName, this.SourceInformationProvider, this.DiagnosticMessageSink) with
168-
override _.RunTestCases(testCases, executionMessageSink, executionOptions) =
169-
use _ = Activity.start $"{assemblyName.Name} {Runtime.InteropServices.RuntimeInformation.FrameworkDescription}" []
131+
132+
// Because xUnit v2 lacks assembly fixture, this is a good place to ensure things get called right at the start of the test run.
133+
// This gets executed once per test assembly.
134+
override x.RunTestCases(testCases, executionMessageSink, executionOptions) =
135+
136+
#if !NETCOREAPP
137+
// We need AssemblyResolver already here, because OpenTelemetry loads some assemblies dynamically.
138+
AssemblyResolver.addResolver ()
139+
#endif
140+
141+
// Configure OpenTelemetry export. Traces can be viewed in Jaeger or other compatible tools.
142+
use tracerProvider =
143+
OpenTelemetry.Sdk.CreateTracerProviderBuilder()
144+
.AddSource(ActivityNames.FscSourceName)
145+
.ConfigureResource(fun r -> r.AddService("F#") |> ignore)
146+
.AddOtlpExporter(fun o ->
147+
// Empirical values to ensure no traces are lost and no significant delay at the end of test run.
148+
o.TimeoutMilliseconds <- 200
149+
o.BatchExportProcessorOptions.MaxQueueSize <- 16384
150+
o.BatchExportProcessorOptions.ScheduledDelayMilliseconds <- 100
151+
)
152+
.Build()
170153

171-
// Because xUnit v2 lacks assembly fixture, the next best place to ensure things get called
172-
// right at the start of the test run is here or in the FSharpXunitFramework constructor.
173-
// This gets executed once per test assembly.
174-
printfn $"Running tests in {assemblyName.Name} with XUNIT_EXTRAS"
175154
logConfig initialConfig
176155
log "Installing TestConsole redirection"
177156
TestConsole.install()
157+
158+
begin
159+
use _ = Activity.startNoTags $"RunTests_{assemblyName.Name} {Runtime.InteropServices.RuntimeInformation.FrameworkDescription}"
160+
// We can't just call base.RunTestCases here, because it's implementation is async void.
161+
use runner = new XunitTestAssemblyRunner (x.TestAssembly, testCases, x.DiagnosticMessageSink, executionMessageSink, executionOptions)
162+
runner.RunAsync().Wait()
163+
end
178164

179-
#if !NETCOREAPP
180-
AssemblyResolver.addResolver ()
181-
#endif
182-
base.RunTestCases(testCases, executionMessageSink, executionOptions)
165+
tracerProvider.ForceFlush() |> ignore
166+
167+
cleanUpTemporaryDirectoryOfThisTestRun ()
183168
}
184169

170+
#if XUNIT_EXTRAS
171+
// Rewrites discovered test cases to support extra parallelization and capturing console as test output.
185172
override this.CreateDiscoverer (assemblyInfo) =
186173
{ new XunitTestFrameworkDiscoverer(assemblyInfo, this.SourceInformationProvider, this.DiagnosticMessageSink) with
187174
override _.FindTestsForType (testClass, includeSourceInformation, messageBus, options) =

0 commit comments

Comments
 (0)