'ww test' gains the istest build path (-T injection in build_one/ buildone) and do_test/dotest accept -I, mirroring do_run - both twins. The 35 converted lib tests drop their interim bare mains (-T synthesizes the entry from @test fns and rejects a user main); their 35 C run-drivers flip 'ww run' -> 'ww test'; 989_lib_byteid compiles lib tests under -T (8 user-main probe fixtures stay non-T, gated on the fixture field). Abort-on-first-failure stands until the deferred record-and-continue harness lands with the multi-package arc.
107 lines
3.2 KiB
Plaintext
107 lines
3.2 KiB
Plaintext
// errnotest — exercises [[errors.errno]] and the [[errors.opaque_]]
|
|
// tail. Run with `out/bin/ww run lib/errors/errnotest.ww`.
|
|
//
|
|
// Parallel `[N]T` arrays of (errno, expected-tag) rather than a
|
|
// `[N]struct{...}` table — the cstage cgen's chained `arr[i].field`
|
|
// 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_test;
|
|
|
|
import errors;
|
|
import os;
|
|
|
|
fn streq(a: str, b: str) bool = {
|
|
if (a.len != b.len) { return false; };
|
|
let i: i32 = 0;
|
|
for (i < a.len) {
|
|
if (a[i] != b[i]) { return false; };
|
|
i += 1;
|
|
};
|
|
return true;
|
|
};
|
|
|
|
// errtag — the union discriminant as a stable int. opaque_ is 99 so a
|
|
// misrouted named condition can't masquerade as the unmapped tail.
|
|
fn errtag(e: errors.error) int = {
|
|
match (e) {
|
|
case errors.busy => return 1;
|
|
case errors.exists => return 2;
|
|
case errors.invalid => return 3;
|
|
case errors.noaccess => return 4;
|
|
case errors.noentry => return 5;
|
|
case errors.overflow => return 6;
|
|
case errors.unsupported => return 7;
|
|
case errors.timeout => return 8;
|
|
case errors.cancelled => return 9;
|
|
case errors.refused => return 10;
|
|
case errors.interrupted => return 11;
|
|
case errors.again => return 12;
|
|
case errors.netunreachable => return 13;
|
|
case let o: errors.opaque_ => return 99;
|
|
};
|
|
};
|
|
|
|
// ---- errno → named condition --------------------------------------
|
|
@test fn mapping() void = {
|
|
let ins: [12]os.errno;
|
|
ins[0] = os.ECONNREFUSED;
|
|
ins[1] = os.ECANCELED;
|
|
ins[2] = os.EOVERFLOW;
|
|
ins[3] = os.EACCES;
|
|
ins[4] = os.EINVAL;
|
|
ins[5] = os.EEXIST;
|
|
ins[6] = os.ENOENT;
|
|
ins[7] = os.ETIMEDOUT;
|
|
ins[8] = os.EBUSY;
|
|
ins[9] = os.EINTR;
|
|
ins[10] = os.EAGAIN;
|
|
ins[11] = os.ENETUNREACH;
|
|
|
|
let exp: [12]int;
|
|
exp[0] = 10; // refused
|
|
exp[1] = 9; // cancelled
|
|
exp[2] = 6; // overflow
|
|
exp[3] = 4; // noaccess
|
|
exp[4] = 3; // invalid
|
|
exp[5] = 2; // exists
|
|
exp[6] = 5; // noentry
|
|
exp[7] = 8; // timeout
|
|
exp[8] = 1; // busy
|
|
exp[9] = 11; // interrupted
|
|
exp[10] = 12; // again
|
|
exp[11] = 13; // netunreachable
|
|
|
|
let i: i32 = 0;
|
|
for (i < 12) {
|
|
// store-to-local, not inline errtag(errors.errno(ins[i])): the
|
|
// inline form hits #222 (large >4-eightbyte union sret-arg
|
|
// clobber, cs==ww). store-then-consume is the idiomatic shape
|
|
// regardless — it is how errno is actually used (cf
|
|
// lib/io/stream.ww:53, and Hare's errors/rt.ha callers).
|
|
let er: errors.error = errors.errno(ins[i]);
|
|
assert(!(errtag(er) != exp[i]));
|
|
i += 1;
|
|
};
|
|
};
|
|
|
|
// ---- unmapped errno → opaque_ tail --------------------------------
|
|
@test fn opaquetail() void = {
|
|
// EIO (5) is outside the mapped set, so it wraps opaque_.
|
|
let e: errors.error = errors.errno(5);
|
|
assert(!(errtag(e) != 99));
|
|
match (e) {
|
|
case let o: errors.opaque_ =>
|
|
assert(!(!streq((*o.strerror)(&o.data), "Unknown error")));
|
|
case =>
|
|
abort();
|
|
};
|
|
};
|
|
|
|
// ---- os.strerror mapped path --------------------------------------
|
|
@test fn strerrortext() void = {
|
|
assert(!(!streq(os.strerror(os.ENOENT), "No such file or directory")));
|
|
assert(!(!streq(os.strerror(os.EINVAL), "Invalid argument")));
|
|
assert(!(!streq(os.strerror(5), "Unknown error")));
|
|
};
|