Files
ww/lib/strings/pad_test.ww
Hojun-Cho 51cb8e7d64 lib: split strings tests per ref/hare/strings families + helpers file
Unblocked by the package-mode ruling: same-package test files now
compose everywhere (coordinator dir-mode owns test-library and the
libbyteid dir entry owns byte identity), so the per-file standalone
constraint is gone and Go's stdlib layout applies directly. The
15-banner strings_test.ww monolith dissolves into per-ref-file
family files — dup, concat, trim, sub, utf8, iter, tokenize
(tokenize+splitn+cut, all of ref tokenize.ha), pad, replace — plus
helpers_test.ww holding exactly the one cross-family helper (streq),
Go's shared test-helper idiom. Pure moves; family-local helpers
(expect_str_token et al.) stay in their family file. Leading banners
that merely restated the filename dropped; 6 interior sub-family
boundaries remain. lib/ census 141 -> 132.

libbyteid gains the dx() dir-mode entry form decided in B2: compose
every *_test.ww under moddir exactly as pkgcombined does
(module-reset separators, byte-lex order) and byte-id the composed
root through both driver stages. The five per-file strings fx
entries retire into one dx("lib/strings") — every source line they
covered is inside the composed unit, and the completeness scan still
keys the module. NENTEXPECT 60 -> 56.

Recorded, not split: fmt_test's 13 banners are scenario groups over
the one fprint surface and ref/hare/fmt itself keeps a single
+test.ha — a print/wrappers split would fight the ref; memio stays
per the standing ruling (everything ww ports lives in ref
stream.ha).
2026-08-08 19:42:49 +09:00

108 lines
3.8 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Vectors mirror ref/hare/strings/pad.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;
// ref/hare/strings/pad.ha:23 (@test fn lpad), :54 (@test fn rpad). Hare's
// row triple (s<maxlen, s==maxlen, s=="" empty) is mirrored; ww extras
// pin s>maxlen (early return path), multibyte s with byte-length
// counting, and multibyte pad rune (encoded width >1 byte). The
// multibyte-pad rows pin Hare's `[..maxlen]` slice contract — a
// multibyte trailing pad may be truncated mid-codepoint, exactly as
// Hare does.
@test fn lpad_cases() void = {
// Hare row 1: shorter s, ASCII pad.
let r1: str = strings.lpad("2", '0', 5);
assert(!(!streq(r1, "00002")));
assert(!(r1.len != 5));
os.free(r1.ptr: *void, r1.len: u64);
// Hare row 2: s.len == maxlen — early dup path.
let r2: str = strings.lpad("12345", '0', 5);
assert(!(!streq(r2, "12345")));
assert(!(r2.len != 5));
os.free(r2.ptr: *void, r2.len: u64);
// Hare row 3: empty s, full-width pad.
let r3: str = strings.lpad("", '0', 5);
assert(!(!streq(r3, "00000")));
assert(!(r3.len != 5));
os.free(r3.ptr: *void, r3.len: u64);
// ww row 1: s.len > maxlen — early dup path returns input copy.
let r4: str = strings.lpad("abcdef", '_', 3);
assert(!(!streq(r4, "abcdef")));
assert(!(r4.len != 6));
os.free(r4.ptr: *void, r4.len: u64);
// ww row 2: multibyte s — byte-length contract (Hare `len(s)`).
// "café" is 5 bytes; maxlen 7 → 2 pad bytes prepended.
let r5: str = strings.lpad("café", '_', 7);
assert(!(!streq(r5, "__café")));
assert(!(r5.len != 7));
os.free(r5.ptr: *void, r5.len: u64);
// ww row 3: multibyte pad rune. 'α' U+03B1 is 2 bytes (0xCE 0xB1).
// s="x" (1 byte), maxlen=5 → npads = 5 - 1 = 4, pad_bytes=2,
// total writes = 4*2 + 1 = 9 bytes; result `[..5]` = "αα" (4 bytes)
// + 0xCE (truncated leading byte of next α) — exactly Hare's slice.
let r6: str = strings.lpad("x", 0x03B1u32: rune, 5);
assert(!(r6.len != 5));
assert(!(r6.ptr[0] != 0xCEu8)); // α byte 0
assert(!(r6.ptr[1] != 0xB1u8)); // α byte 1
assert(!(r6.ptr[2] != 0xCEu8)); // α byte 0
assert(!(r6.ptr[3] != 0xB1u8)); // α byte 1
assert(!(r6.ptr[4] != 0xCEu8)); // truncated α byte 0
os.free(r6.ptr: *void, r6.len: u64);
};
@test fn rpad_cases() void = {
// Hare row 1: shorter s, ASCII pad.
let r1: str = strings.rpad("2", '0', 5);
assert(!(!streq(r1, "20000")));
assert(!(r1.len != 5));
os.free(r1.ptr: *void, r1.len: u64);
// Hare row 2: s.len == maxlen — early dup path.
let r2: str = strings.rpad("12345", '0', 5);
assert(!(!streq(r2, "12345")));
assert(!(r2.len != 5));
os.free(r2.ptr: *void, r2.len: u64);
// Hare row 3: empty s.
let r3: str = strings.rpad("", '0', 5);
assert(!(!streq(r3, "00000")));
assert(!(r3.len != 5));
os.free(r3.ptr: *void, r3.len: u64);
// ww row 1: s.len > maxlen — early dup path.
let r4: str = strings.rpad("abcdef", '_', 3);
assert(!(!streq(r4, "abcdef")));
assert(!(r4.len != 6));
os.free(r4.ptr: *void, r4.len: u64);
// ww row 2: multibyte s — byte-length contract.
let r5: str = strings.rpad("café", '_', 7);
assert(!(!streq(r5, "café__")));
assert(!(r5.len != 7));
os.free(r5.ptr: *void, r5.len: u64);
// ww row 3: multibyte pad rune (2-byte 'α'). s="x" (1 byte),
// maxlen=5 → "xαα" appended is 1 + 4 = 5 bytes (no truncation).
let r6: str = strings.rpad("x", 0x03B1u32: rune, 5);
assert(!(r6.len != 5));
assert(!(r6.ptr[0] != 'x'));
assert(!(r6.ptr[1] != 0xCEu8)); // α byte 0
assert(!(r6.ptr[2] != 0xB1u8)); // α byte 1
assert(!(r6.ptr[3] != 0xCEu8)); // α byte 0
assert(!(r6.ptr[4] != 0xB1u8)); // α byte 1
os.free(r6.ptr: *void, r6.len: u64);
};