mirror of
https://github.com/Smaug123/WoofWare.Expect
synced 2025-10-12 15:38:40 +00:00
Compare commits
1 Commits
e0153ab182
...
fcs
Author | SHA1 | Date | |
---|---|---|---|
|
7d452ea533 |
@@ -19,9 +19,12 @@ module SimpleTest =
|
|||||||
let ``Example of a failing test`` () =
|
let ``Example of a failing test`` () =
|
||||||
expect {
|
expect {
|
||||||
snapshot
|
snapshot
|
||||||
@"snapshot mismatch! snapshot at filepath.fs:99 (Example of a failing test) diff:
|
@"snapshot mismatch! snapshot at filepath.fs:99 (Example of a failing test) was:
|
||||||
|
|
||||||
- 123
|
- 123
|
||||||
|
|
||||||
|
actual was:
|
||||||
|
|
||||||
+ 124"
|
+ 124"
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -61,21 +64,26 @@ module SimpleTest =
|
|||||||
// Out of the box, comments in snapshots cause the JSON parser to throw, so the snapshot fails to match...
|
// Out of the box, comments in snapshots cause the JSON parser to throw, so the snapshot fails to match...
|
||||||
expect {
|
expect {
|
||||||
snapshot
|
snapshot
|
||||||
@"snapshot mismatch! snapshot at file.fs:99 (Custom JSON output) diff:
|
@"snapshot mismatch! snapshot at file.fs:99 (Custom JSON output) was:
|
||||||
|
|
||||||
- [JSON failed to parse:] {
|
- [JSON failed to parse:] {
|
||||||
- // a key here
|
- // a key here
|
||||||
|
- ""a"":3
|
||||||
|
- }
|
||||||
|
|
||||||
|
actual was:
|
||||||
|
|
||||||
+ {
|
+ {
|
||||||
""a"": 3
|
+ ""a"": 3
|
||||||
}"
|
+ }"
|
||||||
|
|
||||||
return
|
return
|
||||||
Assert.Throws<ExpectException> (fun () ->
|
Assert.Throws<ExpectException> (fun () ->
|
||||||
expectWithMockedFilePath ("file.fs", 99) {
|
expectWithMockedFilePath ("file.fs", 99) {
|
||||||
snapshotJson
|
snapshotJson
|
||||||
@"{
|
@"{
|
||||||
// a key here
|
// a key here
|
||||||
""a"": 3
|
""a"":3
|
||||||
}"
|
}"
|
||||||
|
|
||||||
return Map.ofList [ "a", 3 ]
|
return Map.ofList [ "a", 3 ]
|
||||||
|
@@ -1,110 +0,0 @@
|
|||||||
namespace WoofWare.Expect.Test
|
|
||||||
|
|
||||||
open WoofWare.Expect
|
|
||||||
open NUnit.Framework
|
|
||||||
|
|
||||||
[<TestFixture>]
|
|
||||||
module TestDiff =
|
|
||||||
|
|
||||||
[<Test>]
|
|
||||||
let ``Basic diff`` () =
|
|
||||||
let textA =
|
|
||||||
[|
|
|
||||||
"The quick brown fox"
|
|
||||||
"jumps over"
|
|
||||||
"the lazy dog"
|
|
||||||
"Some unique line here"
|
|
||||||
"The end"
|
|
||||||
|]
|
|
||||||
|
|
||||||
let textB =
|
|
||||||
[|
|
|
||||||
"The quick brown fox"
|
|
||||||
"Some unique line here"
|
|
||||||
"jumps over"
|
|
||||||
"the lazy dog"
|
|
||||||
"Another line"
|
|
||||||
"The end"
|
|
||||||
|]
|
|
||||||
|
|
||||||
let diff = Diff.patienceLines textA textB
|
|
||||||
|
|
||||||
expect {
|
|
||||||
snapshot
|
|
||||||
@" 0 0 The quick brown fox
|
|
||||||
+ 1 Some unique line here
|
|
||||||
1 2 jumps over
|
|
||||||
2 3 the lazy dog
|
|
||||||
- 3 Some unique line here
|
|
||||||
+ 4 Another line
|
|
||||||
4 5 The end"
|
|
||||||
|
|
||||||
withFormat Diff.formatWithLineNumbers
|
|
||||||
return diff
|
|
||||||
}
|
|
||||||
|
|
||||||
expect {
|
|
||||||
snapshot
|
|
||||||
@" The quick brown fox
|
|
||||||
+ Some unique line here
|
|
||||||
jumps over
|
|
||||||
the lazy dog
|
|
||||||
- Some unique line here
|
|
||||||
+ Another line
|
|
||||||
The end"
|
|
||||||
|
|
||||||
withFormat Diff.format
|
|
||||||
return diff
|
|
||||||
}
|
|
||||||
|
|
||||||
[<Test>]
|
|
||||||
let ``An example from Incremental`` () =
|
|
||||||
let textA =
|
|
||||||
"""digraph G {
|
|
||||||
rankdir = TB
|
|
||||||
bgcolor = transparent
|
|
||||||
n4 [shape=Mrecord label="{{n4|BindMain|height=2}}" "fontname"="Sans Serif"]
|
|
||||||
n3 [shape=Mrecord label="{{n3|BindLhsChange|height=1}}" "fontname"="Sans Serif"]
|
|
||||||
n1 [shape=Mrecord label="{{n1|Const|height=0}}" "fontname"="Sans Serif"]
|
|
||||||
n2 [shape=Mrecord label="{{n2|Const|height=0}}" "fontname"="Sans Serif"]
|
|
||||||
n3 -> n4
|
|
||||||
n2 -> n4
|
|
||||||
n1 -> n3
|
|
||||||
}"""
|
|
||||||
|
|
||||||
let textB =
|
|
||||||
"""digraph G {
|
|
||||||
rankdir = TB
|
|
||||||
bgcolor = transparent
|
|
||||||
n4 [shape=box label="{{n4|BindMain|height=2}}" ]
|
|
||||||
n3 [shape=box label="{{n3|BindLhsChange|height=1}}" ]
|
|
||||||
n1 [shape=box label="{{n1|Const|height=0}}" ]
|
|
||||||
n2 [shape=box label="{{n2|Const|height=0}}" ]
|
|
||||||
n3 -> n4
|
|
||||||
n2 -> n4
|
|
||||||
n1 -> n3
|
|
||||||
}"""
|
|
||||||
|
|
||||||
let diff = Diff.patience textA textB
|
|
||||||
|
|
||||||
expect {
|
|
||||||
snapshot
|
|
||||||
@" digraph G {
|
|
||||||
rankdir = TB
|
|
||||||
bgcolor = transparent
|
|
||||||
- n4 [shape=Mrecord label=""{{n4|BindMain|height=2}}"" ""fontname""=""Sans Serif""]
|
|
||||||
- n3 [shape=Mrecord label=""{{n3|BindLhsChange|height=1}}"" ""fontname""=""Sans Serif""]
|
|
||||||
- n1 [shape=Mrecord label=""{{n1|Const|height=0}}"" ""fontname""=""Sans Serif""]
|
|
||||||
- n2 [shape=Mrecord label=""{{n2|Const|height=0}}"" ""fontname""=""Sans Serif""]
|
|
||||||
+ n4 [shape=box label=""{{n4|BindMain|height=2}}"" ]
|
|
||||||
+ n3 [shape=box label=""{{n3|BindLhsChange|height=1}}"" ]
|
|
||||||
+ n1 [shape=box label=""{{n1|Const|height=0}}"" ]
|
|
||||||
+ n2 [shape=box label=""{{n2|Const|height=0}}"" ]
|
|
||||||
n3 -> n4
|
|
||||||
n2 -> n4
|
|
||||||
n1 -> n3
|
|
||||||
}"
|
|
||||||
|
|
||||||
withFormat Diff.format
|
|
||||||
return diff
|
|
||||||
}
|
|
@@ -13,7 +13,6 @@
|
|||||||
<Compile Include="Assembly.fs" />
|
<Compile Include="Assembly.fs" />
|
||||||
<Compile Include="BulkUpdateExample.fs" />
|
<Compile Include="BulkUpdateExample.fs" />
|
||||||
<Compile Include="SimpleTest.fs" />
|
<Compile Include="SimpleTest.fs" />
|
||||||
<Compile Include="TestDiff.fs" />
|
|
||||||
<Compile Include="TestExceptionThrowing.fs" />
|
<Compile Include="TestExceptionThrowing.fs" />
|
||||||
<Compile Include="TestSurface.fs" />
|
<Compile Include="TestSurface.fs" />
|
||||||
<Compile Include="TestSnapshotFinding\TestSnapshotFinding.fs" />
|
<Compile Include="TestSnapshotFinding\TestSnapshotFinding.fs" />
|
||||||
|
@@ -298,28 +298,26 @@ type ExpectBuilder (mode : Mode) =
|
|||||||
let raiseError (snapshot : string) (actual : string) : unit =
|
let raiseError (snapshot : string) (actual : string) : unit =
|
||||||
match mode with
|
match mode with
|
||||||
| Mode.AssertMockingSource (mockSource, line) ->
|
| Mode.AssertMockingSource (mockSource, line) ->
|
||||||
let diff = Diff.patience snapshot actual
|
|
||||||
|
|
||||||
sprintf
|
sprintf
|
||||||
"snapshot mismatch! snapshot at %s:%i (%s) diff:\n\n%s"
|
"snapshot mismatch! snapshot at %s:%i (%s) was:\n\n%s\n\nactual was:\n\n%s"
|
||||||
mockSource
|
mockSource
|
||||||
line
|
line
|
||||||
state.Caller.MemberName
|
state.Caller.MemberName
|
||||||
(Diff.format diff)
|
(snapshot |> Text.predent '-')
|
||||||
|
(actual |> Text.predent '+')
|
||||||
|> ExpectException
|
|> ExpectException
|
||||||
|> raise
|
|> raise
|
||||||
| Mode.Assert ->
|
| Mode.Assert ->
|
||||||
if GlobalBuilderConfig.isBulkUpdateMode () then
|
if GlobalBuilderConfig.isBulkUpdateMode () then
|
||||||
GlobalBuilderConfig.registerTest state
|
GlobalBuilderConfig.registerTest state
|
||||||
else
|
else
|
||||||
let diff = Diff.patience snapshot actual
|
|
||||||
|
|
||||||
sprintf
|
sprintf
|
||||||
"snapshot mismatch! snapshot at %s:%i (%s) diff:\n\n%s"
|
"snapshot mismatch! snapshot at %s:%i (%s) was:\n\n%s\n\nactual was:\n\n%s"
|
||||||
state.Caller.FilePath
|
state.Caller.FilePath
|
||||||
state.Caller.LineNumber
|
state.Caller.LineNumber
|
||||||
state.Caller.MemberName
|
state.Caller.MemberName
|
||||||
(Diff.format diff)
|
(snapshot |> Text.predent '-')
|
||||||
|
(actual |> Text.predent '+')
|
||||||
|> ExpectException
|
|> ExpectException
|
||||||
|> raise
|
|> raise
|
||||||
| Mode.Update ->
|
| Mode.Update ->
|
||||||
|
@@ -1,296 +0,0 @@
|
|||||||
namespace WoofWare.Expect
|
|
||||||
|
|
||||||
open System.Collections.Generic
|
|
||||||
|
|
||||||
/// A unit of measure tagging "positions in a sequence".
|
|
||||||
[<Measure>]
|
|
||||||
type pos
|
|
||||||
|
|
||||||
/// Position in a sequence
|
|
||||||
type Position = int<pos>
|
|
||||||
|
|
||||||
/// A Patience diff is composed of a sequence of transformations to get from one string to another.
|
|
||||||
/// This represents those transformations.
|
|
||||||
type DiffOperation =
|
|
||||||
/// This line is the same on both sides of the diff.
|
|
||||||
/// On the left, it appears at position posA. On the right, at position posB.
|
|
||||||
| Match of posA : Position * posB : Position * line : string
|
|
||||||
/// Delete this line, which is at this position.
|
|
||||||
| Delete of posA : Position * line : string
|
|
||||||
/// Insert this line at the given position.
|
|
||||||
| Insert of posB : Position * line : string
|
|
||||||
|
|
||||||
/// The diff between two line-oriented pieces of text.
|
|
||||||
type Diff = private | Diff of DiffOperation list
|
|
||||||
|
|
||||||
/// A match between positions in two sequences
|
|
||||||
type internal LineMatch =
|
|
||||||
{
|
|
||||||
PosA : Position
|
|
||||||
PosB : Position
|
|
||||||
Line : string
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Result of finding unique lines in a sequence
|
|
||||||
type internal UniqueLines =
|
|
||||||
{
|
|
||||||
/// Map from line content to its position (only for unique lines)
|
|
||||||
LinePositions : Map<string, Position>
|
|
||||||
/// All line counts (for verification)
|
|
||||||
LineCounts : Map<string, int>
|
|
||||||
}
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module Diff =
|
|
||||||
/// Find lines that appear exactly once in a sequence
|
|
||||||
let private findUniqueLines (lines : string array) : UniqueLines =
|
|
||||||
let positions = Dictionary<string, Position> ()
|
|
||||||
let counts = Dictionary<string, int> ()
|
|
||||||
|
|
||||||
lines
|
|
||||||
|> Array.iteri (fun i line ->
|
|
||||||
if counts.ContainsKey (line) then
|
|
||||||
counts.[line] <- counts.[line] + 1
|
|
||||||
else
|
|
||||||
counts.[line] <- 1
|
|
||||||
positions.[line] <- i * 1<pos>
|
|
||||||
)
|
|
||||||
|
|
||||||
let uniquePositions =
|
|
||||||
positions
|
|
||||||
|> Seq.filter (fun kvp -> counts.[kvp.Key] = 1)
|
|
||||||
|> Seq.map (fun kvp -> (kvp.Key, kvp.Value))
|
|
||||||
|> Map.ofSeq
|
|
||||||
|
|
||||||
let allCounts = counts |> Seq.map (fun kvp -> (kvp.Key, kvp.Value)) |> Map.ofSeq
|
|
||||||
|
|
||||||
{
|
|
||||||
LinePositions = uniquePositions
|
|
||||||
LineCounts = allCounts
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find longest increasing subsequence based on B positions
|
|
||||||
let private longestIncreasingSubsequence (matches : LineMatch array) : LineMatch list =
|
|
||||||
let n = matches.Length
|
|
||||||
|
|
||||||
if n = 0 then
|
|
||||||
[]
|
|
||||||
else
|
|
||||||
// Dynamic programming arrays
|
|
||||||
let lengths = Array.create n 1
|
|
||||||
let parents = Array.create n -1
|
|
||||||
|
|
||||||
// Build LIS
|
|
||||||
for i in 1 .. n - 1 do
|
|
||||||
for j in 0 .. i - 1 do
|
|
||||||
let bj = matches.[j].PosB
|
|
||||||
let bi = matches.[i].PosB
|
|
||||||
|
|
||||||
if bj < bi && lengths.[j] + 1 > lengths.[i] then
|
|
||||||
lengths.[i] <- lengths.[j] + 1
|
|
||||||
parents.[i] <- j
|
|
||||||
|
|
||||||
// Find longest sequence
|
|
||||||
let maxLength = Array.max lengths
|
|
||||||
let endIndex = Array.findIndex ((=) maxLength) lengths
|
|
||||||
|
|
||||||
// Reconstruct sequence
|
|
||||||
let rec reconstruct idx acc =
|
|
||||||
if idx = -1 then
|
|
||||||
acc
|
|
||||||
else
|
|
||||||
reconstruct parents.[idx] (matches.[idx] :: acc)
|
|
||||||
|
|
||||||
reconstruct endIndex []
|
|
||||||
|
|
||||||
/// Simple Myers diff implementation. You probably want to use `patience` instead, for more human-readable diffs.
|
|
||||||
let myers (a : string array) (b : string array) : Diff =
|
|
||||||
let rec diffHelper (i : int) (j : int) (acc : DiffOperation list) =
|
|
||||||
match i < a.Length, j < b.Length with
|
|
||||||
| false, false -> List.rev acc
|
|
||||||
| true, false ->
|
|
||||||
let deletes =
|
|
||||||
[ i .. a.Length - 1 ] |> List.map (fun idx -> Delete (idx * 1<pos>, a.[idx]))
|
|
||||||
|
|
||||||
(List.rev acc) @ deletes
|
|
||||||
| false, true ->
|
|
||||||
let inserts =
|
|
||||||
[ j .. b.Length - 1 ] |> List.map (fun idx -> Insert (idx * 1<pos>, b.[idx]))
|
|
||||||
|
|
||||||
(List.rev acc) @ inserts
|
|
||||||
| true, true ->
|
|
||||||
if a.[i] = b.[j] then
|
|
||||||
diffHelper (i + 1) (j + 1) (Match (i * 1<pos>, j * 1<pos>, a.[i]) :: acc)
|
|
||||||
else
|
|
||||||
// Look ahead for matches (simple heuristic)
|
|
||||||
let lookAhead = 3
|
|
||||||
|
|
||||||
let aheadMatch =
|
|
||||||
[ 1 .. min lookAhead (min (a.Length - i) (b.Length - j)) ]
|
|
||||||
|> List.tryFind (fun k -> a.[i + k - 1] = b.[j + k - 1])
|
|
||||||
|
|
||||||
match aheadMatch with
|
|
||||||
| Some k when k <= 2 ->
|
|
||||||
// Delete/insert to get to the match
|
|
||||||
let ops =
|
|
||||||
[ 0 .. k - 2 ]
|
|
||||||
|> List.collect (fun offset ->
|
|
||||||
[
|
|
||||||
Delete ((i + offset) * 1<pos>, a.[i + offset])
|
|
||||||
Insert ((j + offset) * 1<pos>, b.[j + offset])
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
diffHelper (i + k - 1) (j + k - 1) (List.rev ops @ acc)
|
|
||||||
| _ ->
|
|
||||||
// No close match, just delete and insert
|
|
||||||
diffHelper (i + 1) j (Delete (i * 1<pos>, a.[i]) :: acc)
|
|
||||||
|
|
||||||
diffHelper 0 0 [] |> Diff
|
|
||||||
|
|
||||||
/// Patience diff: a human-readable line-based diff.
|
|
||||||
/// Operates on lines of string; the function `patience` will split on lines for you.
|
|
||||||
let rec patienceLines (a : string array) (b : string array) : Diff =
|
|
||||||
// Handle empty sequences
|
|
||||||
match a.Length, b.Length with
|
|
||||||
| 0, 0 -> [] |> Diff
|
|
||||||
| 0, _ ->
|
|
||||||
b
|
|
||||||
|> Array.mapi (fun i line -> Insert (i * 1<pos>, line))
|
|
||||||
|> Array.toList
|
|
||||||
|> Diff
|
|
||||||
| _, 0 ->
|
|
||||||
a
|
|
||||||
|> Array.mapi (fun i line -> Delete (i * 1<pos>, line))
|
|
||||||
|> Array.toList
|
|
||||||
|> Diff
|
|
||||||
| _, _ ->
|
|
||||||
// Find unique lines
|
|
||||||
let uniqueA = findUniqueLines a
|
|
||||||
let uniqueB = findUniqueLines b
|
|
||||||
|
|
||||||
// Find common unique lines
|
|
||||||
let commonUniques =
|
|
||||||
Set.intersect
|
|
||||||
(uniqueA.LinePositions |> Map.toSeq |> Seq.map fst |> Set.ofSeq)
|
|
||||||
(uniqueB.LinePositions |> Map.toSeq |> Seq.map fst |> Set.ofSeq)
|
|
||||||
|
|
||||||
if Set.isEmpty commonUniques then
|
|
||||||
// No unique common lines, fall back to Myers
|
|
||||||
myers a b
|
|
||||||
else
|
|
||||||
// Build matches for unique common lines
|
|
||||||
let matches =
|
|
||||||
commonUniques
|
|
||||||
|> Set.toArray
|
|
||||||
|> Array.map (fun line ->
|
|
||||||
{
|
|
||||||
PosA = uniqueA.LinePositions.[line]
|
|
||||||
PosB = uniqueB.LinePositions.[line]
|
|
||||||
Line = line
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|> Array.sortBy (fun m -> m.PosA)
|
|
||||||
|
|
||||||
// Find LIS
|
|
||||||
let anchorMatches = longestIncreasingSubsequence matches |> List.toArray
|
|
||||||
|
|
||||||
// Build diff imperatively
|
|
||||||
let result = ResizeArray<DiffOperation> ()
|
|
||||||
let mutable prevA = 0<pos>
|
|
||||||
let mutable prevB = 0<pos>
|
|
||||||
|
|
||||||
// Process each anchor
|
|
||||||
for anchor in anchorMatches do
|
|
||||||
let anchorA = anchor.PosA
|
|
||||||
let anchorB = anchor.PosB
|
|
||||||
|
|
||||||
// Add diff for section before this anchor
|
|
||||||
if prevA < anchorA || prevB < anchorB then
|
|
||||||
let sectionA = a.[prevA / 1<pos> .. anchorA / 1<pos> - 1]
|
|
||||||
let sectionB = b.[prevB / 1<pos> .. anchorB / 1<pos> - 1]
|
|
||||||
let (Diff sectionDiff) = patienceLines sectionA sectionB
|
|
||||||
|
|
||||||
// Adjust positions and add to result
|
|
||||||
for op in sectionDiff do
|
|
||||||
match op with
|
|
||||||
| Match (pa, pb, line) -> result.Add (Match ((pa + prevA), (pb + prevB), line))
|
|
||||||
| Delete (pa, line) -> result.Add (Delete ((pa + prevA), line))
|
|
||||||
| Insert (pb, line) -> result.Add (Insert ((pb + prevB), line))
|
|
||||||
|
|
||||||
// Add the anchor match
|
|
||||||
result.Add (Match (anchor.PosA, anchor.PosB, anchor.Line))
|
|
||||||
|
|
||||||
// Update positions
|
|
||||||
prevA <- anchorA + 1<pos>
|
|
||||||
prevB <- anchorB + 1<pos>
|
|
||||||
|
|
||||||
// Handle remaining elements after last anchor
|
|
||||||
if prevA / 1<pos> < a.Length || prevB / 1<pos> < b.Length then
|
|
||||||
let remainingA = a.[prevA / 1<pos> ..]
|
|
||||||
let remainingB = b.[prevB / 1<pos> ..]
|
|
||||||
let (Diff remainingDiff) = patienceLines remainingA remainingB
|
|
||||||
|
|
||||||
for op in remainingDiff do
|
|
||||||
match op with
|
|
||||||
| Match (pa, pb, line) -> result.Add (Match ((pa + prevA), (pb + prevB), line))
|
|
||||||
| Delete (pa, line) -> result.Add (Delete ((pa + prevA), line))
|
|
||||||
| Insert (pb, line) -> result.Add (Insert ((pb + prevB), line))
|
|
||||||
|
|
||||||
result |> Seq.toList |> Diff
|
|
||||||
|
|
||||||
/// Patience diff: a human-readable line-based diff.
|
|
||||||
let patience (a : string) (b : string) =
|
|
||||||
patienceLines (a.Split '\n') (b.Split '\n')
|
|
||||||
|
|
||||||
/// Format the diff as a human-readable string, including line numbers at the left.
|
|
||||||
let formatWithLineNumbers (Diff ops) : string =
|
|
||||||
ops
|
|
||||||
|> List.map (fun op ->
|
|
||||||
match op with
|
|
||||||
| Match (a, b, line) -> sprintf " %3d %3d %s" a b line
|
|
||||||
| Delete (a, line) -> sprintf "- %3d %s" a line
|
|
||||||
| Insert (b, line) -> sprintf "+ %3d %s" b line
|
|
||||||
)
|
|
||||||
|> String.concat "\n"
|
|
||||||
|
|
||||||
/// Format the diff as a human-readable string.
|
|
||||||
let format (Diff ops) : string =
|
|
||||||
ops
|
|
||||||
|> List.map (fun op ->
|
|
||||||
match op with
|
|
||||||
| Match (_, _, line) -> sprintf " %s" line
|
|
||||||
| Delete (_, line) -> sprintf "- %s" line
|
|
||||||
| Insert (_, line) -> sprintf "+ %s" line
|
|
||||||
)
|
|
||||||
|> String.concat "\n"
|
|
||||||
|
|
||||||
/// Compute diff statistics
|
|
||||||
type internal DiffStats =
|
|
||||||
{
|
|
||||||
Matches : int
|
|
||||||
Deletions : int
|
|
||||||
Insertions : int
|
|
||||||
TotalOperations : int
|
|
||||||
}
|
|
||||||
|
|
||||||
let internal computeStats (ops : DiffOperation list) : DiffStats =
|
|
||||||
let counts =
|
|
||||||
ops
|
|
||||||
|> List.fold
|
|
||||||
(fun (m, d, i) op ->
|
|
||||||
match op with
|
|
||||||
| Match _ -> (m + 1, d, i)
|
|
||||||
| Delete _ -> (m, d + 1, i)
|
|
||||||
| Insert _ -> (m, d, i + 1)
|
|
||||||
)
|
|
||||||
(0, 0, 0)
|
|
||||||
|
|
||||||
let matches, deletions, insertions = counts
|
|
||||||
|
|
||||||
{
|
|
||||||
Matches = matches
|
|
||||||
Deletions = deletions
|
|
||||||
Insertions = insertions
|
|
||||||
TotalOperations = matches + deletions + insertions
|
|
||||||
}
|
|
@@ -2,6 +2,7 @@ namespace WoofWare.Expect
|
|||||||
|
|
||||||
open System
|
open System
|
||||||
open System.Text.RegularExpressions
|
open System.Text.RegularExpressions
|
||||||
|
open Fantomas.FCS.Text
|
||||||
|
|
||||||
type private StringLiteralInfo =
|
type private StringLiteralInfo =
|
||||||
{
|
{
|
||||||
@@ -15,7 +16,7 @@ type private StringLiteralInfo =
|
|||||||
override this.ToString () =
|
override this.ToString () =
|
||||||
sprintf "%i:%i to %i:%i: %s" this.StartLine this.StartColumn this.EndLine this.EndColumn this.Content
|
sprintf "%i:%i to %i:%i: %s" this.StartLine this.StartColumn this.EndLine this.EndColumn this.Content
|
||||||
|
|
||||||
type private SnapshotPosition =
|
type private Position =
|
||||||
{
|
{
|
||||||
Line : int
|
Line : int
|
||||||
Column : int
|
Column : int
|
||||||
@@ -28,8 +29,8 @@ module internal SnapshotUpdate =
|
|||||||
let tripleQuote = "\"\"\""
|
let tripleQuote = "\"\"\""
|
||||||
|
|
||||||
/// Convert a string position to line/column
|
/// Convert a string position to line/column
|
||||||
let private positionToLineColumn (text : string) (offset : int) : SnapshotPosition =
|
let private positionToLineColumn (text : string) (offset : int) : Position =
|
||||||
let rec loop (line : int) (col : int) (totalOffset : int) (i : int) : SnapshotPosition =
|
let rec loop (line : int) (col : int) (totalOffset : int) (i : int) : Position =
|
||||||
if i >= text.Length || totalOffset = offset then
|
if i >= text.Length || totalOffset = offset then
|
||||||
{
|
{
|
||||||
Line = line
|
Line = line
|
||||||
@@ -207,6 +208,7 @@ module internal SnapshotUpdate =
|
|||||||
/// instead returns the new contents.
|
/// instead returns the new contents.
|
||||||
/// We always write single-quoted @-strings for simplicity.
|
/// We always write single-quoted @-strings for simplicity.
|
||||||
let private updateSnapshot (lines : string[]) (info : StringLiteralInfo) (newContent : string) : string[] =
|
let private updateSnapshot (lines : string[]) (info : StringLiteralInfo) (newContent : string) : string[] =
|
||||||
|
let f, _ = Fantomas.FCS.Parse.parseFile false (SourceText.ofString (String.concat "\n" lines)) []
|
||||||
let newString = "@\"" + newContent.Replace ("\"", "\"\"") + "\""
|
let newString = "@\"" + newContent.Replace ("\"", "\"\"") + "\""
|
||||||
|
|
||||||
if info.StartLine = info.EndLine then
|
if info.StartLine = info.EndLine then
|
||||||
|
@@ -8,48 +8,6 @@ WoofWare.Expect.CallerInfo inherit obj, implements WoofWare.Expect.CallerInfo Sy
|
|||||||
WoofWare.Expect.CallerInfo.Equals [method]: (WoofWare.Expect.CallerInfo, System.Collections.IEqualityComparer) -> bool
|
WoofWare.Expect.CallerInfo.Equals [method]: (WoofWare.Expect.CallerInfo, System.Collections.IEqualityComparer) -> bool
|
||||||
WoofWare.Expect.CompletedSnapshot inherit obj, implements WoofWare.Expect.CompletedSnapshot System.IEquatable, System.Collections.IStructuralEquatable, WoofWare.Expect.CompletedSnapshot System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
WoofWare.Expect.CompletedSnapshot inherit obj, implements WoofWare.Expect.CompletedSnapshot System.IEquatable, System.Collections.IStructuralEquatable, WoofWare.Expect.CompletedSnapshot System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
||||||
WoofWare.Expect.CompletedSnapshot.Equals [method]: (WoofWare.Expect.CompletedSnapshot, System.Collections.IEqualityComparer) -> bool
|
WoofWare.Expect.CompletedSnapshot.Equals [method]: (WoofWare.Expect.CompletedSnapshot, System.Collections.IEqualityComparer) -> bool
|
||||||
WoofWare.Expect.Diff inherit obj, implements WoofWare.Expect.Diff System.IEquatable, System.Collections.IStructuralEquatable, WoofWare.Expect.Diff System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
|
||||||
WoofWare.Expect.Diff.Equals [method]: (WoofWare.Expect.Diff, System.Collections.IEqualityComparer) -> bool
|
|
||||||
WoofWare.Expect.DiffModule inherit obj
|
|
||||||
WoofWare.Expect.DiffModule.format [static method]: WoofWare.Expect.Diff -> string
|
|
||||||
WoofWare.Expect.DiffModule.formatWithLineNumbers [static method]: WoofWare.Expect.Diff -> string
|
|
||||||
WoofWare.Expect.DiffModule.myers [static method]: string [] -> string [] -> WoofWare.Expect.Diff
|
|
||||||
WoofWare.Expect.DiffModule.patience [static method]: string -> string -> WoofWare.Expect.Diff
|
|
||||||
WoofWare.Expect.DiffModule.patienceLines [static method]: string [] -> string [] -> WoofWare.Expect.Diff
|
|
||||||
WoofWare.Expect.DiffOperation inherit obj, implements WoofWare.Expect.DiffOperation System.IEquatable, System.Collections.IStructuralEquatable, WoofWare.Expect.DiffOperation System.IComparable, System.IComparable, System.Collections.IStructuralComparable - union type with 3 cases
|
|
||||||
WoofWare.Expect.DiffOperation+Delete inherit WoofWare.Expect.DiffOperation
|
|
||||||
WoofWare.Expect.DiffOperation+Delete.get_line [method]: unit -> string
|
|
||||||
WoofWare.Expect.DiffOperation+Delete.get_posA [method]: unit -> int
|
|
||||||
WoofWare.Expect.DiffOperation+Delete.line [property]: [read-only] string
|
|
||||||
WoofWare.Expect.DiffOperation+Delete.posA [property]: [read-only] int
|
|
||||||
WoofWare.Expect.DiffOperation+Insert inherit WoofWare.Expect.DiffOperation
|
|
||||||
WoofWare.Expect.DiffOperation+Insert.get_line [method]: unit -> string
|
|
||||||
WoofWare.Expect.DiffOperation+Insert.get_posB [method]: unit -> int
|
|
||||||
WoofWare.Expect.DiffOperation+Insert.line [property]: [read-only] string
|
|
||||||
WoofWare.Expect.DiffOperation+Insert.posB [property]: [read-only] int
|
|
||||||
WoofWare.Expect.DiffOperation+Match inherit WoofWare.Expect.DiffOperation
|
|
||||||
WoofWare.Expect.DiffOperation+Match.get_line [method]: unit -> string
|
|
||||||
WoofWare.Expect.DiffOperation+Match.get_posA [method]: unit -> int
|
|
||||||
WoofWare.Expect.DiffOperation+Match.get_posB [method]: unit -> int
|
|
||||||
WoofWare.Expect.DiffOperation+Match.line [property]: [read-only] string
|
|
||||||
WoofWare.Expect.DiffOperation+Match.posA [property]: [read-only] int
|
|
||||||
WoofWare.Expect.DiffOperation+Match.posB [property]: [read-only] int
|
|
||||||
WoofWare.Expect.DiffOperation+Tags inherit obj
|
|
||||||
WoofWare.Expect.DiffOperation+Tags.Delete [static field]: int = 1
|
|
||||||
WoofWare.Expect.DiffOperation+Tags.Insert [static field]: int = 2
|
|
||||||
WoofWare.Expect.DiffOperation+Tags.Match [static field]: int = 0
|
|
||||||
WoofWare.Expect.DiffOperation.Equals [method]: (WoofWare.Expect.DiffOperation, System.Collections.IEqualityComparer) -> bool
|
|
||||||
WoofWare.Expect.DiffOperation.get_IsDelete [method]: unit -> bool
|
|
||||||
WoofWare.Expect.DiffOperation.get_IsInsert [method]: unit -> bool
|
|
||||||
WoofWare.Expect.DiffOperation.get_IsMatch [method]: unit -> bool
|
|
||||||
WoofWare.Expect.DiffOperation.get_Tag [method]: unit -> int
|
|
||||||
WoofWare.Expect.DiffOperation.IsDelete [property]: [read-only] bool
|
|
||||||
WoofWare.Expect.DiffOperation.IsInsert [property]: [read-only] bool
|
|
||||||
WoofWare.Expect.DiffOperation.IsMatch [property]: [read-only] bool
|
|
||||||
WoofWare.Expect.DiffOperation.NewDelete [static method]: (int, string) -> WoofWare.Expect.DiffOperation
|
|
||||||
WoofWare.Expect.DiffOperation.NewInsert [static method]: (int, string) -> WoofWare.Expect.DiffOperation
|
|
||||||
WoofWare.Expect.DiffOperation.NewMatch [static method]: (int, int, string) -> WoofWare.Expect.DiffOperation
|
|
||||||
WoofWare.Expect.DiffOperation.Tag [property]: [read-only] int
|
|
||||||
WoofWare.Expect.ExpectBuilder inherit obj
|
WoofWare.Expect.ExpectBuilder inherit obj
|
||||||
WoofWare.Expect.ExpectBuilder..ctor [constructor]: (string * int)
|
WoofWare.Expect.ExpectBuilder..ctor [constructor]: (string * int)
|
||||||
WoofWare.Expect.ExpectBuilder..ctor [constructor]: bool
|
WoofWare.Expect.ExpectBuilder..ctor [constructor]: bool
|
||||||
@@ -79,4 +37,3 @@ WoofWare.Expect.GlobalBuilderConfig.enterBulkUpdateMode [static method]: unit ->
|
|||||||
WoofWare.Expect.GlobalBuilderConfig.updateAllSnapshots [static method]: unit -> unit
|
WoofWare.Expect.GlobalBuilderConfig.updateAllSnapshots [static method]: unit -> unit
|
||||||
WoofWare.Expect.Mode inherit obj, implements WoofWare.Expect.Mode System.IEquatable, System.Collections.IStructuralEquatable, WoofWare.Expect.Mode System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
WoofWare.Expect.Mode inherit obj, implements WoofWare.Expect.Mode System.IEquatable, System.Collections.IStructuralEquatable, WoofWare.Expect.Mode System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
||||||
WoofWare.Expect.Mode.Equals [method]: (WoofWare.Expect.Mode, System.Collections.IEqualityComparer) -> bool
|
WoofWare.Expect.Mode.Equals [method]: (WoofWare.Expect.Mode, System.Collections.IEqualityComparer) -> bool
|
||||||
WoofWare.Expect.pos inherit obj
|
|
@@ -19,7 +19,6 @@
|
|||||||
<Compile Include="AssemblyInfo.fs" />
|
<Compile Include="AssemblyInfo.fs" />
|
||||||
<Compile Include="Text.fs" />
|
<Compile Include="Text.fs" />
|
||||||
<Compile Include="File.fs" />
|
<Compile Include="File.fs" />
|
||||||
<Compile Include="Diff.fs" />
|
|
||||||
<Compile Include="Domain.fs" />
|
<Compile Include="Domain.fs" />
|
||||||
<Compile Include="SnapshotUpdate.fs" />
|
<Compile Include="SnapshotUpdate.fs" />
|
||||||
<Compile Include="Config.fs" />
|
<Compile Include="Config.fs" />
|
||||||
@@ -37,8 +36,9 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<!-- FSharp.SystemTextJson requires at least this version -->
|
<!-- Fantomas.FCS requires at least this version -->
|
||||||
<PackageReference Update="FSharp.Core" Version="4.7.0" />
|
<PackageReference Update="FSharp.Core" Version="8.0.100" />
|
||||||
|
<PackageReference Include="Fantomas.FCS" Version="7.0.3" />
|
||||||
<PackageReference Include="FSharp.SystemTextJson" Version="1.4.36" />
|
<PackageReference Include="FSharp.SystemTextJson" Version="1.4.36" />
|
||||||
<!-- Needed for DeepEquals -->
|
<!-- Needed for DeepEquals -->
|
||||||
<PackageReference Include="System.Text.Json" Version="9.0.0" />
|
<PackageReference Include="System.Text.Json" Version="9.0.0" />
|
||||||
|
Reference in New Issue
Block a user