lib: split strings suffix/contains/index/compare tests per ref/hare/strings
Pure move: the hasprefix/hassuffix, contains, byteindex/rbyteindex/
index/rindex, and compare sections of strings_test.ww become sibling
suffix_test.ww, contains_test.ww, index_test.ww, compare_test.ww,
mirroring ref/hare/strings/{suffix,contains,index,compare}.ha
ownership. Blocks are byte-identical; only the section banner lines
are deleted (the file names now carry them).
The remaining families (dup, concat, trim, sub, utf8, iter, tokenize,
pad, replace, cut) stay in strings_test.ww: they all consume the
file-local streq helper, and a helper cannot be shared across split
test files — a single-file test root does not pull same-package
siblings (undefined: helper), while dir-mode composes all package
test files into one unit (duplicate fn helper). Splitting them would
force either helper duplication or renames, both out of scope for a
pure move.
Consumers: Makefile LIBRARY_TESTS gains the four new entries beside
the strings_test.ww row; test/byteid/libbyteid_test.ww roster gains
four fx rows, NENTEXPECT 44->48 (+4).
This commit is contained in:
2
Makefile
2
Makefile
@@ -486,6 +486,8 @@ LIBRARY_TESTS = lib/errors/errno_test.ww lib/ascii/ascii_test.ww \
|
|||||||
lib/ww/syntax/sym_test.ww \
|
lib/ww/syntax/sym_test.ww \
|
||||||
lib/strconv/test/ftos_test.ww lib/strconv/test/stof_test.ww \
|
lib/strconv/test/ftos_test.ww lib/strconv/test/stof_test.ww \
|
||||||
lib/strconv/test/int_test.ww lib/strings/strings_test.ww \
|
lib/strconv/test/int_test.ww lib/strings/strings_test.ww \
|
||||||
|
lib/strings/suffix_test.ww lib/strings/contains_test.ww \
|
||||||
|
lib/strings/index_test.ww lib/strings/compare_test.ww \
|
||||||
lib/bytes/bytes_test.ww lib/encoding/utf8/utf8_test.ww \
|
lib/bytes/bytes_test.ww lib/encoding/utf8/utf8_test.ww \
|
||||||
lib/bufio/bufio_test.ww lib/math/random/random_test.ww \
|
lib/bufio/bufio_test.ww lib/math/random/random_test.ww \
|
||||||
lib/math/checked/checked_test.ww lib/fmt/fmt_test.ww \
|
lib/math/checked/checked_test.ww lib/fmt/fmt_test.ww \
|
||||||
|
|||||||
18
lib/strings/compare_test.ww
Normal file
18
lib/strings/compare_test.ww
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// comparetest — exercises strings.compare. A failing row aborts
|
||||||
|
// via the assert/abort builtin (task #5 @test conversion).
|
||||||
|
// Vectors mirror ref/hare/strings/compare.ha.
|
||||||
|
|
||||||
|
package strings_test;
|
||||||
|
|
||||||
|
import strings;
|
||||||
|
|
||||||
|
// ref/hare/strings/compare.ha:16.
|
||||||
|
|
||||||
|
@test fn compare_cases() void = {
|
||||||
|
assert(!(strings.compare("ABC", "ABC") != 0));
|
||||||
|
assert(!(strings.compare("ABC", "AB") <= 0));
|
||||||
|
assert(!(strings.compare("AB", "ABC") >= 0));
|
||||||
|
assert(!(strings.compare("BCD", "ABC") <= 0));
|
||||||
|
assert(!(strings.compare("ABC", "abc") >= 0));
|
||||||
|
assert(!(strings.compare("ABC", "こんにちは") >= 0));
|
||||||
|
};
|
||||||
28
lib/strings/contains_test.ww
Normal file
28
lib/strings/contains_test.ww
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// containstest — exercises strings.contains. A failing row aborts
|
||||||
|
// via the assert/abort builtin (task #5 @test conversion).
|
||||||
|
// Vectors mirror ref/hare/strings/contains.ha.
|
||||||
|
|
||||||
|
package strings_test;
|
||||||
|
|
||||||
|
import strings;
|
||||||
|
|
||||||
|
// ref/hare/strings/contains.ha:27.
|
||||||
|
|
||||||
|
@test fn contains_cases() void = {
|
||||||
|
assert(!(!strings.contains("hello world", "hello")));
|
||||||
|
assert(!(!strings.contains("hello world", 'h')));
|
||||||
|
assert(!( strings.contains("hello world", 'x')));
|
||||||
|
assert(!(!strings.contains("hello world", "world")));
|
||||||
|
assert(!(!strings.contains("hello world", ""))); // empty hits at 0
|
||||||
|
assert(!( strings.contains("hello world", "foobar")));
|
||||||
|
assert(!(!strings.contains("こんにちは", 0x306Bu32: rune))); // 'に'
|
||||||
|
assert(!(!strings.contains("こんにちは", "ちは")));
|
||||||
|
|
||||||
|
// Variadic rows. ref/hare/strings/contains.ha:27.
|
||||||
|
assert(!( strings.contains("hello")));
|
||||||
|
assert(!(!strings.contains("hello world", "world")));
|
||||||
|
assert(!(!strings.contains("hello", 'l')));
|
||||||
|
assert(!(!strings.contains("hello world", "foo", "world", 'x')));
|
||||||
|
assert(!( strings.contains("hello", "foo", 'z', "bar")));
|
||||||
|
assert(!(!strings.contains("héllo", "x", 0xE9u32: rune)));
|
||||||
|
};
|
||||||
240
lib/strings/index_test.ww
Normal file
240
lib/strings/index_test.ww
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
// indextest — exercises strings.byteindex/rbyteindex/index/rindex.
|
||||||
|
// A failing row aborts via the assert/abort builtin (task #5 @test
|
||||||
|
// conversion). Vectors mirror ref/hare/strings/index.ha.
|
||||||
|
|
||||||
|
package strings_test;
|
||||||
|
|
||||||
|
import strings;
|
||||||
|
|
||||||
|
// ref/hare/strings/index.ha:147 (byteindex tests, both arms).
|
||||||
|
|
||||||
|
@test fn byteindex_str_cases() void = {
|
||||||
|
match (strings.byteindex("hello", "hello")) {
|
||||||
|
case let i: i32 => { assert(!(i != 0)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
match (strings.byteindex("hello world!", "world")) {
|
||||||
|
case let i: i32 => { assert(!(i != 6)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
match (strings.byteindex("hello world!", "orld!")) {
|
||||||
|
case let i: i32 => { assert(!(i != 7)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
match (strings.byteindex("hello world!", "word")) {
|
||||||
|
case let i: i32 => { abort(); };
|
||||||
|
case void => void;
|
||||||
|
};
|
||||||
|
// empty needle hits at 0 (ref/hare/bytes/index.ha:63).
|
||||||
|
match (strings.byteindex("hello", "")) {
|
||||||
|
case let i: i32 => { assert(!(i != 0)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
// empty haystack, non-empty needle — absent.
|
||||||
|
match (strings.byteindex("", "x")) {
|
||||||
|
case let i: i32 => { abort(); };
|
||||||
|
case void => void;
|
||||||
|
};
|
||||||
|
// multibyte substring in multibyte haystack.
|
||||||
|
match (strings.byteindex("こんにちは", "ちは")) {
|
||||||
|
case let i: i32 => { assert(!(i != 9)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
@test fn byteindex_rune_cases() void = {
|
||||||
|
// ASCII rune (1-byte encoding).
|
||||||
|
match (strings.byteindex("hello world", 'w')) {
|
||||||
|
case let i: i32 => { assert(!(i != 6)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
// 2-byte rune U+00E9 'é' inside "café".
|
||||||
|
match (strings.byteindex("café", 0xE9u32: rune)) {
|
||||||
|
case let i: i32 => { assert(!(i != 3)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
// 3-byte rune U+3061 'ち' inside "こんにちは".
|
||||||
|
match (strings.byteindex("こんにちは", 0x3061u32: rune)) {
|
||||||
|
case let i: i32 => { assert(!(i != 9)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
// 4-byte rune U+1F980 '🦀' inside "ab🦀cd".
|
||||||
|
match (strings.byteindex("ab🦀cd", 0x1F980u32: rune)) {
|
||||||
|
case let i: i32 => { assert(!(i != 2)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
// absent.
|
||||||
|
match (strings.byteindex("こんにちは", 'q')) {
|
||||||
|
case let i: i32 => { abort(); };
|
||||||
|
case void => void;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
@test fn rbyteindex_cases() void = {
|
||||||
|
// Two 'た' in "またあったね" — ref/hare/strings/index.ha:160-161.
|
||||||
|
match (strings.byteindex("またあったね", "た")) {
|
||||||
|
case let i: i32 => { assert(!(i != 3)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
match (strings.rbyteindex("またあったね", "た")) {
|
||||||
|
case let i: i32 => { assert(!(i != 12)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
// Rune arm, multi-byte 'に' U+306B.
|
||||||
|
match (strings.rbyteindex("こんにちは", 0x306Bu32: rune)) {
|
||||||
|
case let i: i32 => { assert(!(i != 6)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
// Absent.
|
||||||
|
match (strings.rbyteindex("abc", 'z')) {
|
||||||
|
case let i: i32 => { abort(); };
|
||||||
|
case void => void;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/strings/index.ha:108. Rune-wise offset, NOT byte-wise — the
|
||||||
|
// multibyte rows pin that distinction (Hare doc at index.ha:7).
|
||||||
|
|
||||||
|
@test fn index_cases() void = {
|
||||||
|
// str-arm: ASCII haystack/needle, mid-string match.
|
||||||
|
match (strings.index("hello", "ll")) {
|
||||||
|
case let i: i32 => { assert(!(i != 2)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
// str-arm: absent needle.
|
||||||
|
match (strings.index("hello", "world")) {
|
||||||
|
case let i: i32 => { abort(); };
|
||||||
|
case void => void;
|
||||||
|
};
|
||||||
|
// Hare vectors at ref/hare/strings/index.ha:113-119.
|
||||||
|
match (strings.index("hello world!", "hello")) {
|
||||||
|
case let i: i32 => { assert(!(i != 0)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
match (strings.index("hello world!", "world")) {
|
||||||
|
case let i: i32 => { assert(!(i != 6)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
match (strings.index("hello world!", "orld!")) {
|
||||||
|
case let i: i32 => { assert(!(i != 7)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
// Multibyte haystack + str needle: "ちは" at rune index 3
|
||||||
|
// in "こんにちは" (byteindex returns 9, rune-index is 3).
|
||||||
|
match (strings.index("こんにちは", "ちは")) {
|
||||||
|
case let i: i32 => { assert(!(i != 3)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
// rune-arm.
|
||||||
|
match (strings.index("hello", 'l')) {
|
||||||
|
case let i: i32 => { assert(!(i != 2)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
match (strings.index("hello world", 'w')) {
|
||||||
|
case let i: i32 => { assert(!(i != 6)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
// Multibyte rune: 'é' U+00E9 at rune 1 in "héllo" — pins
|
||||||
|
// rune-index vs byte-index (byteindex returns 1; rune-index is
|
||||||
|
// 1 also — but the str-arm's 1405 row covers the distinction).
|
||||||
|
match (strings.index("héllo", 0xE9u32: rune)) {
|
||||||
|
case let i: i32 => { assert(!(i != 1)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
// Multibyte rune in multibyte haystack: 'ち' U+3061 at rune 3
|
||||||
|
// in "こんにちは" (byteindex returns 9, rune-index is 3).
|
||||||
|
match (strings.index("こんにちは", 0x3061u32: rune)) {
|
||||||
|
case let i: i32 => { assert(!(i != 3)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
match (strings.index("こんにちは", 'q')) {
|
||||||
|
case let i: i32 => { abort(); };
|
||||||
|
case void => void;
|
||||||
|
};
|
||||||
|
// str-arm: rune-index ≠ byte-index again, mid-string match.
|
||||||
|
// "あった" starts at rune 2 (byte 6) in "またあったね"
|
||||||
|
// (each kana is 3 bytes; ref/hare/strings/index.ha:60). Pins
|
||||||
|
// the dual-iterator walk against the discarded byteindex-and-walk
|
||||||
|
// shape (#10).
|
||||||
|
match (strings.index("またあったね", "あった")) {
|
||||||
|
case let i: i32 => { assert(!(i != 2)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
// str-arm: tail-anchored multibyte needle. "は" is at rune 4
|
||||||
|
// (byte 12) in "こんにちは".
|
||||||
|
match (strings.index("こんにちは", "は")) {
|
||||||
|
case let i: i32 => { assert(!(i != 4)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
// Empty needle hits at rune 0 — Hare's `index_string` falls into
|
||||||
|
// the `needle_rune is done` branch on the very first inner step
|
||||||
|
// (ref/hare/strings/index.ha:70).
|
||||||
|
match (strings.index("hello", "")) {
|
||||||
|
case let i: i32 => { assert(!(i != 0)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
match (strings.index("", "")) {
|
||||||
|
case let i: i32 => { assert(!(i != 0)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
// Empty haystack, non-empty needle — absent.
|
||||||
|
match (strings.index("", "x")) {
|
||||||
|
case let i: i32 => { abort(); };
|
||||||
|
case void => void;
|
||||||
|
};
|
||||||
|
// Multibyte haystack, multibyte absent needle — exercises the
|
||||||
|
// inner-loop mismatch-break across runes (#10, Hare row
|
||||||
|
// ref/hare/strings/index.ha:119).
|
||||||
|
match (strings.index("こんにちは", "きょうは")) {
|
||||||
|
case let i: i32 => { abort(); };
|
||||||
|
case void => void;
|
||||||
|
};
|
||||||
|
// Self-match: haystack == needle, Hare row index.ha:113.
|
||||||
|
match (strings.index("hello", "hello")) {
|
||||||
|
case let i: i32 => { assert(!(i != 0)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/strings/index.ha:22. Symmetric: last-occurrence rune index.
|
||||||
|
|
||||||
|
@test fn rindex_cases() void = {
|
||||||
|
// str-arm.
|
||||||
|
match (strings.rindex("hello", "lo")) {
|
||||||
|
case let i: i32 => { assert(!(i != 3)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
// Hare vector at ref/hare/strings/index.ha:122.
|
||||||
|
match (strings.rindex("hello world!", "o")) {
|
||||||
|
case let i: i32 => { assert(!(i != 7)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
match (strings.rindex("hello", "world")) {
|
||||||
|
case let i: i32 => { abort(); };
|
||||||
|
case void => void;
|
||||||
|
};
|
||||||
|
// Multibyte: last "た" in "またあったね" — rbyteindex returns
|
||||||
|
// 12, rune-index is 4 (ま=0 た=1 あ=2 っ=3 た=4 ね=5).
|
||||||
|
match (strings.rindex("またあったね", "た")) {
|
||||||
|
case let i: i32 => { assert(!(i != 4)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
// rune-arm.
|
||||||
|
match (strings.rindex("hello", 'l')) {
|
||||||
|
case let i: i32 => { assert(!(i != 3)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
match (strings.rindex("aaaaa", 'a')) {
|
||||||
|
case let i: i32 => { assert(!(i != 4)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
// Multibyte rune: 'に' U+306B at rune 2 in "こんにちは".
|
||||||
|
match (strings.rindex("こんにちは", 0x306Bu32: rune)) {
|
||||||
|
case let i: i32 => { assert(!(i != 2)); };
|
||||||
|
case void => { abort(); };
|
||||||
|
};
|
||||||
|
match (strings.rindex("hello", 'z')) {
|
||||||
|
case let i: i32 => { abort(); };
|
||||||
|
case void => void;
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -5,8 +5,8 @@
|
|||||||
// abort reports the file, not the row (drew-t2-conversion-spec sec.5;
|
// abort reports the file, not the row (drew-t2-conversion-spec sec.5;
|
||||||
// Hare-equivalent, which reports file:line not loop index).
|
// Hare-equivalent, which reports file:line not loop index).
|
||||||
//
|
//
|
||||||
// Vectors mirror ref/hare/strings/{dup,concat,trim,contains,index,
|
// Vectors mirror ref/hare/strings/{dup,concat,trim,sub,utf8,iter,
|
||||||
// suffix,compare}.ha where ww can express them.
|
// tokenize,pad,replace}.ha where ww can express them.
|
||||||
|
|
||||||
// `_test` suffix: Go external-test-package idiom (sanctioned Go-over-Hare
|
// `_test` suffix: Go external-test-package idiom (sanctioned Go-over-Hare
|
||||||
// departure for lib/ tests) — self-import is hard-rejected, so the test
|
// departure for lib/ tests) — self-import is hard-rejected, so the test
|
||||||
@@ -237,294 +237,6 @@ fn streq(a: str, b: str) bool = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// ---- hasprefix --------------------------------------------------------
|
|
||||||
// ref/hare/strings/suffix.ha:18.
|
|
||||||
|
|
||||||
@test fn hasprefix_cases() void = {
|
|
||||||
assert(!(!strings.hasprefix("hello world", "hello")));
|
|
||||||
assert(!(!strings.hasprefix("hello world", 'h')));
|
|
||||||
assert(!( strings.hasprefix("hello world", "world")));
|
|
||||||
assert(!( strings.hasprefix("hello world", 'q')));
|
|
||||||
assert(!(!strings.hasprefix("hello", "hello"))); // equal-len
|
|
||||||
assert(!(!strings.hasprefix("anything", ""))); // empty prefix
|
|
||||||
assert(!( strings.hasprefix("", "x")));
|
|
||||||
// multibyte rune prefix — '\'é\'' literal blocked by single-byte
|
|
||||||
// lexrune (lib/ww/lex/lex.ww:659); pass codepoint directly.
|
|
||||||
assert(!(!strings.hasprefix("éclat", 0xE9u32: rune)));
|
|
||||||
assert(!(!strings.hasprefix("🦀rust", 0x1F980u32: rune)));
|
|
||||||
};
|
|
||||||
|
|
||||||
// ---- hassuffix --------------------------------------------------------
|
|
||||||
// ref/hare/strings/suffix.ha:36.
|
|
||||||
|
|
||||||
@test fn hassuffix_cases() void = {
|
|
||||||
assert(!(!strings.hassuffix("hello world", "world")));
|
|
||||||
assert(!(!strings.hassuffix("hello world", 'd')));
|
|
||||||
assert(!( strings.hassuffix("hello world", "hello")));
|
|
||||||
assert(!( strings.hassuffix("hello world", 'h')));
|
|
||||||
assert(!(!strings.hassuffix("café", 0xE9u32: rune))); // multibyte
|
|
||||||
};
|
|
||||||
|
|
||||||
// ---- contains ---------------------------------------------------------
|
|
||||||
// ref/hare/strings/contains.ha:27.
|
|
||||||
|
|
||||||
@test fn contains_cases() void = {
|
|
||||||
assert(!(!strings.contains("hello world", "hello")));
|
|
||||||
assert(!(!strings.contains("hello world", 'h')));
|
|
||||||
assert(!( strings.contains("hello world", 'x')));
|
|
||||||
assert(!(!strings.contains("hello world", "world")));
|
|
||||||
assert(!(!strings.contains("hello world", ""))); // empty hits at 0
|
|
||||||
assert(!( strings.contains("hello world", "foobar")));
|
|
||||||
assert(!(!strings.contains("こんにちは", 0x306Bu32: rune))); // 'に'
|
|
||||||
assert(!(!strings.contains("こんにちは", "ちは")));
|
|
||||||
|
|
||||||
// Variadic rows. ref/hare/strings/contains.ha:27.
|
|
||||||
assert(!( strings.contains("hello")));
|
|
||||||
assert(!(!strings.contains("hello world", "world")));
|
|
||||||
assert(!(!strings.contains("hello", 'l')));
|
|
||||||
assert(!(!strings.contains("hello world", "foo", "world", 'x')));
|
|
||||||
assert(!( strings.contains("hello", "foo", 'z', "bar")));
|
|
||||||
assert(!(!strings.contains("héllo", "x", 0xE9u32: rune)));
|
|
||||||
};
|
|
||||||
|
|
||||||
// ---- byteindex --------------------------------------------------------
|
|
||||||
// ref/hare/strings/index.ha:147 (byteindex tests, both arms).
|
|
||||||
|
|
||||||
@test fn byteindex_str_cases() void = {
|
|
||||||
match (strings.byteindex("hello", "hello")) {
|
|
||||||
case let i: i32 => { assert(!(i != 0)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
match (strings.byteindex("hello world!", "world")) {
|
|
||||||
case let i: i32 => { assert(!(i != 6)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
match (strings.byteindex("hello world!", "orld!")) {
|
|
||||||
case let i: i32 => { assert(!(i != 7)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
match (strings.byteindex("hello world!", "word")) {
|
|
||||||
case let i: i32 => { abort(); };
|
|
||||||
case void => void;
|
|
||||||
};
|
|
||||||
// empty needle hits at 0 (ref/hare/bytes/index.ha:63).
|
|
||||||
match (strings.byteindex("hello", "")) {
|
|
||||||
case let i: i32 => { assert(!(i != 0)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
// empty haystack, non-empty needle — absent.
|
|
||||||
match (strings.byteindex("", "x")) {
|
|
||||||
case let i: i32 => { abort(); };
|
|
||||||
case void => void;
|
|
||||||
};
|
|
||||||
// multibyte substring in multibyte haystack.
|
|
||||||
match (strings.byteindex("こんにちは", "ちは")) {
|
|
||||||
case let i: i32 => { assert(!(i != 9)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
@test fn byteindex_rune_cases() void = {
|
|
||||||
// ASCII rune (1-byte encoding).
|
|
||||||
match (strings.byteindex("hello world", 'w')) {
|
|
||||||
case let i: i32 => { assert(!(i != 6)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
// 2-byte rune U+00E9 'é' inside "café".
|
|
||||||
match (strings.byteindex("café", 0xE9u32: rune)) {
|
|
||||||
case let i: i32 => { assert(!(i != 3)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
// 3-byte rune U+3061 'ち' inside "こんにちは".
|
|
||||||
match (strings.byteindex("こんにちは", 0x3061u32: rune)) {
|
|
||||||
case let i: i32 => { assert(!(i != 9)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
// 4-byte rune U+1F980 '🦀' inside "ab🦀cd".
|
|
||||||
match (strings.byteindex("ab🦀cd", 0x1F980u32: rune)) {
|
|
||||||
case let i: i32 => { assert(!(i != 2)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
// absent.
|
|
||||||
match (strings.byteindex("こんにちは", 'q')) {
|
|
||||||
case let i: i32 => { abort(); };
|
|
||||||
case void => void;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// ---- rbyteindex -------------------------------------------------------
|
|
||||||
|
|
||||||
@test fn rbyteindex_cases() void = {
|
|
||||||
// Two 'た' in "またあったね" — ref/hare/strings/index.ha:160-161.
|
|
||||||
match (strings.byteindex("またあったね", "た")) {
|
|
||||||
case let i: i32 => { assert(!(i != 3)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
match (strings.rbyteindex("またあったね", "た")) {
|
|
||||||
case let i: i32 => { assert(!(i != 12)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
// Rune arm, multi-byte 'に' U+306B.
|
|
||||||
match (strings.rbyteindex("こんにちは", 0x306Bu32: rune)) {
|
|
||||||
case let i: i32 => { assert(!(i != 6)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
// Absent.
|
|
||||||
match (strings.rbyteindex("abc", 'z')) {
|
|
||||||
case let i: i32 => { abort(); };
|
|
||||||
case void => void;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// ---- index ------------------------------------------------------------
|
|
||||||
// ref/hare/strings/index.ha:108. Rune-wise offset, NOT byte-wise — the
|
|
||||||
// multibyte rows pin that distinction (Hare doc at index.ha:7).
|
|
||||||
|
|
||||||
@test fn index_cases() void = {
|
|
||||||
// str-arm: ASCII haystack/needle, mid-string match.
|
|
||||||
match (strings.index("hello", "ll")) {
|
|
||||||
case let i: i32 => { assert(!(i != 2)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
// str-arm: absent needle.
|
|
||||||
match (strings.index("hello", "world")) {
|
|
||||||
case let i: i32 => { abort(); };
|
|
||||||
case void => void;
|
|
||||||
};
|
|
||||||
// Hare vectors at ref/hare/strings/index.ha:113-119.
|
|
||||||
match (strings.index("hello world!", "hello")) {
|
|
||||||
case let i: i32 => { assert(!(i != 0)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
match (strings.index("hello world!", "world")) {
|
|
||||||
case let i: i32 => { assert(!(i != 6)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
match (strings.index("hello world!", "orld!")) {
|
|
||||||
case let i: i32 => { assert(!(i != 7)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
// Multibyte haystack + str needle: "ちは" at rune index 3
|
|
||||||
// in "こんにちは" (byteindex returns 9, rune-index is 3).
|
|
||||||
match (strings.index("こんにちは", "ちは")) {
|
|
||||||
case let i: i32 => { assert(!(i != 3)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
// rune-arm.
|
|
||||||
match (strings.index("hello", 'l')) {
|
|
||||||
case let i: i32 => { assert(!(i != 2)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
match (strings.index("hello world", 'w')) {
|
|
||||||
case let i: i32 => { assert(!(i != 6)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
// Multibyte rune: 'é' U+00E9 at rune 1 in "héllo" — pins
|
|
||||||
// rune-index vs byte-index (byteindex returns 1; rune-index is
|
|
||||||
// 1 also — but the str-arm's 1405 row covers the distinction).
|
|
||||||
match (strings.index("héllo", 0xE9u32: rune)) {
|
|
||||||
case let i: i32 => { assert(!(i != 1)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
// Multibyte rune in multibyte haystack: 'ち' U+3061 at rune 3
|
|
||||||
// in "こんにちは" (byteindex returns 9, rune-index is 3).
|
|
||||||
match (strings.index("こんにちは", 0x3061u32: rune)) {
|
|
||||||
case let i: i32 => { assert(!(i != 3)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
match (strings.index("こんにちは", 'q')) {
|
|
||||||
case let i: i32 => { abort(); };
|
|
||||||
case void => void;
|
|
||||||
};
|
|
||||||
// str-arm: rune-index ≠ byte-index again, mid-string match.
|
|
||||||
// "あった" starts at rune 2 (byte 6) in "またあったね"
|
|
||||||
// (each kana is 3 bytes; ref/hare/strings/index.ha:60). Pins
|
|
||||||
// the dual-iterator walk against the discarded byteindex-and-walk
|
|
||||||
// shape (#10).
|
|
||||||
match (strings.index("またあったね", "あった")) {
|
|
||||||
case let i: i32 => { assert(!(i != 2)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
// str-arm: tail-anchored multibyte needle. "は" is at rune 4
|
|
||||||
// (byte 12) in "こんにちは".
|
|
||||||
match (strings.index("こんにちは", "は")) {
|
|
||||||
case let i: i32 => { assert(!(i != 4)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
// Empty needle hits at rune 0 — Hare's `index_string` falls into
|
|
||||||
// the `needle_rune is done` branch on the very first inner step
|
|
||||||
// (ref/hare/strings/index.ha:70).
|
|
||||||
match (strings.index("hello", "")) {
|
|
||||||
case let i: i32 => { assert(!(i != 0)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
match (strings.index("", "")) {
|
|
||||||
case let i: i32 => { assert(!(i != 0)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
// Empty haystack, non-empty needle — absent.
|
|
||||||
match (strings.index("", "x")) {
|
|
||||||
case let i: i32 => { abort(); };
|
|
||||||
case void => void;
|
|
||||||
};
|
|
||||||
// Multibyte haystack, multibyte absent needle — exercises the
|
|
||||||
// inner-loop mismatch-break across runes (#10, Hare row
|
|
||||||
// ref/hare/strings/index.ha:119).
|
|
||||||
match (strings.index("こんにちは", "きょうは")) {
|
|
||||||
case let i: i32 => { abort(); };
|
|
||||||
case void => void;
|
|
||||||
};
|
|
||||||
// Self-match: haystack == needle, Hare row index.ha:113.
|
|
||||||
match (strings.index("hello", "hello")) {
|
|
||||||
case let i: i32 => { assert(!(i != 0)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// ---- rindex -----------------------------------------------------------
|
|
||||||
// ref/hare/strings/index.ha:22. Symmetric: last-occurrence rune index.
|
|
||||||
|
|
||||||
@test fn rindex_cases() void = {
|
|
||||||
// str-arm.
|
|
||||||
match (strings.rindex("hello", "lo")) {
|
|
||||||
case let i: i32 => { assert(!(i != 3)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
// Hare vector at ref/hare/strings/index.ha:122.
|
|
||||||
match (strings.rindex("hello world!", "o")) {
|
|
||||||
case let i: i32 => { assert(!(i != 7)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
match (strings.rindex("hello", "world")) {
|
|
||||||
case let i: i32 => { abort(); };
|
|
||||||
case void => void;
|
|
||||||
};
|
|
||||||
// Multibyte: last "た" in "またあったね" — rbyteindex returns
|
|
||||||
// 12, rune-index is 4 (ま=0 た=1 あ=2 っ=3 た=4 ね=5).
|
|
||||||
match (strings.rindex("またあったね", "た")) {
|
|
||||||
case let i: i32 => { assert(!(i != 4)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
// rune-arm.
|
|
||||||
match (strings.rindex("hello", 'l')) {
|
|
||||||
case let i: i32 => { assert(!(i != 3)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
match (strings.rindex("aaaaa", 'a')) {
|
|
||||||
case let i: i32 => { assert(!(i != 4)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
// Multibyte rune: 'に' U+306B at rune 2 in "こんにちは".
|
|
||||||
match (strings.rindex("こんにちは", 0x306Bu32: rune)) {
|
|
||||||
case let i: i32 => { assert(!(i != 2)); };
|
|
||||||
case void => { abort(); };
|
|
||||||
};
|
|
||||||
match (strings.rindex("hello", 'z')) {
|
|
||||||
case let i: i32 => { abort(); };
|
|
||||||
case void => void;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// ---- trimprefix / trimsuffix ------------------------------------------
|
// ---- trimprefix / trimsuffix ------------------------------------------
|
||||||
// ref/hare/strings/trim.ha:99-107.
|
// ref/hare/strings/trim.ha:99-107.
|
||||||
|
|
||||||
@@ -687,18 +399,6 @@ fn streq(a: str, b: str) bool = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// ---- compare ----------------------------------------------------------
|
|
||||||
// ref/hare/strings/compare.ha:16.
|
|
||||||
|
|
||||||
@test fn compare_cases() void = {
|
|
||||||
assert(!(strings.compare("ABC", "ABC") != 0));
|
|
||||||
assert(!(strings.compare("ABC", "AB") <= 0));
|
|
||||||
assert(!(strings.compare("AB", "ABC") >= 0));
|
|
||||||
assert(!(strings.compare("BCD", "ABC") <= 0));
|
|
||||||
assert(!(strings.compare("ABC", "abc") >= 0));
|
|
||||||
assert(!(strings.compare("ABC", "こんにちは") >= 0));
|
|
||||||
};
|
|
||||||
|
|
||||||
// ---- sub / bytesub ----------------------------------------------------
|
// ---- sub / bytesub ----------------------------------------------------
|
||||||
// ref/hare/strings/sub.ha:44 (@test fn sub), :79 (@test fn bytesub). Hare's
|
// ref/hare/strings/sub.ha:44 (@test fn sub), :79 (@test fn bytesub). Hare's
|
||||||
// 2-arg `sub(s, start)` rows are omitted: ww has no default-parameter
|
// 2-arg `sub(s, start)` rows are omitted: ww has no default-parameter
|
||||||
|
|||||||
33
lib/strings/suffix_test.ww
Normal file
33
lib/strings/suffix_test.ww
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// suffixtest — exercises strings.hasprefix/hassuffix. A failing row
|
||||||
|
// aborts via the assert/abort builtin (task #5 @test conversion).
|
||||||
|
// Vectors mirror ref/hare/strings/suffix.ha.
|
||||||
|
|
||||||
|
package strings_test;
|
||||||
|
|
||||||
|
import strings;
|
||||||
|
|
||||||
|
// ref/hare/strings/suffix.ha:18.
|
||||||
|
|
||||||
|
@test fn hasprefix_cases() void = {
|
||||||
|
assert(!(!strings.hasprefix("hello world", "hello")));
|
||||||
|
assert(!(!strings.hasprefix("hello world", 'h')));
|
||||||
|
assert(!( strings.hasprefix("hello world", "world")));
|
||||||
|
assert(!( strings.hasprefix("hello world", 'q')));
|
||||||
|
assert(!(!strings.hasprefix("hello", "hello"))); // equal-len
|
||||||
|
assert(!(!strings.hasprefix("anything", ""))); // empty prefix
|
||||||
|
assert(!( strings.hasprefix("", "x")));
|
||||||
|
// multibyte rune prefix — '\'é\'' literal blocked by single-byte
|
||||||
|
// lexrune (lib/ww/lex/lex.ww:659); pass codepoint directly.
|
||||||
|
assert(!(!strings.hasprefix("éclat", 0xE9u32: rune)));
|
||||||
|
assert(!(!strings.hasprefix("🦀rust", 0x1F980u32: rune)));
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/strings/suffix.ha:36.
|
||||||
|
|
||||||
|
@test fn hassuffix_cases() void = {
|
||||||
|
assert(!(!strings.hassuffix("hello world", "world")));
|
||||||
|
assert(!(!strings.hassuffix("hello world", 'd')));
|
||||||
|
assert(!( strings.hassuffix("hello world", "hello")));
|
||||||
|
assert(!( strings.hassuffix("hello world", 'h')));
|
||||||
|
assert(!(!strings.hassuffix("café", 0xE9u32: rune))); // multibyte
|
||||||
|
};
|
||||||
@@ -43,7 +43,7 @@ import time;
|
|||||||
def MID: i32 = 0;
|
def MID: i32 = 0;
|
||||||
def MDIVERGE: i32 = 1;
|
def MDIVERGE: i32 = 1;
|
||||||
def MWWREJECT: i32 = 2;
|
def MWWREJECT: i32 = 2;
|
||||||
def NENTEXPECT: i32 = 47;
|
def NENTEXPECT: i32 = 51;
|
||||||
|
|
||||||
type ent = struct {
|
type ent = struct {
|
||||||
fixture: str, // repo-relative .ww; "" -> probe entry
|
fixture: str, // repo-relative .ww; "" -> probe entry
|
||||||
@@ -83,7 +83,7 @@ fn pri(p: str, inc: str, sentinel: str, moddir: str) ent = {
|
|||||||
return e;
|
return e;
|
||||||
};
|
};
|
||||||
|
|
||||||
// The 46-unit roster. Graduation history lives in git (the retired C
|
// The 51-unit roster. Graduation history lives in git (the retired C
|
||||||
// carrier's table comments); cites are kept only where a non-ID pin
|
// carrier's table comments); cites are kept only where a non-ID pin
|
||||||
// would need them.
|
// would need them.
|
||||||
fn corpus() []ent = {
|
fn corpus() []ent = {
|
||||||
@@ -110,6 +110,10 @@ fn corpus() []ent = {
|
|||||||
append(es, fx("lib/strconv/test/stof_test.ww"));
|
append(es, fx("lib/strconv/test/stof_test.ww"));
|
||||||
append(es, fx("lib/strconv/test/int_test.ww"));
|
append(es, fx("lib/strconv/test/int_test.ww"));
|
||||||
append(es, fx("lib/strings/strings_test.ww"));
|
append(es, fx("lib/strings/strings_test.ww"));
|
||||||
|
append(es, fx("lib/strings/suffix_test.ww"));
|
||||||
|
append(es, fx("lib/strings/contains_test.ww"));
|
||||||
|
append(es, fx("lib/strings/index_test.ww"));
|
||||||
|
append(es, fx("lib/strings/compare_test.ww"));
|
||||||
append(es, fx("lib/temp/temp_test.ww"));
|
append(es, fx("lib/temp/temp_test.ww"));
|
||||||
append(es, fx("lib/time/arithm_test.ww"));
|
append(es, fx("lib/time/arithm_test.ww"));
|
||||||
append(es, fx("lib/time/duration_test.ww"));
|
append(es, fx("lib/time/duration_test.ww"));
|
||||||
|
|||||||
Reference in New Issue
Block a user