From 91d598e4770da4b0426f0d87c2a5f224193182f8 Mon Sep 17 00:00:00 2001 From: Abel Braaksma Date: Tue, 21 Jul 2020 17:17:24 +0200 Subject: [PATCH] Fix issue with tests, see #9617 (#9622) * Ignore NCrunch temp files and local user files * Showing only failing line in ExprTests, cleanup temp files * Show sensible message when UnresolvedPathReferenceNoRange is raised by tests * Attempt to re-enable two base tests, may need to be re-disabled for Mono * Cleanup tests that use global state, ensure deletion of temp files * Fix difference Mono, fix spacing difference CI vs VS IDE * Normalize null vs None * Fix next issue difference between CI and local * Make sure both smoke tests use the same filterhack function * Next fix, overlooked * Add procid and threadid to temp file * Move #r System.Numerics to mkStandardProjectReferences * Cleanup * Fix tests that actually count references and are dependent on Common.fs --- .gitignore | 4 + src/fsharp/ErrorLogger.fs | 7 +- tests/service/Common.fs | 1 + tests/service/ExprTests.fs | 256 ++++++++++++++------- tests/service/FsUnit.fs | 20 +- tests/service/MultiProjectAnalysisTests.fs | 4 +- 6 files changed, 204 insertions(+), 88 deletions(-) diff --git a/.gitignore b/.gitignore index 21686006d7d..264c1fb71bc 100644 --- a/.gitignore +++ b/.gitignore @@ -134,3 +134,7 @@ msbuild.binlog /tests/fsharp/regression/5531/compilation.output.test.txt /tests/fsharp/core/fsfromfsviacs/compilation.langversion.old.output.txt /tests/fsharp/core/fsfromfsviacs/compilation.errors.output.txt +*ncrunch*.user +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* diff --git a/src/fsharp/ErrorLogger.fs b/src/fsharp/ErrorLogger.fs index feebf981866..83ff5dba6d0 100755 --- a/src/fsharp/ErrorLogger.fs +++ b/src/fsharp/ErrorLogger.fs @@ -91,7 +91,12 @@ exception PossibleUnverifiableCode of range exception UnresolvedReferenceNoRange of (*assemblyName*) string exception UnresolvedReferenceError of (*assemblyName*) string * range -exception UnresolvedPathReferenceNoRange of (*assemblyName*) string * (*path*) string +exception UnresolvedPathReferenceNoRange of (*assemblyName*) string * (*path*) string with + override this.Message = + match this :> exn with + | UnresolvedPathReferenceNoRange(assemblyName, path) -> sprintf "Assembly: %s, full path: %s" assemblyName path + | _ -> "impossible" + exception UnresolvedPathReference of (*assemblyName*) string * (*path*) string * range diff --git a/tests/service/Common.fs b/tests/service/Common.fs index 8eff8813050..5b48e637d96 100644 --- a/tests/service/Common.fs +++ b/tests/service/Common.fs @@ -96,6 +96,7 @@ let mkStandardProjectReferences () = [ yield sysLib "mscorlib" yield sysLib "System" yield sysLib "System.Core" + yield sysLib "System.Numerics" yield fsCoreDefaultReference() ] #endif diff --git a/tests/service/ExprTests.fs b/tests/service/ExprTests.fs index 4b38a14031e..b2198e71748 100644 --- a/tests/service/ExprTests.fs +++ b/tests/service/ExprTests.fs @@ -13,7 +13,10 @@ open NUnit.Framework open FsUnit open System open System.IO +open System.Text open System.Collections.Generic +open System.Diagnostics +open System.Threading open FSharp.Compiler.SourceCodeServices open FSharp.Compiler.Service.Tests.Common @@ -22,6 +25,63 @@ let internal exprChecker = FSharpChecker.Create(keepAssemblyContents=true) [] module internal Utils = + let getTempPath() = + Path.Combine(Path.GetTempPath(), "ExprTests") + + /// If it doesn't exists, create a folder 'ExprTests' in local user's %TEMP% folder + let createTempDir() = + let tempPath = getTempPath() + do + if Directory.Exists tempPath then () + else Directory.CreateDirectory tempPath |> ignore + + /// Returns the filename part of a temp file name created with Path.GetTempFileName() + /// and an added process id and thread id to ensure uniqueness between threads. + let getTempFileName() = + let tempFileName = Path.GetTempFileName() + try + let tempFile, tempExt = Path.GetFileNameWithoutExtension tempFileName, Path.GetExtension tempFileName + let procId, threadId = Process.GetCurrentProcess().Id, Thread.CurrentThread.ManagedThreadId + String.concat "" [tempFile; "_"; string procId; "_"; string threadId; tempExt] // ext includes dot + finally + try + // Since Path.GetTempFileName() creates a *.tmp file in the %TEMP% folder, we want to clean it up. + // This also prevents a system to run out of available randomized temp files (the pool is only 64k large). + File.Delete tempFileName + with _ -> () + + /// Clean up after a test is run. If you need to inspect the create *.fs files, change this function to do nothing, or just break here. + let cleanupTempFiles files = + for fileName in files do + try + // cleanup: only the source file is written to the temp dir. + File.Delete fileName + with _ -> () + + try + // remove the dir when empty + let tempPath = getTempPath() + if Directory.GetFiles tempPath |> Array.isEmpty then + Directory.Delete tempPath + with _ -> () + + + /// Given just a filename, returns it with changed extension located in %TEMP%\ExprTests + let getTempFilePathChangeExt tmp ext = + Path.Combine(getTempPath(), Path.ChangeExtension(tmp, ext)) + + // This behaves slightly differently on Mono versions, 'null' is printed somethimes, 'None' other times + // Presumably this is very small differences in Mono reflection causing F# printing to change behaviour + // For now just disabling this test. See https://github.com/fsharp/FSharp.Compiler.Service/pull/766 + let filterHack l = + l |> List.map (fun (s:string) -> + // potential difference on Mono + s.Replace("ILArrayShape [(Some 0, None)]", "ILArrayShape [(Some 0, null)]") + // spacing difference when run locally in VS + .Replace("I_ldelema (NormalAddress,false,ILArrayShape [(Some 0, null)],!0)]", "I_ldelema (NormalAddress, false, ILArrayShape [(Some 0, null)], !0)]") + // local VS IDE vs CI env difference + .Replace("Operators.Hash (x)", "x.GetHashCode()")) + let rec printExpr low (e:FSharpExpr) = match e with | BasicPatterns.AddressOf(e1) -> "&"+printExpr 0 e1 @@ -280,11 +340,6 @@ module internal Utils = module internal Project1 = - let fileName1 = Path.ChangeExtension(Path.GetTempFileName(), ".fs") - let base2 = Path.GetTempFileName() - let fileName2 = Path.ChangeExtension(base2, ".fs") - let dllName = Path.ChangeExtension(base2, ".dll") - let projFileName = Path.ChangeExtension(base2, ".fsproj") let fileSource1 = """ module M @@ -539,7 +594,7 @@ let anonRecd = {| X = 1; Y = 2 |} let anonRecdGet = (anonRecd.X, anonRecd.Y) """ - File.WriteAllText(fileName1, fileSource1) + let fileSource2 = """ module N @@ -572,11 +627,27 @@ let testMutableVar = mutableVar 1 let testMutableConst = mutableConst () """ - File.WriteAllText(fileName2, fileSource2) + let createOptions() = + let temp1 = Utils.getTempFileName() + let temp2 = Utils.getTempFileName() + let fileName1 = Utils.getTempFilePathChangeExt temp1 ".fs" // Path.ChangeExtension(Path.GetTempFileName(), ".fs") + let fileName2 = Utils.getTempFilePathChangeExt temp2 ".fs" //Path.ChangeExtension(base2, ".fs") + let dllName = Utils.getTempFilePathChangeExt temp2 ".dll" //Path.ChangeExtension(base2, ".dll") + let projFileName = Utils.getTempFilePathChangeExt temp2 ".fsproj" //Path.ChangeExtension(base2, ".fsproj") + + Utils.createTempDir() + File.WriteAllText(fileName1, fileSource1) + File.WriteAllText(fileName2, fileSource2) + let fileNames = [fileName1; fileName2] + let args = mkProjectCommandLineArgs (dllName, fileNames) + let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + + [fileName1; fileName2; dllName; projFileName], options + + let options = lazy createOptions() + + - let fileNames = [fileName1; fileName2] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) let operatorTests = """ module OperatorTests{0} @@ -637,13 +708,9 @@ let test{0}ToStringOperator (e1:{1}) = string e1 """ -[] -// FCS Has a problem with these tests because of FSharp Core versions. -#if !COMPILER_SERVICE_AS_DLL -[] -#endif +/// This test is run in unison with its optimized counterpart below let ``Test Unoptimized Declarations Project1`` () = - let wholeProjectResults = exprChecker.ParseAndCheckProject(Project1.options) |> Async.RunSynchronously + let wholeProjectResults = exprChecker.ParseAndCheckProject(snd Project1.options.Value) |> Async.RunSynchronously for e in wholeProjectResults.Errors do printfn "Project1 error: <<<%s>>>" e.Message @@ -657,14 +724,6 @@ let ``Test Unoptimized Declarations Project1`` () = let file1 = wholeProjectResults.AssemblyContents.ImplementationFiles.[0] let file2 = wholeProjectResults.AssemblyContents.ImplementationFiles.[1] - // This behaves slightly differently on Mono versions, 'null' is printed somethimes, 'None' other times - // Presumably this is very small differences in Mono reflection causing F# printing to change behavious - // For now just disabling this test. See https://github.com/fsharp/FSharp.Compiler.Service/pull/766 - let filterHack l = - l |> List.map (fun (s:string) -> - s.Replace("ILArrayShape [(Some 0, None)]", "ILArrayShapeFIX") - .Replace("ILArrayShape [(Some 0, null)]", "ILArrayShapeFIX")) - let expected = [ "type M"; "type IntAbbrev"; "let boolEx1 = True @ (6,14--6,18)"; "let intEx1 = 1 @ (7,13--7,14)"; "let int64Ex1 = 1 @ (8,15--8,17)"; @@ -714,7 +773,7 @@ let ``Test Unoptimized Declarations Project1`` () = "member CurriedMethod(x) (a1,b1) (a2,b2) = 1 @ (107,63--107,64)"; "let testFunctionThatCallsMultiArgMethods(unitVar0) = let m: M.MultiArgMethods = new MultiArgMethods(3,4) in Operators.op_Addition (m.Method(7,8),fun tupledArg -> let arg00: Microsoft.FSharp.Core.int = tupledArg.Item0 in let arg01: Microsoft.FSharp.Core.int = tupledArg.Item1 in fun tupledArg -> let arg10: Microsoft.FSharp.Core.int = tupledArg.Item0 in let arg11: Microsoft.FSharp.Core.int = tupledArg.Item1 in m.CurriedMethod(arg00,arg01,arg10,arg11) (9,10) (11,12)) @ (110,8--110,9)"; "let testFunctionThatUsesUnitsOfMeasure(x) (y) = Operators.op_Addition,Microsoft.FSharp.Core.float<'u>,Microsoft.FSharp.Core.float<'u>> (x,y) @ (122,70--122,75)"; - "let testFunctionThatUsesAddressesAndByrefs(x) = let mutable w: Microsoft.FSharp.Core.int = 4 in let y1: Microsoft.FSharp.Core.byref = x in let y2: Microsoft.FSharp.Core.byref = &w in let arr: Microsoft.FSharp.Core.int Microsoft.FSharp.Core.[] = [|3; 4|] in let r: Microsoft.FSharp.Core.int Microsoft.FSharp.Core.ref = Operators.Ref (3) in let y3: Microsoft.FSharp.Core.byref = [I_ldelema (NormalAddress, false, ILArrayShapeFIX, !0)](arr,0) in let y4: Microsoft.FSharp.Core.byref = &r.contents in let z: Microsoft.FSharp.Core.int = Operators.op_Addition (Operators.op_Addition (Operators.op_Addition (x,y1),y2),y3) in (w <- 3; (x <- 4; (y2 <- 4; (y3 <- 5; Operators.op_Addition (Operators.op_Addition (Operators.op_Addition (Operators.op_Addition (Operators.op_Addition (Operators.op_Addition (Operators.op_Addition (z,x),y1),y2),y3),y4),IntrinsicFunctions.GetArray (arr,0)),r.contents))))) @ (125,16--125,17)"; + "let testFunctionThatUsesAddressesAndByrefs(x) = let mutable w: Microsoft.FSharp.Core.int = 4 in let y1: Microsoft.FSharp.Core.byref = x in let y2: Microsoft.FSharp.Core.byref = &w in let arr: Microsoft.FSharp.Core.int Microsoft.FSharp.Core.[] = [|3; 4|] in let r: Microsoft.FSharp.Core.int Microsoft.FSharp.Core.ref = Operators.Ref (3) in let y3: Microsoft.FSharp.Core.byref = [I_ldelema (NormalAddress, false, ILArrayShape [(Some 0, null)], !0)](arr,0) in let y4: Microsoft.FSharp.Core.byref = &r.contents in let z: Microsoft.FSharp.Core.int = Operators.op_Addition (Operators.op_Addition (Operators.op_Addition (x,y1),y2),y3) in (w <- 3; (x <- 4; (y2 <- 4; (y3 <- 5; Operators.op_Addition (Operators.op_Addition (Operators.op_Addition (Operators.op_Addition (Operators.op_Addition (Operators.op_Addition (Operators.op_Addition (z,x),y1),y2),y3),y4),IntrinsicFunctions.GetArray (arr,0)),r.contents))))) @ (125,16--125,17)"; "let testFunctionThatUsesStructs1(dt) = dt.AddDays(3) @ (139,57--139,72)"; "let testFunctionThatUsesStructs2(unitVar0) = let dt1: System.DateTime = DateTime.get_Now () in let mutable dt2: System.DateTime = DateTime.get_Now () in let dt3: System.TimeSpan = Operators.op_Subtraction (dt1,dt2) in let dt4: System.DateTime = dt1.AddDays(3) in let dt5: Microsoft.FSharp.Core.int = dt1.get_Millisecond() in let dt6: Microsoft.FSharp.Core.byref = &dt2 in let dt7: System.TimeSpan = Operators.op_Subtraction (dt6,dt4) in dt7 @ (142,7--142,10)"; "let testFunctionThatUsesWhileLoop(unitVar0) = let mutable x: Microsoft.FSharp.Core.int = 1 in (while Operators.op_LessThan (x,100) do x <- Operators.op_Addition (x,1) done; x) @ (152,15--152,16)"; @@ -771,24 +830,19 @@ let ``Test Unoptimized Declarations Project1`` () = printDeclarations None (List.ofSeq file1.Declarations) |> Seq.toList - |> filterHack - |> shouldEqual (filterHack expected) + |> Utils.filterHack + |> shouldPairwiseEqual (Utils.filterHack expected) printDeclarations None (List.ofSeq file2.Declarations) |> Seq.toList - |> filterHack - |> shouldEqual (filterHack expected2) + |> Utils.filterHack + |> shouldPairwiseEqual (Utils.filterHack expected2) () - -[] -// FCS Has a problem with these tests because of FSharp Core versions -#if !COMPILER_SERVICE_AS_DLL -[] -#endif +/// This test is run in unison with its unoptimized counterpart below let ``Test Optimized Declarations Project1`` () = - let wholeProjectResults = exprChecker.ParseAndCheckProject(Project1.options) |> Async.RunSynchronously + let wholeProjectResults = exprChecker.ParseAndCheckProject(snd Project1.options.Value) |> Async.RunSynchronously for e in wholeProjectResults.Errors do printfn "Project1 error: <<<%s>>>" e.Message @@ -802,14 +856,6 @@ let ``Test Optimized Declarations Project1`` () = let file1 = wholeProjectResults.GetOptimizedAssemblyContents().ImplementationFiles.[0] let file2 = wholeProjectResults.GetOptimizedAssemblyContents().ImplementationFiles.[1] - // This behaves slightly differently on Mono versions, 'null' is printed somethimes, 'None' other times - // Presumably this is very small differences in Mono reflection causing F# printing to change behavious - // For now just disabling this test. See https://github.com/fsharp/FSharp.Compiler.Service/pull/766 - let filterHack l = - l |> List.map (fun (s:string) -> - s.Replace("ILArrayShape [(Some 0, None)]", "ILArrayShapeFIX") - .Replace("ILArrayShape [(Some 0, null)]", "ILArrayShapeFIX")) - let expected = [ "type M"; "type IntAbbrev"; "let boolEx1 = True @ (6,14--6,18)"; "let intEx1 = 1 @ (7,13--7,14)"; "let int64Ex1 = 1 @ (8,15--8,17)"; @@ -859,7 +905,7 @@ let ``Test Optimized Declarations Project1`` () = "member CurriedMethod(x) (a1,b1) (a2,b2) = 1 @ (107,63--107,64)"; "let testFunctionThatCallsMultiArgMethods(unitVar0) = let m: M.MultiArgMethods = new MultiArgMethods(3,4) in Operators.op_Addition (m.Method(7,8),let arg00: Microsoft.FSharp.Core.int = 9 in let arg01: Microsoft.FSharp.Core.int = 10 in let arg10: Microsoft.FSharp.Core.int = 11 in let arg11: Microsoft.FSharp.Core.int = 12 in m.CurriedMethod(arg00,arg01,arg10,arg11)) @ (110,8--110,9)"; "let testFunctionThatUsesUnitsOfMeasure(x) (y) = Operators.op_Addition,Microsoft.FSharp.Core.float<'u>,Microsoft.FSharp.Core.float<'u>> (x,y) @ (122,70--122,75)"; - "let testFunctionThatUsesAddressesAndByrefs(x) = let mutable w: Microsoft.FSharp.Core.int = 4 in let y1: Microsoft.FSharp.Core.byref = x in let y2: Microsoft.FSharp.Core.byref = &w in let arr: Microsoft.FSharp.Core.int Microsoft.FSharp.Core.[] = [|3; 4|] in let r: Microsoft.FSharp.Core.int Microsoft.FSharp.Core.ref = Operators.Ref (3) in let y3: Microsoft.FSharp.Core.byref = [I_ldelema (NormalAddress,false,ILArrayShapeFIX,!0)](arr,0) in let y4: Microsoft.FSharp.Core.byref = &r.contents in let z: Microsoft.FSharp.Core.int = Operators.op_Addition (Operators.op_Addition (Operators.op_Addition (x,y1),y2),y3) in (w <- 3; (x <- 4; (y2 <- 4; (y3 <- 5; Operators.op_Addition (Operators.op_Addition (Operators.op_Addition (Operators.op_Addition (Operators.op_Addition (Operators.op_Addition (Operators.op_Addition (z,x),y1),y2),y3),y4),IntrinsicFunctions.GetArray (arr,0)),r.contents))))) @ (125,16--125,17)"; + "let testFunctionThatUsesAddressesAndByrefs(x) = let mutable w: Microsoft.FSharp.Core.int = 4 in let y1: Microsoft.FSharp.Core.byref = x in let y2: Microsoft.FSharp.Core.byref = &w in let arr: Microsoft.FSharp.Core.int Microsoft.FSharp.Core.[] = [|3; 4|] in let r: Microsoft.FSharp.Core.int Microsoft.FSharp.Core.ref = Operators.Ref (3) in let y3: Microsoft.FSharp.Core.byref = [I_ldelema (NormalAddress, false, ILArrayShape [(Some 0, null)], !0)](arr,0) in let y4: Microsoft.FSharp.Core.byref = &r.contents in let z: Microsoft.FSharp.Core.int = Operators.op_Addition (Operators.op_Addition (Operators.op_Addition (x,y1),y2),y3) in (w <- 3; (x <- 4; (y2 <- 4; (y3 <- 5; Operators.op_Addition (Operators.op_Addition (Operators.op_Addition (Operators.op_Addition (Operators.op_Addition (Operators.op_Addition (Operators.op_Addition (z,x),y1),y2),y3),y4),IntrinsicFunctions.GetArray (arr,0)),r.contents))))) @ (125,16--125,17)"; "let testFunctionThatUsesStructs1(dt) = dt.AddDays(3) @ (139,57--139,72)"; "let testFunctionThatUsesStructs2(unitVar0) = let dt1: System.DateTime = DateTime.get_Now () in let mutable dt2: System.DateTime = DateTime.get_Now () in let dt3: System.TimeSpan = DateTime.op_Subtraction (dt1,dt2) in let dt4: System.DateTime = dt1.AddDays(3) in let dt5: Microsoft.FSharp.Core.int = dt1.get_Millisecond() in let dt6: Microsoft.FSharp.Core.byref = &dt2 in let dt7: System.TimeSpan = DateTime.op_Subtraction (dt6,dt4) in dt7 @ (142,7--142,10)"; "let testFunctionThatUsesWhileLoop(unitVar0) = let mutable x: Microsoft.FSharp.Core.int = 1 in (while Operators.op_LessThan (x,100) do x <- Operators.op_Addition (x,1) done; x) @ (152,15--152,16)"; @@ -918,43 +964,69 @@ let ``Test Optimized Declarations Project1`` () = printDeclarations None (List.ofSeq file1.Declarations) |> Seq.toList - |> filterHack - |> shouldEqual (filterHack expected) + |> Utils.filterHack + |> shouldPairwiseEqual (Utils.filterHack expected) printDeclarations None (List.ofSeq file2.Declarations) |> Seq.toList - |> filterHack - |> shouldEqual (filterHack expected2) + |> Utils.filterHack + |> shouldPairwiseEqual (Utils.filterHack expected2) () +[] +let ``Test Optimized and Unoptimized Declarations for Project1`` () = + let filenames = fst Project1.options.Value + try + ``Test Optimized Declarations Project1`` () + ``Test Unoptimized Declarations Project1`` () + finally + Utils.cleanupTempFiles filenames + let testOperators dnName fsName excludedTests expectedUnoptimized expectedOptimized = - let basePath = Path.GetTempFileName() - let fileName = Path.ChangeExtension(basePath, ".fs") - let dllName = Path.ChangeExtension(basePath, ".dll") - let projFileName = Path.ChangeExtension(basePath, ".fsproj") - let source = System.String.Format(Project1.operatorTests, dnName, fsName) - let replace (s:string) r = s.Replace("let " + r, "// let " + r) - let fileSource = excludedTests |> List.fold replace source - File.WriteAllText(fileName, fileSource) - - let args = mkProjectCommandLineArgsSilent (dllName, [fileName]) - let options = checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) - let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunSynchronously - for e in wholeProjectResults.Errors do - printfn "%s Operator Tests error: <<<%s>>>" dnName e.Message + let tempFileName = Utils.getTempFileName() + let filePath = Utils.getTempFilePathChangeExt tempFileName ".fs" + let dllPath =Utils.getTempFilePathChangeExt tempFileName ".dll" + let projFilePath = Utils.getTempFilePathChangeExt tempFileName ".fsproj" - wholeProjectResults.Errors.Length |> shouldEqual 0 + try + createTempDir() + let source = System.String.Format(Project1.operatorTests, dnName, fsName) + let replace (s:string) r = s.Replace("let " + r, "// let " + r) + let fileSource = excludedTests |> List.fold replace source + File.WriteAllText(filePath, fileSource) + + let args = mkProjectCommandLineArgsSilent (dllPath, [filePath]) + + let options = checker.GetProjectOptionsFromCommandLineArgs (projFilePath, args) + + let wholeProjectResults = exprChecker.ParseAndCheckProject(options) |> Async.RunSynchronously + + for r in wholeProjectResults.ProjectContext.GetReferencedAssemblies() do + printfn "Referenced assembly %s: %O" r.QualifiedName r.FileName + + let errors = StringBuilder() + for e in wholeProjectResults.Errors do + printfn "%s Operator Tests error: <<<%s>>>" dnName e.Message + errors.AppendLine e.Message |> ignore - let fileUnoptimized = wholeProjectResults.AssemblyContents.ImplementationFiles.[0] - let fileOptimized = wholeProjectResults.GetOptimizedAssemblyContents().ImplementationFiles.[0] + errors.ToString() |> shouldEqual "" + wholeProjectResults.Errors.Length |> shouldEqual 0 - printDeclarations None (List.ofSeq fileUnoptimized.Declarations) - |> Seq.toList |> shouldEqual expectedUnoptimized + let fileUnoptimized = wholeProjectResults.AssemblyContents.ImplementationFiles.[0] + let fileOptimized = wholeProjectResults.GetOptimizedAssemblyContents().ImplementationFiles.[0] - printDeclarations None (List.ofSeq fileOptimized.Declarations) - |> Seq.toList |> shouldEqual expectedOptimized + // fail test on first line that fails, show difference in output window + printDeclarations None (List.ofSeq fileUnoptimized.Declarations) + |> shouldPairwiseEqual expectedUnoptimized + + // fail test on first line that fails, show difference in output window + printDeclarations None (List.ofSeq fileOptimized.Declarations) + |> shouldPairwiseEqual expectedOptimized + + finally + Utils.cleanupTempFiles [filePath; dllPath; projFilePath] () @@ -2735,10 +2807,6 @@ let ``Test Operator Declarations for String`` () = module internal ProjectStressBigExpressions = - let fileName1 = Path.ChangeExtension(Path.GetTempFileName(), ".fs") - let base2 = Path.GetTempFileName() - let dllName = Path.ChangeExtension(base2, ".dll") - let projFileName = Path.ChangeExtension(base2, ".fsproj") let fileSource1 = """ module StressBigExpressions @@ -2926,16 +2994,30 @@ let BigSequenceExpression(outFileOpt,docFileOpt,baseAddressOpt) = """ - File.WriteAllText(fileName1, fileSource1) - let fileNames = [fileName1] - let args = mkProjectCommandLineArgs (dllName, fileNames) - let options = exprChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + let createOptions() = + let temp1 = Utils.getTempFileName() + let temp2 = Utils.getTempFileName() + let fileName1 = Utils.getTempFilePathChangeExt temp1 ".fs" //Path.ChangeExtension(Path.GetTempFileName(), ".fs") + let dllName = Utils.getTempFilePathChangeExt temp2 ".dll" //Path.ChangeExtension(base2, ".dll") + let projFileName = Utils.getTempFilePathChangeExt temp2 ".fsproj" //Path.ChangeExtension(base2, ".fsproj") -[] + Utils.createTempDir() + File.WriteAllText(fileName1, fileSource1) + + let fileNames = [fileName1] + let args = mkProjectCommandLineArgs (dllName, fileNames) + let options = exprChecker.GetProjectOptionsFromCommandLineArgs (projFileName, args) + + [fileName1; dllName; projFileName], options + + let options = lazy createOptions() + + +/// This test is run in unison with its optimized counterpart below let ``Test expressions of declarations stress big expressions`` () = - let wholeProjectResults = exprChecker.ParseAndCheckProject(ProjectStressBigExpressions.options) |> Async.RunSynchronously + let wholeProjectResults = exprChecker.ParseAndCheckProject(snd ProjectStressBigExpressions.options.Value) |> Async.RunSynchronously wholeProjectResults.Errors.Length |> shouldEqual 0 @@ -2946,9 +3028,9 @@ let ``Test expressions of declarations stress big expressions`` () = printDeclarations None (List.ofSeq file1.Declarations) |> Seq.toList |> ignore -[] +/// This test is run in unison with its unoptimized counterpart below let ``Test expressions of optimized declarations stress big expressions`` () = - let wholeProjectResults = exprChecker.ParseAndCheckProject(ProjectStressBigExpressions.options) |> Async.RunSynchronously + let wholeProjectResults = exprChecker.ParseAndCheckProject(snd ProjectStressBigExpressions.options.Value) |> Async.RunSynchronously wholeProjectResults.Errors.Length |> shouldEqual 0 @@ -2958,5 +3040,11 @@ let ``Test expressions of optimized declarations stress big expressions`` () = // This should not stack overflow printDeclarations None (List.ofSeq file1.Declarations) |> Seq.toList |> ignore - - +[] +let ``Test expressions of both optimized and unoptimized declarations for StressTest Big Expressions`` () = + let filenames = fst ProjectStressBigExpressions.options.Value + try + ``Test expressions of optimized declarations stress big expressions`` () + ``Test expressions of declarations stress big expressions`` () + finally + Utils.cleanupTempFiles filenames diff --git a/tests/service/FsUnit.fs b/tests/service/FsUnit.fs index 497b492cc5d..34a9d7ebdcd 100644 --- a/tests/service/FsUnit.fs +++ b/tests/service/FsUnit.fs @@ -15,9 +15,27 @@ let should (f : 'a -> #Constraint) x (y : obj) = let equal x = new EqualConstraint(x) -// like "should equal", but validates same-type +/// like "should equal", but validates same-type let shouldEqual (x: 'a) (y: 'a) = Assert.AreEqual(x, y, sprintf "Expected: %A\nActual: %A" x y) +/// Same as 'shouldEqual' but goes pairwise over the collections. Lengths must be equal. +let shouldPairwiseEqual (x: seq<_>) (y: seq<_>) = + // using enumerators, because Seq.iter2 allows different lengths silently + let ex = x.GetEnumerator() + let ey = y.GetEnumerator() + let mutable countx = 0 + let mutable county = 0 + while ex.MoveNext() do + countx <- countx + 1 + if ey.MoveNext() then + county <- county + 1 + ey.Current |> shouldEqual ex.Current + + while ex.MoveNext() do countx <- countx + 1 + while ey.MoveNext() do county <- county + 1 + if countx <> county then + Assert.Fail("Collections are of unequal lengths, expected length {0}, actual length is {1}.", countx, county) + let notEqual x = new NotConstraint(new EqualConstraint(x)) let contain x = new ContainsConstraint(x) diff --git a/tests/service/MultiProjectAnalysisTests.fs b/tests/service/MultiProjectAnalysisTests.fs index 6d915d486a8..8a7cc4b6836 100644 --- a/tests/service/MultiProjectAnalysisTests.fs +++ b/tests/service/MultiProjectAnalysisTests.fs @@ -142,7 +142,7 @@ let ``Test multi project 1 whole project errors`` () = printfn "multi project 1 error: <<<%s>>>" e.Message wholeProjectResults .Errors.Length |> shouldEqual 0 - wholeProjectResults.ProjectContext.GetReferencedAssemblies().Length |> shouldEqual 6 + wholeProjectResults.ProjectContext.GetReferencedAssemblies().Length |> shouldEqual 7 [] let ``Test multi project 1 basic`` () = @@ -335,7 +335,7 @@ let ``Test ManyProjectsStressTest whole project errors`` () = printfn "ManyProjectsStressTest error: <<<%s>>>" e.Message wholeProjectResults .Errors.Length |> shouldEqual 0 - wholeProjectResults.ProjectContext.GetReferencedAssemblies().Length |> shouldEqual (ManyProjectsStressTest.numProjectsForStressTest + 4) + wholeProjectResults.ProjectContext.GetReferencedAssemblies().Length |> shouldEqual (ManyProjectsStressTest.numProjectsForStressTest + 5) [] let ``Test ManyProjectsStressTest basic`` () =