diff --git a/tests/FSharp.Test.Utilities/CompilerAssert.fs b/tests/FSharp.Test.Utilities/CompilerAssert.fs
index f18022a0159..3e6f721379b 100644
--- a/tests/FSharp.Test.Utilities/CompilerAssert.fs
+++ b/tests/FSharp.Test.Utilities/CompilerAssert.fs
@@ -83,7 +83,7 @@ type CompilerAssert private () =
static let _ = config |> ignore
- // Do a one time dotnet sdk build to compute the proper set of reference assemblies to pass to the compiler
+// Do a one time dotnet sdk build to compute the proper set of reference assemblies to pass to the compiler
static let projectFile = """
@@ -98,6 +98,7 @@ type CompilerAssert private () =
+
@@ -116,6 +117,16 @@ type CompilerAssert private () =
"""
+ static let directoryBuildProps = """
+
+
+ true
+
+
+
+
+"""
+
static let programFs = """
open System
@@ -126,13 +137,15 @@ let main argv = 0"""
let mutable output = ""
let mutable errors = ""
let mutable cleanUp = true
- let projectDirectory = Path.Combine(Path.GetTempPath(), "CompilerAssert", Path.GetRandomFileName())
+ let pathToTemp = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "../../../../Temp"))
+ let projectDirectory = Path.Combine(pathToTemp, "CompilerAssert", Path.GetRandomFileName())
let pathToFSharpCore = typeof.Assembly.Location
try
try
Directory.CreateDirectory(projectDirectory) |> ignore
let projectFileName = Path.Combine(projectDirectory, "ProjectFile.fsproj")
let programFsFileName = Path.Combine(projectDirectory, "Program.fs")
+ let directoryBuildPropsFileName = Path.Combine(projectDirectory, "Directory.Build.props")
let frameworkReferencesFileName = Path.Combine(projectDirectory, "FrameworkReferences.txt")
#if NETCOREAPP
File.WriteAllText(projectFileName, projectFile.Replace("$TARGETFRAMEWORK", "netcoreapp3.1").Replace("$FSHARPCORELOCATION", pathToFSharpCore))
@@ -140,6 +153,7 @@ let main argv = 0"""
File.WriteAllText(projectFileName, projectFile.Replace("$TARGETFRAMEWORK", "net472").Replace("$FSHARPCORELOCATION", pathToFSharpCore))
#endif
File.WriteAllText(programFsFileName, programFs)
+ File.WriteAllText(directoryBuildPropsFileName, directoryBuildProps)
let pInfo = ProcessStartInfo ()
pInfo.FileName <- config.DotNetExe
@@ -150,19 +164,24 @@ let main argv = 0"""
pInfo.UseShellExecute <- false
let p = Process.Start(pInfo)
- let succeeded = p.WaitForExit(10000)
+ let timeout = 30000
+ let succeeded = p.WaitForExit(timeout)
output <- p.StandardOutput.ReadToEnd ()
errors <- p.StandardError.ReadToEnd ()
- if not (String.IsNullOrWhiteSpace errors) then Assert.Fail errors
- if p.ExitCode <> 0 || not succeeded then Assert.Fail(sprintf "Program exited with exit code %d" p.ExitCode)
+ if not (String.IsNullOrWhiteSpace errors) then Assert.Fail errors
+ if p.ExitCode <> 0 then Assert.Fail(sprintf "Program exited with exit code %d" p.ExitCode)
+ if not succeeded then Assert.Fail(sprintf "Program timed out after %d ms" timeout)
File.ReadLines(frameworkReferencesFileName) |> Seq.toArray
with | e ->
cleanUp <- false
- printfn "%s" output
- printfn "%s" errors
+ printfn "Project directory: %s" projectDirectory
+ printfn "STDOUT: %s" output
+ File.WriteAllText(Path.Combine(projectDirectory, "project.stdout"), output)
+ printfn "STDERR: %s" errors
+ File.WriteAllText(Path.Combine(projectDirectory, "project.stderror"), errors)
raise (new Exception (sprintf "An error occurred getting netcoreapp references: %A" e))
finally
if cleanUp then
@@ -275,8 +294,10 @@ let main argv = 0"""
Array.zip errors expectedErrors
|> Array.iter (fun (actualError, expectedError) ->
- let (expectedSeverity, expectedErrorNumber, expectedErrorRange, expectedErrorMsg) = expectedError
- let (actualSeverity, actualErrorNumber, actualErrorRange, actualErrorMsg) = actualError
+ let (expectedSeverity, expectedErrorNumber, expectedErrorRange, expectedErrorMsg: string) = expectedError
+ let (actualSeverity, actualErrorNumber, actualErrorRange, actualErrorMsg: string) = actualError
+ let expectedErrorMsg = expectedErrorMsg.Replace("\r\n", "\n")
+ let actualErrorMsg = actualErrorMsg.Replace("\r\n", "\n")
checkEqual "Severity" expectedSeverity actualSeverity
checkEqual "ErrorNumber" expectedErrorNumber actualErrorNumber
checkEqual "ErrorRange" expectedErrorRange actualErrorRange
@@ -300,7 +321,7 @@ let main argv = 0"""
| TestCompilationReference (cmpl) ->
let filename =
match cmpl with
- | TestCompilation.CSharp c -> c.AssemblyName
+ | TestCompilation.CSharp c when not (String.IsNullOrWhiteSpace c.AssemblyName) -> c.AssemblyName
| _ -> Path.GetRandomFileName()
let tmp = Path.Combine(outputPath, Path.ChangeExtension(filename, ".dll"))
disposals.Add({ new IDisposable with
@@ -380,10 +401,10 @@ let main argv = 0"""
// 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.
// TODO: We must not rely on the filesystem when compiling
- static let rec returnCompilation (cmpl: Compilation) =
+ static let rec returnCompilation (cmpl: Compilation) ignoreWarnings =
let compileDirectory = Path.Combine(Path.GetTempPath(), "CompilerAssert", Path.GetRandomFileName())
Directory.CreateDirectory(compileDirectory) |> ignore
- compileCompilationAux compileDirectory (ResizeArray()) false cmpl
+ compileCompilationAux compileDirectory (ResizeArray()) ignoreWarnings cmpl
static let executeBuiltAppAndReturnResult (outputFilePath: string) (deps: string list) : (int * string * string) =
let out = Console.Out
@@ -462,8 +483,8 @@ let main argv = 0"""
static member Compile(cmpl: Compilation, ?ignoreWarnings) =
CompilerAssert.CompileWithErrors(cmpl, [||], defaultArg ignoreWarnings false)
- static member CompileRaw(cmpl: Compilation) =
- lock gate (fun () -> returnCompilation cmpl)
+ static member CompileRaw(cmpl: Compilation, ?ignoreWarnings) =
+ lock gate (fun () -> returnCompilation cmpl (defaultArg ignoreWarnings false))
static member ExecuteAndReturnResult (outputFilePath: string, 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.
@@ -493,6 +514,58 @@ let main argv = 0"""
CompilerAssert.Execute(cmpl, newProcess = true, onOutput = (fun output -> Assert.AreEqual(expectedOutput, output)))
/// Assert that the given source code compiles with the `defaultProjectOptions`, with no errors or warnings
+ static member CompileOfAst isExe source =
+ let outputFilePath = Path.ChangeExtension (Path.GetTempFileName(), if isExe then "exe" else ".dll")
+ let parseOptions = { FSharpParsingOptions.Default with SourceFiles = [|"test.fs"|] }
+
+ let parseResults =
+ checker.ParseFile("test.fs", SourceText.ofString source, parseOptions)
+ |> Async.RunSynchronously
+
+ Assert.IsEmpty(parseResults.Errors, sprintf "Parse errors: %A" parseResults.Errors)
+ Assert.IsTrue(parseResults.ParseTree.IsSome, "no parse tree returned")
+
+ let dependencies =
+ #if NETCOREAPP
+ Array.toList getNetCoreAppReferences
+ #else
+ []
+ #endif
+
+ let compileErrors, statusCode =
+ checker.Compile([parseResults.ParseTree.Value], "test", outputFilePath, dependencies, executable = isExe, noframework = true)
+ |> Async.RunSynchronously
+
+ Assert.IsEmpty(compileErrors, sprintf "Compile errors: %A" compileErrors)
+ Assert.AreEqual(0, statusCode, sprintf "Nonzero status code: %d" statusCode)
+ outputFilePath
+
+ static member CompileOfAstToDynamicAssembly source =
+ let assemblyName = sprintf "test-%O" (Guid.NewGuid())
+ let parseOptions = { FSharpParsingOptions.Default with SourceFiles = [|"test.fs"|] }
+ let parseResults =
+ checker.ParseFile("test.fs", SourceText.ofString source, parseOptions)
+ |> Async.RunSynchronously
+
+ Assert.IsEmpty(parseResults.Errors, sprintf "Parse errors: %A" parseResults.Errors)
+ Assert.IsTrue(parseResults.ParseTree.IsSome, "no parse tree returned")
+
+ let dependencies =
+ #if NETCOREAPP
+ Array.toList getNetCoreAppReferences
+ #else
+ []
+ #endif
+
+ let compileErrors, statusCode, assembly =
+ checker.CompileToDynamicAssembly([parseResults.ParseTree.Value], assemblyName, dependencies, None, noframework = true)
+ |> Async.RunSynchronously
+
+ Assert.IsEmpty(compileErrors, sprintf "Compile errors: %A" compileErrors)
+ Assert.AreEqual(0, statusCode, sprintf "Nonzero status code: %d" statusCode)
+ Assert.IsTrue(assembly.IsSome, "no assembly returned")
+ Option.get assembly
+
static member Pass (source: string) =
lock gate <| fun () ->
let parseResults, fileAnswer = checker.ParseAndCheckFileInProject("test.fs", 0, SourceText.ofString source, defaultProjectOptions) |> Async.RunSynchronously
@@ -550,6 +623,27 @@ let main argv = 0"""
Assert.AreEqual(errorsExpectedBaseLine.Replace("\r\n","\n"), errorsActual.Replace("\r\n","\n"))
+ static member TypeCheckWithOptionsAndName options name (source: string) =
+ lock gate <| fun () ->
+ let errors =
+ let parseResults, fileAnswer =
+ checker.ParseAndCheckFileInProject(
+ name,
+ 0,
+ SourceText.ofString source,
+ { defaultProjectOptions with OtherOptions = Array.append options defaultProjectOptions.OtherOptions; SourceFiles = [|name|] })
+ |> Async.RunSynchronously
+
+ if parseResults.Errors.Length > 0 then
+ parseResults.Errors
+ else
+
+ match fileAnswer with
+ | FSharpCheckFileAnswer.Aborted _ -> Assert.Fail("Type Checker Aborted"); [| |]
+ | FSharpCheckFileAnswer.Succeeded(typeCheckResults) -> typeCheckResults.Errors
+
+ errors
+
static member TypeCheckWithOptions options (source: string) =
lock gate <| fun () ->
let errors =
@@ -638,35 +732,39 @@ let main argv = 0"""
static member CompileLibraryAndVerifyIL (source: string) (f: ILVerifier -> unit) =
CompilerAssert.CompileLibraryAndVerifyILWithOptions [||] source f
- static member RunScriptWithOptions options (source: string) (expectedErrorMessages: string list) =
- lock gate <| fun () ->
- // Intialize output and input streams
- use inStream = new StringReader("")
- use outStream = new StringWriter()
- use errStream = new StringWriter()
-
- // Build command line arguments & start FSI session
- let argv = [| "C:\\fsi.exe" |]
- #if NETCOREAPP
- let args = Array.append argv [|"--noninteractive"; "--targetprofile:netcore"|]
- #else
- let args = Array.append argv [|"--noninteractive"; "--targetprofile:mscorlib"|]
- #endif
- let allArgs = Array.append args options
-
- let fsiConfig = FsiEvaluationSession.GetDefaultConfiguration()
- use fsiSession = FsiEvaluationSession.Create(fsiConfig, allArgs, inStream, outStream, errStream, collectible = true)
-
- let ch, errors = fsiSession.EvalInteractionNonThrowing source
-
- let errorMessages = ResizeArray()
- errors
- |> Seq.iter (fun error -> errorMessages.Add(error.Message))
+ static member RunScriptWithOptionsAndReturnResult options (source: string) =
+ // Intialize output and input streams
+ use inStream = new StringReader("")
+ use outStream = new StringWriter()
+ use errStream = new StringWriter()
- match ch with
- | Choice2Of2 ex -> errorMessages.Add(ex.Message)
- | _ -> ()
+ // Build command line arguments & start FSI session
+ let argv = [| "C:\\fsi.exe" |]
+#if NETCOREAPP
+ let args = Array.append argv [|"--noninteractive"; "--targetprofile:netcore"|]
+#else
+ let args = Array.append argv [|"--noninteractive"; "--targetprofile:mscorlib"|]
+#endif
+ let allArgs = Array.append args options
+
+ let fsiConfig = FsiEvaluationSession.GetDefaultConfiguration()
+ use fsiSession = FsiEvaluationSession.Create(fsiConfig, allArgs, inStream, outStream, errStream, collectible = true)
+
+ let ch, errors = fsiSession.EvalInteractionNonThrowing source
+
+ let errorMessages = ResizeArray()
+ errors
+ |> Seq.iter (fun error -> errorMessages.Add(error.Message))
+ match ch with
+ | Choice2Of2 ex -> errorMessages.Add(ex.Message)
+ | _ -> ()
+
+ errorMessages
+
+ static member RunScriptWithOptions options (source: string) (expectedErrorMessages: string list) =
+ lock gate <| fun () ->
+ let errorMessages = CompilerAssert.RunScriptWithOptionsAndReturnResult options source
if expectedErrorMessages.Length <> errorMessages.Count then
Assert.Fail(sprintf "Expected error messages: %A \n\n Actual error messages: %A" expectedErrorMessages errorMessages)
else