165 lines
6.3 KiB
FSharp
165 lines
6.3 KiB
FSharp
open System
|
|
open System.Diagnostics
|
|
open System.IO
|
|
|
|
type Result = { Code: int; Output: string; Error: string }
|
|
type Fixture = { Root: string; Source: string; Mask: string }
|
|
|
|
let require condition message =
|
|
if not condition then invalidOp message
|
|
|
|
let run root environment executable arguments =
|
|
let start = ProcessStartInfo executable
|
|
start.WorkingDirectory <- root
|
|
start.UseShellExecute <- false
|
|
start.RedirectStandardOutput <- true
|
|
start.RedirectStandardError <- true
|
|
for argument in arguments do start.ArgumentList.Add argument
|
|
for name, value in environment do start.Environment[name] <- value
|
|
use child = Process.Start start
|
|
let output = child.StandardOutput.ReadToEndAsync()
|
|
let error = child.StandardError.ReadToEndAsync()
|
|
child.WaitForExit()
|
|
{ Code = child.ExitCode; Output = output.Result; Error = error.Result }
|
|
|
|
let git fixture arguments =
|
|
let result = run fixture.Root [] "git" arguments
|
|
require (result.Code = 0) result.Error
|
|
result.Output
|
|
|
|
let write path (text: string) = File.WriteAllText(path, text)
|
|
let stage fixture = git fixture [ "add"; "." ] |> ignore
|
|
let hook = Path.Combine(__SOURCE_DIRECTORY__, "pre-commit")
|
|
|
|
let runHook fixture environment =
|
|
let before = git fixture [ "ls-files"; "--stage"; "-z" ]
|
|
let result = run fixture.Root environment hook []
|
|
require (before = git fixture [ "ls-files"; "--stage"; "-z" ]) "hook changed the index"
|
|
result
|
|
|
|
let passes fixture =
|
|
let result = runHook fixture []
|
|
require (result.Code = 0) result.Error
|
|
|
|
let fails expected fixture =
|
|
let result = runHook fixture []
|
|
require (result.Code <> 0) "hook unexpectedly succeeded"
|
|
require (result.Error.Contains(expected: string)) result.Error
|
|
|
|
let directory fixture path = Directory.CreateDirectory(Path.Combine(fixture.Root, path)).FullName
|
|
|
|
let tests: (string * (Fixture -> unit)) list =
|
|
[ "staged tree", fun fixture ->
|
|
write fixture.Source "unstaged source edit\n"
|
|
write fixture.Mask "unstaged invalid mask\n"
|
|
passes fixture
|
|
|
|
"unrelated change", fun fixture ->
|
|
write fixture.Source "other=changed\nvalue=synthetic\n"
|
|
stage fixture
|
|
passes fixture
|
|
|
|
"missing match", fun fixture ->
|
|
write fixture.Source "value=changed\n"
|
|
stage fixture
|
|
fails "no match" fixture
|
|
|
|
"multiple matches", fun fixture ->
|
|
write fixture.Source "synthetic synthetic\n"
|
|
stage fixture
|
|
fails "multiple matches" fixture
|
|
|
|
"overlapping matches", fun fixture ->
|
|
write fixture.Source "aaa"
|
|
write fixture.Mask "[[replace]]\nbefore = 'aa'\nafter = 'b'\n"
|
|
stage fixture
|
|
fails "multiple matches" fixture
|
|
|
|
"empty before", fun fixture ->
|
|
write fixture.Mask "[[replace]]\nbefore = ''\nafter = 'x'\n"
|
|
stage fixture
|
|
fails "before is empty" fixture
|
|
|
|
"ordered rules", fun fixture ->
|
|
write fixture.Mask
|
|
("[[replace]]\nbefore = 'synthetic'\nafter = 'intermediate'\n"
|
|
+ "[[replace]]\nbefore = 'intermediate'\nafter = '********'\n")
|
|
stage fixture
|
|
passes fixture
|
|
|
|
"invalid TOML", fun fixture ->
|
|
write fixture.Mask "invalid = ["
|
|
stage fixture
|
|
let result = runHook fixture []
|
|
require (result.Code <> 0) "invalid TOML was accepted"
|
|
|
|
"invalid schema", fun fixture ->
|
|
write fixture.Mask "[[replace]]\nbefore = 'synthetic'\nafter = 1\n"
|
|
stage fixture
|
|
fails "expected text" fixture
|
|
|
|
"missing target", fun fixture ->
|
|
git fixture [ "rm"; "--cached"; "config.txt" ] |> ignore
|
|
fails "target is not a regular file" fixture
|
|
|
|
"symlink target", fun fixture ->
|
|
File.Delete fixture.Source
|
|
File.CreateSymbolicLink(fixture.Source, "elsewhere") |> ignore
|
|
stage fixture
|
|
fails "target is not a regular file" fixture
|
|
|
|
"omitted subtree", fun fixture ->
|
|
let hidden = directory fixture "hidden"
|
|
write (Path.Combine(hidden, ".omit")) ""
|
|
write (Path.Combine(hidden, "invalid.mask.toml")) "invalid"
|
|
let nested = directory fixture "hidden/nested"
|
|
write (Path.Combine(nested, ".omit")) ""
|
|
write (Path.Combine(nested, "legacy.mask.diff")) "invalid"
|
|
stage fixture
|
|
let result = runHook fixture []
|
|
require (result.Code = 0) result.Error
|
|
require (result.Output.Contains "1 directories omitted") result.Output
|
|
|
|
"omit prefix boundary", fun fixture ->
|
|
let hidden = directory fixture "hidden"
|
|
write (Path.Combine(hidden, ".omit")) ""
|
|
let peer = directory fixture "hidden-peer"
|
|
write (Path.Combine(peer, "invalid.mask.toml")) "invalid"
|
|
stage fixture
|
|
fails "target is not a regular file" fixture
|
|
|
|
"unstaged omit", fun fixture ->
|
|
write fixture.Source "synthetic synthetic"
|
|
stage fixture
|
|
write (Path.Combine(fixture.Root, ".omit")) ""
|
|
fails "multiple matches" fixture
|
|
|
|
"legacy format", fun fixture ->
|
|
write (Path.Combine(fixture.Root, "old.mask.diff")) "invalid"
|
|
stage fixture
|
|
fails "legacy mask format" fixture
|
|
|
|
"alternate index", fun fixture ->
|
|
let index = Path.Combine(fixture.Root, "alternate")
|
|
File.Copy(Path.Combine(fixture.Root, ".git/index"), index)
|
|
write fixture.Source "staged drift\n"
|
|
git fixture [ "add"; "config.txt" ] |> ignore
|
|
let before = File.ReadAllBytes index
|
|
let result = runHook fixture [ "GIT_INDEX_FILE", index ]
|
|
require (result.Code = 0) result.Error
|
|
require (before = File.ReadAllBytes index) "hook changed the alternate index" ]
|
|
|
|
for name, test in tests do
|
|
let root = Directory.CreateDirectory(Path.Combine(fsi.CommandLineArgs[1], $"hook-tests-{Guid.NewGuid():N}")).FullName
|
|
let fixture = { Root = root; Source = Path.Combine(root, "config.txt"); Mask = Path.Combine(root, "config.txt.mask.toml") }
|
|
try
|
|
git fixture [ "init"; "-q" ] |> ignore
|
|
write fixture.Source "value=synthetic\n"
|
|
write fixture.Mask "[[replace]]\nbefore = 'synthetic'\nafter = '********'\n"
|
|
stage fixture
|
|
test fixture
|
|
printfn "passed: %s" name
|
|
finally
|
|
Directory.Delete(root, true)
|
|
|
|
printfn "%d hook tests passed" tests.Length
|