package sepscratch_test; // #59 scratch-ownership contract of build_one_sep, both driver stages. // Port of the retired native carrier test/wcc/989_sepscratch_run.c; // every assertion preserved. // // keep — `build -o` scratch (.sepwork) SURVIVES the build // (keepscratch: the byte-id gates read it). // cleanrun — `run` leaves no /tmp/ww_run_ scratch root behind. // The driver keys the root on its OWN pid, so the child is // launched via `sh -c 'echo $$ > pid; exec ...'` — // exec keeps the shell's pid, and the full driver path // lands in argv[0] (the driver derives self_dir from it). // collision — a pre-existing .sepwork belongs to someone else: // build must refuse with exit EXACTLY 1 (not adopt), and // the foreign sentinel must survive byte-for-byte. // // Dropped C machinery, not assertions: the fork/execl pid capture (the // sh exec trick above carries it) and the per-path unlink/rmdir // accounting (testenv.clean asserts the recursive removal). import os; import os.exec; import strings; import testenv; import time; fn fail(label: str, why: str) void = { let m: str = strings.concat("sepscratch FAIL: ", label, " -- ", why, "\n"); os.write(2, m.ptr, m.len: u64); assert(false); }; fn tmo() time.duration = { return (240i64 * (time.second: i64)): time.duration; }; // A no-import root: builds fast, still produces a real __root scratch. fn rootsrc() str = { return strings.concat( "package main;\n", "fn main() i32 = { return 0; };\n"); }; @test fn keep() void = { let td: str = testenv.fresh(); let rootww: str = strings.concat(td, "/root.ww"); testenv.writefile(rootww, rootsrc()); let drvs: []str = ["ww", "ww_ww"]; let i: i32 = 0; for (i < drvs.len) { let prog: str = strings.concat(td, "/prog_", drvs[i]); let av: []str = [testenv.driver(drvs[i]), "build", "-o", prog, rootww]; let co: testenv.commandout; testenv.runcommand(td, td, strings.concat("build_", drvs[i]), av, tmo(), &co); if (co.termination != exec.termination.EXIT || co.code != 0) { fail(drvs[i], "build -o failed"); }; if (!testenv.exists(strings.concat(prog, ".sepwork"))) { fail(drvs[i], strings.concat("build -o scratch was removed ", "(gates read it; keepscratch must hold)")); }; i += 1; }; testenv.clean(td); }; @test fn cleanrun() void = { let td: str = testenv.fresh(); let rootww: str = strings.concat(td, "/root.ww"); testenv.writefile(rootww, rootsrc()); let drvs: []str = ["ww", "ww_ww"]; let i: i32 = 0; for (i < drvs.len) { let pidf: str = strings.concat(td, "/pid_", drvs[i]); let script: str = strings.concat("echo $$ > ", pidf, "; exec ", testenv.driver(drvs[i]), " run ", rootww); let av: []str = ["/bin/sh", "-c", script]; let co: testenv.commandout; testenv.runcommand(td, td, strings.concat("run_", drvs[i]), av, tmo(), &co); if (co.termination != exec.termination.EXIT || co.code != 0) { fail(drvs[i], strings.concat("run exit != 0 (must have ", "built+run, proving non-vacuity)")); }; let pid: str = strings.trim(testenv.readfile(pidf), '\n'); if (testenv.exists(strings.concat("/tmp/ww_run_", pid))) { fail(drvs[i], "leaked the run scratch root"); }; i += 1; }; testenv.clean(td); }; @test fn collision() void = { let td: str = testenv.fresh(); let rootww: str = strings.concat(td, "/root.ww"); testenv.writefile(rootww, rootsrc()); let drvs: []str = ["ww", "ww_ww"]; let i: i32 = 0; for (i < drvs.len) { let coll: str = strings.concat(td, "/collision_", drvs[i]); let scr: str = strings.concat(coll, ".sepwork"); assert(os.mkdir(scr, 493) == 0); let sentinel: str = strings.concat(scr, "/sentinel"); testenv.writefile(sentinel, "owned\n"); let av: []str = [testenv.driver(drvs[i]), "build", "-o", coll, rootww]; let co: testenv.commandout; testenv.runcommand(td, td, strings.concat("coll_", drvs[i]), av, tmo(), &co); if (co.termination == exec.termination.EXIT && co.code == 0) { fail(drvs[i], "adopted a pre-existing .sepwork"); }; if (co.termination != exec.termination.EXIT || co.code != 1) { fail(drvs[i], "collision refusal exit != 1"); }; if (!testenv.same(testenv.readfile(sentinel), "owned\n")) { fail(drvs[i], "deleted or altered the foreign sentinel"); }; i += 1; }; testenv.clean(td); };