25 renames (git mv, content untouched). _test.ww is what the package coordinator's test detection and the sep loader's canonical exclusion key on; the old *test.ww spellings survived only through the line-leading-@test compatibility scan. Consumers updated in place: LIBRARY_TESTS, the libbyteid roster, the 901/974/975/976 carriers that copy or invoke these files, and the check.c/check.ww + path/ftos comments that cite them. Closes the open-driver-work migration bullet.
570 lines
16 KiB
Plaintext
570 lines
16 KiB
Plaintext
// memiotest — exercises lib/memio. Run with `out/bin/ww run lib/memio/memiotest.ww`.
|
||
//
|
||
// Every @test enumerates parallel `[N]T` arrays of inputs and
|
||
// expectations, then iterates one body across them. Parallel arrays
|
||
// (rather than `[N]struct{...}`) sidestep the cstage cgen's chained
|
||
// `arr[i].field` store gap (task #6). #94 fold-eFinal: the unified
|
||
// value-return surface (`let st = memio.fixed(buf); &st.vt` into the
|
||
// io dispatchers; io.read/write/close return size/io.error).
|
||
|
||
package memio_test;
|
||
|
||
import bytes;
|
||
import errors;
|
||
import io;
|
||
import memio;
|
||
|
||
fn putstr(s: str, into: []u8, off: i32) i32 = {
|
||
let i: i32 = 0;
|
||
for (i < s.len) {
|
||
into[off + i] = s[i];
|
||
i += 1;
|
||
};
|
||
return off + s.len;
|
||
};
|
||
|
||
// ---- fixedread: read sizes drive partial / full / eof outcomes ----------
|
||
|
||
@test fn fixedread() void = {
|
||
let arr: [8]u8;
|
||
arr[0] = 1u8; arr[1] = 2u8; arr[2] = 3u8; arr[3] = 4u8;
|
||
arr[4] = 5u8; arr[5] = 6u8; arr[6] = 7u8; arr[7] = 8u8;
|
||
|
||
// (request, wantn, wantfirst, wantlast, weof)
|
||
// 5 rows: under-remaining, full slot, last-byte-only, post-end, 0-len-at-end.
|
||
let req: [5]i32;
|
||
let wantn: [5]i32;
|
||
let wantf: [5]u8;
|
||
let wantl: [5]u8;
|
||
let weof: [5]i32;
|
||
req[0]=3; wantn[0]=3; wantf[0]=1u8; wantl[0]=3u8; weof[0]=0;
|
||
req[1]=4; wantn[1]=4; wantf[1]=4u8; wantl[1]=7u8; weof[1]=0;
|
||
req[2]=5; wantn[2]=1; wantf[2]=8u8; wantl[2]=8u8; weof[2]=0;
|
||
req[3]=1; wantn[3]=0; wantf[3]=0u8; wantl[3]=0u8; weof[3]=1;
|
||
req[4]=0; wantn[4]=0; wantf[4]=0u8; wantl[4]=0u8; weof[4]=1;
|
||
|
||
let st: memio.stream = memio.fixed(arr[0:8]);
|
||
let s: io.stream = &st.vt;
|
||
|
||
let out: [8]u8;
|
||
let i: i32 = 0;
|
||
for (i < 5) {
|
||
// Clear out so spot checks read real data, not stale bytes.
|
||
let k: i32 = 0;
|
||
for (k < 8) { out[k] = 0u8; k += 1; };
|
||
|
||
let r: (size | io.eof | io.error) = io.read(s, out[0:req[i]]);
|
||
match (r) {
|
||
case let nz: size => {
|
||
let n: i32 = nz: i32;
|
||
assert(!(weof[i] != 0));
|
||
assert(!(n != wantn[i]));
|
||
assert(!(n > 0 && out[0] != wantf[i]));
|
||
assert(!(n > 0 && out[n - 1] != wantl[i]));
|
||
};
|
||
case io.eof => { assert(!(weof[i] == 0)); };
|
||
case let e: io.error => abort();
|
||
};
|
||
i += 1;
|
||
};
|
||
|
||
// fixed stream has no closer → io.close returns void.
|
||
let c: (void | io.error) = io.close(s);
|
||
match (c) { case void => {}; case let e: io.error => abort(); };
|
||
};
|
||
|
||
// ---- fixedwritecases: full-fit / exact-fill / partial / overflow -------------
|
||
|
||
@test fn fixedwritecases() void = {
|
||
let dst: [16]u8;
|
||
let st: memio.stream = memio.fixed(dst[0:16]);
|
||
let s: io.stream = &st.vt;
|
||
|
||
// One flat source; rows index into it via (off, len). Sequence:
|
||
// "hello " (6), "world!!" (7), "XXXXXXX" (7), "Y" (1).
|
||
let src: [32]u8;
|
||
let _: i32 = putstr("hello world!!XXXXXXXY", src[0:32], 0);
|
||
|
||
// (srcoff, inlen, wantn, wantnomem) — wantn diverges from inlen on
|
||
// the partial row; the overflow row writes into a now-full sink and
|
||
// returns nomem (ref/hare/memio/stream.ha:161).
|
||
let off: [4]i32;
|
||
let ln: [4]i32;
|
||
let wantn: [4]i32;
|
||
let wantnomem: [4]bool;
|
||
off[0]=0; ln[0]=6; wantn[0]=6; wantnomem[0]=false; // full fit
|
||
off[1]=6; ln[1]=7; wantn[1]=7; wantnomem[1]=false; // exact-fills cap (pos=13)
|
||
off[2]=13; ln[2]=7; wantn[2]=3; wantnomem[2]=false; // partial: 3 free
|
||
off[3]=20; ln[3]=1; wantn[3]=0; wantnomem[3]=true; // overflow: full sink → nomem
|
||
|
||
let i: i32 = 0;
|
||
for (i < 4) {
|
||
let lo: i32 = off[i];
|
||
let hi: i32 = lo + ln[i];
|
||
let r: (size | io.error) = io.write(s, src[lo:hi]);
|
||
match (r) {
|
||
case let n: size => {
|
||
assert(!wantnomem[i]);
|
||
assert(!(n: i32 != wantn[i]));
|
||
};
|
||
case let e: io.error => {
|
||
assert(wantnomem[i]);
|
||
assert(e is nomem);
|
||
};
|
||
};
|
||
i += 1;
|
||
};
|
||
|
||
let want: [16]u8;
|
||
let _: i32 = putstr("hello world!!XXX", want[0:16], 0);
|
||
assert(!(!bytes.equal(dst[0:16], want[0:16])));
|
||
|
||
// 0-byte write is always 0 with no side effects.
|
||
let r0: (size | io.error) = io.write(s, src[0:0]);
|
||
match (r0) {
|
||
case let n: size => { assert(!(n != 0: size)); };
|
||
case let e: io.error => abort();
|
||
};
|
||
|
||
let c: (void | io.error) = io.close(s);
|
||
match (c) { case void => {}; case let e: io.error => abort(); };
|
||
};
|
||
|
||
// ---- fixedwritefull: the full-sink nomem contract (memio.ww:190) -------
|
||
// A write that exactly fills the sink succeeds; any further non-empty
|
||
// write into the full sink returns nomem, not a 0-byte success
|
||
// (ref/hare/memio/stream.ha:161). A zero-length sink is full from the
|
||
// start, so its first non-empty write is already nomem.
|
||
|
||
@test fn fixedwritefull() void = {
|
||
let buf: [4]u8;
|
||
let st: memio.stream = memio.fixed(buf[0:4]);
|
||
let s: io.stream = &st.vt;
|
||
|
||
let src: [4]u8;
|
||
let _: i32 = putstr("abcd", src[0:4], 0);
|
||
|
||
let r4: (size | io.error) = io.write(s, src[0:4]); // exact fill
|
||
match (r4) {
|
||
case let n: size => { assert(!(n != 4: size)); };
|
||
case let e: io.error => abort();
|
||
};
|
||
|
||
let r1: (size | io.error) = io.write(s, src[0:1]); // full sink
|
||
match (r1) {
|
||
case let n: size => abort();
|
||
case let e: io.error => { assert(e is nomem); };
|
||
};
|
||
|
||
let ze: memio.stream = memio.fixed(buf[0:0]);
|
||
let zs: io.stream = &ze.vt;
|
||
let rz: (size | io.error) = io.write(zs, src[0:1]); // zero-len sink
|
||
match (rz) {
|
||
case let n: size => abort();
|
||
case let e: io.error => { assert(e is nomem); };
|
||
};
|
||
};
|
||
|
||
// ---- dynamicgrowcases: every cap doubling exercised ---------------------
|
||
|
||
// Drive grow 0 → 8 → 16 → 32 by writing sized chunks. Verify
|
||
// accumulated `pos` after each step.
|
||
@test fn dynamicgrowcases() void = {
|
||
let st: memio.stream = memio.dynamic();
|
||
let s: io.stream = &st.vt;
|
||
|
||
let src: [32]u8;
|
||
let _: i32 = putstr("abcdefghijklmnopqrstuvwxyz012345", src[0:32], 0);
|
||
|
||
// (chunkn, totaln) — cap transitions: 0→8 at row 0; 8→16 at row 2;
|
||
// 16→32 at row 4.
|
||
let chunk: [5]i32;
|
||
let total: [5]i32;
|
||
chunk[0]=3; total[0]=3;
|
||
chunk[1]=5; total[1]=8;
|
||
chunk[2]=1; total[2]=9;
|
||
chunk[3]=7; total[3]=16;
|
||
chunk[4]=10; total[4]=26;
|
||
|
||
let off: i32 = 0;
|
||
let i: i32 = 0;
|
||
for (i < 5) {
|
||
let lo: i32 = off;
|
||
let hi: i32 = lo + chunk[i];
|
||
let r: (size | io.error) = io.write(s, src[lo:hi]);
|
||
match (r) {
|
||
case let n: size => { assert(!(n: i32 != chunk[i])); };
|
||
case let e: io.error => abort();
|
||
};
|
||
assert(!(memio.buffer(&st).len != total[i]));
|
||
off += chunk[i];
|
||
i += 1;
|
||
};
|
||
|
||
assert(!(!bytes.equal(memio.buffer(&st), src[0:26])));
|
||
|
||
let c: (void | io.error) = io.close(s);
|
||
match (c) { case void => {}; case let e: io.error => abort(); };
|
||
};
|
||
|
||
// ---- dynamicreset: write / reset / write cycles -------------------------
|
||
|
||
// op=0 writes `ln` bytes from a rolling source; op=1 resets and ignores
|
||
// ln. After each row the accumulated len must equal `want`.
|
||
@test fn dynamicreset() void = {
|
||
let st: memio.stream = memio.dynamic();
|
||
let s: io.stream = &st.vt;
|
||
|
||
let src: [16]u8;
|
||
src[0]=10u8; src[1]=20u8; src[2]=30u8; src[3]=40u8;
|
||
src[4]=50u8; src[5]=60u8; src[6]=70u8; src[7]=80u8;
|
||
|
||
let op: [6]i32;
|
||
let ln: [6]i32;
|
||
let want: [6]i32;
|
||
op[0]=0; ln[0]=5; want[0]=5; // initial write
|
||
op[1]=1; ln[1]=0; want[1]=0; // reset (after writes)
|
||
op[2]=0; ln[2]=3; want[2]=3; // refill from 0
|
||
op[3]=0; ln[3]=2; want[3]=5; // continue
|
||
op[4]=1; ln[4]=0; want[4]=0; // reset (after partial)
|
||
op[5]=0; ln[5]=8; want[5]=8; // larger refill
|
||
|
||
let off: i32 = 0;
|
||
let i: i32 = 0;
|
||
for (i < 6) {
|
||
if (op[i] == 1) {
|
||
memio.reset(&st);
|
||
off = 0;
|
||
} else {
|
||
let lo: i32 = off;
|
||
let hi: i32 = lo + ln[i];
|
||
let r: (size | io.error) = io.write(s, src[lo:hi]);
|
||
match (r) {
|
||
case let n: size => { assert(!(n: i32 != ln[i])); };
|
||
case let e: io.error => abort();
|
||
};
|
||
off += ln[i];
|
||
};
|
||
assert(!(memio.buffer(&st).len != want[i]));
|
||
i += 1;
|
||
};
|
||
|
||
assert(!(!bytes.equal(memio.buffer(&st), src[0:8])));
|
||
|
||
let c: (void | io.error) = io.close(s);
|
||
match (c) { case void => {}; case let e: io.error => abort(); };
|
||
};
|
||
|
||
// ---- borrowedread: under / exact / over / 0-byte ------------------------
|
||
|
||
@test fn borrowedreadcases() void = {
|
||
let arr: [6]u8;
|
||
arr[0]=0u8; arr[1]=1u8; arr[2]=2u8; arr[3]=3u8; arr[4]=4u8; arr[5]=5u8;
|
||
|
||
let st: memio.stream = memio.fixed(arr[0:6]);
|
||
|
||
// (amt, wantfirst, wantlast, weof)
|
||
let amt: [4]i32;
|
||
let wf: [4]u8;
|
||
let wl: [4]u8;
|
||
let weof: [4]i32;
|
||
amt[0]=4; wf[0]=0u8; wl[0]=3u8; weof[0]=0; // under remaining
|
||
amt[1]=2; wf[1]=4u8; wl[1]=5u8; weof[1]=0; // exact remaining
|
||
amt[2]=1; wf[2]=0u8; wl[2]=0u8; weof[2]=1; // past end → eof
|
||
amt[3]=0; wf[3]=0u8; wl[3]=0u8; weof[3]=0; // 0-byte view always ok
|
||
|
||
let i: i32 = 0;
|
||
for (i < 4) {
|
||
let r: ([]u8 | io.eof) = memio.borrowedread(&st, amt[i]);
|
||
match (r) {
|
||
case let v: []u8 => {
|
||
assert(!(weof[i] != 0));
|
||
assert(!(v.len != amt[i]));
|
||
assert(!(v.len > 0 && v[0] != wf[i]));
|
||
assert(!(v.len > 0 && v[v.len - 1] != wl[i]));
|
||
};
|
||
case io.eof => { assert(!(weof[i] == 0)); };
|
||
};
|
||
i += 1;
|
||
};
|
||
};
|
||
|
||
// ---- stringview: len + endpoints track pos across appends ---------------
|
||
|
||
@test fn stringview() void = {
|
||
let st: memio.stream = memio.dynamic();
|
||
let s: io.stream = &st.vt;
|
||
|
||
assert(!(memio.string(&st).len != 0));
|
||
|
||
let src: [32]u8;
|
||
let _: i32 = putstr("hello world!!", src[0:32], 0);
|
||
|
||
// (srcoff, inlen, wantacc, wantfirst, wantlast)
|
||
let off: [3]i32;
|
||
let ln: [3]i32;
|
||
let want: [3]i32;
|
||
let wf: [3]u8;
|
||
let wl: [3]u8;
|
||
off[0]=0; ln[0]=5; want[0]=5; wf[0]=104u8; wl[0]=111u8; // "hello" h..o
|
||
off[1]=5; ln[1]=6; want[1]=11; wf[1]=104u8; wl[1]=100u8; // +" world" h..d
|
||
off[2]=11; ln[2]=2; want[2]=13; wf[2]=104u8; wl[2]=33u8; // +"!!" h..!
|
||
|
||
let i: i32 = 0;
|
||
for (i < 3) {
|
||
let lo: i32 = off[i];
|
||
let hi: i32 = lo + ln[i];
|
||
let r: (size | io.error) = io.write(s, src[lo:hi]);
|
||
match (r) { case let n: size => {}; case let e: io.error => abort(); };
|
||
let v: str = memio.string(&st);
|
||
assert(!(v.len != want[i]));
|
||
assert(!(v[0] != wf[i]));
|
||
assert(!(v[v.len - 1] != wl[i]));
|
||
i += 1;
|
||
};
|
||
|
||
let c: (void | io.error) = io.close(s);
|
||
match (c) { case void => {}; case let e: io.error => abort(); };
|
||
};
|
||
|
||
// ---- dynamicfromseed: ownership-transferred seed, read then write -------
|
||
|
||
@test fn dynamicfromseed() void = {
|
||
// Build a heap-allocated seed via append (rt_ensure path), then
|
||
// hand ownership to memio. dynamicclose then frees `seed.cap`
|
||
// bytes — the regression that motivated the `cap = buf.cap` fix.
|
||
let seed: []u8;
|
||
seed.ptr = nil; seed.len = 0; seed.cap = 0;
|
||
append(seed, 100u8, 101u8, 102u8, 103u8);
|
||
|
||
let st: memio.stream = memio.dynamicfrom(seed);
|
||
let s: io.stream = &st.vt;
|
||
|
||
// (readn, wantbyte, weof)
|
||
let rn: [5]i32;
|
||
let want: [5]u8;
|
||
let weof: [5]i32;
|
||
rn[0]=1; want[0]=100u8; weof[0]=0;
|
||
rn[1]=1; want[1]=101u8; weof[1]=0;
|
||
rn[2]=1; want[2]=102u8; weof[2]=0;
|
||
rn[3]=1; want[3]=103u8; weof[3]=0;
|
||
rn[4]=1; want[4]=0u8; weof[4]=1; // past seed end → eof
|
||
|
||
let out: [4]u8;
|
||
let i: i32 = 0;
|
||
for (i < 5) {
|
||
let r: (size | io.eof | io.error) = io.read(s, out[0:rn[i]]);
|
||
match (r) {
|
||
case let nz: size => {
|
||
let n: i32 = nz: i32;
|
||
assert(!(weof[i] != 0));
|
||
assert(!(n != rn[i]));
|
||
assert(!(out[0] != want[i]));
|
||
};
|
||
case io.eof => { assert(!(weof[i] == 0)); };
|
||
case let e: io.error => abort();
|
||
};
|
||
i += 1;
|
||
};
|
||
|
||
// Now write extends past the seed; grow path runs and close
|
||
// must free the (post-grow) cap, not the seed cap.
|
||
let extra: [4]u8;
|
||
extra[0]=200u8; extra[1]=201u8; extra[2]=202u8; extra[3]=203u8;
|
||
let w: (size | io.error) = io.write(s, extra[0:4]);
|
||
match (w) {
|
||
case let n: size => { assert(!(n: i32 != 4)); };
|
||
case let e: io.error => abort();
|
||
};
|
||
|
||
let c: (void | io.error) = io.close(s);
|
||
match (c) { case void => {}; case let e: io.error => abort(); };
|
||
};
|
||
|
||
// ---- seekcases: SET/CUR/END × in-bounds/OOB over one fixed stream --------
|
||
|
||
// Sequential rows over one 8-byte fixed stream; CUR rows depend on the
|
||
// cursor left by earlier rows. want = resulting pos for valid rows,
|
||
// -1 = errors.invalid expected. After EVERY row an io.tell readback
|
||
// checks the cursor against a shadow `pos` — invalid rows must leave
|
||
// it untouched (ref/hare/memio/stream.ha:122-140 returns before the
|
||
// pos store).
|
||
@test fn seekcases() void = {
|
||
let arr: [8]u8;
|
||
arr[0]=10u8; arr[1]=11u8; arr[2]=12u8; arr[3]=13u8;
|
||
arr[4]=14u8; arr[5]=15u8; arr[6]=16u8; arr[7]=17u8;
|
||
|
||
// (whence, off, want)
|
||
let wh: [12]i32; // 0 SET / 1 CUR / 2 END
|
||
let off: [12]i64;
|
||
let want: [12]i64;
|
||
wh[0] =0; off[0] =3; want[0] =3; // SET forward
|
||
wh[1] =1; off[1] =2; want[1] =5; // CUR forward
|
||
wh[2] =1; off[2] =-5; want[2] =0; // CUR back to start
|
||
wh[3] =2; off[3] =0; want[3] =8; // END exact end
|
||
wh[4] =2; off[4] =-8; want[4] =0; // END back to start
|
||
wh[5] =0; off[5] =9; want[5] =-1; // SET past end
|
||
wh[6] =1; off[6] =-1; want[6] =-1; // CUR before start
|
||
wh[7] =2; off[7] =1; want[7] =-1; // END past end
|
||
wh[8] =2; off[8] =-9; want[8] =-1; // END before start
|
||
wh[9] =0; off[9] =8; want[9] =8; // SET end exact
|
||
wh[10]=1; off[10]=0; want[10]=8; // CUR no-op at end
|
||
wh[11]=0; off[11]=0; want[11]=0; // SET rewind
|
||
|
||
let st: memio.stream = memio.fixed(arr[0:8]);
|
||
let s: io.stream = &st.vt;
|
||
|
||
let pos: i64 = 0;
|
||
let i: i32 = 0;
|
||
for (i < 12) {
|
||
let w: io.whence = wh[i]: io.whence;
|
||
let r: (io.off | io.error) = io.seek(s, off[i], w);
|
||
match (r) {
|
||
case let o: io.off => {
|
||
assert(!(want[i] < 0));
|
||
assert(!((o: i64) != want[i]));
|
||
pos = o: i64;
|
||
};
|
||
case let e: io.error => {
|
||
assert(!(want[i] >= 0));
|
||
match (e) {
|
||
case errors.invalid => {};
|
||
case => abort();
|
||
};
|
||
};
|
||
};
|
||
let t: (io.off | io.error) = io.tell(s);
|
||
match (t) {
|
||
case let o: io.off => { assert(!((o: i64) != pos)); };
|
||
case let e: io.error => abort();
|
||
};
|
||
i += 1;
|
||
};
|
||
|
||
// Data readback through the repositioned cursor: SET 5 then read
|
||
// drains bytes 5..7, then eof.
|
||
let r: (io.off | io.error) = io.seek(s, 5, io.whence.SET);
|
||
match (r) {
|
||
case let o: io.off => { assert(!((o: i64) != 5)); };
|
||
case let e: io.error => abort();
|
||
};
|
||
let out: [8]u8;
|
||
let rd: (size | io.eof | io.error) = io.read(s, out[0:8]);
|
||
match (rd) {
|
||
case let nz: size => {
|
||
assert(!(nz: i32 != 3));
|
||
assert(!(out[0] != 15u8));
|
||
assert(!(out[2] != 17u8));
|
||
};
|
||
case io.eof => abort();
|
||
case let e: io.error => abort();
|
||
};
|
||
let rd2: (size | io.eof | io.error) = io.read(s, out[0:8]);
|
||
match (rd2) {
|
||
case io.eof => {};
|
||
case let nz: size => abort();
|
||
case let e: io.error => abort();
|
||
};
|
||
};
|
||
|
||
// ---- emptyseek: every whence over a 0-length buffer ----------------------
|
||
|
||
@test fn emptyseek() void = {
|
||
let arr: [1]u8;
|
||
arr[0] = 0u8;
|
||
let st: memio.stream = memio.fixed(arr[0:0]);
|
||
let s: io.stream = &st.vt;
|
||
|
||
let wh: [5]i32;
|
||
let off: [5]i64;
|
||
let want: [5]i64;
|
||
wh[0]=0; off[0]=0; want[0]=0; // SET 0 — only valid target
|
||
wh[1]=2; off[1]=0; want[1]=0; // END 0 == start
|
||
wh[2]=0; off[2]=1; want[2]=-1;
|
||
wh[3]=1; off[3]=-1; want[3]=-1;
|
||
wh[4]=2; off[4]=-1; want[4]=-1;
|
||
|
||
let i: i32 = 0;
|
||
for (i < 5) {
|
||
let w: io.whence = wh[i]: io.whence;
|
||
let r: (io.off | io.error) = io.seek(s, off[i], w);
|
||
match (r) {
|
||
case let o: io.off => {
|
||
assert(!(want[i] < 0));
|
||
assert(!((o: i64) != want[i]));
|
||
};
|
||
case let e: io.error => {
|
||
assert(!(want[i] >= 0));
|
||
match (e) {
|
||
case errors.invalid => {};
|
||
case => abort();
|
||
};
|
||
};
|
||
};
|
||
i += 1;
|
||
};
|
||
};
|
||
|
||
// ---- dynamicseek: rewind-and-reread over written data --------------------
|
||
|
||
// The dynamic vtable wires the same seekfn; bounds run against the
|
||
// LOGICAL length (m.len), not capacity (Hare: len(s.buf),
|
||
// ref/hare/memio/stream.ha:131,136).
|
||
@test fn dynamicseek() void = {
|
||
let st: memio.stream = memio.dynamic();
|
||
let s: io.stream = &st.vt;
|
||
|
||
let src: [5]u8;
|
||
src[0]=30u8; src[1]=31u8; src[2]=32u8; src[3]=33u8; src[4]=34u8;
|
||
let wr: (size | io.error) = io.write(s, src[0:5]);
|
||
match (wr) {
|
||
case let n: size => { assert(!(n: i32 != 5)); };
|
||
case let e: io.error => abort();
|
||
};
|
||
|
||
// END 0 → 5 (logical length); cap is 8 post-grow, so END 1 must
|
||
// still be invalid even though capacity would fit it.
|
||
let r: (io.off | io.error) = io.seek(s, 0, io.whence.END);
|
||
match (r) {
|
||
case let o: io.off => { assert(!((o: i64) != 5)); };
|
||
case let e: io.error => abort();
|
||
};
|
||
let r1: (io.off | io.error) = io.seek(s, 1, io.whence.END);
|
||
match (r1) {
|
||
case let o: io.off => abort();
|
||
case let e: io.error => {
|
||
match (e) {
|
||
case errors.invalid => {};
|
||
case => abort();
|
||
};
|
||
};
|
||
};
|
||
|
||
// CUR -2 → 3; read drains 33,34 then eof.
|
||
let r2: (io.off | io.error) = io.seek(s, -2, io.whence.CUR);
|
||
match (r2) {
|
||
case let o: io.off => { assert(!((o: i64) != 3)); };
|
||
case let e: io.error => abort();
|
||
};
|
||
let out: [4]u8;
|
||
let rd: (size | io.eof | io.error) = io.read(s, out[0:4]);
|
||
match (rd) {
|
||
case let nz: size => {
|
||
assert(!(nz: i32 != 2));
|
||
assert(!(out[0] != 33u8));
|
||
assert(!(out[1] != 34u8));
|
||
};
|
||
case io.eof => abort();
|
||
case let e: io.error => abort();
|
||
};
|
||
let rd2: (size | io.eof | io.error) = io.read(s, out[0:4]);
|
||
match (rd2) {
|
||
case io.eof => {};
|
||
case let nz: size => abort();
|
||
case let e: io.error => abort();
|
||
};
|
||
|
||
let c: (void | io.error) = io.close(s);
|
||
match (c) { case void => {}; case let e: io.error => abort(); };
|
||
};
|