lib/test: Go external-test packages (<mod>_test); retire decimaltest (#16 PREP-a)
30 lib test files: package <mod> -> <mod>_test, the sanctioned Go-over-Hare departure (CLAUDE.md rule-9 carve-out; white-box testing deferred, not forbidden). decimaltest retired per .ai/rob-16-decimaltest-ruling.md: Hare ships no decimal_test.ha (engine covered transitively); coverage migrated losslessly into stoftest rows (all-9s carry, nd>19 pure-decimal) + ftostest f64_roundtrip mirroring ref/hare strconv ftos_test.ha tcsf64; k=0 micro-gap documented at site. regex_test keeps its white-box internal probes under a documented rule-7 divergence (rehome = task #9). 922_decimal_run + its 989 byte-id row removed with the fixture.
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
6. ref/hare and ref/plan9front are read-only references — consult before inventing data shapes or syntax
|
||||
7. No workarounds. If a bug forces a workaround, STOP and report with a precise repro. Document any retained divergence at the site with a pointer to the filed task. Never silent.
|
||||
8. Comments are WHY-only. Never narrate WHAT the code does — names carry the WHAT. Comment only non-obvious WHY: a constraint, a divergence from a reference, a citation to a filed task.
|
||||
9. Hare-fidelity over convenience. No ad-hoc extensions, renames, or convenience wrappers in lib/. Cite ref/hare/<module>/<file> for every signature ported. Carve-out: the Hare `_unsafe` suffix convention is dropped wholesale (ww is C/Plan-9-lineage, an unmanaged systems language — no GC, no "safe" baseline to be unsafe relative to). bytes→str is a pure reinterpret (`strings.frombytes`, renamed from Hare's `fromutf8_unsafe`); validation is opt-in via `utf8.validate(b)?` at the IO source, never wrapped per-construction. Rationale: ref/hare/strings/utf8.ha:10,22 — the suffix flags Hare's managed-bytes-safety axis, which ww doesn't have. The honest name (`frombytes`) reserves `fromutf8` for a future true validating helper.
|
||||
9. Hare-fidelity over convenience. No ad-hoc extensions, renames, or convenience wrappers in lib/. Cite ref/hare/<module>/<file> for every signature ported. Carve-out: the Hare `_unsafe` suffix convention is dropped wholesale (ww is C/Plan-9-lineage, an unmanaged systems language — no GC, no "safe" baseline to be unsafe relative to). bytes→str is a pure reinterpret (`strings.frombytes`, renamed from Hare's `fromutf8_unsafe`); validation is opt-in via `utf8.validate(b)?` at the IO source, never wrapped per-construction. Rationale: ref/hare/strings/utf8.ha:10,22 — the suffix flags Hare's managed-bytes-safety axis, which ww doesn't have. The honest name (`frombytes`) reserves `fromutf8` for a future true validating helper. Carve-out: lib/ tests use Go's external-test-package idiom (`package <mod>_test;` + `import <mod>;`), a sanctioned Go-over-Hare departure — ww hard-rejects self-import (full Go), and Hare's same-package `@test` colocation has no ww analogue yet (in-package white-box testing is deferred pending tasks #5/#8, not forbidden). Rule 5/9 binds lib/ *API* surface, not test-authoring style. Ref: Go `foo_test` convention; task #16.
|
||||
10. Symmetric stages. cstage and wwstage MUST emit byte-identical asm for the same input. When inference power differs, align the richer side DOWN to the leaner side, not the other way.
|
||||
11. Split commits when they bundle unrelated concerns. Bisect-cleanliness is the default. Multi-fix commits need a body paragraph explaining why they couldn't split.
|
||||
12. Simple data, simple algorithms. Sea-of-stars style. Mirror Hare's structural choices over clever alternatives.
|
||||
|
||||
6
Makefile
6
Makefile
@@ -475,7 +475,7 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \
|
||||
$(BIN)/test_path_run \
|
||||
$(BIN)/test_letshadow_run \
|
||||
$(BIN)/test_strglobeq_run \
|
||||
$(BIN)/test_decimal_run $(BIN)/test_strconv_int_run \
|
||||
$(BIN)/test_strconv_int_run \
|
||||
$(BIN)/test_stof_run $(BIN)/test_ftos_run \
|
||||
$(BIN)/test_memio_run $(BIN)/test_temp_run $(BIN)/test_getopt_run \
|
||||
$(BIN)/test_errno_run \
|
||||
@@ -2203,10 +2203,6 @@ $(BIN)/test_nkname_run: test/wcc/905_nkname_run.c $(BIN)/ww $(BIN)/w6c \
|
||||
$(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_decimal_run: test/wcc/922_decimal_run.c $(BIN)/ww $(BIN)/w6c \
|
||||
$(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_strconv_int_run: test/wcc/922_strconv_int_run.c $(BIN)/ww $(BIN)/w6c \
|
||||
$(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//
|
||||
// Vectors mirror Hare's @test fn strcasecmp in ref/hare/ascii/string.ha.
|
||||
|
||||
package ascii;
|
||||
package ascii_test;
|
||||
|
||||
import ascii;
|
||||
import os;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// surface — `let st = memio.fixed(buf); &st.vt` into the scanner/stream
|
||||
// init, io.read/write/close return size/io.error.
|
||||
|
||||
package bufio;
|
||||
package bufio_test;
|
||||
|
||||
import bufio;
|
||||
import bytes;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// Vectors mirror Hare's @test fns in ref/hare/bytes/equal.ha,
|
||||
// ref/hare/bytes/index.ha, ref/hare/bytes/contains.ha.
|
||||
|
||||
package bytes;
|
||||
package bytes_test;
|
||||
|
||||
import bytes;
|
||||
import os;
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
// Don't `use io;` here — lib/os and lib/io collide on read/write/
|
||||
// close (task #17). We need only os.getenv + os.exit.
|
||||
|
||||
package dirs;
|
||||
package dirs_test;
|
||||
|
||||
import dirs;
|
||||
import os;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package base32;
|
||||
package base32_test;
|
||||
|
||||
import base32;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
// The streaming decoder (newdecoder) is deferred (#247-sibling), so the
|
||||
// decode side is exercised through decodestr only.
|
||||
|
||||
package base64;
|
||||
package base64_test;
|
||||
|
||||
import base64;
|
||||
import bytes;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
// The streaming decoder (newdecoder) is deferred (#247), so the decode
|
||||
// side is exercised through decodestr only.
|
||||
|
||||
package hex;
|
||||
package hex_test;
|
||||
|
||||
import bytes;
|
||||
import errors;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// store gap (task #6). [[errtag]] collapses each named-void variant to
|
||||
// a small int so the row body is a single integer compare.
|
||||
|
||||
package errors;
|
||||
package errors_test;
|
||||
|
||||
import errors;
|
||||
import os;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
// here threads a different variadic argument-pack into fprint, and the
|
||||
// variadic shape can't be table-driven within a single fn body.
|
||||
|
||||
package fmt;
|
||||
package fmt_test;
|
||||
|
||||
import fmt;
|
||||
import io;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// reports `WEXITSTATUS = 11..N` pointing at the failing scenario.
|
||||
// Same convention as lib/log/logtest.
|
||||
|
||||
package fnmatch;
|
||||
package fnmatch_test;
|
||||
|
||||
import fnmatch;
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
// per task #15 — but the nested-if shape was the original workaround
|
||||
// and is preserved here as a regression marker.
|
||||
|
||||
package getopt;
|
||||
package getopt_test;
|
||||
|
||||
import getopt;
|
||||
import io;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package adler32;
|
||||
package adler32_test;
|
||||
|
||||
import adler32;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package crc16;
|
||||
package crc16_test;
|
||||
|
||||
import crc16;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package crc32;
|
||||
package crc32_test;
|
||||
|
||||
import crc32;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package crc64;
|
||||
package crc64_test;
|
||||
|
||||
import crc64;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package siphash;
|
||||
package siphash_test;
|
||||
|
||||
import siphash;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
// killing the test driver — left as a TODO until the project grows
|
||||
// a subprocess fixture.
|
||||
|
||||
package log;
|
||||
package log_test;
|
||||
|
||||
import fmt;
|
||||
import io;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package random;
|
||||
package random_test;
|
||||
|
||||
import random;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
// value-return surface (`let st = memio.fixed(buf); &st.vt` into the
|
||||
// io dispatchers; io.read/write/close return size/io.error).
|
||||
|
||||
package memio;
|
||||
package memio_test;
|
||||
|
||||
import bytes;
|
||||
import errors;
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
// concat (task #17). os alone is sufficient: getenv + exit are the
|
||||
// only primitives needed.
|
||||
|
||||
package os;
|
||||
package os_test;
|
||||
|
||||
import os;
|
||||
import rt;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
// + 10` tells the harness which row tripped. Same shape as
|
||||
// ostest / temptest / shlextest.
|
||||
|
||||
package os;
|
||||
package os_test;
|
||||
|
||||
import os;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// the table holds {segment, expected-string} pairs (stack.ha:36-42 is
|
||||
// the normalization spec these rows encode).
|
||||
|
||||
package path;
|
||||
package path_test;
|
||||
|
||||
import path;
|
||||
import os;
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
// not `regex.inst_charset { … }`): the parser rejects a module-qualified
|
||||
// name in struct-literal position (#29), and the imported type
|
||||
// is in scope unqualified.
|
||||
package regex;
|
||||
package regex_test;
|
||||
|
||||
import regex;
|
||||
import io;
|
||||
@@ -167,6 +167,8 @@ fn fail() void = { os.exit(signalled + 10); };
|
||||
regex.finish(&re);
|
||||
};
|
||||
|
||||
// rule-7 divergence: white-box probes of regex internals; mislabeled _test pending task #9 (in-pkg test harness via #5/-T dir-enum, #8 boundary).
|
||||
|
||||
// compile("abc") emits the 5-inst literal program: the leading
|
||||
// unanchored inst_skip (regex.ha:261-263), one inst_lit per rune, the
|
||||
// epilogue inst_match(false) (ha:475-477). compile()'s
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
// reports `WEXITSTATUS = 11..N` pointing at the failing scenario.
|
||||
// Same convention as fnmatchtest / logtest.
|
||||
|
||||
package shlex;
|
||||
package shlex_test;
|
||||
|
||||
import shlex;
|
||||
import io;
|
||||
|
||||
@@ -1,212 +0,0 @@
|
||||
// decimaltest — exercises lib/strconv/decimal.ww (Hare decimal.ha port).
|
||||
// Run with `out/bin/ww run lib/strconv/test/decimaltest.ww`. Same
|
||||
// signalled-then-fail()-with-+10 pattern as bytestest / hex / utf8.
|
||||
// Non-zero exit pinpoints the failing scenario.
|
||||
//
|
||||
// Hare has no decimal_test.ha (decimal is exercised transitively via
|
||||
// ftos_test.ha). ww-authored probes per
|
||||
// feedback_test_match_hare_source ("ww-authored where Hare doesn't
|
||||
// ship direct tests"). Coverage per Drew design pre-flight:
|
||||
// trim (RHS-zeros), decimal_shift (k=0/k>0/k<0/|k|>maxshift),
|
||||
// leftshift (small/edge), rightshift (small/break-out), round (at
|
||||
// various dp), decimal_round (small + dp>18 + dp<0).
|
||||
//
|
||||
// Lives in lib/strconv/test/ (not lib/strconv/) so `import strconv`
|
||||
// resolves to the lib/strconv DIRECTORY rather than shadowing on
|
||||
// the strconv.ww FILE — pulls in decimal.ww + stof_data.ww +
|
||||
// strconv.ww as the unified `package strconv` compilation.
|
||||
|
||||
package strconv;
|
||||
|
||||
import strconv;
|
||||
import os;
|
||||
|
||||
let signalled: i32 = 0;
|
||||
fn fail() void = { os.exit(signalled + 10); };
|
||||
|
||||
// Seeds `d` with `vals` as the leading digits + `dp` as the decimal
|
||||
// point, leaving negative/truncated false. Each test starts from a
|
||||
// known state.
|
||||
fn seed(d: *decimal, vals: []u8, dp: i32) void = {
|
||||
let SZ_ONE: size = (1u64: size);
|
||||
let i: size = (0u64: size);
|
||||
for (i < (vals.len: size)) {
|
||||
d.digits[i] = vals[i];
|
||||
i += SZ_ONE;
|
||||
};
|
||||
d.nd = (vals.len: size);
|
||||
d.dp = dp;
|
||||
};
|
||||
|
||||
// ---- trim -------------------------------------------------------------
|
||||
// ref/hare/strconv/decimal.ha:29.
|
||||
|
||||
@test fn trim_cases() void = {
|
||||
let d: decimal;
|
||||
let vals: [5]u8;
|
||||
vals[0] = 1u8; vals[1] = 2u8; vals[2] = 3u8; vals[3] = 0u8; vals[4] = 0u8;
|
||||
seed(&d, vals[0:5], 3);
|
||||
trim(&d);
|
||||
if (d.nd != (3u64: size)) { fail(); };
|
||||
if (d.dp != 3) { fail(); };
|
||||
|
||||
// all-zero case: nd shrinks to 0.
|
||||
let zeros: [3]u8;
|
||||
seed(&d, zeros[0:3], 2);
|
||||
trim(&d);
|
||||
if (d.nd != (0u64: size)) { fail(); };
|
||||
};
|
||||
|
||||
// ---- leftshift / leftshift_newdigits ---------------------------------
|
||||
// ref/hare/strconv/decimal.ha:35,57.
|
||||
|
||||
@test fn leftshift_small_cases() void = {
|
||||
// Shift "5" by 1 → "10" → trim collapses to nd=1 with dp+=1 (Hare
|
||||
// trims trailing zero of the high-shifted byte).
|
||||
let d: decimal;
|
||||
d.digits[0] = 5u8;
|
||||
d.nd = (1u64: size);
|
||||
d.dp = 1;
|
||||
leftshift(&d, 1u32);
|
||||
if (d.dp != 2) { fail(); };
|
||||
if (d.digits[0] != 1u8) { fail(); };
|
||||
// post-trim nd is 1 (trailing 0 stripped).
|
||||
if (d.nd != (1u64: size)) { fail(); };
|
||||
|
||||
// Shift "123" by 1 → "246" (no trim — no trailing zero).
|
||||
let v3: [3]u8;
|
||||
v3[0] = 1u8; v3[1] = 2u8; v3[2] = 3u8;
|
||||
seed(&d, v3[0:3], 3);
|
||||
leftshift(&d, 1u32);
|
||||
if (d.nd != (3u64: size)) { fail(); };
|
||||
if (d.dp != 3) { fail(); };
|
||||
if (d.digits[0] != 2u8) { fail(); };
|
||||
if (d.digits[1] != 4u8) { fail(); };
|
||||
if (d.digits[2] != 6u8) { fail(); };
|
||||
|
||||
// nd == 0 no-op (early-return arm of leftshift).
|
||||
let z: decimal;
|
||||
leftshift(&z, 1u32);
|
||||
if (z.nd != (0u64: size)) { fail(); };
|
||||
};
|
||||
|
||||
// ---- rightshift -----------------------------------------------------
|
||||
// ref/hare/strconv/decimal.ha:93.
|
||||
|
||||
@test fn rightshift_small_cases() void = {
|
||||
// Shift "10" (dp=2) right by 1 → "5" (dp=1).
|
||||
let d: decimal;
|
||||
let vals: [2]u8;
|
||||
vals[0] = 1u8; vals[1] = 0u8;
|
||||
seed(&d, vals[0:2], 2);
|
||||
rightshift(&d, 1u32);
|
||||
if (d.nd != (1u64: size)) { fail(); };
|
||||
if (d.dp != 1) { fail(); };
|
||||
if (d.digits[0] != 5u8) { fail(); };
|
||||
};
|
||||
|
||||
// ---- decimal_shift --------------------------------------------------
|
||||
// ref/hare/strconv/decimal.ha:138.
|
||||
|
||||
@test fn decimal_shift_cases() void = {
|
||||
// k=0 no-op.
|
||||
let d: decimal;
|
||||
let v3: [3]u8; v3[0] = 1u8; v3[1] = 2u8; v3[2] = 3u8;
|
||||
seed(&d, v3[0:3], 3);
|
||||
decimal_shift(&d, 0);
|
||||
if (d.nd != (3u64: size)) { fail(); };
|
||||
if (d.dp != 3) { fail(); };
|
||||
if (d.digits[0] != 1u8) { fail(); };
|
||||
|
||||
// k=2 small positive: 123 << 2 = 492 → digits 4,9,2; dp=3.
|
||||
seed(&d, v3[0:3], 3);
|
||||
decimal_shift(&d, 2);
|
||||
if (d.dp != 3) { fail(); };
|
||||
if (d.digits[0] != 4u8) { fail(); };
|
||||
if (d.digits[1] != 9u8) { fail(); };
|
||||
if (d.digits[2] != 2u8) { fail(); };
|
||||
|
||||
// k>maxshift triggers the break-up loop. Exact digits are checked
|
||||
// transitively by stof in fold-4; verify dp grew.
|
||||
let v: [3]u8; v[0] = 1u8; v[1] = 0u8; v[2] = 0u8;
|
||||
seed(&d, v[0:3], 3);
|
||||
decimal_shift(&d, 70);
|
||||
if (d.dp <= 3) { fail(); };
|
||||
};
|
||||
|
||||
// ---- should_round_up + round + decimal_round ------------------------
|
||||
// ref/hare/strconv/decimal.ha:155,162,188.
|
||||
|
||||
@test fn round_cases() void = {
|
||||
// rounddown — "1234" rounded to 2 digits → "12".
|
||||
let d: decimal;
|
||||
let v: [4]u8; v[0] = 1u8; v[1] = 2u8; v[2] = 3u8; v[3] = 4u8;
|
||||
seed(&d, v[0:4], 4);
|
||||
round(&d, (2u32: uint));
|
||||
if (d.nd != (2u64: size)) { fail(); };
|
||||
if (d.digits[0] != 1u8) { fail(); };
|
||||
if (d.digits[1] != 2u8) { fail(); };
|
||||
|
||||
// roundup — "1259" rounded to 2 digits → "13" (digit[2]==5 →
|
||||
// should_round_up, digit[1]=2 → +1 → digit[1]=3).
|
||||
let r: [4]u8; r[0] = 1u8; r[1] = 2u8; r[2] = 5u8; r[3] = 9u8;
|
||||
seed(&d, r[0:4], 4);
|
||||
round(&d, (2u32: uint));
|
||||
if (d.nd != (2u64: size)) { fail(); };
|
||||
if (d.digits[0] != 1u8) { fail(); };
|
||||
if (d.digits[1] != 3u8) { fail(); };
|
||||
|
||||
// roundup with all-9s carry — "999" rounded to 2 digits → "1"
|
||||
// with dp incremented.
|
||||
let nines: [3]u8; nines[0] = 9u8; nines[1] = 9u8; nines[2] = 9u8;
|
||||
seed(&d, nines[0:3], 3);
|
||||
round(&d, (2u32: uint));
|
||||
if (d.digits[0] != 1u8) { fail(); };
|
||||
if (d.nd != (1u64: size)) { fail(); };
|
||||
if (d.dp != 4) { fail(); };
|
||||
};
|
||||
|
||||
@test fn decimal_round_cases() void = {
|
||||
// dp<0 → 0.
|
||||
let d: decimal;
|
||||
let v: [3]u8; v[0] = 1u8; v[1] = 2u8; v[2] = 3u8;
|
||||
seed(&d, v[0:3], -1);
|
||||
if (decimal_round(&d) != 0u64) { fail(); };
|
||||
|
||||
// nd==0 → 0.
|
||||
let z: decimal;
|
||||
z.dp = 5;
|
||||
if (decimal_round(&z) != 0u64) { fail(); };
|
||||
|
||||
// dp>18 → ~0u64 (all-bits-set).
|
||||
let big: decimal;
|
||||
let one: [1]u8; one[0] = 1u8;
|
||||
seed(&big, one[0:1], 19);
|
||||
let zero_u: u64 = 0u64;
|
||||
if (decimal_round(&big) != ~zero_u) { fail(); };
|
||||
|
||||
// "123" with dp=3 → 123.
|
||||
seed(&d, v[0:3], 3);
|
||||
if (decimal_round(&d) != 123u64) { fail(); };
|
||||
|
||||
// "12" with dp=4 → 1200 (multiply by 10 for each missing digit).
|
||||
let two: [2]u8; two[0] = 1u8; two[1] = 2u8;
|
||||
seed(&d, two[0:2], 4);
|
||||
if (decimal_round(&d) != 1200u64) { fail(); };
|
||||
|
||||
// "126" with dp=2 → 13 (rounded up via should_round_up at dp=2:
|
||||
// digit[2]==6 >= 5).
|
||||
let r: [3]u8; r[0] = 1u8; r[1] = 2u8; r[2] = 6u8;
|
||||
seed(&d, r[0:3], 2);
|
||||
if (decimal_round(&d) != 13u64) { fail(); };
|
||||
};
|
||||
|
||||
export fn main() i32 = {
|
||||
signalled = 1; trim_cases();
|
||||
signalled = 2; leftshift_small_cases();
|
||||
signalled = 3; rightshift_small_cases();
|
||||
signalled = 4; decimal_shift_cases();
|
||||
signalled = 5; round_cases();
|
||||
signalled = 6; decimal_round_cases();
|
||||
return 0;
|
||||
};
|
||||
@@ -20,7 +20,7 @@
|
||||
// DIRECTORY (full package), not the strconv.ww FILE — same rationale as
|
||||
// stoftest/decimaltest.
|
||||
|
||||
package strconv;
|
||||
package strconv_test;
|
||||
|
||||
import strconv;
|
||||
import os;
|
||||
@@ -43,6 +43,17 @@ fn chk(n: f64, want: str) bool = {
|
||||
return streq(f64tos(n), want);
|
||||
};
|
||||
|
||||
// rt64 — bit-exact round-trip: render then parse back must reproduce the
|
||||
// exact bits. The parse-back re-runs strconv's decimal slow path.
|
||||
fn rt64(x: f64) bool = {
|
||||
match (stof64(f64tos(x), base.DEC)) {
|
||||
case let v: f64 => { return math.f64bits(v) == math.f64bits(x); };
|
||||
case let e: invalid => { return false; };
|
||||
case let e: overflow => { return false; };
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
// ftos_test.ha:57-64 (G/void/NONE-equivalent) — fixed-point renders.
|
||||
@test fn f64tos_fixed() void = {
|
||||
if (!chk(13.37, "13.37")) { fail(); };
|
||||
@@ -151,6 +162,21 @@ fn chkf32(n: f32, want: str) bool = {
|
||||
if (!chkf32(nan, "nan")) { fail(); };
|
||||
};
|
||||
|
||||
// ftos_test.ha:196-204 (tcsf64) — Hare's f64-exclusive extremes, here as a
|
||||
// bit-exact ROUND-TRIP (f64bits(stof64(f64tos(x)))==f64bits(x)): the parse-
|
||||
// back re-runs strconv's decimal engine end-to-end, preserving the coverage
|
||||
// the retired white-box decimaltest.ww gave directly (#16). Extremes built
|
||||
// via f64frombits (ww math has no F64_MIN_*/MAX_NORMAL const); exact and
|
||||
// 17-digit-halfway values as literals.
|
||||
@test fn f64_roundtrip() void = {
|
||||
if (!rt64(9007199254740991.0)) { fail(); }; // 2^53-1
|
||||
if (!rt64(90071992547409915.0)) { fail(); }; // 17-digit halfway pair
|
||||
if (!rt64(90071992547409925.0)) { fail(); };
|
||||
if (!rt64(math.f64frombits(0x0000000000000001u64))) { fail(); }; // F64_MIN_SUBNORMAL (5e-324)
|
||||
if (!rt64(math.f64frombits(0x0010000000000000u64))) { fail(); }; // F64_MIN_NORMAL
|
||||
if (!rt64(math.f64frombits(0x7FEFFFFFFFFFFFFFu64))) { fail(); }; // F64_MAX_NORMAL
|
||||
};
|
||||
|
||||
export fn main() i32 = {
|
||||
signalled = 1; f64tos_fixed();
|
||||
signalled = 2; f64tos_scientific();
|
||||
@@ -159,6 +185,7 @@ export fn main() i32 = {
|
||||
signalled = 5; f32tos_scientific();
|
||||
signalled = 6; f32tos_extremes();
|
||||
signalled = 7; f32tos_special();
|
||||
signalled = 8; f64_roundtrip();
|
||||
os.exit(0);
|
||||
return 0;
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
// literal form for the expectation side); the original Hare radix form
|
||||
// is noted inline.
|
||||
|
||||
package strconv;
|
||||
package strconv_test;
|
||||
|
||||
import strconv;
|
||||
import os;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// DIRECTORY (full package: strconv.ww + decimal.ww + stof_data.ww +
|
||||
// stof.ww), not the strconv.ww FILE — same rationale as decimaltest.
|
||||
|
||||
package strconv;
|
||||
package strconv_test;
|
||||
|
||||
import strconv;
|
||||
import os;
|
||||
@@ -123,6 +123,18 @@ fn nan32(s: str) bool = {
|
||||
if (!chk64("-1e-324", base.DEC, nzero)) { fail(); };
|
||||
if (!chk64("5e-324", base.DEC, subn)) { fail(); };
|
||||
|
||||
// Decimal-engine edge probes migrated here when the white-box
|
||||
// decimaltest.ww retired (#16): the external-test idiom can't reach
|
||||
// strconv's unexported decimal fns, so these two black-box rows pin
|
||||
// the only edges not already guaranteed transitively. C1: all-9s
|
||||
// round-up carry + decimal_round dp>18 boundary. C2: nd>19 skips
|
||||
// eisel-lemire, forcing the pure-decimal slow path (extra trim).
|
||||
// Micro-gap (rule-7, honest): decimal_shift's k=0 early-return no-op
|
||||
// is a trivial guard, not reached transitively by any slow-path input
|
||||
// — consciously un-migrated, not silently dropped.
|
||||
if (!chk64("9999999999999999", base.DEC, 1.0e16)) { fail(); };
|
||||
if (!chk64("100000000000000000001", base.DEC, 1.0e20)) { fail(); };
|
||||
|
||||
if (inv64("") != 0) { fail(); };
|
||||
if (inv64("0ZO") != 1) { fail(); };
|
||||
if (inv64("1.23ezz") != 5) { fail(); };
|
||||
|
||||
@@ -6,7 +6,11 @@
|
||||
// Vectors mirror ref/hare/strings/{dup,concat,trim,contains,index,
|
||||
// suffix,compare}.ha where ww can express them.
|
||||
|
||||
package strings;
|
||||
// `_test` suffix: Go external-test-package idiom (sanctioned Go-over-Hare
|
||||
// departure for lib/ tests) — self-import is hard-rejected, so the test
|
||||
// lives in package strings_test and pulls the real package via import.
|
||||
// See CLAUDE.md rule-5/9 carve-out; task #16.
|
||||
package strings_test;
|
||||
|
||||
import strings;
|
||||
import encoding.utf8;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// leave detritus under /tmp. `ls /tmp` before/after each run should
|
||||
// match.
|
||||
|
||||
package temp;
|
||||
package temp_test;
|
||||
|
||||
import os;
|
||||
import temp;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// pattern as fmttest / logtest / stattest so a non-zero exit
|
||||
// pinpoints the offending scenario.
|
||||
|
||||
package time;
|
||||
package time_test;
|
||||
|
||||
import os;
|
||||
import time;
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* 922_decimal_run — execute the lib/strconv/test/decimaltest fixture
|
||||
* under the C-side `ww run` driver and assert exit 0.
|
||||
*
|
||||
* Same thin-wrapper shape as 967_bytes_run / 968_utf8_run / 979_hex_run:
|
||||
* decimaltest.ww carries its own `export fn main()` that drives the
|
||||
* @test fns and signals which case failed via the exit code (signalled
|
||||
* + 10, so cases 1..6 → exit 11..16).
|
||||
*
|
||||
* The test fixture lives in lib/strconv/test/ (not lib/strconv/) so
|
||||
* `import strconv` resolves to the lib/strconv DIRECTORY rather than
|
||||
* shadowing on the strconv.ww FILE — same-dir resolution would pull
|
||||
* in only strconv.ww and miss decimal.ww + stof_data.ww. From the
|
||||
* test/ subdir, the search-path lookup falls through srcd and reaches
|
||||
* the libdir's directory hit, expanding the full strconv package.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
|
||||
const char *src = "lib/strconv/test/decimaltest.ww";
|
||||
char path[1024], cmd[2048];
|
||||
snprintf(path, sizeof path, "%s/%s", cwd, src);
|
||||
snprintf(cmd, sizeof cmd, "%s/ww run %s", bin, path);
|
||||
int rc = runwait(cmd);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "decimal_run FAIL: %s exited %d\n", src, rc);
|
||||
return 1;
|
||||
}
|
||||
printf("decimal_run: %s ok\n", src);
|
||||
return 0;
|
||||
}
|
||||
@@ -85,7 +85,6 @@ static const struct ent ents[] = {
|
||||
{ .fixture = "lib/memio/memiotest.ww", .mode = M_ID },
|
||||
{ .fixture = "lib/os/ostest.ww", .mode = M_ID },
|
||||
{ .fixture = "lib/regex/regex_test.ww", .mode = M_ID },
|
||||
{ .fixture = "lib/strconv/test/decimaltest.ww", .mode = M_ID },
|
||||
{ .fixture = "lib/strconv/test/ftostest.ww", .mode = M_ID },
|
||||
/* graduated from #59.10 DIVERGE by the #62 float-literal fold fix
|
||||
* (wwstage lexer now folds through strconv.stof64, matching
|
||||
|
||||
Reference in New Issue
Block a user