Skip to content

Commit

Permalink
Add tests and fix #1947
Browse files Browse the repository at this point in the history
  • Loading branch information
matthid committed May 21, 2018
1 parent 062e030 commit f221cea
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 25 deletions.
2 changes: 1 addition & 1 deletion src/app/Fake.Runtime/CoreCache.fs
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ let prepareContext (config:FakeConfig) (cache:ICachingProvider) =

let getHashUncached () =
//TODO this is only calculating the hash for the input file, not anything #load-ed
let allScriptContents = getAllScripts config.CompileOptions.FsiOptions.Defines config.ScriptTokens config.ScriptFilePath
let allScriptContents = getAllScripts config.CompileOptions.FsiOptions.Defines config.ScriptTokens.Value config.ScriptFilePath
let getOpts (c:CompileOptions) = c.FsiOptions.AsArgs // @ c.CompileReferences
allScriptContents, getScriptHash allScriptContents (getOpts config.CompileOptions)

Expand Down
74 changes: 50 additions & 24 deletions src/app/Fake.Runtime/HashGeneration.fs
Original file line number Diff line number Diff line change
Expand Up @@ -17,31 +17,32 @@ type Script = {
let getAllScriptContents (pathsAndContents : seq<Script>) =
pathsAndContents |> Seq.map(fun s -> s.HashContent)

let getAllScripts defines (tokens:Lazy<Fake.Runtime.FSharpParser.TokenizedScript>) scriptPath : Script list =
let rec getAllScriptsRec (tokens:Fake.Runtime.FSharpParser.TokenizedScript) scriptPath parentIncludes : Script list =
let resolvePath currentIncludes currentDir relativeOrAbsolute isDir =
let getAllScripts defines (tokens:Fake.Runtime.FSharpParser.TokenizedScript) scriptPath : Script list =
let rec getAllScriptsRec (tokens:Fake.Runtime.FSharpParser.TokenizedScript) workDir (scriptName:string) parentIncludes : Script list =
let tryResolvePath currentIncludes currentDir relativeOrAbsolute isDir =
let possiblePaths =
if Path.IsPathRooted relativeOrAbsolute then [ relativeOrAbsolute ]
else
currentDir :: currentIncludes
|> List.map (fun bas -> Path.Combine(bas, relativeOrAbsolute))
let realPath =
match possiblePaths |> Seq.tryFind (if isDir then Directory.Exists else File.Exists) with
| Some f -> f
| None ->
failwithf "FAKE-CACHING: Could not find %s '%s' in any paths searched. Searched paths:\n%A" (if isDir then "directory" else "file") relativeOrAbsolute (currentDir :: currentIncludes)
realPath
possiblePaths
|> Seq.tryFind (if isDir then Directory.Exists else File.Exists)
|> Option.map Path.GetFullPath
let resolvePath currentIncludes currentDir relativeOrAbsolute isDir =
match tryResolvePath currentIncludes currentDir relativeOrAbsolute isDir with
| Some f -> f
| None ->
failwithf "FAKE-CACHING: Could not find %s '%s' in any paths searched. Searched paths:\n%A" (if isDir then "directory" else "file") relativeOrAbsolute (currentDir :: currentIncludes)

let loadedContents =
tokens
|> FSharpParser.findProcessorDirectives
|> List.fold (fun ((currentIncludes, currentDir, childScripts) as state) preprocessorDirective ->
((parentIncludes, workDir, []), FSharpParser.findProcessorDirectives tokens)
||> List.fold (fun ((currentIncludes, currentDir, childScripts) as state) preprocessorDirective ->
let (|MatchFirstString|_|) (l:FSharpParser.StringLike list) =
match l with
| FSharpParser.StringLike.StringKeyword FSharpParser.SourceDirectory :: _ ->
Some (Path.GetDirectoryName scriptPath)
Some (".")
| FSharpParser.StringLike.StringKeyword FSharpParser.SourceFile :: _ ->
Some (Path.GetFileName scriptPath)
Some (scriptName)
| FSharpParser.StringLike.StringKeyword (FSharpParser.Unknown s) :: _ ->
printfn "FAKE-CACHING: Unknown special key '%s' in preprocessor directive: %A" s preprocessorDirective.Token
None
Expand All @@ -56,25 +57,50 @@ let getAllScripts defines (tokens:Lazy<Fake.Runtime.FSharpParser.TokenizedScript
if name = Runners.loadScriptName && childScriptRelPath.StartsWith ".fake"
then currentIncludes, currentDir, childScripts
else
let realPath = resolvePath currentIncludes currentDir childScriptRelPath false
let realPath =
try resolvePath currentIncludes currentDir childScriptRelPath false
with e ->
let p = String.Join("\n ", currentDir :: currentIncludes)
let msg =
sprintf "%s(%d,%d): error FS0078: Unable to find the file '%s' in any of\n %s"
(Path.Combine(workDir, scriptPath))
preprocessorDirective.Token.LineNumber
(match preprocessorDirective.Token.TokenInfo with Some t -> t.LeftColumn + 1 | None -> 1)
childScriptRelPath
p
raise <| exn(msg, e)
let newWorkDir = Path.GetDirectoryName realPath
let newScriptName = Path.GetFileName realPath
let nestedTokens =
File.ReadLines realPath
|> FSharpParser.getTokenized realPath defines
currentIncludes, currentDir, getAllScriptsRec nestedTokens realPath currentIncludes @ childScripts
currentIncludes, currentDir, getAllScriptsRec nestedTokens newWorkDir newScriptName currentIncludes @ childScripts
| { Token = { Representation = "#cd" }; Strings = MatchFirstString relOrAbsolute } ->
let realPath = resolvePath currentIncludes currentDir relOrAbsolute true
let realPath =
try resolvePath [] currentDir relOrAbsolute true
with e ->
let p = Path.Combine(currentDir, relOrAbsolute)
let msg =
sprintf "%s(%d,%d): error FS2302: Directory '%s' doesn't exist"
(Path.Combine(workDir, scriptPath))
preprocessorDirective.Token.LineNumber
(match preprocessorDirective.Token.TokenInfo with Some t -> t.LeftColumn + 1 | None -> 1)
p
raise <| exn(msg, e)
currentIncludes, realPath, childScripts
| { Token = { Representation = "#I" }; Strings = MatchFirstString relOrAbsolute } ->
let realPath = resolvePath currentIncludes currentDir relOrAbsolute true
realPath :: currentIncludes, currentDir, childScripts
match tryResolvePath currentIncludes currentDir relOrAbsolute true with
| Some realPath ->
realPath :: currentIncludes, currentDir, childScripts
| None -> currentIncludes, currentDir, childScripts
| _ -> state
) (parentIncludes, Path.GetDirectoryName scriptPath, [])
)
|> fun (_, _, c) -> c
|> List.rev
{ Location = scriptPath
{ Location = Path.Combine(workDir, scriptName)
HashContent = FSharpParser.getHashableString tokens } :: loadedContents

getAllScriptsRec tokens.Value scriptPath []
let dir = Path.GetDirectoryName scriptPath
let name = Path.GetFileName scriptPath
getAllScriptsRec tokens dir name []

let getStringHash (s:string) =
use sha256 = System.Security.Cryptography.SHA256.Create()
Expand Down
92 changes: 92 additions & 0 deletions src/test/Fake.Core.UnitTests/Fake.Runtime.fs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
module Fake.RuntimeTests

open System.IO
open Fake.Runtime
open Fake.IO.FileSystemOperators
open Expecto
open Expecto.Flip
open Fake.IO

[<Tests>]
let tests =
Expand Down Expand Up @@ -51,4 +54,93 @@ nuget Fake.Core.SemVer prerelease //"
|> Fake.Runtime.FSharpParser.findInterestingItems
let expected = []
Expect.equal "Expected to find reference." expected interesting

// TODO: Add test if everything works with #ifdefed #r "paket: line"

// Tests that we handle #I and #load properly
testCase "Test #1947 - non-existing folders" <| fun _ ->
let tmpDir = Path.GetTempFileName()
File.Delete tmpDir
Directory.CreateDirectory tmpDir |> ignore
try
Directory.CreateDirectory (tmpDir </> "packages" </> "Octokit" </> "lib" </> "net45") |> ignore
Directory.CreateDirectory (tmpDir </> "paket-files" </> "test") |> ignore
let scriptText = """
#load "paket-files/test/octokit.fsx"
"""
let octokit = """
#I __SOURCE_DIRECTORY__
#I @"../../../../../packages/Octokit/lib/net45"
#I @"../../packages/Octokit/lib/net45"
#I @"../../../../../../packages/build/Octokit/lib/net45"
#r "Octokit.dll"
"""
File.WriteAllText(tmpDir </> "paket-files" </> "test" </> "octokit.fsx", octokit)
let tokens =
Fake.Runtime.FSharpParser.getTokenized "build.fsx" ["DOTNETCORE"; "FAKE"] (scriptText.Split([|'\r';'\n'|]))
let scripts = HashGeneration.getAllScripts [] tokens (tmpDir </> "build.fsx")
let expected = [
tmpDir </> "build.fsx"
tmpDir </> "paket-files" </> "test" </> "octokit.fsx"
]
let actual = scripts |> List.map (fun s -> s.Location)
Expect.equal "Expected to find script." expected actual
finally
Directory.Delete(tmpDir, true)

testCase "Test #1947 - test #I with ." <| fun _ ->
let tmpDir = Path.GetTempFileName()
File.Delete tmpDir
Directory.CreateDirectory tmpDir |> ignore
try
let testScriptPath = tmpDir </> "test.fsx"
let testScript = """
#I "test"
#I "."
#load "file.fsx"
"""
let fileScriptPath = tmpDir </> "test" </> "file.fsx"
let fileScript = """
printfn "Test"
#load "other.fsx"
"""
let otherScriptPath = tmpDir </> "other.fsx"
let otherScript = """
printfn "other.fsx"
"""
Directory.CreateDirectory (tmpDir </> "test") |> ignore
File.WriteAllText(fileScriptPath, fileScript)
File.WriteAllText(otherScriptPath, otherScript)
let tokens =
Fake.Runtime.FSharpParser.getTokenized "test.fsx" ["DOTNETCORE"; "FAKE"] (testScript.Split([|'\r';'\n'|]))
let scripts = HashGeneration.getAllScripts [] tokens testScriptPath
let expected = [
testScriptPath
fileScriptPath
otherScriptPath
]
let actual = scripts |> List.map (fun s -> s.Location)
Expect.equal "Expected to find script." expected actual
finally
Directory.Delete(tmpDir, true)

testCase "Test #1947 - good error message" <| fun _ ->
let tmpDir = Path.GetTempFileName()
File.Delete tmpDir
Directory.CreateDirectory tmpDir |> ignore
try
let testScriptPath = tmpDir </> "test.fsx"
let testScript = """
#cd "asdas"
"""
let tokens =
Fake.Runtime.FSharpParser.getTokenized "test.fsx" ["DOTNETCORE"; "FAKE"] (testScript.Split([|'\r';'\n'|]))
try
let scripts = HashGeneration.getAllScripts [] tokens testScriptPath
Expect.isTrue "Expected an exception" false
with e ->
(e.Message.Contains "test.fsx(2,1): error FS2302: Directory '" && e.Message.Contains "' doesn't exist")
|> Expect.isTrue (sprintf "Expected a good error message, but got: %s" e.Message)
finally
Directory.Delete(tmpDir, true)
]

0 comments on commit f221cea

Please sign in to comment.