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