From d90aff7a5f3bf5a039f9167bd1479f0052e216c2 Mon Sep 17 00:00:00 2001 From: Jakub Majocha <1760221+majocha@users.noreply.github.com> Date: Mon, 9 Mar 2026 18:46:56 +0100 Subject: [PATCH 1/6] wip --- .../InteractiveSession/Misc.fs | 2 ++ tests/FSharp.Test.Utilities/CompilerAssert.fs | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/tests/FSharp.Compiler.ComponentTests/InteractiveSession/Misc.fs b/tests/FSharp.Compiler.ComponentTests/InteractiveSession/Misc.fs index 97d0dcd865b..e7f29aa20e8 100644 --- a/tests/FSharp.Compiler.ComponentTests/InteractiveSession/Misc.fs +++ b/tests/FSharp.Compiler.ComponentTests/InteractiveSession/Misc.fs @@ -1997,9 +1997,11 @@ if toEnd <> [| 3; 4; 5 |] then exit 1 let name = "World" let count = 42 let greeting = $"Hello, {name}! Count: {count}" +printfn "%s" greeting if greeting <> "Hello, World! Count: 42" then exit 1 let formatted = $"Pi is approximately {System.Math.PI:F2}" +printfn "%s" formatted if not (formatted.Contains("3.14")) then exit 1 () """ diff --git a/tests/FSharp.Test.Utilities/CompilerAssert.fs b/tests/FSharp.Test.Utilities/CompilerAssert.fs index 760a8278d04..16f07038ea2 100644 --- a/tests/FSharp.Test.Utilities/CompilerAssert.fs +++ b/tests/FSharp.Test.Utilities/CompilerAssert.fs @@ -1027,6 +1027,13 @@ Updated automatically, please check diffs in your pull request, changes must be let fsiConfig = FsiEvaluationSession.GetDefaultConfiguration() use fsiSession = FsiEvaluationSession.Create(fsiConfig, allArgs, inStream, outStream, errStream, collectible = true) + fsiSession.EvalInteraction """ + let exit (code:int) = + if code = 0 then + () + else failwith $"Script called function 'exit' with code={code}." + """ + let ch, errors = fsiSession.EvalInteractionNonThrowing source let errorMessages = ResizeArray() From 5c308434646240a680143d0550fecb77005fe0bb Mon Sep 17 00:00:00 2001 From: Jakub Majocha <1760221+majocha@users.noreply.github.com> Date: Mon, 9 Mar 2026 19:49:11 +0100 Subject: [PATCH 2/6] fix test --- tests/FSharp.Compiler.ComponentTests/InteractiveSession/Misc.fs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/FSharp.Compiler.ComponentTests/InteractiveSession/Misc.fs b/tests/FSharp.Compiler.ComponentTests/InteractiveSession/Misc.fs index e7f29aa20e8..74298498dbb 100644 --- a/tests/FSharp.Compiler.ComponentTests/InteractiveSession/Misc.fs +++ b/tests/FSharp.Compiler.ComponentTests/InteractiveSession/Misc.fs @@ -2000,6 +2000,8 @@ let greeting = $"Hello, {name}! Count: {count}" printfn "%s" greeting if greeting <> "Hello, World! Count: 42" then exit 1 +System.Globalization.CultureInfo.CurrentCulture <- System.Globalization.CultureInfo.InvariantCulture + let formatted = $"Pi is approximately {System.Math.PI:F2}" printfn "%s" formatted if not (formatted.Contains("3.14")) then exit 1 From 60fc74a78dc653dbcb8d10087186d12668a77954 Mon Sep 17 00:00:00 2001 From: majocha <1760221+majocha@users.noreply.github.com> Date: Tue, 10 Mar 2026 12:18:03 +0100 Subject: [PATCH 3/6] use FSharpScript everywhere, remove ScriptingShims.fsx --- .../Miscellaneous/FsharpSuiteMigrated.fs | 2 +- tests/FSharp.Test.Utilities/Compiler.fs | 12 +++-- tests/FSharp.Test.Utilities/CompilerAssert.fs | 54 ++++--------------- .../FSharp.Test.Utilities.fsproj | 3 +- tests/FSharp.Test.Utilities/ScriptHelpers.fs | 16 ++++-- .../FSharp.Test.Utilities/ScriptingShims.fsx | 8 --- 6 files changed, 35 insertions(+), 60 deletions(-) delete mode 100644 tests/FSharp.Test.Utilities/ScriptingShims.fsx diff --git a/tests/FSharp.Compiler.ComponentTests/Miscellaneous/FsharpSuiteMigrated.fs b/tests/FSharp.Compiler.ComponentTests/Miscellaneous/FsharpSuiteMigrated.fs index 0b6e230a271..e0cfdff2ffd 100644 --- a/tests/FSharp.Compiler.ComponentTests/Miscellaneous/FsharpSuiteMigrated.fs +++ b/tests/FSharp.Compiler.ComponentTests/Miscellaneous/FsharpSuiteMigrated.fs @@ -23,7 +23,7 @@ module ScriptRunner = let private getOrCreateEngine(args,version) sessionIsolation = match sessionIsolation with | ScriptSessionIsolation.Isolated -> - new FSharpScript(args, true, version) + getIsolatedSessionForEval args version | ScriptSessionIsolation.Shared -> getSessionForEval args version diff --git a/tests/FSharp.Test.Utilities/Compiler.fs b/tests/FSharp.Test.Utilities/Compiler.fs index 12d22dcac57..a61ac746854 100644 --- a/tests/FSharp.Test.Utilities/Compiler.fs +++ b/tests/FSharp.Test.Utilities/Compiler.fs @@ -1133,13 +1133,11 @@ module rec Compiler = let outputWritten, errorsWritten = capture.OutText, capture.ErrorText processScriptResults fs result outputWritten errorsWritten - let scriptingShim = Path.Combine(__SOURCE_DIRECTORY__,"ScriptingShims.fsx") let private evalScriptFromDisk (fs: FSharpCompilationSource) (script:FSharpScript) : CompilationResult = let fileNames = (fs.Source :: fs.AdditionalSources) |> List.map (fun x -> x.GetSourceFileName) - |> List.insertAt 0 scriptingShim |> List.map (sprintf " @\"%s\"") |> String.Concat @@ -1158,13 +1156,21 @@ module rec Compiler = let internal sessionCache = Collections.Concurrent.ConcurrentDictionary * LangVersion, FSharpScript>() + + let internal createSessionWithShadowedExit args version = + let script = new FSharpScript(additionalArgs=args,quiet=true,langVersion=version) + script.ApplyExitShadowing() + script + + let getIsolatedSessionForEval args version = + createSessionWithShadowedExit args version let getSessionForEval args version = let key = Set args, version match sessionCache.TryGetValue(key) with | true, script -> script | _ -> - let script = new FSharpScript(additionalArgs=args,quiet=true,langVersion=version) + let script = createSessionWithShadowedExit args version sessionCache.TryAdd(key, script) |> ignore script diff --git a/tests/FSharp.Test.Utilities/CompilerAssert.fs b/tests/FSharp.Test.Utilities/CompilerAssert.fs index 16f07038ea2..052db4efaa8 100644 --- a/tests/FSharp.Test.Utilities/CompilerAssert.fs +++ b/tests/FSharp.Test.Utilities/CompilerAssert.fs @@ -22,6 +22,7 @@ open FSharp.Compiler.BuildGraph open System.Runtime.Loader #endif open FSharp.Test.Utilities +open FSharp.Test.ScriptHelpers open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.CSharp open Xunit @@ -1003,52 +1004,19 @@ Updated automatically, please check diffs in your pull request, changes must be compileLibraryAndVerifyILWithOptions [|"--realsig+"|] (SourceCodeFileKind.Create("test.fs", source)) f static member RunScriptWithOptionsAndReturnResult options (source: string) = - // Save CurrentUICulture and GraphNode.culture to restore after FSI session - // FSI may change these via --preferreduilang option, and the change persists - // in the static GraphNode.culture which affects async computations in other tests - let originalUICulture = System.Threading.Thread.CurrentThread.CurrentUICulture - let originalGraphNodeCulture = GraphNode.culture - - try - // Initialize output and input streams - use inStream = new StringReader("") - use outStream = new StringWriter() - use errStream = new StringWriter() - - // Build command line arguments & start FSI session - let argv = [| "C:\\fsi.exe" |] -#if NETCOREAPP - let args = Array.append argv [|"--noninteractive"; "--targetprofile:netcore"|] -#else - let args = Array.append argv [|"--noninteractive"; "--targetprofile:mscorlib"|] -#endif - let allArgs = Array.append args options + use outStream = new StringWriter() + use errStream = new StringWriter() + use script = new FSharpScript(additionalArgs = Array.append [| "--noninteractive" |] options, quiet = false, outWriter = outStream, errWriter = errStream) + script.ApplyExitShadowing() + let result, errors = script.Eval(source) - let fsiConfig = FsiEvaluationSession.GetDefaultConfiguration() - use fsiSession = FsiEvaluationSession.Create(fsiConfig, allArgs, inStream, outStream, errStream, collectible = true) + let errorMessages = ResizeArray(errors |> Seq.map _.Message) - fsiSession.EvalInteraction """ - let exit (code:int) = - if code = 0 then - () - else failwith $"Script called function 'exit' with code={code}." - """ - - let ch, errors = fsiSession.EvalInteractionNonThrowing source - - let errorMessages = ResizeArray() - errors - |> Seq.iter (fun error -> errorMessages.Add(error.Message)) - - match ch with - | Choice2Of2 ex -> errorMessages.Add(ex.Message) - | _ -> () + match result with + | Result.Error ex -> errorMessages.Add(ex.Message) + | _ -> () - errorMessages, string outStream, string errStream - finally - // Restore CurrentUICulture and GraphNode.culture to prevent culture leaking between tests - System.Threading.Thread.CurrentThread.CurrentUICulture <- originalUICulture - GraphNode.culture <- originalGraphNodeCulture + errorMessages, string outStream, string errStream static member RunScriptWithOptions options (source: string) (expectedErrorMessages: string list) = let errorMessages, _, _ = CompilerAssert.RunScriptWithOptionsAndReturnResult options source diff --git a/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj b/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj index fde44ceb83d..cb5f54ad381 100644 --- a/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj +++ b/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj @@ -25,16 +25,15 @@ scriptlib.fsx - + - diff --git a/tests/FSharp.Test.Utilities/ScriptHelpers.fs b/tests/FSharp.Test.Utilities/ScriptHelpers.fs index a0c5b178488..cb2956ab8ac 100644 --- a/tests/FSharp.Test.Utilities/ScriptHelpers.fs +++ b/tests/FSharp.Test.Utilities/ScriptHelpers.fs @@ -19,7 +19,7 @@ type LangVersion = | Preview | Latest -type FSharpScript(?additionalArgs: string[], ?quiet: bool, ?langVersion: LangVersion) = +type FSharpScript(?additionalArgs: string[], ?quiet: bool, ?langVersion: LangVersion, ?outWriter: IO.TextWriter, ?errWriter: IO.TextWriter) = let additionalArgs = defaultArg additionalArgs [||] let quiet = defaultArg quiet true @@ -44,13 +44,23 @@ type FSharpScript(?additionalArgs: string[], ?quiet: bool, ?langVersion: LangVer let argv = Array.append baseArgs additionalArgs - let fsi = FsiEvaluationSession.Create (config, argv, TextReader.Null, stdout, stderr) + let outWriter = defaultArg outWriter stdout + let errWriter = defaultArg errWriter stderr + let fsi = FsiEvaluationSession.Create (config, argv, TextReader.Null, outWriter, errWriter) member _.ValueBound = fsi.ValueBound member _.Fsi = fsi - member this.Eval(code: string, ?cancellationToken: CancellationToken, ?desiredCulture: Globalization.CultureInfo) = + member _.ApplyExitShadowing() = + fsi.EvalInteraction """ +let exit (code:int) = + if code = 0 then + () + else failwith $"Script called function 'exit' with code={code}." + """ + + member _.Eval(code: string, ?cancellationToken: CancellationToken, ?desiredCulture: Globalization.CultureInfo) = let originalCulture = Thread.CurrentThread.CurrentCulture let originalUICulture = Thread.CurrentThread.CurrentUICulture Thread.CurrentThread.CurrentCulture <- Option.defaultValue Globalization.CultureInfo.InvariantCulture desiredCulture diff --git a/tests/FSharp.Test.Utilities/ScriptingShims.fsx b/tests/FSharp.Test.Utilities/ScriptingShims.fsx deleted file mode 100644 index be9ce9142c5..00000000000 --- a/tests/FSharp.Test.Utilities/ScriptingShims.fsx +++ /dev/null @@ -1,8 +0,0 @@ -namespace global - -[] -module GlobalShims = - let exit (code:int) = - if code = 0 then - () - else failwith $"Script called function 'exit' with code={code}." From e0f642ffc6eba0bf0addfba5f6478ec1e77e77d4 Mon Sep 17 00:00:00 2001 From: majocha <1760221+majocha@users.noreply.github.com> Date: Tue, 10 Mar 2026 12:18:32 +0100 Subject: [PATCH 4/6] cleanup - this can run in parallel --- tests/fsharp/TypeProviderTests.fs | 3 --- tests/fsharp/tests.fs | 3 --- 2 files changed, 6 deletions(-) diff --git a/tests/fsharp/TypeProviderTests.fs b/tests/fsharp/TypeProviderTests.fs index d447c4c7d25..e0057aef22f 100644 --- a/tests/fsharp/TypeProviderTests.fs +++ b/tests/fsharp/TypeProviderTests.fs @@ -7,9 +7,6 @@ #load "../FSharp.Test.Utilities/TestFramework.fs" #load "single-test.fs" #else -// Disable parallel execution because this module contains FSI stdin tests -// that can interfere with other FSI stdin tests in CoreTests -[] module FSharp.Test.FSharpSuite.TypeProviderTests #endif diff --git a/tests/fsharp/tests.fs b/tests/fsharp/tests.fs index 0bfd7191781..e834af94aea 100644 --- a/tests/fsharp/tests.fs +++ b/tests/fsharp/tests.fs @@ -29,9 +29,6 @@ let FSI = FSI_NETFX let log = printfn -// Disable parallel execution for CoreTests because the printing and FSI tests -// spawn external FSI processes with stdin redirection that can interfere with each other -[] module CoreTests = From bda7e87714d6023db39ddc475ffe03dab763e28e Mon Sep 17 00:00:00 2001 From: majocha <1760221+majocha@users.noreply.github.com> Date: Wed, 11 Mar 2026 08:51:57 +0100 Subject: [PATCH 5/6] cleanup --- tests/FSharp.Test.Utilities/TestFramework.fs | 2 -- tests/fsharp/single-test.fs | 2 -- tests/fsharp/tests.fs | 2 -- 3 files changed, 6 deletions(-) diff --git a/tests/FSharp.Test.Utilities/TestFramework.fs b/tests/FSharp.Test.Utilities/TestFramework.fs index dcb64909ad2..2348b09df8e 100644 --- a/tests/FSharp.Test.Utilities/TestFramework.fs +++ b/tests/FSharp.Test.Utilities/TestFramework.fs @@ -60,8 +60,6 @@ let rec copyDirectory (sourceDir: string) (destinationDir: string) (recursive: b [] module Commands = - let gate = obj() - // Execute the process pathToExe passing the arguments: arguments with the working directory: workingDir timeout after timeout milliseconds -1 = wait forever // returns exit code, stdio and stderr as string arrays let executeProcess pathToExe arguments workingDir = diff --git a/tests/fsharp/single-test.fs b/tests/fsharp/single-test.fs index 41f6e032609..e8d26711979 100644 --- a/tests/fsharp/single-test.fs +++ b/tests/fsharp/single-test.fs @@ -9,8 +9,6 @@ open FSharp.Compiler.IO let testConfig = testConfig __SOURCE_DIRECTORY__ -let log = printfn - type Permutation = #if NETCOREAPP | FSC_NETCORE of optimized: bool * buildOnly: bool diff --git a/tests/fsharp/tests.fs b/tests/fsharp/tests.fs index e834af94aea..330ffe93590 100644 --- a/tests/fsharp/tests.fs +++ b/tests/fsharp/tests.fs @@ -27,8 +27,6 @@ let FSI = FSI_NETFX #endif // ^^^^^^^^^^^^ To run these tests in F# Interactive , 'build net40', then send this chunk, then evaluate body of a test ^^^^^^^^^^^^ -let log = printfn - module CoreTests = From a524896e1822f442f160637c1c39808e7413b0bd Mon Sep 17 00:00:00 2001 From: majocha <1760221+majocha@users.noreply.github.com> Date: Fri, 13 Mar 2026 16:57:45 +0100 Subject: [PATCH 6/6] fix --- tests/FSharp.Test.Utilities/CompilerAssert.fs | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/tests/FSharp.Test.Utilities/CompilerAssert.fs b/tests/FSharp.Test.Utilities/CompilerAssert.fs index 052db4efaa8..d09896563e5 100644 --- a/tests/FSharp.Test.Utilities/CompilerAssert.fs +++ b/tests/FSharp.Test.Utilities/CompilerAssert.fs @@ -1004,19 +1004,29 @@ Updated automatically, please check diffs in your pull request, changes must be compileLibraryAndVerifyILWithOptions [|"--realsig+"|] (SourceCodeFileKind.Create("test.fs", source)) f static member RunScriptWithOptionsAndReturnResult options (source: string) = - use outStream = new StringWriter() - use errStream = new StringWriter() - use script = new FSharpScript(additionalArgs = Array.append [| "--noninteractive" |] options, quiet = false, outWriter = outStream, errWriter = errStream) - script.ApplyExitShadowing() - let result, errors = script.Eval(source) + // Save CurrentUICulture and GraphNode.culture to restore after FSI session + // FSI may change these via --preferreduilang option, and the change persists + // in the static GraphNode.culture which affects async computations in other tests + let originalUICulture = CultureInfo.CurrentUICulture + let originalGraphNodeCulture = GraphNode.culture + try + use outStream = new StringWriter() + use errStream = new StringWriter() + use script = new FSharpScript(additionalArgs = Array.append [| "--noninteractive" |] options, quiet = false, outWriter = outStream, errWriter = errStream) + script.ApplyExitShadowing() + let result, errors = script.Eval(source) - let errorMessages = ResizeArray(errors |> Seq.map _.Message) + let errorMessages = ResizeArray(errors |> Seq.map _.Message) - match result with - | Result.Error ex -> errorMessages.Add(ex.Message) - | _ -> () + match result with + | Result.Error ex -> errorMessages.Add(ex.Message) + | _ -> () - errorMessages, string outStream, string errStream + errorMessages, string outStream, string errStream + finally + // Restore CurrentUICulture and GraphNode.culture to prevent culture leaking between tests + CultureInfo.CurrentUICulture <- originalUICulture + GraphNode.culture <- originalGraphNodeCulture static member RunScriptWithOptions options (source: string) (expectedErrorMessages: string list) = let errorMessages, _, _ = CompilerAssert.RunScriptWithOptionsAndReturnResult options source