@@ -11,6 +11,19 @@ open FSharp.Compiler.SourceCodeServices
1111open FSharp.Compiler .Interactive .Shell
1212
1313open NUnit.Framework
14+ open System.Reflection .Emit
15+
16+ [<Sealed>]
17+ type ILVerifier ( dllFilePath : string ) =
18+
19+ member this.VerifyIL ( qualifiedItemName : string , expectedIL : string ) =
20+ ILChecker.checkILItem qualifiedItemName dllFilePath [ expectedIL ]
21+
22+ member this.VerifyIL ( expectedIL : string list ) =
23+ ILChecker.checkIL dllFilePath expectedIL
24+
25+ member this.VerifyILWithLineNumbers ( qualifiedItemName : string , expectedIL : string ) =
26+ ILChecker.checkILItemWithLineNumbers qualifiedItemName dllFilePath [ expectedIL ]
1427
1528[<RequireQualifiedAccess>]
1629module CompilerAssert =
@@ -48,10 +61,45 @@ module CompilerAssert =
4861 Stamp = None
4962 }
5063
51- let lockObj = obj ()
64+ let private gate = obj ()
65+
66+ let private compile isExe source f =
67+ lock gate <| fun () ->
68+ let inputFilePath = Path.ChangeExtension( Path.GetTempFileName(), " .fs" )
69+ let outputFilePath = Path.ChangeExtension ( Path.GetTempFileName(), if isExe then " .exe" else " .dll" )
70+ let runtimeConfigFilePath = Path.ChangeExtension ( outputFilePath, " .runtimeconfig.json" )
71+ let fsCoreDllPath = config.FSCOREDLLPATH
72+ let tmpFsCoreFilePath = Path.Combine ( Path.GetDirectoryName( outputFilePath), Path.GetFileName( fsCoreDllPath))
73+ try
74+ File.Copy ( fsCoreDllPath , tmpFsCoreFilePath, true )
75+ File.WriteAllText ( inputFilePath, source)
76+ File.WriteAllText ( runtimeConfigFilePath, """
77+ {
78+ "runtimeOptions": {
79+ "tfm": "netcoreapp2.1",
80+ "framework": {
81+ "name": "Microsoft.NETCore.App",
82+ "version": "2.1.0"
83+ }
84+ }
85+ }
86+ """ )
87+
88+ let args =
89+ defaultProjectOptions.OtherOptions
90+ |> Array.append [| " fsc.exe" ; inputFilePath; " -o:" + outputFilePath; ( if isExe then " --target:exe" else " --target:library" ); " --nowin32manifest" |]
91+ let errors , _ = checker.Compile args |> Async.RunSynchronously
92+
93+ f ( errors, outputFilePath)
94+
95+ finally
96+ try File.Delete inputFilePath with | _ -> ()
97+ try File.Delete outputFilePath with | _ -> ()
98+ try File.Delete runtimeConfigFilePath with | _ -> ()
99+ try File.Delete tmpFsCoreFilePath with | _ -> ()
52100
53101 let Pass ( source : string ) =
54- lock lockObj <| fun () ->
102+ lock gate <| fun () ->
55103 let parseResults , fileAnswer = checker.ParseAndCheckFileInProject( " test.fs" , 0 , SourceText.ofString source, defaultProjectOptions) |> Async.RunSynchronously
56104
57105 Assert.IsEmpty( parseResults.Errors, sprintf " Parse errors: %A " parseResults.Errors)
@@ -64,7 +112,7 @@ module CompilerAssert =
64112
65113
66114 let TypeCheckSingleError ( source : string ) ( expectedErrorNumber : int ) ( expectedErrorRange : int * int * int * int ) ( expectedErrorMsg : string ) =
67- lock lockObj <| fun () ->
115+ lock gate <| fun () ->
68116 let parseResults , fileAnswer = checker.ParseAndCheckFileInProject( " test.fs" , 0 , SourceText.ofString source, defaultProjectOptions) |> Async.RunSynchronously
69117
70118 Assert.IsEmpty( parseResults.Errors, sprintf " Parse errors: %A " parseResults.Errors)
@@ -82,8 +130,46 @@ module CompilerAssert =
82130 Assert.AreEqual( expectedErrorMsg, info.Message, " expectedErrorMsg" )
83131 )
84132
133+ let CompileExe ( source : string ) =
134+ compile true source ( fun ( errors , _ ) ->
135+ if errors.Length > 0 then
136+ Assert.Fail ( sprintf " Compile had warnings and/or errors: %A " errors))
137+
138+ let CompileExeAndRun ( source : string ) =
139+ compile true source ( fun ( errors , outputExe ) ->
140+
141+ if errors.Length > 0 then
142+ Assert.Fail ( sprintf " Compile had warnings and/or errors: %A " errors)
143+
144+ let pInfo = ProcessStartInfo ()
145+ #if NETCOREAPP
146+ pInfo.FileName <- config.DotNetExe
147+ pInfo.Arguments <- outputExe
148+ #else
149+ pInfo.FileName <- outputExe
150+ #endif
151+
152+ pInfo.RedirectStandardError <- true
153+ pInfo.UseShellExecute <- false
154+
155+ let p = Process.Start( pInfo)
156+
157+ p.WaitForExit()
158+ let errors = p.StandardError.ReadToEnd ()
159+ if not ( String.IsNullOrWhiteSpace errors) then
160+ Assert.Fail errors
161+ )
162+
163+ let CompileLibraryAndVerifyIL ( source : string ) ( f : ILVerifier -> unit ) =
164+ compile false source ( fun ( errors , outputFilePath ) ->
165+ if errors.Length > 0 then
166+ Assert.Fail ( sprintf " Compile had warnings and/or errors: %A " errors)
167+
168+ f ( ILVerifier outputFilePath)
169+ )
170+
85171 let RunScript ( source : string ) ( expectedErrorMessages : string list ) =
86- lock lockObj <| fun () ->
172+ lock gate <| fun () ->
87173 // Intialize output and input streams
88174 use inStream = new StringReader( " " )
89175 use outStream = new StringWriter()
0 commit comments