Skip to content

Commit cf6b53e

Browse files
authored
reuse fsi sessions in tests (#18527)
1 parent 54fb3da commit cf6b53e

File tree

6 files changed

+83
-34
lines changed

6 files changed

+83
-34
lines changed

eng/build.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ function Test() {
226226
projectname="${projectname%.*}"
227227
testlogpath="$artifacts_dir/TestResults/$configuration/${projectname}_$targetframework.xml"
228228
args="test \"$testproject\" --no-restore --no-build -c $configuration -f $targetframework --test-adapter-path . --logger \"xunit;LogFilePath=$testlogpath\" --blame-hang-timeout 5minutes --results-directory $artifacts_dir/TestResults/$configuration -p:vstestusemsbuildoutput=false"
229-
args+=" -- xUnit.MaxParallelThreads=1"
229+
230230
"$DOTNET_INSTALL_DIR/dotnet" $args || exit $?
231231
}
232232

tests/FSharp.Compiler.ComponentTests/Miscellaneous/FsharpSuiteMigrated.fs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,18 @@ open FSharp.Test.ScriptHelpers
1414
module Configuration =
1515
let supportedNames = set ["testlib.fsi";"testlib.fs";"test.mli";"test.ml";"test.fsi";"test.fs";"test2.fsi";"test2.fs";"test.fsx";"test2.fsx"]
1616

17+
[<RequireQualifiedAccess>]
18+
type ScriptSessionIsolation = Shared | Isolated
19+
1720
module ScriptRunner =
1821
open Internal.Utilities.Library
1922

20-
let private createEngine(args,version) =
21-
getSessionForEval args version
23+
let private getOrCreateEngine(args,version) sessionIsolation =
24+
match sessionIsolation with
25+
| ScriptSessionIsolation.Isolated ->
26+
new FSharpScript(args, true, version)
27+
| ScriptSessionIsolation.Shared ->
28+
getSessionForEval args version
2229

2330
let defaultDefines =
2431
[
@@ -27,12 +34,12 @@ module ScriptRunner =
2734
#endif
2835
]
2936

30-
let runScriptFile version (cu:CompilationUnit) =
37+
let runScriptFile version sessionIsolation (cu:CompilationUnit) =
3138
let cu = cu |> withDefines defaultDefines
3239
match cu with
3340
| FS fsSource ->
3441
use capture = new TestConsole.ExecutionCapture()
35-
let engine = createEngine (fsSource.Options |> Array.ofList,version)
42+
let engine = getOrCreateEngine (fsSource.Options |> Array.ofList,version) sessionIsolation
3643
let res = evalScriptFromDiskInSharedSession engine cu
3744
match res with
3845
| CompilationResult.Failure _ -> res
@@ -88,7 +95,7 @@ module TestFrameworkAdapter =
8895
| LangVersion.SupportsMl -> "5.0", "--mlcompatibility" :: bonusArgs
8996

9097

91-
let singleTestBuildAndRunAuxVersion (folder:string) bonusArgs mode langVersion =
98+
let singleTestBuildAndRunAuxVersion (folder:string) bonusArgs mode langVersion sessionIsolation =
9299
let absFolder = Path.Combine(baseFolder,folder)
93100
let supportedNames, files =
94101
match mode with
@@ -137,17 +144,17 @@ module TestFrameworkAdapter =
137144
cu
138145
|> withDebug
139146
|> withNoOptimize
140-
|> ScriptRunner.runScriptFile langVersion
147+
|> ScriptRunner.runScriptFile langVersion sessionIsolation
141148
|> shouldSucceed
142149
| FSC_OPTIMIZED ->
143150
cu
144151
|> withOptimize
145152
|> withNoDebug
146-
|> ScriptRunner.runScriptFile langVersion
153+
|> ScriptRunner.runScriptFile langVersion sessionIsolation
147154
|> shouldSucceed
148155
| FSI ->
149156
cu
150-
|> ScriptRunner.runScriptFile langVersion
157+
|> ScriptRunner.runScriptFile langVersion sessionIsolation
151158
|> shouldSucceed
152159
| COMPILED_EXE_APP ->
153160
cu
@@ -161,7 +168,8 @@ module TestFrameworkAdapter =
161168

162169
let singleTestBuildAndRunAux folder bonusArgs mode = singleTestBuildAndRunAuxVersion folder bonusArgs mode LangVersion.Latest
163170
let singleTestBuildAndRunVersion folder mode version = singleTestBuildAndRunAuxVersion folder [] mode version
164-
let singleTestBuildAndRun folder mode = singleTestBuildAndRunVersion folder mode LangVersion.Latest
171+
let singleTestBuildAndRun folder mode = singleTestBuildAndRunVersion folder mode LangVersion.Latest ScriptSessionIsolation.Shared
172+
let singleTestBuildAndRunIsolated folder mode = singleTestBuildAndRunVersion folder mode LangVersion.Latest ScriptSessionIsolation.Isolated
165173

166174
let singleVersionedNegTestAux folder bonusArgs version testName =
167175
singleTestBuildAndRunAuxVersion folder bonusArgs (NEG_TEST_BUILD testName) version

tests/FSharp.Compiler.ComponentTests/Miscellaneous/MigratedCoreTests.fs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -352,13 +352,13 @@ let ``subtype-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/subtype" FSC_OPTI
352352
let ``subtype-FSI`` () = singleTestBuildAndRun "core/subtype" FSI
353353

354354
[<Fact>]
355-
let ``syntax-FSC_DEBUG`` () = singleTestBuildAndRun "core/syntax" FSC_DEBUG
355+
let ``syntax-FSC_DEBUG`` () = singleTestBuildAndRunIsolated "core/syntax" FSC_DEBUG
356356

357357
[<Fact>]
358-
let ``syntax-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/syntax" FSC_OPTIMIZED
358+
let ``syntax-FSC_OPTIMIZED`` () = singleTestBuildAndRunIsolated "core/syntax" FSC_OPTIMIZED
359359

360360
[<Fact>]
361-
let ``syntax-FSI`` () = singleTestBuildAndRun "core/syntax" FSI
361+
let ``syntax-FSI`` () = singleTestBuildAndRunIsolated "core/syntax" FSI
362362

363363
[<Fact>]
364364
let ``test int32-FSC_DEBUG`` () = singleTestBuildAndRun "core/int32" FSC_DEBUG
@@ -453,10 +453,10 @@ let ``fsi_load-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/fsi-load" FSC_OP
453453
let ``fsi_load-FSI`` () = singleTestBuildAndRun "core/fsi-load" FSI
454454

455455
[<Fact>]
456-
let ``reflect-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/reflect" FSC_OPTIMIZED
456+
let ``reflect-FSC_OPTIMIZED`` () = singleTestBuildAndRunIsolated "core/reflect" FSC_OPTIMIZED
457457

458458
[<Fact>]
459-
let ``reflect-FSI`` () = singleTestBuildAndRun "core/reflect" FSI
459+
let ``reflect-FSI`` () = singleTestBuildAndRunIsolated "core/reflect" FSI
460460

461461
let isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
462462

tests/FSharp.Test.Utilities/Compiler.fs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1154,7 +1154,17 @@ module rec Compiler =
11541154
evalFSharp fs script
11551155
| _ -> failwith "Script evaluation is only supported for F#."
11561156

1157-
let getSessionForEval args version = new FSharpScript(additionalArgs=args,quiet=true,langVersion=version)
1157+
let internal sessionCache =
1158+
Collections.Concurrent.ConcurrentDictionary<Set<string> * LangVersion, FSharpScript>()
1159+
1160+
let getSessionForEval args version =
1161+
let key = Set args, version
1162+
match sessionCache.TryGetValue(key) with
1163+
| true, script -> script
1164+
| _ ->
1165+
let script = new FSharpScript(additionalArgs=args,quiet=true,langVersion=version)
1166+
sessionCache.TryAdd(key, script) |> ignore
1167+
script
11581168

11591169
let evalInSharedSession (script:FSharpScript) (cUnit: CompilationUnit) : CompilationResult =
11601170
match cUnit with

tests/FSharp.Test.Utilities/ScriptHelpers.fs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,10 @@ type FSharpScript(?additionalArgs: string[], ?quiet: bool, ?langVersion: LangVer
6464
Thread.CurrentThread.CurrentCulture <- Option.defaultValue Globalization.CultureInfo.InvariantCulture desiredCulture
6565

6666
let cancellationToken = defaultArg cancellationToken CancellationToken.None
67-
let ch, errors = fsi.EvalInteractionNonThrowing(code, cancellationToken)
67+
let ch, errors =
68+
// lock, because For memory conservation in CI FSharpScripts may be reused between tests
69+
lock fsi <| fun () ->
70+
fsi.EvalInteractionNonThrowing(code, cancellationToken)
6871

6972
Thread.CurrentThread.CurrentCulture <- originalCulture
7073

tests/FSharp.Test.Utilities/XunitHelpers.fs

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ open TestFramework
1212

1313
open FSharp.Compiler.Diagnostics
1414

15-
open OpenTelemetry
1615
open OpenTelemetry.Resources
1716
open OpenTelemetry.Trace
17+
open OpenTelemetry.Metrics
1818

1919
/// Disables custom internal parallelization added with XUNIT_EXTRAS.
2020
/// Execute test cases in a class or a module one by one instead of all at once. Allow other collections to run simultaneously.
@@ -130,6 +130,45 @@ type CustomTheoryTestCase =
130130

131131
#endif
132132

133+
134+
type OpenTelemetryExport(testRunName, enable) =
135+
// On Windows forwarding localhost to wsl2 docker container sometimes does not work. Use IP address instead.
136+
let otlpEndpoint = Uri("http://127.0.0.1:4317")
137+
138+
// Configure OpenTelemetry export.
139+
let providers : IDisposable list =
140+
if not enable then [] else
141+
[
142+
// Configure OpenTelemetry tracing export. Traces can be viewed in Jaeger or other compatible tools.
143+
OpenTelemetry.Sdk.CreateTracerProviderBuilder()
144+
.AddSource(ActivityNames.FscSourceName)
145+
.ConfigureResource(fun r -> r.AddService("F#") |> ignore)
146+
.AddOtlpExporter(fun o ->
147+
o.Endpoint <- otlpEndpoint
148+
o.Protocol <- OpenTelemetry.Exporter.OtlpExportProtocol.Grpc
149+
// Empirical values to ensure no traces are lost and no significant delay at the end of test run.
150+
o.TimeoutMilliseconds <- 200
151+
o.BatchExportProcessorOptions.MaxQueueSize <- 16384
152+
o.BatchExportProcessorOptions.ScheduledDelayMilliseconds <- 100
153+
)
154+
.Build()
155+
156+
// Configure OpenTelemetry metrics export. Metrics can be viewed in Prometheus or other compatible tools.
157+
OpenTelemetry.Sdk.CreateMeterProviderBuilder()
158+
.AddMeter("System.Runtime")
159+
.ConfigureResource(fun r -> r.AddService(testRunName) |> ignore)
160+
.AddOtlpExporter(fun e m ->
161+
e.Endpoint <- otlpEndpoint
162+
e.Protocol <- OpenTelemetry.Exporter.OtlpExportProtocol.Grpc
163+
m.PeriodicExportingMetricReaderOptions.ExportIntervalMilliseconds <- 1000
164+
)
165+
.Build()
166+
]
167+
168+
interface IDisposable with
169+
member this.Dispose() =
170+
for p in providers do p.Dispose()
171+
133172
/// `XunitTestFramework` providing parallel console support and conditionally enabling optional xUnit customizations.
134173
type FSharpXunitFramework(sink: IMessageSink) =
135174
inherit XunitTestFramework(sink)
@@ -145,33 +184,22 @@ type FSharpXunitFramework(sink: IMessageSink) =
145184
// We need AssemblyResolver already here, because OpenTelemetry loads some assemblies dynamically.
146185
AssemblyResolver.addResolver ()
147186
#endif
148-
149-
// Configure OpenTelemetry export. Traces can be viewed in Jaeger or other compatible tools.
150-
use tracerProvider =
151-
OpenTelemetry.Sdk.CreateTracerProviderBuilder()
152-
.AddSource(ActivityNames.FscSourceName)
153-
.ConfigureResource(fun r -> r.AddService("F#") |> ignore)
154-
.AddOtlpExporter(fun o ->
155-
// Empirical values to ensure no traces are lost and no significant delay at the end of test run.
156-
o.TimeoutMilliseconds <- 200
157-
o.BatchExportProcessorOptions.MaxQueueSize <- 16384
158-
o.BatchExportProcessorOptions.ScheduledDelayMilliseconds <- 100
159-
)
160-
.Build()
161187

188+
let testRunName = $"RunTests_{assemblyName.Name} {Runtime.InteropServices.RuntimeInformation.FrameworkDescription}"
189+
190+
use _ = new OpenTelemetryExport(testRunName, Environment.GetEnvironmentVariable("FSHARP_OTEL_EXPORT") <> null)
191+
162192
logConfig initialConfig
163193
log "Installing TestConsole redirection"
164194
TestConsole.install()
165195

166196
begin
167-
use _ = Activity.startNoTags $"RunTests_{assemblyName.Name} {Runtime.InteropServices.RuntimeInformation.FrameworkDescription}"
197+
use _ = Activity.startNoTags testRunName
168198
// We can't just call base.RunTestCases here, because it's implementation is async void.
169199
use runner = new XunitTestAssemblyRunner (x.TestAssembly, testCases, x.DiagnosticMessageSink, executionMessageSink, executionOptions)
170200
runner.RunAsync().Wait()
171201
end
172202

173-
tracerProvider.ForceFlush() |> ignore
174-
175203
cleanUpTemporaryDirectoryOfThisTestRun ()
176204
}
177205

0 commit comments

Comments
 (0)