Compare commits
2 commits
4349310cab
...
10a460d2c3
| Author | SHA1 | Date | |
|---|---|---|---|
| 10a460d2c3 | |||
| ae04034028 |
2 changed files with 35 additions and 22 deletions
|
|
@ -2,6 +2,6 @@
|
|||
|
||||
This allows you to annotate Xunit test cases with a mutation that should cause the test to fail.
|
||||
|
||||
`verify-coverage-mutants.fsx` will apply each mutation and verify that the test actually fails.
|
||||
`mutannot.fsx` will apply each mutation and verify that the test actually fails.
|
||||
|
||||
Current state: LLM-generated prototype
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ let repoRoot =
|
|||
|
||||
let parseArgs (args: string list) =
|
||||
let usage () =
|
||||
fail "Usage: verify-coverage-mutants.fsx <path/to/project.fsproj> [--configuration Debug|Release] [--build-arg <value> ...] [--no-build] [--list | --show <id> | --run [id...]]"
|
||||
fail "Usage: mutannot.fsx <path/to/project.fsproj> [--configuration Debug|Release] [--build-arg <value> ...] [--no-build] [--list | --show <id> | --run [id...]]"
|
||||
|
||||
let rec loop configuration projectPath buildArgs noBuild remaining =
|
||||
match remaining with
|
||||
|
|
@ -178,27 +178,40 @@ let findMutation id mutations =
|
|||
|> Array.tryFind (fun mutation -> mutation.Id = id)
|
||||
|> Option.defaultWith (fun () -> fail $"Unknown mutation id: {id}")
|
||||
|
||||
let lineNumberAt (text: string) index =
|
||||
let mutable line = 1
|
||||
for i in 0 .. index - 1 do
|
||||
if text[i] = '\n' then
|
||||
line <- line + 1
|
||||
line
|
||||
let lineSpan (filePath: string) (text: string) (lineNumber: int) =
|
||||
if lineNumber < 1 then
|
||||
fail $"Line number must be positive: {lineNumber}"
|
||||
|
||||
let replaceNearestOccurrence (mutation: MutationCase) (text: string) =
|
||||
let rec collect fromIndex acc =
|
||||
let idx = text.IndexOf(mutation.Find, fromIndex, StringComparison.Ordinal)
|
||||
if idx < 0 then List.rev acc
|
||||
else collect (idx + 1) (idx :: acc)
|
||||
let mutable currentLine = 1
|
||||
let mutable lineStart = 0
|
||||
let mutable index = 0
|
||||
|
||||
let matches = collect 0 []
|
||||
match matches with
|
||||
| [] -> fail $"Could not find '{mutation.Find}' in {mutation.File}"
|
||||
| _ ->
|
||||
let chosen =
|
||||
matches
|
||||
|> List.minBy (fun idx -> abs (lineNumberAt text idx - mutation.Line))
|
||||
text.Remove(chosen, mutation.Find.Length).Insert(chosen, mutation.Replace)
|
||||
while index < text.Length && currentLine < lineNumber do
|
||||
if text[index] = '\n' then
|
||||
currentLine <- currentLine + 1
|
||||
lineStart <- index + 1
|
||||
|
||||
index <- index + 1
|
||||
|
||||
if currentLine <> lineNumber then
|
||||
fail $"Line {lineNumber} does not exist in {filePath}"
|
||||
|
||||
let mutable lineEnd = lineStart
|
||||
while lineEnd < text.Length && text[lineEnd] <> '\n' && text[lineEnd] <> '\r' do
|
||||
lineEnd <- lineEnd + 1
|
||||
|
||||
lineStart, lineEnd
|
||||
|
||||
let replaceOccurrenceOnLine (mutation: MutationCase) (text: string) =
|
||||
let lineStart, lineEnd = lineSpan mutation.File text mutation.Line
|
||||
let lineText = text.Substring(lineStart, lineEnd - lineStart)
|
||||
let lineOffset = lineText.IndexOf(mutation.Find, StringComparison.Ordinal)
|
||||
|
||||
if lineOffset < 0 then
|
||||
fail $"Line {mutation.Line} in {mutation.File} does not contain '{mutation.Find}'."
|
||||
|
||||
let absoluteOffset = lineStart + lineOffset
|
||||
text.Remove(absoluteOffset, mutation.Find.Length).Insert(absoluteOffset, mutation.Replace)
|
||||
|
||||
let printMutation mutation =
|
||||
printfn "id: %s" mutation.Id
|
||||
|
|
@ -243,7 +256,7 @@ let runMutation (mutation: MutationCase) =
|
|||
fail $"Target file does not exist in worktree: {targetFile}"
|
||||
|
||||
let originalText = File.ReadAllText targetFile
|
||||
let mutatedText = replaceNearestOccurrence mutation originalText
|
||||
let mutatedText = replaceOccurrenceOnLine mutation originalText
|
||||
File.WriteAllText(targetFile, mutatedText)
|
||||
|
||||
printfn "==> %s: %s" mutation.Id mutation.TestName
|
||||
Loading…
Add table
Add a link
Reference in a new issue