ww build: discard exact null outputs
This commit is contained in:
@@ -542,6 +542,16 @@ fn directoryisempty(path: str) bool = {
|
||||
return true;
|
||||
};
|
||||
|
||||
fn discardedlinkcleaned(marker: str) void = {
|
||||
let output: str = readfile(marker);
|
||||
assert(output.len != 0);
|
||||
assert(!has(output, "/dev/null"));
|
||||
let slash: i32 = output.len - 1;
|
||||
for (slash >= 0 && output[slash] != '/') { slash -= 1; };
|
||||
assert(slash > 0);
|
||||
assert(!os.exists(strings.sub(output, 0, slash)));
|
||||
};
|
||||
|
||||
fn byteshasat(s: str, off: i32, want: str) bool = {
|
||||
if (off < 0 || off + want.len > s.len) { return false; };
|
||||
let i: i32 = 0;
|
||||
@@ -12990,3 +13000,490 @@ fn runtimepath(relative: str) str = {
|
||||
assert(!directoryhasnew(parallelwwwork));
|
||||
clean(root);
|
||||
};
|
||||
|
||||
@test fn exact_null_output_discards_build_products() void = {
|
||||
let root: str = fresh();
|
||||
let source: str = strings.concat(root, "/source");
|
||||
let base: str = strings.concat(source, "/base");
|
||||
let alpha: str = strings.concat(source, "/alpha");
|
||||
let beta: str = strings.concat(source, "/beta");
|
||||
let library: str = strings.concat(source, "/library");
|
||||
let bad: str = strings.concat(source, "/bad");
|
||||
let absent: str = strings.concat(source, "/absent");
|
||||
mkdirall(base); mkdirall(alpha); mkdirall(beta);
|
||||
mkdirall(library); mkdirall(bad); mkdirall(absent);
|
||||
writefile(strings.concat(base, "/base.ww"), strings.concat(
|
||||
"package base;\n",
|
||||
"export fn value() i32 = { return 4; };\n"));
|
||||
let alphafile: str = strings.concat(alpha, "/main.ww");
|
||||
let alphaoriginal: str = strings.concat(
|
||||
"package main;\nimport base;\n",
|
||||
"fn main() i32 = { return base.value(); };\n");
|
||||
let alphachanged: str = strings.concat(
|
||||
"package main;\nimport base;\n",
|
||||
"fn main() i32 = { return base.value() + 1; };\n");
|
||||
let alphafailing: str = strings.concat(
|
||||
"package main;\nimport base;\n",
|
||||
"fn main() i32 = { return base.value() + 2; };\n");
|
||||
writefile(alphafile, alphaoriginal);
|
||||
writefile(strings.concat(beta, "/main.ww"),
|
||||
"package main;\nfn main() i32 = { return 6; };\n");
|
||||
writefile(strings.concat(library, "/library.ww"), strings.concat(
|
||||
"package odd;\n",
|
||||
"export fn value() i32 = { return 7; };\n"));
|
||||
writefile(strings.concat(bad, "/main.ww"), strings.concat(
|
||||
"package main;\nimport _ missing.pkg;\n",
|
||||
"fn main() i32 = { return 0; };\n"));
|
||||
let raw: str = strings.concat(source, "/raw.ww");
|
||||
writefile(raw, "package main;\nfn main() i32 = { return 9; };\n");
|
||||
|
||||
let compilerwrapper: str = strings.concat(root, "/null-w6c.sh");
|
||||
let assemblerwrapper: str = strings.concat(root, "/null-w6a.sh");
|
||||
let linkerwrapper: str = strings.concat(root, "/null-w6l.sh");
|
||||
writeexecutable(compilerwrapper, strings.concat(
|
||||
"#!/bin/sh\n",
|
||||
"printf '%s\\n' \"$*\" >> \"$WW_NULL_CTRACE\"\n",
|
||||
"exec \"$WW_NULL_REAL_C\" \"$@\"\n"));
|
||||
writeexecutable(assemblerwrapper, strings.concat(
|
||||
"#!/bin/sh\n",
|
||||
"printf '%s\\n' \"$*\" >> \"$WW_NULL_ATRACE\"\n",
|
||||
"exec \"$WW_NULL_REAL_A\" \"$@\"\n"));
|
||||
writeexecutable(linkerwrapper, strings.concat(
|
||||
"#!/bin/sh\n",
|
||||
"printf '%s\\n' \"$*\" >> \"$WW_NULL_LTRACE\"\n",
|
||||
"out= take=\n",
|
||||
"for arg do\n",
|
||||
" if test \"$take\" = yes; then out=$arg; take=; continue; fi\n",
|
||||
" if test \"$arg\" = -o; then take=yes; fi\n",
|
||||
"done\n",
|
||||
"printf '%s' \"$out\" > \"$WW_NULL_LAST_OUTPUT\"\n",
|
||||
"case \"$WW_NULL_LINK_MODE\" in\n",
|
||||
" fail) printf 'injected null-output linker failure\\n' >&2; exit 97;;\n",
|
||||
" signal) kill -TERM \"$$\";;\n",
|
||||
"esac\n",
|
||||
"\"$WW_NULL_REAL_L\" \"$@\"\n",
|
||||
"rc=$?\n",
|
||||
"if test \"$rc\" -eq 0 && test -n \"$WW_NULL_CAPTURE\"; then\n",
|
||||
" cp \"$out\" \"$WW_NULL_CAPTURE\" || exit 98\n",
|
||||
"fi\n",
|
||||
"exit \"$rc\"\n"));
|
||||
|
||||
let nullbefore: os.filestat;
|
||||
match (os.stat(&nullbefore, "/dev/null")) {
|
||||
case void => void;
|
||||
case let e: os.oserror => abort("cannot stat /dev/null");
|
||||
};
|
||||
assert(((nullbefore.mode as u32) & 61440u32)
|
||||
== (os.mode.CHR as u32));
|
||||
let stages: []str = ["ww", "ww_ww"];
|
||||
let compilers: []str = ["w6c", "w6c_ww"];
|
||||
let assemblers: []str = ["w6a", "w6a_ww"];
|
||||
let linkers: []str = ["w6l", "w6l_ww"];
|
||||
let tags: []str = ["c", "ww"];
|
||||
let coldref: str = "";
|
||||
let changedref: str = "";
|
||||
let rawref: str = "";
|
||||
let lookalikeref: str = "";
|
||||
let faildiagref: str = "";
|
||||
let signaldiagref: str = "";
|
||||
let missingdiagref: str = "";
|
||||
let emptydiagref: str = "";
|
||||
let rejectdiagref: str = "";
|
||||
let testcompileref: str = "";
|
||||
let testoutrefs: []str = ["", "", ""];
|
||||
let testerrrefs: []str = ["", "", ""];
|
||||
let baseenv: []str = os.getenvs();
|
||||
let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"];
|
||||
let out: commandout;
|
||||
let si: i32 = 0;
|
||||
for (si < stages.len) {
|
||||
rewritefile(alphafile, alphaoriginal);
|
||||
let work: str = strings.concat(root, "/work-", tags[si]);
|
||||
mkdirall(work);
|
||||
let ctrace: str = strings.concat(root, "/", tags[si], "-c.trace");
|
||||
let atrace: str = strings.concat(root, "/", tags[si], "-a.trace");
|
||||
let ltrace: str = strings.concat(root, "/", tags[si], "-l.trace");
|
||||
let lastout: str = strings.concat(root, "/", tags[si], "-last.out");
|
||||
let capture: str = strings.concat(root, "/", tags[si], "-capture");
|
||||
writefile(ctrace, ""); writefile(atrace, "");
|
||||
writefile(ltrace, ""); writefile(lastout, "");
|
||||
let env: []str = alloc([], (baseenv.len + 12): u64)!;
|
||||
let ei: i32 = 0;
|
||||
for (ei < baseenv.len) {
|
||||
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||||
&& !strings.hasprefix(baseenv[ei], "WW_W6A=")
|
||||
&& !strings.hasprefix(baseenv[ei], "WW_W6L=")
|
||||
&& !strings.hasprefix(baseenv[ei], "WW_NULL_")) {
|
||||
append(env, baseenv[ei]);
|
||||
};
|
||||
ei += 1;
|
||||
};
|
||||
append(env, strings.concat("WW_W6C=", compilerwrapper));
|
||||
append(env, strings.concat("WW_W6A=", assemblerwrapper));
|
||||
append(env, strings.concat("WW_W6L=", linkerwrapper));
|
||||
append(env, strings.concat("WW_NULL_CTRACE=", ctrace));
|
||||
append(env, strings.concat("WW_NULL_ATRACE=", atrace));
|
||||
append(env, strings.concat("WW_NULL_LTRACE=", ltrace));
|
||||
append(env, strings.concat("WW_NULL_REAL_C=",
|
||||
driver(compilers[si])));
|
||||
append(env, strings.concat("WW_NULL_REAL_A=",
|
||||
driver(assemblers[si])));
|
||||
append(env, strings.concat("WW_NULL_REAL_L=",
|
||||
driver(linkers[si])));
|
||||
let linkmodeindex: i32 = env.len;
|
||||
append(env, "WW_NULL_LINK_MODE=");
|
||||
let captureindex: i32 = env.len;
|
||||
append(env, strings.concat("WW_NULL_CAPTURE=", capture));
|
||||
append(env, strings.concat("WW_NULL_LAST_OUTPUT=", lastout));
|
||||
|
||||
// Exact null is no-install policy. Loading, dependency production,
|
||||
// assembly, archiving, and the command link still occur normally.
|
||||
let coldav: []str = [driver(stages[si]), "build", "-w", work,
|
||||
"-I", source, "-o", "/dev/null", alpha];
|
||||
runcommandenvdir(root, strings.concat("null-cold-", tags[si]), coldav,
|
||||
env, root, (120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||||
assert(occurrences(readfile(ctrace), "\n") == 2);
|
||||
assert(occurrences(readfile(atrace), "\n") == 3);
|
||||
assert(occurrences(readfile(ltrace), "\n") == 1);
|
||||
assert(has(readfile(ctrace), "/base.unit.new"));
|
||||
assert(has(readfile(ctrace), "/alpha.unit.new"));
|
||||
discardedlinkcleaned(lastout);
|
||||
let coldbytes: str = readfile(capture);
|
||||
if (si == 0) { coldref = strings.dup(coldbytes); }
|
||||
else { assert(same(coldref, coldbytes)); };
|
||||
let runav: []str = [capture];
|
||||
runcommand(root, strings.concat("null-cold-run-", tags[si]), runav,
|
||||
time.second, &out);
|
||||
expectexit(&out, 4);
|
||||
let originalunit: str = readfile(strings.concat(work,
|
||||
"/alpha.unit.ww"));
|
||||
|
||||
// Unchanged actions are warm-reused, but the command links again into
|
||||
// fresh private storage because null output is not a result cache.
|
||||
rewritefile(ctrace, ""); rewritefile(atrace, "");
|
||||
rewritefile(ltrace, ""); rewritefile(lastout, "");
|
||||
rewritefile(capture, "");
|
||||
runcommandenvdir(root, strings.concat("null-warm-", tags[si]), coldav,
|
||||
env, root, (120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0);
|
||||
assert(occurrences(readfile(ltrace), "\n") == 1);
|
||||
assert(same(coldbytes, readfile(capture)));
|
||||
discardedlinkcleaned(lastout);
|
||||
|
||||
// Source change invalidates the owning action and linked bytes while
|
||||
// preserving the same action names and persistent representation.
|
||||
rewritefile(alphafile, alphachanged);
|
||||
rewritefile(ctrace, ""); rewritefile(atrace, "");
|
||||
rewritefile(ltrace, ""); rewritefile(lastout, "");
|
||||
rewritefile(capture, "");
|
||||
runcommandenvdir(root, strings.concat("null-change-", tags[si]), coldav,
|
||||
env, root, (120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(occurrences(readfile(ctrace), "\n") == 1);
|
||||
assert(occurrences(readfile(atrace), "\n") == 1);
|
||||
assert(occurrences(readfile(ltrace), "\n") == 1);
|
||||
assert(!same(originalunit, readfile(strings.concat(work,
|
||||
"/alpha.unit.ww"))));
|
||||
let changedbytes: str = readfile(capture);
|
||||
assert(!same(coldbytes, changedbytes));
|
||||
if (si == 0) { changedref = strings.dup(changedbytes); }
|
||||
else { assert(same(changedref, changedbytes)); };
|
||||
discardedlinkcleaned(lastout);
|
||||
let kept: []str = alloc([], suffixes.len: u64)!;
|
||||
let xi: i32 = 0;
|
||||
for (xi < suffixes.len) {
|
||||
append(kept, strings.dup(readfile(strings.concat(work,
|
||||
"/alpha", suffixes[xi]))));
|
||||
xi += 1;
|
||||
};
|
||||
|
||||
// A late producer failure and a producer signal cannot publish and
|
||||
// cannot commit an invalidated persistent generation.
|
||||
rewritefile(alphafile, alphafailing);
|
||||
rewritefile(ctrace, ""); rewritefile(atrace, "");
|
||||
rewritefile(ltrace, ""); rewritefile(lastout, "");
|
||||
env[linkmodeindex] = "WW_NULL_LINK_MODE=fail";
|
||||
runcommandenvdir(root, strings.concat("null-fail-", tags[si]), coldav,
|
||||
env, root, (120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
assert(has(out.stderr, "injected null-output linker failure\n"));
|
||||
assert(has(out.stderr, "ww: w6l failed\n"));
|
||||
assert(occurrences(readfile(ltrace), "\n") == 1);
|
||||
discardedlinkcleaned(lastout);
|
||||
xi = 0;
|
||||
for (xi < suffixes.len) {
|
||||
assert(same(kept[xi], readfile(strings.concat(work,
|
||||
"/alpha", suffixes[xi]))));
|
||||
xi += 1;
|
||||
};
|
||||
assert(!directoryhasnew(work));
|
||||
if (si == 0) { faildiagref = strings.dup(out.stderr); }
|
||||
else { assert(same(faildiagref, out.stderr)); };
|
||||
|
||||
rewritefile(ctrace, ""); rewritefile(atrace, "");
|
||||
rewritefile(ltrace, ""); rewritefile(lastout, "");
|
||||
env[linkmodeindex] = "WW_NULL_LINK_MODE=signal";
|
||||
runcommandenvdir(root, strings.concat("null-signal-", tags[si]), coldav,
|
||||
env, root, (120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
assert(has(out.stderr, "ww: w6l failed\n"));
|
||||
discardedlinkcleaned(lastout);
|
||||
xi = 0;
|
||||
for (xi < suffixes.len) {
|
||||
assert(same(kept[xi], readfile(strings.concat(work,
|
||||
"/alpha", suffixes[xi]))));
|
||||
xi += 1;
|
||||
};
|
||||
assert(!directoryhasnew(work));
|
||||
if (si == 0) { signaldiagref = strings.dup(out.stderr); }
|
||||
else { assert(same(signaldiagref, out.stderr)); };
|
||||
env[linkmodeindex] = "WW_NULL_LINK_MODE=";
|
||||
|
||||
// Import loading and empty-pattern handling precede output planning.
|
||||
// The exact null exception changes neither diagnostic nor tool order.
|
||||
rewritefile(ctrace, ""); rewritefile(atrace, "");
|
||||
rewritefile(ltrace, ""); rewritefile(lastout, "");
|
||||
let badav: []str = [driver(stages[si]), "build", "-I", source,
|
||||
"-o", "/dev/null", bad];
|
||||
runcommandenvdir(root, strings.concat("null-import-", tags[si]), badav,
|
||||
env, root, (60i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
assert(has(out.stderr, "cannot find package missing.pkg\n"));
|
||||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0
|
||||
&& readfile(ltrace).len == 0 && readfile(lastout).len == 0);
|
||||
if (si == 0) { missingdiagref = strings.dup(out.stderr); }
|
||||
else { assert(same(missingdiagref, out.stderr)); };
|
||||
|
||||
let emptyroot: str = strings.concat(source, "/absent/...");
|
||||
let emptyav: []str = [driver(stages[si]), "build", "-o",
|
||||
"/dev/null", emptyroot];
|
||||
runcommandenvdir(root, strings.concat("null-empty-", tags[si]), emptyav,
|
||||
env, root, time.second, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(has(out.stderr, "matched no packages\n"));
|
||||
assert(readfile(ctrace).len == 0 && readfile(ltrace).len == 0);
|
||||
if (si == 0) { emptydiagref = strings.dup(out.stderr); }
|
||||
else { assert(same(emptydiagref, out.stderr)); };
|
||||
|
||||
// One discard request may contain commands and a non-main library.
|
||||
// Every action runs, only commands link, and no basename is published.
|
||||
rewritefile(alphafile, alphaoriginal);
|
||||
rewritefile(ctrace, ""); rewritefile(atrace, "");
|
||||
rewritefile(ltrace, ""); rewritefile(lastout, "");
|
||||
let multiav: []str = [driver(stages[si]), "build", "-I", source,
|
||||
"-o", "/dev/null", alpha, beta, library];
|
||||
runcommandenvdir(root, strings.concat("null-multi-", tags[si]), multiav,
|
||||
env, root, (120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||||
assert(occurrences(readfile(ctrace), "\n") == 4);
|
||||
assert(occurrences(readfile(atrace), "\n") == 6);
|
||||
assert(occurrences(readfile(ltrace), "\n") == 2);
|
||||
assert(!os.exists(strings.concat(root, "/alpha")));
|
||||
assert(!os.exists(strings.concat(root, "/beta")));
|
||||
assert(!os.exists(strings.concat(root, "/library")));
|
||||
assert(!os.exists("/dev/null.new"));
|
||||
assert(!os.exists("/dev/null.sepwork"));
|
||||
|
||||
// Assembly-only keeps the same loading/action exception, has no link,
|
||||
// and needs no persistent workdir solely to make discarded files visible.
|
||||
rewritefile(ctrace, ""); rewritefile(atrace, "");
|
||||
rewritefile(ltrace, ""); rewritefile(lastout, "");
|
||||
let asmav: []str = [driver(stages[si]), "build", "-S", "-I", source,
|
||||
"-o", "/dev/null", alpha, beta, library];
|
||||
runcommandenvdir(root, strings.concat("null-asm-", tags[si]), asmav,
|
||||
env, root, (120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(occurrences(readfile(ctrace), "\n") == 4);
|
||||
assert(readfile(atrace).len == 0 && readfile(ltrace).len == 0);
|
||||
|
||||
// The direct raw-file route links privately too. A wrapper capture proves
|
||||
// the linked bytes are runnable before the driver's cleanup.
|
||||
rewritefile(ctrace, ""); rewritefile(atrace, "");
|
||||
rewritefile(ltrace, ""); rewritefile(lastout, "");
|
||||
rewritefile(capture, "");
|
||||
let rawav: []str = [driver(stages[si]), "build", "-o",
|
||||
"/dev/null", raw];
|
||||
runcommandenvdir(root, strings.concat("null-raw-", tags[si]), rawav,
|
||||
env, root, (60i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(occurrences(readfile(ltrace), "\n") == 1);
|
||||
discardedlinkcleaned(lastout);
|
||||
let rawbytes: str = readfile(capture);
|
||||
if (si == 0) { rawref = strings.dup(rawbytes); }
|
||||
else { assert(same(rawref, rawbytes)); };
|
||||
let rawrun: []str = [capture];
|
||||
runcommand(root, strings.concat("null-raw-run-", tags[si]), rawrun,
|
||||
time.second, &out);
|
||||
expectexit(&out, 9);
|
||||
|
||||
// Null recognition is exact. A regular lookalike is retained with its
|
||||
// established adjacent scratch; a multi-root lookalike still rejects.
|
||||
let lookalike: str = strings.concat(root, "/null-like-", tags[si]);
|
||||
rewritefile(ctrace, ""); rewritefile(atrace, "");
|
||||
rewritefile(ltrace, ""); rewritefile(lastout, "");
|
||||
env[captureindex] = "WW_NULL_CAPTURE=";
|
||||
let lookav: []str = [driver(stages[si]), "build", "-o",
|
||||
lookalike, raw];
|
||||
runcommandenvdir(root, strings.concat("null-look-", tags[si]), lookav,
|
||||
env, root, (60i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(os.exists(lookalike));
|
||||
assert(os.exists(strings.concat(lookalike, ".sepwork")));
|
||||
let lookbytes: str = readfile(lookalike);
|
||||
if (si == 0) { lookalikeref = strings.dup(lookbytes); }
|
||||
else { assert(same(lookalikeref, lookbytes)); };
|
||||
let rejectout: str = strings.concat(root, "/multi-like-", tags[si]);
|
||||
rewritefile(ctrace, ""); rewritefile(atrace, "");
|
||||
rewritefile(ltrace, "");
|
||||
let rejectav: []str = [driver(stages[si]), "build", "-I", source,
|
||||
"-o", rejectout, alpha, beta];
|
||||
runcommandenvdir(root, strings.concat("null-reject-", tags[si]),
|
||||
rejectav, env, root, time.second, &out);
|
||||
expectexit(&out, 2);
|
||||
assert(has(out.stderr, "cannot use -o with multiple packages\n"));
|
||||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0
|
||||
&& readfile(ltrace).len == 0);
|
||||
if (si == 0) { rejectdiagref = strings.dup(out.stderr); }
|
||||
else { assert(same(rejectdiagref, out.stderr)); };
|
||||
env[captureindex] = strings.concat("WW_NULL_CAPTURE=", capture);
|
||||
|
||||
// Raw test compilation has the same private-output rule. Running forms
|
||||
// preserve ordinary success, assertion, and signal outcomes.
|
||||
let compileav: []str = [driver(stages[si]), "test", "-c", "-o",
|
||||
"/dev/null", runtimepath("success_test.ww")];
|
||||
rewritefile(ctrace, ""); rewritefile(atrace, "");
|
||||
rewritefile(ltrace, ""); rewritefile(lastout, "");
|
||||
rewritefile(capture, "");
|
||||
runcommandenvdir(root, strings.concat("null-test-c-", tags[si]),
|
||||
compileav, env, root,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 0);
|
||||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||||
assert(occurrences(readfile(ltrace), "\n") == 1);
|
||||
discardedlinkcleaned(lastout);
|
||||
let testbytes: str = readfile(capture);
|
||||
if (si == 0) { testcompileref = strings.dup(testbytes); }
|
||||
else { assert(same(testcompileref, testbytes)); };
|
||||
|
||||
let runtargets: []str = ["success_test.ww", "fail_test.ww",
|
||||
"signal_test.ww"];
|
||||
let exitcodes: []i32 = [0, 1, 1];
|
||||
let needles: []str = ["alpha_pass ... ok\n",
|
||||
"assertion_failure ... FAIL (exit 1)\n",
|
||||
"signal_is_not_exit ... FAIL (signal 15)\n"];
|
||||
let ri: i32 = 0;
|
||||
for (ri < runtargets.len) {
|
||||
let testav: []str = [driver(stages[si]), "test"];
|
||||
append(testav, "-o"); append(testav, "/dev/null");
|
||||
append(testav, runtimepath(runtargets[ri]));
|
||||
rewritefile(ctrace, ""); rewritefile(atrace, "");
|
||||
rewritefile(ltrace, ""); rewritefile(lastout, "");
|
||||
runcommandenvdir(root, strings.concat("null-test-run-", tags[si],
|
||||
"-", boundarypkgname(ri)), testav, env, root,
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, exitcodes[ri]);
|
||||
assert(has(out.stdout, needles[ri]));
|
||||
assert(occurrences(readfile(ltrace), "\n") == 1);
|
||||
discardedlinkcleaned(lastout);
|
||||
if (si == 0) {
|
||||
testoutrefs[ri] = strings.dup(out.stdout);
|
||||
testerrrefs[ri] = strings.dup(out.stderr);
|
||||
} else {
|
||||
assert(same(testoutrefs[ri], out.stdout));
|
||||
assert(same(testerrrefs[ri], out.stderr));
|
||||
};
|
||||
ri += 1;
|
||||
};
|
||||
assert(!os.exists("/dev/null.new"));
|
||||
assert(!os.exists("/dev/null.sepwork"));
|
||||
si += 1;
|
||||
};
|
||||
|
||||
// Persisted semantic artifacts remain byte-identical across stages. Output
|
||||
// disposition is request metadata and does not enter these identities.
|
||||
let actions: []str = ["base", "alpha"];
|
||||
let ai: i32 = 0;
|
||||
for (ai < actions.len) {
|
||||
let xi: i32 = 0;
|
||||
for (xi < suffixes.len) {
|
||||
assert(same(readfile(strings.concat(root, "/work-c/",
|
||||
actions[ai], suffixes[xi])), readfile(strings.concat(root,
|
||||
"/work-ww/", actions[ai], suffixes[xi]))));
|
||||
xi += 1;
|
||||
};
|
||||
ai += 1;
|
||||
};
|
||||
|
||||
// Independent Cstage and WWstage discard requests can execute together.
|
||||
// Their private products and persistent generations remain isolated.
|
||||
let pcwork: str = strings.concat(root, "/parallel-c-work");
|
||||
let pwwork: str = strings.concat(root, "/parallel-ww-work");
|
||||
mkdirall(pcwork); mkdirall(pwwork);
|
||||
let pcav: []str = [driver("ww"), "build", "-w", pcwork,
|
||||
"-I", source, "-o", "/dev/null", alpha, beta, library];
|
||||
let pwav: []str = [driver("ww_ww"), "build", "-w", pwwork,
|
||||
"-I", source, "-o", "/dev/null", alpha, beta, library];
|
||||
let pc: exec.command;
|
||||
pc.path = pcav[0]; pc.argv = pcav; pc.env = os.getenvs(); pc.dir = root;
|
||||
pc.stdoutpath = strings.concat(root, "/parallel-c.stdout");
|
||||
pc.stderrpath = strings.concat(root, "/parallel-c.stderr");
|
||||
pc.deadline = time.add(time.now(time.clock.monotonic),
|
||||
(120i64 * (time.second: i64)): time.duration);
|
||||
pc.grace = (100i64 * (time.millisecond: i64)): time.duration;
|
||||
let pw: exec.command;
|
||||
pw.path = pwav[0]; pw.argv = pwav; pw.env = os.getenvs(); pw.dir = root;
|
||||
pw.stdoutpath = strings.concat(root, "/parallel-ww.stdout");
|
||||
pw.stderrpath = strings.concat(root, "/parallel-ww.stderr");
|
||||
pw.deadline = time.add(time.now(time.clock.monotonic),
|
||||
(120i64 * (time.second: i64)): time.duration);
|
||||
pw.grace = (100i64 * (time.millisecond: i64)): time.duration;
|
||||
let pcp: exec.process;
|
||||
let pwp: exec.process;
|
||||
exec.start(&pcp, &pc); exec.start(&pwp, &pw);
|
||||
let pcdone: bool = false;
|
||||
let pwdone: bool = false;
|
||||
for (!pcdone || !pwdone) {
|
||||
if (!pcdone) { pcdone = exec.poll(&pcp); };
|
||||
if (!pwdone) { pwdone = exec.poll(&pwp); };
|
||||
if (!pcdone || !pwdone) {
|
||||
time.sleep(time.millisecond, time.clock.monotonic);
|
||||
};
|
||||
};
|
||||
assert(pcp.result.errno == 0 && pcp.result.cleanuperrno == 0);
|
||||
assert(pwp.result.errno == 0 && pwp.result.cleanuperrno == 0);
|
||||
assert(pcp.result.termination == exec.termination.EXIT
|
||||
&& pcp.result.code == 0);
|
||||
assert(pwp.result.termination == exec.termination.EXIT
|
||||
&& pwp.result.code == 0);
|
||||
assert(readfile(pc.stdoutpath).len == 0 && readfile(pc.stderrpath).len == 0);
|
||||
assert(readfile(pw.stdoutpath).len == 0 && readfile(pw.stderrpath).len == 0);
|
||||
assert(!directoryhasnew(pcwork) && !directoryhasnew(pwwork));
|
||||
ai = 0;
|
||||
let parallel: []str = ["base", "alpha", "beta", "library"];
|
||||
for (ai < parallel.len) {
|
||||
let xi: i32 = 0;
|
||||
for (xi < suffixes.len) {
|
||||
assert(same(readfile(strings.concat(pcwork, "/", parallel[ai],
|
||||
suffixes[xi])), readfile(strings.concat(pwwork, "/",
|
||||
parallel[ai], suffixes[xi]))));
|
||||
xi += 1;
|
||||
};
|
||||
ai += 1;
|
||||
};
|
||||
|
||||
let nullafter: os.filestat;
|
||||
match (os.stat(&nullafter, "/dev/null")) {
|
||||
case void => void;
|
||||
case let e: os.oserror => abort("cannot restat /dev/null");
|
||||
};
|
||||
assert((nullafter.mode as u32) == (nullbefore.mode as u32));
|
||||
assert(nullafter.inode == nullbefore.inode);
|
||||
assert(nullafter.sz == nullbefore.sz);
|
||||
assert(!os.exists("/dev/null.new"));
|
||||
assert(!os.exists("/dev/null.sepwork"));
|
||||
clean(root);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user