// Vectors mirror ref/hare/strings/replace.ha where ww can express them. // External strings_test package per the Go foo_test idiom (rule-5/9 // carve-out, task #16); the shared streq helper lives in // helpers_test.ww — same-package test files compose in dir-mode // (the package, not the file, is the unit of testing). package strings_test; import strings; import os; import test; // ref/hare/strings/replace.ha:46 (#4). Hare rows 1-5 (target found // once / multiple times / shorter / longer / multibyte) plus ww rows // (no-match returns fresh copy, empty-replacement, result-shrinks-to- // empty). @test fn replace_cases() void = { // Hare row 1: target found once. match (strings.replace("Hello world!", "world", "there")) { case let s: str => { assert(!(!streq(s, "Hello there!"))); os.free(s.ptr: *void, s.len: u64); }; case nomem => { abort(); }; }; // Hare row 2: needle found four times (replacement same length). match (strings.replace("I like dogs, dogs, birds, dogs", "dogs", "cats")) { case let s: str => { assert(!(!streq(s, "I like cats, cats, birds, cats"))); os.free(s.ptr: *void, s.len: u64); }; case nomem => { abort(); }; }; // Hare row 3: replacement shorter than needle, non-overlapping // matches (Hare advances by needle.len, not 1). match (strings.replace("aaaaaa", "aa", "a")) { case let s: str => { assert(!(!streq(s, "aaa"))); os.free(s.ptr: *void, s.len: u64); }; case nomem => { abort(); }; }; // Hare row 4: replacement longer than needle. match (strings.replace("aaa", "a", "aa")) { case let s: str => { assert(!(!streq(s, "aaaaaa"))); os.free(s.ptr: *void, s.len: u64); }; case nomem => { abort(); }; }; // Hare row 5: multibyte UTF-8 (3-byte runes). match (strings.replace("こんにちは", "にち", "ばん")) { case let s: str => { assert(!(!streq(s, "こんばんは"))); os.free(s.ptr: *void, s.len: u64); }; case nomem => { abort(); }; }; // ww row 1: needle not found — fresh copy (distinct from // borrowed input ptr). let src: str = "hello world"; match (strings.replace(src, "xyz", "abc")) { case let s: str => { assert(!(!streq(s, "hello world"))); assert(!(s.ptr == src.ptr)); os.free(s.ptr: *void, s.len: u64); }; case nomem => { abort(); }; }; // ww row 2: empty replacement — needle is removed. match (strings.replace("foo-bar-baz", "-", "")) { case let s: str => { assert(!(!streq(s, "foobarbaz"))); os.free(s.ptr: *void, s.len: u64); }; case nomem => { abort(); }; }; // ww row 3: result shrinks to empty — total==0 nil-alloc path. match (strings.replace("aaaa", "aa", "")) { case let s: str => { assert(!(!streq(s, ""))); assert(!(s.len != 0)); }; case nomem => { abort(); }; }; // ww row 4: multibyte single-rune replace — every "に" → "X". match (strings.replace("こんにちは", "に", "X")) { case let s: str => { assert(!(!streq(s, "こんXちは"))); os.free(s.ptr: *void, s.len: u64); }; case nomem => { abort(); }; }; // ww row 5: empty input — returns empty. match (strings.replace("", "x", "y")) { case let s: str => { assert(!(s.len != 0)); }; case nomem => { abort(); }; }; }; @test fn replace_empty_needle_aborts() void = { test.expectabort(); strings.replace("abc", "", "x"); };