build: omit named test source operands

This commit is contained in:
2026-08-22 21:53:10 +09:00
parent 3922f1c34e
commit 2e5451a601
13 changed files with 1666 additions and 21 deletions

View File

@@ -705,6 +705,136 @@ fn directoryisempty(path: str) bool = {
return true;
};
fn snapshotu64(out: *[]u8, value: u64) void = {
let v: u64 = value;
let i: i32 = 0;
for (i < 8) {
append(*out, (v & 255u64): u8);
v >>= 8u64;
i += 1;
};
};
fn snapshotbytes(out: *[]u8, value: str) void = {
snapshotu64(out, value.len: u64);
let i: i32 = 0;
for (i < value.len) { append(*out, value[i]); i += 1; };
};
fn treepaths(root: str, relative: str, paths: *[]str) void = {
let current: str = root;
if (relative.len != 0) { current = strings.concat(root, "/", relative); };
let fd: i32 = os.open(current, os.flag.RDONLY, 0i32);
assert(fd >= 0);
let buf: []u8 = alloc([], 16384u64)!;
buf.len = 16384;
let n: i64 = os.getdents64(fd, buf.ptr, buf.len: u64);
for (n > 0i64) {
let off: i32 = 0;
for (off < n: i32) {
let reclen: i32 = (buf[off + 16]: i32)
+ ((buf[off + 17]: i32) * 256);
assert(reclen >= 20 && off + reclen <= n: i32);
let len: i32 = 0;
for (buf[off + 19 + len] != 0u8) { len += 1; };
let dot: bool = len == 1 && buf[off + 19] == '.';
let dotdot: bool = len == 2 && buf[off + 19] == '.'
&& buf[off + 20] == '.';
if (!dot && !dotdot) {
let name: str;
name.ptr = buf.ptr + (off + 19): u64;
name.len = len;
let child: str = strings.dup(name);
if (relative.len != 0) {
child = strings.concat(relative, "/", child);
};
append(*paths, child);
let full: str = strings.concat(root, "/", child);
let fi: os.filestat;
match (os.lstat(&fi, full)) {
case void => void;
case let e: os.oserror => abort("tree lstat failed");
};
let kind: u32 = (fi.mode: u32) & 61440u32;
if (kind == os.mode.DIR: u32) {
treepaths(root, child, paths);
};
};
off += reclen;
};
n = os.getdents64(fd, buf.ptr, buf.len: u64);
};
assert(n == 0i64);
assert(os.close(fd) == 0);
};
// Canonical complete-tree state: relative names, types, modes, and regular or
// symlink bytes. Timestamps and inode numbers are observation metadata, not
// semantic build state, and are deliberately excluded.
fn treesnapshotfiltered(root: str, skipdriver: bool) str = {
let paths: []str = alloc([], 16u64)!;
append(paths, "");
treepaths(root, "", &paths);
let i: i32 = 1;
for (i < paths.len) {
let j: i32 = i;
for (j > 0 && strings.compare(paths[j - 1], paths[j]) > 0) {
let swap: str = paths[j - 1];
paths[j - 1] = paths[j];
paths[j] = swap;
j -= 1;
};
i += 1;
};
let out: []u8 = alloc([], 4096u64)!;
i = 0;
for (i < paths.len) {
if (skipdriver && same(paths[i], ".wwtool.ww")) {
i += 1;
continue;
};
let full: str = root;
if (paths[i].len != 0) {
full = strings.concat(root, "/", paths[i]);
};
let fi: os.filestat;
match (os.lstat(&fi, full)) {
case void => void;
case let e: os.oserror => abort("snapshot lstat failed");
};
snapshotbytes(&out, paths[i]);
snapshotu64(&out, fi.mode: u32: u64);
let kind: u32 = (fi.mode: u32) & 61440u32;
if (kind == os.mode.REG: u32) {
snapshotbytes(&out, readfile(full));
} else { if (kind == os.mode.LINK: u32) {
let target: [4096]u8;
let got: i64 = os.readlink(full, &target[0], 4096u64);
assert(got >= 0i64 && got <= 4096i64);
let value: str;
value.ptr = &target[0];
value.len = got: i32;
snapshotbytes(&out, value);
} else {
snapshotu64(&out, 0u64);
}; };
i += 1;
};
return strings.frombytes(out);
};
fn treesnapshot(root: str) str = {
return treesnapshotfiltered(root, false);
};
// The two bootstrap stages intentionally snapshot different driver binaries
// in `.wwtool.ww`. Exclude only that producer-provenance file when comparing
// semantic artifacts across stages; per-stage preservation uses the complete
// snapshot above and therefore still covers it.
fn artifacttreesnapshot(root: str) str = {
return treesnapshotfiltered(root, true);
};
fn discardedlinkcleaned(marker: str) void = {
let output: str = readfile(marker);
assert(output.len != 0);
@@ -1335,6 +1465,493 @@ fn cwdwritedata(dir: str, label: str) void = {
clean(root);
};
// A visible raw test-source build observes only its initial package/import
// header. It must not turn that observation into a package, import, action,
// artifact, or work-state identity.
@test fn named_test_sources_are_header_loaded_then_omitted() void = {
let root: str = fresh();
let source: str = strings.concat(root, "/source");
let dirsource: str = strings.concat(source, "/directory");
let dirtarget: str = strings.concat(source, "/directory-target");
mkdirall(source); mkdirall(dirsource); mkdirall(dirtarget);
let ignored: str = strings.concat(source, "/raw_windows_test.ww");
let platform: str = strings.concat(source, "/platform_windows_test.ww");
let badpackage: str = strings.concat(source, "/bad_package_test.ww");
let badimport: str = strings.concat(source, "/bad_import_test.ww");
let bodyutf8: str = strings.concat(source, "/body_utf8_test.ww");
let bodybom: str = strings.concat(source, "/body_bom_test.ww");
let bodynul: str = strings.concat(source, "/body_nul_test.ww");
let badcomment: str = strings.concat(source, "/bad_comment_test.ww");
let linktarget: str = strings.concat(source, "/target.ww");
let link: str = strings.concat(source, "/requested_test.ww");
let dirlink: str = strings.concat(source, "/directory_test.ww");
let rawmain: str = strings.concat(source, "/raw.ww");
let runsource: str = strings.concat(source, "/run_test.ww");
let testroute: str = strings.concat(source, "/route_test.ww");
let warmpath: str = strings.concat(source, "/warm.ww");
let warmtest: str = strings.concat(source, "/warm_test.ww");
let header: str = strings.concat(
"package raw_test;\nimport absent.pkg;\n",
"this body is deliberately malformed and never reaches a producer {\n",
"import late.missing;\n");
writefile(ignored, header);
writefile(platform, header);
writefile(linktarget, header);
writefile(badpackage, "this source has no package clause\n");
writefile(badimport, "package malformed;\nimport ;\n");
putinvalidutf8file(bodyutf8, "package body;\n",
"fn deliberately_unparsed(\n", false);
writemidbomfile(bodybom, "package body;\n",
"fn deliberately_unparsed(\n");
writenulfile(bodynul, "package body;\n", "fn body() void = {};\n");
writefile(badcomment, "package body;\n/* unterminated");
writefile(rawmain, "package main;\nfn main() i32 = { return 4; };\n");
writefile(runsource,
"package main;\nfn main() i32 = { return 9; };\n");
writefile(testroute, strings.concat(
"package route;\n",
"@test fn still_selected() void = { assert(true); };\n"));
writefile(warmpath, "package main;\nfn main() i32 = { return 6; };\n");
writefile(strings.concat(dirsource, "/main.ww"),
"package main;\nfn main() i32 = { return 3; };\n");
writefile(strings.concat(dirtarget, "/main.ww"),
"package main;\nfn main() i32 = { return 5; };\n");
assert(os.symlink(linktarget, link) == 0);
assert(os.symlink(dirtarget, dirlink) == 0);
let compilerwrapper: str = strings.concat(root, "/testonly-w6c.sh");
let assemblerwrapper: str = strings.concat(root, "/testonly-w6a.sh");
let linkerwrapper: str = strings.concat(root, "/testonly-w6l.sh");
writeexecutable(compilerwrapper, strings.concat(
"#!/bin/sh\nprintf 'compile\\n' >> \"$WW_TESTONLY_CTRACE\"\n",
"exec \"$WW_TESTONLY_REAL_C\" \"$@\"\n"));
writeexecutable(assemblerwrapper, strings.concat(
"#!/bin/sh\nprintf 'assemble\\n' >> \"$WW_TESTONLY_ATRACE\"\n",
"exec \"$WW_TESTONLY_REAL_A\" \"$@\"\n"));
writeexecutable(linkerwrapper, strings.concat(
"#!/bin/sh\nprintf 'link\\n' >> \"$WW_TESTONLY_LTRACE\"\n",
"exec \"$WW_TESTONLY_REAL_L\" \"$@\"\n"));
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 packagebad: []str = ["", ""];
let importbad: []str = ["", ""];
let nulbad: []str = ["", ""];
let commentbad: []str = ["", ""];
let multibad: []str = ["", ""];
let environments: [][]str = alloc([], stages.len: u64)!;
let tracecs: []str = alloc([], stages.len: u64)!;
let traceas: []str = alloc([], stages.len: u64)!;
let tracels: []str = alloc([], stages.len: u64)!;
let cwarmbin: str = "";
let cwarmartifacts: str = "";
let ccompiled: str = "";
let ctestasm: str = "";
let crawbin: str = "";
let baseenv: []str = os.getenvs();
let si: i32 = 0;
for (si < stages.len) {
let ctrace: str = strings.concat(root, "/testonly-c-", tags[si]);
let atrace: str = strings.concat(root, "/testonly-a-", tags[si]);
let ltrace: str = strings.concat(root, "/testonly-l-", tags[si]);
writefile(ctrace, ""); writefile(atrace, ""); writefile(ltrace, "");
append(tracecs, ctrace); append(traceas, atrace); append(tracels, ltrace);
let env: []str = alloc([], (baseenv.len + 10): 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_TESTONLY_")) {
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_TESTONLY_CTRACE=", ctrace));
append(env, strings.concat("WW_TESTONLY_ATRACE=", atrace));
append(env, strings.concat("WW_TESTONLY_LTRACE=", ltrace));
append(env, strings.concat("WW_TESTONLY_REAL_C=", driver(compilers[si])));
append(env, strings.concat("WW_TESTONLY_REAL_A=", driver(assemblers[si])));
append(env, strings.concat("WW_TESTONLY_REAL_L=", driver(linkers[si])));
append(environments, env);
let out: commandout;
let unused: str = strings.concat(root, "/unused-", tags[si]);
let defaultav: []str = [driver(stages[si]), "build", "-w", unused,
ignored];
runcommandenv(root, strings.concat("testonly-default-", tags[si]),
defaultav, env, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
let defaultoutput: str = strings.concat(source, "/raw_windows_test");
let defaultsepwork: str = strings.concat(defaultoutput, ".sepwork");
assert(out.stdout.len == 0 && out.stderr.len == 0
&& !os.exists(unused) && !os.exists(defaultoutput)
&& !os.exists(defaultsepwork)
&& readfile(ctrace).len == 0 && readfile(atrace).len == 0
&& readfile(ltrace).len == 0);
let asmwork: str = strings.concat(root, "/asm-unused-", tags[si]);
let asmav: []str = [driver(stages[si]), "build", "-S", "-w", asmwork,
ignored];
runcommandenv(root, strings.concat("testonly-assembly-", tags[si]),
asmav, env, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0 && !os.exists(asmwork)
&& !os.exists(defaultoutput) && !os.exists(defaultsepwork)
&& readfile(ctrace).len == 0 && readfile(atrace).len == 0
&& readfile(ltrace).len == 0);
let nullwork: str = strings.concat(root, "/null-unused-", tags[si]);
let nullav: []str = [driver(stages[si]), "build", "-w", nullwork,
"-o", "/dev/null", ignored];
runcommandenv(root, strings.concat("testonly-null-", tags[si]),
nullav, env, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0 && !os.exists(nullwork)
&& readfile(ctrace).len == 0 && readfile(atrace).len == 0
&& readfile(ltrace).len == 0);
let filework: str = strings.concat(root, "/file-unused-", tags[si]);
let fileout: str = strings.concat(root, "/file-output-", tags[si]);
let fileav: []str = [driver(stages[si]), "build", "-w", filework,
"-o", fileout, ignored];
runcommandenv(root, strings.concat("testonly-file-", tags[si]),
fileav, env, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(out.stdout.len == 0 && same(out.stderr, "ww: no packages to build\n")
&& !os.exists(filework) && !os.exists(fileout)
&& !os.exists(strings.concat(fileout, ".new"))
&& readfile(ctrace).len == 0 && readfile(atrace).len == 0
&& readfile(ltrace).len == 0);
let fileasmav: []str = [driver(stages[si]), "build", "-S", "-w",
filework, "-o", fileout, ignored];
runcommandenv(root, strings.concat("testonly-file-assembly-", tags[si]),
fileasmav, env, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(out.stdout.len == 0 && same(out.stderr, "ww: no packages to build\n")
&& !os.exists(filework) && !os.exists(fileout));
let outputdir: str = strings.concat(root, "/output-dir-", tags[si]);
let marker: str = strings.concat(outputdir, "/marker");
mkdirall(outputdir); writefile(marker, "caller-owned\n");
let dirwork: str = strings.concat(root, "/dir-unused-", tags[si]);
let dirav: []str = [driver(stages[si]), "build", "-w", dirwork,
"-o", outputdir, ignored];
runcommandenv(root, strings.concat("testonly-directory-", tags[si]),
dirav, env, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(out.stdout.len == 0
&& same(out.stderr, "ww: no main packages to build\n")
&& same(readfile(marker), "caller-owned\n") && !os.exists(dirwork));
let trailingav: []str = [driver(stages[si]), "build", "-w", dirwork,
"-o", strings.concat(outputdir, "/"), ignored];
runcommandenv(root, strings.concat("testonly-directory-trailing-", tags[si]),
trailingav, env, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(out.stdout.len == 0
&& same(out.stderr, "ww: no main packages to build\n")
&& same(readfile(marker), "caller-owned\n") && !os.exists(dirwork)
&& readfile(ctrace).len == 0 && readfile(atrace).len == 0
&& readfile(ltrace).len == 0);
let badwork: str = strings.concat(root, "/bad-unused-", tags[si]);
let badout: str = strings.concat(root, "/bad-output-", tags[si]);
let badpackageav: []str = [driver(stages[si]), "build", "-w", badwork,
"-o", badout, badpackage];
runcommandenv(root, strings.concat("testonly-bad-package-", tags[si]),
badpackageav, env, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(out.stdout.len == 0
&& has(out.stderr, "invalid or missing package clause\n")
&& !has(out.stderr, "no packages to build") && !os.exists(badwork)
&& !os.exists(badout) && readfile(ctrace).len == 0
&& readfile(atrace).len == 0 && readfile(ltrace).len == 0);
packagebad[si] = strings.dup(out.stderr);
let badimportav: []str = [driver(stages[si]), "build", "-w", badwork,
"-o", badout, badimport];
runcommandenv(root, strings.concat("testonly-bad-import-", tags[si]),
badimportav, env, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
let importwant: str = strings.concat(badimport,
":2:8: error: expected identifier, got ;\n");
assert(out.stdout.len == 0 && same(out.stderr, importwant)
&& !has(out.stderr, "no packages to build") && !os.exists(badwork)
&& !os.exists(badout) && readfile(ctrace).len == 0
&& readfile(atrace).len == 0 && readfile(ltrace).len == 0);
importbad[si] = strings.dup(out.stderr);
// Pinned readGoInfo discards the first ordinary body stop byte, even
// when it starts malformed UTF-8 or a non-leading BOM. Reached NUL and
// unterminated header-trivia comments remain loader errors.
let utf8av: []str = [driver(stages[si]), "build", "-w", badwork,
"-o", badout, bodyutf8];
runcommandenv(root, strings.concat("testonly-body-utf8-", tags[si]),
utf8av, env, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(out.stdout.len == 0
&& same(out.stderr, "ww: no packages to build\n")
&& !os.exists(badwork) && !os.exists(badout));
let bomav: []str = [driver(stages[si]), "build", "-w", badwork,
"-o", badout, bodybom];
runcommandenv(root, strings.concat("testonly-body-bom-", tags[si]),
bomav, env, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(out.stdout.len == 0
&& same(out.stderr, "ww: no packages to build\n")
&& !os.exists(badwork) && !os.exists(badout));
let nulav: []str = [driver(stages[si]), "build", "-w", badwork,
"-o", badout, bodynul];
runcommandenv(root, strings.concat("testonly-body-nul-", tags[si]),
nulav, env, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
let nulwant: str = strings.concat(bodynul,
":2:1: error: invalid NUL character\n");
assert(out.stdout.len == 0 && same(out.stderr, nulwant)
&& !os.exists(badwork) && !os.exists(badout));
nulbad[si] = strings.dup(out.stderr);
let commentav: []str = [driver(stages[si]), "build", "-w", badwork,
"-o", badout, badcomment];
runcommandenv(root, strings.concat("testonly-bad-comment-", tags[si]),
commentav, env, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
let commentwant: str = strings.concat(badcomment,
":2:16: error: unterminated /* comment\n");
assert(out.stdout.len == 0 && same(out.stderr, commentwant)
&& !os.exists(badwork) && !os.exists(badout)
&& readfile(ctrace).len == 0 && readfile(atrace).len == 0
&& readfile(ltrace).len == 0);
commentbad[si] = strings.dup(out.stderr);
let spellingwork: str = strings.concat(root, "/spelling-unused-", tags[si]);
let platformav: []str = [driver(stages[si]), "build", "-w",
spellingwork, platform];
runcommandenv(root, strings.concat("testonly-platform-", tags[si]),
platformav, env, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0 && !os.exists(spellingwork));
let linkav: []str = [driver(stages[si]), "build", "-w", spellingwork,
link];
runcommandenv(root, strings.concat("testonly-requested-link-", tags[si]),
linkav, env, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0 && !os.exists(spellingwork)
&& readfile(ctrace).len == 0 && readfile(atrace).len == 0
&& readfile(ltrace).len == 0);
// A directory target remains a directory request despite a test-looking
// requested spelling; this is deliberately the one producer-positive row.
let linkdirwork: str = strings.concat(root, "/link-dir-work-", tags[si]);
let linkdirout: str = strings.concat(root, "/link-dir-output-", tags[si]);
mkdirall(linkdirwork);
let linkdirav: []str = [driver(stages[si]), "build", "-w", linkdirwork,
"-o", linkdirout, dirlink];
runcommandenv(root, strings.concat("testonly-directory-link-", tags[si]),
linkdirav, env, (60i64 * (time.second: i64)): time.duration, &out);
if (si == 0) {
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0
&& os.exists(linkdirout) && readfile(ctrace).len != 0
&& readfile(atrace).len != 0 && readfile(ltrace).len != 0);
} else {
// The selected non-directory classifier must not absorb this
// pre-existing WWstage resolution difference into the slice.
expectexit(&out, 1);
assert(out.stdout.len == 0
&& same(out.stderr, "ww: cannot read source\n")
&& !os.exists(linkdirout) && readfile(ctrace).len == 0
&& readfile(atrace).len == 0 && readfile(ltrace).len == 0);
};
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
// A valid retained raw source seeds state. Renaming only its requested
// basename to test-only must leave all public and private bytes untouched.
let warmwork: str = strings.concat(root, "/warm-work-", tags[si]);
let warmout: str = strings.concat(root, "/warm-output-", tags[si]);
mkdirall(warmwork);
let warmav: []str = [driver(stages[si]), "build", "-w", warmwork,
"-o", warmout, warmpath];
runcommandenv(root, strings.concat("testonly-warm-seed-", tags[si]),
warmav, env, (60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(warmout));
let warmbin: str = strings.dup(readfile(warmout));
let warmtree: str = strings.dup(treesnapshot(warmwork));
if (si == 0) {
cwarmbin = strings.dup(warmbin);
cwarmartifacts = strings.dup(artifacttreesnapshot(warmwork));
} else {
assert(same(cwarmbin, warmbin)
&& same(cwarmartifacts, artifacttreesnapshot(warmwork)));
};
assert(os.rename(warmpath, warmtest) == 0);
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
let warmtestav: []str = [driver(stages[si]), "build", "-w", warmwork,
"-o", warmout, warmtest];
runcommandenv(root, strings.concat("testonly-warm-omit-", tags[si]),
warmtestav, env, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(out.stdout.len == 0 && same(out.stderr, "ww: no packages to build\n")
&& same(warmbin, readfile(warmout)) && !os.exists(strings.concat(warmout, ".new"))
&& readfile(ctrace).len == 0 && readfile(atrace).len == 0
&& readfile(ltrace).len == 0 && !directoryhasnew(warmwork)
&& !directoryhasfragment(warmwork, ".wwtxn.")
&& same(warmtree, treesnapshot(warmwork)));
if (si == 1) {
assert(same(cwarmbin, readfile(warmout))
&& same(cwarmartifacts, artifacttreesnapshot(warmwork)));
};
assert(os.rename(warmtest, warmpath) == 0);
runcommandenv(root, strings.concat("testonly-warm-restored-", tags[si]),
warmav, env, (60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0
&& same(warmbin, readfile(warmout)) && !directoryhasnew(warmwork)
&& same(warmtree, treesnapshot(warmwork)));
if (si == 1) {
assert(same(cwarmbin, readfile(warmout))
&& same(cwarmartifacts, artifacttreesnapshot(warmwork)));
};
// Test and run keep their existing raw-source semantics, while a visible
// non-test raw build and a multi-operand error do not enter this slice.
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
let testwork: str = strings.concat(root, "/route-work-", tags[si]);
mkdirall(testwork);
let testav: []str = [driver(stages[si]), "test", "-w", testwork,
testroute];
runcommandenv(root, strings.concat("testonly-raw-test-", tags[si]),
testav, env, (60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stderr.len == 0 && has(out.stdout, "still_selected ... ok\n")
&& readfile(ctrace).len != 0);
let compiled: str = strings.concat(root, "/route-compiled-", tags[si]);
let compileav: []str = [driver(stages[si]), "test", "-c", "-o", compiled,
testroute];
runcommandenv(root, strings.concat("testonly-raw-test-compile-", tags[si]),
compileav, env, (60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(compiled));
let compiledbytes: str = strings.dup(readfile(compiled));
if (si == 0) { ccompiled = strings.dup(compiledbytes); }
else { assert(same(ccompiled, compiledbytes)); };
let testasm: str = strings.concat(root, "/route-assembly-", tags[si]);
let testasmav: []str = [driver(stages[si]), "test", "-S", "-o", testasm,
testroute];
runcommandenv(root, strings.concat("testonly-raw-test-assembly-", tags[si]),
testasmav, env, (60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
let testasmtree: str = strings.concat(testasm, ".sepwork");
assert(out.stdout.len == 0 && out.stderr.len == 0
&& os.exists(testasmtree));
let testasmstate: str = strings.dup(artifacttreesnapshot(testasmtree));
if (si == 0) { ctestasm = strings.dup(testasmstate); }
else { assert(same(ctestasm, testasmstate)); };
let runav: []str = [driver(stages[si]), "run", runsource];
runcommandenv(root, strings.concat("testonly-run-", tags[si]), runav,
env, (60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 9);
let rawout: str = strings.concat(root, "/raw-output-", tags[si]);
let rawav: []str = [driver(stages[si]), "build", "-o", rawout, rawmain];
runcommandenv(root, strings.concat("testonly-raw-build-", tags[si]),
rawav, env, (60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(rawout));
let rawbytes: str = strings.dup(readfile(rawout));
if (si == 0) { crawbin = strings.dup(rawbytes); }
else { assert(same(crawbin, rawbytes)); };
let multiout: str = strings.concat(root, "/multi-output-", tags[si]);
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
let multiav: []str = [driver(stages[si]), "build", "-o", multiout,
rawmain, ignored];
runcommandenv(root, strings.concat("testonly-multiple-", tags[si]),
multiav, env, (30i64 * (time.second: i64)): time.duration, &out);
let multiwant: str = strings.concat(
"wwtest package: ", rawmain,
": cannot canonicalize package directory\n",
"wwtest package: ", ignored,
": cannot canonicalize package directory\n");
assert(out.termination == exec.termination.EXIT && out.code != 0
&& out.stdout.len == 0 && same(out.stderr, multiwant)
&& !os.exists(multiout) && readfile(ctrace).len == 0
&& readfile(atrace).len == 0 && readfile(ltrace).len == 0);
multibad[si] = strings.dup(out.stderr);
si += 1;
};
assert(same(packagebad[0], packagebad[1]));
assert(same(importbad[0], importbad[1]));
assert(same(nulbad[0], nulbad[1]));
assert(same(commentbad[0], commentbad[1]));
assert(same(multibad[0], multibad[1]));
// Two simultaneous omitted requests have no work/output collision and no
// producer invocation in either stage.
let ti: i32 = 0;
for (ti < stages.len) {
rewritefile(tracecs[ti], "");
rewritefile(traceas[ti], "");
rewritefile(tracels[ti], "");
ti += 1;
};
let pcwork: str = strings.concat(root, "/parallel-c-unused");
let pwwork: str = strings.concat(root, "/parallel-ww-unused");
let pcav: []str = [driver("ww"), "build", "-w", pcwork, ignored];
let pwav: []str = [driver("ww_ww"), "build", "-w", pwwork, ignored];
let pc: exec.command;
pc.path = pcav[0]; pc.argv = pcav; pc.env = environments[0]; pc.dir = repo();
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),
(30i64 * (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 = environments[1]; pw.dir = repo();
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),
(30i64 * (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
&& pcp.result.termination == exec.termination.EXIT && pcp.result.code == 0
&& pwp.result.errno == 0 && pwp.result.cleanuperrno == 0
&& pwp.result.termination == exec.termination.EXIT && pwp.result.code == 0);
assert(readfile(pc.stdoutpath).len == 0 && readfile(pc.stderrpath).len == 0
&& readfile(pw.stdoutpath).len == 0 && readfile(pw.stderrpath).len == 0
&& !os.exists(pcwork) && !os.exists(pwwork));
ti = 0;
for (ti < stages.len) {
assert(readfile(tracecs[ti]).len == 0
&& readfile(traceas[ti]).len == 0
&& readfile(tracels[ti]).len == 0);
ti += 1;
};
assert(!directoryhasnew(root) && !directoryhasfragment(root, ".wwtxn.")
&& !directoryhasfragment(root, ".install")
&& !directoryhasfragment(root, ".capture")
&& !directoryhasfragment(root, ".result")
&& !directoryhasfragment(root, ".request"));
clean(root);
};
// A source file has one contiguous import section immediately after its
// package clause. The parser may continue after a late import for recovery,
// but the imports-only loader and the full compiler parser both reject it.