605 lines
19 KiB
Standard ML
605 lines
19 KiB
Standard ML
structure PersistentVectorTests =
|
|
struct
|
|
open Railroad
|
|
open Railroad.Test
|
|
|
|
fun isNotInRange (lst, pv) =
|
|
let
|
|
fun loopNotInRange lst =
|
|
case lst of
|
|
hd :: tl =>
|
|
if PersistentVector.isInRange (hd, pv) then
|
|
let
|
|
val msg =
|
|
"idx " ^ Int.toString hd
|
|
^ " is in range when it shouldn't be\n"
|
|
val () = print msg
|
|
in
|
|
Expect.isTrue false
|
|
end
|
|
else
|
|
loopNotInRange tl
|
|
| [] => Expect.isTrue true
|
|
in
|
|
loopNotInRange lst
|
|
end
|
|
|
|
fun isInRange (lst, pv) =
|
|
let
|
|
fun loopInRange lst =
|
|
case lst of
|
|
hd :: tl =>
|
|
if PersistentVector.isInRange (hd, pv) then
|
|
loopInRange tl
|
|
else
|
|
let
|
|
val msg =
|
|
"idx " ^ Int.toString hd
|
|
^ " is not in range when it should be\n"
|
|
val () = print msg
|
|
in
|
|
Expect.isTrue false
|
|
end
|
|
| [] => Expect.isTrue true
|
|
in
|
|
loopInRange lst
|
|
end
|
|
|
|
fun printVec pv =
|
|
let
|
|
val outputList = PersistentVector.toList pv
|
|
val str =
|
|
List.map
|
|
(fn {start, finish} =>
|
|
"{start = " ^ Int.toString start ^ ", finish = "
|
|
^ Int.toString finish ^ "}") outputList
|
|
val str = String.concatWith "\n " str ^ "\n"
|
|
in
|
|
print str
|
|
end
|
|
|
|
val appendTests = describe "PersistentVector.append"
|
|
[ test "contains appended values in range" (fn _ =>
|
|
let
|
|
(* arrange *)
|
|
val f = PersistentVector.append
|
|
val pv = PersistentVector.empty
|
|
|
|
(* act *)
|
|
val pv = f (1, 3, pv)
|
|
val pv = f (5, 7, pv)
|
|
val pv = f (9, 13, pv)
|
|
val pv = f (19, 27, pv)
|
|
val pv = f (33, 33, pv)
|
|
|
|
(* assert *)
|
|
(* we split the list into several smaller lists
|
|
* and then concatenate at the end
|
|
* so that the formatter does not cause
|
|
* each list element to take its own line *)
|
|
val indicesInRange1 = [1, 2, 3, 5, 6, 7, 9]
|
|
val indicesInRange2 = [10, 11, 12, 13, 19, 20]
|
|
val indicesInRange3 = [21, 22, 23, 24, 25, 26, 27, 33]
|
|
|
|
val indicesInRange =
|
|
indicesInRange1 @ indicesInRange2 @ indicesInRange3
|
|
in
|
|
isInRange (indicesInRange, pv)
|
|
end)
|
|
, test "does not contain values in range that were not appended" (fn _ =>
|
|
let
|
|
(* arrange *)
|
|
val f = PersistentVector.append
|
|
val pv = PersistentVector.empty
|
|
(* act *)
|
|
val pv = f (1, 3, pv)
|
|
val pv = f (5, 7, pv)
|
|
val pv = f (9, 13, pv)
|
|
val pv = f (19, 27, pv)
|
|
val pv = f (33, 33, pv)
|
|
|
|
(* assert *)
|
|
val indicesNotInRange =
|
|
[0, 4, 8, 14, 15, 16, 17, 18, 28, 29, 30, 31, 32, 34, 35]
|
|
in
|
|
isNotInRange (indicesNotInRange, pv)
|
|
end)
|
|
]
|
|
|
|
val toListTests = describe "PersistentVector.toList"
|
|
[ test "returns input list when input list has 5 elements" (fn _ =>
|
|
let
|
|
(* arrange *)
|
|
val inputList =
|
|
[ {start = 1, finish = 3}
|
|
, {start = 5, finish = 7}
|
|
, {start = 9, finish = 13}
|
|
, {start = 19, finish = 27}
|
|
, {start = 33, finish = 33}
|
|
]
|
|
val pv = PersistentVector.fromList inputList
|
|
|
|
(* act *)
|
|
val outputList = PersistentVector.toList pv
|
|
|
|
(* assert *)
|
|
in
|
|
Expect.isTrue (inputList = outputList)
|
|
end)
|
|
, test "returns input list when input list has more than 32 elements"
|
|
(fn _ =>
|
|
let
|
|
(* arrange *)
|
|
val inputList =
|
|
[ {start = 1, finish = 1}
|
|
, {start = 2, finish = 2}
|
|
, {start = 3, finish = 3}
|
|
, {start = 4, finish = 4}
|
|
, {start = 5, finish = 5}
|
|
, {start = 6, finish = 6}
|
|
, {start = 7, finish = 7}
|
|
, {start = 8, finish = 8}
|
|
, {start = 9, finish = 9}
|
|
, {start = 10, finish = 10}
|
|
, {start = 11, finish = 11}
|
|
, {start = 12, finish = 12}
|
|
, {start = 13, finish = 13}
|
|
, {start = 14, finish = 14}
|
|
, {start = 15, finish = 15}
|
|
, {start = 16, finish = 16}
|
|
, {start = 17, finish = 17}
|
|
, {start = 18, finish = 18}
|
|
, {start = 19, finish = 19}
|
|
, {start = 20, finish = 20}
|
|
, {start = 21, finish = 21}
|
|
, {start = 22, finish = 22}
|
|
, {start = 23, finish = 23}
|
|
, {start = 24, finish = 24}
|
|
, {start = 25, finish = 25}
|
|
, {start = 26, finish = 26}
|
|
, {start = 27, finish = 27}
|
|
, {start = 28, finish = 28}
|
|
, {start = 29, finish = 29}
|
|
, {start = 30, finish = 30}
|
|
, {start = 31, finish = 31}
|
|
, {start = 32, finish = 32}
|
|
, {start = 33, finish = 33}
|
|
, {start = 34, finish = 34}
|
|
, {start = 35, finish = 35}
|
|
]
|
|
val pv = PersistentVector.fromList inputList
|
|
|
|
(* act *)
|
|
val outputList = PersistentVector.toList pv
|
|
|
|
(* assert *)
|
|
in
|
|
Expect.isTrue (inputList = outputList)
|
|
end)
|
|
]
|
|
|
|
val splitLeftTests = describe "PersistentVector.splitLeft"
|
|
[ test
|
|
"returns same vector when split idx is greater than any idx in vector"
|
|
(fn _ =>
|
|
let
|
|
(* arrange *)
|
|
val inputList =
|
|
[ {start = 1, finish = 1}
|
|
, {start = 2, finish = 2}
|
|
, {start = 3, finish = 3}
|
|
, {start = 4, finish = 4}
|
|
, {start = 5, finish = 5}
|
|
, {start = 6, finish = 6}
|
|
, {start = 7, finish = 7}
|
|
, {start = 8, finish = 8}
|
|
]
|
|
val pv = PersistentVector.fromList inputList
|
|
|
|
(* act *)
|
|
val pv = PersistentVector.splitLeft (9, pv)
|
|
|
|
(* assert *)
|
|
val outputList = PersistentVector.toList pv
|
|
in
|
|
Expect.isTrue (inputList = outputList)
|
|
end)
|
|
, test "removes last element when split idx is = to last element" (fn _ =>
|
|
let
|
|
(* arrange *)
|
|
val inputList =
|
|
[ {start = 1, finish = 1}
|
|
, {start = 2, finish = 2}
|
|
, {start = 3, finish = 3}
|
|
, {start = 4, finish = 4}
|
|
, {start = 5, finish = 5}
|
|
, {start = 6, finish = 6}
|
|
, {start = 7, finish = 7}
|
|
, {start = 8, finish = 8}
|
|
]
|
|
val pv = PersistentVector.fromList inputList
|
|
|
|
(* act *)
|
|
val pv = PersistentVector.splitLeft (8, pv)
|
|
|
|
(* assert *)
|
|
val outputList = PersistentVector.toList pv
|
|
val expectedOutput =
|
|
[ {start = 1, finish = 1}
|
|
, {start = 2, finish = 2}
|
|
, {start = 3, finish = 3}
|
|
, {start = 4, finish = 4}
|
|
, {start = 5, finish = 5}
|
|
, {start = 6, finish = 6}
|
|
, {start = 7, finish = 7}
|
|
]
|
|
in
|
|
Expect.isTrue (outputList = expectedOutput)
|
|
end)
|
|
, test "removes all elements when split idx = first element" (fn _ =>
|
|
let
|
|
(* arrange *)
|
|
val inputList =
|
|
[ {start = 1, finish = 1}
|
|
, {start = 2, finish = 2}
|
|
, {start = 3, finish = 3}
|
|
, {start = 4, finish = 4}
|
|
, {start = 5, finish = 5}
|
|
, {start = 6, finish = 6}
|
|
, {start = 7, finish = 7}
|
|
, {start = 8, finish = 8}
|
|
]
|
|
val pv = PersistentVector.fromList inputList
|
|
|
|
(* act *)
|
|
val pv = PersistentVector.splitLeft (1, pv)
|
|
|
|
(* assert *)
|
|
val outputList = PersistentVector.toList pv
|
|
val expectedOutput = []
|
|
in
|
|
Expect.isTrue (outputList = expectedOutput)
|
|
end)
|
|
, test
|
|
"removes element whose start and finish is in range \
|
|
\of the split idx, and removes all elements after it too"
|
|
(fn _ =>
|
|
let
|
|
(* arrange *)
|
|
val inputList =
|
|
[ {start = 1, finish = 1}
|
|
, {start = 2, finish = 2}
|
|
, {start = 3, finish = 3}
|
|
, {start = 4, finish = 4}
|
|
, {start = 5, finish = 155}
|
|
, {start = 200, finish = 200}
|
|
, {start = 210, finish = 210}
|
|
, {start = 220, finish = 220}
|
|
, {start = 230, finish = 230}
|
|
, {start = 240, finish = 240}
|
|
, {start = 250, finish = 250}
|
|
]
|
|
val pv = PersistentVector.fromList inputList
|
|
|
|
(* act *)
|
|
val pv = PersistentVector.splitLeft (7, pv)
|
|
|
|
(* assert *)
|
|
val outputList = PersistentVector.toList pv
|
|
val expectedOutput =
|
|
[ {start = 1, finish = 1}
|
|
, {start = 2, finish = 2}
|
|
, {start = 3, finish = 3}
|
|
, {start = 4, finish = 4}
|
|
]
|
|
in
|
|
Expect.isTrue (outputList = expectedOutput)
|
|
end)
|
|
]
|
|
|
|
val deleteTests = describe "PersistentVector.delete"
|
|
[ test "returns empty vector when deletion range includes every element"
|
|
(fn _ =>
|
|
let
|
|
(* arrange *)
|
|
val inputList =
|
|
[ {start = 1, finish = 1}
|
|
, {start = 2, finish = 2}
|
|
, {start = 3, finish = 3}
|
|
, {start = 4, finish = 4}
|
|
, {start = 5, finish = 5}
|
|
, {start = 6, finish = 6}
|
|
, {start = 7, finish = 7}
|
|
, {start = 8, finish = 8}
|
|
]
|
|
val pv = PersistentVector.fromList inputList
|
|
|
|
(* act *)
|
|
val pv = PersistentVector.delete (0, 11, pv)
|
|
|
|
(* assert *)
|
|
val outputList = PersistentVector.toList pv
|
|
val expectedOutput = []
|
|
in
|
|
Expect.isTrue (outputList = expectedOutput)
|
|
end)
|
|
, test
|
|
"returns the left side of the vector \
|
|
\when 'length' is greater than any element in the vector"
|
|
(fn _ =>
|
|
let
|
|
(* arrange *)
|
|
val inputList =
|
|
[ {start = 1, finish = 1}
|
|
, {start = 2, finish = 2}
|
|
, {start = 3, finish = 3}
|
|
, {start = 4, finish = 4}
|
|
, {start = 5, finish = 5}
|
|
, {start = 6, finish = 6}
|
|
, {start = 7, finish = 7}
|
|
, {start = 8, finish = 8}
|
|
]
|
|
val pv = PersistentVector.fromList inputList
|
|
|
|
(* act *)
|
|
val pv = PersistentVector.delete (5, 4, pv)
|
|
|
|
(* assert *)
|
|
val outputList = PersistentVector.toList pv
|
|
val expectedOutput =
|
|
[ {start = 1, finish = 1}
|
|
, {start = 2, finish = 2}
|
|
, {start = 3, finish = 3}
|
|
, {start = 4, finish = 4}
|
|
]
|
|
in
|
|
Expect.isTrue (outputList = expectedOutput)
|
|
end)
|
|
, test
|
|
"decrements subsequent elements correctly \
|
|
\when deletion range is before first element to middle element"
|
|
(fn _ =>
|
|
let
|
|
(* arrange *)
|
|
val inputList =
|
|
[ {start = 1, finish = 1}
|
|
, {start = 2, finish = 2}
|
|
, {start = 3, finish = 3}
|
|
, {start = 4, finish = 4}
|
|
, {start = 50, finish = 50}
|
|
, {start = 60, finish = 60}
|
|
, {start = 70, finish = 70}
|
|
, {start = 80, finish = 80}
|
|
]
|
|
val pv = PersistentVector.fromList inputList
|
|
|
|
(* act *)
|
|
val pv = PersistentVector.delete (0, 3, pv)
|
|
|
|
(* assert *)
|
|
val outputList = PersistentVector.toList pv
|
|
val expectedOutput =
|
|
[ {start = 1, finish = 1}
|
|
, {start = 47, finish = 47}
|
|
, {start = 57, finish = 57}
|
|
, {start = 67, finish = 67}
|
|
, {start = 77, finish = 77}
|
|
]
|
|
in
|
|
Expect.isTrue (outputList = expectedOutput)
|
|
end)
|
|
, test
|
|
"decrements subsequent elements correctly \
|
|
\when deletion range is between two elements, \
|
|
\but deletes no elements"
|
|
(fn _ =>
|
|
let
|
|
(* arrange *)
|
|
val inputList =
|
|
[ {start = 1, finish = 3}
|
|
, {start = 15, finish = 19}
|
|
, {start = 35, finish = 39}
|
|
]
|
|
val pv = PersistentVector.fromList inputList
|
|
|
|
(* act *)
|
|
val pv = PersistentVector.delete (21, 3, pv)
|
|
|
|
(* assert *)
|
|
val outputList = PersistentVector.toList pv
|
|
val expectedOutput =
|
|
[ {start = 1, finish = 3}
|
|
, {start = 15, finish = 19}
|
|
, {start = 32, finish = 36}
|
|
]
|
|
in
|
|
Expect.isTrue (outputList = expectedOutput)
|
|
end)
|
|
, test "deletes element when deletion range is inside that element" (fn _ =>
|
|
let
|
|
(* arrange *)
|
|
val inputList =
|
|
[ {start = 1, finish = 3}
|
|
, {start = 15, finish = 19}
|
|
, {start = 35, finish = 39}
|
|
]
|
|
val pv = PersistentVector.fromList inputList
|
|
|
|
(* act *)
|
|
val pv = PersistentVector.delete (17, 1, pv)
|
|
|
|
(* assert *)
|
|
val outputList = PersistentVector.toList pv
|
|
val expectedOutput =
|
|
[{start = 1, finish = 3}, {start = 34, finish = 38}]
|
|
in
|
|
Expect.isTrue (outputList = expectedOutput)
|
|
end)
|
|
, test
|
|
"returns preceding elements when \
|
|
\deletion range starts in middle and deletes to end of vector"
|
|
(fn _ =>
|
|
let
|
|
(* arrange *)
|
|
val inputList =
|
|
[ {start = 1, finish = 1}
|
|
, {start = 2, finish = 2}
|
|
, {start = 3, finish = 3}
|
|
, {start = 4, finish = 4}
|
|
, {start = 5, finish = 5}
|
|
, {start = 6, finish = 6}
|
|
, {start = 7, finish = 7}
|
|
, {start = 8, finish = 8}
|
|
]
|
|
val pv = PersistentVector.fromList inputList
|
|
|
|
(* act *)
|
|
val pv = PersistentVector.delete (5, 9, pv)
|
|
|
|
(* assert *)
|
|
val outputList = PersistentVector.toList pv
|
|
val expectedOutput =
|
|
[ {start = 1, finish = 1}
|
|
, {start = 2, finish = 2}
|
|
, {start = 3, finish = 3}
|
|
, {start = 4, finish = 4}
|
|
]
|
|
in
|
|
Expect.isTrue (outputList = expectedOutput)
|
|
end)
|
|
, test
|
|
"deletes middle elements and decrements subsequent elements \
|
|
\when deletion range starts after first element \
|
|
\and ends before last element"
|
|
(fn _ =>
|
|
let
|
|
(* arrange *)
|
|
val inputList =
|
|
[ {start = 1, finish = 1}
|
|
, {start = 2, finish = 2}
|
|
, {start = 3, finish = 3}
|
|
, {start = 4, finish = 4}
|
|
, {start = 5, finish = 5}
|
|
, {start = 60, finish = 60}
|
|
, {start = 70, finish = 70}
|
|
, {start = 80, finish = 80}
|
|
]
|
|
val pv = PersistentVector.fromList inputList
|
|
|
|
(* act *)
|
|
val pv = PersistentVector.delete (3, 3, pv)
|
|
|
|
(* assert *)
|
|
val outputList = PersistentVector.toList pv
|
|
val expectedOutput =
|
|
[ {start = 1, finish = 1}
|
|
, {start = 2, finish = 2}
|
|
, {start = 57, finish = 57}
|
|
, {start = 67, finish = 67}
|
|
, {start = 77, finish = 77}
|
|
]
|
|
in
|
|
Expect.isTrue (outputList = expectedOutput)
|
|
end)
|
|
, test
|
|
"maintains balance with all leaves at same depth \
|
|
\when deleting a large portion of nodes in the middle"
|
|
(fn _ =>
|
|
let
|
|
(* arrange *)
|
|
val inputList = List.tabulate (228, fn i =>
|
|
{start = i, finish = i})
|
|
val pv = PersistentVector.fromList inputList
|
|
|
|
(* act *)
|
|
val pv = PersistentVector.delete (19, 15, pv)
|
|
|
|
(* assert *)
|
|
val isBalanced = PersistentVector.allLeavesAtSameDepth pv
|
|
in
|
|
Expect.isTrue isBalanced
|
|
end)
|
|
]
|
|
|
|
val extendExistingMatchTests = describe "PersistentVector.extendExistingMatch"
|
|
[ test
|
|
"leaves subsequent matches untouched \
|
|
\if their 'finish' is greater than the extended finish"
|
|
(fn _ =>
|
|
let
|
|
(* arrange *)
|
|
val inputList =
|
|
[ {start = 1, finish = 1}
|
|
, {start = 2, finish = 2}
|
|
, {start = 3, finish = 3}
|
|
, {start = 4, finish = 4}
|
|
, {start = 5, finish = 5}
|
|
, {start = 60, finish = 60}
|
|
, {start = 70, finish = 70}
|
|
, {start = 80, finish = 80}
|
|
]
|
|
val pv = PersistentVector.fromList inputList
|
|
|
|
(* act *)
|
|
val pv = PersistentVector.extendExistingMatch (5, 50, pv)
|
|
|
|
(* assert *)
|
|
val outputList = PersistentVector.toList pv
|
|
val expectedOutput =
|
|
[ {start = 1, finish = 1}
|
|
, {start = 2, finish = 2}
|
|
, {start = 3, finish = 3}
|
|
, {start = 4, finish = 4}
|
|
, {start = 5, finish = 50}
|
|
, {start = 60, finish = 60}
|
|
, {start = 70, finish = 70}
|
|
, {start = 80, finish = 80}
|
|
]
|
|
in
|
|
Expect.isTrue (outputList = expectedOutput)
|
|
end)
|
|
, test
|
|
"removes subsequent matches whose 'finish' is less than \
|
|
\the newly extended element's 'finish'"
|
|
(fn _ =>
|
|
let
|
|
(* arrange *)
|
|
val inputList =
|
|
[ {start = 1, finish = 1}
|
|
, {start = 2, finish = 2}
|
|
, {start = 3, finish = 3}
|
|
, {start = 4, finish = 4}
|
|
, {start = 5, finish = 5}
|
|
, {start = 60, finish = 60}
|
|
, {start = 70, finish = 70}
|
|
, {start = 80, finish = 80}
|
|
]
|
|
val pv = PersistentVector.fromList inputList
|
|
|
|
(* act *)
|
|
val pv = PersistentVector.extendExistingMatch (5, 75, pv)
|
|
|
|
(* assert *)
|
|
val outputList = PersistentVector.toList pv
|
|
val expectedOutput =
|
|
[ {start = 1, finish = 1}
|
|
, {start = 2, finish = 2}
|
|
, {start = 3, finish = 3}
|
|
, {start = 4, finish = 4}
|
|
, {start = 5, finish = 75}
|
|
, {start = 80, finish = 80}
|
|
]
|
|
in
|
|
Expect.isTrue (outputList = expectedOutput)
|
|
end)
|
|
]
|
|
|
|
val tests =
|
|
[ appendTests
|
|
, toListTests
|
|
, splitLeftTests
|
|
, deleteTests
|
|
, extendExistingMatchTests
|
|
]
|
|
end
|