-
Notifications
You must be signed in to change notification settings - Fork 831
Baseline parser tests #14721
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Baseline parser tests #14721
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
2975b4e
Add parser baseline tests.
nojaf 4f0c791
Remove ported tests.
nojaf 6e3e324
Update FSharp.Compiler.UnitTests.fsproj
nojaf 67432f9
Add a newline before and after the file content.
nojaf ce341ab
Move original tests into designated folder.
nojaf 6a5b1a4
Add ending newline to baseline files.
nojaf 030bf0e
Take __SOURCE_DIRECTORY__ result into account.
nojaf a2c2335
Sanitize AST for current
nojaf File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,170 @@ | ||
| module Tests.Service.SyntaxTree | ||
|
|
||
| open System.IO | ||
| open FSharp.Compiler.CodeAnalysis | ||
| open FSharp.Compiler.Service.Tests.Common | ||
| open FSharp.Compiler.Syntax | ||
| open FSharp.Compiler.Text | ||
| open NUnit.Framework | ||
|
|
||
| let testCasesDir = Path.Combine(__SOURCE_DIRECTORY__, "data", "SyntaxTree") | ||
|
|
||
| let allTestCases = | ||
| Directory.EnumerateFiles(testCasesDir, "*.fs?", SearchOption.AllDirectories) | ||
| |> Seq.map (fun f -> | ||
| let fileInfo = FileInfo(f) | ||
| let fileName = Path.Combine(fileInfo.Directory.Name, fileInfo.Name) | ||
| [| fileName :> obj |]) | ||
| |> Seq.toArray | ||
|
|
||
| [<Literal>] | ||
| let RootDirectory = @"/root" | ||
|
|
||
| /// <summary> | ||
| /// Everytime `__SOURCE_DIRECTORY__` was used in the code, the ast will contain an invalid value and range for it. | ||
| /// This should be cleaned up when the test runs during CI/CD. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// This function is incomplete and does not clean up the entire ParsedInput. | ||
| /// A shortcut was made to only support the existing use-cases. | ||
| /// </remarks> | ||
| let private sanitizeAST (sourceDirectoryValue: string) (ast: ParsedInput) : ParsedInput = | ||
| let isZero (m: range) = | ||
| m.StartLine = 0 && m.StartColumn = 0 && m.EndLine = 0 && m.EndColumn = 0 | ||
|
|
||
| // __SOURCE_DIRECTORY__ will contain the evaluated value, so we want to replace it with a stable value instead. | ||
| let mapParsedHashDirective (ParsedHashDirective(ident, args, _) as phd) = | ||
| match args with | ||
| | [ ParsedHashDirectiveArgument.SourceIdentifier("__SOURCE_DIRECTORY__", _, mSourceDirectory) ] -> | ||
| let mZero = | ||
| Range.mkRange mSourceDirectory.FileName (Position.mkPos 0 0) (Position.mkPos 0 0) | ||
|
|
||
| ParsedHashDirective( | ||
| ident, | ||
| [ ParsedHashDirectiveArgument.SourceIdentifier("__SOURCE_DIRECTORY__", sourceDirectoryValue, mZero) ], | ||
| mZero | ||
| ) | ||
| | _ -> phd | ||
|
|
||
| let (|SourceDirectoryConstant|_|) (constant: SynConst) = | ||
| match constant with | ||
| | SynConst.SourceIdentifier("__SOURCE_DIRECTORY__", _, mSourceDirectory) -> | ||
| let mZero = | ||
| Range.mkRange mSourceDirectory.FileName (Position.mkPos 0 0) (Position.mkPos 0 0) | ||
|
|
||
| Some(SynConst.SourceIdentifier("__SOURCE_DIRECTORY__", sourceDirectoryValue, mZero), mZero) | ||
| | _ -> None | ||
|
|
||
| let (|SourceDirectoryConstantExpr|_|) (expr: SynExpr) = | ||
| match expr with | ||
| | SynExpr.Const(SourceDirectoryConstant(constant, mZero), _) -> Some(SynExpr.Const(constant, mZero)) | ||
| | _ -> None | ||
|
|
||
| let rec mapSynModuleDecl (mdl: SynModuleDecl) = | ||
| match mdl with | ||
| | SynModuleDecl.HashDirective(ParsedHashDirective(range = mZero) as hd, m) -> | ||
| let hd = mapParsedHashDirective hd | ||
| // Only update the range of SynModuleSigDecl.HashDirective if the value was updated. | ||
| let m = if isZero mZero then mZero else m | ||
| SynModuleDecl.HashDirective(hd, m) | ||
| | SynModuleDecl.NestedModule(moduleInfo, isRecursive, decls, isContinuing, range, trivia) -> | ||
| SynModuleDecl.NestedModule(moduleInfo, isRecursive, List.map mapSynModuleDecl decls, isContinuing, range, trivia) | ||
| | SynModuleDecl.Expr(SourceDirectoryConstantExpr(expr), _) -> SynModuleDecl.Expr(expr, expr.Range) | ||
| | _ -> mdl | ||
|
|
||
| let mapSynModuleOrNamespace (SynModuleOrNamespace(longId, isRecursive, kind, decls, xmlDoc, attribs, ao, range, trivia)) = | ||
| SynModuleOrNamespace(longId, isRecursive, kind, List.map mapSynModuleDecl decls, xmlDoc, attribs, ao, range, trivia) | ||
|
|
||
| let rec mapSynModuleDeclSig (msdl: SynModuleSigDecl) = | ||
| match msdl with | ||
| | SynModuleSigDecl.HashDirective(ParsedHashDirective(range = mZero) as hd, m) -> | ||
| let hd = mapParsedHashDirective hd | ||
| // Only update the range of SynModuleSigDecl.HashDirective if the value was updated. | ||
| let m = if isZero mZero then mZero else m | ||
| SynModuleSigDecl.HashDirective(hd, m) | ||
| | SynModuleSigDecl.NestedModule(moduleInfo, isRecursive, decls, range, trivia) -> | ||
| SynModuleSigDecl.NestedModule(moduleInfo, isRecursive, List.map mapSynModuleDeclSig decls, range, trivia) | ||
| | _ -> msdl | ||
|
|
||
| let mapSynModuleOrNamespaceSig (SynModuleOrNamespaceSig(longId, isRecursive, kind, decls, xmlDoc, attribs, ao, range, trivia)) = | ||
| SynModuleOrNamespaceSig(longId, isRecursive, kind, List.map mapSynModuleDeclSig decls, xmlDoc, attribs, ao, range, trivia) | ||
|
|
||
| match ast with | ||
| | ParsedInput.ImplFile(ParsedImplFileInput(fileName, | ||
| isScript, | ||
| qualifiedNameOfFile, | ||
| scopedPragmas, | ||
| hashDirectives, | ||
| contents, | ||
| flags, | ||
| trivia, | ||
| identifiers)) -> | ||
| ParsedImplFileInput( | ||
| fileName, | ||
| isScript, | ||
| qualifiedNameOfFile, | ||
| scopedPragmas, | ||
| List.map mapParsedHashDirective hashDirectives, | ||
| List.map mapSynModuleOrNamespace contents, | ||
| flags, | ||
| trivia, | ||
| identifiers | ||
| ) | ||
| |> ParsedInput.ImplFile | ||
| | ParsedInput.SigFile(ParsedSigFileInput(fileName, qualifiedNameOfFile, scopedPragmas, hashDirectives, contents, trivia, identifiers)) -> | ||
| ParsedSigFileInput( | ||
| fileName, | ||
| qualifiedNameOfFile, | ||
| scopedPragmas, | ||
| List.map mapParsedHashDirective hashDirectives, | ||
| List.map mapSynModuleOrNamespaceSig contents, | ||
| trivia, | ||
| identifiers | ||
| ) | ||
| |> ParsedInput.SigFile | ||
|
|
||
| let parseSourceCode (name: string, code: string) = | ||
| let location = Path.Combine(RootDirectory, name).Replace("\\", "/") | ||
|
|
||
| let parseResults = | ||
| checker.ParseFile( | ||
| location, | ||
| SourceText.ofString code, | ||
| { FSharpParsingOptions.Default with | ||
| SourceFiles = [| location |] } | ||
| ) | ||
| |> Async.RunImmediate | ||
|
|
||
| let tree = parseResults.ParseTree | ||
| let sourceDirectoryValue = $"{RootDirectory}/{FileInfo(location).Directory.Name}" | ||
| sanitizeAST sourceDirectoryValue tree | ||
|
|
||
| /// Asserts the parsed untyped tree matches the expected baseline. | ||
| /// | ||
| /// To update a baseline: | ||
| /// CMD: set TEST_UPDATE_BSL=1 & dotnet test --filter "ParseFile" | ||
| /// PowerShell: $env:TEST_UPDATE_BSL = "1" ; dotnet test --filter "ParseFile" | ||
| /// Linux/macOS: export TEST_UPDATE_BSL=1 & dotnet test --filter "ParseFile" | ||
| /// | ||
| /// Assuming your current directory is tests/FSharp.Compiler.Service.Tests | ||
| [<TestCaseSource(nameof allTestCases)>] | ||
| let ParseFile fileName = | ||
| let fullPath = Path.Combine(testCasesDir, fileName) | ||
| let contents = File.ReadAllText fullPath | ||
| let ast = parseSourceCode (fileName, contents) | ||
| let normalize (s: string) = s.Replace("\r", "") | ||
| let actual = sprintf "%A" ast |> normalize |> sprintf "%s\n" | ||
| let bslPath = $"{fullPath}.bsl" | ||
|
|
||
| let expected = | ||
| if File.Exists bslPath then | ||
| File.ReadAllText bslPath |> normalize | ||
| else | ||
| "No baseline was found" | ||
|
|
||
| let testUpdateBSLEnv = System.Environment.GetEnvironmentVariable("TEST_UPDATE_BSL") | ||
|
|
||
| if not (isNull testUpdateBSLEnv) && testUpdateBSLEnv.Trim() = "1" then | ||
| File.WriteAllText(bslPath, actual) | ||
|
|
||
| Assert.AreEqual(expected, actual) | ||
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there an easy way to run/debug a specific test? It's going to be crucial when debugging some cases in the parser (e.g. debugging helped me a lot when I worked on the parser in #13352).
It would also be very handy to be able to dump tokens for a specific tests easily: it may really help when changing LexFilter, as in #13089.
@nojaf @vzarytovskii Would it be easy to have separate named tests, similar to what we have here? https://github.com/JetBrains/resharper-fsharp/blob/net231/ReSharper.FSharp/test/src/FSharp.Tests/Parsing/FSharpParserTest.fs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ouch, I'm a bit late 😞
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can debug each individual file:
