Skip to content

Commit

Permalink
update testing
Browse files Browse the repository at this point in the history
  • Loading branch information
KevinRansom committed Jun 2, 2019
1 parent b2dd67b commit f08fb25
Show file tree
Hide file tree
Showing 16 changed files with 3,359 additions and 2,993 deletions.
6 changes: 6 additions & 0 deletions fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@
<Compile Include="$(FSharpSourcesRoot)/fsharp/sr.fs">
<Link>ErrorText/sr.fs</Link>
</Compile>
<Compile Include="$(FSharpSourcesRoot)/fsharp/LanguageFeatures.fsi">
<Link>Driver\LanguageFeatures.fsi</Link>
</Compile>
<Compile Include="$(FSharpSourcesRoot)/fsharp/LanguageFeatures.fs">
<Link>Driver\LanguageFeatures.fs</Link>
</Compile>
<Compile Include="$(FSharpSourcesRoot)/utils/prim-lexing.fsi">
<Link>LexYaccRuntime/prim-lexing.fsi</Link>
</Compile>
Expand Down
2 changes: 1 addition & 1 deletion src/fsharp/FSharp.Build/Fsc.fs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ type public Fsc () as this =
for item in embeddedFiles do
builder.AppendSwitchIfNotNull("--embed:", item.ItemSpec)
builder.AppendSwitchIfNotNull("--sourcelink:", sourceLink)
builder.AppendSwitchIfNotNull("--langVersion:", langVersion)
builder.AppendSwitchIfNotNull("--langversion:", langVersion)
// NoFramework
if noFramework then
builder.AppendSwitch("--noframework")
Expand Down
27 changes: 19 additions & 8 deletions src/fsharp/ast.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1967,14 +1967,14 @@ let PushCurriedPatternsToExpr synArgNameGenerator wholem isMember pats rhs =
expr
spatsl, expr

/// Helper for parsing the inline IL fragments.
let internal internalParseAssemblyCodeInstructions s isFeatureSupported m =
#if NO_INLINE_IL_PARSER
let ParseAssemblyCodeInstructions _s m =
ignore s
ignore isFeatureSupported

errorR(Error((193, "Inline IL not valid in a hosted environment"), m))
[| |]
#else
let ParseAssemblyCodeInstructions s m =
let isFeatureSupported (_featureId:LanguageFeature) = true //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
try
FSharp.Compiler.AbstractIL.Internal.AsciiParser.ilInstrs
FSharp.Compiler.AbstractIL.Internal.AsciiLexer.token
Expand All @@ -1983,15 +1983,20 @@ let ParseAssemblyCodeInstructions s m =
errorR(Error(FSComp.SR.astParseEmbeddedILError(), m)); [||]
#endif

let ParseAssemblyCodeInstructions s m =
// Public API can not answer the isFeatureSupported questions, so here we support everything
let isFeatureSupported (_featureId:LanguageFeature) = true
internalParseAssemblyCodeInstructions s isFeatureSupported m

let internal internalParseAssemblyCodeType s isFeatureSupported m =
ignore s
ignore isFeatureSupported

/// Helper for parsing the inline IL fragments.
#if NO_INLINE_IL_PARSER
let ParseAssemblyCodeType _s m =
errorR(Error((193, "Inline IL not valid in a hosted environment"), m))
IL.EcmaMscorlibILGlobals.typ_Object
#else
let ParseAssemblyCodeType s m =
let isFeatureSupported (_featureId:LanguageFeature) = true //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
let isFeatureSupported (_featureId:LanguageFeature) = true
try
FSharp.Compiler.AbstractIL.Internal.AsciiParser.ilType
FSharp.Compiler.AbstractIL.Internal.AsciiLexer.token
Expand All @@ -2001,6 +2006,12 @@ let ParseAssemblyCodeType s m =
IL.EcmaMscorlibILGlobals.typ_Object
#endif

/// Helper for parsing the inline IL fragments.
let ParseAssemblyCodeType s m =
// Public API can not answer the isFeatureSupported questions, so here we support everything
let isFeatureSupported (_featureId:LanguageFeature) = true
internalParseAssemblyCodeType s isFeatureSupported m

//------------------------------------------------------------------------
// AST constructors
//------------------------------------------------------------------------
Expand Down
5 changes: 2 additions & 3 deletions src/fsharp/lex.fsl
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,7 @@ let shouldStartFile args lexbuf (m:range) err tok =
if (m.StartColumn <> 0 || m.StartLine <> 1) then fail args lexbuf err tok
else tok

let evalIfDefExpression startPos args (lookup:string->bool) (lexed:string) =
let isFeatureSupported (_featureId:LanguageFeature) = true //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
let evalIfDefExpression startPos isFeatureSupported args (lookup:string->bool) (lexed:string) =
let lexbuf = LexBuffer<char>.FromChars (isFeatureSupported, lexed.ToCharArray ())
lexbuf.StartPos <- startPos
lexbuf.EndPos <- startPos
Expand Down Expand Up @@ -608,7 +607,7 @@ rule token args skip = parse
{ let m = lexbuf.LexemeRange
let lookup id = List.contains id args.defines
let lexed = lexeme lexbuf
let isTrue = evalIfDefExpression lexbuf.StartPos args lookup lexed
let isTrue = evalIfDefExpression lexbuf.StartPos lexbuf.SupportsFeature args lookup lexed
args.ifdefStack := (IfDefIf,m) :: !(args.ifdefStack)

// Get the token; make sure it starts at zero position & return
Expand Down
12 changes: 6 additions & 6 deletions src/fsharp/pars.fsy
Original file line number Diff line number Diff line change
Expand Up @@ -2109,7 +2109,7 @@ inlineAssemblyTyconRepr:
| HASH stringOrKeywordString HASH
{ libraryOnlyError (lhs parseState)
let lhsm = lhs parseState
SynTypeDefnSimpleRepr.LibraryOnlyILAssembly (ParseAssemblyCodeType $2 (rhs parseState 2),lhsm) }
SynTypeDefnSimpleRepr.LibraryOnlyILAssembly (internalParseAssemblyCodeType $2 parseState.LexBuffer.SupportsFeature (rhs parseState 2),lhsm) }

classOrInterfaceOrStruct:
| CLASS { TyconClass }
Expand Down Expand Up @@ -4005,11 +4005,11 @@ inlineAssemblyExpr:
| HASH stringOrKeywordString opt_inlineAssemblyTypeArg opt_curriedArgExprs opt_inlineAssemblyReturnTypes HASH
{ libraryOnlyWarning (lhs parseState)
let s,sm = $2,rhs parseState 2
(fun m -> SynExpr.LibraryOnlyILAssembly (ParseAssemblyCodeInstructions s sm,$3,List.rev $4,$5,m)) }
opt_curriedArgExprs:
| opt_curriedArgExprs argExpr %prec expr_args
{ $2 :: $1 }
(fun m -> SynExpr.LibraryOnlyILAssembly (internalParseAssemblyCodeInstructions s parseState.LexBuffer.SupportsFeature sm, $3, List.rev $4, $5, m)) }

opt_curriedArgExprs:
| opt_curriedArgExprs argExpr %prec expr_args
{ $2 :: $1 }

|
{ [] }
Expand Down
7 changes: 5 additions & 2 deletions src/fsharp/service/ServiceLexing.fs
Original file line number Diff line number Diff line change
Expand Up @@ -768,18 +768,21 @@ type FSharpLineTokenizer(lexbuf: UnicodeLexing.Lexbuf,

[<Sealed>]
type FSharpSourceTokenizer(defineConstants: string list, filename: string option) =

// Public callers are unable to answer LanguageVersion feature support questions.
// External Tools including the VS IDE will enable the default LanguageVersion
let isFeatureSupported (_featureId:LanguageFeature) = true

let lexResourceManager = new Lexhelp.LexResourceManager()

let lexArgsLightOn = mkLexargs(filename, defineConstants, LightSyntaxStatus(true, false), lexResourceManager, ref [], DiscardErrorsLogger, PathMap.empty)
let lexArgsLightOff = mkLexargs(filename, defineConstants, LightSyntaxStatus(false, false), lexResourceManager, ref [], DiscardErrorsLogger, PathMap.empty)

member this.CreateLineTokenizer(lineText: string) =
let isFeatureSupported (_featureId:LanguageFeature) = true //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
let lexbuf = UnicodeLexing.StringAsLexbuf(isFeatureSupported, lineText)
FSharpLineTokenizer(lexbuf, Some lineText.Length, filename, lexArgsLightOn, lexArgsLightOff)

member this.CreateBufferTokenizer bufferFiller =
let isFeatureSupported (_featureId:LanguageFeature) = true //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
let lexbuf = UnicodeLexing.FunctionAsLexbuf(isFeatureSupported, bufferFiller)
FSharpLineTokenizer(lexbuf, None, filename, lexArgsLightOn, lexArgsLightOff)

Expand Down
4 changes: 3 additions & 1 deletion src/fsharp/service/service.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1534,7 +1534,9 @@ module internal Parser =
let tokenizer = LexFilter.LexFilter(lightSyntaxStatus, options.CompilingFsLib, Lexer.token lexargs true, lexbuf)
tokenizer.Lexer

let isFeatureSupported (_featureId:LanguageFeature) = true //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// Public callers are unable to answer LanguageVersion feature support questions.
// External Tools including the VS IDE will enable the default LanguageVersion
let isFeatureSupported (_featureId:LanguageFeature) = true
let createLexbuf sourceText isFeatureSupported =
UnicodeLexing.SourceTextAsLexbuf(isFeatureSupported, sourceText)

Expand Down
1 change: 0 additions & 1 deletion src/utils/CompilerLocationUtils.fs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,6 @@ module internal FSharpEnvironment =
// For the prototype compiler, we can just use the current domain
tryCurrentDomain()
with e ->
System.Diagnostics.Debug.Assert(false, "Error while determining default location of F# compiler")
None


Expand Down
1 change: 1 addition & 0 deletions tests/fsharp/FSharpSuite.Tests.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
<Compile Include="..\FSharp.Compiler.UnitTests\NunitHelpers.fs">
<Link>NunitHelpers.fs</Link>
</Compile>
<Compile Include="HandleExpects.fs" />
<Compile Include="single-test.fs" />
<Compile Include="TypeProviderTests.fs" />
<Compile Include="tests.fs" />
Expand Down
196 changes: 196 additions & 0 deletions tests/fsharp/HandleExpects.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
module HandleExpects

open System
open System.IO
open System.Text.RegularExpressions
open System.Xml

type Expects = { status:string; id:string; span:string; pattern:string; mutable matched:bool; line:string }
type ErrorMessage = { source:string; status:string; id:string; span:string; text:string; mutable matched:bool; line:string }
type Span = { startrow:int; startcol:int; endrow:int; endcol:int }

let tryParseSpan (span:string) =
let s = span.Trim([| '('; ')' |]).Split(',')
match s.Length with
| 2 -> { startrow=Int32.Parse(s.[0]); startcol=Int32.Parse(s.[1]); endrow=Int32.MaxValue; endcol=Int32.MaxValue }
| 4 -> { startrow=Int32.Parse(s.[0]); startcol=Int32.Parse(s.[1]); endrow=Int32.Parse(s.[2]); endcol=Int32.Parse(s.[3]) }
| _ -> raise (InvalidDataException(sprintf "The span : '%s' is invalid" span));

let isStringEmpty s = String.IsNullOrWhiteSpace(s)
let isStringNotEmpty s = not (isStringEmpty s)
let stringToLower s = if isStringNotEmpty s then s.ToLower() else s
let areStringsEqual s1 s2 = String.Compare(s1, s2, StringComparison.OrdinalIgnoreCase) = 0
let areSpansEqual s1 s2 =
let span1 = tryParseSpan s1
let span2 = tryParseSpan s2
if span1.startrow <> span2.startrow then false
elif span1.startcol <> span2.startcol then false
elif span1.endrow <> span2.endrow then false
elif span1.endcol <> span2.endcol then false
else true

let stripFromFileExpectations source =
let readExpect expect =
let pattern = "(?<tagOpen><Expects{1}[^>]*>{1})(?<tagContent>.*)(?<tagClose></Expects>)" //"(<!--((?!-->).)*-->|<\w*((?!\/<).)*\/>|<(?<Expects>\w+)[^>]*>(?>[^<]|(?R))*<\/\k<Expects>\s*>)"
let rx = new Regex(pattern)
let matched = rx.Match(expect)
if matched.Success then
// The content of the Expects group contains a lot of invalid Xml and the Xml reader fails when it sees it.
// So we just save it away, remove it from the xml, then read the xml and put it back
// Save away the contents of the element and strip it out of expect pattern
let content = (matched.Groups.[2]).ToString()
let nocontentxpect =
if isStringEmpty content then expect
else expect.Replace(content, "")

let rdr = XmlReader.Create(new StringReader(nocontentxpect))
let mutable element = { status="success"; id = ""; span = ""; pattern = content; matched = false; line=nocontentxpect }
let mutable insideExpects = false
let mutable foundOne = false
try
let rec loop () =
if rdr.Read() then
match rdr.NodeType with
| XmlNodeType.Element when String.Compare(rdr.Name, "Expects", StringComparison.OrdinalIgnoreCase) = 0 ->
insideExpects <- true
if rdr.AttributeCount > 0 then
let status = stringToLower (rdr.GetAttribute("status"))
let span = rdr.GetAttribute("span")
let id = stringToLower (rdr.GetAttribute("id"))
element <- {element with status=status; id=id; span=span }
foundOne <- true
| XmlNodeType.EndElement when String.Compare(rdr.Name, "Expects", StringComparison.OrdinalIgnoreCase) = 0 ->
insideExpects <- false
| _ -> ()
loop ()
else ()
loop ()
if foundOne then Some element
else None
with | e -> printfn "Oops !!! %A" e; reraise()
else None

File.ReadAllLines(source)
|> Array.filter(fun line -> line.Trim().StartsWith(@"//"))
|> Array.map(fun line -> line.Trim().Substring(2).Trim())
|> Array.filter(fun line -> line.StartsWith(@"<Expects", StringComparison.OrdinalIgnoreCase))
|> Array.map(fun expect -> readExpect expect)
|> Array.filter(fun expect -> expect.IsSome)
|> Array.map(fun expect -> expect.Value)

let readErrorMessagesFromOutput output =
//Formats of error messages
// Syntax error in code:
// 1. filename(row,col): (sometext perhaps typecheck) error|warning ErrorNo: ErrorText
// e.g: Program.fs(5,9): error ErrorNo: ErrorText
// 2. Program.fs(5,3,5,20): (sometext perhaps typecheck) error FS0039: ErrorText
// e.g:
// Program.fs(5,3,5,20): (sometext perhaps typecheck) error FS0039: PicturePoint ...
// 3. error ErrorNo: ErrorText
// e.g: error FS0207: No inputs specified
let getErrorMessage line pattern =
let rx = new Regex(pattern)
let matched = rx.Match(line)
let getMatchForName (name:string) = matched.Groups.[name].ToString()

if matched.Success then Some {
source = (getMatchForName "tagSourceFileName")
status = stringToLower (getMatchForName "tagStatus")
id = stringToLower (getMatchForName "tagErrorNo")
span = (getMatchForName "tagSpan")
text = (getMatchForName "tagText")
matched = false
line = line
}
else None

let rgxTagSourceFileName = "(?<tagSourceFileName>[^(]{1,})(?:[(]{1})"
let rgxTagSpan = "(?<tagSpan>[^):]{1,})(?:[)]{1})(?:[(\s:]*)"
let rgxTagStatus = "(?<tagStatus>(error|typecheck error|warning|success|notin))"
let rgxColonWhiteSpace = "(?:[\s:]*)"
let rgxWhiteSpace = "(?:[\s]*)"
let rgxTagErrorNo = "(?<tagErrorNo>\s*[^:\s]*)"
let rgxTagText = "(?<tagText>.*)"
let rgxTagTail = "(?<tagTail>\s\[.*\]$)"

// E.g: Q:\version46\test.fs(25,13): error FS0010: Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. [Q:\Temp\FSharp.Cambridge\vaw2t1vp.cai\f0bi0hny.wwx.fsproj]
let rgxFull = rgxTagSourceFileName + rgxTagSpan + rgxColonWhiteSpace + rgxTagStatus + rgxWhiteSpace + rgxTagErrorNo + rgxColonWhiteSpace + rgxTagText + rgxWhiteSpace + rgxTagTail

// E.g: FSC : error FS0010: Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. [Q:\Temp\FSharp.Cambridge\vaw2t1vp.cai\f0bi0hny.wwx.fsproj]
let rgxShort = rgxTagStatus + rgxTagErrorNo + rgxColonWhiteSpace + rgxTagText + rgxWhiteSpace + rgxTagTail
[|
for line in output do
let errorMessage =
getErrorMessage line rgxFull
|> Option.orElse (getErrorMessage line rgxShort)
match errorMessage with
| Some e -> yield e
| _ -> ()
|]

let compareResults output (expectations:Expects array) (errorMessages:ErrorMessage array) =
for expect in expectations do
match expect.status with
| "error"
| "typecheck error"
| "warning" ->
// Check for this error/warning in found errors list
for msg in errorMessages do
let matched =
if isStringNotEmpty expect.id && not (areStringsEqual expect.id msg.id) then false
elif isStringNotEmpty expect.status && not (areStringsEqual expect.status msg.status) then false
elif isStringNotEmpty expect.span && not (areSpansEqual expect.span msg.span) then false
elif isStringNotEmpty expect.pattern then
let regex = new Regex(expect.pattern)
let matched = regex.Match(msg.text)
matched.Success
else true
if matched then
expect.matched <- true
msg.matched <- true
| "success" ->
// In this case search for text in the page
let regex = new Regex(expect.pattern)
for line in output do
let matched = regex.Match(line)
if matched.Success then expect.matched <- true
| "notin" ->
// In this case search for text not appearing in the page
let regex = new Regex(expect.pattern)
let mutable found = false
for line in output do
let matched = regex.Match(line)
if matched.Success then found <- true
if not found then expect.matched <- true
| _ -> ()

let verifyResults source outputPath =
let output = File.ReadAllLines(outputPath)
let expectations = stripFromFileExpectations source
if expectations.Length > 0 then
// There must be at least one <Expects></Expects> to do this testing
let errorMessages = readErrorMessagesFromOutput output
compareResults output expectations errorMessages

// Print out discovered expects
let verifiedexpectations =
expectations
|> Seq.fold(fun result expects ->
if not (expects.matched) then
printfn "Failed to match expected result '%s'" expects.line
false
else result
) true
let verifiederrormessages =
errorMessages
|> Seq.fold(fun result msg ->
if not (msg.matched) then
printfn "Failed to match produced error message: '%s'" msg.line
false
else result
) true

if not (verifiedexpectations && verifiederrormessages) then
failwith (sprintf "Failed validating error codes")

//HandleExpects.verifyResults @"C:\Users\kevinr\AppData\Local\Temp\FSharp.Cambridge\bcnyzkvb.ict\test.fs" @"C:\Users\kevinr\AppData\Local\Temp\FSharp.Cambridge\bcnyzkvb.ict\buildoutput.txt"
Loading

0 comments on commit f08fb25

Please sign in to comment.