Add 'shf/' from commit 'b6c5a95b664aeb861d7b33ffc9eefe447ba99dd7'
git-subtree-dir: shf git-subtree-mainline:401408448fgit-subtree-split:b6c5a95b66
This commit is contained in:
7
shf/test/README.md
Normal file
7
shf/test/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# shf-tests
|
||||
|
||||
Unit tests for shf.
|
||||
|
||||
The tests require (Railroad)[https://github.com/PerplexSystems/Railroad] testing framework.
|
||||
|
||||
|
||||
1
shf/test/Railroad
Submodule
1
shf/test/Railroad
Submodule
Submodule shf/test/Railroad added at b5aa94a880
7925
shf/test/normal-delete-tests.sml
Normal file
7925
shf/test/normal-delete-tests.sml
Normal file
File diff suppressed because it is too large
Load Diff
2143
shf/test/normal-move-tests.sml
Normal file
2143
shf/test/normal-move-tests.sml
Normal file
File diff suppressed because it is too large
Load Diff
836
shf/test/normal-yank-tests.sml
Normal file
836
shf/test/normal-yank-tests.sml
Normal file
@@ -0,0 +1,836 @@
|
||||
structure NormalYankTests =
|
||||
struct
|
||||
open Railroad
|
||||
open Railroad.Test
|
||||
open InputMsg
|
||||
|
||||
val yhYank = describe "yank motion 'yh'"
|
||||
[ test "yanks empty string when cursor is at index 0" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "hello world\n"
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, 0)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yh")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = ""
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test "yanks empty string when character before cursor is a newline"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "hello\nworld\n"
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, 6)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yh")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = ""
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test "yanks one char to the left when on a non-newline" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "hello world\n"
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, 5)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yh")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = "o"
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test "yanks 3 chars when count is 3" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalIdx = 5
|
||||
val originalString = "hello world\n"
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "3yh")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = "llo"
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test
|
||||
"yanks from cursor position to start column when \
|
||||
\count is greater than current column"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalIdx = 5
|
||||
val originalString = "hello world\n"
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "9yh")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = "hello"
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
]
|
||||
|
||||
val ylYank = describe "yank motion 'yl'"
|
||||
[ test "yanks last char in line when next char is newline" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "hello\nworld\n"
|
||||
val originalIdx = 4
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yl")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = "o"
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test "yanks char that cursor is currently on when not on newline" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalIdx = 0
|
||||
val originalString = "hello world\n"
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yl")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = "h"
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test "yanks newline character when cursor is on a newline" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalIdx = 5
|
||||
val originalString = "hello\n\nworld\n"
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yl")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = "\n"
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test
|
||||
"does not yank past newline when specifying a range \
|
||||
\greater than number of columns"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalIdx = 2
|
||||
val originalString = "hello\nworld\n"
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "33yl")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = "llo"
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test
|
||||
"yanks last line, excluding newline, \
|
||||
\when cursor is on first character of last line \
|
||||
\and last line ends with a newline"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalIdx = 6
|
||||
val originalString = "hello\nworld\n"
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "33yl")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = "world"
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test
|
||||
"yanks last line, excluding newline, \
|
||||
\when cursor is on first character of last line \
|
||||
\and last line does not end with a newline"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalIdx = 6
|
||||
val originalString = "hello\nworld"
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "33yl")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = "world"
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
]
|
||||
|
||||
val ykYank = describe "yank motion 'yk'"
|
||||
[ test "does not yank when cursor is on first line" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "hello\nworld\n"
|
||||
val originalIdx = 0
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yk")
|
||||
in
|
||||
(* assert *)
|
||||
TestUtils.expectNoYank app
|
||||
end)
|
||||
, test
|
||||
"yanks first two lines \
|
||||
\when there are two lines and cursor is on second line"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "hello\nworld\n"
|
||||
val originalIdx = 6
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yk")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = "hello\nworld\n"
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test
|
||||
"yanks last two lines when there are three lines in the buffer \
|
||||
\and cursor is on third line"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "hello\nworld\nagain\n"
|
||||
val originalIdx = 15
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yk")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = "world\nagain\n"
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test
|
||||
"yanks whole buffer when on last line \
|
||||
\and count is greater than number of lines"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "hello\nworld\nagain\n"
|
||||
val originalIdx = 15
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "33yk")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = originalString
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test
|
||||
"yanks newline and preceding line when cursor is second line \
|
||||
\and second line contains only a newline"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "hello\n\nagain\n"
|
||||
val originalIdx = 6
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yk")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = "hello\n\n"
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test
|
||||
"yanks just newline and line above when cursor is on third line \
|
||||
\and third line contains only a newline"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString =
|
||||
"hello\n\
|
||||
\world\n\
|
||||
\\n\
|
||||
\trello\n\
|
||||
\brillo\n"
|
||||
val originalIdx = 12
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yk")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = "world\n\n"
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test
|
||||
"yanks second and third lines when cursor is on \
|
||||
\last non-newline character of third line"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "hello\n\nagain\n"
|
||||
val originalString =
|
||||
"hello\n\
|
||||
\world\n\
|
||||
\trello\n\
|
||||
\brillo\n"
|
||||
val originalIdx = 17
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yk")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = "world\ntrello\n"
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test
|
||||
"yanks last two lines when cursor is on last line \
|
||||
\and last line only has a newline"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString =
|
||||
"hello\n\
|
||||
\world\n\
|
||||
\\n"
|
||||
val originalIdx = String.size originalString - 1
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yk")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = "world\n\n"
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
]
|
||||
|
||||
val yjYank = describe "yank motion 'yj'"
|
||||
[ test "does not yank any text when cursor is on last line" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "hello\nworld\n"
|
||||
val originalIdx = String.size originalString - 3
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yj")
|
||||
in
|
||||
(* assert *)
|
||||
TestUtils.expectNoYank app
|
||||
end)
|
||||
, test "does not yank when there is only one line" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "hello\n"
|
||||
val originalIdx = 0
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yj")
|
||||
in
|
||||
(* assert *)
|
||||
TestUtils.expectNoYank app
|
||||
end)
|
||||
, test
|
||||
"yanks first two lines when cursor is on first line \
|
||||
\and there are at least two lines"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "hello\nworld\n"
|
||||
val originalIdx = 0
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yj")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = originalString
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test
|
||||
"yanks first two lines when there are three lines \
|
||||
\and cursor is on first line"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalIdx = 0
|
||||
val originalString = "hello\nworld\nbye world\n"
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yj")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = "hello\nworld\n"
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test
|
||||
"yanks last two lines when there are three lines \
|
||||
\and cursor is on second line"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalIdx = 6
|
||||
val originalString = "hello\nworld\nbye world\n"
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yj")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = "world\nbye world\n"
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test
|
||||
"yanks entire file when cursor is on first line \
|
||||
\and a count is given which is larger \
|
||||
\than the total number of lines in the file"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "hello\nworld\n"
|
||||
val originalIdx = 0
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "33yj")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = originalString
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test
|
||||
"yanks entire file when a count greater than the total number of lines \
|
||||
\is given, while the file does not end with a newline"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "hello\nworld"
|
||||
val originalIdx = 0
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "33yj")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = originalString
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test "yanks two lines when cursor is on a newline" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "\nhello\nworld\ntrello\nbrillo\n"
|
||||
val originalIdx = 0
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yj")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = "\nhello\n"
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
]
|
||||
|
||||
val yyYank = describe "yank motion 'yy'"
|
||||
[ test
|
||||
"yanks last line when there is more than one line \
|
||||
\and cursor is on last line"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "hello\nworld\n"
|
||||
val originalIdx = String.size originalString - 3
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yy")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = "world\n"
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test "yanks whole buffer when buffer consists of one line" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "hello\n"
|
||||
val originalIdx = 0
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yy")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = originalString
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test
|
||||
"yanks first line when cursor is on first line \
|
||||
\and there are only two lines"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "hello\nworld\n"
|
||||
val originalIdx = 0
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yy")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = "hello\n"
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test
|
||||
"yanks whole file when cursor is on first line \
|
||||
\and a count is given which is greater than \
|
||||
\the number of total lines"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "hello\nworld\n"
|
||||
val originalIdx = 0
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "33yy")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = originalString
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test
|
||||
"yanks whole file when cursor is on first line, \
|
||||
\count given is greater than number of lines, \
|
||||
\and the file does not end with a newline"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "hello\nworld"
|
||||
val originalIdx = 0
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "33yy")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = originalString
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test
|
||||
"yanks just newline when cursor is on a line \
|
||||
\that contains only a single newline"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "\nhello\nworld\n"
|
||||
val originalIdx = 0
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yy")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = "\n"
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
]
|
||||
|
||||
fun yankSeconWordAlpha (pos, expectedString) =
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "hello world again\n"
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, pos)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yw")
|
||||
in
|
||||
(* assert *)
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end
|
||||
|
||||
val ywYank = describe "yank motion 'yw'"
|
||||
[ test "yanks last character when cursor is on last character of line"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "hello world\n"
|
||||
val originalIdx = String.size originalString - 2
|
||||
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, originalIdx)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yw")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = "d"
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test
|
||||
"yanks from second word up to (and excluding) third word \
|
||||
\when cursor is on first character of second word"
|
||||
(fn _ => yankSeconWordAlpha (6, "world "))
|
||||
, test
|
||||
"yanks from second character of second word \
|
||||
\up to (and excluding) third word \
|
||||
\when cursor is on second character of second word"
|
||||
(fn _ => yankSeconWordAlpha (7, "orld "))
|
||||
, test
|
||||
"yanks from third character of second word \
|
||||
\up to (and excluding) third word \
|
||||
\when cursor is on third character of second word"
|
||||
(fn _ => yankSeconWordAlpha (8, "rld "))
|
||||
, test
|
||||
"yanks from fourth character of second word \
|
||||
\up to (and excluding) third word \
|
||||
\when cursor is on fourth character of second word"
|
||||
(fn _ => yankSeconWordAlpha (9, "ld "))
|
||||
, test
|
||||
"yanks from fifth character of second word \
|
||||
\up to (and excluding) third word \
|
||||
\when cursor is on fifth character of second word"
|
||||
(fn _ => yankSeconWordAlpha (10, "d "))
|
||||
, test "yanks space when cursor is on space preceding an alpha char"
|
||||
(fn _ => yankSeconWordAlpha (11, " "))
|
||||
, test "does not yank newline when cursor is on last word of line" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "hello\nworld\nagain\n"
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, 0)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yw")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = "hello"
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test
|
||||
"yanks until first punctuation char when on an alpha char \
|
||||
\and there is no space between alpha and punctuation"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "hello!world!again\n"
|
||||
val app = TestUtils.init originalString
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yw")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = "hello"
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test
|
||||
"yanks until first alpha char when on punctuation \
|
||||
\and there is no space between punctuation and alpha"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "!#%&QWERTY#!\n"
|
||||
val app = TestUtils.init originalString
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yw")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = "!#%&"
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test
|
||||
"yanks until first alpha char (exluding) \
|
||||
\when cursor is on space and next char is alpha"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "h ello\n"
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, 1)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yw")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = " "
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test
|
||||
"yanks until first alpha char \
|
||||
\when cursor is on space, many spaces are ahead, \
|
||||
\and first char after spaces is alpha"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "h ello\n"
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, 3)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yw")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = " "
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test
|
||||
"yanks until first punctuation char \
|
||||
\when cursor is on space and next non-space char is punctuation"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val originalString = "! @#$%\n"
|
||||
val app = TestUtils.init originalString
|
||||
val app = AppWith.idx (app, 2)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yw")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = " "
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
, test
|
||||
"yanks last char when on last word \
|
||||
\and there is no newline after current word"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val app = TestUtils.init "hello world"
|
||||
val app = AppWith.idx (app, 6)
|
||||
|
||||
(* act *)
|
||||
val app = TestUtils.updateMany (app, "yw")
|
||||
|
||||
(* assert *)
|
||||
val expectedString = "world"
|
||||
in
|
||||
TestUtils.expectYank (app, expectedString)
|
||||
end)
|
||||
]
|
||||
|
||||
val tests = [yhYank, ylYank, ykYank, yjYank, yyYank, ywYank]
|
||||
end
|
||||
630
shf/test/persistent-vector-tests.sml
Normal file
630
shf/test/persistent-vector-tests.sml
Normal file
@@ -0,0 +1,630 @@
|
||||
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)
|
||||
, test
|
||||
"removes all subsequent elements when new 'finish' is greater \
|
||||
\than any finish in the vector"
|
||||
(fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val inputList = List.tabulate (500, fn i =>
|
||||
{start = i, finish = i})
|
||||
val pv = PersistentVector.fromList inputList
|
||||
|
||||
(* act *)
|
||||
val pv = PersistentVector.extendExistingMatch (5, 999, pv)
|
||||
|
||||
(* assert *)
|
||||
val outputList = PersistentVector.toList pv
|
||||
val expectedOutput =
|
||||
[ {start = 0, finish = 0}
|
||||
, {start = 1, finish = 1}
|
||||
, {start = 2, finish = 2}
|
||||
, {start = 3, finish = 3}
|
||||
, {start = 4, finish = 4}
|
||||
, {start = 5, finish = 999}
|
||||
]
|
||||
in
|
||||
Expect.isTrue (outputList = expectedOutput)
|
||||
end)
|
||||
]
|
||||
|
||||
val tests =
|
||||
[ appendTests
|
||||
, toListTests
|
||||
, splitLeftTests
|
||||
, deleteTests
|
||||
, extendExistingMatchTests
|
||||
]
|
||||
end
|
||||
618
shf/test/regex-tests.sml
Normal file
618
shf/test/regex-tests.sml
Normal file
@@ -0,0 +1,618 @@
|
||||
structure RegexTests =
|
||||
struct
|
||||
open Railroad
|
||||
open Railroad.Test
|
||||
|
||||
structure CiDfa = CaseInsensitiveDfa
|
||||
structure CsDfa = CaseSensitiveDfa
|
||||
|
||||
val caseInsensitiveTests = describe "case insensitive regex"
|
||||
[ test "recognises word 'hello' in string 'Hello world'" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val regexString = "hello"
|
||||
val dfa = CiDfa.fromString regexString
|
||||
val inputString = "Hello world"
|
||||
|
||||
(* act *)
|
||||
val matches = CiDfa.matchString (dfa, inputString)
|
||||
|
||||
(* assert *)
|
||||
val expectedMatches = [(0, 4)]
|
||||
in
|
||||
Expect.isTrue (matches = expectedMatches)
|
||||
end)
|
||||
, test "recognises word 'world' in string 'HELLO WORLD'" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val regexString = "world"
|
||||
val dfa = CiDfa.fromString regexString
|
||||
val inputString = "HELLO WORLD"
|
||||
|
||||
(* act *)
|
||||
val matches = CiDfa.matchString (dfa, inputString)
|
||||
|
||||
(* assert *)
|
||||
val expectedMatches = [(6, 10)]
|
||||
in
|
||||
Expect.isTrue (matches = expectedMatches)
|
||||
end)
|
||||
]
|
||||
|
||||
val caseSensitiveTests = describe "case sensitive regex"
|
||||
[ test "does not recognise word 'hello' in string 'Hello world'" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val regexString = "hello"
|
||||
val dfa = CsDfa.fromString regexString
|
||||
val inputString = "Hello world"
|
||||
|
||||
(* act *)
|
||||
val matches = CsDfa.matchString (dfa, inputString)
|
||||
|
||||
(* assert *)
|
||||
val expectedMatches = []
|
||||
in
|
||||
Expect.isTrue (matches = expectedMatches)
|
||||
end)
|
||||
, test "recognises word 'Hello' in string 'Hello world'" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val regexString = "Hello"
|
||||
val dfa = CsDfa.fromString regexString
|
||||
val inputString = "Hello world"
|
||||
|
||||
(* act *)
|
||||
val matches = CsDfa.matchString (dfa, inputString)
|
||||
|
||||
(* assert *)
|
||||
val expectedMatches = [(0, 4)]
|
||||
in
|
||||
Expect.isTrue (matches = expectedMatches)
|
||||
end)
|
||||
, test "does not recognise word 'world' in string 'HELLO WORLD'" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val regexString = "world"
|
||||
val dfa = CsDfa.fromString regexString
|
||||
val inputString = "HELLO WORLD"
|
||||
|
||||
(* act *)
|
||||
val matches = CsDfa.matchString (dfa, inputString)
|
||||
|
||||
(* assert *)
|
||||
val expectedMatches = []
|
||||
in
|
||||
Expect.isTrue (matches = expectedMatches)
|
||||
end)
|
||||
, test "recognises word 'WORLD' in string 'HELLO WORLD'" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val regexString = "WORLD"
|
||||
val dfa = CsDfa.fromString regexString
|
||||
val inputString = "HELLO WORLD"
|
||||
|
||||
(* act *)
|
||||
val matches = CsDfa.matchString (dfa, inputString)
|
||||
|
||||
(* assert *)
|
||||
val expectedMatches = [(6, 10)]
|
||||
in
|
||||
Expect.isTrue (matches = expectedMatches)
|
||||
end)
|
||||
]
|
||||
|
||||
val endMarkerTests = describe "regex endMarker"
|
||||
[ test "returns an empty DFA when regexString contains endMarker" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
(* the end marker is #"\^@" *)
|
||||
val regexString = "hello \^@ world"
|
||||
|
||||
(* act *)
|
||||
val dfa = CsDfa.fromString regexString
|
||||
|
||||
(* assert *)
|
||||
val actualLength = Vector.length dfa
|
||||
val expectedLength = 0
|
||||
in
|
||||
Expect.isTrue (actualLength = expectedLength)
|
||||
end)
|
||||
, test "matches a string when regex has question mark at the end" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val sentence = "favo"
|
||||
val regexString = "favou?"
|
||||
val dfa = CsDfa.fromString regexString
|
||||
|
||||
(* act *)
|
||||
val matches = CsDfa.matchString (dfa, sentence)
|
||||
|
||||
(* assert *)
|
||||
val expectedMatches = [(0, 3)]
|
||||
in
|
||||
Expect.isTrue (matches = expectedMatches)
|
||||
end)
|
||||
]
|
||||
|
||||
fun recogniseEscapeSequence (regexString, inputString) =
|
||||
let
|
||||
(* arrange *)
|
||||
val dfa = CiDfa.fromString regexString
|
||||
|
||||
(* act *)
|
||||
val matches = CiDfa.matchString (dfa, inputString)
|
||||
|
||||
(* assert *)
|
||||
val expectedMatches = [(6, 6)]
|
||||
in
|
||||
Expect.isTrue (matches = expectedMatches)
|
||||
end
|
||||
|
||||
fun doesNotRecogniseUnescaped (regexString, inputString) =
|
||||
let
|
||||
(* arrange *)
|
||||
val dfa = CiDfa.fromString regexString
|
||||
|
||||
(* act *)
|
||||
val matches = CiDfa.matchString (dfa, inputString)
|
||||
|
||||
(* assert *)
|
||||
val expectedMatches = []
|
||||
in
|
||||
Expect.isTrue (matches = expectedMatches)
|
||||
end
|
||||
|
||||
val escapeSequenceTests = describe "regex escape sequences"
|
||||
[ test "recognises alert" (fn _ =>
|
||||
recogniseEscapeSequence ("\\a", "hello \a world"))
|
||||
, test "recognises backspace" (fn _ =>
|
||||
recogniseEscapeSequence ("\\b", "hello \b world"))
|
||||
, test "recognises tab" (fn _ =>
|
||||
recogniseEscapeSequence ("\\t", "hello \t world"))
|
||||
, test "recognises newline" (fn _ =>
|
||||
recogniseEscapeSequence ("\\n", "hello \n world"))
|
||||
, test "recognises vertical tab" (fn _ =>
|
||||
recogniseEscapeSequence ("\\v", "hello \v world"))
|
||||
, test "recognises form feed" (fn _ =>
|
||||
recogniseEscapeSequence ("\\f", "hello \f world"))
|
||||
, test "recognises carriage return" (fn _ =>
|
||||
recogniseEscapeSequence ("\\r", "hello \r world"))
|
||||
, test "recognises backslash" (fn _ =>
|
||||
recogniseEscapeSequence ("\\\\", "hello \\ world"))
|
||||
]
|
||||
|
||||
val metacharacterEscapeTest = describe "regex metacharacter escape sequences"
|
||||
[ test "recognises (" (fn _ =>
|
||||
recogniseEscapeSequence ("\\(", "hello ( world"))
|
||||
, test "recognises )" (fn _ =>
|
||||
recogniseEscapeSequence ("\\)", "hello ) world"))
|
||||
, test "recognises [" (fn _ =>
|
||||
recogniseEscapeSequence ("\\[", "hello [ world"))
|
||||
, test "recognises ]" (fn _ =>
|
||||
recogniseEscapeSequence ("\\]", "hello ] world"))
|
||||
, test "recognises +" (fn _ =>
|
||||
recogniseEscapeSequence ("\\+", "hello + world"))
|
||||
, test "recognises |" (fn _ =>
|
||||
recogniseEscapeSequence ("\\|", "hello | world"))
|
||||
, test "recognises ?" (fn _ =>
|
||||
recogniseEscapeSequence ("\\?", "hello ? world"))
|
||||
, test "recognises ." (fn _ =>
|
||||
recogniseEscapeSequence ("\\.", "hello . world"))
|
||||
, test "recognises -" (fn _ =>
|
||||
recogniseEscapeSequence ("\\-", "hello - world"))
|
||||
|
||||
(* checking that unescaped metacharacter is not recognised *)
|
||||
, test "does not recognise (" (fn _ =>
|
||||
doesNotRecogniseUnescaped ("(", "hello ( world"))
|
||||
, test "does not recognise )" (fn _ =>
|
||||
doesNotRecogniseUnescaped (")", "hello ) world"))
|
||||
, test "does not recognise [" (fn _ =>
|
||||
doesNotRecogniseUnescaped ("[", "hello [ world"))
|
||||
, test "does not recognise ]" (fn _ =>
|
||||
doesNotRecogniseUnescaped ("[", "hello ] world"))
|
||||
, test "does not recognise +" (fn _ =>
|
||||
doesNotRecogniseUnescaped ("+", "hello + world"))
|
||||
, test "does not recognise |" (fn _ =>
|
||||
doesNotRecogniseUnescaped ("|", "hello | world"))
|
||||
, test "does not recognise ?" (fn _ =>
|
||||
doesNotRecogniseUnescaped ("?", "hello ? world"))
|
||||
, test "does not recognise -" (fn _ =>
|
||||
doesNotRecogniseUnescaped ("-", "hello - world"))
|
||||
]
|
||||
|
||||
(* tests based on regex tutorial by FreeCodeCamp *)
|
||||
val freeCodeCampTests = describe "regex freeCodeCamp tests"
|
||||
[ test "The dog chased the cat" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val sentence = "The dog chased the cat"
|
||||
val regexString = "the"
|
||||
val caseSensitiveDfa = CsDfa.fromString regexString
|
||||
val caseInsensitiveDfa = CiDfa.fromString regexString
|
||||
|
||||
(* act *)
|
||||
val caseSensitiveMatches =
|
||||
CsDfa.matchString (caseSensitiveDfa, sentence)
|
||||
val caseInsensitiveMatches =
|
||||
CiDfa.matchString (caseInsensitiveDfa, sentence)
|
||||
|
||||
(* assert *)
|
||||
val expectedCaseSensitive = [(15, 17)]
|
||||
val expectedCaseInsensitive = [(0, 2), (15, 17)]
|
||||
val expected =
|
||||
caseSensitiveMatches = expectedCaseSensitive
|
||||
andalso caseInsensitiveMatches = expectedCaseInsensitive
|
||||
in
|
||||
Expect.isTrue (expected)
|
||||
end)
|
||||
, test "Somewhere Waldo is hiding in this text." (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val sentence = "Somewhere Waldo is hiding in this text."
|
||||
val regexString = "Waldo"
|
||||
val dfa = CsDfa.fromString regexString
|
||||
|
||||
(* act *)
|
||||
val matches = CsDfa.matchString (dfa, sentence)
|
||||
|
||||
(* assert *)
|
||||
val expectedMatches = [(10, 14)]
|
||||
in
|
||||
Expect.isTrue (expectedMatches = matches)
|
||||
end)
|
||||
, test "James has a pet cat." (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val sentence = "James has a pet cat."
|
||||
val regexString = "dog|cat|bird|fish"
|
||||
val dfa = CsDfa.fromString regexString
|
||||
|
||||
(* act *)
|
||||
val matches = CsDfa.matchString (dfa, sentence)
|
||||
|
||||
(* assert *)
|
||||
val expectedMatches = [(16, 18)]
|
||||
in
|
||||
Expect.isTrue (expectedMatches = matches)
|
||||
end)
|
||||
, test "Ignore Case While Matching" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val sentence = "freeCodeCamp"
|
||||
val regexString = "freecodecamp"
|
||||
val dfa = CiDfa.fromString regexString
|
||||
|
||||
(* act *)
|
||||
val matches = CiDfa.matchString (dfa, sentence)
|
||||
|
||||
(* assert *)
|
||||
val expectedMatches = [(0, 11)]
|
||||
in
|
||||
Expect.isTrue (expectedMatches = matches)
|
||||
end)
|
||||
, test "Extract the word 'coding' from this string" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val sentence = "Extract the word 'coding' from this string"
|
||||
val regexString = "coding"
|
||||
val dfa = CsDfa.fromString regexString
|
||||
|
||||
(* act *)
|
||||
val matches = CsDfa.matchString (dfa, sentence)
|
||||
|
||||
(* assert *)
|
||||
val expectedMatches = [(18, 23)]
|
||||
in
|
||||
Expect.isTrue (expectedMatches = matches)
|
||||
end)
|
||||
, test "Repeat, Repeat, Repeat" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val sentence = "Repeat, Repeat, Repeat"
|
||||
val regexString = "Repeat"
|
||||
val dfa = CsDfa.fromString regexString
|
||||
|
||||
(* act *)
|
||||
val matches = CsDfa.matchString (dfa, sentence)
|
||||
|
||||
(* assert *)
|
||||
val expectedMatches = [(0, 5), (8, 13), (16, 21)]
|
||||
in
|
||||
Expect.isTrue (expectedMatches = matches)
|
||||
end)
|
||||
, test "Twinkle, twinkle, little start" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val sentence = "Twinkle, twinkle, little start"
|
||||
val regexString = "twinkle"
|
||||
val dfa = CiDfa.fromString regexString
|
||||
|
||||
(* act *)
|
||||
val matches = CiDfa.matchString (dfa, sentence)
|
||||
|
||||
(* assert *)
|
||||
val expectedMatches = [(0, 6), (9, 15)]
|
||||
in
|
||||
Expect.isTrue (expectedMatches = matches)
|
||||
end)
|
||||
, test "hu. regex" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val regexString = "hu."
|
||||
val dfa = CsDfa.fromString regexString
|
||||
|
||||
val humStr = "To mock a humming bird"
|
||||
val hugStr = "Bear hug"
|
||||
|
||||
(* act *)
|
||||
val humMatches = CiDfa.matchString (dfa, humStr)
|
||||
val hugMatches = CiDfa.matchString (dfa, hugStr)
|
||||
|
||||
(* assert *)
|
||||
val expectedHumMatches = [(10, 12)]
|
||||
val expectedHugMatches = [(5, 7)]
|
||||
val isExpected =
|
||||
humMatches = expectedHumMatches
|
||||
andalso hugMatches = expectedHugMatches
|
||||
in
|
||||
Expect.isTrue isExpected
|
||||
end)
|
||||
, test "Let's have fun with regular expressions!" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val sentence = "Let's have fun with regular expressions!"
|
||||
val regexString = ".un"
|
||||
val dfa = CsDfa.fromString regexString
|
||||
|
||||
(* act *)
|
||||
val matches = CsDfa.matchString (dfa, sentence)
|
||||
|
||||
(* assert *)
|
||||
val expectedMatches = [(11, 13)]
|
||||
in
|
||||
Expect.isTrue (matches = expectedMatches)
|
||||
end)
|
||||
, test "Beware of bugs in the above code" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val sentence =
|
||||
"Beware of bugs in the above code;\
|
||||
\I have only proved it correct, not tried it."
|
||||
val regexString = "[aeiou]"
|
||||
val dfa = CiDfa.fromString regexString
|
||||
|
||||
(* act *)
|
||||
val matches = CiDfa.matchString (dfa, sentence)
|
||||
|
||||
(* assert *)
|
||||
val expectedMatches =
|
||||
[ (1, 1)
|
||||
, (3, 3)
|
||||
, (5, 5)
|
||||
, (7, 7)
|
||||
, (11, 11)
|
||||
, (15, 15)
|
||||
, (20, 20)
|
||||
, (22, 22)
|
||||
, (24, 24)
|
||||
, (26, 26)
|
||||
, (29, 29)
|
||||
, (31, 31)
|
||||
, (33, 33)
|
||||
, (36, 36)
|
||||
, (38, 38)
|
||||
, (40, 40)
|
||||
, (47, 47)
|
||||
, (49, 49)
|
||||
, (52, 52)
|
||||
, (56, 56)
|
||||
, (59, 59)
|
||||
, (65, 65)
|
||||
, (70, 70)
|
||||
, (71, 71)
|
||||
, (74, 74)
|
||||
]
|
||||
in
|
||||
Expect.isTrue (matches = expectedMatches)
|
||||
end)
|
||||
, test "The quick brown fox jumps over the lazy dog." (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val sentence = "The quick brown fox jumps over the lazy dog."
|
||||
val regexString = "[a-zA-Z]"
|
||||
val dfa = CiDfa.fromString regexString
|
||||
|
||||
(* act *)
|
||||
val matches = CiDfa.matchString (dfa, sentence)
|
||||
|
||||
(* assert *)
|
||||
val expectedMatches =
|
||||
[ (0, 0)
|
||||
, (1, 1)
|
||||
, (2, 2)
|
||||
, (4, 4)
|
||||
, (5, 5)
|
||||
, (6, 6)
|
||||
, (7, 7)
|
||||
, (8, 8)
|
||||
, (10, 10)
|
||||
, (11, 11)
|
||||
, (12, 12)
|
||||
, (13, 13)
|
||||
, (14, 14)
|
||||
, (16, 16)
|
||||
, (17, 17)
|
||||
, (18, 18)
|
||||
, (20, 20)
|
||||
, (21, 21)
|
||||
, (22, 22)
|
||||
, (23, 23)
|
||||
, (24, 24)
|
||||
, (26, 26)
|
||||
, (27, 27)
|
||||
, (28, 28)
|
||||
, (29, 29)
|
||||
, (31, 31)
|
||||
, (32, 32)
|
||||
, (33, 33)
|
||||
, (35, 35)
|
||||
, (36, 36)
|
||||
, (37, 37)
|
||||
, (38, 38)
|
||||
, (40, 40)
|
||||
, (41, 41)
|
||||
, (42, 42)
|
||||
]
|
||||
in
|
||||
Expect.isTrue (matches = expectedMatches)
|
||||
end)
|
||||
, test "Blueberry 3.141592653s are delicious." (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val sentence = "Blueberry 3.141592653s are delicious."
|
||||
val regexString = "[2-6h-s]"
|
||||
val dfa = CiDfa.fromString regexString
|
||||
|
||||
(* act *)
|
||||
val matches = CiDfa.matchString (dfa, sentence)
|
||||
|
||||
(* assert *)
|
||||
val expectedMatches =
|
||||
[ (1, 1)
|
||||
, (6, 6)
|
||||
, (7, 7)
|
||||
, (10, 10)
|
||||
, (13, 13)
|
||||
, (15, 15)
|
||||
, (17, 17)
|
||||
, (18, 18)
|
||||
, (19, 19)
|
||||
, (20, 20)
|
||||
, (21, 21)
|
||||
, (24, 24)
|
||||
, (29, 29)
|
||||
, (30, 30)
|
||||
, (32, 32)
|
||||
, (33, 33)
|
||||
, (35, 35)
|
||||
]
|
||||
in
|
||||
Expect.isTrue (expectedMatches = matches)
|
||||
end)
|
||||
, test "3 blind mice." (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val sentence = "3 blind mice."
|
||||
val regexString = "[^0-9aeiou]"
|
||||
val dfa = CiDfa.fromString regexString
|
||||
|
||||
(* act *)
|
||||
val matches = CiDfa.matchString (dfa, sentence)
|
||||
|
||||
(* assert *)
|
||||
val expectedMatches =
|
||||
[ (1, 1)
|
||||
, (2, 2)
|
||||
, (3, 3)
|
||||
, (5, 5)
|
||||
, (6, 6)
|
||||
, (7, 7)
|
||||
, (8, 8)
|
||||
, (10, 10)
|
||||
, (12, 12)
|
||||
]
|
||||
in
|
||||
Expect.isTrue (expectedMatches = matches)
|
||||
end)
|
||||
, test "Mississipi" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val sentence = "Mississipi"
|
||||
val regexString = "s+"
|
||||
val dfa = CiDfa.fromString regexString
|
||||
|
||||
(* act *)
|
||||
val matches = CiDfa.matchString (dfa, sentence)
|
||||
|
||||
(* assert *)
|
||||
val expectedMatches = [(2, 3), (5, 6)]
|
||||
in
|
||||
Expect.isTrue (expectedMatches = matches)
|
||||
end)
|
||||
, test "goooal" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val soccerSentence = "goooal"
|
||||
val gPhrase = "gut feeling"
|
||||
val oPhrase = "over the moon"
|
||||
|
||||
val goRegex = "go*"
|
||||
val dfa = CsDfa.fromString goRegex
|
||||
|
||||
(* act *)
|
||||
val soccerMatches = CsDfa.matchString (dfa, soccerSentence)
|
||||
val gPhraseMatches = CsDfa.matchString (dfa, gPhrase)
|
||||
val oPhraseMatches = CsDfa.matchString (dfa, oPhrase)
|
||||
|
||||
(* assert *)
|
||||
val expectedSoccerMatches = [(0, 3)]
|
||||
val expectedGPhraseMatches = [(0, 0), (10, 10)]
|
||||
val expectedOPhraseMatches = []
|
||||
|
||||
val isExpected =
|
||||
soccerMatches = expectedSoccerMatches
|
||||
andalso gPhraseMatches = expectedGPhraseMatches
|
||||
andalso oPhraseMatches = expectedOPhraseMatches
|
||||
in
|
||||
Expect.isTrue isExpected
|
||||
end)
|
||||
, test "chewie quote" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val sentence = "Aaaaaaargh"
|
||||
val regexString = "Aa*"
|
||||
val dfa = CsDfa.fromString regexString
|
||||
|
||||
(* act *)
|
||||
val matches = CsDfa.matchString (dfa, sentence)
|
||||
|
||||
(* assert *)
|
||||
val expectedMatches = [(0, 6)]
|
||||
in
|
||||
Expect.isTrue (matches = expectedMatches)
|
||||
end)
|
||||
, test "favorite" (fn _ =>
|
||||
let
|
||||
(* arrange *)
|
||||
val sentenceWithoutU = "favorite"
|
||||
val sentenceWithU = "favourite"
|
||||
|
||||
val regexString = "favou?rite"
|
||||
val dfa = CsDfa.fromString regexString
|
||||
|
||||
(* act *)
|
||||
val matchesWithoutU = CsDfa.matchString (dfa, sentenceWithoutU)
|
||||
val matchesWithU = CsDfa.matchString (dfa, sentenceWithU)
|
||||
|
||||
(* assert *)
|
||||
val expectedMatchesWithoutU = [(0, 7)]
|
||||
val expectedMatchesWithU = [(0, 8)]
|
||||
|
||||
val isExpected =
|
||||
matchesWithoutU = expectedMatchesWithoutU
|
||||
andalso matchesWithU = expectedMatchesWithU
|
||||
in
|
||||
Expect.isTrue isExpected
|
||||
end)
|
||||
]
|
||||
|
||||
val tests =
|
||||
[ caseInsensitiveTests
|
||||
, caseSensitiveTests
|
||||
, endMarkerTests
|
||||
, escapeSequenceTests
|
||||
, metacharacterEscapeTest
|
||||
, freeCodeCampTests
|
||||
]
|
||||
end
|
||||
82
shf/test/regression-tests.sml
Normal file
82
shf/test/regression-tests.sml
Normal file
@@ -0,0 +1,82 @@
|
||||
structure RegressionTests =
|
||||
struct
|
||||
open Railroad
|
||||
open Railroad.Test
|
||||
|
||||
fun updateLoop (pos, str, app) =
|
||||
if pos = String.size str then
|
||||
app
|
||||
else
|
||||
let
|
||||
val chr = String.sub (str, pos)
|
||||
val () = ExceptionLogger.addCommand (InputMsg.CHAR_EVENT chr)
|
||||
val app = TestUtils.update (app, InputMsg.CHAR_EVENT chr)
|
||||
in
|
||||
updateLoop (pos + 1, str, app)
|
||||
end
|
||||
|
||||
fun appFromText text = TestUtils.init text
|
||||
|
||||
fun loadFromFile (io, acc) =
|
||||
case TextIO.inputLine io of
|
||||
SOME line => loadFromFile (io, acc ^ line)
|
||||
| NONE => acc
|
||||
|
||||
val initialText =
|
||||
let
|
||||
val io = TextIO.openIn "temp.txt"
|
||||
val str = loadFromFile (io, "")
|
||||
val () = TextIO.closeIn io
|
||||
in
|
||||
str
|
||||
end
|
||||
|
||||
val charEventTests = describe "CHAR_EVENT regressions"
|
||||
[ test "SearchList.goToNum vector bounds regression (1)" (fn _ =>
|
||||
let
|
||||
val app = TestUtils.init initialText
|
||||
val history = "G12dk"
|
||||
val newApp = TestUtils.updateMany (app, history)
|
||||
in
|
||||
(* just expect that we do not fail or throw an exception *)
|
||||
Expect.isTrue true
|
||||
end)
|
||||
, test "No error raised when moving cursor up/down after deleting" (fn _ =>
|
||||
let
|
||||
val app = TestUtils.init initialText
|
||||
val history =
|
||||
"16G18ddjjjjjjjjjdkdkdkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj"
|
||||
val newApp = TestUtils.updateMany (app, history)
|
||||
in
|
||||
Expect.isTrue true
|
||||
end)
|
||||
, test
|
||||
"SearchList.buildRange does not cause exception \
|
||||
\when deleting (1)"
|
||||
(fn _ =>
|
||||
let
|
||||
val app = TestUtils.init "h ello world\n"
|
||||
|
||||
(* search *)
|
||||
val search = "/ello"
|
||||
val app = TestUtils.updateMany (app, search)
|
||||
val app = TestUtils.update (app, InputMsg.KEY_ENTER)
|
||||
|
||||
(* move and then delete twice *)
|
||||
val app = TestUtils.updateMany (app, "edede")
|
||||
in
|
||||
Expect.isTrue true
|
||||
end)
|
||||
, test
|
||||
"DfaGen does not cause exception \
|
||||
\when parsing alternation that contains a char \
|
||||
\from the previous alternation (1)"
|
||||
(fn _ =>
|
||||
(let val dfa = CaseSensitiveDfa.fromString "str|s"
|
||||
in Expect.isTrue true
|
||||
end)
|
||||
handle _ => Expect.isTrue false)
|
||||
]
|
||||
|
||||
val tests = [charEventTests]
|
||||
end
|
||||
71
shf/test/test-utils.sml
Normal file
71
shf/test/test-utils.sml
Normal file
@@ -0,0 +1,71 @@
|
||||
structure TestUtils =
|
||||
struct
|
||||
fun init bufferString =
|
||||
let val buffer = LineGap.fromString bufferString
|
||||
in AppType.init (buffer, 0, 0, Time.now ())
|
||||
end
|
||||
|
||||
fun update (app, cmd) =
|
||||
AppUpdate.update (app, cmd, Time.now ())
|
||||
|
||||
fun updateMany (app, str) =
|
||||
let
|
||||
fun loop (pos, app) =
|
||||
if pos = String.size str then
|
||||
app
|
||||
else
|
||||
let
|
||||
val chr = String.sub (str, pos)
|
||||
val chr = InputMsg.CHAR_EVENT chr
|
||||
val app = update (app, chr)
|
||||
in
|
||||
loop (pos + 1, app)
|
||||
end
|
||||
in
|
||||
loop (0, app)
|
||||
end
|
||||
|
||||
fun expectYank (app: AppType.app_type, expectedString) =
|
||||
let
|
||||
open MailboxType
|
||||
open DrawMsg
|
||||
open Railroad
|
||||
open Railroad.Test
|
||||
|
||||
fun loop (hd :: tl) =
|
||||
(case hd of
|
||||
DRAW (YANK actualString) =>
|
||||
if actualString = expectedString then
|
||||
Expect.isTrue (actualString = expectedString)
|
||||
else
|
||||
let
|
||||
val () = print
|
||||
("expectedString = [" ^ expectedString ^ "]\n")
|
||||
val () = print ("actualString = [" ^ actualString ^ "]\n")
|
||||
val () = print "\n"
|
||||
in
|
||||
Expect.isTrue (actualString = expectedString)
|
||||
end
|
||||
| _ => loop tl)
|
||||
| loop ([]) =
|
||||
let val () = print "no string yanked\n"
|
||||
in Expect.isTrue false
|
||||
end
|
||||
in
|
||||
loop (#msgs app)
|
||||
end
|
||||
|
||||
fun expectNoYank (app: AppType.app_type) =
|
||||
let
|
||||
open MailboxType
|
||||
open DrawMsg
|
||||
open Railroad
|
||||
open Railroad.Test
|
||||
|
||||
fun loop (DRAW (YANK _) :: _) = Expect.isTrue false
|
||||
| loop (hd :: tl) = loop tl
|
||||
| loop ([]) = Expect.isTrue true
|
||||
in
|
||||
loop (#msgs app)
|
||||
end
|
||||
end
|
||||
23
shf/test/test.sml
Normal file
23
shf/test/test.sml
Normal file
@@ -0,0 +1,23 @@
|
||||
structure Test =
|
||||
struct
|
||||
open Railroad
|
||||
open Railroad.Test
|
||||
|
||||
fun main () =
|
||||
let
|
||||
val tests = List.concat
|
||||
[ NormalMoveTests.tests
|
||||
, NormalDeleteTests.tests
|
||||
, NormalYankTests.tests
|
||||
, PersistentVectorTests.tests
|
||||
, RegressionTests.tests
|
||||
, RegexTests.tests
|
||||
]
|
||||
val tests = concat tests
|
||||
in
|
||||
runWithConfig [Configuration.PrintPassed false] tests
|
||||
handle e => ExceptionLogger.log e
|
||||
end
|
||||
end
|
||||
|
||||
val () = Test.main ()
|
||||
Reference in New Issue
Block a user