Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
Expand Up @@ -42,96 +42,7 @@
<Compile Include="TestDoubles.fs" />
<Compile Include="ConsoleOnlyOptionsTests.fs" />
<Compile Include="VisualStudioVersusConsoleContextTests.fs" />
<Compile Include="..\service\SyntaxTreeTests\TypeTests.fs">
<Link>SyntaxTree\TypeTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\ExpressionTests.fs">
<Link>SyntaxTree\ExpressionTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\StringTests.fs">
<Link>SyntaxTree\StringTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\ModuleOrNamespaceTests.fs">
<Link>SyntaxTree\ModuleOrNamespaceTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\ModuleOrNamespaceSigTests.fs">
<Link>SyntaxTree\ModuleOrNamespaceSigTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\SignatureTypeTests.fs">
<Link>SyntaxTree\SignatureTypeTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\MeasureTests.fs">
<Link>SyntaxTree\MeasureTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\MatchClauseTests.fs">
<Link>SyntaxTree\MatchClauseTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\SourceIdentifierTests.fs">
<Link>SyntaxTree\SourceIdentifierTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\NestedModuleTests.fs">
<Link>SyntaxTree\NestedModuleTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\BindingTests.fs">
<Link>SyntaxTree\BindingTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\ParsedHashDirectiveTests.fs">
<Link>SyntaxTree\ParsedHashDirectiveTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\LambdaTests.fs">
<Link>SyntaxTree\LambdaTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\IfThenElseTests.fs">
<Link>SyntaxTree\IfThenElseTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\UnionCaseTests.fs">
<Link>SyntaxTree\UnionCaseTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\EnumCaseTests.fs">
<Link>SyntaxTree\EnumCaseTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\PatternTests.fs">
<Link>SyntaxTree\PatternTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\ExceptionTests.fs">
<Link>SyntaxTree\ExceptionTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\MemberFlagTests.fs">
<Link>SyntaxTree\MemberFlagTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\MemberTests.fs">
<Link>SyntaxTree\MemberTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\ComputationExpressionTests.fs">
<Link>SyntaxTree\ComputationExpressionTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\ConditionalDirectiveTests.fs">
<Link>SyntaxTree\ConditionalDirectiveTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\CodeCommentTests.fs">
<Link>SyntaxTree\CodeCommentTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\OperatorNameTests.fs">
<Link>SyntaxTree\OperatorNameTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\SynIdentTests.fs">
<Link>SyntaxTree\SynIdentTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\SynTypeTests.fs">
<Link>SyntaxTree\SynTypeTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\AttributeTests.fs">
<Link>SyntaxTree\AttributeTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\ExternTests.fs">
<Link>SyntaxTree\ExternTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\LeadingKeywordTests.fs">
<Link>SyntaxTree\LeadingKeywordTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests\ValTests.fs">
<Link>SyntaxTree\ValTests.fs</Link>
</Compile>
<Compile Include="..\service\SyntaxTreeTests.fs" />
<Compile Include="..\service\FileSystemTests.fs">
<Link>FileSystemTests.fs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,75 +36,7 @@
<Compile Include="..\..\tests\service\Symbols.fs">
<Link>CompilerService\Symbols.fs</Link>
</Compile>
<Compile Include="..\..\tests\service\SyntaxTreeTests\TypeTests.fs">
<Link>SyntaxTree\TypeTests.fs</Link>
</Compile>
<Compile Include="..\..\tests\service\SyntaxTreeTests\ExpressionTests.fs">
<Link>SyntaxTree\ExpressionTests.fs</Link>
</Compile>
<Compile Include="..\..\tests\service\SyntaxTreeTests\StringTests.fs">
<Link>SyntaxTree\StringTests.fs</Link>
</Compile>
<Compile Include="..\..\tests\service\SyntaxTreeTests\ModuleOrNamespaceTests.fs">
<Link>SyntaxTree\ModuleOrNamespaceTests.fs</Link>
</Compile>
<Compile Include="..\..\tests\service\SyntaxTreeTests\ModuleOrNamespaceSigTests.fs">
<Link>SyntaxTree\ModuleOrNamespaceSigTests.fs</Link>
</Compile>
<Compile Include="..\..\tests\service\SyntaxTreeTests\SignatureTypeTests.fs">
<Link>SyntaxTree\SignatureTypeTests.fs</Link>
</Compile>
<Compile Include="..\..\tests\service\SyntaxTreeTests\MeasureTests.fs">
<Link>SyntaxTree\MeasureTests.fs</Link>
</Compile>
<Compile Include="..\..\tests\service\SyntaxTreeTests\MatchClauseTests.fs">
<Link>SyntaxTree\MatchClauseTests.fs</Link>
</Compile>
<Compile Include="..\..\tests\service\SyntaxTreeTests\SourceIdentifierTests.fs">
<Link>SyntaxTree\SourceIdentifierTests.fs</Link>
</Compile>
<Compile Include="..\..\tests\service\SyntaxTreeTests\NestedModuleTests.fs">
<Link>SyntaxTree\NestedModuleTests.fs</Link>
</Compile>
<Compile Include="..\..\tests\service\SyntaxTreeTests\BindingTests.fs">
<Link>SyntaxTree\BindingTests.fs</Link>
</Compile>
<Compile Include="..\..\tests\service\SyntaxTreeTests\ParsedHashDirectiveTests.fs">
<Link>SyntaxTree\ParsedHashDirectiveTests.fs</Link>
</Compile>
<Compile Include="..\..\tests\service\SyntaxTreeTests\LambdaTests.fs">
<Link>SyntaxTree\LambdaTests.fs</Link>
</Compile>
<Compile Include="..\..\tests\service\SyntaxTreeTests\IfThenElseTests.fs">
<Link>SyntaxTree\IfThenElseTests.fs</Link>
</Compile>
<Compile Include="..\..\tests\service\SyntaxTreeTests\UnionCaseTests.fs">
<Link>SyntaxTree\UnionCaseTests.fs</Link>
</Compile>
<Compile Include="..\..\tests\service\SyntaxTreeTests\EnumCaseTests.fs">
<Link>SyntaxTree\EnumCaseTests.fs</Link>
</Compile>
<Compile Include="..\..\tests\service\SyntaxTreeTests\PatternTests.fs">
<Link>SyntaxTree\PatternTests.fs</Link>
</Compile>
<Compile Include="..\..\tests\service\SyntaxTreeTests\ExceptionTests.fs">
<Link>SyntaxTree\ExceptionTests.fs</Link>
</Compile>
<Compile Include="..\..\tests\service\SyntaxTreeTests\MemberFlagTests.fs">
<Link>SyntaxTree\MemberFlagTests.fs</Link>
</Compile>
<Compile Include="..\..\tests\service\SyntaxTreeTests\ComputationExpressionTests.fs">
<Link>SyntaxTree\ComputationExpressionTests.fs</Link>
</Compile>
<Compile Include="..\..\tests\service\SyntaxTreeTests\ConditionalDirectiveTests.fs">
<Link>SyntaxTree\ConditionalDirectiveTests.fs</Link>
</Compile>
<Compile Include="..\..\tests\service\SyntaxTreeTests\CodeCommentTests.fs">
<Link>SyntaxTree\CodeCommentTests.fs</Link>
</Compile>
<Compile Include="..\..\tests\service\SyntaxTreeTests\OperatorNameTests.fs">
<Link>SyntaxTree\OperatorNameTests.fs</Link>
</Compile>
<Compile Include="..\..\tests\service\SyntaxTreeTests.fs" />
<Compile Include="..\..\tests\service\EditorTests.fs">
<Link>CompilerService\EditorTests.fs</Link>
</Compile>
Expand Down
170 changes: 170 additions & 0 deletions tests/service/SyntaxTreeTests.fs
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 =
Comment on lines +150 to +151
Copy link
Member

@auduchinok auduchinok Feb 10, 2023

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

Copy link
Member

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 😞

Copy link
Contributor Author

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:
image

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)
50 changes: 0 additions & 50 deletions tests/service/SyntaxTreeTests/AttributeTests.fs

This file was deleted.

Loading