diff --git a/azure-pipelines-PR.yml b/azure-pipelines-PR.yml index 0c7bc9b5a17..9a5c59441e2 100644 --- a/azure-pipelines-PR.yml +++ b/azure-pipelines-PR.yml @@ -488,6 +488,8 @@ stages: displayName: Publish Test Results inputs: testResultsFormat: 'XUnit' + testRunTitle: WindowsCompressedMetadata $(_testKind) + mergeTestResults: true testResultsFiles: '*.xml' searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_configuration)' continueOnError: true @@ -558,7 +560,9 @@ stages: displayName: Publish Test Results inputs: testResultsFormat: 'XUnit' + testRunTitle: Linux testResultsFiles: '*.xml' + mergeTestResults: true searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' continueOnError: true condition: always() @@ -602,6 +606,8 @@ stages: inputs: testResultsFormat: 'XUnit' testResultsFiles: '*.xml' + testRunTitle: MacOS + mergeTestResults: true searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' continueOnError: true condition: always() diff --git a/docs/release-notes/.FSharp.Compiler.Service/9.0.200.md b/docs/release-notes/.FSharp.Compiler.Service/9.0.200.md index fe3cde0444a..2484bfa53ce 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/9.0.200.md +++ b/docs/release-notes/.FSharp.Compiler.Service/9.0.200.md @@ -13,6 +13,7 @@ ### Changed * Make ILTypeDef interface impls calculation lazy. ([PR #17392](https://github.com/dotnet/fsharp/pull/17392)) +* Adjustments to better run in parallel testing ([PR #17662](https://github.com/dotnet/fsharp/pull/17662)) * Remove non-functional useSyntaxTreeCache option. ([PR #17768](https://github.com/dotnet/fsharp/pull/17768)) * Better ranges for CE `let!` and `use!` error reporting. ([PR #17712](https://github.com/dotnet/fsharp/pull/17712)) * Better ranges for CE `do!` error reporting. ([PR #17779](https://github.com/dotnet/fsharp/pull/17779)) @@ -20,4 +21,5 @@ * Better ranges for CE `match!`. ([PR #17789](https://github.com/dotnet/fsharp/pull/17789)) * Better ranges for CE `use` error reporting. ([PR #17811](https://github.com/dotnet/fsharp/pull/17811)) + ### Breaking Changes diff --git a/eng/Build.ps1 b/eng/Build.ps1 index 483b60b10df..04f9df3a5fc 100644 --- a/eng/Build.ps1 +++ b/eng/Build.ps1 @@ -369,7 +369,7 @@ function TestUsingMSBuild([string] $testProject, [string] $targetFramework, [str $testLogPath = "$ArtifactsDir\TestResults\$configuration\${projectName}_$targetFramework.xml" $testBinLogPath = "$LogDir\${projectName}_$targetFramework.binlog" $args = "test $testProject -c $configuration -f $targetFramework -v n --test-adapter-path $testadapterpath --logger ""xunit;LogFilePath=$testLogPath"" /bl:$testBinLogPath" - $args += " --blame --results-directory $ArtifactsDir\TestResults\$configuration -p:vstestusemsbuildoutput=false" + $args += " --blame --blame-hang-timeout 5minutes --results-directory $ArtifactsDir\TestResults\$configuration -p:vstestusemsbuildoutput=false" if (-not $noVisualStudio -or $norestore) { $args += " --no-restore" @@ -395,6 +395,28 @@ function TestUsingMSBuild([string] $testProject, [string] $targetFramework, [str } } +function TestSolutionUsingMSBuild([string] $testSolution, [string] $targetFramework, [string] $testadapterpath) { + $dotnetPath = InitializeDotNetCli + $dotnetExe = Join-Path $dotnetPath "dotnet.exe" + $solutionName = [System.IO.Path]::GetFileNameWithoutExtension($testSolution) + $testLogPath = "$ArtifactsDir\TestResults\$configuration\{assembly}.{framework}.xml" + $testBinLogPath = "$LogDir\${solutionName}_$targetFramework.binlog" + + $args = "test $testSolution -c $configuration -f $targetFramework --test-adapter-path $testadapterpath -v minimal --logger ""xunit;LogFilePath=$testLogPath"" /bl:$testBinLogPath" + $args += " --blame-hang-timeout 5minutes --results-directory $ArtifactsDir\TestResults\$configuration /p:VsTestUseMSBuildOutput=true" + + if (-not $noVisualStudio -or $norestore) { + $args += " --no-restore" + } + + if (-not $noVisualStudio) { + $args += " --no-build" + } + + Write-Host("$args") + Exec-Console $dotnetExe $args +} + function Prepare-TempDir() { Copy-Item (Join-Path $RepoRoot "tests\Resources\Directory.Build.props") $TempDir Copy-Item (Join-Path $RepoRoot "tests\Resources\Directory.Build.targets") $TempDir @@ -589,31 +611,11 @@ try { $script:BuildMessage = "Failure running tests" if ($testCoreClr) { - $bgJob = TestUsingMSBuild -testProject "$RepoRoot\tests\fsharp\FSharpSuite.Tests.fsproj" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharpSuite.Tests\" -asBackgroundJob $true - - TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Compiler.ComponentTests\FSharp.Compiler.ComponentTests.fsproj" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Compiler.ComponentTests\" - TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Compiler.Service.Tests\FSharp.Compiler.Service.Tests.fsproj" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Compiler.Service.Tests\" - TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Compiler.Private.Scripting.UnitTests\FSharp.Compiler.Private.Scripting.UnitTests.fsproj" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Compiler.Private.Scripting.UnitTests\" - TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Build.UnitTests\FSharp.Build.UnitTests.fsproj" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Build.UnitTests\" - TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests\FSharp.Core.UnitTests.fsproj" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests\" - - # Collect output from background jobs - Wait-job $bgJob | out-null - Receive-Job $bgJob -ErrorAction Stop + TestSolutionUsingMSBuild -testSolution "$RepoRoot\FSharp.sln" -targetFramework $script:coreclrTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Compiler.ComponentTests\" } if ($testDesktop) { - $bgJob = TestUsingMSBuild -testProject "$RepoRoot\tests\fsharp\FSharpSuite.Tests.fsproj" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharpSuite.Tests\" -asBackgroundJob $true - - TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Compiler.ComponentTests\FSharp.Compiler.ComponentTests.fsproj" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Compiler.ComponentTests\" - TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Compiler.Service.Tests\FSharp.Compiler.Service.Tests.fsproj" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Compiler.Service.Tests\" - TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Compiler.Private.Scripting.UnitTests\FSharp.Compiler.Private.Scripting.UnitTests.fsproj" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Compiler.Private.Scripting.UnitTests\" - TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Build.UnitTests\FSharp.Build.UnitTests.fsproj" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Build.UnitTests\" - TestUsingMSBuild -testProject "$RepoRoot\tests\FSharp.Core.UnitTests\FSharp.Core.UnitTests.fsproj" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Core.UnitTests\" - - # Collect output from background jobs - Wait-job $bgJob | out-null - Receive-Job $bgJob -ErrorAction Stop + TestSolutionUsingMSBuild -testSolution "$RepoRoot\FSharp.sln" -targetFramework $script:desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Compiler.ComponentTests\" } if ($testFSharpQA) { diff --git a/eng/Versions.props b/eng/Versions.props index d89aec3f3cd..202d17a6a19 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -184,7 +184,7 @@ 3.1.0 5.0.0-preview.7.20364.11 5.0.0-preview.7.20364.11 - 17.4.0 + 17.11.1 13.0.3 1.0.0-beta2-dev3 2.18.48 diff --git a/eng/build.sh b/eng/build.sh old mode 100755 new mode 100644 index c4abb23f6f1..05e31af8d63 --- a/eng/build.sh +++ b/eng/build.sh @@ -214,7 +214,7 @@ function Test() { projectname=$(basename -- "$testproject") projectname="${projectname%.*}" testlogpath="$artifacts_dir/TestResults/$configuration/${projectname}_$targetframework.xml" - args="test \"$testproject\" --no-restore --no-build -c $configuration -f $targetframework --test-adapter-path . --logger \"xunit;LogFilePath=$testlogpath\" --blame --results-directory $artifacts_dir/TestResults/$configuration -p:vstestusemsbuildoutput=false" + 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" "$DOTNET_INSTALL_DIR/dotnet" $args || exit $? } diff --git a/src/Compiler/Driver/fsc.fs b/src/Compiler/Driver/fsc.fs index ac4ee179538..a74f35f21b5 100644 --- a/src/Compiler/Driver/fsc.fs +++ b/src/Compiler/Driver/fsc.fs @@ -273,9 +273,6 @@ let SetProcessThreadLocals tcConfigB = | Some s -> Thread.CurrentThread.CurrentUICulture <- CultureInfo(s) | None -> () - if tcConfigB.utf8output then - Console.OutputEncoding <- Encoding.UTF8 - let ProcessCommandLineFlags (tcConfigB: TcConfigBuilder, lcidFromCodePage, argv) = let mutable inputFilesRef = [] @@ -550,6 +547,17 @@ let main1 | Some parallelReferenceResolution -> tcConfigB.parallelReferenceResolution <- parallelReferenceResolution | None -> () + if tcConfigB.utf8output && Console.OutputEncoding <> Encoding.UTF8 then + let previousEncoding = Console.OutputEncoding + Console.OutputEncoding <- Encoding.UTF8 + + disposables.Register( + { new IDisposable with + member _.Dispose() = + Console.OutputEncoding <- previousEncoding + } + ) + // Display the banner text, if necessary if not bannerAlreadyPrinted then Console.Write(GetBannerText tcConfigB) @@ -1242,16 +1250,6 @@ let CompileFromCommandLineArguments ) = use disposables = new DisposablesTracker() - let savedOut = Console.Out - - use _ = - { new IDisposable with - member _.Dispose() = - try - Console.SetOut(savedOut) - with _ -> - () - } main1 ( ctok, diff --git a/src/Compiler/Interactive/fsi.fs b/src/Compiler/Interactive/fsi.fs index 74fcc37340c..c1c60d6e47d 100644 --- a/src/Compiler/Interactive/fsi.fs +++ b/src/Compiler/Interactive/fsi.fs @@ -846,7 +846,7 @@ type internal FsiStdinSyphon(errorWriter: TextWriter) = /// Encapsulates functions used to write to outWriter and errorWriter type internal FsiConsoleOutput(tcConfigB, outWriter: TextWriter, errorWriter: TextWriter) = - let nullOut = new StreamWriter(Stream.Null) :> TextWriter + let nullOut = TextWriter.Null let fprintfnn (os: TextWriter) fmt = Printf.kfprintf @@ -1203,11 +1203,6 @@ type internal FsiCommandLineOptions(fsi: FsiEvaluationSessionHostConfig, argv: s if tcConfigB.clearResultsCache then dependencyProvider.ClearResultsCache(tcConfigB.compilerToolPaths, getOutputDir tcConfigB, reportError rangeCmdArgs) - if tcConfigB.utf8output then - let prev = Console.OutputEncoding - Console.OutputEncoding <- Encoding.UTF8 - System.AppDomain.CurrentDomain.ProcessExit.Add(fun _ -> Console.OutputEncoding <- prev) - do let firstArg = match sourceFiles with @@ -4646,6 +4641,20 @@ type FsiEvaluationSession with e -> warning (e) + let restoreEncoding = + if tcConfigB.utf8output && Console.OutputEncoding <> Text.Encoding.UTF8 then + let previousEncoding = Console.OutputEncoding + Console.OutputEncoding <- Encoding.UTF8 + + Some( + { new IDisposable with + member _.Dispose() = + Console.OutputEncoding <- previousEncoding + } + ) + else + None + do updateBannerText () // resetting banner text after parsing options @@ -4789,6 +4798,7 @@ type FsiEvaluationSession member _.Dispose() = (tcImports :> IDisposable).Dispose() uninstallMagicAssemblyResolution.Dispose() + restoreEncoding |> Option.iter (fun x -> x.Dispose()) /// Load the dummy interaction, load the initial files, and, /// if interacting, start the background thread to read the standard input. diff --git a/src/Compiler/Service/service.fs b/src/Compiler/Service/service.fs index 525faf3be3d..f78512dbc5b 100644 --- a/src/Compiler/Service/service.fs +++ b/src/Compiler/Service/service.fs @@ -100,14 +100,6 @@ module CompileHelpers = diagnostics.ToArray(), result - let setOutputStreams execute = - // Set the output streams, if requested - match execute with - | Some(writer, error) -> - Console.SetOut writer - Console.SetError error - | None -> () - [] // There is typically only one instance of this type in an IDE process. type FSharpChecker diff --git a/src/Compiler/Utilities/Activity.fs b/src/Compiler/Utilities/Activity.fs index b6fafe1c1b9..3c22526c896 100644 --- a/src/Compiler/Utilities/Activity.fs +++ b/src/Compiler/Utilities/Activity.fs @@ -237,38 +237,36 @@ module internal Activity = sb.ToString() - let addCsvFileListener (pathToFile:string) = - if pathToFile |> File.Exists |> not then - File.WriteAllLines( - pathToFile, - [ - "Name,StartTime,EndTime,Duration(s),Id,ParentId,RootId," - + String.concat "," Tags.AllKnownTags - ] - ) - - let sw = new StreamWriter(path = pathToFile, append = true) + let addCsvFileListener (pathToFile: string) = + let newFile = pathToFile |> File.Exists |> not + // FileShare.Read to avoid sporadic file locking during tests. + let stream = new FileStream(pathToFile, FileMode.Append, FileAccess.Write, FileShare.Read) + let csvWriter = new StreamWriter(stream) + + if newFile then csvWriter.WriteLine( + "Name,StartTime,EndTime,Duration(s),Id,ParentId,RootId," + + String.concat "," Tags.AllKnownTags) let msgQueue = MailboxProcessor.Start(fun inbox -> async { while true do let! msg = inbox.Receive() - do! sw.WriteLineAsync(msg) |> Async.AwaitTask + do! csvWriter.WriteLineAsync(msg) |> Async.AwaitTask }) - let l = + let listener = new ActivityListener( ShouldListenTo = (fun a ->ActivityNames.AllRelevantNames |> Array.contains a.Name), Sample = (fun _ -> ActivitySamplingResult.AllData), ActivityStopped = (fun a -> msgQueue.Post(createCsvRow a)) ) - ActivitySource.AddActivityListener(l) + ActivitySource.AddActivityListener(listener) { new IDisposable with member this.Dispose() = - l.Dispose() // Unregister from listening new activities first + listener.Dispose() // Unregister from listening new activities first (msgQueue :> IDisposable).Dispose() // Wait for the msg queue to be written out - sw.Dispose() // Only then flush the messages and close the file + csvWriter.Dispose() // Only then flush the messages and close the file } diff --git a/src/Compiler/Utilities/illib.fs b/src/Compiler/Utilities/illib.fs index e09c650e39b..3744167dd1a 100644 --- a/src/Compiler/Utilities/illib.fs +++ b/src/Compiler/Utilities/illib.fs @@ -137,7 +137,7 @@ module internal PervasiveAutoOpens = type Async with static member RunImmediate(computation: Async<'T>, ?cancellationToken) = - let cancellationToken = defaultArg cancellationToken Async.DefaultCancellationToken + let cancellationToken = defaultArg cancellationToken CancellationToken.None let ts = TaskCompletionSource<'T>() diff --git a/src/fsi/fsimain.fs b/src/fsi/fsimain.fs index c13f37c11bc..4b9e92704a7 100644 --- a/src/fsi/fsimain.fs +++ b/src/fsi/fsimain.fs @@ -358,16 +358,6 @@ let evaluateSession (argv: string[]) = let MainMain argv = ignore argv let argv = System.Environment.GetCommandLineArgs() - let savedOut = Console.Out - - use __ = - { new IDisposable with - member _.Dispose() = - try - Console.SetOut(savedOut) - with _ -> - () - } let timesFlag = argv |> Array.exists (fun x -> x = "/times" || x = "--times") diff --git a/tests/EndToEndBuildTests/BasicProvider/BasicProvider.Tests/BasicProvider.Tests.fsproj b/tests/EndToEndBuildTests/BasicProvider/BasicProvider.Tests/BasicProvider.Tests.fsproj index 4e4aef01856..b98bced9759 100644 --- a/tests/EndToEndBuildTests/BasicProvider/BasicProvider.Tests/BasicProvider.Tests.fsproj +++ b/tests/EndToEndBuildTests/BasicProvider/BasicProvider.Tests/BasicProvider.Tests.fsproj @@ -21,7 +21,7 @@ content\myfiles\ - + diff --git a/tests/EndToEndBuildTests/ComboProvider/ComboProvider.Tests/ComboProvider.Tests.fsproj b/tests/EndToEndBuildTests/ComboProvider/ComboProvider.Tests/ComboProvider.Tests.fsproj index d4d410bacd4..0135c83f57d 100644 --- a/tests/EndToEndBuildTests/ComboProvider/ComboProvider.Tests/ComboProvider.Tests.fsproj +++ b/tests/EndToEndBuildTests/ComboProvider/ComboProvider.Tests/ComboProvider.Tests.fsproj @@ -18,7 +18,8 @@ - + + diff --git a/tests/FSharp.Build.UnitTests/FSharp.Build.UnitTests.fsproj b/tests/FSharp.Build.UnitTests/FSharp.Build.UnitTests.fsproj index 0a2421f3262..853737b3ca8 100644 --- a/tests/FSharp.Build.UnitTests/FSharp.Build.UnitTests.fsproj +++ b/tests/FSharp.Build.UnitTests/FSharp.Build.UnitTests.fsproj @@ -4,17 +4,26 @@ net472;$(FSharpNetCoreProductTargetFramework) - $(FSharpNetCoreProductTargetFramework) + $(FSharpNetCoreProductTargetFramework) Library true xunit + + XunitSetup.fs + + + + PreserveNewest + + + diff --git a/tests/FSharp.Build.UnitTests/xunit.runner.json b/tests/FSharp.Build.UnitTests/xunit.runner.json new file mode 100644 index 00000000000..b01c50a3cb5 --- /dev/null +++ b/tests/FSharp.Build.UnitTests/xunit.runner.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "appDomain": "denied", + "parallelizeAssembly": true +} diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/misc/utf8output.fs b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/misc/utf8output.fs new file mode 100644 index 00000000000..650bb63c76a --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/misc/utf8output.fs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace CompilerOptions.Fsc + +open Xunit +open FSharp.Test +open FSharp.Test.Compiler +open System + +[] +module utf8output = + + [] + let ``OutputEncoding is restored after executing compilation`` () = + let currentEncoding = Console.OutputEncoding + use restoreCurrentEncodingAfterTest = { new IDisposable with member _.Dispose() = Console.OutputEncoding <- currentEncoding } + + let encoding = Text.Encoding.GetEncoding("iso-8859-1") + + Console.OutputEncoding <- encoding + + Fs """printfn "Hello world" """ + |> asExe + |> withOptionsString "--utf8output" + |> compile + |> shouldSucceed + |> ignore + + Console.OutputEncoding.BodyName |> Assert.shouldBe encoding.BodyName + + [] + let ``OutputEncoding is restored after running script`` () = + let currentEncoding = Console.OutputEncoding + use restoreCurrentEncodingAfterTest = { new IDisposable with member _.Dispose() = Console.OutputEncoding <- currentEncoding } + + let encoding = Text.Encoding.GetEncoding("iso-8859-1") + + Console.OutputEncoding <- encoding + + Fsx """printfn "Hello world" """ + |> withOptionsString "--utf8output" + |> runFsi + |> shouldSucceed + |> ignore + + Console.OutputEncoding.BodyName |> Assert.shouldBe encoding.BodyName diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/times/times.fs b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/times/times.fs index b18e5472ec0..939a1b9b9a8 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/times/times.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/times/times.fs @@ -8,6 +8,8 @@ open FSharp.Test.Compiler open System open System.IO +// reportTime uses global state. +[] module times = // This test was automatically generated (moved from FSharpQA suite - CompilerOptions/fsc/times) @@ -62,24 +64,31 @@ module times = "Typecheck" "GC0" "Duration"|] + |> ignore + [] + let ``times - to csv file`` (compilation: CompilationUnit) = + let tempPath = compilation.OutputDirectory ++ "times.csv" - [] - let ``times - to csv file`` compilation = - let tempPath = Path.Combine(Path.GetTempPath(),Guid.NewGuid().ToString() + ".csv") - use _ = {new IDisposable with - member this.Dispose() = File.Delete(tempPath) } + use watcher = new FileSystemWatcher(compilation.OutputDirectory, Filter = "times.csv", EnableRaisingEvents = true) + let changed = System.Threading.Tasks.TaskCompletionSource<_>() + watcher.Changed.Add (changed.TrySetResult >> ignore) compilation |> asFsx - |> withOptions ["--times:"+tempPath] + |> withOptions ["--times:" + tempPath] |> ignoreWarnings |> compile |> shouldSucceed - |> ignore - - let csvContents = File.ReadAllLines(tempPath) + |> ignore + + changed.Task.Wait() - Assert.Contains("Name,StartTime,EndTime,Duration(s),Id,ParentId,RootId",csvContents[0]) - Assert.Contains(csvContents, fun row -> row.Contains("Typecheck")) - Assert.Contains(csvContents, fun row -> row.Contains("Parse inputs")) + // Shared access to avoid sporadic file locking during tests. + use reader = new StreamReader(new FileStream(tempPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + let header = reader.ReadLine() + Assert.Contains("Name,StartTime,EndTime,Duration(s),Id,ParentId,RootId", header) + + let csvContents = reader.ReadToEnd() + Assert.Contains("Typecheck", csvContents) + Assert.Contains("Parse inputs", csvContents) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs index 72dd62e397c..802f07012de 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/AsyncMemoize.fs @@ -2,164 +2,148 @@ module CompilerService.AsyncMemoize open System open System.Threading -open Xunit open Internal.Utilities.Collections open System.Threading.Tasks open System.Diagnostics -open System.Collections.Concurrent + open FSharp.Compiler.DiagnosticsLogger open FSharp.Compiler.Diagnostics -open FSharp.Compiler.BuildGraph +open Xunit -let timeout = TimeSpan.FromSeconds 10. +[] +module internal JobEvents = -let waitFor (mre: ManualResetEvent) = - if not <| mre.WaitOne timeout then - failwith "waitFor timed out" + let publishEvent (cache: AsyncMemoize<_, _, _>) = + let wrapper = Event<_>() + cache.OnEvent (fun e -> lock wrapper <| fun () -> wrapper.Trigger e) + wrapper.Publish |> Event.map (fun (jobEvent, (_,k,_)) -> jobEvent, k) -let waitUntil condition value = - task { - let sw = Stopwatch.StartNew() - while not <| condition value do - if sw.Elapsed > timeout then - failwith "waitUntil timed out" - do! Task.Delay 10 - } + let collectEvents cache = + cache |> publishEvent |> Event.scan (fun es e -> e :: es) [] |> Event.map List.rev -let rec internal spinFor (duration: TimeSpan) = - async { - let sw = Stopwatch.StartNew() - do! Async.Sleep 10 - let remaining = duration - sw.Elapsed - if remaining > TimeSpan.Zero then - return! spinFor remaining - } - -#if BUILDING_WITH_LKG -type internal EventRecorder<'a, 'b, 'c when 'a : equality and 'b : equality>(memoize: AsyncMemoize<'a,'b,'c>) as self = -#else -type internal EventRecorder<'a, 'b, 'c when 'a : equality and 'b : equality and 'a:not null and 'b:not null>(memoize: AsyncMemoize<'a,'b,'c>) as self = -#endif + /// Exposes a live view of the list of JobEvents generated by AsyncMemoize. + let observe cache = + let updateAvailable = new AutoResetEvent(false) + let mutable recorded = [] - let events = ConcurrentQueue() + let update next = + Debug.WriteLine $"%A{next}" + recorded <- next + updateAvailable.Set() |> ignore - do memoize.OnEvent self.Add + collectEvents cache |> Event.add update - member _.Add (e, (_label, k, _version)) = events.Enqueue (e, k) + let waitForUpdate = updateAvailable |> Async.AwaitWaitHandle |> Async.Ignore - member _.Received value = events |> Seq.exists (fst >> (=) value) - - member _.CountOf value count = events |> Seq.filter (fst >> (=) value) |> Seq.length |> (=) count + async { + Debug.WriteLine $"current: %A{recorded}" + return recorded, waitForUpdate + } - member _.ShouldBe (expected) = - let expected = expected |> Seq.toArray - let actual = events |> Seq.toArray - Assert.Equal<_ array>(expected, actual) + let countOf value count events = events |> Seq.filter (fst >> (=) value) |> Seq.length |> (=) count - member _.Sequence = events |> Seq.map id + let received value events = events |> Seq.exists (fst >> (=) value) + let waitUntil observedCache condition = + let rec loop() = async { + let! current, waitForUpdate = observedCache + if current |> condition |> not then + do! waitForUpdate + return! loop() + } + loop() [] let ``Basics``() = - - let computation key = async { - do! Async.Sleep 1 - return key * 2 - } - - let memoize = AsyncMemoize() - let events = EventRecorder(memoize) - - let result = - seq { - memoize.Get'(5, computation 5) - memoize.Get'(5, computation 5) - memoize.Get'(2, computation 2) - memoize.Get'(5, computation 5) - memoize.Get'(3, computation 3) - memoize.Get'(2, computation 2) + task { + let computation key = async { + do! Async.Sleep 1 + return key * 2 } - |> Async.Parallel - |> Async.RunSynchronously - let expected = [| 10; 10; 4; 10; 6; 4|] + let memoize = AsyncMemoize() + let events = observe memoize + + let result = + seq { + memoize.Get'(5, computation 5) + memoize.Get'(5, computation 5) + memoize.Get'(2, computation 2) + memoize.Get'(5, computation 5) + memoize.Get'(3, computation 3) + memoize.Get'(2, computation 2) + } + |> Async.Parallel + |> Async.RunSynchronously - Assert.Equal(expected, result) + let expected = [| 10; 10; 4; 10; 6; 4|] - (waitUntil (events.CountOf Finished) 3).Wait() + Assert.Equal(expected, result) - let groups = events.Sequence |> Seq.groupBy snd |> Seq.toList - Assert.Equal(3, groups.Length) - for key, events in groups do - Assert.Equal>(Set [ Requested, key; Started, key; Finished, key ], Set events) + do! waitUntil events (countOf Finished 3) + let! current, _ = events + let groups = current |> Seq.groupBy snd |> Seq.toList + Assert.Equal(3, groups.Length) + for key, events in groups do + Assert.Equal>(Set [ Requested, key; Started, key; Finished, key ], Set events) + } [] let ``We can cancel a job`` () = task { - let jobStarted = new ManualResetEvent(false) + let jobStarted = new ManualResetEventSlim(false) + let cts = new CancellationTokenSource() + let ctsCancelled = new ManualResetEventSlim(false) - let computation action = async { - action() |> ignore - do! spinFor timeout + let computation = async { + jobStarted.Set() + ctsCancelled.Wait() + do! async { } failwith "Should be canceled before it gets here" } let memoize = AsyncMemoize<_, int, _>() - let events = EventRecorder(memoize) - - use cts1 = new CancellationTokenSource() - use cts2 = new CancellationTokenSource() - use cts3 = new CancellationTokenSource() + let events = observe memoize let key = 1 - let _task1 = Async.StartAsTask( memoize.Get'(key, computation jobStarted.Set), cancellationToken = cts1.Token) - - waitFor jobStarted - jobStarted.Reset() |> ignore + let _task1 = Async.StartAsTask( memoize.Get'(1, computation), cancellationToken = cts.Token) - let _task2 = Async.StartAsTask( memoize.Get'(key, computation ignore), cancellationToken = cts2.Token) - let _task3 = Async.StartAsTask( memoize.Get'(key, computation ignore), cancellationToken = cts3.Token) + jobStarted.Wait() + cts.Cancel() + ctsCancelled.Set() - do! waitUntil (events.CountOf Requested) 3 + do! waitUntil events (received Canceled) + let! current, _ = events - cts1.Cancel() - cts2.Cancel() - - waitFor jobStarted - - cts3.Cancel() - - do! waitUntil events.Received Canceled - - events.ShouldBe [ - Requested, key - Started, key - Requested, key - Requested, key - Restarted, key - Canceled, key - ] + Assert.Equal<_ list>( + [ + Requested, key + Started, key + Canceled, key + ], + current + ) } [] let ``Job is restarted if first requestor cancels`` () = task { - let jobStarted = new ManualResetEvent(false) + let jobStarted = new SemaphoreSlim(0) - let jobCanComplete = new ManualResetEvent(false) + let jobCanComplete = new ManualResetEventSlim(false) let computation key = async { - jobStarted.Set() |> ignore - waitFor jobCanComplete + jobStarted.Release() |> ignore + + jobCanComplete.Wait() return key * 2 } let memoize = AsyncMemoize<_, int, _>() - let events = EventRecorder(memoize) - + let events = observe memoize use cts1 = new CancellationTokenSource() use cts2 = new CancellationTokenSource() @@ -169,48 +153,49 @@ let ``Job is restarted if first requestor cancels`` () = let _task1 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts1.Token) - waitFor jobStarted - jobStarted.Reset() |> ignore - + do! jobStarted.WaitAsync() let _task2 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts2.Token) let _task3 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts3.Token) - do! waitUntil (events.CountOf Requested) 3 + do! waitUntil events (countOf Requested 3) cts1.Cancel() - waitFor jobStarted - jobCanComplete.Set() |> ignore + do! jobStarted.WaitAsync() + let! result = _task2 Assert.Equal(2, result) - events.ShouldBe [ - Requested, key + let! current, _ = events + + Assert.Equal<_ list>( + [ Requested, key Started, key Requested, key Requested, key Restarted, key - Finished, key ] + Finished, key ], + current + ) } [] let ``Job is restarted if first requestor cancels but keeps running if second requestor cancels`` () = task { - let jobStarted = new ManualResetEvent(false) + let jobStarted = new ManualResetEventSlim(false) - let jobCanComplete = new ManualResetEvent(false) + let jobCanComplete = new ManualResetEventSlim(false) let computation key = async { jobStarted.Set() |> ignore - waitFor jobCanComplete + jobCanComplete.Wait() return key * 2 } let memoize = AsyncMemoize<_, int, _>() - let events = EventRecorder(memoize) - + let events = observe memoize use cts1 = new CancellationTokenSource() use cts2 = new CancellationTokenSource() @@ -220,17 +205,17 @@ let ``Job is restarted if first requestor cancels but keeps running if second re let _task1 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts1.Token) - waitFor jobStarted + jobStarted.Wait() jobStarted.Reset() |> ignore let _task2 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts2.Token) let _task3 = Async.StartAsTask( memoize.Get'(key, computation key), cancellationToken = cts3.Token) - do! waitUntil (events.CountOf Requested) 3 + do! waitUntil events (countOf Requested 3) cts1.Cancel() - waitFor jobStarted + jobStarted.Wait() cts2.Cancel() @@ -239,13 +224,17 @@ let ``Job is restarted if first requestor cancels but keeps running if second re let! result = _task3 Assert.Equal(2, result) - events.ShouldBe [ - Requested, key + let! current, _ = events + + Assert.Equal<_ list>( + [ Requested, key Started, key Requested, key Requested, key Restarted, key - Finished, key ] + Finished, key ], + current + ) } @@ -376,59 +365,56 @@ let ``Stress test`` () = [] [] let ``Cancel running jobs with the same key`` cancelDuplicate expectFinished = - task { - let cache = AsyncMemoize(cancelDuplicateRunningJobs=cancelDuplicate) - - let mutable started = 0 - let mutable finished = 0 + let cache = AsyncMemoize(cancelDuplicateRunningJobs=cancelDuplicate) - let job1started = new ManualResetEvent(false) - let job1finished = new ManualResetEvent(false) + let mutable started = 0 + let mutable finished = 0 - let jobCanContinue = new ManualResetEvent(false) + let job1started = new ManualResetEventSlim(false) + let job1finished = new ManualResetEventSlim(false) - let job2started = new ManualResetEvent(false) - let job2finished = new ManualResetEvent(false) + let jobCanContinue = new ManualResetEventSlim(false) - let work onStart onFinish = async { - Interlocked.Increment &started |> ignore - onStart() |> ignore - waitFor jobCanContinue - do! spinFor (TimeSpan.FromMilliseconds 100) - Interlocked.Increment &finished |> ignore - onFinish() |> ignore - } + let job2started = new ManualResetEventSlim(false) + let job2finished = new ManualResetEventSlim(false) - let key1 = - { new ICacheKey<_, _> with - member _.GetKey() = 1 - member _.GetVersion() = 1 - member _.GetLabel() = "key1" } + let work onStart onFinish = async { + Interlocked.Increment &started |> ignore + onStart() |> ignore + jobCanContinue.Wait() + do! Async.Sleep 100 + Interlocked.Increment &finished |> ignore + onFinish() |> ignore + } - cache.Get(key1, work job1started.Set job1finished.Set) |> Async.Start + let key1 = + { new ICacheKey<_, _> with + member _.GetKey() = 1 + member _.GetVersion() = 1 + member _.GetLabel() = "key1" } - waitFor job1started + cache.Get(key1, work job1started.Set job1finished.Set) |> Async.Catch |> Async.Ignore |> Async.Start - let key2 = - { new ICacheKey<_, _> with - member _.GetKey() = key1.GetKey() - member _.GetVersion() = key1.GetVersion() + 1 - member _.GetLabel() = "key2" } + job1started.Wait() - cache.Get(key2, work job2started.Set job2finished.Set ) |> Async.Start + let key2 = + { new ICacheKey<_, _> with + member _.GetKey() = key1.GetKey() + member _.GetVersion() = key1.GetVersion() + 1 + member _.GetLabel() = "key2" } - waitFor job2started + cache.Get(key2, work job2started.Set job2finished.Set ) |> Async.Catch |> Async.Ignore |> Async.Start - jobCanContinue.Set() |> ignore + job2started.Wait() - waitFor job2finished + jobCanContinue.Set() |> ignore - if not cancelDuplicate then - waitFor job1finished - - Assert.Equal((2, expectFinished), (started, finished)) - } + job2finished.Wait() + + if not cancelDuplicate then + job1finished.Wait() + Assert.Equal((2, expectFinished), (started, finished)) type DummyException(msg) = inherit Exception(msg) @@ -490,7 +476,7 @@ let ``Preserve thread static diagnostics`` () = let diagnostics = diagnosticsLogger.GetDiagnostics() - //Assert.Equal(3, diagnostics.Length) + Assert.Equal(4, diagnostics.Length) return result, diagnostics } @@ -498,9 +484,9 @@ let ``Preserve thread static diagnostics`` () = let results = (Task.WhenAll tasks).Result - let _diagnosticCounts = results |> Seq.map snd |> Seq.map Array.length |> Seq.groupBy id |> Seq.map (fun (k, v) -> k, v |> Seq.length) |> Seq.sortBy fst |> Seq.toList + let diagnosticCounts = results |> Seq.map snd |> Seq.map Array.length |> Seq.groupBy id |> Seq.map (fun (k, v) -> k, v |> Seq.length) |> Seq.sortBy fst |> Seq.toList - //Assert.Equal<(int * int) list>([4, 100], diagnosticCounts) + Assert.Equal<(int * int) list>([4, 100], diagnosticCounts) let diagnosticMessages = results |> Seq.map snd |> Seq.map (Array.map (fun (d, _) -> d.Exception.Message) >> Array.toList) |> Set @@ -523,7 +509,7 @@ let ``Preserve thread static diagnostics already completed job`` () = return Ok input } - async { + task { let diagnosticsLogger = CompilationDiagnosticLogger($"Testing", FSharpDiagnosticOptions.Default) @@ -534,10 +520,9 @@ let ``Preserve thread static diagnostics already completed job`` () = let diagnosticMessages = diagnosticsLogger.GetDiagnostics() |> Array.map (fun (d, _) -> d.Exception.Message) |> Array.toList - Assert.Equal>(["job 1 error"; "job 1 error"], diagnosticMessages) + Assert.Equal<_ list>(["job 1 error"; "job 1 error"], diagnosticMessages) } - |> Async.StartAsTask [] @@ -550,34 +535,22 @@ let ``We get diagnostics from the job that failed`` () = member _.GetVersion() = 1 member _.GetLabel() = "job1" } - let job (input: int) = async { - let ex = DummyException($"job {input} error") - do! Async.Sleep 100 - DiagnosticsThreadStatics.DiagnosticsLogger.Error(ex) + let job = async { + let ex = DummyException($"job error") + + // no recovery + DiagnosticsThreadStatics.DiagnosticsLogger.Error ex return 5 } - let result = - [1; 2] - |> Seq.map (fun i -> - async { - let diagnosticsLogger = CompilationDiagnosticLogger($"Testing", FSharpDiagnosticOptions.Default) - - use _ = new CompilationGlobalsScope(diagnosticsLogger, BuildPhase.Optimize) - try - let! _ = cache.Get(key, job i ) - () - with _ -> - () - let diagnosticMessages = diagnosticsLogger.GetDiagnostics() |> Array.map (fun (d, _) -> d.Exception.Message) |> Array.toList - - return diagnosticMessages - }) - |> Async.Parallel - |> Async.StartAsTask - |> (fun t -> t.Result) - |> Array.toList - - Assert.True( - result = [["job 1 error"]; ["job 1 error"]] || - result = [["job 2 error"]; ["job 2 error"]] ) + task { + let logger = CapturingDiagnosticsLogger("AsyncMemoize diagnostics test") + + SetThreadDiagnosticsLoggerNoUnwind logger + + do! cache.Get(key, job ) |> Async.Catch |> Async.Ignore + + let messages = logger.Diagnostics |> List.map fst |> List.map _.Exception.Message + + Assert.Equal<_ list>(["job error"], messages) + } diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/AssemblyVersion04.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/AssemblyVersion04.fs index 2a67d906999..fd876e72abb 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/AssemblyVersion04.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/AttributeUsage/AssemblyVersion04.fs @@ -14,5 +14,5 @@ let success = asm.Version.Major = 1 && asm.Version.Minor = 2 && asm.Version.Build = 3 && - (abs (asm.Version.Revision - (int defaultRevision))) < 10 // default value is seconds in the current day / 2. Check if within 10 sec of that. + (abs (asm.Version.Revision - (int defaultRevision))) < 60 // default value is seconds in the current day / 2. Check if within 60 sec of that. if success then () else failwith "Failed: 1" \ No newline at end of file diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/Events/Basic/Basic.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/Events/Basic/Basic.fs index 87d12baea7c..911891a3320 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/Events/Basic/Basic.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/Events/Basic/Basic.fs @@ -6,6 +6,7 @@ open Xunit open FSharp.Test open FSharp.Test.Compiler +[] module Events = let verifyCompile compilation = diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunctions.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunctions.fs index 9b2ce64194e..5e760a94e73 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunctions.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunctions.fs @@ -5,7 +5,7 @@ open System.IO open FSharp.Test open FSharp.Test.Compiler -module TestFunctions = +module Helpers = let verifyCore compilation = compilation @@ -26,6 +26,10 @@ module TestFunctions = |> verifyCore |> verifyILBaseline +open Helpers + +module TestFunctions1 = + //SOURCE=TestFunction01.fs SCFLAGS="-g --test:EmitFeeFeeAs100001 --optimize-" COMPILE_ONLY=1 POSTCMD="..\\CompareIL.cmd TestFunction01.exe" # TestFunction01.fs [] let ``TestFunction01_fs`` compilation = @@ -116,6 +120,8 @@ module TestFunctions = compilation |> verifyCompilation +module TestFunctions2 = + //SOURCE=TestFunction09b4.fs SCFLAGS="-g --test:EmitFeeFeeAs100001 --optimize-" COMPILE_ONLY=1 POSTCMD="..\\CompareIL.cmd TestFunction09b4.exe" # TestFunction09b4.fs [] let ``TestFunction09b4_RealInternalSignatureOff_fs`` compilation = @@ -196,6 +202,8 @@ module TestFunctions = compilation |> verifyCompilation +module TestFunctions3 = + //SOURCE=TestFunction21.fs SCFLAGS="-g --test:EmitFeeFeeAs100001 --optimize-" COMPILE_ONLY=1 POSTCMD="..\\CompareIL.cmd TestFunction21.exe" # TestFunction21.fs - [] let ``TestFunction21_fs`` compilation = @@ -293,6 +301,8 @@ module TestFunctions = |> withRealInternalSignatureOff |> verifyCompilation +module TestFunctions4 = + //SOURCE=TestFunction22g.fs SCFLAGS="-g --test:EmitFeeFeeAs100001 --optimize-" COMPILE_ONLY=1 POSTCMD="..\\CompareIL.cmd TestFunction22g.exe" # TestFunction22g.fs [] let ``TestFunction22g_RealInternalSignatureOn_fs`` compilation = diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index 4b50b28aa12..550aabfe8df 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -31,6 +31,9 @@ FsUnit.fs + + XunitSetup.fs + @@ -287,6 +290,7 @@ + diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs index 0395a421895..8bc9ac09f05 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/TransparentCompiler.fs @@ -13,6 +13,7 @@ open FSharp.Compiler.Diagnostics open Xunit +open FSharp.Test open FSharp.Test.ProjectGeneration open FSharp.Test.ProjectGeneration.Helpers open System.IO @@ -1064,67 +1065,71 @@ type private LoadClosureTestShim(currentFileSystem: IFileSystem) = ?shouldShadowCopy = shouldShadowCopy ) -[] -[] -[] -let ``The script load closure should always be evaluated`` useTransparentCompiler = - async { - // The LoadScriptClosure uses the file system shim so we need to reset that. - let currentFileSystem = FileSystemAutoOpens.FileSystem - let assumeDotNetFramework = - // The old BackgroundCompiler uses assumeDotNetFramework = true - // This is not always correctly loading when this test runs on non-Windows. - if System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework") then - None - else - Some false +// Because it is mutating FileSystem! +[] +module TestsMutatingFileSystem = + + [] + [] + [] + let ``The script load closure should always be evaluated`` useTransparentCompiler = + async { + // The LoadScriptClosure uses the file system shim so we need to reset that. + let currentFileSystem = FileSystemAutoOpens.FileSystem + let assumeDotNetFramework = + // The old BackgroundCompiler uses assumeDotNetFramework = true + // This is not always correctly loading when this test runs on non-Windows. + if System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework") then + None + else + Some false - try - let checker = FSharpChecker.Create(useTransparentCompiler = useTransparentCompiler) - let fileSystemShim = LoadClosureTestShim(currentFileSystem) - // Override the file system shim for loading b.fsx - FileSystem <- fileSystemShim - - let! initialSnapshot, _ = - checker.GetProjectSnapshotFromScript( - "a.fsx", - SourceTextNew.ofString fileSystemShim.aFsx, - documentSource = DocumentSource.Custom fileSystemShim.DocumentSource, - ?assumeDotNetFramework = assumeDotNetFramework - ) - - // File b.fsx should also be included in the snapshot. - Assert.Equal(2, initialSnapshot.SourceFiles.Length) - - let! checkResults = checker.ParseAndCheckFileInProject("a.fsx", initialSnapshot) - - match snd checkResults with - | FSharpCheckFileAnswer.Aborted -> failwith "Did not expected FSharpCheckFileAnswer.Aborted" - | FSharpCheckFileAnswer.Succeeded checkFileResults -> Assert.Equal(0, checkFileResults.Diagnostics.Length) + try + let checker = FSharpChecker.Create(useTransparentCompiler = useTransparentCompiler) + let fileSystemShim = LoadClosureTestShim(currentFileSystem) + // Override the file system shim for loading b.fsx + FileSystem <- fileSystemShim + + let! initialSnapshot, _ = + checker.GetProjectSnapshotFromScript( + "a.fsx", + SourceTextNew.ofString fileSystemShim.aFsx, + documentSource = DocumentSource.Custom fileSystemShim.DocumentSource, + ?assumeDotNetFramework = assumeDotNetFramework + ) + + // File b.fsx should also be included in the snapshot. + Assert.Equal(2, initialSnapshot.SourceFiles.Length) + + let! checkResults = checker.ParseAndCheckFileInProject("a.fsx", initialSnapshot) + + match snd checkResults with + | FSharpCheckFileAnswer.Aborted -> failwith "Did not expected FSharpCheckFileAnswer.Aborted" + | FSharpCheckFileAnswer.Succeeded checkFileResults -> Assert.Equal(0, checkFileResults.Diagnostics.Length) - // Update b.fsx, it should now load c.fsx - fileSystemShim.UpdateB() - - // The constructed key for the load closure will the exactly the same as the first GetProjectSnapshotFromScript call. - // However, a none cached version will be computed first in GetProjectSnapshotFromScript and update the cache afterwards. - let! secondSnapshot, _ = - checker.GetProjectSnapshotFromScript( - "a.fsx", - SourceTextNew.ofString fileSystemShim.aFsx, - documentSource = DocumentSource.Custom fileSystemShim.DocumentSource, - ?assumeDotNetFramework = assumeDotNetFramework - ) - - Assert.Equal(3, secondSnapshot.SourceFiles.Length) - - let! checkResults = checker.ParseAndCheckFileInProject("a.fsx", secondSnapshot) - - match snd checkResults with - | FSharpCheckFileAnswer.Aborted -> failwith "Did not expected FSharpCheckFileAnswer.Aborted" - | FSharpCheckFileAnswer.Succeeded checkFileResults -> Assert.Equal(0, checkFileResults.Diagnostics.Length) - finally - FileSystemAutoOpens.FileSystem <- currentFileSystem - } + // Update b.fsx, it should now load c.fsx + fileSystemShim.UpdateB() + + // The constructed key for the load closure will the exactly the same as the first GetProjectSnapshotFromScript call. + // However, a none cached version will be computed first in GetProjectSnapshotFromScript and update the cache afterwards. + let! secondSnapshot, _ = + checker.GetProjectSnapshotFromScript( + "a.fsx", + SourceTextNew.ofString fileSystemShim.aFsx, + documentSource = DocumentSource.Custom fileSystemShim.DocumentSource, + ?assumeDotNetFramework = assumeDotNetFramework + ) + + Assert.Equal(3, secondSnapshot.SourceFiles.Length) + + let! checkResults = checker.ParseAndCheckFileInProject("a.fsx", secondSnapshot) + + match snd checkResults with + | FSharpCheckFileAnswer.Aborted -> failwith "Did not expected FSharpCheckFileAnswer.Aborted" + | FSharpCheckFileAnswer.Succeeded checkFileResults -> Assert.Equal(0, checkFileResults.Diagnostics.Length) + finally + FileSystemAutoOpens.FileSystem <- currentFileSystem + } [] let ``Parsing without cache and without project snapshot`` () = diff --git a/tests/FSharp.Compiler.ComponentTests/Miscellaneous/FsharpSuiteMigrated.fs b/tests/FSharp.Compiler.ComponentTests/Miscellaneous/FsharpSuiteMigrated.fs index 3f17e0cfa11..35ff5c1837b 100644 --- a/tests/FSharp.Compiler.ComponentTests/Miscellaneous/FsharpSuiteMigrated.fs +++ b/tests/FSharp.Compiler.ComponentTests/Miscellaneous/FsharpSuiteMigrated.fs @@ -36,7 +36,8 @@ module ScriptRunner = match res with | CompilationResult.Failure _ -> res | CompilationResult.Success s -> - if engine.GetOutput().Contains "TEST PASSED OK" then + + if engine.GetOutput() |> TestFramework.outputPassed then res else failwith $"Results looked correct, but 'TEST PASSED OK' was not printed. Result: %A{s}" diff --git a/tests/FSharp.Compiler.ComponentTests/Miscellaneous/MigratedCoreTests.fs b/tests/FSharp.Compiler.ComponentTests/Miscellaneous/MigratedCoreTests.fs index 2d9c6657901..852e092e5e1 100644 --- a/tests/FSharp.Compiler.ComponentTests/Miscellaneous/MigratedCoreTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Miscellaneous/MigratedCoreTests.fs @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -module Miscellaneous.FsharpSuiteMigrated_CoreTests +namespace Miscellaneous.FsharpSuiteMigrated_CoreTests open Xunit open FSharp.Test @@ -8,458 +8,477 @@ open FSharp.Test.ScriptHelpers open System.Runtime.InteropServices open Miscellaneous.FsharpSuiteMigrated.TestFrameworkAdapter -// These tests are enabled for .NET Framework and .NET Core -[] -let ``access-FSC_DEBUG``() = singleTestBuildAndRun "core/access" FSC_DEBUG +module Tests1 = -[] -let ``access-FSC_OPTIMIZED``() = singleTestBuildAndRun "core/access" FSC_OPTIMIZED + // These tests are enabled for .NET Framework and .NET Core + [] + let ``access-FSC_DEBUG``() = singleTestBuildAndRun "core/access" FSC_DEBUG -[] -let ``access-FSI``() = singleTestBuildAndRun "core/access" FSI + [] + let ``access-FSC_OPTIMIZED``() = singleTestBuildAndRun "core/access" FSC_OPTIMIZED -[] -let ``apporder-FSC_DEBUG`` () = singleTestBuildAndRun "core/apporder" FSC_DEBUG + [] + let ``access-FSI``() = singleTestBuildAndRun "core/access" FSI -[] -let ``apporder-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/apporder" FSC_OPTIMIZED + [] + let ``apporder-FSC_DEBUG`` () = singleTestBuildAndRun "core/apporder" FSC_DEBUG -[] -let ``apporder-FSI`` () = singleTestBuildAndRun "core/apporder" FSI + [] + let ``apporder-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/apporder" FSC_OPTIMIZED -[] -let ``array-FSC_DEBUG-5_0`` () = singleTestBuildAndRunVersion "core/array" FSC_DEBUG LangVersion.V50 + [] + let ``apporder-FSI`` () = singleTestBuildAndRun "core/apporder" FSI -[] -let ``array-FSC_OPTIMIZED-5_0`` () = singleTestBuildAndRunVersion "core/array" FSC_OPTIMIZED LangVersion.V50 + [] + let ``array-FSC_DEBUG-5_0`` () = singleTestBuildAndRunVersion "core/array" FSC_DEBUG LangVersion.V50 -[] -let ``array-FSI-5_0`` () = singleTestBuildAndRunVersion "core/array" FSI LangVersion.V50 + [] + let ``array-FSC_OPTIMIZED-5_0`` () = singleTestBuildAndRunVersion "core/array" FSC_OPTIMIZED LangVersion.V50 -[] -let ``array-FSC_OPTIMIZED-preview`` () = singleTestBuildAndRunVersion "core/array" FSC_OPTIMIZED LangVersion.Preview + [] + let ``array-FSI-5_0`` () = singleTestBuildAndRunVersion "core/array" FSI LangVersion.V50 -[] -let ``array-no-dot-FSC_DEBUG`` () = singleTestBuildAndRunVersion "core/array-no-dot" FSC_DEBUG LangVersion.Preview + [] + let ``array-FSC_OPTIMIZED-preview`` () = singleTestBuildAndRunVersion "core/array" FSC_OPTIMIZED LangVersion.Preview -[] -let ``array-no-dot-FSC_OPTIMIZED`` () = singleTestBuildAndRunVersion "core/array-no-dot" FSC_OPTIMIZED LangVersion.Preview + [] + let ``array-no-dot-FSC_DEBUG`` () = singleTestBuildAndRunVersion "core/array-no-dot" FSC_DEBUG LangVersion.Preview -[] -let ``array-no-dot-FSI`` () = singleTestBuildAndRunVersion "core/array-no-dot" FSI LangVersion.Preview + [] + let ``array-no-dot-FSC_OPTIMIZED`` () = singleTestBuildAndRunVersion "core/array-no-dot" FSC_OPTIMIZED LangVersion.Preview -[] -let ``array-no-dot-warnings-langversion-default`` () = - singleVersionedNegTest "core/array-no-dot-warnings" LangVersion.Latest "test-langversion-default" + [] + let ``array-no-dot-FSI`` () = singleTestBuildAndRunVersion "core/array-no-dot" FSI LangVersion.Preview -[] -let ``array-no-dot-warnings-langversion-5_0`` () = - singleVersionedNegTest "core/array-no-dot-warnings" LangVersion.V50 "test-langversion-5.0" + [] + let ``array-no-dot-warnings-langversion-default`` () = + singleVersionedNegTest "core/array-no-dot-warnings" LangVersion.Latest "test-langversion-default" -[] -let ``ref-ops-deprecation-langversion-preview`` () = - singleVersionedNegTest "core/ref-ops-deprecation" LangVersion.Preview "test-langversion-preview" + [] + let ``array-no-dot-warnings-langversion-5_0`` () = + singleVersionedNegTest "core/array-no-dot-warnings" LangVersion.V50 "test-langversion-5.0" -[] -let ``auto-widen-version-5_0``() = - singleVersionedNegTest "core/auto-widen/5.0" LangVersion.V50 "test" + [] + let ``ref-ops-deprecation-langversion-preview`` () = + singleVersionedNegTest "core/ref-ops-deprecation" LangVersion.Preview "test-langversion-preview" -[] -let ``auto-widen-version-FSC_DEBUG-preview``() = - singleTestBuildAndRunVersion "core/auto-widen/preview" FSC_DEBUG LangVersion.Preview + [] + let ``auto-widen-version-5_0``() = + singleVersionedNegTest "core/auto-widen/5.0" LangVersion.V50 "test" -[] -let ``auto-widen-version-FSC_OPTIMIZED-preview``() = - singleTestBuildAndRunVersion "core/auto-widen/preview" FSC_OPTIMIZED LangVersion.Preview + [] + let ``auto-widen-version-FSC_DEBUG-preview``() = + singleTestBuildAndRunVersion "core/auto-widen/preview" FSC_DEBUG LangVersion.Preview -[] -let ``auto-widen-minimal``() = - singleTestBuildAndRunVersion "core/auto-widen/minimal" FSC_OPTIMIZED LangVersion.V70 + [] + let ``auto-widen-version-FSC_OPTIMIZED-preview``() = + singleTestBuildAndRunVersion "core/auto-widen/preview" FSC_OPTIMIZED LangVersion.Preview -[] -let ``auto-widen-version-preview-warns-on``() = - singleVersionedNegTestAux "core/auto-widen/preview" ["--warnon:3388";"--warnon:3389";"--warnon:3395";"--warnaserror+";"--define:NEGATIVE"] LangVersion.V80 "test" + [] + let ``auto-widen-minimal``() = + singleTestBuildAndRunVersion "core/auto-widen/minimal" FSC_OPTIMIZED LangVersion.V70 -[] -let ``auto-widen-version-preview-default-warns``() = - singleVersionedNegTestAux "core/auto-widen/preview-default-warns" ["--warnaserror+";"--define:NEGATIVE"] LangVersion.V80 "test" + [] + let ``auto-widen-version-preview-warns-on``() = + singleVersionedNegTestAux "core/auto-widen/preview" ["--warnon:3388";"--warnon:3389";"--warnon:3395";"--warnaserror+";"--define:NEGATIVE"] LangVersion.V80 "test" -[] -let ``comprehensions-FSC_DEBUG`` () = singleTestBuildAndRun "core/comprehensions" FSC_DEBUG + [] + let ``auto-widen-version-preview-default-warns``() = + singleVersionedNegTestAux "core/auto-widen/preview-default-warns" ["--warnaserror+";"--define:NEGATIVE"] LangVersion.V80 "test" -[] -let ``comprehensions-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/comprehensions" FSC_OPTIMIZED +module Tests2 = -[] -let ``comprehensions-FSI`` () = singleTestBuildAndRun "core/comprehensions" FSI + [] + let ``comprehensions-FSC_DEBUG`` () = singleTestBuildAndRun "core/comprehensions" FSC_DEBUG -[] -let ``comprehensionshw-FSC_DEBUG`` () = singleTestBuildAndRun "core/comprehensions-hw" FSC_DEBUG + [] + let ``comprehensions-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/comprehensions" FSC_OPTIMIZED -[] -let ``comprehensionshw-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/comprehensions-hw" FSC_OPTIMIZED + [] + let ``comprehensions-FSI`` () = singleTestBuildAndRun "core/comprehensions" FSI -[] -let ``comprehensionshw-FSI`` () = singleTestBuildAndRun "core/comprehensions-hw" FSI + [] + let ``comprehensionshw-FSC_DEBUG`` () = singleTestBuildAndRun "core/comprehensions-hw" FSC_DEBUG -[] -let ``genericmeasures-FSC_DEBUG`` () = singleTestBuildAndRun "core/genericmeasures" FSC_DEBUG + [] + let ``comprehensionshw-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/comprehensions-hw" FSC_OPTIMIZED -[] -let ``genericmeasures-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/genericmeasures" FSC_OPTIMIZED + [] + let ``comprehensionshw-FSI`` () = singleTestBuildAndRun "core/comprehensions-hw" FSI -[] -let ``genericmeasures-FSI`` () = singleTestBuildAndRun "core/genericmeasures" FSI + [] + let ``genericmeasures-FSC_DEBUG`` () = singleTestBuildAndRun "core/genericmeasures" FSC_DEBUG -[] -let ``innerpoly-FSC_DEBUG`` () = singleTestBuildAndRun "core/innerpoly" FSC_DEBUG + [] + let ``genericmeasures-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/genericmeasures" FSC_OPTIMIZED -[] -let ``innerpoly-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/innerpoly" FSC_OPTIMIZED + [] + let ``genericmeasures-FSI`` () = singleTestBuildAndRun "core/genericmeasures" FSI -[] -let ``innerpoly-FSI`` () = singleTestBuildAndRun "core/innerpoly" FSI + [] + let ``innerpoly-FSC_DEBUG`` () = singleTestBuildAndRun "core/innerpoly" FSC_DEBUG -[] -let ``namespaceAttributes-FSC_DEBUG`` () = singleTestBuildAndRunVersion "core/namespaces" COMPILED_EXE_APP LangVersion.Preview + [] + let ``innerpoly-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/innerpoly" FSC_OPTIMIZED -[] -let ``namespaceAttributes-FSC_OPTIMIZED`` () = singleTestBuildAndRunVersion "core/namespaces" COMPILED_EXE_APP LangVersion.Preview + [] + let ``innerpoly-FSI`` () = singleTestBuildAndRun "core/innerpoly" FSI -[] -let ``unicode2-FSC_DEBUG`` () = singleTestBuildAndRun "core/unicode" FSC_DEBUG // TODO: fails on coreclr + [] + let ``namespaceAttributes-FSC_DEBUG`` () = singleTestBuildAndRunVersion "core/namespaces" COMPILED_EXE_APP LangVersion.Preview -[] -let ``unicode2-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/unicode" FSC_OPTIMIZED // TODO: fails on coreclr + [] + let ``namespaceAttributes-FSC_OPTIMIZED`` () = singleTestBuildAndRunVersion "core/namespaces" COMPILED_EXE_APP LangVersion.Preview -[] -let ``unicode2-FSI`` () = singleTestBuildAndRun "core/unicode" FSI + [] + let ``unicode2-FSC_DEBUG`` () = singleTestBuildAndRun "core/unicode" FSC_DEBUG // TODO: fails on coreclr -[] -let ``lazy test-FSC_DEBUG`` () = singleTestBuildAndRun "core/lazy" FSC_DEBUG + [] + let ``unicode2-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/unicode" FSC_OPTIMIZED // TODO: fails on coreclr -[] -let ``lazy test-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/lazy" FSC_OPTIMIZED + [] + let ``unicode2-FSI`` () = singleTestBuildAndRun "core/unicode" FSI -[] -let ``lazy test-FSI`` () = singleTestBuildAndRun "core/lazy" FSI + [] + let ``lazy test-FSC_DEBUG`` () = singleTestBuildAndRun "core/lazy" FSC_DEBUG -[] -let ``letrec-FSC_DEBUG`` () = singleTestBuildAndRun "core/letrec" FSC_DEBUG + [] + let ``lazy test-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/lazy" FSC_OPTIMIZED -[] -let ``letrec-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/letrec" FSC_OPTIMIZED + [] + let ``lazy test-FSI`` () = singleTestBuildAndRun "core/lazy" FSI -[] -let ``letrec-FSI`` () = singleTestBuildAndRun "core/letrec" FSI + [] + let ``letrec-FSC_DEBUG`` () = singleTestBuildAndRun "core/letrec" FSC_DEBUG -[] -let ``letrec (mutrec variations part one) FSC_DEBUG`` () = singleTestBuildAndRun "core/letrec-mutrec" FSC_DEBUG + [] + let ``letrec-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/letrec" FSC_OPTIMIZED -[] -let ``letrec (mutrec variations part one) FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/letrec-mutrec" FSC_OPTIMIZED + [] + let ``letrec-FSI`` () = singleTestBuildAndRun "core/letrec" FSI -[] -let ``letrec (mutrec variations part one) FSI`` () = singleTestBuildAndRun "core/letrec-mutrec" FSI + [] + let ``letrec (mutrec variations part one) FSC_DEBUG`` () = singleTestBuildAndRun "core/letrec-mutrec" FSC_DEBUG -[] -let ``libtest-FSC_DEBUG`` () = singleTestBuildAndRun "core/libtest" FSC_DEBUG + [] + let ``letrec (mutrec variations part one) FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/letrec-mutrec" FSC_OPTIMIZED -[] -let ``libtest-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/libtest" FSC_OPTIMIZED + [] + let ``letrec (mutrec variations part one) FSI`` () = singleTestBuildAndRun "core/letrec-mutrec" FSI -[] -let ``libtest-FSI`` () = singleTestBuildAndRun "core/libtest" FSI +module Tests3 = -[] -let ``lift-FSC_DEBUG`` () = singleTestBuildAndRun "core/lift" FSC_DEBUG + [] + let ``libtest-FSC_DEBUG`` () = singleTestBuildAndRun "core/libtest" FSC_DEBUG -[] -let ``lift-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/lift" FSC_OPTIMIZED + [] + let ``libtest-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/libtest" FSC_OPTIMIZED -[] -let ``lift-FSI`` () = singleTestBuildAndRun "core/lift" FSI + [] + let ``libtest-FSI`` () = singleTestBuildAndRun "core/libtest" FSI -[] -let ``map-FSC_DEBUG`` () = singleTestBuildAndRun "core/map" FSC_DEBUG + [] + let ``lift-FSC_DEBUG`` () = singleTestBuildAndRun "core/lift" FSC_DEBUG -[] -let ``map-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/map" FSC_OPTIMIZED + [] + let ``lift-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/lift" FSC_OPTIMIZED -[] -let ``map-FSI`` () = singleTestBuildAndRun "core/map" FSI + [] + let ``lift-FSI`` () = singleTestBuildAndRun "core/lift" FSI -[] -let ``measures-FSC_DEBUG`` () = singleTestBuildAndRun "core/measures" FSC_DEBUG + [] + let ``map-FSC_DEBUG`` () = singleTestBuildAndRun "core/map" FSC_DEBUG -[] -let ``measures-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/measures" FSC_OPTIMIZED + [] + let ``map-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/map" FSC_OPTIMIZED -[] -let ``measures-FSI`` () = singleTestBuildAndRun "core/measures" FSI + [] + let ``map-FSI`` () = singleTestBuildAndRun "core/map" FSI -[] -let ``nested-FSC_DEBUG`` () = singleTestBuildAndRun "core/nested" FSC_DEBUG + [] + let ``measures-FSC_DEBUG`` () = singleTestBuildAndRun "core/measures" FSC_DEBUG -[] -let ``nested-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/nested" FSC_OPTIMIZED + [] + let ``measures-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/measures" FSC_OPTIMIZED -[] -let ``nested-FSI`` () = singleTestBuildAndRun "core/nested" FSI + [] + let ``measures-FSI`` () = singleTestBuildAndRun "core/measures" FSI -[] -let ``members-basics-hw`` () = singleTestBuildAndRun "core/members/basics-hw" FSC_OPTIMIZED + [] + let ``nested-FSC_DEBUG`` () = singleTestBuildAndRun "core/nested" FSC_DEBUG -[] -let ``members-basics-hw-mutrec-realinternalsignature`` () = singleTestBuildAndRun "core/members/basics-hw-mutrec" FSC_DEBUG//OPTIMIZED + [] + let ``nested-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/nested" FSC_OPTIMIZED -[] -let ``members-incremental-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/members/incremental" FSC_OPTIMIZED + [] + let ``nested-FSI`` () = singleTestBuildAndRun "core/nested" FSI -[] -let ``members-incremental-FSI`` () = singleTestBuildAndRun "core/members/incremental" FSI + [] + let ``members-basics-hw`` () = singleTestBuildAndRun "core/members/basics-hw" FSC_OPTIMIZED -[] -let ``members-incremental-hw-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/members/incremental-hw" FSC_OPTIMIZED + [] + let ``members-basics-hw-mutrec-realinternalsignature`` () = singleTestBuildAndRun "core/members/basics-hw-mutrec" FSC_DEBUG//OPTIMIZED -[] -let ``members-incremental-hw-FSI`` () = singleTestBuildAndRun "core/members/incremental-hw" FSI + [] + let ``members-incremental-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/members/incremental" FSC_OPTIMIZED -[] -let ``members-incremental-hw-mutrec-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/members/incremental-hw-mutrec" FSC_OPTIMIZED + [] + let ``members-incremental-FSI`` () = singleTestBuildAndRun "core/members/incremental" FSI -[] -let ``members-ops-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/members/ops" FSC_OPTIMIZED + [] + let ``members-incremental-hw-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/members/incremental-hw" FSC_OPTIMIZED -[] -let ``members-ops-FSC_DEBUG`` () = singleTestBuildAndRun "core/members/ops" FSC_DEBUG + [] + let ``members-incremental-hw-FSI`` () = singleTestBuildAndRun "core/members/incremental-hw" FSI -[] -let ``members-ops-FSI`` () = singleTestBuildAndRun "core/members/ops" FSI + [] + let ``members-incremental-hw-mutrec-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/members/incremental-hw-mutrec" FSC_OPTIMIZED -[] -let ``members-ops-mutrec-FSC_DEBUG`` () = singleTestBuildAndRun "core/members/ops-mutrec" FSC_DEBUG + [] + let ``members-ops-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/members/ops" FSC_OPTIMIZED -[] -let ``members-ops-mutrec-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/members/ops-mutrec" FSC_OPTIMIZED + [] + let ``members-ops-FSC_DEBUG`` () = singleTestBuildAndRun "core/members/ops" FSC_DEBUG -[] -let ``members-ops-mutrec-FSI`` () = singleTestBuildAndRun "core/members/ops-mutrec" FSI + [] + let ``members-ops-FSI`` () = singleTestBuildAndRun "core/members/ops" FSI -[] -let ``seq-FSC_DEBUG`` () = singleTestBuildAndRun "core/seq" FSC_DEBUG + [] + let ``members-ops-mutrec-FSC_DEBUG`` () = singleTestBuildAndRun "core/members/ops-mutrec" FSC_DEBUG -[] -let ``seq-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/seq" FSC_OPTIMIZED + [] + let ``members-ops-mutrec-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/members/ops-mutrec" FSC_OPTIMIZED -[] -let ``seq-FSI`` () = singleTestBuildAndRun "core/seq" FSI + [] + let ``members-ops-mutrec-FSI`` () = singleTestBuildAndRun "core/members/ops-mutrec" FSI -[] -let ``math-numbers-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/math/numbers" FSC_OPTIMIZED +module Tests4 = -[] -let ``math-numbers-FSI`` () = singleTestBuildAndRun "core/math/numbers" FSI + [] + let ``seq-FSC_DEBUG`` () = singleTestBuildAndRun "core/seq" FSC_DEBUG -[] -let ``members-ctree-FSC_DEBUG`` () = singleTestBuildAndRun "core/members/ctree" FSC_DEBUG + [] + let ``seq-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/seq" FSC_OPTIMIZED -[] -let ``members-ctree-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/members/ctree" FSC_OPTIMIZED + [] + let ``seq-FSI`` () = singleTestBuildAndRun "core/seq" FSI -[] -let ``members-ctree-FSI`` () = singleTestBuildAndRun "core/members/ctree" FSI + [] + let ``math-numbers-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/math/numbers" FSC_OPTIMIZED -[] -let ``members-factors-FSC_DEBUG`` () = singleTestBuildAndRun "core/members/factors" FSC_DEBUG + [] + let ``math-numbers-FSI`` () = singleTestBuildAndRun "core/math/numbers" FSI -[] -let ``members-factors-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/members/factors" FSC_OPTIMIZED + [] + let ``members-ctree-FSC_DEBUG`` () = singleTestBuildAndRun "core/members/ctree" FSC_DEBUG -[] -let ``members-factors-FSI`` () = singleTestBuildAndRun "core/members/factors" FSI + [] + let ``members-ctree-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/members/ctree" FSC_OPTIMIZED -[] -let ``members-factors-mutrec-FSC_DEBUG`` () = singleTestBuildAndRun "core/members/factors-mutrec" FSC_DEBUG + [] + let ``members-ctree-FSI`` () = singleTestBuildAndRun "core/members/ctree" FSI -[] -let ``members-factors-mutrec-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/members/factors-mutrec" FSC_OPTIMIZED + [] + let ``members-factors-FSC_DEBUG`` () = singleTestBuildAndRun "core/members/factors" FSC_DEBUG -[] -let ``members-factors-mutrec-FSI`` () = singleTestBuildAndRun "core/members/factors-mutrec" FSI + [] + let ``members-factors-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/members/factors" FSC_OPTIMIZED -[] -let ``graph-FSC_DEBUG`` () = singleTestBuildAndRunVersion "perf/graph" FSC_DEBUG LangVersion.SupportsMl + [] + let ``members-factors-FSI`` () = singleTestBuildAndRun "core/members/factors" FSI -[] -let ``graph-FSC_OPTIMIZED`` () = singleTestBuildAndRunVersion "perf/graph" FSC_OPTIMIZED LangVersion.SupportsMl + [] + let ``members-factors-mutrec-FSC_DEBUG`` () = singleTestBuildAndRun "core/members/factors-mutrec" FSC_DEBUG -[] -let ``graph-FSI`` () = singleTestBuildAndRunVersion "perf/graph" FSI LangVersion.SupportsMl + [] + let ``members-factors-mutrec-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/members/factors-mutrec" FSC_OPTIMIZED -[] -let ``nbody-FSC_DEBUG`` () = singleTestBuildAndRunVersion "perf/nbody" FSC_DEBUG LangVersion.SupportsMl + [] + let ``members-factors-mutrec-FSI`` () = singleTestBuildAndRun "core/members/factors-mutrec" FSI -[] -let ``nbody-FSC_OPTIMIZED`` () = singleTestBuildAndRunVersion "perf/nbody" FSC_OPTIMIZED LangVersion.SupportsMl + [] + let ``graph-FSC_DEBUG`` () = singleTestBuildAndRunVersion "perf/graph" FSC_DEBUG LangVersion.SupportsMl -[] -let ``nbody-FSI`` () = singleTestBuildAndRunVersion "perf/nbody" FSI LangVersion.SupportsMl + [] + let ``graph-FSC_OPTIMIZED`` () = singleTestBuildAndRunVersion "perf/graph" FSC_OPTIMIZED LangVersion.SupportsMl -[] -let ``forexpression-FSC_DEBUG`` () = singleTestBuildAndRun "core/forexpression" FSC_DEBUG + [] + let ``graph-FSI`` () = singleTestBuildAndRunVersion "perf/graph" FSI LangVersion.SupportsMl -[] -let ``forexpression-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/forexpression" FSC_OPTIMIZED + [] + let ``nbody-FSC_DEBUG`` () = singleTestBuildAndRunVersion "perf/nbody" FSC_DEBUG LangVersion.SupportsMl -[] -let ``forexpression-FSI`` () = singleTestBuildAndRun "core/forexpression" FSI + [] + let ``nbody-FSC_OPTIMIZED`` () = singleTestBuildAndRunVersion "perf/nbody" FSC_OPTIMIZED LangVersion.SupportsMl -[] -let ``letrec (mutrec variations part two) FSC_DEBUG`` () = singleTestBuildAndRun "core/letrec-mutrec2" FSC_DEBUG + [] + let ``nbody-FSI`` () = singleTestBuildAndRunVersion "perf/nbody" FSI LangVersion.SupportsMl -[] -let ``letrec (mutrec variations part two) FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/letrec-mutrec2" FSC_OPTIMIZED +module Tests5 = -[] -let ``letrec (mutrec variations part two) FSI`` () = singleTestBuildAndRun "core/letrec-mutrec2" FSI + [] + let ``forexpression-FSC_DEBUG`` () = singleTestBuildAndRun "core/forexpression" FSC_DEBUG -[] -let ``printf`` () = singleTestBuildAndRunVersion "core/printf" FSC_OPTIMIZED LangVersion.Preview + [] + let ``forexpression-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/forexpression" FSC_OPTIMIZED -[] -let ``printf-interpolated`` () = singleTestBuildAndRunVersion "core/printf-interpolated" FSC_OPTIMIZED LangVersion.Preview + [] + let ``forexpression-FSI`` () = singleTestBuildAndRun "core/forexpression" FSI -[] -let ``tlr-FSC_DEBUG`` () = singleTestBuildAndRun "core/tlr" FSC_DEBUG + [] + let ``letrec (mutrec variations part two) FSC_DEBUG`` () = singleTestBuildAndRun "core/letrec-mutrec2" FSC_DEBUG -[] -let ``tlr-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/tlr" FSC_OPTIMIZED + [] + let ``letrec (mutrec variations part two) FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/letrec-mutrec2" FSC_OPTIMIZED -[] -let ``tlr-FSI`` () = singleTestBuildAndRun "core/tlr" FSI + [] + let ``letrec (mutrec variations part two) FSI`` () = singleTestBuildAndRun "core/letrec-mutrec2" FSI -[] -let ``subtype-FSC_DEBUG`` () = singleTestBuildAndRun "core/subtype" FSC_DEBUG + [] + let ``printf`` () = singleTestBuildAndRunVersion "core/printf" FSC_OPTIMIZED LangVersion.Preview -[] -let ``subtype-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/subtype" FSC_OPTIMIZED + [] + let ``printf-interpolated`` () = singleTestBuildAndRunVersion "core/printf-interpolated" FSC_OPTIMIZED LangVersion.Preview -[] -let ``subtype-FSI`` () = singleTestBuildAndRun "core/subtype" FSI + [] + let ``tlr-FSC_DEBUG`` () = singleTestBuildAndRun "core/tlr" FSC_DEBUG -[] -let ``syntax-FSC_DEBUG`` () = singleTestBuildAndRun "core/syntax" FSC_DEBUG + [] + let ``tlr-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/tlr" FSC_OPTIMIZED -[] -let ``syntax-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/syntax" FSC_OPTIMIZED + [] + let ``tlr-FSI`` () = singleTestBuildAndRun "core/tlr" FSI -[] -let ``syntax-FSI`` () = singleTestBuildAndRun "core/syntax" FSI + [] + let ``subtype-FSC_DEBUG`` () = singleTestBuildAndRun "core/subtype" FSC_DEBUG -[] -let ``test int32-FSC_DEBUG`` () = singleTestBuildAndRun "core/int32" FSC_DEBUG + [] + let ``subtype-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/subtype" FSC_OPTIMIZED -[] -let ``test int32-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/int32" FSC_OPTIMIZED + [] + let ``subtype-FSI`` () = singleTestBuildAndRun "core/subtype" FSI -[] -let ``test int32-FSI`` () = singleTestBuildAndRun "core/int32" FSI + [] + let ``syntax-FSC_DEBUG`` () = singleTestBuildAndRun "core/syntax" FSC_DEBUG -[] -let ``recordResolution-FSC_DEBUG`` () = singleTestBuildAndRun "core/recordResolution" FSC_DEBUG + [] + let ``syntax-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/syntax" FSC_OPTIMIZED -[] -let ``recordResolution-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/recordResolution" FSC_OPTIMIZED + [] + let ``syntax-FSI`` () = singleTestBuildAndRun "core/syntax" FSI -[] -let ``recordResolution-FSI`` () = singleTestBuildAndRun "core/recordResolution" FSI + [] + let ``test int32-FSC_DEBUG`` () = singleTestBuildAndRun "core/int32" FSC_DEBUG -// This test has hardcoded expectations about current synchronization context -// Will be moved out of FsharpSuite.Tests in a later phase for desktop framework -[] -let ``control-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/control" FSC_OPTIMIZED + [] + let ``test int32-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/int32" FSC_OPTIMIZED -[] -let ``control-FSI`` () = singleTestBuildAndRun "core/control" FSI + [] + let ``test int32-FSI`` () = singleTestBuildAndRun "core/int32" FSI -[] -let ``control --tailcalls`` () = - let cfg = "core/control" - singleTestBuildAndRunAux cfg ["--tailcalls"] FSC_OPTIMIZED -[] -let ``controlChamenos-FSC_OPTIMIZED`` () = - let cfg = "core/controlChamenos" - singleTestBuildAndRunAux cfg ["--tailcalls"] FSC_OPTIMIZED +[] +module CancelsDefaultToken = + // This test has hardcoded expectations about current synchronization context + // Will be moved out of FsharpSuite.Tests in a later phase for desktop framework + [] + let ``control-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/control" FSC_OPTIMIZED -[] -let ``controlChamenos-FSI`` () = - let cfg = "core/controlChamenos" - singleTestBuildAndRunAux cfg ["--tailcalls"] FSI + [] + let ``control-FSI`` () = singleTestBuildAndRun "core/control" FSI -[] -let ``controlMailbox-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/controlMailbox" FSC_OPTIMIZED + [] + let ``control --tailcalls`` () = + let cfg = "core/control" + singleTestBuildAndRunAux cfg ["--tailcalls"] FSC_OPTIMIZED -[] -let ``controlMailbox-FSI`` () = singleTestBuildAndRun "core/controlMailbox" FSI +module Tests6 = -[] -let ``controlMailbox --tailcalls`` () = - let cfg = "core/controlMailbox" - singleTestBuildAndRunAux cfg ["--tailcalls"] FSC_OPTIMIZED + [] + let ``recordResolution-FSC_DEBUG`` () = singleTestBuildAndRun "core/recordResolution" FSC_DEBUG -[] -let ``csext-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/csext" FSC_OPTIMIZED + [] + let ``recordResolution-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/recordResolution" FSC_OPTIMIZED -[] -let ``csext-FSI`` () = singleTestBuildAndRun "core/csext" FSI + [] + let ``recordResolution-FSI`` () = singleTestBuildAndRun "core/recordResolution" FSI -[] -let ``enum-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/enum" FSC_OPTIMIZED + [] + let ``controlChamenos-FSC_OPTIMIZED`` () = + let cfg = "core/controlChamenos" + singleTestBuildAndRunAux cfg ["--tailcalls"] FSC_OPTIMIZED -[] -let ``enum-FSI`` () = singleTestBuildAndRun "core/enum" FSI + [] + let ``controlChamenos-FSI`` () = + let cfg = "core/controlChamenos" + singleTestBuildAndRunAux cfg ["--tailcalls"] FSI -[] -let ``longnames-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/longnames" FSC_OPTIMIZED +module ControlMailbox = -[] -let ``longnames-FSI`` () = singleTestBuildAndRun "core/longnames" FSI + [] + let ``controlMailbox-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/controlMailbox" FSC_OPTIMIZED -[] -let ``math-numbersVS2008-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/math/numbersVS2008" FSC_OPTIMIZED + [] + let ``controlMailbox-FSI`` () = singleTestBuildAndRun "core/controlMailbox" FSI -[] -let ``math-numbersVS2008-FSI`` () = singleTestBuildAndRun "core/math/numbersVS2008" FSI + [] + let ``controlMailbox-FSC_OPTIMIZED --tailcalls-`` () = singleTestBuildAndRunAux "core/controlMailbox" ["--tailcalls-"] FSC_OPTIMIZED -[] -let ``patterns-FSC_OPTIMIZED`` () = singleTestBuildAndRunVersion "core/patterns" FSC_OPTIMIZED LangVersion.Preview +module Tests7 = -// This requires --multiemit on by default, which is not the case for .NET Framework -[] -let ``patterns-FSI`` () = singleTestBuildAndRun "core/patterns" FSI + [] + let ``csext-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/csext" FSC_OPTIMIZED -[] -let ``fsi_load-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/fsi-load" FSC_OPTIMIZED + [] + let ``csext-FSI`` () = singleTestBuildAndRun "core/csext" FSI -[] -let ``fsi_load-FSI`` () = singleTestBuildAndRun "core/fsi-load" FSI + [] + let ``enum-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/enum" FSC_OPTIMIZED -[] -let ``reflect-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/reflect" FSC_OPTIMIZED + [] + let ``enum-FSI`` () = singleTestBuildAndRun "core/enum" FSI -[] -let ``reflect-FSI`` () = singleTestBuildAndRun "core/reflect" FSI + [] + let ``longnames-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/longnames" FSC_OPTIMIZED -let isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + [] + let ``longnames-FSI`` () = singleTestBuildAndRun "core/longnames" FSI -[] -let ``pinvoke-FSC_OPTIMIZED`` () = - if isWindows then - singleTestBuildAndRun "core/pinvoke" FSC_OPTIMIZED + [] + let ``math-numbersVS2008-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/math/numbersVS2008" FSC_OPTIMIZED -[] -let ``pinvoke-FSI`` () = - if isWindows then - singleTestBuildAndRun "core/pinvoke" FSI + [] + let ``math-numbersVS2008-FSI`` () = singleTestBuildAndRun "core/math/numbersVS2008" FSI + +module Tests8 = + + [] + let ``patterns-FSC_OPTIMIZED`` () = singleTestBuildAndRunVersion "core/patterns" FSC_OPTIMIZED LangVersion.Preview + + // This requires --multiemit on by default, which is not the case for .NET Framework + [] + let ``patterns-FSI`` () = singleTestBuildAndRun "core/patterns" FSI + + [] + let ``fsi_load-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/fsi-load" FSC_OPTIMIZED + + [] + let ``fsi_load-FSI`` () = singleTestBuildAndRun "core/fsi-load" FSI + + [] + let ``reflect-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/reflect" FSC_OPTIMIZED + + [] + let ``reflect-FSI`` () = singleTestBuildAndRun "core/reflect" FSI + + let isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + + [] + let ``pinvoke-FSC_OPTIMIZED`` () = + if isWindows then + singleTestBuildAndRun "core/pinvoke" FSC_OPTIMIZED + + [] + let ``pinvoke-FSI`` () = + if isWindows then + singleTestBuildAndRun "core/pinvoke" FSI diff --git a/tests/FSharp.Compiler.ComponentTests/TypeChecks/TyparNameTests.fs b/tests/FSharp.Compiler.ComponentTests/TypeChecks/TyparNameTests.fs index d8316d365e7..e8df564a776 100644 --- a/tests/FSharp.Compiler.ComponentTests/TypeChecks/TyparNameTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/TypeChecks/TyparNameTests.fs @@ -14,7 +14,7 @@ module TyparNameTests = (additionalFile: SourceCodeFileKind) : string array = let typeCheckResult = - cUnit |> withAdditionalSourceFile additionalFile |> typecheckProject false CompilerAssertHelpers.UseTransparentCompiler + cUnit |> withAdditionalSourceFile additionalFile |> typecheckProject false TestContext.UseTransparentCompiler assert (Array.isEmpty typeCheckResult.Diagnostics) diff --git a/tests/FSharp.Compiler.ComponentTests/xunit.runner.json b/tests/FSharp.Compiler.ComponentTests/xunit.runner.json index 2d07715ae5f..b01c50a3cb5 100644 --- a/tests/FSharp.Compiler.ComponentTests/xunit.runner.json +++ b/tests/FSharp.Compiler.ComponentTests/xunit.runner.json @@ -1,7 +1,5 @@ { - "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", - "appDomain": "ifAvailable", - "shadowCopy": false, - "parallelizeTestCollections": false, - "maxParallelThreads": 1 + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "appDomain": "denied", + "parallelizeAssembly": true } diff --git a/tests/FSharp.Compiler.Private.Scripting.UnitTests/DependencyManagerInteractiveTests.fs b/tests/FSharp.Compiler.Private.Scripting.UnitTests/DependencyManagerInteractiveTests.fs index cc027d098af..8b4be398ddd 100644 --- a/tests/FSharp.Compiler.Private.Scripting.UnitTests/DependencyManagerInteractiveTests.fs +++ b/tests/FSharp.Compiler.Private.Scripting.UnitTests/DependencyManagerInteractiveTests.fs @@ -13,6 +13,7 @@ open FSharp.Compiler.DependencyManager open FSharp.Compiler.Diagnostics open FSharp.DependencyManager.Nuget open FSharp.Test.ScriptHelpers +open FSharp.Test open FSharp.Test.Utilities open Internal.Utilities @@ -25,6 +26,7 @@ module Native = type scriptHost (?langVersion: LangVersion) = inherit FSharpScript(langVersion=defaultArg langVersion LangVersion.Preview) +[] type DependencyManagerInteractiveTests() = let getValue ((value: Result), (errors: FSharpDiagnostic[])) = @@ -148,8 +150,7 @@ type DependencyManagerInteractiveTests() = Assert.Equal(0, result.Roots |> Seq.length) () - - [] + [] member _.``Multiple Instances of DependencyProvider should be isolated``() = let assemblyProbingPaths () = Seq.empty diff --git a/tests/FSharp.Compiler.Private.Scripting.UnitTests/FSharp.Compiler.Private.Scripting.UnitTests.fsproj b/tests/FSharp.Compiler.Private.Scripting.UnitTests/FSharp.Compiler.Private.Scripting.UnitTests.fsproj index 3bf2d528a4f..e0d064e12f9 100644 --- a/tests/FSharp.Compiler.Private.Scripting.UnitTests/FSharp.Compiler.Private.Scripting.UnitTests.fsproj +++ b/tests/FSharp.Compiler.Private.Scripting.UnitTests/FSharp.Compiler.Private.Scripting.UnitTests.fsproj @@ -3,7 +3,7 @@ net472;$(FSharpNetCoreProductTargetFramework) - $(FSharpNetCoreProductTargetFramework) + $(FSharpNetCoreProductTargetFramework) Library true xunit @@ -12,6 +12,9 @@ + + XunitSetup.fs + diff --git a/tests/FSharp.Compiler.Private.Scripting.UnitTests/FSharpScriptTests.fs b/tests/FSharp.Compiler.Private.Scripting.UnitTests/FSharpScriptTests.fs index aff47308ad2..77ee417a087 100644 --- a/tests/FSharp.Compiler.Private.Scripting.UnitTests/FSharpScriptTests.fs +++ b/tests/FSharp.Compiler.Private.Scripting.UnitTests/FSharpScriptTests.fs @@ -12,12 +12,16 @@ open System.Threading.Tasks open FSharp.Compiler.Interactive open FSharp.Compiler.Interactive.Shell open FSharp.Test.ScriptHelpers -open FSharp.Test.Utilities open Xunit type InteractiveTests() = + let copyHousingToTemp() = + let tempName = TestFramework.getTemporaryFileName() + File.Copy(__SOURCE_DIRECTORY__ ++ "housing.csv", tempName + ".csv") + tempName + [] member _.``ValueRestriction error message should not have type variables fully solved``() = use script = new FSharpScript() @@ -248,10 +252,10 @@ System.Configuration.ConfigurationManager.AppSettings.Item "Environment" <- "LOC if RuntimeInformation.ProcessArchitecture = Architecture.Arm64 then () else - let code = @" -#r ""nuget:Microsoft.ML,version=1.4.0-preview"" -#r ""nuget:Microsoft.ML.AutoML,version=0.16.0-preview"" -#r ""nuget:Microsoft.Data.Analysis,version=0.4.0"" + let code = $""" +#r "nuget:Microsoft.ML,version=1.4.0-preview" +#r "nuget:Microsoft.ML.AutoML,version=0.16.0-preview" +#r "nuget:Microsoft.Data.Analysis,version=0.4.0" open System open System.IO @@ -267,7 +271,7 @@ let Shuffle (arr:int[]) = arr.[i] <- temp arr -let housingPath = ""housing.csv"" +let housingPath = @"{copyHousingToTemp()}.csv" let housingData = DataFrame.LoadCsv(housingPath) let randomIndices = (Shuffle(Enumerable.Range(0, (int (housingData.Rows.Count) - 1)).ToArray())) let testSize = int (float (housingData.Rows.Count) * 0.1) @@ -281,11 +285,11 @@ open Microsoft.ML.AutoML let mlContext = MLContext() let experiment = mlContext.Auto().CreateRegressionExperiment(maxExperimentTimeInSeconds = 15u) -let result = experiment.Execute(housing_train, labelColumnName = ""median_house_value"") +let result = experiment.Execute(housing_train, labelColumnName = "median_house_value") let details = result.RunDetails -printfn ""%A"" result +printfn "{@"%A"}" result 123 -" +""" use script = new FSharpScript(additionalArgs=[| |]) let opt = script.Eval(code) |> getValue let value = opt.Value @@ -475,6 +479,9 @@ let x = script.Eval(code) |> ignoreValue Assert.False(foundInner) +// Fails in NETFRAMEWORK with exception +// System.MissingMethodException : Method not found: 'Microsoft.FSharp.Core.FSharpFunc`2,FParsec.Reply`1> FParsec.CharParsers.pfloat()'. +#if NETCOREAPP [] member _.``Script with nuget package that yields out of order dependencies works correctly``() = // regression test for: https://github.com/dotnet/fsharp/issues/9217 @@ -497,6 +504,7 @@ test pfloat "1.234" let opt = script.Eval(code) |> getValue let value = opt.Value Assert.True(true = downcast value.ReflectionValue) +#endif [] member _.``Nuget package with method duplicates differing only in generic arity``() = @@ -511,3 +519,4 @@ let add (col:IServiceCollection) = use script = new FSharpScript(additionalArgs=[| |]) let _value,diag = script.Eval(code) Assert.Empty(diag) + diff --git a/tests/FSharp.Compiler.Private.Scripting.UnitTests/xunit.runner.json b/tests/FSharp.Compiler.Private.Scripting.UnitTests/xunit.runner.json index 2d07715ae5f..b01c50a3cb5 100644 --- a/tests/FSharp.Compiler.Private.Scripting.UnitTests/xunit.runner.json +++ b/tests/FSharp.Compiler.Private.Scripting.UnitTests/xunit.runner.json @@ -1,7 +1,5 @@ { - "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", - "appDomain": "ifAvailable", - "shadowCopy": false, - "parallelizeTestCollections": false, - "maxParallelThreads": 1 + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "appDomain": "denied", + "parallelizeAssembly": true } diff --git a/tests/FSharp.Compiler.Service.Tests/AssemblyContentProviderTests.fs b/tests/FSharp.Compiler.Service.Tests/AssemblyContentProviderTests.fs index cb4521b7af4..b7d2a4add83 100644 --- a/tests/FSharp.Compiler.Service.Tests/AssemblyContentProviderTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/AssemblyContentProviderTests.fs @@ -22,7 +22,7 @@ let private projectOptions : FSharpProjectOptions = UnresolvedReferences = None Stamp = None } -let private checker = FSharpChecker.Create(useTransparentCompiler=CompilerAssertHelpers.UseTransparentCompiler) +let private checker = FSharpChecker.Create(useTransparentCompiler = TestContext.UseTransparentCompiler) let private assertAreEqual (expected, actual) = if actual <> expected then diff --git a/tests/FSharp.Compiler.Service.Tests/BuildGraphTests.fs b/tests/FSharp.Compiler.Service.Tests/BuildGraphTests.fs index 16b0ff7b878..4769b4c322d 100644 --- a/tests/FSharp.Compiler.Service.Tests/BuildGraphTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/BuildGraphTests.fs @@ -385,22 +385,17 @@ module BuildGraphTests = for i in 1 .. 300 do async { - Interlocked.Increment(&count) |> ignore - errorR (ExampleException $"{i}") + errorR (ExampleException $"{Interlocked.Increment(&count)}") + error (ExampleException $"{Interlocked.Increment(&count)}") } ] - let run = - tasks |> MultipleDiagnosticsLoggers.Parallel |> Async.Catch |> Async.StartAsTask - - Assert.True( - run.Wait(1000), - "MultipleDiagnosticsLoggers.Parallel did not finish." - ) - - // Diagnostics from all started tasks should be collected despite the exception. - errorCountShouldBe count + task { + do! tasks |> MultipleDiagnosticsLoggers.Parallel |> Async.Catch |> Async.Ignore + // Diagnostics from all started tasks should be collected despite the exception. + errorCountShouldBe count + } [] let ``AsyncLocal diagnostics context flows correctly`` () = diff --git a/tests/FSharp.Compiler.Service.Tests/Common.fs b/tests/FSharp.Compiler.Service.Tests/Common.fs index df51f666ccd..4eb05598d52 100644 --- a/tests/FSharp.Compiler.Service.Tests/Common.fs +++ b/tests/FSharp.Compiler.Service.Tests/Common.fs @@ -19,7 +19,7 @@ open FSharp.Test.Utilities type Async with static member RunImmediate (computation: Async<'T>, ?cancellationToken ) = - let cancellationToken = defaultArg cancellationToken Async.DefaultCancellationToken + let cancellationToken = defaultArg cancellationToken CancellationToken.None let ts = TaskCompletionSource<'T>() let task = ts.Task Async.StartWithContinuations( @@ -31,7 +31,7 @@ type Async with task.Result // Create one global interactive checker instance -let checker = FSharpChecker.Create(useTransparentCompiler=FSharp.Compiler.CompilerConfig.FSharpExperimentalFeaturesEnabledAutomatically) +let checker = FSharpChecker.Create(useTransparentCompiler = FSharp.Test.TestContext.UseTransparentCompiler) type TempFile(ext, contents: string) = let tmpFile = Path.ChangeExtension(getTemporaryFileName (), ext) diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj index 9f0c7f230b9..831143ba698 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj @@ -24,6 +24,9 @@ FsUnit.fs + + XunitSetup.fs + diff --git a/tests/FSharp.Compiler.Service.Tests/FSharpExprPatternsTests.fs b/tests/FSharp.Compiler.Service.Tests/FSharpExprPatternsTests.fs index abc6bbc9de4..b9367b12759 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharpExprPatternsTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/FSharpExprPatternsTests.fs @@ -139,7 +139,7 @@ let testPatterns handler source = } let checker = - FSharpChecker.Create(documentSource = DocumentSource.Custom documentSource, keepAssemblyContents = true, useTransparentCompiler = CompilerAssertHelpers.UseTransparentCompiler) + FSharpChecker.Create(documentSource = DocumentSource.Custom documentSource, keepAssemblyContents = true, useTransparentCompiler = TestContext.UseTransparentCompiler) let checkResult = checker.ParseAndCheckFileInProject("A.fs", 0, Map.find "A.fs" files, projectOptions) diff --git a/tests/FSharp.Compiler.Service.Tests/FileSystemTests.fs b/tests/FSharp.Compiler.Service.Tests/FileSystemTests.fs index 77a1d657308..f5170d0c4e5 100644 --- a/tests/FSharp.Compiler.Service.Tests/FileSystemTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/FileSystemTests.fs @@ -25,6 +25,8 @@ let file2 = """ module File2 let B = File1.A + File1.A""" +// FileSystem is a global shared resource. +[] type internal MyFileSystem() = inherit DefaultFileSystem() static member FilesCache = dict [(fileName1, file1); (fileName2, file2)] diff --git a/tests/FSharp.Compiler.Service.Tests/FsiHelpTests.fs b/tests/FSharp.Compiler.Service.Tests/FsiHelpTests.fs index d17b3421ed9..110cc1a09e7 100644 --- a/tests/FSharp.Compiler.Service.Tests/FsiHelpTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/FsiHelpTests.fs @@ -3,7 +3,6 @@ open FSharp.Test.Assert open Xunit -[] module FsiHelpTests = [] diff --git a/tests/FSharp.Compiler.Service.Tests/FsiTests.fs b/tests/FSharp.Compiler.Service.Tests/FsiTests.fs index f6a785af4f1..ccd0668f3ae 100644 --- a/tests/FSharp.Compiler.Service.Tests/FsiTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/FsiTests.fs @@ -14,21 +14,15 @@ type Sentinel () = module MyModule = let test(x: int) = () -[] module FsiTests = let createFsiSession (useOneDynamicAssembly: bool) = - // Initialize output and input streams - let inStream = new StringReader("") - let outStream = new CompilerOutputStream() - let errStream = new CompilerOutputStream() - // Build command line arguments & start FSI session let argv = [| "C:\\fsi.exe" |] let allArgs = Array.append argv [|"--noninteractive"; if useOneDynamicAssembly then "--multiemit-" else "--multiemit+" |] let fsiConfig = FsiEvaluationSession.GetDefaultConfiguration() - FsiEvaluationSession.Create(fsiConfig, allArgs, inStream, new StreamWriter(outStream), new StreamWriter(errStream), collectible = true) + FsiEvaluationSession.Create(fsiConfig, allArgs, TextReader.Null, TextWriter.Null, TextWriter.Null, collectible = true) [] let ``No bound values at the start of FSI session`` () = diff --git a/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs b/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs index 78d03ac8a9b..807fa90c78c 100644 --- a/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs @@ -19,6 +19,8 @@ open FSharp.Compiler.Symbols open FSharp.Compiler.Symbols.FSharpExprPatterns open TestFramework +// Exculde because of some GC tests +[] module internal Project1 = let fileName1 = Path.ChangeExtension(getTemporaryFileName (), ".fs") @@ -123,7 +125,14 @@ let ``Test project1 and make sure TcImports gets cleaned up`` () = let weakTcImports = test () checker.InvalidateConfiguration Project1.options checker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() - GC.Collect(2, GCCollectionMode.Forced, blocking = true) + + //collect 2 more times for good measure, + // See for example: https://github.com/dotnet/runtime/discussions/108081 + GC.Collect(2, GCCollectionMode.Forced, true) + GC.WaitForPendingFinalizers() + GC.Collect() + GC.WaitForPendingFinalizers() + Assert.False weakTcImports.IsAlive [] diff --git a/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs b/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs index 0b30a5a61e8..2dc445199b3 100644 --- a/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs @@ -33,7 +33,7 @@ let testXmlDocFallbackToSigFileWhileInImplFile sigSource implSource line colAtEn let checker = FSharpChecker.Create(documentSource = DocumentSource.Custom documentSource, - useTransparentCompiler = CompilerAssertHelpers.UseTransparentCompiler) + useTransparentCompiler = TestContext.UseTransparentCompiler) let checkResult = checker.ParseAndCheckFileInProject("A.fs", 0, Map.find "A.fs" files, projectOptions) @@ -281,7 +281,7 @@ let testToolTipSquashing source line colAtEndOfNames lineText names tokenTag = let checker = FSharpChecker.Create(documentSource = DocumentSource.Custom documentSource, - useTransparentCompiler = CompilerAssertHelpers.UseTransparentCompiler) + useTransparentCompiler = TestContext.UseTransparentCompiler) let checkResult = checker.ParseAndCheckFileInProject("A.fs", 0, Map.find "A.fs" files, projectOptions) diff --git a/tests/FSharp.Compiler.Service.Tests/xunit.runner.json b/tests/FSharp.Compiler.Service.Tests/xunit.runner.json index 743febb7028..b01c50a3cb5 100644 --- a/tests/FSharp.Compiler.Service.Tests/xunit.runner.json +++ b/tests/FSharp.Compiler.Service.Tests/xunit.runner.json @@ -1,5 +1,5 @@ { - "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", - "appDomain": "ifAvailable", - "shadowCopy": false + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "appDomain": "denied", + "parallelizeAssembly": true } diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.UnitTests.fsproj b/tests/FSharp.Core.UnitTests/FSharp.Core.UnitTests.fsproj index 2ba4f6f837d..3e6bbbd282d 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core.UnitTests.fsproj +++ b/tests/FSharp.Core.UnitTests/FSharp.Core.UnitTests.fsproj @@ -4,7 +4,7 @@ $(FSharpNetCoreProductTargetFramework);net472 - $(FSharpNetCoreProductTargetFramework) + $(FSharpNetCoreProductTargetFramework) Library FSharp.Core.UnitTests @@ -26,6 +26,9 @@ + + XunitSetup.fs + diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncModule.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncModule.fs index 145d9a70c3e..f58e616e32a 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncModule.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncModule.fs @@ -7,6 +7,7 @@ namespace FSharp.Core.UnitTests.Control open System open System.Threading +open System.Threading.Tasks open FSharp.Core.UnitTests.LibraryTestFx open Xunit open FsCheck @@ -147,6 +148,7 @@ module LeakUtils = // --------------------------------------------------- +// [] type AsyncModule() = /// Simple asynchronous task that delays 200ms and returns a list of the current tick count @@ -273,8 +275,7 @@ type AsyncModule() = } Async.RunSynchronously test - // test is flaky: https://github.com/dotnet/fsharp/issues/11586 - //[] + [] member _.``OnCancel.RaceBetweenCancellationHandlerAndDisposingHandlerRegistration``() = let test() = use flag = new ManualResetEvent(false) @@ -297,8 +298,7 @@ type AsyncModule() = for _i = 1 to 300 do test() - // test is flaky: https://github.com/dotnet/fsharp/issues/11586 - //[] + [] member _.``OnCancel.RaceBetweenCancellationAndDispose``() = let mutable flag = 0 let cts = new System.Threading.CancellationTokenSource() @@ -316,8 +316,7 @@ type AsyncModule() = :? System.OperationCanceledException -> () Assert.AreEqual(1, flag) - // test is flaky: https://github.com/dotnet/fsharp/issues/11586 - //[] + [] member _.``OnCancel.CancelThatWasSignalledBeforeRunningTheComputation``() = let test() = let cts = new System.Threading.CancellationTokenSource() @@ -379,23 +378,25 @@ type AsyncModule() = [] member _.``AwaitWaitHandle.DisposedWaitHandle2``() = - let wh = new System.Threading.ManualResetEvent(false) - let barrier = new System.Threading.ManualResetEvent(false) + let wh = new ManualResetEvent(false) + let started = new ManualResetEventSlim(false) - let test = async { - let! timeout = Async.AwaitWaitHandle(wh, 10000) - Assert.False(timeout, "Timeout expected") - barrier.Set() |> ignore + let test = + async { + started.Set() + let! timeout = Async.AwaitWaitHandle(wh, 5000) + Assert.False(timeout, "Timeout expected") } - Async.Start test - - // await 3 secs then dispose waithandle - nothing should happen - let timeout = wait barrier 3000 - Assert.False(timeout, "Barrier was reached too early") - dispose wh - - let ok = wait barrier 10000 - if not ok then Assert.Fail("Async computation was not completed in given time") + |> Async.StartAsTask + + task { + started.Wait() + // Wait a moment then dispose waithandle - nothing should happen + do! Task.Delay 500 + Assert.False(test.IsCompleted, "Test completed too early") + dispose wh + do! test + } [] member _.``RunSynchronously.NoThreadJumpsAndTimeout``() = @@ -467,20 +468,19 @@ type AsyncModule() = [] member _.``error on one workflow should cancel all others``() = - let counter = - async { - let mutable counter = 0 - let job i = async { - if i = 55 then failwith "boom" - else - do! Async.Sleep 1000 - counter <- counter + 1 - } + let go = new ManualResetEvent(false) + let mutable counter = 0 + let job i = async { + if i = 55 then + go.Set() |> ignore + failwith "boom" + else + do! Async.AwaitWaitHandle go |> Async.Ignore + counter <- counter + 1 + } - let! _ = Async.Parallel [ for i in 1 .. 100 -> job i ] |> Async.Catch - do! Async.Sleep 5000 - return counter - } |> Async.RunSynchronously + let t = Async.Parallel [ for i in 1 .. 100 -> job i ] |> Async.Catch |> Async.Ignore |> Async.StartAsTask + t.Wait() Assert.AreEqual(0, counter) @@ -641,7 +641,6 @@ type AsyncModule() = member _.``Parallel with maxDegreeOfParallelism`` () = let mutable i = 1 let action j = async { - do! Async.Sleep 1 Assert.Equal(j, i) i <- i + 1 } diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs index 950432ccc8e..5405c5476e5 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs @@ -11,6 +11,8 @@ open Xunit open System.Threading open System.Threading.Tasks +// Cancels default token. +[] module AsyncType = type ExpectedContinuation = Success | Exception | Cancellation @@ -38,8 +40,7 @@ module AsyncType = async { return () } |> expect Success - - +[] type AsyncType() = let ignoreSynchCtx f = @@ -54,16 +55,20 @@ type AsyncType() = [] member _.AsyncRunSynchronouslyReusesThreadPoolThread() = - let action = async { async { () } |> Async.RunSynchronously } - let computation = - [| for i in 1 .. 1000 -> action |] - |> Async.Parallel + let action _ = + async { + return + async { return Thread.CurrentThread.ManagedThreadId } + |> Async.RunSynchronously + } // This test needs approximately 1000 ThreadPool threads // if Async.RunSynchronously doesn't reuse them. - // In such case TimeoutException is raised - // since ThreadPool cannot provide 1000 threads in 1 second - // (the number of threads in ThreadPool is adjusted slowly). - Async.RunSynchronously(computation, timeout = 1000) |> ignore + let usedThreads = + Seq.init 1000 action + |> Async.Parallel + |> Async.RunSynchronously + |> Set.ofArray + Assert.True(usedThreads.Count < 256, $"RunSynchronously used {usedThreads.Count} threads.") [] [] @@ -231,7 +236,8 @@ type AsyncType() = use t = Async.StartAsTask a let mutable exceptionThrown = false try - waitASec t + // waitASec t + t.Wait() with e -> exceptionThrown <- true Assert.True (t.IsFaulted) @@ -269,7 +275,7 @@ type AsyncType() = // printfn "%A" t.Status let mutable exceptionThrown = false try - waitASec t + t.Wait() with e -> exceptionThrown <- true Assert.True (exceptionThrown) Assert.True(t.IsCanceled) @@ -302,56 +308,50 @@ type AsyncType() = use t = Async.StartImmediateAsTask a let mutable exceptionThrown = false try - waitASec t + t.Wait() with e -> exceptionThrown <- true Assert.True (t.IsFaulted) Assert.True(exceptionThrown) -#if IGNORED [] - [] member _.CancellationPropagatesToImmediateTask () = let a = async { - while true do () + while true do + do! Async.Sleep 100 } use t = Async.StartImmediateAsTask a Async.CancelDefaultToken () let mutable exceptionThrown = false try - waitASec t + t.Wait() with e -> exceptionThrown <- true Assert.True (exceptionThrown) Assert.True(t.IsCanceled) -#endif -#if IGNORED [] - [] member _.CancellationPropagatesToGroupImmediate () = let ewh = new ManualResetEvent(false) - let cancelled = ref false + let mutable cancelled = false let a = async { - use! holder = Async.OnCancel (fun _ -> cancelled := true) + use! holder = Async.OnCancel (fun _ -> cancelled <- true) ewh.Set() |> Assert.True - while true do () + while true do + do! Async.Sleep 100 } let cts = new CancellationTokenSource() let token = cts.Token use t = Async.StartImmediateAsTask(a, cancellationToken=token) -// printfn "%A" t.Status ewh.WaitOne() |> Assert.True cts.Cancel() -// printfn "%A" t.Status let mutable exceptionThrown = false try - waitASec t + t.Wait() with e -> exceptionThrown <- true Assert.True (exceptionThrown) Assert.True(t.IsCanceled) - Assert.True(!cancelled) -#endif + Assert.True(cancelled) [] member _.TaskAsyncValue () = @@ -411,8 +411,7 @@ type AsyncType() = } Async.RunSynchronously(a) |> Assert.True - // test is flaky: https://github.com/dotnet/fsharp/issues/11586 - //[] + [] member _.TaskAsyncValueCancellation () = use ewh = new ManualResetEvent(false) let cts = new CancellationTokenSource() @@ -430,9 +429,11 @@ type AsyncType() = :? TaskCanceledException -> ewh.Set() |> ignore // this is ok } - Async.Start a + let t1 = Async.StartAsTask a cts.Cancel() ewh.WaitOne(10000) |> ignore + // Don't leave unobserved background tasks, because they can crash the test run. + t1.Wait() [] member _.NonGenericTaskAsyncValue () = @@ -473,9 +474,10 @@ type AsyncType() = :? TaskCanceledException -> ewh.Set() |> ignore // this is ok } - Async.Start a + let t1 = Async.StartAsTask a cts.Cancel() ewh.WaitOne(10000) |> ignore + t1.Wait() [] member _.CancellationExceptionThrown () = diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/Cancellation.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/Cancellation.fs index 4e04f64bc1f..5369edaf567 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/Cancellation.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/Cancellation.fs @@ -5,9 +5,11 @@ namespace FSharp.Core.UnitTests.Control open System open FSharp.Core.UnitTests.LibraryTestFx open Xunit +open FSharp.Test open System.Threading +open System.Threading.Tasks - +[] type CancellationType() = [] @@ -229,6 +231,7 @@ type CancellationType() = } asyncs |> Async.Parallel |> Async.RunSynchronously |> ignore + // See https://github.com/dotnet/fsharp/issues/3254 [] member this.AwaitTaskCancellationAfterAsyncTokenCancellation() = let StartCatchCancellation cancellationToken (work) = @@ -262,11 +265,11 @@ type CancellationType() = let cts = new CancellationTokenSource() let tcs = System.Threading.Tasks.TaskCompletionSource<_>() - let t = + let test() = async { do! tcs.Task |> Async.AwaitTask } - |> StartAsTaskProperCancel None (Some cts.Token) + |> StartAsTaskProperCancel None (Some cts.Token) :> Task // First cancel the token, then set the task as cancelled. async { @@ -274,15 +277,31 @@ type CancellationType() = cts.Cancel() do! Async.Sleep 100 tcs.TrySetException (TimeoutException "Task timed out after token.") - |> ignore + |> ignore } |> Async.Start - try - let res = t.Wait(2000) - let msg = sprintf "Excepted TimeoutException wrapped in an AggregateException, but got %A" res - printfn "failure msg: %s" msg - Assert.Fail (msg) - with :? AggregateException as agg -> () + task { + let! agg = Assert.ThrowsAsync(test) + let inner = agg.InnerException + Assert.True(inner :? TimeoutException, $"Excepted TimeoutException wrapped in an AggregateException, but got %A{inner}") + } + + // Simpler regression test for https://github.com/dotnet/fsharp/issues/3254 + [] + member this.AwaitTaskCancellationAfterAsyncTokenCancellation2() = + let tcs = new TaskCompletionSource() + let cts = new CancellationTokenSource() + let _ = cts.Token.Register(fun () -> tcs.SetResult 42) + Assert.ThrowsAsync( fun () -> + Async.StartAsTask( + async { + cts.CancelAfter 100 + let! result = tcs.Task |> Async.AwaitTask + return result + }, + cancellationToken = cts.Token + ) + ) [] member this.Equality() = diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/MailboxProcessorType.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/MailboxProcessorType.fs index 904bc7dc622..f49df8df124 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/MailboxProcessorType.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/MailboxProcessorType.fs @@ -24,6 +24,7 @@ type StartImmediateThreadInfo = type StartImmediateMessage = | GetThreadInfo of AsyncReplyChannel +// [] type MailboxProcessorType() = let getSimpleMailbox() = @@ -125,7 +126,7 @@ type MailboxProcessorType() = use mre2 = new ManualResetEventSlim(false) // https://github.com/dotnet/fsharp/issues/3337 - let cts = new CancellationTokenSource () + use cts = new CancellationTokenSource () let addMsg msg = match result with @@ -204,25 +205,25 @@ type MailboxProcessorType() = [] member this.``Receive Races with Post``() = - let receiveEv = new ManualResetEvent(false) - let postEv = new ManualResetEvent(false) - let finishedEv = new ManualResetEvent(false) + let receiveEv = new AutoResetEvent(false) + let postEv = new AutoResetEvent(false) + let finishedEv = new AutoResetEvent(false) + use cts = new CancellationTokenSource() let mb = MailboxProcessor.Start ( fun inbox -> async { while true do - let w = receiveEv.WaitOne() - receiveEv.Reset() |> ignore + receiveEv.WaitOne() |> ignore let! (msg) = inbox.Receive () finishedEv.Set() |> ignore - }) + }, + cancellationToken = cts.Token) let post = async { - while true do - let r = postEv.WaitOne() - postEv.Reset() |> ignore + while not cts.IsCancellationRequested do + postEv.WaitOne() |> ignore mb.Post(fun () -> ()) - } |> Async.Start + } |> Async.StartAsTask for i in 0 .. 100000 do if i % 2 = 0 then receiveEv.Set() |> ignore @@ -232,32 +233,37 @@ type MailboxProcessorType() = receiveEv.Set() |> ignore finishedEv.WaitOne() |> ignore - finishedEv.Reset() |> ignore + + cts.Cancel() + // Let the post task finish. + postEv.Set() |> ignore + post.Wait() [] member this.``Receive Races with Post on timeout``() = - let receiveEv = new ManualResetEvent(false) - let postEv = new ManualResetEvent(false) - let finishedEv = new ManualResetEvent(false) + let receiveEv = new AutoResetEvent(false) + let postEv = new AutoResetEvent(false) + let finishedEv = new AutoResetEvent(false) + use cts = new CancellationTokenSource() let mb = MailboxProcessor.Start ( fun inbox -> async { while true do - let w = receiveEv.WaitOne() - receiveEv.Reset() |> ignore + receiveEv.WaitOne() |> ignore let! (msg) = inbox.Receive (5000) finishedEv.Set() |> ignore - }) + }, + cancellationToken = cts.Token) let isErrored = mb.Error |> Async.AwaitEvent |> Async.StartAsTask let post = async { - while true do - let r = postEv.WaitOne() - postEv.Reset() |> ignore + while not cts.IsCancellationRequested do + postEv.WaitOne() |> ignore mb.Post(fun () -> ()) - } |> Async.Start + } + |> Async.StartAsTask for i in 0 .. 10000 do if i % 2 = 0 then @@ -271,32 +277,35 @@ type MailboxProcessorType() = if isErrored.IsCompleted then raise <| Exception("Mailbox should not fail!", isErrored.Result) - finishedEv.Reset() |> ignore + cts.Cancel() + // Let the post task finish. + postEv.Set() |> ignore + post.Wait() [] member this.``TryReceive Races with Post on timeout``() = - let receiveEv = new ManualResetEvent(false) - let postEv = new ManualResetEvent(false) - let finishedEv = new ManualResetEvent(false) + let receiveEv = new AutoResetEvent(false) + let postEv = new AutoResetEvent(false) + let finishedEv = new AutoResetEvent(false) + use cts = new CancellationTokenSource() let mb = MailboxProcessor.Start ( fun inbox -> async { while true do - let w = receiveEv.WaitOne() - receiveEv.Reset() |> ignore + receiveEv.WaitOne() |> ignore let! (msg) = inbox.TryReceive (5000) finishedEv.Set() |> ignore - }) + }, + cancellationToken = cts.Token) let isErrored = mb.Error |> Async.AwaitEvent |> Async.StartAsTask let post = async { - while true do - let r = postEv.WaitOne() - postEv.Reset() |> ignore + while not cts.IsCancellationRequested do + postEv.WaitOne() |> ignore mb.Post(fun () -> ()) - } |> Async.Start + } |> Async.StartAsTask for i in 0 .. 10000 do if i % 2 = 0 then @@ -310,9 +319,13 @@ type MailboxProcessorType() = if isErrored.IsCompleted then raise <| Exception("Mailbox should not fail!", isErrored.Result) - finishedEv.Reset() |> ignore + cts.Cancel() + // Let the post task finish. + postEv.Set() |> ignore + post.Wait() - [] + // TODO: Attempts to access disposed event at mailbox.fs:193 + [] member this.``After dispose is called, mailbox should stop receiving and processing messages``() = task { let mutable isSkip = false let mutable actualSkipMessagesCount = 0 @@ -320,8 +333,9 @@ type MailboxProcessorType() = let sleepDueTime = 100 let expectedMessagesCount = 2 use mre = new ManualResetEventSlim(false) + use cts = new CancellationTokenSource() let mb = - MailboxProcessor.Start(fun b -> + MailboxProcessor.Start((fun b -> let rec loop() = async { match! b.Receive() with @@ -338,7 +352,8 @@ type MailboxProcessorType() = return! loop() | _ -> () } - loop() + loop()), + cancellationToken = cts.Token ) let post() = Increment 1 |> mb.Post @@ -353,6 +368,7 @@ type MailboxProcessorType() = Assert.Equal(expectedMessagesCount, actualMessagesCount) Assert.Equal(0, actualSkipMessagesCount) Assert.Equal(0, mb.CurrentQueueLength) + cts.Cancel() } [] @@ -363,6 +379,7 @@ type MailboxProcessorType() = let sleepDueTime = 100 let expectedMessagesCount = 2 use mre = new ManualResetEventSlim(false) + use cts = new CancellationTokenSource() let mb = MailboxProcessor.Start((fun b -> let rec loop() = @@ -382,7 +399,8 @@ type MailboxProcessorType() = | _ -> () } loop()), - true + true, + cancellationToken = cts.Token ) let post() = Increment 1 |> mb.Post @@ -397,6 +415,7 @@ type MailboxProcessorType() = Assert.Equal(expectedMessagesCount, actualMessagesCount) Assert.Equal(0, actualSkipMessagesCount) Assert.Equal(0, mb.CurrentQueueLength) + cts.Cancel() } [] @@ -496,3 +515,56 @@ type MailboxProcessorType() = // If StartImmediate worked correctly, the information should be identical since // the threads should be the same. Assert.Equal(callingThreadInfo, mailboxThreadInfo) + +module MailboxProcessorType = + + [] + let TryScan () = + let tcs = TaskCompletionSource<_>() + use mailbox = + new MailboxProcessor(fun inbox -> async { + do! + inbox.TryScan( function + | Reset -> async { tcs.SetResult "Reset processed" } |> Some + | _ -> None) + |> Async.Ignore + }) + mailbox.Start() + + for i in 1 .. 100 do + mailbox.Post(Increment i) + mailbox.Post Reset + + Assert.Equal("Reset processed", tcs.Task.Result) + Assert.Equal(100, mailbox.CurrentQueueLength) + + [] + let ``TryScan with timeout`` () = + let tcs = TaskCompletionSource<_>() + use mailbox = + new MailboxProcessor(fun inbox -> + let rec loop i = async { + match! + inbox.TryScan( function + | Reset -> async { tcs.SetResult i } |> Some + | _ -> None) + with + | None -> do! loop (i + 1) + | _ -> () + } + loop 1 + ) + mailbox.DefaultTimeout <- 10 + mailbox.Start() + + let iteration = + task { + for i in 1 .. 100 do + mailbox.Post(Increment 1) + do! Task.Delay 10 + mailbox.Post Reset + + return! tcs.Task + } + + Assert.True(iteration.Result > 1, "TryScan did not timeout") diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/Tasks.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/Tasks.fs index 5d533b880c3..d4a17e08518 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/Tasks.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/Tasks.fs @@ -190,8 +190,14 @@ module Helpers = let BIG = 10 // let BIG = 10000 let require x msg = if not x then failwith msg + + let requireNotSet (e: ManualResetEventSlim) msg = if e.IsSet then failwith msg + + let requireSet (e: ManualResetEventSlim) msg = if not e.IsSet then failwith msg + let failtest str = raise (TestException str) +[] type Basics() = [] member _.testShortCircuitResult() = @@ -236,15 +242,16 @@ type Basics() = [] member _.testNonBlocking() = printfn "Running testNonBlocking..." - let sw = Stopwatch() - sw.Start() + let allowContinue = new SemaphoreSlim(0) + let finished = new ManualResetEventSlim() let t = task { - do! Task.Yield() + do! allowContinue.WaitAsync() Thread.Sleep(100) + finished.Set() } - sw.Stop() - require (sw.ElapsedMilliseconds < 50L) "sleep blocked caller" + allowContinue.Release() |> ignore + require (not finished.IsSet) "sleep blocked caller" t.Wait() [] @@ -908,58 +915,60 @@ type Basics() = [] member _.testExceptionThrownInFinally() = printfn "running testExceptionThrownInFinally" - for i in 1 .. 5 do - let mutable ranInitial = false - let mutable ranNext = false + for i in 1 .. 5 do + use stepOutside = new SemaphoreSlim(0) + use ranInitial = new ManualResetEventSlim() + use ranNext = new ManualResetEventSlim() let mutable ranFinally = 0 let t = task { try - ranInitial <- true + ranInitial.Set() do! Task.Yield() Thread.Sleep(100) // shouldn't be blocking so we should get through to requires before this finishes - ranNext <- true + ranNext.Set() finally ranFinally <- ranFinally + 1 failtest "finally exn!" } - require ranInitial "didn't run initial" - require (not ranNext) "ran next too early" + require ranInitial.IsSet "didn't run initial" + require (not ranNext.IsSet) "ran next too early" try t.Wait() require false "shouldn't get here" with | _ -> () - require ranNext "didn't run next" + require ranNext.IsSet "didn't run next" require (ranFinally = 1) "didn't run finally exactly once" [] member _.test2ndExceptionThrownInFinally() = printfn "running test2ndExceptionThrownInFinally" for i in 1 .. 5 do - let mutable ranInitial = false - let mutable ranNext = false + use ranInitial = new ManualResetEventSlim() + use continueTask = new SemaphoreSlim(0) + use ranNext = new ManualResetEventSlim() let mutable ranFinally = 0 let t = task { try - ranInitial <- true + ranInitial.Set() + do! continueTask.WaitAsync() + ranNext.Set() do! Task.Yield() - Thread.Sleep(100) // shouldn't be blocking so we should get through to requires before this finishes - ranNext <- true failtest "uhoh" finally ranFinally <- ranFinally + 1 failtest "2nd exn!" } - require ranInitial "didn't run initial" - require (not ranNext) "ran next too early" + ranInitial.Wait() + continueTask.Release() |> ignore try t.Wait() require false "shouldn't get here" with | _ -> () - require ranNext "didn't run next" + require ranNext.IsSet "didn't run next" require (ranFinally = 1) "didn't run finally exactly once" [] @@ -1198,8 +1207,6 @@ type Basics() = } |> ignore -[] -type BasicsNotInParallel() = [] member _.testTaskUsesSyncContext() = diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/TasksDynamic.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/TasksDynamic.fs index c811aecaa5c..7d9effabaee 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/TasksDynamic.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/TasksDynamic.fs @@ -311,8 +311,15 @@ module Helpers = let BIG = 10 // let BIG = 10000 let require x msg = if not x then failwith msg + + let requireSet (e: ManualResetEventSlim) msg = + if not (e.Wait(TimeSpan.FromSeconds 5.)) then failwith msg + + let requireNotSet (e: ManualResetEventSlim) msg = if e.IsSet then failwith msg + let failtest str = raise (TestException str) +[] type Basics() = [] member _.testShortCircuitResult() = @@ -357,15 +364,16 @@ type Basics() = [] member _.testNonBlocking() = printfn "Running testNonBlocking..." - let sw = Stopwatch() - sw.Start() + let allowContinue = new SemaphoreSlim(0) + let finished = new ManualResetEventSlim() let t = taskDynamic { - do! Task.Yield() + do! allowContinue.WaitAsync() Thread.Sleep(100) + finished.Set() } - sw.Stop() - require (sw.ElapsedMilliseconds < 50L) "sleep blocked caller" + allowContinue.Release() |> ignore + require (not finished.IsSet) "sleep blocked caller" t.Wait() [] @@ -982,58 +990,60 @@ type Basics() = [] member _.testExceptionThrownInFinally() = printfn "running testExceptionThrownInFinally" - for i in 1 .. 5 do - let mutable ranInitial = false - let mutable ranNext = false + for i in 1 .. 5 do + use stepOutside = new SemaphoreSlim(0) + use ranInitial = new ManualResetEventSlim() + use ranNext = new ManualResetEventSlim() let mutable ranFinally = 0 let t = taskDynamic { try - ranInitial <- true + ranInitial.Set() do! Task.Yield() Thread.Sleep(100) // shouldn't be blocking so we should get through to requires before this finishes - ranNext <- true + ranNext.Set() finally ranFinally <- ranFinally + 1 failtest "finally exn!" } - require ranInitial "didn't run initial" - require (not ranNext) "ran next too early" + require ranInitial.IsSet "didn't run initial" + require (not ranNext.IsSet) "ran next too early" try t.Wait() require false "shouldn't get here" with | _ -> () - require ranNext "didn't run next" + require ranNext.IsSet "didn't run next" require (ranFinally = 1) "didn't run finally exactly once" [] member _.test2ndExceptionThrownInFinally() = printfn "running test2ndExceptionThrownInFinally" for i in 1 .. 5 do - let mutable ranInitial = false - let mutable ranNext = false + use ranInitial = new ManualResetEventSlim() + use continueTask = new SemaphoreSlim(0) + use ranNext = new ManualResetEventSlim() let mutable ranFinally = 0 let t = taskDynamic { try - ranInitial <- true + ranInitial.Set() + do! continueTask.WaitAsync() + ranNext.Set() do! Task.Yield() - Thread.Sleep(100) // shouldn't be blocking so we should get through to requires before this finishes - ranNext <- true failtest "uhoh" finally ranFinally <- ranFinally + 1 failtest "2nd exn!" } - require ranInitial "didn't run initial" - require (not ranNext) "ran next too early" + ranInitial.Wait() + continueTask.Release() |> ignore try t.Wait() require false "shouldn't get here" with | _ -> () - require ranNext "didn't run next" + require ranNext.IsSet "didn't run next" require (ranFinally = 1) "didn't run finally exactly once" [] @@ -1256,10 +1266,6 @@ type Basics() = } |> ignore - -[] -type BasicsNotInParallel() = - [] member _.testTaskUsesSyncContext() = printfn "Running testBackgroundTask..." diff --git a/tests/FSharp.Core.UnitTests/xunit.runner.json b/tests/FSharp.Core.UnitTests/xunit.runner.json index 2d07715ae5f..b01c50a3cb5 100644 --- a/tests/FSharp.Core.UnitTests/xunit.runner.json +++ b/tests/FSharp.Core.UnitTests/xunit.runner.json @@ -1,7 +1,5 @@ { - "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", - "appDomain": "ifAvailable", - "shadowCopy": false, - "parallelizeTestCollections": false, - "maxParallelThreads": 1 + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "appDomain": "denied", + "parallelizeAssembly": true } diff --git a/tests/FSharp.Test.Utilities/Compiler.fs b/tests/FSharp.Test.Utilities/Compiler.fs index f535bc21c5a..8d7ae499e9c 100644 --- a/tests/FSharp.Test.Utilities/Compiler.fs +++ b/tests/FSharp.Test.Utilities/Compiler.fs @@ -175,8 +175,13 @@ module rec Compiler = StdOut: string StdErr: string } + type EvalOutput = + { Result: Result + StdOut: string + StdErr: string } + type RunOutput = - | EvalOutput of Result + | EvalOutput of EvalOutput | ExecutionOutput of ExecutionOutput type SourceCodeFileName = string @@ -698,7 +703,6 @@ module rec Compiler = let private compileFSharpCompilation compilation ignoreWarnings (cUnit: CompilationUnit) : CompilationResult = - use redirect = new RedirectConsole() let ((err: FSharpDiagnostic[], rc: int, outputFilePath: string), deps) = CompilerAssert.CompileRaw(compilation, ignoreWarnings) @@ -711,7 +715,7 @@ module rec Compiler = Adjust = 0 PerFileErrors = diagnostics Diagnostics = diagnostics |> List.map snd - Output = Some (RunOutput.ExecutionOutput { ExitCode = rc; StdOut = redirect.Output(); StdErr = redirect.ErrorOutput() }) + Output = Some (RunOutput.ExecutionOutput { ExitCode = rc; StdOut = ParallelConsole.OutText; StdErr = ParallelConsole.ErrorText }) Compilation = cUnit } @@ -918,7 +922,6 @@ module rec Compiler = let fileName = fsSource.Source.ChangeExtension.GetSourceFileName let references = - let disposals = ResizeArray() let outputDirectory = match fsSource.OutputDirectory with | Some di -> di @@ -928,10 +931,9 @@ module rec Compiler = Array.empty else outputDirectory.Create() - disposals.Add({ new IDisposable with member _.Dispose() = outputDirectory.Delete(true) }) // Note that only the references are relevant here let compilation = Compilation.Compilation([], CompileOutput.Exe,Array.empty, TargetFramework.Current, references, None, None) - evaluateReferences outputDirectory disposals fsSource.IgnoreWarnings compilation + evaluateReferences outputDirectory fsSource.IgnoreWarnings compilation |> fst let options = @@ -979,8 +981,6 @@ module rec Compiler = | _ -> false | _ -> false let exitCode, output, errors = CompilerAssert.ExecuteAndReturnResult (p, isFsx, s.Dependencies, false) - printfn "---------output-------\n%s\n-------" output - printfn "---------errors-------\n%s\n-------" errors let executionResult = { s with Output = Some (ExecutionOutput { ExitCode = exitCode; StdOut = output; StdErr = errors }) } if exitCode = 0 then CompilationResult.Success executionResult @@ -991,7 +991,7 @@ module rec Compiler = let compileExeAndRun = asExe >> compileAndRun - let private processScriptResults fs (evalResult: Result, err: FSharpDiagnostic[]) = + let private processScriptResults fs (evalResult: Result, err: FSharpDiagnostic[]) outputWritten errorsWritten = let perFileDiagnostics = err |> fromFSharpDiagnostic let diagnostics = perFileDiagnostics |> List.map snd let (errors, warnings) = partitionErrors diagnostics @@ -1001,7 +1001,7 @@ module rec Compiler = Adjust = 0 Diagnostics = if fs.IgnoreWarnings then errors else diagnostics PerFileErrors = perFileDiagnostics - Output = Some (EvalOutput evalResult) + Output = Some (EvalOutput ({Result = evalResult; StdOut = outputWritten; StdErr = errorsWritten})) Compilation = FS fs } let evalError = match evalResult with Ok _ -> false | _ -> true @@ -1013,7 +1013,9 @@ module rec Compiler = let private evalFSharp (fs: FSharpCompilationSource) (script:FSharpScript) : CompilationResult = let source = fs.Source.GetSourceText |> Option.defaultValue "" - script.Eval(source) |> (processScriptResults fs) + let result = script.Eval(source) + let outputWritten, errorsWritten = script.GetOutput(), script.GetErrorOutput() + processScriptResults fs result outputWritten errorsWritten let scriptingShim = Path.Combine(__SOURCE_DIRECTORY__,"ScriptingShims.fsx") let private evalScriptFromDisk (fs: FSharpCompilationSource) (script:FSharpScript) : CompilationResult = @@ -1025,7 +1027,9 @@ module rec Compiler = |> List.map (sprintf " @\"%s\"") |> String.Concat - script.Eval("#load " + fileNames ) |> (processScriptResults fs) + let result = script.Eval("#load " + fileNames) + let outputWritten, errorsWritten = script.GetOutput(), script.GetErrorOutput() + processScriptResults fs result outputWritten errorsWritten let eval (cUnit: CompilationUnit) : CompilationResult = match cUnit with @@ -1050,60 +1054,53 @@ module rec Compiler = let runFsi (cUnit: CompilationUnit) : CompilationResult = match cUnit with | FS fs -> - let disposals = ResizeArray() - try - let source = fs.Source.GetSourceText |> Option.defaultValue "" - let name = fs.Name |> Option.defaultValue "unnamed" - let options = fs.Options |> Array.ofList - let outputDirectory = - match fs.OutputDirectory with - | Some di -> di - | None -> DirectoryInfo(createTemporaryDirectory "runFsi") - outputDirectory.Create() - disposals.Add({ new IDisposable with member _.Dispose() = outputDirectory.Delete(true) }) - - let references = processReferences fs.References outputDirectory - let cmpl = Compilation.Create(fs.Source, fs.OutputType, options, fs.TargetFramework, references, name, outputDirectory) - let _compilationRefs, _deps = evaluateReferences outputDirectory disposals fs.IgnoreWarnings cmpl - let options = - let opts = new ResizeArray(fs.Options) - - // For every built reference add a -I path so that fsi can find it easily - for reference in references do - match reference with - | CompilationReference( cmpl, _) -> - match cmpl with - | Compilation(_sources, _outputType, _options, _targetFramework, _references, _name, outputDirectory) -> - if outputDirectory.IsSome then - opts.Add($"-I:\"{(outputDirectory.Value.FullName)}\"") - | _ -> () - opts.ToArray() - let errors, stdOut = CompilerAssert.RunScriptWithOptionsAndReturnResult options source - - let executionOutputwithStdOut: RunOutput option = - ExecutionOutput { StdOut = stdOut; ExitCode = 0; StdErr = "" } - |> Some - let result = - { OutputPath = None - Dependencies = [] - Adjust = 0 - Diagnostics = [] - PerFileErrors= [] - Output = executionOutputwithStdOut - Compilation = cUnit } - - if errors.Count > 0 then - let output = ExecutionOutput { - ExitCode = -1 - StdOut = String.Empty - StdErr = ((errors |> String.concat "\n").Replace("\r\n","\n")) } - CompilationResult.Failure { result with Output = Some output } - else - CompilationResult.Success result - - finally - disposals - |> Seq.iter (fun x -> x.Dispose()) + let source = fs.Source.GetSourceText |> Option.defaultValue "" + let name = fs.Name |> Option.defaultValue "unnamed" + let options = fs.Options |> Array.ofList + let outputDirectory = + match fs.OutputDirectory with + | Some di -> di + | None -> DirectoryInfo(createTemporaryDirectory "runFsi") + outputDirectory.Create() + + let references = processReferences fs.References outputDirectory + let cmpl = Compilation.Create(fs.Source, fs.OutputType, options, fs.TargetFramework, references, name, outputDirectory) + let _compilationRefs, _deps = evaluateReferences outputDirectory fs.IgnoreWarnings cmpl + let options = + let opts = new ResizeArray(fs.Options) + + // For every built reference add a -I path so that fsi can find it easily + for reference in references do + match reference with + | CompilationReference( cmpl, _) -> + match cmpl with + | Compilation(_sources, _outputType, _options, _targetFramework, _references, _name, outputDirectory) -> + if outputDirectory.IsSome then + opts.Add($"-I:\"{(outputDirectory.Value.FullName)}\"") + | _ -> () + opts.ToArray() + let errors, stdOut = CompilerAssert.RunScriptWithOptionsAndReturnResult options source + + let executionOutputwithStdOut: RunOutput option = + ExecutionOutput { StdOut = stdOut; ExitCode = 0; StdErr = "" } + |> Some + let result = + { OutputPath = None + Dependencies = [] + Adjust = 0 + Diagnostics = [] + PerFileErrors= [] + Output = executionOutputwithStdOut + Compilation = cUnit } + + if errors.Count > 0 then + let output = ExecutionOutput { + ExitCode = -1 + StdOut = String.Empty + StdErr = ((errors |> String.concat "\n").Replace("\r\n","\n")) } + CompilationResult.Failure { result with Output = Some output } + else + CompilationResult.Success result | _ -> failwith "FSI running only supports F#." @@ -1282,7 +1279,7 @@ Actual: | Some actual -> let expected = stripVersion (normalizeNewlines expected) if expected <> actual then - failwith $"""Output does not match expected: ------------{Environment.NewLine}{expected}{Environment.NewLine}Actual: ------------{Environment.NewLine}{actual}{Environment.NewLine}""" + failwith $"""Output does not match expected:{Environment.NewLine}{expected}{Environment.NewLine}Actual:{Environment.NewLine}{actual}{Environment.NewLine}""" else cResult @@ -1295,7 +1292,7 @@ Actual: | Some actual -> for item in expected do if not(actual.Contains(item)) then - failwith $"""Output does not match expected: ------------{Environment.NewLine}{item}{Environment.NewLine}Actual: ------------{Environment.NewLine}{actual}{Environment.NewLine}""" + failwith $"""Output does not match expected:{Environment.NewLine}{item}{Environment.NewLine}Actual:{Environment.NewLine}{actual}{Environment.NewLine}""" cResult type ImportScope = { Kind: ImportDefinitionKind; Name: string } @@ -1520,10 +1517,14 @@ Actual: match r.Output with | Some (ExecutionOutput output) -> sprintf "----output-----\n%s\n----error-------\n%s\n----------" output.StdOut output.StdErr - | Some (EvalOutput (Result.Error exn) ) -> - sprintf "----script error-----\n%s\n----------" (exn.ToString()) - | Some (EvalOutput (Result.Ok fsiVal) ) -> - sprintf "----script output-----\n%A\n----------" (fsiVal) + | Some (EvalOutput output) -> + match output.Result with + | Result.Error exn -> + sprintf "----script error-----\n%s\n----------" (exn.ToString()) + sprintf "----output-----\n%s\n----error-------\n%s\n----------" output.StdOut output.StdErr + | Result.Ok fsiVal -> + sprintf "----script output-----\n%A\n----------" (fsiVal) + sprintf "----output-----\n%s\n----error-------\n%s\n----------" output.StdOut output.StdErr | _ -> () ] |> String.concat "\n" failwith message @@ -1743,9 +1744,11 @@ Actual: let private assertEvalOutput (selector: FsiValue -> 'T) (value: 'T) (result: CompilationResult) : CompilationResult = match result.RunOutput with | None -> failwith "Execution output is missing cannot check value." - | Some (EvalOutput (Ok (Some e))) -> Assert.Equal<'T>(value, (selector e)) - | Some (EvalOutput (Ok None )) -> failwith "Cannot assert value of evaluation, since it is None." - | Some (EvalOutput (Result.Error ex)) -> raise ex + | Some (EvalOutput output) -> + match output.Result with + | Ok (Some e) -> Assert.Equal<'T>(value, (selector e)) + | Ok None -> failwith "Cannot assert value of evaluation, since it is None." + | Result.Error ex -> raise ex | Some _ -> failwith "Only 'eval' output is supported." result diff --git a/tests/FSharp.Test.Utilities/CompilerAssert.fs b/tests/FSharp.Test.Utilities/CompilerAssert.fs index fc2875d8955..9e2ee1d7cbb 100644 --- a/tests/FSharp.Test.Utilities/CompilerAssert.fs +++ b/tests/FSharp.Test.Utilities/CompilerAssert.fs @@ -2,6 +2,8 @@ namespace FSharp.Test +open System.Threading + #nowarn "57" open System @@ -295,14 +297,15 @@ and Compilation = | n -> Some n Compilation(sources, output, options, targetFramework, cmplRefs, name, outputDirectory) - -module rec CompilerAssertHelpers = +module TestContext = let UseTransparentCompiler = FSharp.Compiler.CompilerConfig.FSharpExperimentalFeaturesEnabledAutomatically || not (String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TEST_TRANSPARENT_COMPILER"))) - let checker = FSharpChecker.Create(suggestNamesForErrors=true, useTransparentCompiler=UseTransparentCompiler) + let Checker = FSharpChecker.Create(suggestNamesForErrors=true, useTransparentCompiler = UseTransparentCompiler) + +module CompilerAssertHelpers = // Unlike C# whose entrypoint is always string[] F# can make an entrypoint with 0 args, or with an array of string[] let mkDefaultArgs (entryPoint:MethodBase) : obj[] = [| @@ -322,48 +325,56 @@ module rec CompilerAssertHelpers = else entryPoint let args = mkDefaultArgs entryPoint - captureConsoleOutputs (fun () -> entryPoint.Invoke(Unchecked.defaultof, args) |> ignore) + entryPoint.Invoke(Unchecked.defaultof, args) |> ignore #if NETCOREAPP - let executeBuiltApp assembly deps isFsx = + let executeBuiltApp assemblyPath deps isFsx = let ctxt = AssemblyLoadContext("ContextName", true) try ctxt.add_Resolving(fun ctxt name -> deps |> List.tryFind (fun (x: string) -> Path.GetFileNameWithoutExtension x = name.Name) |> Option.map ctxt.LoadFromAssemblyPath - |> Option.defaultValue null) + |> Option.toObj) - executeAssemblyEntryPoint (ctxt.LoadFromAssemblyPath assembly) isFsx + executeAssemblyEntryPoint (ctxt.LoadFromAssemblyPath assemblyPath) isFsx finally ctxt.Unload() #else type Worker () = inherit MarshalByRefObject() - member x.ExecuteTestCase assemblyPath (deps: string[]) isFsx = - AppDomain.CurrentDomain.add_AssemblyResolve(ResolveEventHandler(fun _ args -> - deps - |> Array.tryFind (fun (x: string) -> Path.GetFileNameWithoutExtension x = AssemblyName(args.Name).Name) - |> Option.bind (fun x -> if FileSystem.FileExistsShim x then Some x else None) - |> Option.map Assembly.LoadFile - |> Option.defaultValue null)) - + member x.ExecuteTestCase assemblyPath isFsx = + // Set console streams for the AppDomain. + ParallelConsole.initStreamsCapture() + ParallelConsole.resetWriters() let assembly = Assembly.LoadFrom assemblyPath - executeAssemblyEntryPoint assembly isFsx - - let adSetup = - let setup = new System.AppDomainSetup () - let directory = Path.GetDirectoryName(typeof.Assembly.Location) - setup.ApplicationBase <- directory - setup + let ex = try executeAssemblyEntryPoint assembly isFsx; None with ex -> Some ex + ParallelConsole.OutText, ParallelConsole.ErrorText, ex + + let executeBuiltApp assembly dependecies isFsx = + let thisAssemblyDirectory = Path.GetDirectoryName(typeof.Assembly.Location) + let setup = AppDomainSetup(ApplicationBase = thisAssemblyDirectory) + let testCaseDomain = AppDomain.CreateDomain($"built app {assembly}", null, setup) + + testCaseDomain.add_AssemblyResolve(fun _ args -> + dependecies + |> List.tryFind (fun path -> Path.GetFileNameWithoutExtension path = AssemblyName(args.Name).Name) + |> Option.filter FileSystem.FileExistsShim + |> Option.map Assembly.LoadFile + |> Option.toObj + ) - let executeBuiltApp assembly deps = - let ad = AppDomain.CreateDomain((Guid()).ToString(), null, adSetup) let worker = - use _ = new AlreadyLoadedAppDomainResolver() - (ad.CreateInstanceFromAndUnwrap(typeof.Assembly.CodeBase, typeof.FullName)) :?> Worker - worker.ExecuteTestCase assembly (deps |> Array.ofList) + (testCaseDomain.CreateInstanceFromAndUnwrap(typeof.Assembly.CodeBase, typeof.FullName)) :?> Worker + + let out, error, ex = worker.ExecuteTestCase assembly isFsx + + AppDomain.Unload testCaseDomain + + printf $"{out}" + eprintf $"{error}" + ex |> Option.iter raise #endif let defaultProjectOptions (targetFramework: TargetFramework) = @@ -409,31 +420,16 @@ module rec CompilerAssertHelpers = // Generate a response file, purely for diagnostic reasons. File.WriteAllLines(Path.ChangeExtension(outputFilePath, ".rsp"), args) - let errors, rc = checker.Compile args |> Async.RunImmediate + let errors, rc = TestContext.Checker.Compile args |> Async.RunImmediate errors, rc, outputFilePath let compileDisposable (outputDirectory:DirectoryInfo) isExe options targetFramework nameOpt (sources:SourceCodeFileKind list) = - let disposeFile path = - { - new IDisposable with - member _.Dispose() = - try File.Delete path with | _ -> () - } - let disposals = ResizeArray() - let disposeList = - { - new IDisposable with - member _.Dispose() = - for item in disposals do - item.Dispose() - } let name = match nameOpt with | Some name -> name | _ -> getTemporaryFileNameInDirectory outputDirectory.FullName let outputFilePath = Path.ChangeExtension (Path.Combine(outputDirectory.FullName, name), if isExe then ".exe" else ".dll") - disposals.Add(disposeFile outputFilePath) let sources = [ for item in sources do @@ -443,7 +439,6 @@ module rec CompilerAssertHelpers = let source = item.ChangeExtension let destFileName = Path.Combine(outputDirectory.FullName, Path.GetFileName(source.GetSourceFileName)) File.WriteAllText (destFileName, text) - disposals.Add(disposeFile destFileName) yield source.WithFileName(destFileName) | None -> // On Disk file @@ -451,15 +446,9 @@ module rec CompilerAssertHelpers = let source = item.ChangeExtension let destFileName = Path.Combine(outputDirectory.FullName, Path.GetFileName(source.GetSourceFileName)) File.Copy(sourceFileName, destFileName, true) - disposals.Add(disposeFile destFileName) yield source.WithFileName(destFileName) ] - try - disposeList, rawCompile outputFilePath isExe options targetFramework sources - with - | _ -> - disposeList.Dispose() - reraise() + rawCompile outputFilePath isExe options targetFramework sources let assertErrors libAdjust ignoreWarnings (errors: FSharpDiagnostic []) expectedErrors = let errorMessage (error: FSharpDiagnostic) = @@ -520,7 +509,29 @@ module rec CompilerAssertHelpers = finally try Directory.Delete(tempDir, true) with | _ -> () - let rec evaluateReferences (outputPath:DirectoryInfo) (disposals: ResizeArray) ignoreWarnings (cmpl: Compilation) : string[] * string list = + let rec compileCompilationAux outputDirectory ignoreWarnings (cmpl: Compilation) : (FSharpDiagnostic[] * int * string) * string list = + + let compilationRefs, deps = evaluateReferences outputDirectory ignoreWarnings cmpl + let isExe, sources, options, targetFramework, name = + match cmpl with + | Compilation(sources, output, options, targetFramework, _, name, _) -> + (match output with | Module -> false | Library -> false | Exe -> true), // isExe + sources, + options, + targetFramework, + name + + let res = compileDisposable outputDirectory isExe (Array.append options compilationRefs) targetFramework name sources + + let deps2 = + compilationRefs + |> Array.filter (fun x -> not (x.Contains("--staticlink"))) + |> Array.map (fun x -> x.Replace("-r:", String.Empty)) + |> List.ofArray + + res, (deps @ deps2) + + and evaluateReferences (outputPath:DirectoryInfo) ignoreWarnings (cmpl: Compilation) : string[] * string list = match cmpl with | Compilation(_, _, _, _, cmpls, _, _) -> let compiledRefs = @@ -528,14 +539,13 @@ module rec CompilerAssertHelpers = |> List.map (fun cmpl -> match cmpl with | CompilationReference (cmpl, staticLink) -> - compileCompilationAux outputPath disposals ignoreWarnings cmpl, staticLink + compileCompilationAux outputPath ignoreWarnings cmpl, staticLink | TestCompilationReference (cmpl) -> let fileName = match cmpl with | TestCompilation.CSharp c when not (String.IsNullOrWhiteSpace c.AssemblyName) -> c.AssemblyName | _ -> getTemporaryFileNameInDirectory outputPath.FullName let tmp = Path.Combine(outputPath.FullName, Path.ChangeExtension(fileName, ".dll")) - disposals.Add({ new IDisposable with member _.Dispose() = File.Delete tmp }) cmpl.EmitAsFile tmp (([||], 0, tmp), []), false) @@ -559,38 +569,9 @@ module rec CompilerAssertHelpers = compilationRefs, deps - let compileCompilationAux outputDirectory (disposals: ResizeArray) ignoreWarnings (cmpl: Compilation) : (FSharpDiagnostic[] * int * string) * string list = - - let compilationRefs, deps = evaluateReferences outputDirectory disposals ignoreWarnings cmpl - let isExe, sources, options, targetFramework, name = - match cmpl with - | Compilation(sources, output, options, targetFramework, _, name, _) -> - (match output with | Module -> false | Library -> false | Exe -> true), // isExe - sources, - options, - targetFramework, - name - - let disposal, res = compileDisposable outputDirectory isExe (Array.append options compilationRefs) targetFramework name sources - disposals.Add(disposal) - - let deps2 = - compilationRefs - |> Array.filter (fun x -> not (x.Contains("--staticlink"))) - |> Array.map (fun x -> x.Replace("-r:", String.Empty)) - |> List.ofArray - - res, (deps @ deps2) - let compileCompilation ignoreWarnings (cmpl: Compilation) f = - let disposals = ResizeArray() - try - let outputDirectory = DirectoryInfo(createTemporaryDirectory "compileCompilation") - disposals.Add({ new IDisposable with member _.Dispose() = try File.Delete (outputDirectory.FullName) with | _ -> () }) - f (compileCompilationAux outputDirectory disposals ignoreWarnings cmpl) - finally - disposals - |> Seq.iter (fun x -> x.Dispose()) + let outputDirectory = DirectoryInfo(createTemporaryDirectory "compileCompilation") + f (compileCompilationAux outputDirectory ignoreWarnings cmpl) // NOTE: This function will not clean up all the compiled projects after itself. // The reason behind is so we can compose verification of test runs easier. @@ -602,41 +583,13 @@ module rec CompilerAssertHelpers = | Compilation _ -> DirectoryInfo(createTemporaryDirectory "returnCompilation") outputDirectory.Create() - compileCompilationAux outputDirectory (ResizeArray()) ignoreWarnings cmpl - - let captureConsoleOutputs (func: unit -> unit) = - let out = Console.Out - let err = Console.Error + compileCompilationAux outputDirectory ignoreWarnings cmpl - let stdout = StringBuilder () - let stderr = StringBuilder () + let unwrapException (ex: exn) = ex.InnerException |> Option.ofObj |> Option.map _.Message |> Option.defaultValue ex.Message - use outWriter = new StringWriter (stdout) - use errWriter = new StringWriter (stderr) + let executeBuiltAppAndReturnResult (outputFilePath: string) (deps: string list) isFsx = + executeBuiltApp outputFilePath deps isFsx - let succeeded, exn = - try - try - Console.SetOut outWriter - Console.SetError errWriter - func () - true, None - with e -> - let errorMessage = if e.InnerException <> null then e.InnerException.ToString() else e.ToString() - stderr.Append errorMessage |> ignore - false, Some e - finally - Console.SetOut out - Console.SetError err - outWriter.Close() - errWriter.Close() - - succeeded, stdout.ToString(), stderr.ToString(), exn - - let executeBuiltAppAndReturnResult (outputFilePath: string) (deps: string list) isFsx : (int * string * string) = - let succeeded, stdout, stderr, _ = executeBuiltApp outputFilePath deps isFsx - let exitCode = if succeeded then 0 else -1 - exitCode, stdout, stderr let executeBuiltAppNewProcessAndReturnResult (outputFilePath: string) : (int * string * string) = #if !NETCOREAPP @@ -658,12 +611,8 @@ module rec CompilerAssertHelpers = }""" let runtimeconfigPath = Path.ChangeExtension(outputFilePath, ".runtimeconfig.json") File.WriteAllText(runtimeconfigPath, runtimeconfig) - use _disposal = - { new IDisposable with - member _.Dispose() = try File.Delete runtimeconfigPath with | _ -> () } #endif - let timeout = 30000 - let exitCode, output, errors = Commands.executeProcess (Some fileName) arguments (Path.GetDirectoryName(outputFilePath)) timeout + let exitCode, output, errors = Commands.executeProcess (Some fileName) arguments (Path.GetDirectoryName(outputFilePath)) (exitCode, output |> String.concat "\n", errors |> String.concat "\n") open CompilerAssertHelpers @@ -677,7 +626,7 @@ type CompilerAssert private () = if errors.Length > 0 then Assert.Fail (sprintf "Compile had warnings and/or errors: %A" errors) - executeBuiltApp outputExe [] false |> ignore + executeBuiltApp outputExe [] false ) static let compileLibraryAndVerifyILWithOptions options (source: SourceCodeFileKind) (f: ILVerifier -> unit) = @@ -690,7 +639,6 @@ type CompilerAssert private () = f (ILVerifier outputFilePath) ) - static let compileLibraryAndVerifyDebugInfoWithOptions options (expectedFile: string) (source: SourceCodeFileKind) = let options = [| yield! options; yield"--test:DumpDebugInfo" |] compile false options source (fun (errors, _, outputFilePath) -> @@ -713,8 +661,6 @@ Updated automatically, please check diffs in your pull request, changes must be """ ) - static member Checker = checker - static member DefaultProjectOptions = defaultProjectOptions static member GenerateFsInputPath() = @@ -740,10 +686,11 @@ Updated automatically, please check diffs in your pull request, changes must be static member ExecuteAndReturnResult (outputFilePath: string, isFsx: bool, deps: string list, newProcess: bool) = // If we execute in-process (true by default), then the only way of getting STDOUT is to redirect it to SB, and STDERR is from catching an exception. - if not newProcess then - executeBuiltAppAndReturnResult outputFilePath deps isFsx - else - executeBuiltAppNewProcessAndReturnResult outputFilePath + if not newProcess then + let exitCode = try executeBuiltAppAndReturnResult outputFilePath deps isFsx; 0 with _ -> -1 + exitCode, ParallelConsole.OutText, ParallelConsole.ErrorText + else + executeBuiltAppNewProcessAndReturnResult outputFilePath static member Execute(cmpl: Compilation, ?ignoreWarnings, ?beforeExecute, ?newProcess, ?onOutput) = @@ -767,14 +714,14 @@ Updated automatically, please check diffs in your pull request, changes must be Assert.Fail errors onOutput output else - let _succeeded, _stdout, _stderr, exn = executeBuiltApp outputFilePath deps false - exn |> Option.iter raise) + executeBuiltApp outputFilePath deps false + ) static member ExecutionHasOutput(cmpl: Compilation, expectedOutput: string) = CompilerAssert.Execute(cmpl, newProcess = true, onOutput = (fun output -> Assert.Equal(expectedOutput, output))) static member Pass (source: string) = - let parseResults, fileAnswer = checker.ParseAndCheckFileInProject("test.fs", 0, SourceText.ofString source, defaultProjectOptions TargetFramework.Current) |> Async.RunImmediate + let parseResults, fileAnswer = TestContext.Checker.ParseAndCheckFileInProject("test.fs", 0, SourceText.ofString source, defaultProjectOptions TargetFramework.Current) |> Async.RunImmediate Assert.Empty(parseResults.Diagnostics) @@ -788,7 +735,7 @@ Updated automatically, please check diffs in your pull request, changes must be let defaultOptions = defaultProjectOptions TargetFramework.Current let options = { defaultOptions with OtherOptions = Array.append options defaultOptions.OtherOptions} - let parseResults, fileAnswer = checker.ParseAndCheckFileInProject("test.fs", 0, SourceText.ofString source, options) |> Async.RunImmediate + let parseResults, fileAnswer = TestContext.Checker.ParseAndCheckFileInProject("test.fs", 0, SourceText.ofString source, options) |> Async.RunImmediate Assert.Empty(parseResults.Diagnostics) @@ -802,7 +749,7 @@ Updated automatically, please check diffs in your pull request, changes must be let absoluteSourceFile = System.IO.Path.Combine(sourceDirectory, sourceFile) let parseResults, fileAnswer = let defaultOptions = defaultProjectOptions TargetFramework.Current - checker.ParseAndCheckFileInProject( + TestContext.Checker.ParseAndCheckFileInProject( sourceFile, 0, SourceText.ofString (File.ReadAllText absoluteSourceFile), @@ -833,7 +780,7 @@ Updated automatically, please check diffs in your pull request, changes must be let errors = let parseResults, fileAnswer = let defaultOptions = defaultProjectOptions TargetFramework.Current - checker.ParseAndCheckFileInProject( + TestContext.Checker.ParseAndCheckFileInProject( name, 0, SourceText.ofString source, @@ -859,7 +806,7 @@ Updated automatically, please check diffs in your pull request, changes must be let errors = let parseResults, fileAnswer = let defaultOptions = defaultProjectOptions TargetFramework.Current - checker.ParseAndCheckFileInProject( + TestContext.Checker.ParseAndCheckFileInProject( "test.fs", 0, SourceText.ofString source, @@ -880,7 +827,7 @@ Updated automatically, please check diffs in your pull request, changes must be static member ParseAndTypeCheck(options, name, source: string) = let parseResults, fileAnswer = let defaultOptions = defaultProjectOptionsForFilePath name TargetFramework.Current - checker.ParseAndCheckFileInProject( + TestContext.Checker.ParseAndCheckFileInProject( name, 0, SourceText.ofString source, @@ -903,7 +850,7 @@ Updated automatically, please check diffs in your pull request, changes must be let errors = let parseResults, fileAnswer = let defaultOptions = defaultProjectOptions TargetFramework.Current - checker.ParseAndCheckFileInProject( + TestContext.Checker.ParseAndCheckFileInProject( "test.fs", 0, SourceText.ofString source, @@ -951,7 +898,7 @@ Updated automatically, please check diffs in your pull request, changes must be } )) - let snapshot = FSharpProjectSnapshot.FromOptions(projectOptions, getFileSnapshot) |> Async.RunSynchronously + let snapshot = FSharpProjectSnapshot.FromOptions(projectOptions, getFileSnapshot) |> Async.RunImmediate checker.ParseAndCheckProject(snapshot) else @@ -1057,7 +1004,7 @@ Updated automatically, please check diffs in your pull request, changes must be { FSharpParsingOptions.Default with SourceFiles = [| sourceFileName |] LangVersionText = langVersion } - checker.ParseFile(sourceFileName, SourceText.ofString source, parsingOptions) |> Async.RunImmediate + TestContext.Checker.ParseFile(sourceFileName, SourceText.ofString source, parsingOptions) |> Async.RunImmediate static member ParseWithErrors (source: string, ?langVersion: string) = fun expectedParseErrors -> let parseResults = CompilerAssert.Parse (source, ?langVersion=langVersion) diff --git a/tests/FSharp.Test.Utilities/DirectoryAttribute.fs b/tests/FSharp.Test.Utilities/DirectoryAttribute.fs index c1561fa6c9b..1266bc295a8 100644 --- a/tests/FSharp.Test.Utilities/DirectoryAttribute.fs +++ b/tests/FSharp.Test.Utilities/DirectoryAttribute.fs @@ -9,6 +9,7 @@ open Xunit.Sdk open FSharp.Compiler.IO open FSharp.Test.Compiler open FSharp.Test.Utilities +open TestFramework /// Attribute to use with Xunit's TheoryAttribute. /// Takes a directory, relative to current test suite's root. @@ -22,7 +23,6 @@ type DirectoryAttribute(dir: string) = invalidArg "dir" "Directory cannot be null, empty or whitespace only." let dirInfo = normalizePathSeparator (Path.GetFullPath(dir)) - let outputDirectory methodName extraDirectory = getTestOutputDirectory dir methodName extraDirectory let mutable baselineSuffix = "" let mutable includes = Array.empty @@ -31,19 +31,8 @@ type DirectoryAttribute(dir: string) = | true -> Some (File.ReadAllText path) | _ -> None - let createCompilationUnit path (filename: string) methodName multipleFiles = - // if there are multiple files being processed, add extra directory for each test to avoid reference file conflicts - let extraDirectory = - if multipleFiles then - let extension = Path.GetExtension(filename) - filename.Substring(0, filename.Length - extension.Length) // remove .fs/the extension - |> normalizeName - else "" - let outputDirectory = outputDirectory methodName extraDirectory - let outputDirectoryPath = - match outputDirectory with - | Some path -> path.FullName - | None -> failwith "Can't set the output directory" + let createCompilationUnit path (filename: string) = + let outputDirectoryPath = createTemporaryDirectory "dir" let sourceFilePath = normalizePathSeparator (path ++ filename) let fsBslFilePath = sourceFilePath + baselineSuffix + ".err.bsl" let ilBslFilePath = @@ -97,7 +86,7 @@ type DirectoryAttribute(dir: string) = Name = Some filename IgnoreWarnings = false References = [] - OutputDirectory = outputDirectory + OutputDirectory = Some (DirectoryInfo(outputDirectoryPath)) TargetFramework = TargetFramework.Current StaticLink = false } |> FS @@ -107,7 +96,7 @@ type DirectoryAttribute(dir: string) = member _.BaselineSuffix with get() = baselineSuffix and set v = baselineSuffix <- v member _.Includes with get() = includes and set v = includes <- v - override _.GetData(method: MethodInfo) = + override _.GetData _ = if not (Directory.Exists(dirInfo)) then failwith (sprintf "Directory does not exist: \"%s\"." dirInfo) @@ -127,8 +116,6 @@ type DirectoryAttribute(dir: string) = if not <| FileSystem.FileExistsShim(f) then failwithf "Requested file \"%s\" not found.\nAll files: %A.\nIncludes:%A." f allFiles includes - let multipleFiles = fsFiles |> Array.length > 1 - fsFiles - |> Array.map (fun fs -> createCompilationUnit dirInfo fs method.Name multipleFiles) + |> Array.map (fun fs -> createCompilationUnit dirInfo fs) |> Seq.map (fun c -> [| c |]) diff --git a/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj b/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj index 7ccc5306751..5ec38117917 100644 --- a/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj +++ b/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj @@ -10,6 +10,7 @@ true false false + false $(OtherFlags) --warnon:1182 true @@ -28,6 +29,7 @@ scriptlib.fsx + @@ -42,6 +44,10 @@ + + + + @@ -58,6 +64,9 @@ + + + @@ -99,7 +108,7 @@ - + diff --git a/tests/FSharp.Test.Utilities/ILChecker.fs b/tests/FSharp.Test.Utilities/ILChecker.fs index 4474ef01c10..e09d5134af2 100644 --- a/tests/FSharp.Test.Utilities/ILChecker.fs +++ b/tests/FSharp.Test.Utilities/ILChecker.fs @@ -15,8 +15,7 @@ module ILChecker = let private exec exe args = let arguments = args |> String.concat " " - let timeout = 30000 - let exitCode, _output, errors = Commands.executeProcess (Some exe) arguments "" timeout + let exitCode, _output, errors = Commands.executeProcess (Some exe) arguments "" let errors = errors |> String.concat Environment.NewLine errors, exitCode diff --git a/tests/FSharp.Test.Utilities/Peverifier.fs b/tests/FSharp.Test.Utilities/Peverifier.fs index 35db5b208fb..7b9ff436b15 100644 --- a/tests/FSharp.Test.Utilities/Peverifier.fs +++ b/tests/FSharp.Test.Utilities/Peverifier.fs @@ -24,8 +24,7 @@ module PEVerifier = let private exec exe args = let arguments = args |> String.concat " " - let timeout = 30000 - let exitCode, _output, errors = Commands.executeProcess (Some exe) arguments "" timeout + let exitCode, _output, errors = Commands.executeProcess (Some exe) arguments "" let errors = errors |> String.concat Environment.NewLine errors, exitCode diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 78feb049435..b1dbe3ce3fa 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -31,6 +31,8 @@ open FSharp.Compiler.Text open Xunit +open FSharp.Test.Utilities + open OpenTelemetry open OpenTelemetry.Resources open OpenTelemetry.Trace @@ -224,9 +226,7 @@ let sourceFile fileId deps = IsPhysicalFile = false } -let OptionsCache = ConcurrentDictionary() - - +let OptionsCache = ConcurrentDictionary<_, FSharpProjectOptions>() type SyntheticProject = @@ -295,7 +295,7 @@ type SyntheticProject = member this.GetProjectOptions(checker: FSharpChecker) = - let cacheKey = + let key = this.GetAllFiles() |> List.collect (fun (p, f) -> [ p.Name @@ -305,53 +305,55 @@ type SyntheticProject = this.FrameworkReferences, this.NugetReferences - if not (OptionsCache.ContainsKey cacheKey) then - OptionsCache[cacheKey] <- - use _ = Activity.start "SyntheticProject.GetProjectOptions" [ "project", this.Name ] + let factory _ = + use _ = Activity.start "SyntheticProject.GetProjectOptions" [ "project", this.Name ] - let referenceScript = - seq { - yield! this.FrameworkReferences |> Seq.map getFrameworkReference + let referenceScript = + seq { + yield! this.FrameworkReferences |> Seq.map getFrameworkReference + if not this.NugetReferences.IsEmpty then this.NugetReferences |> getNugetReferences (Some "https://api.nuget.org/v3/index.json") - } - |> String.concat "\n" - - let baseOptions, _ = - checker.GetProjectOptionsFromScript( - "file.fsx", - SourceText.ofString referenceScript, - assumeDotNetFramework = false - ) - |> Async.RunSynchronously - - { - ProjectFileName = this.ProjectFileName - ProjectId = None - SourceFiles = - [| for f in this.SourceFiles do - if f.HasSignatureFile then - this.ProjectDir ++ f.SignatureFileName - - this.ProjectDir ++ f.FileName |] - OtherOptions = - Set [ - yield! baseOptions.OtherOptions - "--optimize+" - for p in this.DependsOn do - $"-r:{p.OutputFilename}" - yield! this.OtherOptions ] - |> Set.toArray - ReferencedProjects = - [| for p in this.DependsOn do - FSharpReferencedProject.FSharpReference(p.OutputFilename, p.GetProjectOptions checker) |] - IsIncompleteTypeCheckEnvironment = false - UseScriptResolutionRules = this.UseScriptResolutionRules - LoadTime = DateTime() - UnresolvedReferences = None - OriginalLoadReferences = [] - Stamp = None } - - OptionsCache[cacheKey] + } + |> String.concat "\n" + + let baseOptions, _ = + checker.GetProjectOptionsFromScript( + "file.fsx", + SourceText.ofString referenceScript, + assumeDotNetFramework = false + ) + |> Async.RunImmediate + + { + ProjectFileName = this.ProjectFileName + ProjectId = None + SourceFiles = + [| for f in this.SourceFiles do + if f.HasSignatureFile then + this.ProjectDir ++ f.SignatureFileName + + this.ProjectDir ++ f.FileName |] + OtherOptions = + Set [ + yield! baseOptions.OtherOptions + "--optimize+" + for p in this.DependsOn do + $"-r:{p.OutputFilename}" + yield! this.OtherOptions ] + |> Set.toArray + ReferencedProjects = + [| for p in this.DependsOn do + FSharpReferencedProject.FSharpReference(p.OutputFilename, p.GetProjectOptions checker) |] + IsIncompleteTypeCheckEnvironment = false + UseScriptResolutionRules = this.UseScriptResolutionRules + LoadTime = DateTime() + UnresolvedReferences = None + OriginalLoadReferences = [] + Stamp = None } + + + OptionsCache.GetOrAdd(key, factory) + member this.GetAllProjects() = [ this @@ -951,7 +953,7 @@ type ProjectWorkflowBuilder ?enablePartialTypeChecking ) = - let useTransparentCompiler = defaultArg useTransparentCompiler CompilerAssertHelpers.UseTransparentCompiler + let useTransparentCompiler = defaultArg useTransparentCompiler TestContext.UseTransparentCompiler let useGetSource = not useTransparentCompiler && defaultArg useGetSource false let useChangeNotifications = not useTransparentCompiler && defaultArg useChangeNotifications false let autoStart = defaultArg autoStart true @@ -1027,11 +1029,12 @@ type ProjectWorkflowBuilder member this.Execute(workflow: Async) = try - Async.RunSynchronously(workflow, timeout = defaultArg runTimeout 600_000) + // We don't want the defaultCancellationToken. + Async.RunSynchronously(workflow, cancellationToken = Threading.CancellationToken.None, ?timeout = runTimeout) finally if initialContext.IsNone && not isExistingProject then this.DeleteProjectDir() - activity |> Option.iter (fun x -> x.Dispose()) + activity |> Option.iter (fun x -> if not (isNull x) then x.Dispose()) tracerProvider |> Option.iter (fun x -> x.ForceFlush() |> ignore x.Dispose()) @@ -1135,14 +1138,10 @@ type ProjectWorkflowBuilder async { let! ctx = workflow - use activity = - Activity.start "ProjectWorkflowBuilder.CheckFile" [ Activity.Tags.project, initialProject.Name; "fileId", fileId ] - let! results = + use _ = Activity.start "ProjectWorkflowBuilder.CheckFile" [ Activity.Tags.project, initialProject.Name; "fileId", fileId ] checkFile fileId ctx.Project checker - activity.Dispose() - let oldSignature = ctx.Signatures[fileId] let newSignature = getSignature results diff --git a/tests/FSharp.Test.Utilities/ScriptHelpers.fs b/tests/FSharp.Test.Utilities/ScriptHelpers.fs index 688941934c9..9c4130dff47 100644 --- a/tests/FSharp.Test.Utilities/ScriptHelpers.fs +++ b/tests/FSharp.Test.Utilities/ScriptHelpers.fs @@ -76,12 +76,10 @@ type FSharpScript(?additionalArgs: string[], ?quiet: bool, ?langVersion: LangVer let outWriter = new EventedTextWriter() let errorWriter = new EventedTextWriter() - let previousIn, previousOut, previousError = Console.In, Console.Out, Console.Error - do - Console.SetIn inReader - Console.SetOut outWriter - Console.SetError errorWriter + ParallelConsole.localIn.Set inReader + ParallelConsole.localOut.Set outWriter + ParallelConsole.localError.Set errorWriter let fsi = FsiEvaluationSession.Create (config, argv, stdin, stdout, stderr) @@ -93,9 +91,9 @@ type FSharpScript(?additionalArgs: string[], ?quiet: bool, ?langVersion: LangVer member _.ErrorProduced = errorWriter.LineWritten - member _.GetOutput() = string outWriter + member _.GetOutput() = ParallelConsole.OutText - member _.GetErrorOutput() = string errorWriter + member _.GetErrorOutput() = ParallelConsole.ErrorText member this.Eval(code: string, ?cancellationToken: CancellationToken, ?desiredCulture: Globalization.CultureInfo) = let originalCulture = Thread.CurrentThread.CurrentCulture @@ -127,9 +125,6 @@ type FSharpScript(?additionalArgs: string[], ?quiet: bool, ?langVersion: LangVer interface IDisposable with member this.Dispose() = ((this.Fsi) :> IDisposable).Dispose() - Console.SetIn previousIn - Console.SetOut previousOut - Console.SetError previousError [] module TestHelpers = diff --git a/tests/FSharp.Test.Utilities/ScriptingShims.fsx b/tests/FSharp.Test.Utilities/ScriptingShims.fsx index 392d2b002b6..be9ce9142c5 100644 --- a/tests/FSharp.Test.Utilities/ScriptingShims.fsx +++ b/tests/FSharp.Test.Utilities/ScriptingShims.fsx @@ -2,13 +2,7 @@ namespace global [] module GlobalShims = - - let errorStringWriter = new System.IO.StringWriter() - let oldConsoleError = System.Console.Error - do System.Console.SetError(errorStringWriter) - let exit (code:int) = - System.Console.SetError(oldConsoleError) - if code=0 then + if code = 0 then () - else failwith $"Script called function 'exit' with code={code} and collected in stderr: {errorStringWriter.ToString()}" \ No newline at end of file + else failwith $"Script called function 'exit' with code={code}." diff --git a/tests/FSharp.Test.Utilities/TestFramework.fs b/tests/FSharp.Test.Utilities/TestFramework.fs index 6e1611beb5c..bdc2d31ed56 100644 --- a/tests/FSharp.Test.Utilities/TestFramework.fs +++ b/tests/FSharp.Test.Utilities/TestFramework.fs @@ -4,22 +4,23 @@ module TestFramework open System open System.IO -open System.Reflection open System.Diagnostics +open System.Reflection open Scripting open Xunit open FSharp.Compiler.IO -open Xunit.Sdk +open FSharp.Test let getShortId() = Guid.NewGuid().ToString().[..7] // Temporary directory is TempPath + "/FSharp.Test.Utilities/yyy-MM-dd-xxxxxxx/" let tempDirectoryOfThisTestRun = - let tempDir = Path.GetTempPath() + let temp = Path.GetTempPath() let today = DateTime.Now.ToString("yyyy-MM-dd") - DirectoryInfo(tempDir) - .CreateSubdirectory($"FSharp.Test.Utilities/{today}-{getShortId()}") - .FullName + let directory = + DirectoryInfo(temp).CreateSubdirectory($"FSharp.Test.Utilities/{today}-{getShortId()}") + + directory.FullName let createTemporaryDirectory (part: string) = DirectoryInfo(tempDirectoryOfThisTestRun) @@ -62,7 +63,7 @@ module Commands = // 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 (timeout:int) = + let executeProcess pathToExe arguments workingDir = match pathToExe with | Some path -> let commandLine = ResizeArray() @@ -103,11 +104,7 @@ module Commands = if p.Start() then p.BeginOutputReadLine() p.BeginErrorReadLine() - if not(p.WaitForExit(timeout)) then - // Timed out resolving throw a diagnostic. - raise (new TimeoutException(sprintf "Timeout executing command '%s' '%s'" (psi.FileName) (psi.Arguments))) - else - p.WaitForExit() + p.WaitForExit() #if DEBUG let workingDir' = if workingDir = "" @@ -428,13 +425,12 @@ let logConfig (cfg: TestConfig) = log "PEVERIFY = %s" cfg.PEVERIFY log "---------------------------------------------------------------" -let checkOutputPassed (output: string) = - Assert.True(output.Contains "TEST PASSED OK", $"Output does not contain 'TEST PASSED OK':\n{output}") - +let outputPassed (output: string) = output.Contains "TEST PASSED OK" + let checkResultPassed result = match result with | CmdResult.ErrorLevel (msg1, err) -> Assert.Fail (sprintf "%s. ERRORLEVEL %d" msg1 err) - | CmdResult.Success output -> checkOutputPassed output + | CmdResult.Success output -> Assert.True(outputPassed output, $"Output does not contain 'TEST PASSED OK':\n{output}") let checkResult result = match result with @@ -545,7 +541,7 @@ module Command = | :? System.IO.IOException -> //input closed is ok if process is closed () } - sources |> pipeFile |> Async.RunSynchronously + Async.RunSynchronously(sources |> pipeFile, cancellationToken = Threading.CancellationToken.None) let inF fCont cmdArgs = match redirect.Input with @@ -569,8 +565,7 @@ module Command = | Output r -> use writer = openWrite r use outFile = redirectTo writer - use toLog = redirectToLog () - fCont { cmdArgs with RedirectOutput = Some (outFile.Post); RedirectError = Some (toLog.Post) } + fCont { cmdArgs with RedirectOutput = Some (outFile.Post); RedirectError = Some ignore } | OutputAndError (r1,r2) -> use writer1 = openWrite r1 use writer2 = openWrite r2 @@ -584,8 +579,7 @@ module Command = | Error r -> use writer = openWrite r use outFile = redirectTo writer - use toLog = redirectToLog () - fCont { cmdArgs with RedirectOutput = Some (toLog.Post); RedirectError = Some (outFile.Post) } + fCont { cmdArgs with RedirectOutput = Some ignore; RedirectError = Some (outFile.Post) } let exec cmdArgs = log "%s" (logExec dir path args redirect) diff --git a/tests/FSharp.Test.Utilities/Utilities.fs b/tests/FSharp.Test.Utilities/Utilities.fs index 6ed885d4e44..152b1001fd4 100644 --- a/tests/FSharp.Test.Utilities/Utilities.fs +++ b/tests/FSharp.Test.Utilities/Utilities.fs @@ -18,6 +18,7 @@ open System.Collections.Generic open FSharp.Compiler.CodeAnalysis open Newtonsoft.Json open Newtonsoft.Json.Linq +open Xunit.Sdk type TheoryForNETCOREAPPAttribute() = @@ -40,25 +41,10 @@ type FactForDESKTOPAttribute() = // This file mimics how Roslyn handles their compilation references for compilation testing module Utilities = - type RedirectConsole() = - let oldStdOut = Console.Out - let oldStdErr = Console.Error - let newStdOut = new StringWriter() - let newStdErr = new StringWriter() - do Console.SetOut(newStdOut) - do Console.SetError(newStdErr) - member _.Output () = string newStdOut - - member _.ErrorOutput () =string newStdErr - - interface IDisposable with - member _.Dispose() = - Console.SetOut(oldStdOut) - Console.SetError(oldStdErr) type Async with static member RunImmediate (computation: Async<'T>, ?cancellationToken ) = - let cancellationToken = defaultArg cancellationToken Async.DefaultCancellationToken + let cancellationToken = defaultArg cancellationToken CancellationToken.None let ts = TaskCompletionSource<'T>() let task = ts.Task Async.StartWithContinuations( @@ -69,20 +55,6 @@ module Utilities = cancellationToken) task.Result - /// Disposable type to implement a simple resolve handler that searches the currently loaded assemblies to see if the requested assembly is already loaded. - type AlreadyLoadedAppDomainResolver () = - let resolveHandler = - ResolveEventHandler(fun _ args -> - let assemblies = AppDomain.CurrentDomain.GetAssemblies() - let assembly = assemblies |> Array.tryFind(fun a -> String.Compare(a.FullName, args.Name,StringComparison.OrdinalIgnoreCase) = 0) - assembly |> Option.defaultValue Unchecked.defaultof - ) - do AppDomain.CurrentDomain.add_AssemblyResolve(resolveHandler) - - interface IDisposable with - member this.Dispose() = AppDomain.CurrentDomain.remove_AssemblyResolve(resolveHandler) - - [] type TargetFramework = | NetStandard20 @@ -244,8 +216,7 @@ let main argv = 0""" File.WriteAllText(directoryBuildPropsFileName, directoryBuildProps) File.WriteAllText(directoryBuildTargetsFileName, directoryBuildTargets) - let timeout = 120000 - let exitCode, dotnetoutput, dotneterrors = Commands.executeProcess (Some config.DotNetExe) "build" projectDirectory timeout + let exitCode, dotnetoutput, dotneterrors = Commands.executeProcess (Some config.DotNetExe) "build" projectDirectory if exitCode <> 0 || errors.Length > 0 then errors <- dotneterrors diff --git a/tests/FSharp.Test.Utilities/XunitHelpers.fs b/tests/FSharp.Test.Utilities/XunitHelpers.fs new file mode 100644 index 00000000000..dfe0623fe6f --- /dev/null +++ b/tests/FSharp.Test.Utilities/XunitHelpers.fs @@ -0,0 +1,67 @@ +namespace FSharp.Test + +open System +open System.IO +open System.Text +open System.Threading + +open Xunit.Sdk + +module internal ParallelConsole = + /// Redirects reads performed on different threads or async execution contexts to the relevant TextReader held by AsyncLocal. + type RedirectingTextReader(initial: TextReader) = + inherit TextReader() + let holder = AsyncLocal<_>() + do holder.Value <- initial + + override _.Peek() = holder.Value.Peek() + override _.Read() = holder.Value.Read() + member _.Set (reader: TextReader) = holder.Value <- reader + + /// Redirects writes performed on different threads or async execution contexts to the relevant TextWriter held by AsyncLocal. + type RedirectingTextWriter(initial: TextWriter) = + inherit TextWriter() + let holder = AsyncLocal<_>() + do holder.Value <- initial + + override _.Encoding = Encoding.UTF8 + override _.Write(value: char) = holder.Value.Write(value) + override _.Write(value: string) = holder.Value.Write(value) + override _.WriteLine(value: string) = holder.Value.WriteLine(value) + member _.Value = holder.Value + member _.Set (writer: TextWriter) = holder.Value <- writer + + let localIn = new RedirectingTextReader(TextReader.Null) + let localOut = new RedirectingTextWriter(TextWriter.Null) + let localError = new RedirectingTextWriter(TextWriter.Null) + + let initStreamsCapture () = + Console.SetIn localIn + Console.SetOut localOut + Console.SetError localError + + let resetWriters() = + new StringWriter() |> localOut.Set + new StringWriter() |> localError.Set + +type ParallelConsole = + static member OutText = + Console.Out.Flush() + string ParallelConsole.localOut.Value + + static member ErrorText = + Console.Error.Flush() + string ParallelConsole.localError.Value + +[] +type ResetConsoleWriters() = + inherit BeforeAfterTestAttribute() + override _.Before (_methodUnderTest: Reflection.MethodInfo): unit = + // Ensure fresh empty writers before each individual test. + ParallelConsole.resetWriters() + +type TestRun(sink) = + inherit XunitTestFramework(sink) + do + MessageSink.sinkWriter |> ignore + ParallelConsole.initStreamsCapture() diff --git a/tests/FSharp.Test.Utilities/XunitSetup.fs b/tests/FSharp.Test.Utilities/XunitSetup.fs new file mode 100644 index 00000000000..d029c4ddbf6 --- /dev/null +++ b/tests/FSharp.Test.Utilities/XunitSetup.fs @@ -0,0 +1,12 @@ +namespace FSharp.Test + +open Xunit + +[] +type DoNotRunInParallel = class end + +module XUnitSetup = + + [] + [] + do () diff --git a/tests/fsharp/Compiler/Libraries/Core/Async/AsyncTests.fs b/tests/fsharp/Compiler/Libraries/Core/Async/AsyncTests.fs index 708e7e58e2e..3b83b97db7a 100644 --- a/tests/fsharp/Compiler/Libraries/Core/Async/AsyncTests.fs +++ b/tests/fsharp/Compiler/Libraries/Core/Async/AsyncTests.fs @@ -8,7 +8,7 @@ open FSharp.Test module AsyncTests = // Regression for FSHARP1.0:5969 // Async.StartChild: error when wait async is executed more than once - [] + [] let ``Execute Async multiple times``() = CompilerAssert.CompileExeAndRun """ @@ -24,13 +24,12 @@ let a = async { return result } |> Async.RunSynchronously -exit 0 """ // Regression for FSHARP1.0:5970 // Async.StartChild: race in implementation of ResultCell in FSharp.Core - [] + [] let ``Joining StartChild``() = CompilerAssert.CompileExeAndRun """ @@ -54,12 +53,10 @@ let r = with _ -> (0,0) -exit 0 - """ // Regression test for FSHARP1.0:6086 - [] + [] let ``Mailbox Async dot not StackOverflow``() = CompilerAssert.CompileExeAndRun """ @@ -128,12 +125,11 @@ for meet in meets do printfn "%d" meet printfn "Total: %d in %O" (Seq.sum meets) (watch.Elapsed) -exit 0 """ // Regression for FSHARP1.0:5971 - [] + [] let ``StartChild do not throw ObjectDisposedException``() = CompilerAssert.CompileExeAndRun """ @@ -142,10 +138,9 @@ module M let b = async {return 5} |> Async.StartChild printfn "%A" (b |> Async.RunSynchronously |> Async.RunSynchronously) -exit 0 """ - [] + [] let ``StartChild test Trampoline HijackLimit``() = CompilerAssert.CompileExeAndRun """ @@ -164,5 +159,4 @@ let r = () } |> Async.RunSynchronously -exit 0 """ diff --git a/tests/fsharp/Compiler/Service/MultiProjectTests.fs b/tests/fsharp/Compiler/Service/MultiProjectTests.fs index 9e89927220d..7a5b860d891 100644 --- a/tests/fsharp/Compiler/Service/MultiProjectTests.fs +++ b/tests/fsharp/Compiler/Service/MultiProjectTests.fs @@ -63,7 +63,7 @@ let test() = """ |> SourceText.ofString let _, checkAnswer = - CompilerAssert.Checker.ParseAndCheckFileInProject("test.fs", 0, fsText, fsOptions) + TestContext.Checker.ParseAndCheckFileInProject("test.fs", 0, fsText, fsOptions) |> Async.RunImmediate @@ -128,13 +128,13 @@ let test() = [] let ``Using a CSharp reference project in-memory and it gets GCed``() = let weakRef = AssertInMemoryCSharpReferenceIsValid() - CompilerAssert.Checker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() + TestContext.Checker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() GC.Collect(2, GCCollectionMode.Forced, true) Assert.shouldBeFalse(weakRef.IsAlive) [] let ``Using compiler service, file referencing a DLL will correctly update when the referenced DLL file changes``() = - let checker = CompilerAssert.Checker + let checker = TestContext.Checker // Create an assembly with the module Script1 and function x. let dllPath1 = diff --git a/tests/fsharp/FSharpSuite.Tests.fsproj b/tests/fsharp/FSharpSuite.Tests.fsproj index 6673da9f911..7afb977b6aa 100644 --- a/tests/fsharp/FSharpSuite.Tests.fsproj +++ b/tests/fsharp/FSharpSuite.Tests.fsproj @@ -17,9 +17,9 @@ - - scriptlib.fsx - + + XunitSetup.fs + diff --git a/tests/fsharp/TypeProviderTests.fs b/tests/fsharp/TypeProviderTests.fs index ab9f2e7e5ef..92e5220b398 100644 --- a/tests/fsharp/TypeProviderTests.fs +++ b/tests/fsharp/TypeProviderTests.fs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. #if INTERACTIVE //#r @"../../release/net40/bin/FSharp.Compiler.dll" @@ -233,35 +233,48 @@ let singleNegTest name = SingleTest.singleNegTest cfg name -[] -[] -[] -[] -[] -[] -[] -[] -[] -[] -[] -[] -[] -[] -[] -[] -[] -[] -[] -[] -[] -[] -[] -[] -[] -[] -[] -[] -let ``negative type provider tests`` (name:string) = singleNegTest name +module Neg1 = + [] + [] + [] + [] + [] + [] + [] + [] + [] + let ``negative type provider tests 1`` (name:string) = singleNegTest name + +module Neg2 = + [] + [] + [] + [] + [] + [] + [] + let ``negative type provider tests 2`` (name:string) = singleNegTest name + +module Neg3 = + [] + [] + [] + [] + [] + [] + [] + [] + let ``negative type provider tests 3`` (name:string) = singleNegTest name + +module Neg4 = + [] + [] + [] + [] + [] + [] + [] + let ``negative type provider tests 4`` (name:string) = singleNegTest name let splitAssembly subdir project = let cfg = testConfig project diff --git a/tests/fsharp/XunitHelpers.fs b/tests/fsharp/XunitHelpers.fs index c7e7493c046..a77c6e768e6 100644 --- a/tests/fsharp/XunitHelpers.fs +++ b/tests/fsharp/XunitHelpers.fs @@ -4,9 +4,6 @@ open Xunit module Assert = - [] - do() - let inline fail message = Assert.Fail message let inline failf fmt = Printf.kprintf fail fmt diff --git a/tests/fsharp/core/controlMailbox/test.fsx b/tests/fsharp/core/controlMailbox/test.fsx index 98d6a7d2f31..5aa65035ecd 100644 --- a/tests/fsharp/core/controlMailbox/test.fsx +++ b/tests/fsharp/core/controlMailbox/test.fsx @@ -6,9 +6,7 @@ module Core_controlMailBox #nowarn "40" // recursive references -#if NETCOREAPP open System.Threading.Tasks -#endif let biggerThanTrampoliningLimit = 10000 @@ -49,22 +47,27 @@ let checkQuiet s x1 x2 = (test s false; log (sprintf "expected: %A, got %A" x2 x1)) -let check s x1 x2 = +let check s x1 x2 = if x1 = x2 then test s true else (test s false; log (sprintf "expected: %A, got %A" x2 x1)) +let checkAsync s (x1: Task<_>) x2 = check s x1.Result x2 + open Microsoft.FSharp.Control -open Microsoft.FSharp.Control.WebExtensions module MailboxProcessorBasicTests = + let test() = check "c32398u6: MailboxProcessor null" - (let mb1 = new MailboxProcessor(fun inbox -> async { return () }) - mb1.Start(); - 100) - 100 + ( + let mb1 = new MailboxProcessor(fun inbox -> async { return () }) + mb1.Start() + 100 + ) + + 100 check "c32398u7: MailboxProcessor Receive/PostAndReply" @@ -197,9 +200,10 @@ module MailboxProcessorBasicTests = 200 for n in [0; 1; 100; 1000; 100000 ] do - check + checkAsync (sprintf "c32398u: MailboxProcessor Post/Receive, n=%d" n) - (let received = ref 0 + (task { + let received = ref 0 let mb1 = new MailboxProcessor(fun inbox -> async { for i in 0 .. n-1 do let! _ = inbox.Receive() @@ -208,48 +212,37 @@ module MailboxProcessorBasicTests = for i in 0 .. n-1 do mb1.Post(i) while !received < n do - if !received % 100 = 0 then - printfn "received = %d" !received -#if NETCOREAPP - Task.Delay(1).Wait() -#else - System.Threading.Thread.Sleep(1) -#endif - !received) + do! Task.Yield() + return !received}) n for timeout in [0; 10] do for n in [0; 1; 100] do - check + checkAsync (sprintf "c32398u: MailboxProcessor Post/TryReceive, n=%d, timeout=%d" n timeout) - (let received = ref 0 + (task { + let received = ref 0 let mb1 = new MailboxProcessor(fun inbox -> async { while !received < n do - let! msgOpt = inbox.TryReceive(timeout=timeout) - match msgOpt with - | None -> - do if !received % 100 = 0 then - printfn "timeout!, received = %d" !received - | Some _ -> do incr received }) + match! inbox.TryReceive(timeout=timeout) with + | Some _ -> incr received + | _ -> () + }) + + mb1.Post(0) + mb1.Start(); for i in 0 .. n-1 do -#if NETCOREAPP - Task.Delay(1).Wait(); -#else - System.Threading.Thread.Sleep(1) -#endif mb1.Post(i) + do! Task.Yield() while !received < n do - if !received % 100 = 0 then - printfn "main thread: received = %d" !received -#if NETCOREAPP - Task.Delay(1).Wait(); -#else - System.Threading.Thread.Sleep(1) -#endif - !received) + do! Task.Yield() + return !received}) n + +(* Disabled for timing issues. Some replacement TryScan tests were added to FSharp.Core.UnitTests. + for i in 1..10 do for sleep in [0;1;10] do for timeout in [10;1;0] do @@ -284,8 +277,11 @@ module MailboxProcessorBasicTests = !timedOut) (Some true) - check "cf72361: MailboxProcessor TryScan wo/timeout" - (let timedOut = ref None +*) + + checkAsync "cf72361: MailboxProcessor TryScan wo/timeout" + (task { + let timedOut = ref None let mb = new MailboxProcessor(fun inbox -> async { let! result = inbox.TryScan((fun i -> if i then async { return () } |> Some else None)) @@ -298,65 +294,58 @@ module MailboxProcessorBasicTests = w.Start() while w.ElapsedMilliseconds < 100L do mb.Post(false) -#if NETCOREAPP - Task.Delay(0).Wait(); -#else - System.Threading.Thread.Sleep(0) -#endif + do! Task.Yield() let r = !timedOut mb.Post(true) - r) + return r}) None module MailboxProcessorErrorEventTests = exception Err of int let test() = // Make sure the event doesn't get raised if no error - check + checkAsync "c32398u9330: MailboxProcessor Error (0)" - (let mb1 = new MailboxProcessor(fun inbox -> async { return () }) - let res = ref 100 - mb1.Error.Add(fun _ -> res := 0) + (task { + let mb1 = new MailboxProcessor(fun inbox -> async { return () }) + mb1.Error.Add(fun _ -> failwith "unexpected error event") mb1.Start(); -#if NETCOREAPP - Task.Delay(200).Wait(); -#else - System.Threading.Thread.Sleep(200) -#endif - !res) + do! Task.Delay(200) + return 100}) 100 // Make sure the event does get raised if error - check + check "c32398u9331: MailboxProcessor Error (1)" (let mb1 = new MailboxProcessor(fun inbox -> async { failwith "fail" }) - let res = ref 0 - mb1.Error.Add(fun _ -> res := 100) + use res = new System.Threading.ManualResetEventSlim(false) + mb1.Error.Add(fun _ -> res.Set()) mb1.Start(); -#if NETCOREAPP - Task.Delay(200).Wait(); -#else - System.Threading.Thread.Sleep(200) -#endif - !res) - 100 + res.Wait() + true) + true // Make sure the event does get raised after message receive - check + checkAsync "c32398u9332: MailboxProcessor Error (2)" - (let mb1 = new MailboxProcessor(fun inbox -> - async { let! msg = inbox.Receive() - raise (Err msg) }) - let res = ref 0 - mb1.Error.Add(function Err n -> res := n | _ -> check "rwe90r - unexpected error" 0 1) - mb1.Start(); - mb1.Post 100 -#if NETCOREAPP - Task.Delay(200).Wait(); -#else - System.Threading.Thread.Sleep(200) -#endif - !res) + ( + let errorNumber = TaskCompletionSource<_>() + + let mb1 = new MailboxProcessor( fun inbox -> async { + let! msg = inbox.Receive() + raise (Err msg) + }) + + mb1.Error.Add(function + | Err n -> errorNumber.SetResult n + | _ -> + check "rwe90r - unexpected error" 0 1 ) + + mb1.Start(); + mb1.Post 100 + + errorNumber.Task + ) 100 type msg = Increment of int | Fetch of AsyncReplyChannel | Reset @@ -472,13 +461,10 @@ let test7() = let timeoutboxes str = new MailboxProcessor<'b>(fun inbox -> - async { for i in 1 .. 10 do -#if NETCOREAPP - Task.Delay(200).Wait() -#else - do System.Threading.Thread.Sleep 200 -#endif - }) + async { + for i in 1 .. 10 do + do! Async.Sleep 200 + }) // Timeout let timeout_tpar() = @@ -553,17 +539,9 @@ let timeout_para_def() = test "default timeout & PostAndAsyncReply" false with _ -> test "default timeout & PostAndAsyncReply" true -// Useful class: put "checkpoints" in the code. -// Check they are called in the right order. -type Path(str) = - let mutable current = 0 - member p.Check n = check (str + " #" + string (current+1)) n (current+1) - current <- n - - - module LotsOfMessages = let test () = + task { let N = 200000 let count = ref N @@ -586,12 +564,9 @@ module LotsOfMessages = check "celrv09ervkn" (queueLength >= logger.CurrentQueueLength) true queueLength <- logger.CurrentQueueLength -#if NETCOREAPP - Task.Delay(10).Wait() -#else - System.Threading.Thread.Sleep(10) -#endif + do! Task.Delay(10) check "celrv09ervknf3ew" logger.CurrentQueueLength 0 + } let RunAll() = MailboxProcessorBasicTests.test() @@ -608,11 +583,11 @@ let RunAll() = timeout_tpar_def() // ToDo: 7/31/2008: Disabled because of probable timing issue. QA needs to re-enable post-CTP. // Tracked by bug FSharp 1.0:2891 - //test15() + // test15() // ToDo: 7/31/2008: Disabled because of probable timing issue. QA needs to re-enable post-CTP. // Tracked by bug FSharp 1.0:2891 - //test15b() - LotsOfMessages.test() + // test15b() + LotsOfMessages.test().Wait() #if TESTS_AS_APP let RUN() = RunAll(); failures @@ -621,6 +596,9 @@ RunAll() let aa = if not failures.IsEmpty then stdout.WriteLine "Test Failed" + stdout.WriteLine() + stdout.WriteLine "failures:" + failures |> List.iter stdout.WriteLine exit 1 else stdout.WriteLine "Test Passed" diff --git a/tests/fsharp/tests.fs b/tests/fsharp/tests.fs index ed99adda96a..deaf36b2dc8 100644 --- a/tests/fsharp/tests.fs +++ b/tests/fsharp/tests.fs @@ -75,6 +75,7 @@ module CoreTests = exec cfg cfg.DotNetExe ($"msbuild {projectFile} /p:Configuration={cfg.BUILD_CONFIG} -property:FSharpRepositoryPath={FSharpRepositoryPath}") #if !NETCOREAPP +module CoreTests1 = [] let ``attributes-FSC_OPTIMIZED`` () = singleTestBuildAndRun "core/attributes" FSC_OPTIMIZED @@ -255,6 +256,7 @@ module CoreTests = #if !NETCOREAPP +module CoreTests2 = // Requires winforms will not run on coreclr [] @@ -763,7 +765,8 @@ module CoreTests = #endif -#if !NETCOREAPP +#if !NETCOREAPP +module CoreTests3 = [] let quotes () = let cfg = testConfig "core/quotes" @@ -1172,6 +1175,7 @@ module CoreTests = #if !NETCOREAPP +module CoreTests4 = [] let ``measures-FSC_NETFX_TEST_ROUNDTRIP_AS_DLL`` () = singleTestBuildAndRun "core/measures" FSC_NETFX_TEST_ROUNDTRIP_AS_DLL @@ -1338,6 +1342,7 @@ module CoreTests = #if !NETCOREAPP +module CoreTests5 = [] let refnormalization () = let cfg = testConfig "core/refnormalization" diff --git a/tests/fsharp/xunit.runner.json b/tests/fsharp/xunit.runner.json index 4e5a48343ec..f47fec5d745 100644 --- a/tests/fsharp/xunit.runner.json +++ b/tests/fsharp/xunit.runner.json @@ -1,5 +1,5 @@ { "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", - "appDomain": "ifAvailable", - "shadowCopy": false + "appDomain": "denied", + "parallelizeAssembly": true } \ No newline at end of file diff --git a/tests/scripts/scriptlib.fsx b/tests/scripts/scriptlib.fsx index 853aecb496e..ae6fd001cfa 100644 --- a/tests/scripts/scriptlib.fsx +++ b/tests/scripts/scriptlib.fsx @@ -10,6 +10,14 @@ open System.IO open System.Text open System.Diagnostics +module MessageSink = + let sinkWriter = +#if DEBUG + Console.Out +#else + TextWriter.Null +#endif + [] module Scripting = @@ -77,7 +85,7 @@ module Scripting = if Directory.Exists output then Directory.Delete(output, true) - let log format = printfn format + let log format = fprintfn MessageSink.sinkWriter format type FilePath = string @@ -169,8 +177,6 @@ module Scripting = let redirectTo (writer: TextWriter) = new OutPipe (writer) - let redirectToLog () = redirectTo System.Console.Out - #if !NETCOREAPP let defaultPlatform = match Environment.OSVersion.Platform, Environment.Is64BitOperatingSystem with diff --git a/vsintegration/tests/FSharp.Editor.Tests/BraceMatchingServiceTests.fs b/vsintegration/tests/FSharp.Editor.Tests/BraceMatchingServiceTests.fs index 8027a06e85f..638b40d92f5 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/BraceMatchingServiceTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/BraceMatchingServiceTests.fs @@ -11,8 +11,7 @@ open FSharp.Editor.Tests.Helpers open FSharp.Test type BraceMatchingServiceTests() = - let checker = - FSharpChecker.Create(useTransparentCompiler = CompilerAssertHelpers.UseTransparentCompiler) + let checker = TestContext.Checker let fileName = "C:\\test.fs" diff --git a/vsintegration/tests/FSharp.Editor.Tests/EditorFormattingServiceTests.fs b/vsintegration/tests/FSharp.Editor.Tests/EditorFormattingServiceTests.fs index 985abc67e31..da719e12b18 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/EditorFormattingServiceTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/EditorFormattingServiceTests.fs @@ -57,8 +57,7 @@ marker4""" [] [] member this.TestIndentation(marker: string, expectedLine: string) = - let checker = - FSharpChecker.Create(useTransparentCompiler = CompilerAssertHelpers.UseTransparentCompiler) + let checker = TestContext.Checker let position = indentTemplate.IndexOf(marker) Assert.True(position >= 0, "Precondition failed: unable to find marker in template") @@ -96,8 +95,7 @@ marker4""" [] [] member this.TestPasteChanges_PastingOntoIndentedLine(enabled: bool, prefix: string) = - let checker = - FSharpChecker.Create(useTransparentCompiler = CompilerAssertHelpers.UseTransparentCompiler) + let checker = TestContext.Checker let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions RoslynTestHelpers.DefaultProjectOptions @@ -163,8 +161,7 @@ somethingElseHere [] [] member this.TestPasteChanges_PastingOntoEmptyLine(prefix: string) = - let checker = - FSharpChecker.Create(useTransparentCompiler = CompilerAssertHelpers.UseTransparentCompiler) + let checker = TestContext.Checker let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions RoslynTestHelpers.DefaultProjectOptions @@ -224,8 +221,7 @@ somethingElseHere [] member this.TestPasteChanges_PastingWithAutoIndentationInPasteSpan() = - let checker = - FSharpChecker.Create(useTransparentCompiler = CompilerAssertHelpers.UseTransparentCompiler) + let checker = TestContext.Checker let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions RoslynTestHelpers.DefaultProjectOptions diff --git a/vsintegration/tests/FSharp.Editor.Tests/IndentationServiceTests.fs b/vsintegration/tests/FSharp.Editor.Tests/IndentationServiceTests.fs index 70a7c17dfcf..b9426b3cd4c 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/IndentationServiceTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/IndentationServiceTests.fs @@ -12,8 +12,7 @@ open FSharp.Editor.Tests.Helpers open FSharp.Test type IndentationServiceTests() = - let checker = - FSharpChecker.Create(useTransparentCompiler = CompilerAssertHelpers.UseTransparentCompiler) + let checker = TestContext.Checker let filePath = "C:\\test.fs" diff --git a/vsintegration/tests/FSharp.Editor.Tests/SignatureHelpProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/SignatureHelpProviderTests.fs index f85ee51a6c8..7af641bb442 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/SignatureHelpProviderTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/SignatureHelpProviderTests.fs @@ -21,8 +21,7 @@ module SignatureHelpProvider = override doc.AppendDocumentation(_, _, _, _, _, _, _, _) = () } - let checker = - FSharpChecker.Create(useTransparentCompiler = CompilerAssertHelpers.UseTransparentCompiler) + let checker = TestContext.Checker let filePath = "C:\\test.fs"