package package_test; // Make builds and invokes this one binary; discovery, subprocess ownership, // output interpretation, and assertions remain WW code. import crypto.sha256; import hash; import os; import os.exec; import strings; import temp; import time; type commandout = struct { termination: exec.termination, code: i32, stdout: str, stderr: str, }; fn wrongsuffixrecord(out: *commandout, code: i32, records: *[]str) void = { expectexit(out, code); append(*records, strings.dup(out.stdout)); append(*records, strings.dup(out.stderr)); }; fn wrongsuffixsame(a: []str, b: []str) void = { assert(a.len == b.len); let i: i32 = 0; for (i < a.len) { assert(same(a[i], b[i])); i += 1; }; }; fn wrongsuffixstageparity(a: []str, b: []str) void = { assert(a.len == b.len); let i: i32 = 0; for (i < a.len) { if (i != 3 && i != 10 && i != 13 && i != 17 && i != 20) { assert(same(a[i], b[i])); }; i += 1; }; }; fn wrongsuffixartifactparity(a: str, b: str, names: []str) void = { let i: i32 = 0; for (i < names.len) { assert(same(readfile(strings.concat(a, "/", names[i])), readfile(strings.concat(b, "/", names[i])))); i += 1; }; }; fn wrongsuffixprovenance(path: str) bool = { return same(path, ".wwtool.ww") || same(path, ".wwtool.w6c") || same(path, ".wwtool.w6a"); }; // Producer paths are execution provenance rather than semantic action state; // the stamp and every other relative entry remain part of parity. fn wrongsuffixsemantictreesnapshot(root: str) 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 (wrongsuffixprovenance(paths[i])) { 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("wrong suffix 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 wrongsuffixsemanticparity(a: str, b: str) void = { assert(same(wrongsuffixsemantictreesnapshot(a), wrongsuffixsemantictreesnapshot(b))); }; fn wrongsuffixfamilyabsent(work: str, action: str) void = { let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a", ".init.unit.ww", ".init.s", ".init.o"]; let i: i32 = 0; for (i < suffixes.len) { assert(!os.exists(strings.concat(work, "/", action, suffixes[i]))); i += 1; }; }; fn wrongsuffixpathfragment(root: str, needle: str) bool = { let paths: []str = alloc([], 16u64)!; treepaths(root, "", &paths); let i: i32 = 0; for (i < paths.len) { if (has(paths[i], needle)) { return true; }; i += 1; }; return false; }; fn wrongsuffixlogicalcapture(root: str, label: str, stage: str, dir: str, buildsource: str, testsource: str, records: *[]str) void = { let deadline: time.duration = (120i64 * (time.second: i64)): time.duration; let marker: str = "WRONG_SUFFIX_BYTES_MUST_NOT_APPEAR"; let roots: []str = ["/__root.unit.ww", "/__root.wwi", "/__root.s", "/__root.o", "/__root.a", "/__root.init.unit.ww", "/__root.init.s", "/__root.init.o"]; let out: commandout; let buildwork: str = strings.concat(root, "/", label, "-build-work"); let buildout: str = strings.concat(root, "/", label, "-build-output"); mkdirall(buildwork); let buildav: []str = [driver(stage), "build", "-w", buildwork, "-I", dir, "-o", buildout, "foo.bar"]; runcommanddir(root, strings.concat(label, "-build"), dir, buildav, deadline, &out); wrongsuffixrecord(&out, 0, records); assert(os.exists(buildout) && !has(out.stdout, marker) && !has(out.stderr, marker)); append(*records, strings.dup(readfile(buildout))); let buildtree: str = strings.dup(artifacttreesnapshot(buildwork)); append(*records, buildtree); let i: i32 = 0; for (i < roots.len) { assert(os.exists(strings.concat(buildwork, roots[i]))); i += 1; }; wrongsuffixfamilyabsent(buildwork, "foo.bar"); assert(!directoryhasnew(buildwork) && !directoryhasfragment(buildwork, ".wwtxn.") && !directoryhasfragment(buildwork, ".install") && !directoryhasfragment(buildwork, ".sepwork")); let builtav: []str = [buildout]; runcommand(root, strings.concat(label, "-built"), builtav, time.second, &out); wrongsuffixrecord(&out, 23, records); let runav: []str = [driver(stage), "run", "-I", dir, "foo.bar"]; runcommanddir(root, strings.concat(label, "-run"), dir, runav, deadline, &out); wrongsuffixrecord(&out, 23, records); assert(!has(out.stdout, marker) && !has(out.stderr, marker)); rewritefile(strings.concat(dir, "/foo/bar.ww"), testsource); let rawwork: str = strings.concat(root, "/", label, "-raw-work"); mkdirall(rawwork); let rawav: []str = [driver(stage), "test", "-w", rawwork, "-I", dir, "foo.bar"]; runcommanddir(root, strings.concat(label, "-raw"), dir, rawav, deadline, &out); wrongsuffixrecord(&out, 0, records); assert(occurrences(out.stdout, "logical_selected ... ok\n") == 1 && occurrences(out.stdout, "logical_second ... ok\n") == 1 && !has(out.stdout, "wrong_physical_selected") && !has(out.stdout, marker) && !has(out.stderr, marker)); let rawtree: str = strings.dup(artifacttreesnapshot(rawwork)); append(*records, rawtree); wrongsuffixfamilyabsent(rawwork, "foo.bar"); assert(os.exists(strings.concat(rawwork, "/__root.unit.ww")) && os.exists(strings.concat(rawwork, "/__root.wwi")) && os.exists(strings.concat(rawwork, "/__root.a")) && !directoryhasnew(rawwork) && !directoryhasfragment(rawwork, ".wwtxn.") && !directoryhasfragment(rawwork, ".sepwork")); let filterwork: str = strings.concat(root, "/", label, "-filter-work"); mkdirall(filterwork); let filterav: []str = [driver(stage), "test", "-w", filterwork, "-I", dir, "foo.bar", "logical_selected"]; runcommanddir(root, strings.concat(label, "-filter"), dir, filterav, deadline, &out); wrongsuffixrecord(&out, 0, records); assert(occurrences(out.stdout, "logical_selected ... ok\n") == 1 && !has(out.stdout, "logical_second") && !has(out.stdout, "wrong_physical_selected") && !has(out.stdout, marker) && !has(out.stderr, marker)); let filtertree: str = strings.dup(artifacttreesnapshot(filterwork)); append(*records, filtertree); wrongsuffixfamilyabsent(filterwork, "foo.bar"); assert(os.exists(strings.concat(filterwork, "/__root.unit.ww")) && !directoryhasnew(filterwork) && !directoryhasfragment(filterwork, ".wwtxn.")); let compilework: str = strings.concat(root, "/", label, "-compile-work"); let compileout: str = strings.concat(root, "/", label, "-compiled.test"); mkdirall(compilework); let compileav: []str = [driver(stage), "test", "-c", "-w", compilework, "-I", dir, "-o", compileout, "foo.bar"]; runcommanddir(root, strings.concat(label, "-compile"), dir, compileav, deadline, &out); wrongsuffixrecord(&out, 0, records); assert(os.exists(compileout) && !has(out.stdout, marker) && !has(out.stderr, marker)); append(*records, strings.dup(readfile(compileout))); let compiletree: str = strings.dup(artifacttreesnapshot(compilework)); append(*records, compiletree); wrongsuffixfamilyabsent(compilework, "foo.bar"); assert(os.exists(strings.concat(compilework, "/__root.unit.ww")) && !directoryhasnew(compilework) && !directoryhasfragment(compilework, ".wwtxn.")); let asmwork: str = strings.concat(root, "/", label, "-asm-work"); let asmout: str = strings.concat(root, "/", label, "-assembly"); mkdirall(asmwork); let asmav: []str = [driver(stage), "test", "-S", "-w", asmwork, "-I", dir, "-o", asmout, "foo.bar"]; runcommanddir(root, strings.concat(label, "-assembly"), dir, asmav, deadline, &out); wrongsuffixrecord(&out, 0, records); let asmtree: str = strings.dup(artifacttreesnapshot(asmwork)); append(*records, asmtree); assert(!os.exists(asmout)); append(*records, "assembly destination absent"); wrongsuffixfamilyabsent(asmwork, "foo.bar"); assert(os.exists(strings.concat(asmwork, "/__root.unit.ww")) && os.exists(strings.concat(asmwork, "/__root.wwi")) && os.exists(strings.concat(asmwork, "/__root.s")) && has(readfile(strings.concat(asmwork, "/__root.unit.ww")), "logical_selected") && !has(readfile(strings.concat(asmwork, "/__root.unit.ww")), "wrong_physical_selected") && !directoryhasnew(asmwork) && !directoryhasfragment(asmwork, ".wwtxn.") && !directoryhasfragment(asmwork, ".sepwork")); rewritefile(strings.concat(dir, "/foo/bar.ww"), buildsource); }; // A physical non-directory can select a named source only through the public // requested spelling. Once that spelling is ineligible, every command must // observe the same logical request and action as if the object were absent. @test fn wrong_suffix_physical_files_do_not_hijack_dotted_requests() void = { let root: str = fresh(); let filedir: str = strings.concat(root, "/file-request"); let logicalparent: str = strings.concat(filedir, "/foo"); let dependency: str = strings.concat(filedir, "/proof/marker"); mkdirall(logicalparent); mkdirall(dependency); let logicalfile: str = strings.concat(logicalparent, "/bar.ww"); let logicalvalid: str = strings.concat( "package main;\nimport proof.marker;\n", "fn main() i32 = { return marker.value(); };\n"); let logicaltestvalid: str = strings.concat( "package main;\nimport proof.marker;\n", "@test fn logical_selected() void = { ", "assert(marker.value() == 23); };\n", "@test fn logical_second() void = { ", "assert(marker.value() != 99); };\n"); let logicalinvalid: str = strings.concat( "package main;\nimport proof.marker;\n", "fn main() i32 = { return marker.missing(); };\n"); let logicalruntimefail: str = strings.concat( "package main;\nimport proof.marker;\n", "@test fn logical_selected() void = { ", "assert(marker.value() == 23); abort(); };\n", "@test fn logical_second() void = { assert(true); };\n"); let wrongbytes: str = strings.concat( "WRONG_SUFFIX_BYTES_MUST_NOT_APPEAR\n", "package main;\nimport never.selected;\n", "fn main() i32 = { return 99; };\n", "@test fn wrong_physical_selected() void = { abort(); };\n"); writefile(logicalfile, logicalvalid); writefile(strings.concat(dependency, "/main.ww"), "package marker;\nexport fn value() i32 = { return 23; };\n"); let collision: str = strings.concat(filedir, "/foo.bar"); let wrongtarget: str = strings.concat(root, "/wrong-target"); writefile(wrongtarget, wrongbytes); let stages: []str = ["ww", "ww_ww"]; let tags: []str = ["c", "ww"]; let reference: []str = alloc([], 32u64)!; let wwreference: []str = alloc([], 32u64)!; let current: []str = alloc([], 32u64)!; wrongsuffixlogicalcapture(root, "file-absent-c", stages[0], filedir, logicalvalid, logicaltestvalid, &reference); wrongsuffixlogicalcapture(root, "file-absent-ww", stages[1], filedir, logicalvalid, logicaltestvalid, ¤t); let ci: i32 = 0; for (ci < current.len) { append(wwreference, strings.dup(current[ci])); ci += 1; }; wrongsuffixstageparity(reference, wwreference); let buildartifacts: []str = ["__root.unit.ww", "__root.wwi", "__root.s", "__root.o", "__root.a", "__root.init.unit.ww", "__root.init.s", "__root.init.o", "proof.marker.unit.ww", "proof.marker.wwi", "proof.marker.s", "proof.marker.o", "proof.marker.a"]; let testartifacts: []str = ["__root.unit.ww", "__root.wwi", "__root.s", "__root.o", "__root.a", "__root.init.unit.ww", "__root.init.s", "__root.init.o", "proof.marker.unit.ww", "proof.marker.wwi", "proof.marker.s", "proof.marker.o", "proof.marker.a", "test.unit.ww", "test.wwi", "test.s", "test.o", "test.a"]; let asmartifacts: []str = ["__root.unit.ww", "__root.wwi", "__root.s", "__root.init.unit.ww", "__root.init.s", "proof.marker.unit.ww", "proof.marker.wwi", "proof.marker.s", "test.unit.ww", "test.wwi", "test.s"]; wrongsuffixartifactparity(strings.concat(root, "/file-absent-c-build-work"), strings.concat(root, "/file-absent-ww-build-work"), buildartifacts); wrongsuffixartifactparity(strings.concat(root, "/file-absent-c-raw-work"), strings.concat(root, "/file-absent-ww-raw-work"), testartifacts); wrongsuffixartifactparity(strings.concat(root, "/file-absent-c-filter-work"), strings.concat(root, "/file-absent-ww-filter-work"), testartifacts); wrongsuffixartifactparity(strings.concat(root, "/file-absent-c-compile-work"), strings.concat(root, "/file-absent-ww-compile-work"), testartifacts); wrongsuffixartifactparity(strings.concat(root, "/file-absent-c-asm-work"), strings.concat(root, "/file-absent-ww-asm-work"), asmartifacts); let modes: []str = ["build", "raw", "filter", "compile", "asm"]; let mi: i32 = 0; for (mi < modes.len) { wrongsuffixsemanticparity(strings.concat(root, "/file-absent-c-", modes[mi], "-work"), strings.concat(root, "/file-absent-ww-", modes[mi], "-work")); mi += 1; }; let state: i32 = 0; for (state < 3) { if (state == 0) { writefile(collision, wrongbytes); } else if (state == 1) { assert(os.symlink(wrongtarget, collision) == 0); } else { assert(os.symlink("/dev/null", collision) == 0); }; let si: i32 = 0; for (si < stages.len) { let observed: []str = alloc([], 32u64)!; let statetag: str = "special"; if (state == 0) { statetag = "regular"; } else if (state == 1) { statetag = "link"; }; let label: str = strings.concat("file-state-", tags[si], "-", statetag); wrongsuffixlogicalcapture(root, label, stages[si], filedir, logicalvalid, logicaltestvalid, &observed); if (si == 0) { wrongsuffixsame(reference, observed); } else { wrongsuffixsame(wwreference, observed); }; si += 1; }; let statetag: str = "special"; if (state == 0) { statetag = "regular"; } else if (state == 1) { statetag = "link"; }; mi = 0; for (mi < modes.len) { wrongsuffixsemanticparity(strings.concat(root, "/file-state-c-", statetag, "-", modes[mi], "-work"), strings.concat(root, "/file-state-ww-", statetag, "-", modes[mi], "-work")); mi += 1; }; assert(os.remove(collision) == 0); state += 1; }; // A single persistent action is reusable while the colliding object moves // only among absent and stat-successful non-directory states. let warmrefs: []str = alloc([], 4u64)!; let runtimepriorrefs: []str = alloc([], 2u64)!; let runtimefailedrefs: []str = alloc([], 2u64)!; let si: i32 = 0; for (si < stages.len) { let ctrace: str = strings.concat(root, "/warm-", tags[si], ".c.trace"); let atrace: str = strings.concat(root, "/warm-", tags[si], ".a.trace"); let ltrace: str = strings.concat(root, "/warm-", tags[si], ".l.trace"); writefile(ctrace, ""); writefile(atrace, ""); writefile(ltrace, ""); let cwrap: str = strings.concat(root, "/warm-", tags[si], "-w6c"); let awrap: str = strings.concat(root, "/warm-", tags[si], "-w6a"); let lwrap: str = strings.concat(root, "/warm-", tags[si], "-w6l"); writeexecutable(cwrap, strings.concat("#!/bin/sh\n", "printf '%s\\n' \"$*\" >> \"$WW_SUFFIX_CTRACE\"\n", "exec \"$WW_SUFFIX_REAL_C\" \"$@\"\n")); writeexecutable(awrap, strings.concat("#!/bin/sh\n", "printf '%s\\n' \"$*\" >> \"$WW_SUFFIX_ATRACE\"\n", "exec \"$WW_SUFFIX_REAL_A\" \"$@\"\n")); writeexecutable(lwrap, strings.concat("#!/bin/sh\n", "printf '%s\\n' \"$*\" >> \"$WW_SUFFIX_LTRACE\"\n", "exec \"$WW_SUFFIX_REAL_L\" \"$@\"\n")); let inherited: []str = os.getenvs(); let env: []str = alloc([], (inherited.len + 9): u64)!; let ei: i32 = 0; for (ei < inherited.len) { if (!strings.hasprefix(inherited[ei], "WW_W6C=") && !strings.hasprefix(inherited[ei], "WW_W6A=") && !strings.hasprefix(inherited[ei], "WW_W6L=") && !strings.hasprefix(inherited[ei], "WW_SUFFIX_")) { append(env, inherited[ei]); }; ei += 1; }; append(env, strings.concat("WW_W6C=", cwrap)); append(env, strings.concat("WW_W6A=", awrap)); append(env, strings.concat("WW_W6L=", lwrap)); append(env, strings.concat("WW_SUFFIX_CTRACE=", ctrace)); append(env, strings.concat("WW_SUFFIX_ATRACE=", atrace)); append(env, strings.concat("WW_SUFFIX_LTRACE=", ltrace)); let realc: str = "w6c_ww"; let reala: str = "w6a_ww"; let reall: str = "w6l_ww"; if (si == 0) { realc = "w6c"; reala = "w6a"; reall = "w6l"; }; append(env, strings.concat("WW_SUFFIX_REAL_C=", driver(realc))); append(env, strings.concat("WW_SUFFIX_REAL_A=", driver(reala))); append(env, strings.concat("WW_SUFFIX_REAL_L=", driver(reall))); let work: str = strings.concat(root, "/warm-", tags[si], "-work"); let output: str = strings.concat(root, "/warm-", tags[si], "-output"); mkdirall(work); let av: []str = [driver(stages[si]), "build", "-w", work, "-I", filedir, "-o", output, "foo.bar"]; let out: commandout; runcommandenvdir(root, strings.concat("warm-", tags[si], "-seed"), av, env, filedir, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && readfile(ctrace).len != 0 && readfile(atrace).len != 0 && readfile(ltrace).len != 0); let binref: str = strings.dup(readfile(output)); let treeref: str = strings.dup(treesnapshot(work)); if (si == 0) { append(warmrefs, strings.dup(binref)); append(warmrefs, strings.dup(artifacttreesnapshot(work))); } else { assert(same(warmrefs[0], binref)); }; rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); runcommandenvdir(root, strings.concat("warm-", tags[si], "-absent-control"), av, env, filedir, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && same(binref, readfile(output)) && same(treeref, treesnapshot(work)) && readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len != 0); let linkref: str = strings.dup(readfile(ltrace)); let transition: i32 = 0; for (transition < 7) { if (transition == 0) { writefile(collision, wrongbytes); } else if (transition == 1) { let chmodav: []str = ["/bin/chmod", "000", collision]; runcommand(root, strings.concat("warm-", tags[si], "-chmod-zero"), chmodav, time.second, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && permissionmode(collision) == 0u32); } else if (transition == 2) { let chmodav: []str = ["/bin/chmod", "600", collision]; runcommand(root, strings.concat("warm-", tags[si], "-chmod-restore"), chmodav, time.second, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && permissionmode(collision) == 384u32); rewritefile(collision, "different malformed bytes { must stay unread\n"); } else if (transition == 3) { assert(os.remove(collision) == 0); assert(os.symlink(wrongtarget, collision) == 0); } else if (transition == 4) { assert(os.remove(collision) == 0); assert(os.symlink("/dev/null", collision) == 0); } else if (transition == 5) { assert(os.remove(collision) == 0); } else { void; }; rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); let transitiontag: str = "repeat"; if (transition == 0) { transitiontag = "regular"; } else if (transition == 1) { transitiontag = "chmod"; } else if (transition == 2) { transitiontag = "mutated"; } else if (transition == 3) { transitiontag = "link"; } else if (transition == 4) { transitiontag = "special"; } else if (transition == 5) { transitiontag = "absent"; }; runcommandenvdir(root, strings.concat("warm-", tags[si], "-", transitiontag), av, env, filedir, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && same(binref, readfile(output)) && same(treeref, treesnapshot(work)) && readfile(ctrace).len == 0 && readfile(atrace).len == 0 && same(linkref, readfile(ltrace)) && !directoryhasnew(work) && !directoryhasfragment(work, ".wwtxn.") && !directoryhasfragment(work, ".install") && !directoryhasfragment(work, ".sepwork")); transition += 1; }; // A reachable logical producer failure rolls back the generation; the // ignored object's malformed content never reaches this path. rewritefile(logicalfile, logicalinvalid); rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); runcommandenvdir(root, strings.concat("warm-", tags[si], "-invalid"), av, env, filedir, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && has(out.stderr, "missing") && same(binref, readfile(output)) && same(treeref, treesnapshot(work)) && readfile(ctrace).len != 0 && !os.exists(strings.concat(output, ".new")) && !os.exists(strings.concat(output, ".sepwork")) && !directoryhasnew(work) && !directoryhasfragment(work, ".wwtxn.") && !wrongsuffixpathfragment(work, ".new") && !wrongsuffixpathfragment(work, ".wwtxn.") && !wrongsuffixpathfragment(work, ".install") && !wrongsuffixpathfragment(work, ".sepwork") && !wrongsuffixpathfragment(work, ".capture") && !wrongsuffixpathfragment(work, ".result") && !wrongsuffixpathfragment(work, ".request")); rewritefile(logicalfile, logicalvalid); runcommandenvdir(root, strings.concat("warm-", tags[si], "-restored"), av, env, filedir, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && same(binref, readfile(output)) && same(treeref, treesnapshot(work))); let testwork: str = strings.concat(root, "/runtime-", tags[si], "-work"); let retained: str = strings.concat(root, "/runtime-", tags[si], ".test"); mkdirall(testwork); let testav: []str = [driver(stages[si]), "test", "-w", testwork, "-I", filedir, "-o", retained, "foo.bar", "logical_selected"]; rewritefile(logicalfile, logicaltestvalid); runcommanddir(root, strings.concat("runtime-", tags[si], "-seed"), filedir, testav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0 && os.exists(retained)); let retainedref: str = strings.dup(readfile(retained)); let runtimepriorfull: str = strings.dup(treesnapshot(testwork)); let runtimepriorsemantic: str = strings.dup( wrongsuffixsemantictreesnapshot(testwork)); append(runtimepriorrefs, strings.dup(runtimepriorsemantic)); rewritefile(logicalfile, logicalruntimefail); runcommanddir(root, strings.concat("runtime-", tags[si], "-failed"), filedir, testav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); let runtimefailedsemantic: str = strings.dup( wrongsuffixsemantictreesnapshot(testwork)); append(runtimefailedrefs, strings.dup(runtimefailedsemantic)); assert(strings.hassuffix(out.stdout, "\nFAIL\n") && out.stderr.len == 0 && same(retainedref, readfile(retained)) && !same(runtimepriorsemantic, runtimefailedsemantic) && os.exists(strings.concat(testwork, "/__root.unit.ww")) && os.exists(strings.concat(testwork, "/__root.wwi")) && os.exists(strings.concat(testwork, "/__root.s")) && os.exists(strings.concat(testwork, "/__root.o")) && os.exists(strings.concat(testwork, "/__root.a")) && !os.exists(strings.concat(retained, ".new")) && !os.exists(strings.concat(retained, ".sepwork")) && !directoryhasnew(testwork) && !wrongsuffixpathfragment(testwork, ".new") && !wrongsuffixpathfragment(testwork, ".wwtxn.") && !wrongsuffixpathfragment(testwork, ".install") && !wrongsuffixpathfragment(testwork, ".sepwork") && !wrongsuffixpathfragment(testwork, ".capture") && !wrongsuffixpathfragment(testwork, ".result") && !wrongsuffixpathfragment(testwork, ".request")); wrongsuffixfamilyabsent(testwork, "foo.bar"); rewritefile(logicalfile, logicaltestvalid); runcommanddir(root, strings.concat("runtime-", tags[si], "-restored"), filedir, testav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(retainedref, readfile(retained)) && same(runtimepriorfull, treesnapshot(testwork))); rewritefile(logicalfile, logicalvalid); si += 1; }; assert(runtimepriorrefs.len == 2 && runtimefailedrefs.len == 2 && same(runtimepriorrefs[0], runtimepriorrefs[1]) && same(runtimefailedrefs[0], runtimefailedrefs[1])); wrongsuffixartifactparity(strings.concat(root, "/warm-c-work"), strings.concat(root, "/warm-ww-work"), buildartifacts); wrongsuffixsemanticparity(strings.concat(root, "/warm-c-work"), strings.concat(root, "/warm-ww-work")); // A directory selected through the same dotted request retains dotted // action identity; a physical collision cannot turn it into __root. let dirdir: str = strings.concat(root, "/directory-request"); let dirprovider: str = strings.concat(dirdir, "/foo/bar"); let dirdep: str = strings.concat(dirdir, "/proof/marker"); mkdirall(dirprovider); mkdirall(dirdep); writefile(strings.concat(dirprovider, "/main.ww"), strings.concat( "package main;\nimport proof.marker;\n", "fn main() i32 = { return marker.value() + 8; };\n")); writefile(strings.concat(dirdep, "/main.ww"), "package marker;\nexport fn value() i32 = { return 23; };\n"); let dircollision: str = strings.concat(dirdir, "/foo.bar"); let dirrefs: []str = alloc([], 8u64)!; state = 0; for (state < 2) { if (state == 1) { writefile(dircollision, wrongbytes); }; si = 0; for (si < stages.len) { let statetag: str = "collision"; if (state == 0) { statetag = "absent"; }; let work: str = strings.concat(root, "/directory-", tags[si], "-", statetag, "-work"); let output: str = strings.concat(root, "/directory-", tags[si], "-", statetag, "-output"); mkdirall(work); let av: []str = [driver(stages[si]), "build", "-w", work, "-I", dirdir, "-o", output, "foo.bar"]; let out: commandout; runcommanddir(root, strings.concat("directory-build-", tags[si], "-", statetag), dirdir, av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(strings.concat(work, "/foo.bar.unit.ww")) && os.exists(strings.concat(work, "/foo.bar.wwi")) && os.exists(strings.concat(work, "/foo.bar.s")) && os.exists(strings.concat(work, "/foo.bar.a")) && os.exists(strings.concat(work, "/foo.bar.init.unit.ww")) && !os.exists(strings.concat(work, "/__root.unit.ww"))); wrongsuffixfamilyabsent(work, "__root"); let bytes: str = strings.dup(readfile(output)); let tree: str = strings.dup(artifacttreesnapshot(work)); if (state == 0) { append(dirrefs, bytes); append(dirrefs, tree); if (si == 1) { assert(same(dirrefs[0], dirrefs[2])); }; } else { let off: i32 = si * 2; assert(same(dirrefs[off], bytes) && same(dirrefs[off + 1], tree)); }; let runav: []str = [output]; runcommand(root, strings.concat("directory-built-", tags[si], "-", statetag), runav, time.second, &out); expectexit(&out, 31); assert(out.stdout.len == 0 && out.stderr.len == 0 && !directoryhasnew(work) && !directoryhasfragment(work, ".wwtxn.")); si += 1; }; state += 1; }; let dirartifacts: []str = ["foo.bar.unit.ww", "foo.bar.wwi", "foo.bar.s", "foo.bar.o", "foo.bar.a", "foo.bar.init.unit.ww", "foo.bar.init.s", "foo.bar.init.o", "proof.marker.unit.ww", "proof.marker.wwi", "proof.marker.s", "proof.marker.o", "proof.marker.a"]; wrongsuffixartifactparity(strings.concat(root, "/directory-c-absent-work"), strings.concat(root, "/directory-ww-absent-work"), dirartifacts); wrongsuffixartifactparity(strings.concat(root, "/directory-c-collision-work"), strings.concat(root, "/directory-ww-collision-work"), dirartifacts); wrongsuffixsemanticparity(strings.concat(root, "/directory-c-absent-work"), strings.concat(root, "/directory-ww-absent-work")); wrongsuffixsemanticparity(strings.concat(root, "/directory-c-collision-work"), strings.concat(root, "/directory-ww-collision-work")); assert(os.remove(dircollision) == 0); let directdir: str = strings.concat(root, "/direct-directory"); mkdirall(directdir); writefile(strings.concat(directdir, "/main.ww"), "package main;\nfn main() i32 = { return 43; };\n"); assert(os.symlink(directdir, dircollision) == 0); si = 0; for (si < stages.len) { let av: []str = [driver(stages[si]), "run", "-I", dirdir, "foo.bar"]; let out: commandout; runcommanddir(root, strings.concat("wrong-suffix-directory-link-", tags[si]), dirdir, av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 43); assert(out.stdout.len == 0 && out.stderr.len == 0); si += 1; }; assert(os.remove(dircollision) == 0); // Missing logical providers retain ordinary driver rejection, while a // second positional retains the historical package-coordinator route. let missingdir: str = strings.concat(root, "/missing-request"); mkdirall(missingdir); let missingcollision: str = strings.concat(missingdir, "/missing.pkg"); let ordinaryrefs: []str = alloc([], 32u64)!; let filteredrefs: []str = alloc([], 32u64)!; let coordinatordiag: str = strings.concat( "wwtest package: logical_selected: cannot canonicalize package directory\n", "wwtest package: missing.pkg: cannot canonicalize package directory\n"); let coordinatorusage: str = strings.concat( "usage: wwtest package [-c] [-S] [-list] [-j N] [-I DIR] [-L DIR] [-l LIB] [-w DIR] [-run|-filter GLOB] [-timeout-ms=N] [DIR | DIR/... ...] [-- GLOB ...]\n", " *_test.ww is the sole test-source form; @test elsewhere is rejected\n", " -c retains without running; -o retains and still runs unless -c is present\n", " -w DIR is one persistent semantic-action store shared by the selected packages\n"); assert(coordinatorusage.len == 381); let rejectenvs: [][]str = alloc([], 2u64)!; let rejectctraces: []str = alloc([], 2u64)!; let rejectatraces: []str = alloc([], 2u64)!; let rejectltraces: []str = alloc([], 2u64)!; si = 0; for (si < stages.len) { let ctrace: str = strings.concat(root, "/reject-", tags[si], ".c.trace"); let atrace: str = strings.concat(root, "/reject-", tags[si], ".a.trace"); let ltrace: str = strings.concat(root, "/reject-", tags[si], ".l.trace"); writefile(ctrace, ""); writefile(atrace, ""); writefile(ltrace, ""); append(rejectctraces, ctrace); append(rejectatraces, atrace); append(rejectltraces, ltrace); let cwrap: str = strings.concat(root, "/reject-", tags[si], "-w6c"); let awrap: str = strings.concat(root, "/reject-", tags[si], "-w6a"); let lwrap: str = strings.concat(root, "/reject-", tags[si], "-w6l"); writeexecutable(cwrap, strings.concat("#!/bin/sh\n", "printf invoked >> \"$WW_SUFFIX_REJECT_CTRACE\"\nexit 97\n")); writeexecutable(awrap, strings.concat("#!/bin/sh\n", "printf invoked >> \"$WW_SUFFIX_REJECT_ATRACE\"\nexit 97\n")); writeexecutable(lwrap, strings.concat("#!/bin/sh\n", "printf invoked >> \"$WW_SUFFIX_REJECT_LTRACE\"\nexit 97\n")); let inherited: []str = os.getenvs(); let rejectenv: []str = alloc([], (inherited.len + 6): u64)!; let ei: i32 = 0; for (ei < inherited.len) { if (!strings.hasprefix(inherited[ei], "WW_W6C=") && !strings.hasprefix(inherited[ei], "WW_W6A=") && !strings.hasprefix(inherited[ei], "WW_W6L=") && !strings.hasprefix(inherited[ei], "WW_SUFFIX_REJECT_")) { append(rejectenv, inherited[ei]); }; ei += 1; }; append(rejectenv, strings.concat("WW_W6C=", cwrap)); append(rejectenv, strings.concat("WW_W6A=", awrap)); append(rejectenv, strings.concat("WW_W6L=", lwrap)); append(rejectenv, strings.concat("WW_SUFFIX_REJECT_CTRACE=", ctrace)); append(rejectenv, strings.concat("WW_SUFFIX_REJECT_ATRACE=", atrace)); append(rejectenv, strings.concat("WW_SUFFIX_REJECT_LTRACE=", ltrace)); append(rejectenvs, rejectenv); si += 1; }; state = 0; for (state < 2) { if (state == 1) { writefile(missingcollision, wrongbytes); }; si = 0; for (si < stages.len) { let ordinary: []str = alloc([], 24u64)!; let filtered: []str = alloc([], 24u64)!; let statetag: str = "collision-"; if (state == 0) { statetag = "absent-"; }; let prefix: str = strings.concat("missing-", tags[si], "-", statetag); let out: commandout; let buildwork: str = strings.concat(root, "/", prefix, "build-work"); let buildout: str = strings.concat(root, "/", prefix, "build-output"); let buildav: []str = [driver(stages[si]), "build", "-w", buildwork, "-o", buildout, "missing.pkg"]; runcommanddir(root, strings.concat(prefix, "build"), missingdir, buildav, (30i64 * (time.second: i64)): time.duration, &out); wrongsuffixrecord(&out, 1, &ordinary); assert(same(out.stderr, "ww build: cannot find module missing.pkg\n") && !os.exists(buildwork) && !os.exists(buildout)); let runav: []str = [driver(stages[si]), "run", "missing.pkg"]; runcommanddir(root, strings.concat(prefix, "run"), missingdir, runav, (30i64 * (time.second: i64)): time.duration, &out); wrongsuffixrecord(&out, 1, &ordinary); assert(same(out.stderr, "ww run: cannot find module missing.pkg\n")); let rawwork: str = strings.concat(root, "/", prefix, "raw-work"); let rawav: []str = [driver(stages[si]), "test", "-w", rawwork, "missing.pkg"]; runcommanddir(root, strings.concat(prefix, "raw"), missingdir, rawav, (30i64 * (time.second: i64)): time.duration, &out); wrongsuffixrecord(&out, 1, &ordinary); assert(same(out.stdout, "FAIL\n") && same(out.stderr, "ww test: cannot find missing.pkg\n") && !os.exists(rawwork)); let compiled: str = strings.concat(root, "/", prefix, "compiled"); let compileav: []str = [driver(stages[si]), "test", "-c", "-o", compiled, "missing.pkg"]; runcommanddir(root, strings.concat(prefix, "compile"), missingdir, compileav, (30i64 * (time.second: i64)): time.duration, &out); wrongsuffixrecord(&out, 1, &ordinary); assert(out.stdout.len == 0 && same(out.stderr, "ww test: cannot find missing.pkg\n") && !os.exists(compiled)); let assembly: str = strings.concat(root, "/", prefix, "assembly"); let asmav: []str = [driver(stages[si]), "test", "-S", "-o", assembly, "missing.pkg"]; runcommanddir(root, strings.concat(prefix, "assembly"), missingdir, asmav, (30i64 * (time.second: i64)): time.duration, &out); wrongsuffixrecord(&out, 1, &ordinary); assert(out.stdout.len == 0 && same(out.stderr, "ww test: cannot find missing.pkg\n") && !os.exists(assembly)); rewritefile(rejectctraces[si], ""); rewritefile(rejectatraces[si], ""); rewritefile(rejectltraces[si], ""); let frawav: []str = [driver(stages[si]), "test", "missing.pkg", "logical_selected"]; runcommandenvdir(root, strings.concat(prefix, "filtered-raw"), frawav, rejectenvs[si], missingdir, (30i64 * (time.second: i64)): time.duration, &out); wrongsuffixrecord(&out, 1, &filtered); assert(same(out.stdout, "FAIL\n") && same(out.stderr, coordinatordiag)); let fcompiled: str = strings.concat(root, "/", prefix, "filtered-compiled"); let fcompileav: []str = [driver(stages[si]), "test", "-c", "-o", fcompiled, "missing.pkg", "logical_selected"]; runcommandenvdir(root, strings.concat(prefix, "filtered-compile"), fcompileav, rejectenvs[si], missingdir, (30i64 * (time.second: i64)): time.duration, &out); wrongsuffixrecord(&out, 1, &filtered); assert(out.stdout.len == 0 && same(out.stderr, coordinatordiag) && !os.exists(fcompiled)); let fnooutav: []str = [driver(stages[si]), "test", "-S", "missing.pkg", "logical_selected"]; runcommandenvdir(root, strings.concat(prefix, "filtered-assembly-noout"), fnooutav, rejectenvs[si], missingdir, (30i64 * (time.second: i64)): time.duration, &out); wrongsuffixrecord(&out, 2, &filtered); assert(out.stdout.len == 0 && same(out.stderr, "ww test: -S needs -o\n")); let fassembly: str = strings.concat(root, "/", prefix, "filtered-assembly"); let fasmav: []str = [driver(stages[si]), "test", "-S", "-o", fassembly, "missing.pkg", "logical_selected"]; runcommandenvdir(root, strings.concat(prefix, "filtered-assembly"), fasmav, rejectenvs[si], missingdir, (30i64 * (time.second: i64)): time.duration, &out); wrongsuffixrecord(&out, 2, &filtered); assert(out.stdout.len == 0 && out.stderr.len == 381 && same(out.stderr, coordinatorusage) && !os.exists(fassembly) && readfile(rejectctraces[si]).len == 0 && readfile(rejectatraces[si]).len == 0 && readfile(rejectltraces[si]).len == 0); if (state == 0 && si == 0) { let oi: i32 = 0; for (oi < ordinary.len) { append(ordinaryrefs, strings.dup(ordinary[oi])); oi += 1; }; let fi: i32 = 0; for (fi < filtered.len) { append(filteredrefs, strings.dup(filtered[fi])); fi += 1; }; } else { wrongsuffixsame(ordinaryrefs, ordinary); wrongsuffixsame(filteredrefs, filtered); }; si += 1; }; state += 1; }; assert(os.remove(missingcollision) == 0); // Eligible visible .ww spellings stay named sources, including a symlink // to a regular source. A .ww directory keeps its established rejection at // run's named-source front while remaining an ordinary build directory. let controls: str = strings.concat(root, "/positive-controls"); mkdirall(controls); let visible: str = strings.concat(controls, "/visible.ww"); let visiblelink: str = strings.concat(controls, "/visible-link.ww"); writefile(visible, "package main;\nfn main() i32 = { return 53; };\n"); assert(os.symlink(visible, visiblelink) == 0); let positivebins: []str = alloc([], 2u64)!; let positivetrees: []str = alloc([], 2u64)!; let routes: []str = [visible, visiblelink]; let ri: i32 = 0; for (ri < routes.len) { si = 0; for (si < stages.len) { let routetag: str = "link"; if (ri == 0) { routetag = "regular"; }; let work: str = strings.concat(root, "/positive-", tags[si], "-", routetag, "-work"); let output: str = strings.concat(root, "/positive-", tags[si], "-", routetag, "-output"); mkdirall(work); let av: []str = [driver(stages[si]), "build", "-w", work, "-o", output, routes[ri]]; let out: commandout; runcommand(root, strings.concat("positive-build-", tags[si], "-", routetag), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); if (ri == 0) { append(positivebins, strings.dup(readfile(output))); append(positivetrees, strings.dup(artifacttreesnapshot(work))); if (si == 1) { assert(same(positivebins[0], positivebins[1])); }; } else { assert(same(positivebins[si], readfile(output)) && same(positivetrees[si], artifacttreesnapshot(work))); }; let runav: []str = [output]; runcommand(root, strings.concat("positive-built-", tags[si], "-", routetag), runav, time.second, &out); expectexit(&out, 53); si += 1; }; ri += 1; }; let positiveartifacts: []str = ["__root.unit.ww", "__root.wwi", "__root.s", "__root.o", "__root.a", "__root.init.unit.ww", "__root.init.s", "__root.init.o"]; wrongsuffixartifactparity(strings.concat(root, "/positive-c-regular-work"), strings.concat(root, "/positive-ww-regular-work"), positiveartifacts); wrongsuffixartifactparity(strings.concat(root, "/positive-c-link-work"), strings.concat(root, "/positive-ww-link-work"), positiveartifacts); wrongsuffixsemanticparity(strings.concat(root, "/positive-c-regular-work"), strings.concat(root, "/positive-ww-regular-work")); wrongsuffixsemanticparity(strings.concat(root, "/positive-c-link-work"), strings.concat(root, "/positive-ww-link-work")); let nameddir: str = strings.concat(controls, "/directory.ww"); mkdirall(nameddir); writefile(strings.concat(nameddir, "/main.ww"), "package main;\nfn main() i32 = { return 55; };\n"); let nameddirbin: str = ""; si = 0; for (si < stages.len) { let work: str = strings.concat(root, "/named-directory-", tags[si], "-work"); let output: str = strings.concat(root, "/named-directory-", tags[si], "-output"); mkdirall(work); let av: []str = [driver(stages[si]), "build", "-w", work, "-o", output, nameddir]; let out: commandout; runcommand(root, strings.concat("named-directory-build-", tags[si]), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); if (si == 0) { nameddirbin = strings.dup(readfile(output)); } else { assert(same(nameddirbin, readfile(output))); }; let runav: []str = [driver(stages[si]), "run", nameddir]; runcommand(root, strings.concat("named-directory-run-", tags[si]), runav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && same(out.stderr, strings.concat(nameddir, " is a directory, should be a WW file\n"))); si += 1; }; wrongsuffixsemanticparity(strings.concat(root, "/named-directory-c-work"), strings.concat(root, "/named-directory-ww-work")); // The suffix decision is request-local: concurrent stages can build the // same logical action through one ignored collision into separate owners. writefile(collision, wrongbytes); let pcwork: str = strings.concat(root, "/parallel-c-work"); let pwwork: str = strings.concat(root, "/parallel-ww-work"); let pcout: str = strings.concat(root, "/parallel-c-output"); let pwout: str = strings.concat(root, "/parallel-ww-output"); mkdirall(pcwork); mkdirall(pwwork); let pcav: []str = [driver("ww"), "build", "-w", pcwork, "-I", filedir, "-o", pcout, "foo.bar"]; let pwav: []str = [driver("ww_ww"), "build", "-w", pwwork, "-I", filedir, "-o", pwout, "foo.bar"]; let cc: exec.command; cc.path = pcav[0]; cc.argv = pcav; cc.env = os.getenvs(); cc.dir = filedir; cc.stdoutpath = strings.concat(root, "/parallel-c.stdout"); cc.stderrpath = strings.concat(root, "/parallel-c.stderr"); cc.deadline = time.add(time.now(time.clock.monotonic), (120i64 * (time.second: i64)): time.duration); cc.grace = (100i64 * (time.millisecond: i64)): time.duration; let wc: exec.command; wc.path = pwav[0]; wc.argv = pwav; wc.env = os.getenvs(); wc.dir = filedir; wc.stdoutpath = strings.concat(root, "/parallel-ww.stdout"); wc.stderrpath = strings.concat(root, "/parallel-ww.stderr"); wc.deadline = time.add(time.now(time.clock.monotonic), (120i64 * (time.second: i64)): time.duration); wc.grace = (100i64 * (time.millisecond: i64)): time.duration; let cp: exec.process; let wp: exec.process; exec.start(&cp, &cc); exec.start(&wp, &wc); let cdone: bool = false; let wdone: bool = false; for (!cdone || !wdone) { if (!cdone) { cdone = exec.poll(&cp); }; if (!wdone) { wdone = exec.poll(&wp); }; if (!cdone || !wdone) { time.sleep(time.millisecond, time.clock.monotonic); }; }; assert(cp.result.errno == 0 && cp.result.cleanuperrno == 0 && cp.result.termination == exec.termination.EXIT && cp.result.code == 0 && wp.result.errno == 0 && wp.result.cleanuperrno == 0 && wp.result.termination == exec.termination.EXIT && wp.result.code == 0 && readfile(cc.stdoutpath).len == 0 && readfile(cc.stderrpath).len == 0 && readfile(wc.stdoutpath).len == 0 && readfile(wc.stderrpath).len == 0 && same(reference[2], readfile(pcout)) && same(reference[2], readfile(pwout)) && same(reference[3], artifacttreesnapshot(pcwork)) && same(wwreference[3], artifacttreesnapshot(pwwork)) && !directoryhasnew(pcwork) && !directoryhasnew(pwwork) && !directoryhasfragment(pcwork, ".wwtxn.") && !directoryhasfragment(pwwork, ".wwtxn.")); wrongsuffixsemanticparity(pcwork, pwwork); assert(os.remove(collision) == 0); assert(!directoryhasnew(root) && !directoryhasfragment(root, ".wwtxn.") && !directoryhasfragment(root, ".install") && !directoryhasfragment(root, ".sepwork") && !directoryhasfragment(root, ".capture") && !directoryhasfragment(root, ".result") && !directoryhasfragment(root, ".request") && !wrongsuffixpathfragment(root, ".new") && !wrongsuffixpathfragment(root, ".wwtxn.") && !wrongsuffixpathfragment(root, ".install") && !wrongsuffixpathfragment(root, ".sepwork") && !wrongsuffixpathfragment(root, ".capture") && !wrongsuffixpathfragment(root, ".result") && !wrongsuffixpathfragment(root, ".request")); clean(root); }; fn blankpackagebaseenv() []str = { let inherited: []str = os.getenvs(); let env: []str = alloc([], inherited.len: u64)!; let i: i32 = 0; for (i < inherited.len) { if (!strings.hasprefix(inherited[i], "WW_W6C=") && !strings.hasprefix(inherited[i], "WW_W6A=") && !strings.hasprefix(inherited[i], "WW_W6L=") && !strings.hasprefix(inherited[i], "WW_BLANK_PACKAGE_")) { append(env, inherited[i]); }; i += 1; }; return env; }; fn blankpackageenv(compilerwrapper: str, assemblerwrapper: str, linkerwrapper: str, compiler: str, assembler: str, linker: str, ctrace: str, atrace: str, ltrace: str) []str = { let env: []str = blankpackagebaseenv(); 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_BLANK_PACKAGE_CTRACE=", ctrace)); append(env, strings.concat("WW_BLANK_PACKAGE_ATRACE=", atrace)); append(env, strings.concat("WW_BLANK_PACKAGE_LTRACE=", ltrace)); append(env, strings.concat("WW_BLANK_PACKAGE_REAL_C=", compiler)); append(env, strings.concat("WW_BLANK_PACKAGE_REAL_A=", assembler)); append(env, strings.concat("WW_BLANK_PACKAGE_REAL_L=", linker)); return env; }; fn blankpackageresettraces(ctrace: str, atrace: str, ltrace: str) void = { rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); }; fn blankpackagenoresidue(root: str) void = { assert(!wrongsuffixpathfragment(root, ".new") && !wrongsuffixpathfragment(root, ".old") && !wrongsuffixpathfragment(root, ".wwtxn.") && !wrongsuffixpathfragment(root, ".install") && !wrongsuffixpathfragment(root, ".sepwork") && !wrongsuffixpathfragment(root, ".capture") && !wrongsuffixpathfragment(root, ".result") && !wrongsuffixpathfragment(root, ".request")); }; fn blankpackageassert(out: *commandout, stdout: str, count: i32) void = { expectexit(out, 1); assert(same(out.stdout, stdout) && occurrences(out.stderr, "invalid package name _") == count && !has(out.stderr, "invalid or missing package clause")); }; // A blank declared package name is syntax, not a malformed package clause. // The compiler checker owns its one exact diagnostic, while named-source // routing, MainOnly, omitted test-source, and full-syntax precedence retain // their existing driver boundaries in both bootstrap stages. @test fn blank_declared_package_name_compiler_and_named_routes() void = { let root: str = fresh(); let simple: str = strings.concat(root, "/simple.ww"); let continued: str = strings.concat(root, "/continued.ww"); let command: str = strings.concat(root, "/command.ww"); let omitted: str = strings.concat(root, "/omitted_test.ww"); let rawtest: str = strings.concat(root, "/raw_test.ww"); let rawi: str = strings.concat(root, "/raw-i.ww"); let malformedheader: str = strings.concat(root, "/malformed-header.ww"); let malformedimport: str = strings.concat(root, "/malformed-import.ww"); writefile(simple, strings.concat( "package _;\n", "export fn value() i32 = { return 17; };\n")); writefile(continued, strings.concat( "package _;\n", "export fn value() i32 = { return missing; };\n")); writefile(command, "package _;\nfn main() i32 = { return 19; };\n"); writefile(omitted, strings.concat( "package _;\n", "this malformed body is omitted by ordinary build;\n")); writefile(rawtest, strings.concat( "package _;\n", "@test fn blank_test() void = { assert(true); };\n")); writefile(rawi, "package _;\ni this_is_not_import;\n"); writefile(malformedheader, "package ;\n"); writefile(malformedimport, "package _;\nimport ;\n"); // Direct compiler ownership is exact, including the name-token position, // diagnostic continuation, and atomic preservation of both publications. let compilers: []str = ["w6c", "w6c_ww"]; let directdiag: str = ""; let continueddiag: str = ""; let i: i32 = 0; for (i < compilers.len) { let asmout: str = strings.concat(root, "/direct-", compilers[i], ".s"); let wwiout: str = strings.concat(root, "/direct-", compilers[i], ".wwi"); writefile(asmout, "prior assembly bytes\n"); writefile(wwiout, "prior interface bytes\n"); let av: []str = [driver(compilers[i]), "-c", "-o", asmout, "-I", wwiout, simple]; let out: commandout; runcommand(root, strings.concat("blank-direct-", compilers[i]), av, (30i64 * (time.second: i64)): time.duration, &out); blankpackageassert(&out, "", 1); let want: str = strings.concat(simple, ":1:9: error: invalid package name _\n"); assert(same(out.stderr, want) && same(readfile(asmout), "prior assembly bytes\n") && same(readfile(wwiout), "prior interface bytes\n")); if (i == 0) { directdiag = strings.dup(out.stderr); } else { assert(same(directdiag, out.stderr)); }; let continuedout: str = strings.concat(root, "/continued-", compilers[i], ".s"); let continuedav: []str = [driver(compilers[i]), "-c", "-o", continuedout, continued]; runcommand(root, strings.concat("blank-continued-", compilers[i]), continuedav, (30i64 * (time.second: i64)): time.duration, &out); blankpackageassert(&out, "", 1); assert(has(out.stderr, "undefined: missing") && pos(out.stderr, "invalid package name _") < pos(out.stderr, "undefined: missing") && !os.exists(continuedout)); if (i == 0) { continueddiag = strings.dup(out.stderr); } else { assert(same(continueddiag, out.stderr)); }; let rawout: str = strings.concat(root, "/raw-i-", compilers[i], ".s"); let rawav: []str = [driver(compilers[i]), "-o", rawout, rawi]; runcommand(root, strings.concat("blank-raw-i-direct-", compilers[i]), rawav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && !os.exists(rawout) && !has(out.stderr, "invalid package name _") && has(out.stderr, "expected top-level decl")); i += 1; }; let compilerwrapper: str = strings.concat(root, "/blank-w6c.sh"); let assemblerwrapper: str = strings.concat(root, "/blank-w6a.sh"); let linkerwrapper: str = strings.concat(root, "/blank-w6l.sh"); writeexecutable(compilerwrapper, strings.concat( "#!/bin/sh\nprintf 'BEGIN' >> \"$WW_BLANK_PACKAGE_CTRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_BLANK_PACKAGE_CTRACE\"; done\n", "printf '\\n' >> \"$WW_BLANK_PACKAGE_CTRACE\"\n", "exec \"$WW_BLANK_PACKAGE_REAL_C\" \"$@\"\n")); writeexecutable(assemblerwrapper, strings.concat( "#!/bin/sh\nprintf 'assemble\\n' >> \"$WW_BLANK_PACKAGE_ATRACE\"\n", "exec \"$WW_BLANK_PACKAGE_REAL_A\" \"$@\"\n")); writeexecutable(linkerwrapper, strings.concat( "#!/bin/sh\nprintf 'link\\n' >> \"$WW_BLANK_PACKAGE_LTRACE\"\n", "exec \"$WW_BLANK_PACKAGE_REAL_L\" \"$@\"\n")); let stages: []str = ["ww", "ww_ww"]; let assemblers: []str = ["w6a", "w6a_ww"]; let linkers: []str = ["w6l", "w6l_ww"]; let tags: []str = ["c", "ww"]; let namedrefs: []str = ["", "", "", "", "", ""]; i = 0; for (i < stages.len) { let ctrace: str = strings.concat(root, "/named-c-", tags[i], ".trace"); let atrace: str = strings.concat(root, "/named-a-", tags[i], ".trace"); let ltrace: str = strings.concat(root, "/named-l-", tags[i], ".trace"); writefile(ctrace, ""); writefile(atrace, ""); writefile(ltrace, ""); let env: []str = blankpackageenv(compilerwrapper, assemblerwrapper, linkerwrapper, driver(compilers[i]), driver(assemblers[i]), driver(linkers[i]), ctrace, atrace, ltrace); let out: commandout; let work: str = strings.concat(root, "/named-work-", tags[i]); let public: str = strings.concat(root, "/named-output-", tags[i]); mkdirall(work); writefile(public, "prior public bytes\n"); let buildav: []str = [driver(stages[i]), "build", "-w", work, "-o", public, simple]; runcommandenv(root, strings.concat("blank-named-build-", tags[i]), buildav, env, (60i64 * (time.second: i64)): time.duration, &out); blankpackageassert(&out, "", 1); assert(same(readfile(public), "prior public bytes\n") && occurrences(readfile(ctrace), "BEGIN") == 1 && readfile(atrace).len == 0 && readfile(ltrace).len == 0 && has(out.stderr, "ww: w6c failed for (root)\n")); wrongsuffixfamilyabsent(work, "__root"); let normalized: str = normalizedtrace(out.stderr, strings.concat(work, "/"), public); if (i == 0) { namedrefs[0] = strings.dup(normalized); } else { assert(same(namedrefs[0], normalized)); }; blankpackageresettraces(ctrace, atrace, ltrace); let runav: []str = [driver(stages[i]), "run", command]; runcommandenv(root, strings.concat("blank-named-run-", tags[i]), runav, env, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); let runwant: str = strings.concat("ww: package ", command, " is not a main package\n"); assert(out.stdout.len == 0 && same(out.stderr, runwant) && readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); if (i == 0) { namedrefs[1] = strings.dup(out.stderr); } else { assert(same(namedrefs[1], out.stderr)); }; // Header/import validation occurs, but the malformed body and blank // declared name of a named test source do not enter an ordinary build. blankpackageresettraces(ctrace, atrace, ltrace); let omitwork: str = strings.concat(root, "/omit-work-", tags[i]); let omitav: []str = [driver(stages[i]), "build", "-w", omitwork, omitted]; runcommandenv(root, strings.concat("blank-omitted-", tags[i]), omitav, env, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && !os.exists(omitwork) && readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); let modes: []str = ["run", "compile", "assembly"]; let mode: i32 = 0; for (mode < modes.len) { blankpackageresettraces(ctrace, atrace, ltrace); let testwork: str = strings.concat(root, "/raw-test-", tags[i], "-", modes[mode]); let testout: str = strings.concat(root, "/raw-output-", tags[i], "-", modes[mode]); mkdirall(testwork); let av: []str = alloc([], 9u64)!; append(av, driver(stages[i])); append(av, "test"); if (mode == 1) { append(av, "-c"); } else if (mode == 2) { append(av, "-S"); }; append(av, "-w"); append(av, testwork); if (mode != 0) { append(av, "-o"); append(av, testout); }; append(av, rawtest); runcommandenv(root, strings.concat("blank-raw-test-", tags[i], "-", modes[mode]), av, env, (90i64 * (time.second: i64)): time.duration, &out); let stdout: str = ""; if (mode == 0) { stdout = "FAIL\n"; }; blankpackageassert(&out, stdout, 1); assert(!os.exists(testout) && !has(out.stdout, "blank_test") && !has(out.stderr, "blank_test")); wrongsuffixfamilyabsent(testwork, "__root"); let index: i32 = 2 + mode; let norm: str = normalizedtrace(out.stderr, strings.concat(testwork, "/"), testout); if (i == 0) { namedrefs[index] = strings.dup(norm); } else { assert(same(namedrefs[index], norm)); }; mode += 1; }; // Header syntax prevents a compiler action. Raw `i` is a body token, // so full compiler syntax instead suppresses the semantic blank error. blankpackageresettraces(ctrace, atrace, ltrace); let headerwork: str = strings.concat(root, "/header-work-", tags[i]); let headerout: str = strings.concat(root, "/header-out-", tags[i]); mkdirall(headerwork); let headerav: []str = [driver(stages[i]), "build", "-w", headerwork, "-o", headerout, malformedimport]; runcommandenv(root, strings.concat("blank-malformed-import-", tags[i]), headerav, env, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); let headerwant: str = strings.concat(malformedimport, ":2:8: error: expected identifier, got ;\n"); assert(out.stdout.len == 0 && same(out.stderr, headerwant) && readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0 && !os.exists(headerout)); if (i == 0) { namedrefs[5] = strings.dup(out.stderr); } else { assert(same(namedrefs[5], out.stderr)); }; blankpackageresettraces(ctrace, atrace, ltrace); let badheaderwork: str = strings.concat(root, "/bad-header-work-", tags[i]); mkdirall(badheaderwork); let badheaderav: []str = [driver(stages[i]), "build", "-w", badheaderwork, malformedheader]; runcommandenv(root, strings.concat("blank-malformed-header-", tags[i]), badheaderav, env, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && same(out.stderr, strings.concat(malformedheader, ":1:9: error: invalid or missing package clause\n")) && readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); blankpackageresettraces(ctrace, atrace, ltrace); let rawiwork: str = strings.concat(root, "/raw-i-work-", tags[i]); mkdirall(rawiwork); let rawiav: []str = [driver(stages[i]), "build", "-w", rawiwork, rawi]; runcommandenv(root, strings.concat("blank-raw-i-", tags[i]), rawiav, env, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && !has(out.stderr, "invalid package name _") && has(out.stderr, "expected top-level decl") && readfile(ctrace).len != 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); i += 1; }; blankpackagenoresidue(root); clean(root); }; // Directory loading retains `_` long enough to form ordinary package/import // and test-family graphs. Checker failure then rolls back the whole request; // loader, dependency, mixed-family, MainOnly, and syntax errors keep their // established precedence, without inventing blank identity or artifacts. @test fn blank_declared_package_name_graph_test_import_and_rollback() void = { let root: str = fresh(); let include: str = strings.concat(root, "/include"); let allblank: str = strings.concat(include, "/allblank"); let prodonly: str = strings.concat(include, "/prodonly"); let testonly: str = strings.concat(include, "/testonly"); let validblanktest: str = strings.concat(include, "/validblanktest"); let blankvalidtest: str = strings.concat(include, "/blankvalidtest"); let blankexternal: str = strings.concat(include, "/blankexternal"); let blankprovider: str = strings.concat(include, "/deps/blank"); let gooddep: str = strings.concat(include, "/deps/good"); let brokendep: str = strings.concat(include, "/deps/broken"); let importer: str = strings.concat(include, "/importer"); let blankparent: str = strings.concat(include, "/blankparent"); let missingparent: str = strings.concat(include, "/missingparent"); let brokenparent: str = strings.concat(include, "/brokenparent"); let warm: str = strings.concat(include, "/warm"); let dirs: []str = [allblank, prodonly, testonly, validblanktest, blankvalidtest, blankexternal, blankprovider, gooddep, brokendep, importer, blankparent, missingparent, brokenparent, warm]; let i: i32 = 0; for (i < dirs.len) { mkdirall(dirs[i]); i += 1; }; writefile(strings.concat(allblank, "/a.ww"), "package _;\nexport fn a() i32 = { return 1; };\n"); writefile(strings.concat(allblank, "/b.ww"), "package _;\nexport fn b() i32 = { return 2; };\n"); writefile(strings.concat(allblank, "/a_test.ww"), "package _;\n@test fn a_test() void = { assert(true); };\n"); writefile(strings.concat(prodonly, "/prod.ww"), "package _;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(testonly, "/only_test.ww"), "package _;\n@test fn only() void = { assert(true); };\n"); writefile(strings.concat(validblanktest, "/prod.ww"), "package validblanktest;\nfn value() i32 = { return 1; };\n"); writefile(strings.concat(validblanktest, "/prod_test.ww"), "package _;\n@test fn must_not_run() void = { abort(); };\n"); writefile(strings.concat(blankvalidtest, "/prod.ww"), "package _;\nfn value() i32 = { return 1; };\n"); writefile(strings.concat(blankvalidtest, "/prod_test.ww"), "package blankvalidtest;\n@test fn must_not_run() void = { abort(); };\n"); writefile(strings.concat(blankexternal, "/prod.ww"), "package _;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(blankexternal, "/external_test.ww"), "package __test;\n@test fn must_not_run() void = { abort(); };\n"); writefile(strings.concat(blankprovider, "/provider.ww"), "package _;\nexport fn value() i32 = { return 7; };\n"); writefile(strings.concat(gooddep, "/good.ww"), "package good;\nexport fn value() i32 = { return 11; };\n"); writefile(strings.concat(brokendep, "/broken.ww"), strings.concat( "package broken;\n", "export fn value() i32 = { return missing; };\n")); writefile(strings.concat(importer, "/main.ww"), strings.concat( "package main;\nimport deps.blank;\n", "fn main() i32 = { return blank.value(); };\n")); writefile(strings.concat(blankparent, "/parent.ww"), strings.concat( "package _;\nimport deps.good;\n", "export fn value() i32 = { return good.value(); };\n")); writefile(strings.concat(missingparent, "/parent.ww"), strings.concat( "package _;\nimport absent.pkg;\n", "export fn value() i32 = { return 1; };\n")); writefile(strings.concat(brokenparent, "/parent.ww"), strings.concat( "package _;\nimport deps.broken;\n", "export fn value() i32 = { return broken.value(); };\n")); let warmfile: str = strings.concat(warm, "/main.ww"); let warmvalid: str = "package main;\nfn main() i32 = { return 23; };\n"; let warminvalid: str = "package _;\nfn main() i32 = { return 23; };\n"; writefile(warmfile, warmvalid); let compilerwrapper: str = strings.concat(root, "/graph-w6c.sh"); let assemblerwrapper: str = strings.concat(root, "/graph-w6a.sh"); let linkerwrapper: str = strings.concat(root, "/graph-w6l.sh"); writeexecutable(compilerwrapper, strings.concat( "#!/bin/sh\nprintf 'BEGIN' >> \"$WW_BLANK_PACKAGE_CTRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_BLANK_PACKAGE_CTRACE\"; done\n", "printf '\\n' >> \"$WW_BLANK_PACKAGE_CTRACE\"\n", "exec \"$WW_BLANK_PACKAGE_REAL_C\" \"$@\"\n")); writeexecutable(assemblerwrapper, strings.concat( "#!/bin/sh\nprintf 'assemble\\n' >> \"$WW_BLANK_PACKAGE_ATRACE\"\n", "exec \"$WW_BLANK_PACKAGE_REAL_A\" \"$@\"\n")); writeexecutable(linkerwrapper, strings.concat( "#!/bin/sh\nprintf 'link\\n' >> \"$WW_BLANK_PACKAGE_LTRACE\"\n", "exec \"$WW_BLANK_PACKAGE_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 allblankdiag: str = ""; let familydiag: []str = ["", ""]; let importdiag: str = ""; let validdependencyartifacts: str = ""; let warmbinref: str = ""; i = 0; for (i < stages.len) { let ctrace: str = strings.concat(root, "/graph-c-", tags[i], ".trace"); let atrace: str = strings.concat(root, "/graph-a-", tags[i], ".trace"); let ltrace: str = strings.concat(root, "/graph-l-", tags[i], ".trace"); writefile(ctrace, ""); writefile(atrace, ""); writefile(ltrace, ""); let env: []str = blankpackageenv(compilerwrapper, assemblerwrapper, linkerwrapper, driver(compilers[i]), driver(assemblers[i]), driver(linkers[i]), ctrace, atrace, ltrace); let out: commandout; let allbuildwork: str = strings.concat(root, "/allbuild-work-", tags[i]); mkdirall(allbuildwork); let allbuildav: []str = [driver(stages[i]), "build", "-w", allbuildwork, "-I", include, allblank]; runcommandenv(root, strings.concat("blank-all-build-", tags[i]), allbuildav, env, (60i64 * (time.second: i64)): time.duration, &out); blankpackageassert(&out, "", 2); assert(occurrences(readfile(ctrace), "BEGIN") == 1 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); wrongsuffixfamilyabsent(allbuildwork, "allblank"); blankpackageresettraces(ctrace, atrace, ltrace); let allwork: str = strings.concat(root, "/allblank-work-", tags[i]); mkdirall(allwork); let allav: []str = [driver(stages[i]), "test", "-w", allwork, "-I", include, allblank]; runcommandenv(root, strings.concat("blank-all-test-", tags[i]), allav, env, (120i64 * (time.second: i64)): time.duration, &out); blankpackageassert(&out, "FAIL\n", 3); assert(has(out.stderr, strings.concat("FAIL ", allblank, " [_] ")) && occurrences(readfile(ctrace), "-internal-test.unit.new") == 1 && !has(out.stdout, "a_test") && readfile(ltrace).len == 0); wrongsuffixfamilyabsent(allwork, "allblank-internal-test"); let diag: str = normalizedtrace(out.stderr, strings.concat(allwork, "/"), strings.concat(root, "/unused-allblank-output")); if (i == 0) { allblankdiag = strings.dup(diag); } else { assert(same(allblankdiag, diag)); }; // Compile-only uses the same substituted internal-test action: one // compiler invocation owns all three retained package markers. blankpackageresettraces(ctrace, atrace, ltrace); let allcompilework: str = strings.concat(root, "/allcompile-work-", tags[i]); let allcompileout: str = strings.concat(root, "/allcompile-", tags[i]); mkdirall(allcompilework); let allcompileav: []str = [driver(stages[i]), "test", "-c", "-w", allcompilework, "-I", include, "-o", allcompileout, allblank]; runcommandenv(root, strings.concat("blank-all-compile-", tags[i]), allcompileav, env, (120i64 * (time.second: i64)): time.duration, &out); blankpackageassert(&out, "", 3); assert(occurrences(readfile(ctrace), "-internal-test.unit.new") == 1 && !os.exists(allcompileout) && readfile(ltrace).len == 0); wrongsuffixfamilyabsent(allcompilework, "allblank-internal-test"); let testdirs: []str = [prodonly, testonly]; let ti: i32 = 0; for (ti < testdirs.len) { blankpackageresettraces(ctrace, atrace, ltrace); let testwork: str = strings.concat(root, "/single-test-work-", tags[i], "-", boundarypkgname(ti)); mkdirall(testwork); let testout: str = strings.concat(root, "/single-test-output-", tags[i], "-", boundarypkgname(ti)); let testav: []str = alloc([], 10u64)!; append(testav, driver(stages[i])); append(testav, "test"); if (ti == 1) { append(testav, "-c"); }; append(testav, "-w"); append(testav, testwork); append(testav, "-I"); append(testav, include); if (ti == 1) { append(testav, "-o"); append(testav, testout); }; append(testav, testdirs[ti]); runcommandenv(root, strings.concat("blank-single-test-", tags[i], "-", boundarypkgname(ti)), testav, env, (120i64 * (time.second: i64)): time.duration, &out); let teststdout: str = "FAIL\n"; if (ti == 1) { teststdout = ""; }; blankpackageassert(&out, teststdout, 1); assert(!has(out.stdout, " [no test files]\n") && !os.exists(testout) && readfile(ltrace).len == 0); if (ti == 0) { wrongsuffixfamilyabsent(testwork, "prodonly"); } else { wrongsuffixfamilyabsent(testwork, "testonly-internal-test"); }; ti += 1; }; // Mixed families remain coordinator-owned and invoke no compiler. let mixed: []str = [validblanktest, blankvalidtest]; ti = 0; for (ti < mixed.len) { blankpackageresettraces(ctrace, atrace, ltrace); let mixedav: []str = [driver(stages[i]), "test", "-I", include, mixed[ti]]; runcommandenv(root, strings.concat("blank-mixed-", tags[i], "-", boundarypkgname(ti)), mixedav, env, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stdout, "FAIL\n") && has(out.stderr, "test package must match production package or _test\n") && !has(out.stderr, "invalid package name _") && readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); if (i == 0) { familydiag[ti] = strings.dup(out.stderr); } else { assert(same(familydiag[ti], out.stderr)); }; ti += 1; }; // `__test` is the actual external family of `_`; it passes family // validation and is stopped by the production blank dependency. blankpackageresettraces(ctrace, atrace, ltrace); let externalav: []str = [driver(stages[i]), "test", "-I", include, blankexternal]; runcommandenv(root, strings.concat("blank-external-", tags[i]), externalav, env, (120i64 * (time.second: i64)): time.duration, &out); blankpackageassert(&out, "FAIL\n", 1); assert(!has(out.stderr, "test package must match production package") && !has(out.stdout, "must_not_run") && readfile(ltrace).len == 0); // Imported blank providers preserve dotted action/import identity and // fail before their importer can compile or publish. blankpackageresettraces(ctrace, atrace, ltrace); let importwork: str = strings.concat(root, "/import-work-", tags[i]); let importout: str = strings.concat(root, "/import-output-", tags[i]); mkdirall(importwork); let importav: []str = [driver(stages[i]), "build", "-w", importwork, "-I", include, "-o", importout, "importer"]; runcommandenv(root, strings.concat("blank-import-provider-", tags[i]), importav, env, (90i64 * (time.second: i64)): time.duration, &out); blankpackageassert(&out, "", 1); assert(has(out.stderr, "ww: w6c failed for deps.blank\n") && !has(readfile(ctrace), "/importer.unit.new") && !os.exists(importout) && readfile(atrace).len == 0 && readfile(ltrace).len == 0); wrongsuffixfamilyabsent(importwork, "deps.blank"); wrongsuffixfamilyabsent(importwork, "importer"); let inorm: str = normalizedtrace(out.stderr, strings.concat(importwork, "/"), importout); if (i == 0) { importdiag = strings.dup(inorm); } else { assert(same(importdiag, inorm)); }; // A valid dependency is produced first, but request rollback leaves no // committed dependency or parent generation after the parent checker. blankpackageresettraces(ctrace, atrace, ltrace); let parentwork: str = strings.concat(root, "/parent-work-", tags[i]); mkdirall(parentwork); let parentav: []str = [driver(stages[i]), "build", "-w", parentwork, "-I", include, "blankparent"]; runcommandenv(root, strings.concat("blank-valid-dependency-", tags[i]), parentav, env, (90i64 * (time.second: i64)): time.duration, &out); blankpackageassert(&out, "", 1); assert(occurrences(readfile(ctrace), "BEGIN") == 2 && readfile(atrace).len != 0 && readfile(ltrace).len == 0 && !os.exists(strings.concat(parentwork, "/deps.good.wwi")) && !os.exists(strings.concat(parentwork, "/blankparent.wwi"))); wrongsuffixfamilyabsent(parentwork, "deps.good"); wrongsuffixfamilyabsent(parentwork, "blankparent"); let depstate: str = artifacttreesnapshot(parentwork); if (i == 0) { validdependencyartifacts = strings.dup(depstate); } else { assert(same(validdependencyartifacts, depstate)); }; // Missing dependency load and dependency producer failure each win // before the parent compiler's blank-name check. let precedence: []str = [missingparent, brokenparent]; ti = 0; for (ti < precedence.len) { blankpackageresettraces(ctrace, atrace, ltrace); let precedencework: str = strings.concat(root, "/precedence-work-", tags[i], "-", boundarypkgname(ti)); mkdirall(precedencework); let precedenceav: []str = [driver(stages[i]), "build", "-w", precedencework, "-I", include, precedence[ti]]; runcommandenv(root, strings.concat("blank-precedence-", tags[i], "-", boundarypkgname(ti)), precedenceav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && !has(out.stderr, "invalid package name _")); if (ti == 0) { assert(has(out.stderr, "cannot find package absent.pkg") && readfile(ctrace).len == 0); } else { assert(has(out.stderr, "undefined: missing") && has(out.stderr, "ww: w6c failed for deps.broken\n") && !has(readfile(ctrace), "/brokenparent.unit.new")); }; ti += 1; }; // Run performs complete dependency loading, then MainOnly, before any // producer. A missing import still precedes that command-kind boundary. blankpackageresettraces(ctrace, atrace, ltrace); let runav: []str = [driver(stages[i]), "run", "-I", include, "blankparent"]; runcommandenv(root, strings.concat("blank-run-mainonly-", tags[i]), runav, env, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && same(out.stderr, "ww: package blankparent is not a main package\n") && readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); blankpackageresettraces(ctrace, atrace, ltrace); let runmissingav: []str = [driver(stages[i]), "run", "-I", include, "missingparent"]; runcommandenv(root, strings.concat("blank-run-missing-", tags[i]), runmissingav, env, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && has(out.stderr, "cannot find package absent.pkg") && !has(out.stderr, "is not a main package") && !has(out.stderr, "invalid package name _") && readfile(ctrace).len == 0); // Warm invalidation is source-byte based. Failed replacement preserves // the entire generation and public binary; exact restoration reuses it. let warmwork: str = strings.concat(root, "/warm-work-", tags[i]); let warmout: str = strings.concat(root, "/warm-output-", tags[i]); mkdirall(warmwork); let warmav: []str = [driver(stages[i]), "build", "-w", warmwork, "-I", include, "-o", warmout, "warm"]; blankpackageresettraces(ctrace, atrace, ltrace); runcommandenv(root, strings.concat("blank-warm-seed-", tags[i]), warmav, env, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(warmout) && readfile(ctrace).len != 0 && readfile(atrace).len != 0 && readfile(ltrace).len != 0); let warmbytes: str = strings.dup(readfile(warmout)); let warmstate: str = strings.dup(treesnapshot(warmwork)); if (i == 0) { warmbinref = strings.dup(warmbytes); } else { assert(same(warmbinref, warmbytes)); }; rewritefile(warmfile, warminvalid); blankpackageresettraces(ctrace, atrace, ltrace); runcommandenv(root, strings.concat("blank-warm-invalid-", tags[i]), warmav, env, (90i64 * (time.second: i64)): time.duration, &out); blankpackageassert(&out, "", 1); assert(same(warmbytes, readfile(warmout)) && same(warmstate, treesnapshot(warmwork)) && readfile(ctrace).len != 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); rewritefile(warmfile, warmvalid); blankpackageresettraces(ctrace, atrace, ltrace); runcommandenv(root, strings.concat("blank-warm-restored-", tags[i]), warmav, env, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && same(warmbytes, readfile(warmout)) && same(warmstate, treesnapshot(warmwork)) && readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len != 0); i += 1; }; // Concurrent stages read the same blank package but own disjoint work and // public paths; each receives only its own one checker diagnostic. let pcwork: str = strings.concat(root, "/parallel-c-work"); let pwwork: str = strings.concat(root, "/parallel-ww-work"); let pcout: str = strings.concat(root, "/parallel-c-output"); let pwout: str = strings.concat(root, "/parallel-ww-output"); mkdirall(pcwork); mkdirall(pwwork); let pcctrace: str = strings.concat(root, "/parallel-c-compiler.trace"); let pcatrace: str = strings.concat(root, "/parallel-c-assembler.trace"); let pcltrace: str = strings.concat(root, "/parallel-c-linker.trace"); let pwctrace: str = strings.concat(root, "/parallel-ww-compiler.trace"); let pwatrace: str = strings.concat(root, "/parallel-ww-assembler.trace"); let pwltrace: str = strings.concat(root, "/parallel-ww-linker.trace"); writefile(pcctrace, ""); writefile(pcatrace, ""); writefile(pcltrace, ""); writefile(pwctrace, ""); writefile(pwatrace, ""); writefile(pwltrace, ""); let pcenv: []str = blankpackageenv(compilerwrapper, assemblerwrapper, linkerwrapper, driver("w6c"), driver("w6a"), driver("w6l"), pcctrace, pcatrace, pcltrace); let pwenv: []str = blankpackageenv(compilerwrapper, assemblerwrapper, linkerwrapper, driver("w6c_ww"), driver("w6a_ww"), driver("w6l_ww"), pwctrace, pwatrace, pwltrace); let pcav: []str = [driver("ww"), "build", "-w", pcwork, "-I", include, "-o", pcout, "prodonly"]; let pwav: []str = [driver("ww_ww"), "build", "-w", pwwork, "-I", include, "-o", pwout, "prodonly"]; let cc: exec.command; cc.path = pcav[0]; cc.argv = pcav; cc.env = pcenv; cc.dir = repo(); cc.stdoutpath = strings.concat(root, "/blank-parallel-c.stdout"); cc.stderrpath = strings.concat(root, "/blank-parallel-c.stderr"); cc.deadline = time.add(time.now(time.clock.monotonic), (60i64 * (time.second: i64)): time.duration); cc.grace = (100i64 * (time.millisecond: i64)): time.duration; let wc: exec.command; wc.path = pwav[0]; wc.argv = pwav; wc.env = pwenv; wc.dir = repo(); wc.stdoutpath = strings.concat(root, "/blank-parallel-ww.stdout"); wc.stderrpath = strings.concat(root, "/blank-parallel-ww.stderr"); wc.deadline = time.add(time.now(time.clock.monotonic), (60i64 * (time.second: i64)): time.duration); wc.grace = (100i64 * (time.millisecond: i64)): time.duration; let cp: exec.process; let wp: exec.process; exec.start(&cp, &cc); exec.start(&wp, &wc); let cdone: bool = false; let wdone: bool = false; for (!cdone || !wdone) { if (!cdone) { cdone = exec.poll(&cp); }; if (!wdone) { wdone = exec.poll(&wp); }; if (!cdone || !wdone) { time.sleep(time.millisecond, time.clock.monotonic); }; }; let pcerr: str = readfile(cc.stderrpath); let pwerr: str = readfile(wc.stderrpath); assert(cp.result.errno == 0 && cp.result.cleanuperrno == 0 && cp.result.termination == exec.termination.EXIT && cp.result.code == 1 && wp.result.errno == 0 && wp.result.cleanuperrno == 0 && wp.result.termination == exec.termination.EXIT && wp.result.code == 1 && readfile(cc.stdoutpath).len == 0 && readfile(wc.stdoutpath).len == 0 && occurrences(pcerr, "invalid package name _") == 1 && occurrences(pwerr, "invalid package name _") == 1 && same(normalizedtrace(pcerr, strings.concat(pcwork, "/"), pcout), normalizedtrace(pwerr, strings.concat(pwwork, "/"), pwout)) && occurrences(readfile(pcctrace), "BEGIN") == 1 && occurrences(readfile(pwctrace), "BEGIN") == 1 && readfile(pcatrace).len == 0 && readfile(pcltrace).len == 0 && readfile(pwatrace).len == 0 && readfile(pwltrace).len == 0 && !os.exists(pcout) && !os.exists(pwout)); wrongsuffixfamilyabsent(pcwork, "prodonly"); wrongsuffixfamilyabsent(pwwork, "prodonly"); blankpackagenoresidue(root); clean(root); }; fn blankwwi(path: str, pkg: str, declarations: str) str = { return strings.concat("//ww:module ", path, "\npackage ", pkg, ";\n", declarations); }; fn blankwwiinvalid(out: *commandout, path: str, count: i32) void = { expectexit(out, 1); assert(out.stdout.len == 0 && occurrences(out.stderr, strings.concat("could not import ", path, " (invalid package name: \"_\")")) == count && !has(out.stderr, "invalid package name _") && !has(out.stderr, "imported and not used")); }; fn blankwwinocascade(stderr: str) void = { assert(!has(stderr, "selector '") && !has(stderr, "unknown type '") && !has(stderr, "calling non-function") && !has(stderr, "is not exported")); }; // Imported package-object names are validated after complete primary syntax, // at the source import's path token. Invalid providers get one cached fake // package per canonical path: aliases cannot hide them, selectors do not // cascade, and unreachable interface origin facts are wholly inert. @test fn blank_imported_package_name_direct_resolution() void = { let root: str = fresh(); let one: str = strings.concat(root, "/bad-one.wwi"); let two: str = strings.concat(root, "/bad-two.wwi"); let mixed: str = strings.concat(root, "/mixed.wwi"); let support: str = strings.concat(root, "/test.wwi"); let reservedsupport: str = strings.concat(root, "/__wwtest.wwi"); let validtarget: str = strings.concat(root, "/good-target.wwi"); let declarations: str = strings.concat( "export type Thing = i32;\n", "export let VALUE: i32;\n", "export fn call() i32;\n", "export let hidden: i32;\n"); writefile(one, blankwwi("bad.one", "_", declarations)); writefile(two, blankwwi("bad.two", "_", declarations)); writefile(mixed, strings.concat( blankwwi("bad.one", "_", "export fn hidden() i32;\n"), "//ww:module bridge.good\n", "package bridge;\n", "import bad.one;\n", "export fn run() i32;\n")); writefile(support, blankwwi("test", "_", "export fn run() void;\n")); writefile(reservedsupport, blankwwi("__wwtest", "_", "export fn run() void;\n")); writefile(validtarget, blankwwi("good.one", "targetpkg", "@test fn imported_test() void;\n")); let defaults: str = strings.concat(root, "/default.ww"); let explicit: str = strings.concat(root, "/explicit.ww"); let blank: str = strings.concat(root, "/blank.ww"); let shadow: str = strings.concat(root, "/shadow.ww"); writefile(defaults, strings.concat( "package main;\n", "import bad.one;\n", "fn qualified(x: one.Thing) i32 = { ", "return one.VALUE + one.call(); };\n", // This unqualified use would resolve if invalid-owner declarations // escaped the reached-owner filter. "fn independent() i32 = { return hidden; };\n")); writefile(explicit, strings.concat( "package main;\n", "import local bad.one;\n", "fn qualified(x: local.Thing) i32 = { ", "return local.VALUE + local.call(); };\n", "fn independent() i32 = { return missing_explicit; };\n")); writefile(blank, strings.concat( "package main;\n", "import _ bad.one;\n", "fn independent() i32 = { return missing_blank; };\n")); writefile(shadow, strings.concat( "package main;\n", "import local bad.one;\n", "fn shadow(local: i32) i32 = { return local.value; };\n")); let sources: []str = [defaults, explicit, blank]; let missing: []str = ["hidden", "missing_explicit", "missing_blank"]; let missinglines: []str = ["4", "4", "3"]; let compilers: []str = ["w6c", "w6c_ww"]; let tags: []str = ["c", "ww"]; let aliasdiags: []str = ["", "", ""]; let shadowdiag: str = ""; let si: i32 = 0; for (si < compilers.len) { let ci: i32 = 0; for (ci < sources.len) { let asmout: str = strings.concat(root, "/alias-", tags[si], "-", boundarypkgname(ci), ".s"); let wwiout: str = strings.concat(root, "/alias-", tags[si], "-", boundarypkgname(ci), ".wwi"); if (ci == 1) { writefile(asmout, "prior imported assembly\n"); writefile(wwiout, "prior imported interface\n"); }; let av: []str = [driver(compilers[si]), "-c", "--import", "bad.one", one, "-o", asmout, "-I", wwiout, sources[ci]]; let out: commandout; runcommand(root, strings.concat("blank-wwi-alias-", tags[si], "-", boundarypkgname(ci)), av, (30i64 * (time.second: i64)): time.duration, &out); blankwwiinvalid(&out, "bad.one", 1); let want: str = ""; if (ci == 0) { want = strings.concat(sources[ci], ":2:8: error: could not import bad.one ", "(invalid package name: \"_\")\n"); } else if (ci == 1) { want = strings.concat(sources[ci], ":2:14: error: could not import bad.one ", "(invalid package name: \"_\")\n"); } else { want = strings.concat(sources[ci], ":2:10: error: could not import bad.one ", "(invalid package name: \"_\")\n"); }; let exact: str = strings.concat(want, sources[ci], ":", missinglines[ci], ":33: error: undefined: ", missing[ci], "\n"); assert(same(out.stderr, exact)); blankwwinocascade(out.stderr); if (ci == 1) { assert(same(readfile(asmout), "prior imported assembly\n") && same(readfile(wwiout), "prior imported interface\n")); } else { assert(!os.exists(asmout) && !os.exists(wwiout)); }; if (si == 0) { aliasdiags[ci] = strings.dup(out.stderr); } else { assert(same(aliasdiags[ci], out.stderr)); }; ci += 1; }; let shadowout: str = strings.concat(root, "/shadow-", tags[si], ".s"); let shadowav: []str = [driver(compilers[si]), "-c", "--import", "bad.one", one, "-o", shadowout, shadow]; let shadowresult: commandout; runcommand(root, strings.concat("blank-wwi-shadow-", tags[si]), shadowav, (30i64 * (time.second: i64)): time.duration, &shadowresult); blankwwiinvalid(&shadowresult, "bad.one", 1); assert(has(shadowresult.stderr, "selector 'value' undefined") && !has(shadowresult.stderr, "package 'local'") && !os.exists(shadowout)); if (si == 0) { shadowdiag = strings.dup(shadowresult.stderr); } else { assert(same(shadowdiag, shadowresult.stderr)); }; si += 1; }; let repeated: str = strings.concat(root, "/repeated.ww"); let ordered: str = strings.concat(root, "/ordered.ww"); let syntaxbad: str = strings.concat(root, "/syntax-bad.ww"); let bridge: str = strings.concat(root, "/bridge.ww"); let inert: str = strings.concat(root, "/inert.ww"); let testsource: str = strings.concat(root, "/support_test.ww"); let badtargetsource: str = strings.concat(root, "/bad-target_test.ww"); let goodtargetsource: str = strings.concat(root, "/good-target.ww"); writefile(repeated, strings.concat( "package main;\n", "import first bad.one;\n", "import second bad.one;\n", "fn independent() i32 = { return after_repeat; };\n")); writefile(ordered, strings.concat( "package main;\n", "import second bad.two;\n", "import first bad.one;\n", "fn independent() i32 = { return after_order; };\n")); writefile(syntaxbad, "package main;\nimport bad.one;\nimport ;\n"); writefile(bridge, strings.concat( "package main;\nimport bridge.good;\n", "fn main() i32 = { return bridge.run(); };\n")); writefile(inert, "package main;\nfn main() i32 = { return 0; };\n"); writefile(testsource, "package supportcase;\n@test fn selected() void = { assert(true); };\n"); writefile(badtargetsource, strings.concat( "package targetcase;\nimport bad.one;\n", "@test fn selected() void = { let x: i32 = one.VALUE; assert(x == 0); };\n")); writefile(goodtargetsource, "package main;\nimport good.one;\n"); let repeatedref: str = ""; let orderedref: str = ""; let bridgeref: str = ""; let inertasmref: str = ""; let inertwwiref: str = ""; let fallbackasmrefs: []str = ["", ""]; let fallbackwwirefs: []str = ["", ""]; let targetasmref: str = ""; let targetwwiref: str = ""; si = 0; for (si < compilers.len) { let out: commandout; let repeatout: str = strings.concat(root, "/repeat-", tags[si], ".s"); let repeatav: []str = [driver(compilers[si]), "-c", "--import", "bad.one", one, "-o", repeatout, repeated]; runcommand(root, strings.concat("blank-wwi-repeat-", tags[si]), repeatav, (30i64 * (time.second: i64)): time.duration, &out); blankwwiinvalid(&out, "bad.one", 1); let repeatexact: str = strings.concat(repeated, ":2:14: error: could not import bad.one ", "(invalid package name: \"_\")\n", repeated, ":4:33: error: undefined: after_repeat\n"); assert(same(out.stderr, repeatexact) && !os.exists(repeatout)); if (si == 0) { repeatedref = strings.dup(out.stderr); } else { assert(same(repeatedref, out.stderr)); }; let orderout: str = strings.concat(root, "/order-", tags[si], ".s"); let orderav: []str = [driver(compilers[si]), "-c", "--import", "bad.one", one, "--import", "bad.two", two, "-o", orderout, ordered]; runcommand(root, strings.concat("blank-wwi-order-", tags[si]), orderav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); let twodiag: str = "could not import bad.two (invalid package name: \"_\")"; let onediag: str = "could not import bad.one (invalid package name: \"_\")"; let orderexact: str = strings.concat(ordered, ":2:15: error: ", twodiag, "\n", ordered, ":3:14: error: ", onediag, "\n", ordered, ":4:33: error: undefined: after_order\n"); assert(out.stdout.len == 0 && same(out.stderr, orderexact) && occurrences(out.stderr, twodiag) == 1 && occurrences(out.stderr, onediag) == 1 && pos(out.stderr, twodiag) < pos(out.stderr, onediag) && pos(out.stderr, onediag) < pos(out.stderr, "undefined: after_order") && !os.exists(orderout)); blankwwinocascade(out.stderr); if (si == 0) { orderedref = strings.dup(out.stderr); } else { assert(same(orderedref, out.stderr)); }; let syntaxout: str = strings.concat(root, "/syntax-", tags[si], ".s"); let syntaxav: []str = [driver(compilers[si]), "-c", "--import", "bad.one", one, "-o", syntaxout, syntaxbad]; runcommand(root, strings.concat("blank-wwi-syntax-", tags[si]), syntaxav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && has(out.stderr, "expected identifier") && !has(out.stderr, "could not import") && !os.exists(syntaxout)); // The mixed interface is not a reachability root. Its invalid owner, // valid embedded origin, import edge, and declarations are byte-inert. let noasm: str = strings.concat(root, "/inert-no-", tags[si], ".s"); let nowwi: str = strings.concat(root, "/inert-no-", tags[si], ".wwi"); let withasm: str = strings.concat(root, "/inert-with-", tags[si], ".s"); let withwwi: str = strings.concat(root, "/inert-with-", tags[si], ".wwi"); let noav: []str = [driver(compilers[si]), "-c", "-o", noasm, "-I", nowwi, inert]; let withav: []str = [driver(compilers[si]), "-c", "--import", "bad.one", mixed, "-o", withasm, "-I", withwwi, inert]; runcommand(root, strings.concat("blank-wwi-inert-no-", tags[si]), noav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); runcommand(root, strings.concat("blank-wwi-inert-with-", tags[si]), withav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && same(readfile(noasm), readfile(withasm)) && same(readfile(nowwi), readfile(withwwi))); if (si == 0) { inertasmref = strings.dup(readfile(noasm)); inertwwiref = strings.dup(readfile(nowwi)); } else { assert(same(inertasmref, readfile(noasm)) && same(inertwwiref, readfile(nowwi))); }; // Reaching the valid embedded origin separately makes its bad.one // edge live; the diagnostic belongs to the interface import token. let bridgeout: str = strings.concat(root, "/bridge-", tags[si], ".s"); let bridgeav: []str = [driver(compilers[si]), "-c", "--import", "bad.one", mixed, "-o", bridgeout, bridge]; runcommand(root, strings.concat("blank-wwi-bridge-", tags[si]), bridgeav, (30i64 * (time.second: i64)): time.duration, &out); blankwwiinvalid(&out, "bad.one", 1); assert(same(out.stderr, strings.concat(mixed, ":6:8: error: could not import bad.one ", "(invalid package name: \"_\")\n")) && !os.exists(bridgeout)); blankwwinocascade(out.stderr); if (si == 0) { bridgeref = strings.dup(out.stderr); } else { assert(same(bridgeref, out.stderr)); }; let supportpaths: []str = ["test", "__wwtest"]; let supportfiles: []str = [support, reservedsupport]; let ti: i32 = 0; for (ti < supportpaths.len) { let supportout: str = strings.concat(root, "/support-bad-", tags[si], "-", boundarypkgname(ti), ".s"); let supportav: []str = [driver(compilers[si]), "-c", "-T", "--test-support-module", supportpaths[ti], "--import", supportpaths[ti], supportfiles[ti], "-o", supportout, testsource]; runcommand(root, strings.concat("blank-wwi-support-bad-", tags[si], "-", boundarypkgname(ti)), supportav, (30i64 * (time.second: i64)): time.duration, &out); blankwwiinvalid(&out, supportpaths[ti], 1); let supportwant: str = strings.concat(testsource, ":1:1: error: could not import ", supportpaths[ti], " (invalid package name: \"_\")\n"); assert(same(out.stderr, supportwant) && !os.exists(supportout)); // With no supplied support interface, both compiler-owned support // spellings retain the established raw external fallback. let fallbackasm: str = strings.concat(root, "/support-ok-", tags[si], "-", boundarypkgname(ti), ".s"); let fallbackwwi: str = strings.concat(root, "/support-ok-", tags[si], "-", boundarypkgname(ti), ".wwi"); let fallbackav: []str = [driver(compilers[si]), "-c", "-T", "--test-support-module", supportpaths[ti], "-o", fallbackasm, "-I", fallbackwwi, testsource]; runcommand(root, strings.concat("blank-wwi-support-ok-", tags[si], "-", boundarypkgname(ti)), fallbackav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(fallbackasm) && os.exists(fallbackwwi)); if (si == 0) { fallbackasmrefs[ti] = strings.dup(readfile(fallbackasm)); fallbackwwirefs[ti] = strings.dup(readfile(fallbackwwi)); } else { assert(same(fallbackasmrefs[ti], readfile(fallbackasm)) && same(fallbackwwirefs[ti], readfile(fallbackwwi))); }; ti += 1; }; // Generated test-target canonicalization must not overwrite an invalid // provider's path-leaf fake qualifier. The valid control still publishes // with the compiler-owned full canonical target spelling. let badtargetout: str = strings.concat(root, "/bad-target-", tags[si], ".s"); let badtargetav: []str = [driver(compilers[si]), "-c", "-T", "--test-support-module", "test", "--test-target-package", "bad.one", "--import", "bad.one", one, "-o", badtargetout, badtargetsource]; runcommand(root, strings.concat("blank-wwi-bad-target-", tags[si]), badtargetav, (30i64 * (time.second: i64)): time.duration, &out); blankwwiinvalid(&out, "bad.one", 1); let badtargetwant: str = strings.concat(badtargetsource, ":2:8: error: could not import bad.one ", "(invalid package name: \"_\")\n"); assert(same(out.stderr, badtargetwant) && !os.exists(badtargetout)); let goodtargetasm: str = strings.concat(root, "/good-target-", tags[si], ".s"); let goodtargetwwi: str = strings.concat(root, "/good-target-", tags[si], ".wwi"); let goodtargetav: []str = [driver(compilers[si]), "-c", "-T", "--test-support-module", "test", "--test-target-package", "good.one", "--import", "good.one", validtarget, "-o", goodtargetasm, "-I", goodtargetwwi, goodtargetsource]; runcommand(root, strings.concat("blank-wwi-good-target-", tags[si]), goodtargetav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(goodtargetasm) && os.exists(goodtargetwwi)); if (si == 0) { targetasmref = strings.dup(readfile(goodtargetasm)); targetwwiref = strings.dup(readfile(goodtargetwwi)); } else { assert(same(targetasmref, readfile(goodtargetasm)) && same(targetwwiref, readfile(goodtargetwwi))); }; si += 1; }; // Independent compiler processes do not share fake-package or dedupe // state. Both preserve disjoint output paths and return the same stream. let cconout: str = strings.concat(root, "/concurrent-c.s"); let wconout: str = strings.concat(root, "/concurrent-ww.s"); let cav: []str = [driver("w6c"), "-c", "--import", "bad.one", one, "-o", cconout, defaults]; let wav: []str = [driver("w6c_ww"), "-c", "--import", "bad.one", one, "-o", wconout, defaults]; let concurrentenv: []str = blankpackagebaseenv(); let cc: exec.command; cc.path = cav[0]; cc.argv = cav; cc.env = concurrentenv; cc.dir = repo(); cc.stdoutpath = strings.concat(root, "/blank-wwi-concurrent-c.stdout"); cc.stderrpath = strings.concat(root, "/blank-wwi-concurrent-c.stderr"); cc.deadline = time.add(time.now(time.clock.monotonic), (30i64 * (time.second: i64)): time.duration); cc.grace = (100i64 * (time.millisecond: i64)): time.duration; let wc: exec.command; wc.path = wav[0]; wc.argv = wav; wc.env = concurrentenv; wc.dir = repo(); wc.stdoutpath = strings.concat(root, "/blank-wwi-concurrent-ww.stdout"); wc.stderrpath = strings.concat(root, "/blank-wwi-concurrent-ww.stderr"); wc.deadline = time.add(time.now(time.clock.monotonic), (30i64 * (time.second: i64)): time.duration); wc.grace = (100i64 * (time.millisecond: i64)): time.duration; let cp: exec.process; let wp: exec.process; exec.start(&cp, &cc); exec.start(&wp, &wc); let cdone: bool = false; let wdone: bool = false; for (!cdone || !wdone) { if (!cdone) { cdone = exec.poll(&cp); }; if (!wdone) { wdone = exec.poll(&wp); }; if (!cdone || !wdone) { time.sleep(time.millisecond, time.clock.monotonic); }; }; assert(cp.result.errno == 0 && cp.result.cleanuperrno == 0 && cp.result.termination == exec.termination.EXIT && cp.result.code == 1 && wp.result.errno == 0 && wp.result.cleanuperrno == 0 && wp.result.termination == exec.termination.EXIT && wp.result.code == 1 && readfile(cc.stdoutpath).len == 0 && readfile(wc.stdoutpath).len == 0 && same(readfile(cc.stderrpath), readfile(wc.stderrpath)) && occurrences(readfile(cc.stderrpath), "could not import bad.one") == 1 && !os.exists(cconout) && !os.exists(wconout)); blankpackagenoresidue(root); clean(root); }; fn blankwwicorrupt(valid: str) str = { assert(occurrences(valid, "package renamed;") == 1); match (strings.replace(valid, "package renamed;", "package _;")) { case let corrupted: str => return corrupted; case nomem => abort("blank interface corruption allocation failed"); }; }; // Persistent `.wwi` bytes are compiler inputs, never canonical package // identity. A caller-corrupted provider interface invalidates only consumers, // preserves every prior committed/public byte, and becomes reusable again // after exact restoration in both build and test workdir formats. @test fn blank_imported_package_name_public_persistence() void = { let root: str = fresh(); let source: str = strings.concat(root, "/source"); let dep: str = strings.concat(source, "/dep"); let app: str = strings.concat(source, "/app"); mkdirall(dep); mkdirall(app); writefile(strings.concat(dep, "/provider.ww"), strings.concat( "package renamed;\n", "export fn value() i32 = { return 31; };\n")); // A same-named physical source remains an import decoy; only the dotted // directory provider may produce dep's interface and action. writefile(strings.concat(source, "/dep.ww"), strings.concat( "package _;\n", "export fn decoy() i32 = { return 99; };\n")); writefile(strings.concat(app, "/main.ww"), strings.concat( "package main;\nimport wire dep;\n", "fn dependency() i32 = { return wire.value(); };\n", "fn main() i32 = { return dependency() - 31; };\n")); writefile(strings.concat(app, "/main_test.ww"), strings.concat( "package main;\n", "@test fn imported_value() void = { assert(dependency() == 31); };\n")); let compilerwrapper: str = strings.concat(root, "/public-w6c.sh"); let assemblerwrapper: str = strings.concat(root, "/public-w6a.sh"); let linkerwrapper: str = strings.concat(root, "/public-w6l.sh"); writeexecutable(compilerwrapper, strings.concat( "#!/bin/sh\nprintf 'BEGIN' >> \"$WW_BLANK_PACKAGE_CTRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_BLANK_PACKAGE_CTRACE\"; done\n", "printf '\\n' >> \"$WW_BLANK_PACKAGE_CTRACE\"\n", "exec \"$WW_BLANK_PACKAGE_REAL_C\" \"$@\"\n")); writeexecutable(assemblerwrapper, strings.concat( "#!/bin/sh\nprintf 'assemble\\n' >> \"$WW_BLANK_PACKAGE_ATRACE\"\n", "exec \"$WW_BLANK_PACKAGE_REAL_A\" \"$@\"\n")); writeexecutable(linkerwrapper, strings.concat( "#!/bin/sh\nprintf 'link\\n' >> \"$WW_BLANK_PACKAGE_LTRACE\"\n", "exec \"$WW_BLANK_PACKAGE_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 buildbinref: str = ""; let buildartifactsref: str = ""; let builddiagref: str = ""; let testbinref: str = ""; let testartifactsref: str = ""; let testdiagref: str = ""; let runtestdiagref: str = ""; let testcontrolref: str = ""; let si: i32 = 0; for (si < stages.len) { let ctrace: str = strings.concat(root, "/public-c-", tags[si], ".trace"); let atrace: str = strings.concat(root, "/public-a-", tags[si], ".trace"); let ltrace: str = strings.concat(root, "/public-l-", tags[si], ".trace"); writefile(ctrace, ""); writefile(atrace, ""); writefile(ltrace, ""); let env: []str = blankpackageenv(compilerwrapper, assemblerwrapper, linkerwrapper, driver(compilers[si]), driver(assemblers[si]), driver(linkers[si]), ctrace, atrace, ltrace); let out: commandout; let work: str = strings.concat(root, "/public-build-work-", tags[si]); let output: str = strings.concat(root, "/public-build-output-", tags[si]); mkdirall(work); let buildav: []str = [driver(stages[si]), "build", "-w", work, "-I", source, "-o", output, "app"]; runcommandenv(root, strings.concat("blank-wwi-build-seed-", tags[si]), buildav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(output) && occurrences(readfile(ctrace), "BEGIN") == 2 && readfile(atrace).len != 0 && readfile(ltrace).len != 0 && !has(readfile(strings.concat(work, "/app.unit.ww")), "decoy")); let runav: []str = [output]; runcommand(root, strings.concat("blank-wwi-build-run-", tags[si]), runav, (10i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); let validinterface: str = strings.dup(readfile(strings.concat(work, "/dep.wwi"))); let corruptinterface: str = blankwwicorrupt(validinterface); let built: str = strings.dup(readfile(output)); let validstate: str = strings.dup(treesnapshot(work)); let validartifacts: str = strings.dup(artifacttreesnapshot(work)); if (si == 0) { buildbinref = strings.dup(built); buildartifactsref = strings.dup(validartifacts); } else { assert(same(buildbinref, built) && same(buildartifactsref, validartifacts)); }; rewritefile(strings.concat(work, "/dep.wwi"), corruptinterface); let corruptstate: str = strings.dup(treesnapshot(work)); blankpackageresettraces(ctrace, atrace, ltrace); runcommandenv(root, strings.concat("blank-wwi-build-corrupt-", tags[si]), buildav, env, (120i64 * (time.second: i64)): time.duration, &out); blankwwiinvalid(&out, "dep", 1); blankwwinocascade(out.stderr); assert(same(built, readfile(output)) && same(corruptstate, treesnapshot(work)) && occurrences(readfile(ctrace), "BEGIN") == 1 && has(readfile(ctrace), "/app.unit.new") && !has(readfile(ctrace), "/dep.unit.new") && readfile(atrace).len == 0 && readfile(ltrace).len == 0); let builddiag: str = normalizedtrace(out.stderr, strings.concat(work, "/"), output); if (si == 0) { builddiagref = strings.dup(builddiag); } else { assert(same(builddiagref, builddiag)); }; rewritefile(strings.concat(work, "/dep.wwi"), validinterface); blankpackageresettraces(ctrace, atrace, ltrace); runcommandenv(root, strings.concat("blank-wwi-build-restored-", tags[si]), buildav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && same(built, readfile(output)) && same(validstate, treesnapshot(work)) && readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len != 0); // The same corrupted committed interface fails the internal-test // consumer and preserves a previously retained test executable. let testwork: str = strings.concat(root, "/public-test-work-", tags[si]); let testbin: str = strings.concat(root, "/public-test-output-", tags[si]); mkdirall(testwork); let testav: []str = [driver(stages[si]), "test", "-c", "-w", testwork, "-I", source, "-o", testbin, "app"]; blankpackageresettraces(ctrace, atrace, ltrace); runcommandenv(root, strings.concat("blank-wwi-test-seed-", tags[si]), testav, env, (180i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(testbin)); let testcontrolav: []str = [testbin]; runcommand(root, strings.concat("blank-wwi-test-control-", tags[si]), testcontrolav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0 && has(out.stdout, "imported_value ... ok\n")); if (si == 0) { testcontrolref = strings.dup(out.stdout); } else { assert(same(testcontrolref, out.stdout)); }; let validtestinterface: str = strings.dup(readfile(strings.concat(testwork, "/dep.wwi"))); let corrupttestinterface: str = blankwwicorrupt(validtestinterface); let testbytes: str = strings.dup(readfile(testbin)); let validteststate: str = strings.dup(treesnapshot(testwork)); let validtestartifacts: str = strings.dup( artifacttreesnapshot(testwork)); if (si == 0) { testbinref = strings.dup(testbytes); testartifactsref = strings.dup(validtestartifacts); } else { assert(same(testbinref, testbytes) && same(testartifactsref, validtestartifacts)); }; rewritefile(strings.concat(testwork, "/dep.wwi"), corrupttestinterface); let corruptteststate: str = strings.dup(treesnapshot(testwork)); blankpackageresettraces(ctrace, atrace, ltrace); runcommandenv(root, strings.concat("blank-wwi-test-corrupt-", tags[si]), testav, env, (180i64 * (time.second: i64)): time.duration, &out); blankwwiinvalid(&out, "dep", 1); blankwwinocascade(out.stderr); assert(same(testbytes, readfile(testbin)) && same(corruptteststate, treesnapshot(testwork)) && occurrences(readfile(ctrace), "BEGIN") == 1 && has(readfile(ctrace), "app-internal-test.unit.new") && !has(readfile(ctrace), "/dep.unit.new") && readfile(atrace).len == 0 && readfile(ltrace).len == 0); let testdiag: str = normalizedtrace(out.stderr, strings.concat(testwork, "/"), testbin); if (si == 0) { testdiagref = strings.dup(testdiag); } else { assert(same(testdiagref, testdiag)); }; blankpackageresettraces(ctrace, atrace, ltrace); let runtestav: []str = [driver(stages[si]), "test", "-w", testwork, "-I", source, "-o", testbin, "app"]; runcommandenv(root, strings.concat("blank-wwi-test-run-corrupt-", tags[si]), runtestav, env, (180i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stdout, "FAIL\n") && occurrences(out.stderr, "could not import dep (invalid package name: \"_\")") == 1 && !has(out.stderr, "invalid package name _") && !has(out.stderr, "imported and not used") && same(testbytes, readfile(testbin)) && same(corruptteststate, treesnapshot(testwork)) && occurrences(readfile(ctrace), "BEGIN") == 1 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); let runtestdiag: str = normalizedtrace(out.stderr, strings.concat(testwork, "/"), testbin); if (si == 0) { runtestdiagref = strings.dup(runtestdiag); } else { assert(same(runtestdiagref, runtestdiag)); }; rewritefile(strings.concat(testwork, "/dep.wwi"), validtestinterface); blankpackageresettraces(ctrace, atrace, ltrace); runcommandenv(root, strings.concat("blank-wwi-test-restored-", tags[si]), testav, env, (180i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && same(testbytes, readfile(testbin)) && same(validteststate, treesnapshot(testwork)) && readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len != 0); si += 1; }; blankpackagenoresidue(root); clean(root); }; fn envrequired(name: str) str = { match (os.getenv(name)) { case let value: str => { assert(value.len != 0); return strings.dup(value); }; case void => abort("missing package-test environment"); }; }; fn repo() str = { return envrequired("WW_PACKAGE_REPO"); }; fn driver(name: str) str = { return strings.concat(repo(), "/out/bin/", name); }; fn readfile(path: str) str = { let fd: i32 = os.open(path, os.flag.RDONLY, 0i32); assert(fd >= 0); let sr: (i64 | os.oserror) = os.filesize(fd); let n: i64 = -1i64; match (sr) { case let v: i64 => n = v; case let e: os.oserror => abort("filesize failed"); }; assert(n >= 0i64); let b: []u8 = alloc([], (n + 1i64): u64)!; b.len = (n + 1i64): i32; let rr: (i64 | os.oserror) = os.readall(fd, b.ptr, n: u64); os.close(fd); let got: i64 = -1i64; match (rr) { case let v: i64 => got = v; case let e: os.oserror => abort("read failed"); }; assert(got == n); let ni: i32 = n: i32; b[ni] = 0u8; let out: str; out.ptr = b.ptr; out.len = n: i32; return out; }; fn permissionmode(path: str) u32 = { let fi: os.filestat; match (os.stat(&fi, path)) { case void => return (fi.mode: u32) & 511u32; case let e: os.oserror => abort("stat failed"); }; }; fn writefile(path: str, content: str) void = { let fd: i32 = os.open(path, os.flag.WRONLY | os.flag.CREATE | os.flag.EXCL, 384i32); assert(fd >= 0); match (os.writeall(fd, content.ptr, content.len: u64)) { case let n: i64 => assert(n == content.len: i64); case let e: os.oserror => abort("write failed"); }; assert(os.close(fd) == 0); }; fn rewritefile(path: str, content: str) void = { let fd: i32 = os.open(path, os.flag.WRONLY | os.flag.TRUNC, 384i32); assert(fd >= 0); match (os.writeall(fd, content.ptr, content.len: u64)) { case let n: i64 => assert(n == content.len: i64); case let e: os.oserror => abort("write failed"); }; assert(os.close(fd) == 0); }; fn putbomfile(path: str, before: str, after: str, rewrite: bool) void = { let flags: os.flag = os.flag.WRONLY; if (rewrite) { flags |= os.flag.TRUNC; } else { flags |= os.flag.CREATE | os.flag.EXCL; }; let fd: i32 = os.open(path, flags, 384i32); assert(fd >= 0); match (os.writeall(fd, before.ptr, before.len: u64)) { case let n: i64 => assert(n == before.len: i64); case let e: os.oserror => abort("write before BOM failed"); }; let bom: [3]u8; bom[0] = 0xefu8; bom[1] = 0xbbu8; bom[2] = 0xbfu8; match (os.writeall(fd, bom.ptr, 3u64)) { case let n: i64 => assert(n == 3i64); case let e: os.oserror => abort("write BOM failed"); }; match (os.writeall(fd, after.ptr, after.len: u64)) { case let n: i64 => assert(n == after.len: i64); case let e: os.oserror => abort("write after BOM failed"); }; assert(os.close(fd) == 0); }; fn writebomfile(path: str, content: str) void = { putbomfile(path, "", content, false); }; fn rewritebomfile(path: str, content: str) void = { putbomfile(path, "", content, true); }; fn writemidbomfile(path: str, before: str, after: str) void = { putbomfile(path, before, after, false); }; fn rewritemidbomfile(path: str, before: str, after: str) void = { putbomfile(path, before, after, true); }; fn putnulfile(path: str, before: str, after: str, rewrite: bool) void = { let flags: os.flag = os.flag.WRONLY; if (rewrite) { flags |= os.flag.TRUNC; } else { flags |= os.flag.CREATE | os.flag.EXCL; }; let fd: i32 = os.open(path, flags, 384i32); assert(fd >= 0); match (os.writeall(fd, before.ptr, before.len: u64)) { case let n: i64 => assert(n == before.len: i64); case let e: os.oserror => abort("write before NUL failed"); }; let nul: [1]u8; nul[0] = 0u8; match (os.writeall(fd, nul.ptr, 1u64)) { case let n: i64 => assert(n == 1i64); case let e: os.oserror => abort("write NUL failed"); }; match (os.writeall(fd, after.ptr, after.len: u64)) { case let n: i64 => assert(n == after.len: i64); case let e: os.oserror => abort("write after NUL failed"); }; assert(os.close(fd) == 0); }; fn writenulfile(path: str, before: str, after: str) void = { putnulfile(path, before, after, false); }; fn rewritenulfile(path: str, before: str, after: str) void = { putnulfile(path, before, after, true); }; fn putinvalidutf8file(path: str, before: str, after: str, rewrite: bool) void = { let flags: os.flag = os.flag.WRONLY; if (rewrite) { flags |= os.flag.TRUNC; } else { flags |= os.flag.CREATE | os.flag.EXCL; }; let fd: i32 = os.open(path, flags, 384i32); assert(fd >= 0); match (os.writeall(fd, before.ptr, before.len: u64)) { case let n: i64 => assert(n == before.len: i64); case let e: os.oserror => abort("write before invalid UTF-8 failed"); }; let invalid: [1]u8; invalid[0] = 0xffu8; match (os.writeall(fd, invalid.ptr, 1u64)) { case let n: i64 => assert(n == 1i64); case let e: os.oserror => abort("write invalid UTF-8 failed"); }; match (os.writeall(fd, after.ptr, after.len: u64)) { case let n: i64 => assert(n == after.len: i64); case let e: os.oserror => abort("write after invalid UTF-8 failed"); }; assert(os.close(fd) == 0); }; fn writeinvalidutf8file(path: str, before: str, after: str) void = { putinvalidutf8file(path, before, after, false); }; fn rewriteinvalidutf8file(path: str, before: str, after: str) void = { putinvalidutf8file(path, before, after, true); }; fn writedoublenulfile(path: str, before: str, after: str) void = { let fd: i32 = os.open(path, os.flag.WRONLY | os.flag.CREATE | os.flag.EXCL, 384i32); assert(fd >= 0); match (os.writeall(fd, before.ptr, before.len: u64)) { case let n: i64 => assert(n == before.len: i64); case let e: os.oserror => abort("write before NUL pair failed"); }; let pair: [2]u8; pair[0] = 0u8; pair[1] = 0u8; match (os.writeall(fd, pair.ptr, 2u64)) { case let n: i64 => assert(n == 2i64); case let e: os.oserror => abort("write NUL pair failed"); }; match (os.writeall(fd, after.ptr, after.len: u64)) { case let n: i64 => assert(n == after.len: i64); case let e: os.oserror => abort("write after NUL pair failed"); }; assert(os.close(fd) == 0); }; fn writeexecutable(path: str, content: str) void = { let fd: i32 = os.open(path, os.flag.WRONLY | os.flag.CREATE | os.flag.EXCL, 448i32); assert(fd >= 0); match (os.writeall(fd, content.ptr, content.len: u64)) { case let n: i64 => assert(n == content.len: i64); case let e: os.oserror => abort("write failed"); }; assert(os.close(fd) == 0); }; fn runcommand(root: str, name: str, argv: []str, lifetime: time.duration, out: *commandout) void = { let c: exec.command; c.path = argv[0]; c.argv = argv; c.env = os.getenvs(); c.dir = repo(); c.stdoutpath = strings.concat(root, "/", name, ".stdout"); c.stderrpath = strings.concat(root, "/", name, ".stderr"); c.deadline = time.add(time.now(time.clock.monotonic), lifetime); c.grace = (100i64 * (time.millisecond: i64)): time.duration; let r: exec.result; exec.run(&c, &r); assert(r.errno == 0 && r.cleanuperrno == 0); out.termination = r.termination; out.code = r.code; out.stdout = readfile(c.stdoutpath); out.stderr = readfile(c.stderrpath); }; fn runcommanddir(root: str, name: str, dir: str, argv: []str, lifetime: time.duration, out: *commandout) void = { let c: exec.command; c.path = argv[0]; c.argv = argv; c.env = os.getenvs(); c.dir = dir; c.stdoutpath = strings.concat(root, "/", name, ".stdout"); c.stderrpath = strings.concat(root, "/", name, ".stderr"); c.deadline = time.add(time.now(time.clock.monotonic), lifetime); c.grace = (100i64 * (time.millisecond: i64)): time.duration; let r: exec.result; exec.run(&c, &r); assert(r.errno == 0 && r.cleanuperrno == 0); out.termination = r.termination; out.code = r.code; out.stdout = readfile(c.stdoutpath); out.stderr = readfile(c.stderrpath); }; fn runcommandenv(root: str, name: str, argv: []str, env: []str, lifetime: time.duration, out: *commandout) void = { let c: exec.command; c.path = argv[0]; c.argv = argv; c.env = env; c.dir = repo(); c.stdoutpath = strings.concat(root, "/", name, ".stdout"); c.stderrpath = strings.concat(root, "/", name, ".stderr"); c.deadline = time.add(time.now(time.clock.monotonic), lifetime); c.grace = (100i64 * (time.millisecond: i64)): time.duration; let r: exec.result; exec.run(&c, &r); assert(r.errno == 0 && r.cleanuperrno == 0); out.termination = r.termination; out.code = r.code; out.stdout = readfile(c.stdoutpath); out.stderr = readfile(c.stderrpath); }; fn runcommandenvdir(root: str, name: str, argv: []str, env: []str, dir: str, lifetime: time.duration, out: *commandout) void = { let c: exec.command; c.path = argv[0]; c.argv = argv; c.env = env; c.dir = dir; c.stdoutpath = strings.concat(root, "/", name, ".stdout"); c.stderrpath = strings.concat(root, "/", name, ".stderr"); c.deadline = time.add(time.now(time.clock.monotonic), lifetime); c.grace = (100i64 * (time.millisecond: i64)): time.duration; let r: exec.result; exec.run(&c, &r); assert(r.errno == 0 && r.cleanuperrno == 0); out.termination = r.termination; out.code = r.code; out.stdout = readfile(c.stdoutpath); out.stderr = readfile(c.stderrpath); }; // A direct descriptor mapping keeps the caller's duplicate environment and cwd // intact while giving the nested driver known nonempty input. fn runcommandinputenvdir(root: str, name: str, argv: []str, env: []str, dir: str, input: str, lifetime: time.duration, out: *commandout) void = { let c: exec.command; c.path = argv[0]; c.argv = argv; c.env = env; c.dir = dir; c.stdinpath = input; c.stdoutpath = strings.concat(root, "/", name, ".stdout"); c.stderrpath = strings.concat(root, "/", name, ".stderr"); c.deadline = time.add(time.now(time.clock.monotonic), lifetime); c.grace = (100i64 * (time.millisecond: i64)): time.duration; let r: exec.result; exec.run(&c, &r); assert(r.errno == 0 && r.cleanuperrno == 0); out.termination = r.termination; out.code = r.code; out.stdout = readfile(c.stdoutpath); out.stderr = readfile(c.stderrpath); }; fn clean(root: str) void = { let av: []str = ["/bin/rm", "-rf", "--", root]; let c: exec.command; c.path = av[0]; c.argv = av; c.env = os.getenvs(); c.dir = "/"; c.stdoutpath = strings.concat(root, ".cleanup.stdout"); c.stderrpath = strings.concat(root, ".cleanup.stderr"); c.deadline = time.add(time.now(time.clock.monotonic), time.second); c.grace = (50i64 * (time.millisecond: i64)): time.duration; let r: exec.result; exec.run(&c, &r); assert(r.termination == exec.termination.EXIT && r.code == 0); assert(os.remove(c.stdoutpath) == 0); assert(os.remove(c.stderrpath) == 0); }; fn fresh() str = { return strings.dup(temp.dir()); }; fn pos(haystack: str, needle: str) i32 = { match (strings.index(haystack, needle)) { case let n: i32 => return n; case void => return -1; }; }; fn has(haystack: str, needle: str) bool = { return pos(haystack, needle) >= 0; }; fn same(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; }; fn hasbom(s: str) bool = { let i: i32 = 0; for (i + 2 < s.len) { if (s[i] == 0xefu8 && s[i + 1] == 0xbbu8 && s[i + 2] == 0xbfu8) { return true; }; i += 1; }; return false; }; fn occurrences(haystack: str, needle: str) i32 = { if (needle.len == 0 || haystack.len < needle.len) { return 0; }; let count: i32 = 0; let i: i32 = 0; for (i + needle.len <= haystack.len) { let matches: bool = true; let j: i32 = 0; for (j < needle.len) { if (haystack[i + j] != needle[j]) { matches = false; break; }; j += 1; }; if (matches) { count += 1; i += needle.len; } else { i += 1; }; }; return count; }; fn boundarypkgname(n: i32) str = { assert(n >= 0 && n < 1000); let out: []u8 = alloc([], 4u64)!; append(out, 'p'); append(out, ('0': i32 + (n / 100)): u8); append(out, ('0': i32 + ((n / 10) % 10)): u8); append(out, ('0': i32 + (n % 10)): u8); return strings.frombytes(out); }; fn linecontaining(text: str, needle: str) str = { let start: i32 = 0; let i: i32 = 0; for (i <= text.len) { if (i == text.len || text[i] == '\n': u8) { let line: str; line.ptr = text.ptr + (start: u64); line.len = i - start; if (has(line, needle)) { return line; }; start = i + 1; }; i += 1; }; abort("missing trace line"); }; fn expectexit(out: *commandout, code: i32) void = { assert(out.termination == exec.termination.EXIT); assert(out.code == code); }; fn primarydiagnostic(s: str) str = { let begin: i32 = pos(s, ": error: "); if (begin >= 0) { begin += 9; } else { begin = pos(s, "ww: dependency cycle: "); }; if (begin < 0) { return s; }; let end: i32 = begin; for (end < s.len && s[end] != '\n') { end += 1; }; return strings.sub(s, begin, end); }; fn rejectpackagestable(root: str, label: str, target: str, needle: str) void = { let drivers: []str = ["ww", "ww_ww", "ww", "ww_ww"]; let tags: []str = ["c1", "w1", "c2", "w2"]; let diagnostics: []str = ["", "", "", ""]; let outstem: str = strings.concat(root, "/reject-", label); let i: i32 = 0; for (i < drivers.len) { let av: []str = [driver(drivers[i]), "test", "-c", "-o", outstem, "-I", root, target]; let out: commandout; runcommand(root, strings.concat("reject-", label, "-", tags[i]), av, (60i64 * (time.second: i64)): time.duration, &out); assert(out.termination == exec.termination.EXIT && out.code != 0); assert(has(out.stderr, needle)); diagnostics[i] = strings.dup(primarydiagnostic(out.stderr)); let scratch: str = strings.concat(outstem, ".sepwork"); if (os.exists(scratch)) { clean(scratch); }; if (os.exists(outstem)) { clean(outstem); }; i += 1; }; assert(same(diagnostics[0], diagnostics[2])); assert(same(diagnostics[1], diagnostics[3])); assert(same(diagnostics[0], diagnostics[1])); }; fn packagepath(relative: str) str = { return strings.concat(repo(), "/test/package/", relative); }; fn workescape(s: str) str = { let out: []u8 = alloc([], (s.len * 2 + 1): u64)!; let i: i32 = 0; for (i < s.len) { if (s[i] == '_') { append(out, '_'); append(out, 'u'); } else { if (s[i] == '/') { append(out, '_'); append(out, 's'); } else { append(out, s[i]); }; }; i += 1; }; return strings.frombytes(out); }; fn localidentity(dir: str, declaredname: str) str = { let out: []u8 = alloc([], (dir.len * 4 + 16): u64)!; let prefix: str = "__wwlocal.p"; let i: i32 = 0; for (i < prefix.len) { append(out, prefix[i]); i += 1; }; let hex: str = "0123456789abcdef"; i = 0; for (i < dir.len) { let c: u8 = dir[i]; if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) { append(out, c); } else if (c == '_') { append(out, '_'); append(out, 'u'); } else if (c == '/') { append(out, '_'); append(out, 's'); } else { let high: i32 = (c / 16u8): i32; let low: i32 = (c % 16u8): i32; append(out, '_'); append(out, 'x'); append(out, hex[high]); append(out, hex[low]); }; i += 1; }; return strings.frombytes(out); }; fn digesthex(h: *hash.hash) str = { let digest: [32]u8; hash.sum(h, digest[0:32]); let out: []u8 = alloc([], 65u64)!; let digits: str = "0123456789abcdef"; let i: i32 = 0; for (i < 32) { let high: i32 = (digest[i] / 16u8): i32; let low: i32 = (digest[i] % 16u8): i32; append(out, digits[high]); append(out, digits[low]); i += 1; }; return strings.frombytes(out); }; fn packagestoragekeyfor(path: str, canon: str, variant: i32, role: i32, for_test: str) str = { let state: sha256.state = sha256.sha256(); let h: *hash.hash = (&state): *hash.hash; hash.write(h, strings.toutf8("ww-package-storage-v3:")); let tag: [4]u8; tag[0] = ('0': i32 + variant): u8; tag[1] = ':': u8; tag[2] = ('0': i32 + role): u8; tag[3] = ':': u8; hash.write(h, tag[0:4]); hash.write(h, strings.toutf8(path)); let zero: [1]u8; zero[0] = 0u8; hash.write(h, zero[0:1]); hash.write(h, strings.toutf8(canon)); hash.write(h, zero[0:1]); if (for_test.len != 0) { hash.write(h, strings.toutf8(for_test)); }; let out: []u8 = alloc([], 81u64)!; let prefix: str = "__wwpkg.v"; let i: i32 = 0; for (i < prefix.len) { append(out, prefix[i]); i += 1; }; append(out, tag[0]); append(out, '.'); append(out, 'r'); append(out, tag[2]); append(out, '.'); append(out, 'h'); let hex: str = digesthex(h); i = 0; for (i < hex.len) { append(out, hex[i]); i += 1; }; return strings.frombytes(out); }; fn packagestoragekey(path: str, canon: str, variant: i32, role: i32) str = { return packagestoragekeyfor(path, canon, variant, role, ""); }; fn importrelative(path: str) str = { let out: []u8 = alloc([], (path.len + 1): u64)!; let i: i32 = 0; for (i < path.len) { if (path[i] == '.') { append(out, '/'); } else { append(out, path[i]); }; i += 1; }; return strings.frombytes(out); }; fn longidentity(leaf: str) str = { let out: []u8 = alloc([], 384u64)!; let component: str = "segmentabcdefghijklmnop"; let n: i32 = 0; for (n < 13) { let i: i32 = 0; for (i < component.len) { append(out, component[i]); i += 1; }; append(out, '.'); n += 1; }; let i: i32 = 0; for (i < leaf.len) { append(out, leaf[i]); i += 1; }; return strings.frombytes(out); }; fn deeppunctuationpath(root: str, branch: str) str = { let out: str = strings.concat(root, "/outside +% @ _/", branch); let component: str = "/punctuation + percent% at@ under_score"; let n: i32 = 0; for (n < 9) { out = strings.concat(out, component); n += 1; }; return out; }; fn mkdirall(path: str) void = { match (os.mkdirs(path, 448i32)) { case void => void; case let e: os.oserror => abort("mkdirs failed"); }; }; fn normalizedtrace(trace: str, work: str, output: str) str = { let first: str; match (strings.replace(trace, work, "$WORK/")) { case let value: str => first = value; case nomem => abort("trace normalization failed"); }; match (strings.replace(first, output, "$OUTPUT")) { case let value: str => return value; case nomem => abort("trace normalization failed"); }; }; fn directoryhasnew(path: str) bool = { let fd: i32 = os.open(path, 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; }; if (len >= 4 && buf[off + 19 + len - 4] == '.' && buf[off + 19 + len - 3] == 'n' && buf[off + 19 + len - 2] == 'e' && buf[off + 19 + len - 1] == 'w') { assert(os.close(fd) == 0); return true; }; off += reclen; }; n = os.getdents64(fd, buf.ptr, buf.len: u64); }; assert(n == 0i64); assert(os.close(fd) == 0); return false; }; fn directoryhasfragment(path: str, needle: str) bool = { let fd: i32 = os.open(path, 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 name: str; name.ptr = buf.ptr + (off + 19): u64; name.len = len; if (has(name, needle)) { assert(os.close(fd) == 0); return true; }; off += reclen; }; n = os.getdents64(fd, buf.ptr, buf.len: u64); }; assert(n == 0i64); assert(os.close(fd) == 0); return false; }; fn directoryisempty(path: str) bool = { let fd: i32 = os.open(path, 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) { assert(os.close(fd) == 0); return false; }; off += reclen; }; n = os.getdents64(fd, buf.ptr, buf.len: u64); }; assert(n == 0i64); assert(os.close(fd) == 0); 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); 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; for (i < want.len) { if (s[off + i] != want[i]) { return false; }; i += 1; }; return true; }; fn archivememberend(ar: str, off: i32, name: str) i32 = { assert(byteshasat(ar, off, name)); assert(byteshasat(ar, off + 58, "`\n")); let size: i32 = 0; let seen: bool = false; let i: i32 = 0; for (i < 10) { let c: u8 = ar[off + 48 + i]; if (c >= '0' && c <= '9') { seen = true; size = size * 10 + ((c - '0'): i32); } else { assert(c == ' '); }; i += 1; }; assert(seen); return off + 60 + size + (size & 1); }; fn checkactionarchive(ar: str, hasinit: bool) void = { assert(byteshasat(ar, 0, "!\n")); let off: i32 = archivememberend(ar, 8, "pkg.o/"); if (hasinit) { off = archivememberend(ar, off, "init.o/"); }; assert(off == ar.len); }; fn hexbytes(value: str) str = { let out: []u8 = alloc([], (value.len * 2 + 1): u64)!; let digits: str = "0123456789abcdef"; let i: i32 = 0; for (i < value.len) { let high: i32 = (value[i] / 16u8): i32; let low: i32 = (value[i] % 16u8): i32; append(out, digits[high]); append(out, digits[low]); i += 1; }; return strings.frombytes(out); }; fn cwdtestenv(stage: str) []str = { let inherited: []str = os.getenvs(); let env: []str = alloc([], (inherited.len + 24): u64)!; let i: i32 = 0; for (i < inherited.len) { if (!strings.hasprefix(inherited[i], "PWD=") && !strings.hasprefix(inherited[i], "PATH=") && !strings.hasprefix(inherited[i], "LC_ALL=") && !strings.hasprefix(inherited[i], "TMPDIR=") && !strings.hasprefix(inherited[i], "WW_CWD_ORIGINAL=") && !strings.hasprefix(inherited[i], "WW_CWD_EMPTY=") && !strings.hasprefix(inherited[i], "lc_all=") && !strings.hasprefix(inherited[i], "WW_W6C=") && !strings.hasprefix(inherited[i], "WW_W6A=") && !strings.hasprefix(inherited[i], "WW_W6L=") && !strings.hasprefix(inherited[i], "WW_SRCLIB=") && !strings.hasprefix(inherited[i], "WW_CWD_")) { append(env, inherited[i]); }; i += 1; }; append(env, "PATH=/ww-path-inherited-first"); append(env, "PWD=/ww-cwd-inherited-first"); append(env, "LC_ALL=C.UTF-8"); append(env, "TMPDIR=/ww-caller-tmp-first"); append(env, "WW_CWD_ORIGINAL=first"); append(env, "WW_CWD_EMPTY="); append(env, "lc_all=case-kept"); append(env, "WW_CWD_MALFORMED"); append(env, ""); append(env, "WW_CWD_MALFORMED"); if (same(stage, "ww")) { append(env, strings.concat("WW_W6C=", driver("w6c"))); append(env, strings.concat("WW_W6A=", driver("w6a"))); append(env, strings.concat("WW_W6L=", driver("w6l"))); } else { assert(same(stage, "ww_ww")); append(env, strings.concat("WW_W6C=", driver("w6c_ww"))); append(env, strings.concat("WW_W6A=", driver("w6a_ww"))); append(env, strings.concat("WW_W6L=", driver("w6l_ww"))); }; append(env, strings.concat("WW_SRCLIB=", repo(), "/lib")); append(env, "LC_ALL=C"); append(env, "TMPDIR=/ww-caller-tmp-last"); append(env, "WW_CWD_ORIGINAL=last"); append(env, "PATH=/ww-path-inherited-last"); append(env, "PWD=/ww-cwd-inherited-last"); return env; }; fn cwdassertpath(text: str, label: str, path: str, count: str) void = { assert(has(text, strings.concat(label, " PATH=", path, "\n"))); assert(has(text, strings.concat(label, " PATH-count=", count, "\n"))); }; fn cwdasserttestpath(text: str, label: str) void = { cwdassertpath(text, label, strings.concat(repo(), "/out/bin:/ww-path-inherited-first"), "1"); }; fn cwdassertcallerpath(text: str, label: str) void = { cwdassertpath(text, label, "/ww-path-inherited-first", "2"); }; fn cwdassertenvvalues(text: str, label: str) void = { assert(has(text, strings.concat(label, " LC_ALL=C.UTF-8\n"))); assert(has(text, strings.concat(label, " TMPDIR=/ww-caller-tmp-first\n"))); assert(has(text, strings.concat(label, " WW_CWD_ORIGINAL=first\n"))); assert(has(text, strings.concat(label, " WW_CWD_EMPTY=\n"))); assert(has(text, strings.concat(label, " lc_all=case-kept\n"))); }; fn cwdassertdirectoryenv(text: str, label: str) void = { cwdassertenvvalues(text, label); assert(has(text, strings.concat(label, " LC_ALL-count=1\n"))); assert(has(text, strings.concat(label, " TMPDIR-count=1\n"))); assert(has(text, strings.concat(label, " WW_CWD_ORIGINAL-count=1\n"))); assert(has(text, strings.concat(label, " WW_CWD_EMPTY-count=1\n"))); assert(has(text, strings.concat(label, " lc_all-count=1\n"))); assert(has(text, strings.concat(label, " malformed-count=2\n"))); assert(has(text, strings.concat(label, " empty-entry-count=0\n"))); }; fn cwdassertcallerenv(text: str, label: str) void = { cwdassertenvvalues(text, label); assert(has(text, strings.concat(label, " LC_ALL-count=2\n"))); assert(has(text, strings.concat(label, " TMPDIR-count=2\n"))); assert(has(text, strings.concat(label, " WW_CWD_ORIGINAL-count=2\n"))); assert(has(text, strings.concat(label, " WW_CWD_EMPTY-count=1\n"))); assert(has(text, strings.concat(label, " lc_all-count=1\n"))); assert(has(text, strings.concat(label, " malformed-count=2\n"))); assert(has(text, strings.concat(label, " empty-entry-count=1\n"))); }; fn cwdassertrecord(text: str, label: str, cwd: str, pwd: str, pwdcount: str, pwdlast: str, data: str, testdata: str, input: str) void = { assert(has(text, strings.concat(label, " cwd=", cwd, "\n"))); assert(has(text, strings.concat(label, " PWD=", pwd, "\n"))); assert(has(text, strings.concat(label, " PWD-count=", pwdcount, "\n"))); assert(has(text, strings.concat(label, " PWD-last=", pwdlast, "\n"))); assert(has(text, strings.concat(label, " data=", data, "\n"))); assert(has(text, strings.concat(label, " testdata=", testdata, "\n"))); assert(has(text, strings.concat(label, " stdin=", input, "\n"))); assert(has(text, strings.concat(label, " create=ok\n"))); }; fn cwdassertmergedstreams(text: str, label: str, dir: str) void = { let out1: str = strings.concat("STREAM ", label, " out-1 cwd=", dir, "\n"); let err1: str = strings.concat("STREAM ", label, " err-1 cwd=", dir, "\n"); let out2: str = strings.concat("STREAM ", label, " out-2 cwd=", dir, "\n"); let err2: str = strings.concat("STREAM ", label, " err-2 cwd=", dir, "\n"); let p0: i32 = pos(text, out1); let p1: i32 = pos(text, err1); let p2: i32 = pos(text, out2); let p3: i32 = pos(text, err2); assert(p0 >= 0 && p0 < p1 && p1 < p2 && p2 < p3); assert(occurrences(text, out1) == 1 && occurrences(text, err1) == 1); assert(occurrences(text, out2) == 1 && occurrences(text, err2) == 1); }; fn cwdassertsplitstreams(stdout: str, stderr: str, label: str, dir: str) void = { let out1: str = strings.concat("STREAM ", label, " out-1 cwd=", dir, "\n"); let err1: str = strings.concat("STREAM ", label, " err-1 cwd=", dir, "\n"); let out2: str = strings.concat("STREAM ", label, " out-2 cwd=", dir, "\n"); let err2: str = strings.concat("STREAM ", label, " err-2 cwd=", dir, "\n"); let p0: i32 = pos(stdout, out1); let p2: i32 = pos(stdout, out2); let p1: i32 = pos(stderr, err1); let p3: i32 = pos(stderr, err2); assert(p0 >= 0 && p0 < p2 && p1 >= 0 && p1 < p3); assert(!has(stdout, err1) && !has(stdout, err2)); assert(!has(stderr, out1) && !has(stderr, out2)); }; fn cwdassertpackage(text: str, label: str, dir: str, data: str, testdata: str) void = { cwdassertrecord(text, label, dir, dir, "1", "yes", data, testdata, "eof"); cwdassertdirectoryenv(text, label); cwdasserttestpath(text, label); cwdassertmergedstreams(text, label, dir); }; fn cwdwritedata(dir: str, label: str) void = { let testdata: str = strings.concat(dir, "/testdata"); assert(os.mkdir(testdata, 448i32) == 0); writefile(strings.concat(dir, "/data.txt"), strings.concat(label, "-data\n")); writefile(strings.concat(testdata, "/input.txt"), strings.concat(label, "-testdata\n")); }; @test fn deterministic_routing_and_filters() void = { let root: str = fresh(); let target: str = packagepath("routing"); let sourceroot: str = strings.concat(repo(), "/test/package"); let av: []str = [driver("ww"), "test", "-I", sourceroot, "-list", target]; let out: commandout; runcommand(root, "list", av, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let first: i32 = pos(out.stdout, "routing.private_helper_first\n"); let second: i32 = pos(out.stdout, "routing.same_package\n"); let third: i32 = pos(out.stdout, "routing.external_package\n"); assert(first >= 0 && first < second && second < third); assert(occurrences(out.stdout, "routing.private_helper_first\n") == 1); assert(occurrences(out.stdout, "routing.same_package\n") == 1); assert(occurrences(out.stdout, "routing.external_package\n") == 1); let rav: []str = [driver("ww"), "test", "-I", sourceroot, target]; runcommand(root, "run", rav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "routing.private_helper_first ... ok\n")); assert(has(out.stdout, "routing.same_package ... ok\n")); assert(has(out.stdout, "routing.external_package ... ok\n")); assert(has(out.stdout, "3 passed, 0 failed, 0 skipped, 0 harness errors\n")); assert(occurrences(out.stdout, "3 passed, 0 failed, 0 skipped, 0 harness errors\n") == 1); let fav: []str = [driver("ww"), "test", "-run", "routing.private_helper_first", "-filter", "external_package", "-I", sourceroot, target]; runcommand(root, "filters", fav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "routing.private_helper_first ... ok\n")); assert(!has(out.stdout, "routing.same_package ...")); assert(has(out.stdout, "routing.external_package ... ok\n")); let nav: []str = [driver("ww"), "test", "-I", sourceroot, "-run", "no-such-*", target]; runcommand(root, "nomatch", nav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0); assert(occurrences(out.stdout, "testing: warning: no tests to run\n") == 1); assert(occurrences(out.stdout, "3 discovered, 0 selected, 0 started, 0 completed\n") == 1); assert(occurrences(out.stdout, strings.concat("ok ", target, " [routing] [no tests to run]\n")) == 1); let lnav: []str = [driver("ww"), "test", "-list", "-run", "no-such-*", "-I", sourceroot, target]; runcommand(root, "list-nomatch", lnav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(!has(out.stdout, "testing: warning: no tests to run")); assert(!has(out.stdout, " [no tests to run]")); assert(!has(out.stdout, " discovered, ")); clean(root); }; @test fn named_source_prefixes_are_ignored() void = { let root: str = fresh(); let source: str = strings.concat(root, "/source"); let hiddenparent: str = strings.concat(source, "/.parent"); let symlinks: str = strings.concat(source, "/symlinks"); let warm: str = strings.concat(source, "/warm"); mkdirall(source); mkdirall(hiddenparent); mkdirall(symlinks); mkdirall(warm); let maintext: str = "package main;\nfn main() i32 = { return 0; };\n"; let testtext: str = strings.concat( "package raw_windows_test;\n", "@test fn selected() void = { assert(true); };\n"); let missingmain: str = strings.concat( "package main;\nimport absent.pkg;\n", "fn main() i32 = { return pkg.value(); };\n"); let missingtest: str = strings.concat( "package missing_test;\nimport absent.pkg;\n", "@test fn never() void = { abort(); };\n"); let malformed: str = "this source must never be opened {\n"; let hiddenbuild: str = strings.concat(source, "/_broken_windows_test.ww"); let dotbuild: str = strings.concat(source, "/.missing.ww"); let hiddentest: str = strings.concat(source, "/_broken_windows_test.ww"); let dottest: str = strings.concat(source, "/.missing_test.ww"); let visiblewrong: str = strings.concat(source, "/visible_windows.ww"); let visiblewrongtest: str = strings.concat(source, "/visible_windows_test.ww"); let hiddenparentmain: str = strings.concat(hiddenparent, "/main.ww"); let visibletarget: str = strings.concat(symlinks, "/target.ww"); let hiddentarget: str = strings.concat(symlinks, "/.target.ww"); let hiddenlink: str = strings.concat(symlinks, "/_link.ww"); let hiddendevice: str = strings.concat(symlinks, "/_device.ww"); let visiblelink: str = strings.concat(symlinks, "/visible-link.ww"); let warmvisible: str = strings.concat(warm, "/warm.ww"); let warmhidden: str = strings.concat(warm, "/_warm.ww"); writefile(hiddenbuild, malformed); writefile(dotbuild, missingmain); writefile(dottest, missingtest); writefile(visiblewrong, maintext); writefile(visiblewrongtest, testtext); writefile(hiddenparentmain, maintext); writefile(visibletarget, maintext); writefile(hiddentarget, maintext); assert(os.symlink(visibletarget, hiddenlink) == 0); assert(os.symlink("/dev/null", hiddendevice) == 0); assert(os.symlink(hiddentarget, visiblelink) == 0); writefile(warmvisible, maintext); let stages: []str = ["ww", "ww_ww"]; let tags: []str = ["c", "ww"]; let rootartifacts: []str = ["/__root.unit.ww", "/__root.wwi", "/__root.s", "/__root.o", "/__root.a", "/__root.init.unit.ww", "/__root.init.s", "/__root.init.o"]; let visiblebuildbin: str = ""; let visiblebuildartifacts: []str = alloc([], rootartifacts.len: u64)!; let visibletestbin: str = ""; let visibletestartifacts: []str = alloc([], rootartifacts.len: u64)!; let visibleteststdout: str = ""; let hiddendiagnostics: []str = alloc([], 20u64)!; let si: i32 = 0; for (si < stages.len) { let prefix: str = strings.concat(tags[si], "-"); let coldwork: str = strings.concat(root, "/cold-work-", tags[si]); let coldout: str = strings.concat(root, "/cold-output-", tags[si]); let out: commandout; // A final requested basename, rather than any parent component, owns // Go's unconditional editor-temporary exclusion. let av: []str = [driver(stages[si]), "build", "-w", coldwork, "-o", coldout, "_broken_windows_test.ww"]; runcommanddir(root, strings.concat(prefix, "relative-hidden-build"), source, av, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); let relativebuilddiag: str = "ww: .: directory contains no WW package sources\n"; assert(out.stdout.len == 0 && same(out.stderr, relativebuilddiag) && !os.exists(coldwork) && !os.exists(coldout) && !os.exists(strings.concat(coldout, ".sepwork"))); append(hiddendiagnostics, strings.dup(out.stderr)); let dotbuildav: []str = [driver(stages[si]), "build", "-o", strings.concat(root, "/dot-output-", tags[si]), dotbuild]; runcommand(root, strings.concat(prefix, "dot-missing-build"), dotbuildav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); let sourcediag: str = strings.concat("ww: ", source, ": directory contains no WW package sources\n"); assert(out.stdout.len == 0 && same(out.stderr, sourcediag) && !has(out.stderr, "absent.pkg") && !os.exists(strings.concat(root, "/dot-output-", tags[si])) && !os.exists(strings.concat(root, "/dot-output-", tags[si], ".sepwork"))); append(hiddendiagnostics, strings.dup(out.stderr)); let testcoldwork: str = strings.concat(root, "/cold-test-work-", tags[si]); let testcoldout: str = strings.concat(root, "/cold-test-output-", tags[si]); let hiddentestrunav: []str = [driver(stages[si]), "test", "-w", testcoldwork, "-o", testcoldout, hiddentest]; runcommand(root, strings.concat(prefix, "hidden-test-run"), hiddentestrunav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stdout, "FAIL\n") && same(out.stderr, sourcediag) && !has(out.stderr, "invalid or missing package clause") && !os.exists(testcoldwork) && !os.exists(testcoldout) && !os.exists(strings.concat(testcoldout, ".sepwork"))); append(hiddendiagnostics, strings.dup(out.stderr)); let compiled: str = strings.concat(root, "/hidden-compiled-", tags[si]); let hiddencompileav: []str = [driver(stages[si]), "test", "-c", "-o", compiled, hiddentest]; runcommand(root, strings.concat(prefix, "hidden-test-compile"), hiddencompileav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && same(out.stderr, sourcediag) && !os.exists(compiled) && !os.exists(strings.concat(compiled, ".sepwork"))); append(hiddendiagnostics, strings.dup(out.stderr)); let assembly: str = strings.concat(root, "/hidden-assembly-", tags[si]); let hiddenasmav: []str = [driver(stages[si]), "test", "-S", "-o", assembly, hiddentest]; runcommand(root, strings.concat(prefix, "hidden-test-assembly"), hiddenasmav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && same(out.stderr, sourcediag) && !os.exists(assembly) && !os.exists(strings.concat(assembly, ".sepwork"))); append(hiddendiagnostics, strings.dup(out.stderr)); let dottestav: []str = [driver(stages[si]), "test", dottest]; runcommand(root, strings.concat(prefix, "dot-missing-test"), dottestav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stdout, "FAIL\n") && same(out.stderr, sourcediag) && !has(out.stderr, "absent.pkg")); append(hiddendiagnostics, strings.dup(out.stderr)); let visiblework: str = strings.concat(root, "/visible-work-", tags[si]); let visibleout: str = strings.concat(root, "/visible-output-", tags[si]); mkdirall(visiblework); let visiblebuildav: []str = [driver(stages[si]), "build", "-w", visiblework, "-o", visibleout, visiblewrong]; runcommand(root, strings.concat(prefix, "visible-wrong-platform-build"), visiblebuildav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(visibleout)); let runav: []str = [visibleout]; runcommand(root, strings.concat(prefix, "visible-wrong-platform-run"), runav, (10i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); let visiblebytes: str = strings.dup(readfile(visibleout)); let ai: i32 = 0; if (si == 0) { visiblebuildbin = strings.dup(visiblebytes); } else { assert(same(visiblebuildbin, visiblebytes)); }; for (ai < rootartifacts.len) { let bytes: str = strings.dup(readfile(strings.concat(visiblework, rootartifacts[ai]))); if (si == 0) { append(visiblebuildartifacts, bytes); } else { assert(same(visiblebuildartifacts[ai], bytes)); }; ai += 1; }; let testwork: str = strings.concat(root, "/visible-test-work-", tags[si]); mkdirall(testwork); let visibletestav: []str = [driver(stages[si]), "test", "-w", testwork, visiblewrongtest]; runcommand(root, strings.concat(prefix, "visible-wrong-platform-test"), visibletestav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0 && occurrences(out.stdout, "selected ... ok\n") == 1 && occurrences(out.stdout, "1 passed, 0 failed, 0 skipped, 0 harness errors\n") == 1); let testbytes: str = strings.dup(readfile(strings.concat(testwork, "/main"))); if (si == 0) { visibletestbin = strings.dup(testbytes); visibleteststdout = strings.dup(out.stdout); } else { assert(same(visibletestbin, testbytes) && same(visibleteststdout, out.stdout)); }; ai = 0; for (ai < rootartifacts.len) { let bytes: str = strings.dup(readfile(strings.concat(testwork, rootartifacts[ai]))); if (si == 0) { append(visibletestartifacts, bytes); } else { assert(same(visibletestartifacts[ai], bytes)); }; ai += 1; }; let parentout: str = strings.concat(root, "/hidden-parent-", tags[si]); let parentwork: str = strings.concat(root, "/hidden-parent-work-", tags[si]); mkdirall(parentwork); let parentav: []str = [driver(stages[si]), "build", "-w", parentwork, "-o", parentout, hiddenparentmain]; runcommand(root, strings.concat(prefix, "hidden-parent-visible-base"), parentav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && same(visiblebuildbin, readfile(parentout))); let hiddenlinkout: str = strings.concat(root, "/hidden-link-", tags[si]); let hiddenlinkav: []str = [driver(stages[si]), "build", "-o", hiddenlinkout, hiddenlink]; runcommand(root, strings.concat(prefix, "hidden-request-symlink"), hiddenlinkav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); let symlinkdiag: str = strings.concat("ww: ", symlinks, ": directory contains no WW package sources\n"); assert(out.stdout.len == 0 && same(out.stderr, symlinkdiag) && !os.exists(hiddenlinkout) && !os.exists(strings.concat(hiddenlinkout, ".sepwork"))); append(hiddendiagnostics, strings.dup(out.stderr)); let deviceout: str = strings.concat(root, "/hidden-device-", tags[si]); let devicework: str = strings.concat(root, "/hidden-device-work-", tags[si]); let deviceav: []str = [driver(stages[si]), "build", "-w", devicework, "-o", deviceout, hiddendevice]; runcommand(root, strings.concat(prefix, "hidden-device-build"), deviceav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && same(out.stderr, symlinkdiag) && !os.exists(devicework) && !os.exists(deviceout) && !os.exists(strings.concat(deviceout, ".sepwork"))); append(hiddendiagnostics, strings.dup(out.stderr)); let devicetestwork: str = strings.concat(root, "/hidden-device-test-work-", tags[si]); let devicetestout: str = strings.concat(root, "/hidden-device-test-output-", tags[si]); let devicetestav: []str = [driver(stages[si]), "test", "-w", devicetestwork, "-o", devicetestout, hiddendevice, "selected"]; runcommand(root, strings.concat(prefix, "hidden-device-test"), devicetestav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stdout, "FAIL\n") && same(out.stderr, symlinkdiag) && !os.exists(devicetestwork) && !os.exists(devicetestout) && !os.exists(strings.concat(devicetestout, ".sepwork"))); append(hiddendiagnostics, strings.dup(out.stderr)); let visiblelinkout: str = strings.concat(root, "/visible-link-", tags[si]); let visiblelinkwork: str = strings.concat(root, "/visible-link-work-", tags[si]); mkdirall(visiblelinkwork); let visiblelinkav: []str = [driver(stages[si]), "build", "-w", visiblelinkwork, "-o", visiblelinkout, visiblelink]; runcommand(root, strings.concat(prefix, "visible-request-symlink"), visiblelinkav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && same(visiblebuildbin, readfile(visiblelinkout))); let warmwork: str = strings.concat(root, "/warm-work-", tags[si]); let warmout: str = strings.concat(root, "/warm-output-", tags[si]); mkdirall(warmwork); let warmvalidav: []str = [driver(stages[si]), "build", "-w", warmwork, "-o", warmout, warmvisible]; runcommand(root, strings.concat(prefix, "warm-visible"), warmvalidav, (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 warmsnapshots: []str = alloc([], rootartifacts.len: u64)!; ai = 0; for (ai < rootartifacts.len) { append(warmsnapshots, strings.dup(readfile(strings.concat(warmwork, rootartifacts[ai])))); ai += 1; }; assert(os.rename(warmvisible, warmhidden) == 0); let warmhiddenav: []str = [driver(stages[si]), "build", "-w", warmwork, "-o", warmout, warmhidden]; runcommand(root, strings.concat(prefix, "warm-hidden"), warmhiddenav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); let warmdiag: str = strings.concat("ww: ", warm, ": directory contains no WW package sources\n"); assert(out.stdout.len == 0 && same(out.stderr, warmdiag) && same(warmbin, readfile(warmout)) && !os.exists(strings.concat(warmout, ".new")) && !os.exists(strings.concat(warmout, ".sepwork"))); append(hiddendiagnostics, strings.dup(out.stderr)); ai = 0; for (ai < rootartifacts.len) { assert(same(warmsnapshots[ai], readfile(strings.concat(warmwork, rootartifacts[ai])))); ai += 1; }; assert(!directoryhasnew(warmwork) && !directoryhasfragment(warmwork, ".wwtxn.") && !directoryhasfragment(warmwork, ".install") && !directoryhasfragment(warmwork, ".sepwork")); assert(os.rename(warmhidden, warmvisible) == 0); runcommand(root, strings.concat(prefix, "warm-restored"), warmvalidav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && same(warmbin, readfile(warmout))); ai = 0; for (ai < rootartifacts.len) { assert(same(warmsnapshots[ai], readfile(strings.concat(warmwork, rootartifacts[ai])))); ai += 1; }; assert(!directoryhasnew(warmwork) && !directoryhasfragment(warmwork, ".wwtxn.") && !directoryhasfragment(warmwork, ".install") && !directoryhasfragment(warmwork, ".sepwork")); si += 1; }; let half: i32 = hiddendiagnostics.len / 2; let di: i32 = 0; for (di < half) { assert(same(hiddendiagnostics[di], hiddendiagnostics[di + half])); di += 1; }; assert(!directoryhasnew(root) && !directoryhasfragment(root, ".wwtxn.") && !directoryhasfragment(root, ".install") && !directoryhasfragment(root, ".sepwork") && !directoryhasfragment(root, ".capture") && !directoryhasfragment(root, ".result") && !directoryhasfragment(root, ".request")); clean(root); }; // A visible raw test-source build observes only its initial package/import // header and then omits it. Build and test retain their directory-package // front door, while run's named-source front rejects a .ww-spelled directory. // Neither classification may redefine package or dotted-import identity. @test fn named_test_sources_are_header_loaded_then_omitted() void = { let root: str = fresh(); let source: str = strings.concat(root, "/source"); let include: str = strings.concat(root, "/include"); let dependency: str = strings.concat(include, "/domain/value"); let dirsource: str = strings.concat(source, "/directory.ww"); let dirtarget: str = strings.concat(source, "/directory-target"); let emptydir: str = strings.concat(source, "/empty.ww"); let baddirtarget: str = strings.concat(source, "/broken-target"); mkdirall(source); mkdirall(dependency); mkdirall(dirsource); mkdirall(dirtarget); mkdirall(emptydir); mkdirall(baddirtarget); 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 baddirlink: str = strings.concat(source, "/broken_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 dirtargettest: str = strings.concat(dirtarget, "/ignored_test.ww"); let dirtargettestexcluded: str = "package main;\nimport missing.test.only;\n"; let dirtargettestselected: str = strings.concat( "package main;\nimport domain.value;\n", "@test fn directory_selected() void = { assert(value.number() == 5); };\n"); 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(dependency, "/value.ww"), strings.concat( "package value;\n", "export fn number() i32 = { return 5; };\n")); writefile(strings.concat(dirsource, "/main.ww"), "package main;\nfn main() i32 = { return 3; };\n"); writefile(strings.concat(dirsource, "/ignored_test.ww"), "package main;\nimport missing.test.only;\n"); writefile(strings.concat(dirtarget, "/main.ww"), strings.concat( "package main;\nimport domain.value;\n", "fn main() i32 = { return value.number(); };\n")); writefile(dirtargettest, dirtargettestexcluded); writefile(strings.concat(baddirtarget, "/main.ww"), "package main;\nimport ;\n"); assert(os.symlink(linktarget, link) == 0); assert(os.symlink(dirtarget, dirlink) == 0); assert(os.symlink(baddirtarget, baddirlink) == 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", "if test \"$WW_TESTONLY_FAIL_C\" = 1; then\n", " printf 'injected directory compiler failure\\n' >&2\n", " exit 97\nfi\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", "if test \"$WW_TESTONLY_LINK_MODE\" = signal; then\n", " kill -TERM \"$$\"\n", "fi\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 cdirectdirbin: str = ""; let cdirectdirartifacts: str = ""; let clinkdirbin: str = ""; let clinkdirartifacts: str = ""; let clinkdirasm: str = ""; let clinkdirplainasm: str = ""; let clinkdirchanged: str = ""; let clinkdirchangedartifacts: str = ""; let compilerfaildiag: str = ""; let linkersignaldiag: str = ""; let directorytestrunout: str = ""; let directorytestrunerr: str = ""; let directoryrunout: str = ""; let directoryrunerr: str = ""; let directorytestrunartifacts: str = ""; let directorytestbin: str = ""; let directorytestcompileartifacts: str = ""; let directorytestasm: str = ""; let baseenv: []str = os.getenvs(); let si: i32 = 0; for (si < stages.len) { rewritefile(dirtargettest, dirtargettestexcluded); 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 + 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_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]))); let failcindex: i32 = env.len; append(env, "WW_TESTONLY_FAIL_C="); let linkmodeindex: i32 = env.len; append(env, "WW_TESTONLY_LINK_MODE="); 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); // Directory loading wins before raw-source suffix classification. Empty // and malformed directories retain directory diagnostics, including the // requested symlink spelling of a selected source. let emptywork: str = strings.concat(root, "/empty-dir-work-", tags[si]); let emptyout: str = strings.concat(root, "/empty-dir-output-", tags[si]); mkdirall(emptywork); let emptyav: []str = [driver(stages[si]), "build", "-w", emptywork, "-I", include, "-o", emptyout, emptydir]; runcommandenv(root, strings.concat("testonly-empty-directory-", tags[si]), emptyav, env, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); let emptywant: str = strings.concat("ww: ", emptydir, ": directory contains no WW package sources\n"); assert(out.stdout.len == 0 && same(out.stderr, emptywant) && !has(out.stderr, "cannot read source") && directoryisempty(emptywork) && !os.exists(emptyout) && readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); let baddirwork: str = strings.concat(root, "/bad-dir-work-", tags[si]); let baddirout: str = strings.concat(root, "/bad-dir-output-", tags[si]); mkdirall(baddirwork); let baddirav: []str = [driver(stages[si]), "build", "-w", baddirwork, "-I", include, "-o", baddirout, baddirlink]; runcommandenv(root, strings.concat("testonly-bad-directory-", tags[si]), baddirav, env, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); let baddirwant: str = strings.concat(baddirlink, "/main.ww:2:8: error: expected identifier, got ;\n"); assert(out.stdout.len == 0 && same(out.stderr, baddirwant) && !has(out.stderr, "cannot read source") && directoryisempty(baddirwork) && !os.exists(baddirout) && readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); // A real directory whose requested basename ends in .ww remains a // directory request. The suffix does not make the directory a raw source. let directdirwork: str = strings.concat(root, "/direct-dir-work-", tags[si]); let directdirout: str = strings.concat(root, "/direct-dir-output-", tags[si]); mkdirall(directdirwork); let directdirav: []str = [driver(stages[si]), "build", "-w", directdirwork, "-I", include, "-o", directdirout, dirsource]; runcommandenv(root, strings.concat("testonly-direct-directory-", tags[si]), directdirav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(directdirout) && readfile(ctrace).len != 0 && readfile(atrace).len != 0 && readfile(ltrace).len != 0); let directdirbytes: str = strings.dup(readfile(directdirout)); let directdirartifacts: str = strings.dup( artifacttreesnapshot(directdirwork)); if (si == 0) { cdirectdirbin = strings.dup(directdirbytes); cdirectdirartifacts = strings.dup(directdirartifacts); } else { assert(same(cdirectdirbin, directdirbytes) && same(cdirectdirartifacts, directdirartifacts)); }; let directrun: []str = [directdirout]; runcommand(root, strings.concat("testonly-direct-directory-run-", tags[si]), directrun, time.second, &out); expectexit(&out, 3); rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); // A directory target remains a directory request despite a test-looking // requested symlink spelling. Both stages reach the ordinary directory // graph and produce byte-identical commands and semantic artifacts. 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, "-I", include, "-o", linkdirout, dirlink]; runcommandenv(root, strings.concat("testonly-directory-link-", tags[si]), linkdirav, env, (60i64 * (time.second: i64)): time.duration, &out); 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); let linkdirbytes: str = strings.dup(readfile(linkdirout)); let linkdirartifacts: str = strings.dup( artifacttreesnapshot(linkdirwork)); if (si == 0) { clinkdirbin = strings.dup(linkdirbytes); clinkdirartifacts = strings.dup(linkdirartifacts); } else { assert(same(clinkdirbin, linkdirbytes) && same(clinkdirartifacts, linkdirartifacts)); }; let linkdirrun: []str = [linkdirout]; runcommand(root, strings.concat("testonly-directory-link-run-", tags[si]), linkdirrun, time.second, &out); expectexit(&out, 5); rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); runcommandenv(root, strings.concat("testonly-directory-link-warm-", tags[si]), linkdirav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && same(linkdirbytes, readfile(linkdirout)) && same(clinkdirartifacts, artifacttreesnapshot(linkdirwork)) && readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len != 0); rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); // Output-directory presentation comes from the canonical selected target, // while exact-null and assembly-only disposition do not change selection. let linkoutdir: str = strings.concat(root, "/link-output-directory-", tags[si]); let linkoutwork: str = strings.concat(root, "/link-output-work-", tags[si]); mkdirall(linkoutdir); mkdirall(linkoutwork); writefile(strings.concat(linkoutdir, "/sentinel"), "caller-owned\n"); let linkoutav: []str = [driver(stages[si]), "build", "-w", linkoutwork, "-I", include, "-o", linkoutdir, dirlink]; runcommandenv(root, strings.concat("testonly-link-output-directory-", tags[si]), linkoutav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let linkoutchild: str = strings.concat(linkoutdir, "/directory-target"); assert(out.stdout.len == 0 && out.stderr.len == 0 && same(readfile(strings.concat(linkoutdir, "/sentinel")), "caller-owned\n") && same(linkdirbytes, readfile(linkoutchild)) && readfile(ctrace).len != 0 && readfile(atrace).len != 0 && readfile(ltrace).len != 0); rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); let linknullwork: str = strings.concat(root, "/link-null-work-", tags[si]); mkdirall(linknullwork); let linknullav: []str = [driver(stages[si]), "build", "-w", linknullwork, "-I", include, "-o", "/dev/null", dirlink]; runcommandenv(root, strings.concat("testonly-link-null-", tags[si]), linknullav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && readfile(ctrace).len != 0 && readfile(atrace).len != 0 && readfile(ltrace).len != 0 && artifacttreesnapshot(linknullwork).len != 0); rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); let asmdirwork: str = strings.concat(root, "/link-asm-work-", tags[si]); let asmdirstem: str = strings.concat(root, "/link-asm-output-", tags[si]); mkdirall(asmdirwork); let asmdirav: []str = [driver(stages[si]), "build", "-S", "-w", asmdirwork, "-I", include, "-o", asmdirstem, dirlink]; runcommandenv(root, strings.concat("testonly-link-assembly-", tags[si]), asmdirav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && !os.exists(asmdirstem) && readfile(ctrace).len != 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); let asmdirsnapshot: str = strings.dup( artifacttreesnapshot(asmdirwork)); assert(asmdirsnapshot.len != 0); if (si == 0) { clinkdirasm = strings.dup(asmdirsnapshot); } else { assert(same(clinkdirasm, asmdirsnapshot)); }; // Without -o, the requested directory spelling owns presentation only. // Plain -S keeps that selection but stops before public output, assembler, // and link actions just like the explicit assembly-only form. let defaultcaller: str = strings.concat(root, "/link-default-caller-", tags[si]); let defaultwork: str = strings.concat(root, "/link-default-work-", tags[si]); mkdirall(defaultcaller); mkdirall(defaultwork); let defaultdirav: []str = [driver(stages[si]), "build", "-w", defaultwork, "-I", include, dirlink]; rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); runcommandenvdir(root, strings.concat("testonly-link-default-", tags[si]), defaultdirav, env, defaultcaller, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let defaultdirout: str = strings.concat(defaultcaller, "/directory_test.ww"); assert(out.stdout.len == 0 && out.stderr.len == 0 && same(linkdirbytes, readfile(defaultdirout)) && !os.exists(strings.concat(defaultcaller, "/directory-target")) && same(clinkdirartifacts, artifacttreesnapshot(defaultwork)) && readfile(ctrace).len != 0 && readfile(atrace).len != 0 && readfile(ltrace).len != 0); let defaultrun: []str = [defaultdirout]; runcommand(root, strings.concat("testonly-link-default-run-", tags[si]), defaultrun, time.second, &out); expectexit(&out, 5); let plainasmcaller: str = strings.concat(root, "/link-plain-asm-caller-", tags[si]); let plainasmwork: str = strings.concat(root, "/link-plain-asm-work-", tags[si]); mkdirall(plainasmcaller); mkdirall(plainasmwork); let plainasmav: []str = [driver(stages[si]), "build", "-S", "-w", plainasmwork, "-I", include, dirlink]; rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); runcommandenvdir(root, strings.concat("testonly-link-plain-assembly-", tags[si]), plainasmav, env, plainasmcaller, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && !os.exists(strings.concat(plainasmcaller, "/directory_test.ww")) && readfile(ctrace).len != 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); let plainasmsnapshot: str = strings.dup( artifacttreesnapshot(plainasmwork)); assert(same(asmdirsnapshot, plainasmsnapshot)); if (si == 0) { clinkdirplainasm = strings.dup(plainasmsnapshot); } else { assert(same(clinkdirplainasm, plainasmsnapshot)); }; // A failed invalidation leaves the prior command and committed work // byte-for-byte intact. A later successful invalidation and restoration // reuse the same directory identity rather than a raw-source identity. let dirtargetmain: str = strings.concat(dirtarget, "/main.ww"); let dirtargetoriginal: str = strings.concat( "package main;\nimport domain.value;\n", "fn main() i32 = { return value.number(); };\n"); let dirtargetchanged: str = strings.concat( "package main;\nimport domain.value;\n", "fn main() i32 = { return value.number() + 2; };\n"); let linkdirworkbefore: str = strings.dup(treesnapshot(linkdirwork)); rewritefile(dirtargetmain, dirtargetchanged); env[failcindex] = "WW_TESTONLY_FAIL_C=1"; rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); runcommandenv(root, strings.concat("testonly-link-rollback-", tags[si]), linkdirav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && has(out.stderr, "injected directory compiler failure\n") && same(linkdirbytes, readfile(linkdirout)) && same(linkdirworkbefore, treesnapshot(linkdirwork)) && readfile(ctrace).len != 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0 && !os.exists(strings.concat(linkdirout, ".new")) && !directoryhasnew(linkdirwork) && !directoryhasfragment(linkdirwork, ".wwtxn.")); if (si == 0) { compilerfaildiag = strings.dup(out.stderr); } else { assert(same(compilerfaildiag, out.stderr)); }; env[failcindex] = "WW_TESTONLY_FAIL_C="; env[linkmodeindex] = "WW_TESTONLY_LINK_MODE=signal"; rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); runcommandenv(root, strings.concat("testonly-link-interrupted-", tags[si]), linkdirav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && out.stderr.len != 0 && has(out.stderr, "ww: w6l failed\n") && same(linkdirbytes, readfile(linkdirout)) && same(linkdirworkbefore, treesnapshot(linkdirwork)) && readfile(ctrace).len != 0 && readfile(atrace).len != 0 && readfile(ltrace).len != 0 && !os.exists(strings.concat(linkdirout, ".new")) && !directoryhasnew(linkdirwork) && !directoryhasfragment(linkdirwork, ".wwtxn.")); if (si == 0) { linkersignaldiag = strings.dup(out.stderr); } else { assert(same(linkersignaldiag, out.stderr)); }; env[linkmodeindex] = "WW_TESTONLY_LINK_MODE="; rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); runcommandenv(root, strings.concat("testonly-link-invalidated-", tags[si]), linkdirav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let changedbytes: str = strings.dup(readfile(linkdirout)); let changedartifacts: str = strings.dup( artifacttreesnapshot(linkdirwork)); assert(out.stdout.len == 0 && out.stderr.len == 0 && !same(linkdirbytes, changedbytes) && readfile(ctrace).len != 0 && readfile(atrace).len != 0 && readfile(ltrace).len != 0); if (si == 0) { clinkdirchanged = strings.dup(changedbytes); clinkdirchangedartifacts = strings.dup(changedartifacts); } else { assert(same(clinkdirchanged, changedbytes) && same(clinkdirchangedartifacts, changedartifacts)); }; runcommand(root, strings.concat("testonly-link-invalidated-run-", tags[si]), linkdirrun, time.second, &out); expectexit(&out, 7); rewritefile(dirtargetmain, dirtargetoriginal); rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); runcommandenv(root, strings.concat("testonly-link-restored-", tags[si]), linkdirav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && same(linkdirbytes, readfile(linkdirout)) && same(clinkdirartifacts, artifacttreesnapshot(linkdirwork)) && readfile(ctrace).len != 0 && readfile(atrace).len != 0 && readfile(ltrace).len != 0 && !directoryhasnew(linkdirwork) && !directoryhasfragment(linkdirwork, ".wwtxn.")); 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, test -c, and test -S already route this symlink as a directory. // Temporarily make its otherwise build-excluded test source valid and pin // exact stage parity without changing the negative production-build rows. rewritefile(dirtargettest, dirtargettestselected); rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); let directorytestwork: str = strings.concat(root, "/directory-test-work-", tags[si]); mkdirall(directorytestwork); let directorytestav: []str = [driver(stages[si]), "test", "-w", directorytestwork, "-I", include, dirlink]; runcommandenv(root, strings.concat("testonly-directory-test-", tags[si]), directorytestav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0 && has(out.stdout, "directory_selected ... ok\n") && readfile(ctrace).len != 0 && readfile(atrace).len != 0 && readfile(ltrace).len != 0); let directorytestrunstate: str = strings.dup( artifacttreesnapshot(directorytestwork)); if (si == 0) { directorytestrunout = strings.dup(out.stdout); directorytestrunerr = strings.dup(out.stderr); directorytestrunartifacts = strings.dup(directorytestrunstate); } else { assert(same(directorytestrunout, out.stdout) && same(directorytestrunerr, out.stderr) && same(directorytestrunartifacts, directorytestrunstate)); }; let directorycompiled: str = strings.concat(root, "/directory-test-compiled-", tags[si]); let directorycompilework: str = strings.concat(root, "/directory-test-compile-work-", tags[si]); mkdirall(directorycompilework); let directorycompileav: []str = [driver(stages[si]), "test", "-c", "-w", directorycompilework, "-I", include, "-o", directorycompiled, dirlink]; runcommandenv(root, strings.concat("testonly-directory-test-compile-", tags[si]), directorycompileav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(directorycompiled)); let directorycompiledbytes: str = strings.dup(readfile(directorycompiled)); let directorycompiledstate: str = strings.dup( artifacttreesnapshot(directorycompilework)); if (si == 0) { directorytestbin = strings.dup(directorycompiledbytes); directorytestcompileartifacts = strings.dup(directorycompiledstate); } else { assert(same(directorytestbin, directorycompiledbytes) && same(directorytestcompileartifacts, directorycompiledstate)); }; let directorytestasmwork: str = strings.concat(root, "/directory-test-assembly-work-", tags[si]); let directorytestasmout: str = strings.concat(root, "/directory-test-assembly-output-", tags[si]); mkdirall(directorytestasmwork); let directorytestasmav: []str = [driver(stages[si]), "test", "-S", "-w", directorytestasmwork, "-I", include, "-o", directorytestasmout, dirlink]; rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); runcommandenv(root, strings.concat("testonly-directory-test-assembly-", tags[si]), directorytestasmav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 2); assert(out.stdout.len == 0 && same(out.stderr, "ww test: -S needs a single test file\n") && !os.exists(directorytestasmout) && directoryisempty(directorytestasmwork) && readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); if (si == 0) { directorytestasm = strings.dup(out.stderr); } else { assert(same(directorytestasm, out.stderr)); }; // Run's named-source front rejects the requested _test.ww spelling // before directory loading, tool production, or runtime execution. let directoryrunav: []str = [driver(stages[si]), "run", "-I", include, dirlink]; runcommandenv(root, strings.concat("testonly-directory-run-", tags[si]), directoryrunav, env, (60i64 * (time.second: i64)): time.duration, &out); let directoryrundiag: str = strings.concat( "ww: cannot run *_test.ww files (", dirlink, ")\n"); expectexit(&out, 1); assert(out.stdout.len == 0 && same(out.stderr, directoryrundiag) && readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); if (si == 0) { directoryrunout = strings.dup(out.stdout); directoryrunerr = strings.dup(out.stderr); } else { assert(same(directoryrunout, out.stdout) && same(directoryrunerr, out.stderr)); }; rewritefile(dirtargettest, dirtargettestexcluded); // Raw-source test and run retain their separate existing semantics, while // a visible non-test raw build and multi-operand error stay out of 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])); // Simultaneous selected-directory requests use isolated work and output // transactions while retaining exact cross-stage product and artifact bytes. let ti: i32 = 0; for (ti < stages.len) { rewritefile(tracecs[ti], ""); rewritefile(traceas[ti], ""); rewritefile(tracels[ti], ""); ti += 1; }; let pdcwork: str = strings.concat(root, "/parallel-directory-c-work"); let pdwwork: str = strings.concat(root, "/parallel-directory-ww-work"); let pdcout: str = strings.concat(root, "/parallel-directory-c-output"); let pdwout: str = strings.concat(root, "/parallel-directory-ww-output"); mkdirall(pdcwork); mkdirall(pdwwork); let pdcav: []str = [driver("ww"), "build", "-w", pdcwork, "-I", include, "-o", pdcout, dirlink]; let pdwav: []str = [driver("ww_ww"), "build", "-w", pdwwork, "-I", include, "-o", pdwout, dirlink]; let pdc: exec.command; pdc.path = pdcav[0]; pdc.argv = pdcav; pdc.env = environments[0]; pdc.dir = repo(); pdc.stdoutpath = strings.concat(root, "/parallel-directory-c.stdout"); pdc.stderrpath = strings.concat(root, "/parallel-directory-c.stderr"); pdc.deadline = time.add(time.now(time.clock.monotonic), (60i64 * (time.second: i64)): time.duration); pdc.grace = (100i64 * (time.millisecond: i64)): time.duration; let pdw: exec.command; pdw.path = pdwav[0]; pdw.argv = pdwav; pdw.env = environments[1]; pdw.dir = repo(); pdw.stdoutpath = strings.concat(root, "/parallel-directory-ww.stdout"); pdw.stderrpath = strings.concat(root, "/parallel-directory-ww.stderr"); pdw.deadline = time.add(time.now(time.clock.monotonic), (60i64 * (time.second: i64)): time.duration); pdw.grace = (100i64 * (time.millisecond: i64)): time.duration; let pdcp: exec.process; let pdwp: exec.process; exec.start(&pdcp, &pdc); exec.start(&pdwp, &pdw); let pdcdone: bool = false; let pdwdone: bool = false; for (!pdcdone || !pdwdone) { if (!pdcdone) { pdcdone = exec.poll(&pdcp); }; if (!pdwdone) { pdwdone = exec.poll(&pdwp); }; if (!pdcdone || !pdwdone) { time.sleep(time.millisecond, time.clock.monotonic); }; }; assert(pdcp.result.errno == 0 && pdcp.result.cleanuperrno == 0 && pdcp.result.termination == exec.termination.EXIT && pdcp.result.code == 0 && pdwp.result.errno == 0 && pdwp.result.cleanuperrno == 0 && pdwp.result.termination == exec.termination.EXIT && pdwp.result.code == 0); assert(readfile(pdc.stdoutpath).len == 0 && readfile(pdc.stderrpath).len == 0 && readfile(pdw.stdoutpath).len == 0 && readfile(pdw.stderrpath).len == 0 && same(clinkdirbin, readfile(pdcout)) && same(clinkdirbin, readfile(pdwout)) && same(clinkdirartifacts, artifacttreesnapshot(pdcwork)) && same(clinkdirartifacts, artifacttreesnapshot(pdwwork)) && readfile(tracecs[0]).len != 0 && readfile(traceas[0]).len != 0 && readfile(tracels[0]).len != 0 && readfile(tracecs[1]).len != 0 && readfile(traceas[1]).len != 0 && readfile(tracels[1]).len != 0 && !directoryhasnew(pdcwork) && !directoryhasnew(pdwwork) && !directoryhasfragment(pdcwork, ".wwtxn.") && !directoryhasfragment(pdwwork, ".wwtxn.")); // Two simultaneous omitted requests have no work/output collision and no // producer invocation in either stage. ti = 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); }; fn expectrundirectoryrejection(root: str, name: str, av: []str, env: []str, pidrecord: str, ctrace: str, atrace: str, ltrace: str, want: str) void = { rewritefile(pidrecord, ""); rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); let out: commandout; runcommandenv(root, name, av, env, (60i64 * (time.second: i64)): time.duration, &out); let ownedrun: str = readfile(pidrecord); let ownedmarker: str = strings.concat(ownedrun, "/observer-marker"); let ownedvalid: bool = ownedrun.len > 12 && strings.hasprefix(ownedrun, "/tmp/ww_run_"); let observed: bool = out.termination == exec.termination.EXIT && out.code == 1 && out.stdout.len == 0 && same(out.stderr, want) && ownedvalid && os.exists(ownedrun) && same(readfile(ownedmarker), "observer-owned\n") && readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0; let markerclean: i32 = -1; let dirclean: i32 = -1; if (ownedvalid) { markerclean = os.remove(ownedmarker); dirclean = os.rmdir(ownedrun); }; assert(observed && markerclean == 0 && dirclean == 0 && !os.exists(ownedrun)); }; // Run's named-source spelling owns the directory rejection. The physical // directory remains only loader metadata for the distinct build and test // fronts, and rejected source contents cannot create package or import state. @test fn run_dot_ww_directories_are_named_sources() void = { let root: str = fresh(); let source: str = strings.concat(root, "/source"); let include: str = strings.concat(root, "/include"); let dependency: str = strings.concat(include, "/domain/value"); let logical: str = strings.concat(include, "/domain/logical"); let ordinary: str = strings.concat(source, "/ordinary.ww"); let ordinarylink: str = strings.concat(source, "/requested.ww"); let testdirectory: str = strings.concat(source, "/direct_test.ww"); let testlink: str = strings.concat(source, "/requested_test.ww"); let hidden: str = strings.concat(source, "/.hidden.ww"); let private: str = strings.concat(source, "/_hidden.ww"); let malformed: str = strings.concat(source, "/malformed.ww"); let missingimport: str = strings.concat(source, "/missing-import.ww"); let nonsuffix: str = strings.concat(source, "/directory-control"); let nonsuffixlink: str = strings.concat(source, "/directory-link"); let regular: str = strings.concat(source, "/regular.ww"); let rawtest: str = strings.concat(source, "/raw_control_test.ww"); let prior: str = strings.concat(root, "/prior-public-output"); mkdirall(dependency); mkdirall(logical); mkdirall(ordinary); mkdirall(testdirectory); mkdirall(hidden); mkdirall(private); mkdirall(malformed); mkdirall(missingimport); mkdirall(nonsuffix); writefile(strings.concat(dependency, "/value.ww"), strings.concat( "package value;\n", "export fn number() i32 = { return 37; };\n")); writefile(strings.concat(ordinary, "/main.ww"), strings.concat( "package main;\nimport domain.value;\n", "fn main() i32 = { return value.number(); };\n")); writefile(strings.concat(testdirectory, "/main.ww"), "package main;\nfn main() i32 = { return 47; };\n"); writefile(strings.concat(hidden, "/main.ww"), "package main;\nfn main() i32 = { return 48; };\n"); writefile(strings.concat(private, "/main.ww"), "package main;\nfn main() i32 = { return 49; };\n"); writefile(strings.concat(malformed, "/main.ww"), "this directory has no package clause\n"); writefile(strings.concat(missingimport, "/main.ww"), strings.concat( "package main;\nimport absent.before.run.classification;\n", "fn main() i32 = { return 50; };\n")); writefile(strings.concat(nonsuffix, "/main.ww"), "package main;\nfn main() i32 = { return 43; };\n"); writefile(regular, "package main;\nfn main() i32 = { return 39; };\n"); writefile(strings.concat(logical, "/main.ww"), "package main;\nfn main() i32 = { return 41; };\n"); writefile(rawtest, strings.concat( "package runroute;\n", "@test fn raw_control() void = { assert(true); };\n")); writefile(prior, "prior-public-bytes\n"); assert(os.symlink(ordinary, ordinarylink) == 0); assert(os.symlink(testdirectory, testlink) == 0); assert(os.symlink(nonsuffix, nonsuffixlink) == 0); let driverwrapper: str = strings.concat(root, "/driver-wrapper.sh"); let compilerwrapper: str = strings.concat(root, "/compiler-wrapper.sh"); let assemblerwrapper: str = strings.concat(root, "/assembler-wrapper.sh"); let linkerwrapper: str = strings.concat(root, "/linker-wrapper.sh"); writeexecutable(driverwrapper, strings.concat( "#!/bin/sh\nowned=/tmp/ww_run_$$\n", "printf '%s' \"$owned\" > \"$WW_RUN_PID_RECORD\"\n", "if test \"$WW_RUN_PREOCCUPY\" = 1; then\n", " mkdir \"$owned\" || exit 124\n", " printf 'observer-owned\\n' > \"$owned/observer-marker\"\n", "fi\n", "exec \"$WW_RUN_REAL_DRIVER\" \"$@\"\n")); writeexecutable(compilerwrapper, strings.concat( "#!/bin/sh\nprintf 'compile\\n' >> \"$WW_RUN_CTRACE\"\n", "exec \"$WW_RUN_REAL_C\" \"$@\"\n")); writeexecutable(assemblerwrapper, strings.concat( "#!/bin/sh\nprintf 'assemble\\n' >> \"$WW_RUN_ATRACE\"\n", "exec \"$WW_RUN_REAL_A\" \"$@\"\n")); writeexecutable(linkerwrapper, strings.concat( "#!/bin/sh\nprintf 'link\\n' >> \"$WW_RUN_LTRACE\"\n", "exec \"$WW_RUN_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 environments: [][]str = alloc([], 2u64)!; let rejectenvironments: [][]str = alloc([], 2u64)!; let pidrecords: []str = alloc([], 2u64)!; let ctraces: []str = alloc([], 2u64)!; let atraces: []str = alloc([], 2u64)!; let ltraces: []str = alloc([], 2u64)!; let inherited: []str = os.getenvs(); let si: i32 = 0; for (si < stages.len) { let pidrecord: str = strings.concat(root, "/driver-pid-", tags[si]); let ctrace: str = strings.concat(root, "/compiler-trace-", tags[si]); let atrace: str = strings.concat(root, "/assembler-trace-", tags[si]); let ltrace: str = strings.concat(root, "/linker-trace-", tags[si]); writefile(pidrecord, ""); writefile(ctrace, ""); writefile(atrace, ""); writefile(ltrace, ""); append(pidrecords, pidrecord); append(ctraces, ctrace); append(atraces, atrace); append(ltraces, ltrace); let env: []str = alloc([], (inherited.len + 11): u64)!; let ei: i32 = 0; for (ei < inherited.len) { if (!strings.hasprefix(inherited[ei], "WW_W6C=") && !strings.hasprefix(inherited[ei], "WW_W6A=") && !strings.hasprefix(inherited[ei], "WW_W6L=") && !strings.hasprefix(inherited[ei], "WW_RUN_")) { append(env, inherited[ei]); }; ei += 1; }; append(env, strings.concat("WW_RUN_REAL_DRIVER=", driver(stages[si]))); append(env, strings.concat("WW_RUN_PID_RECORD=", pidrecord)); 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_RUN_CTRACE=", ctrace)); append(env, strings.concat("WW_RUN_ATRACE=", atrace)); append(env, strings.concat("WW_RUN_LTRACE=", ltrace)); append(env, strings.concat("WW_RUN_REAL_C=", driver(compilers[si]))); append(env, strings.concat("WW_RUN_REAL_A=", driver(assemblers[si]))); append(env, strings.concat("WW_RUN_REAL_L=", driver(linkers[si]))); append(environments, env); let rejectenv: []str = alloc([], (env.len + 1): u64)!; let ri: i32 = 0; for (ri < env.len) { append(rejectenv, env[ri]); ri += 1; }; append(rejectenv, "WW_RUN_PREOCCUPY=1"); append(rejectenvironments, rejectenv); si += 1; }; let selected: []str = [ordinary, ordinarylink, testdirectory, testlink, hidden, private, malformed, missingimport]; let selectedlabels: []str = ["direct", "symlink", "direct-test", "symlink-test", "dot-hidden", "underscore-hidden", "malformed", "missing-import"]; let selectedtest: []bool = [false, false, true, true, false, false, false, false]; let sourcebefore: str = strings.dup(treesnapshot(source)); si = 0; for (si < stages.len) { let ri: i32 = 0; for (ri < selected.len) { let want: str; if (selectedtest[ri]) { want = strings.concat("ww: cannot run *_test.ww files (", selected[ri], ")\n"); } else { want = strings.concat(selected[ri], " is a directory, should be a WW file\n"); }; let av: []str = [driverwrapper, "run", "-I", include, selected[ri]]; expectrundirectoryrejection(root, strings.concat("run-reject-", tags[si], "-", selectedlabels[ri]), av, rejectenvironments[si], pidrecords[si], ctraces[si], atraces[si], ltraces[si], want); ri += 1; }; let boundaryav: []str = [driverwrapper, "run", "-I", include, ordinarylink, "-I", "program-argument"]; let boundarywant: str = strings.concat(ordinarylink, " is a directory, should be a WW file\n"); expectrundirectoryrejection(root, strings.concat("run-boundary-", tags[si]), boundaryav, rejectenvironments[si], pidrecords[si], ctraces[si], atraces[si], ltraces[si], boundarywant); si += 1; }; assert(same(sourcebefore, treesnapshot(source)) && same(readfile(prior), "prior-public-bytes\n") && !directoryhasfragment(root, ".sepwork") && !directoryhasfragment(root, ".wwtxn.") && !directoryhasfragment(root, ".capture") && !directoryhasfragment(root, ".result") && !directoryhasfragment(root, ".request")); // Concurrent preflight rejections leave independently preoccupied exact // run paths and diagnostics untouched. let pi: i32 = 0; for (pi < stages.len) { rewritefile(pidrecords[pi], ""); rewritefile(ctraces[pi], ""); rewritefile(atraces[pi], ""); rewritefile(ltraces[pi], ""); pi += 1; }; let cav: []str = [driverwrapper, "run", ordinary]; let wav: []str = [driverwrapper, "run", ordinarylink]; let cc: exec.command; cc.path = cav[0]; cc.argv = cav; cc.env = rejectenvironments[0]; cc.dir = repo(); cc.stdoutpath = strings.concat(root, "/parallel-c.stdout"); cc.stderrpath = strings.concat(root, "/parallel-c.stderr"); cc.deadline = time.add(time.now(time.clock.monotonic), (60i64 * (time.second: i64)): time.duration); cc.grace = (100i64 * (time.millisecond: i64)): time.duration; let wc: exec.command; wc.path = wav[0]; wc.argv = wav; wc.env = rejectenvironments[1]; wc.dir = repo(); wc.stdoutpath = strings.concat(root, "/parallel-ww.stdout"); wc.stderrpath = strings.concat(root, "/parallel-ww.stderr"); wc.deadline = time.add(time.now(time.clock.monotonic), (60i64 * (time.second: i64)): time.duration); wc.grace = (100i64 * (time.millisecond: i64)): time.duration; let cp: exec.process; let wp: exec.process; exec.start(&cp, &cc); exec.start(&wp, &wc); let cdone: bool = false; let wdone: bool = false; for (!cdone || !wdone) { if (!cdone) { cdone = exec.poll(&cp); }; if (!wdone) { wdone = exec.poll(&wp); }; if (!cdone || !wdone) { time.sleep(time.millisecond, time.clock.monotonic); }; }; let cownedrun: str = readfile(pidrecords[0]); let wownedrun: str = readfile(pidrecords[1]); let cownedmarker: str = strings.concat(cownedrun, "/observer-marker"); let wownedmarker: str = strings.concat(wownedrun, "/observer-marker"); let cownedvalid: bool = cownedrun.len > 12 && strings.hasprefix(cownedrun, "/tmp/ww_run_"); let wownedvalid: bool = wownedrun.len > 12 && strings.hasprefix(wownedrun, "/tmp/ww_run_"); let parallelobserved: bool = cp.result.errno == 0 && cp.result.cleanuperrno == 0 && cp.result.termination == exec.termination.EXIT && cp.result.code == 1 && wp.result.errno == 0 && wp.result.cleanuperrno == 0 && wp.result.termination == exec.termination.EXIT && wp.result.code == 1 && readfile(cc.stdoutpath).len == 0 && readfile(wc.stdoutpath).len == 0 && same(readfile(cc.stderrpath), strings.concat(ordinary, " is a directory, should be a WW file\n")) && same(readfile(wc.stderrpath), strings.concat(ordinarylink, " is a directory, should be a WW file\n")) && cownedvalid && wownedvalid && os.exists(cownedrun) && os.exists(wownedrun) && same(readfile(cownedmarker), "observer-owned\n") && same(readfile(wownedmarker), "observer-owned\n") && readfile(ctraces[0]).len == 0 && readfile(atraces[0]).len == 0 && readfile(ltraces[0]).len == 0 && readfile(ctraces[1]).len == 0 && readfile(atraces[1]).len == 0 && readfile(ltraces[1]).len == 0 && same(sourcebefore, treesnapshot(source)) && same(readfile(prior), "prior-public-bytes\n"); let cmarkerclean: i32 = -1; let cdirclean: i32 = -1; let wmarkerclean: i32 = -1; let wdirclean: i32 = -1; if (cownedvalid) { cmarkerclean = os.remove(cownedmarker); cdirclean = os.rmdir(cownedrun); }; if (wownedvalid) { wmarkerclean = os.remove(wownedmarker); wdirclean = os.rmdir(wownedrun); }; assert(parallelobserved && cmarkerclean == 0 && cdirclean == 0 && wmarkerclean == 0 && wdirclean == 0 && !os.exists(cownedrun) && !os.exists(wownedrun)); let cbuildbytes: str = ""; let cbuildartifacts: str = ""; let cteststdout: str = ""; let ctestbinary: str = ""; si = 0; for (si < stages.len) { let out: commandout; let trailing: str = strings.concat(ordinary, "/"); let trailingav: []str = [driverwrapper, "run", "-I", include, trailing]; runcommandenv(root, strings.concat("run-trailing-", tags[si]), trailingav, environments[si], (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 37); assert(out.stdout.len == 0 && out.stderr.len == 0 && !os.exists(readfile(pidrecords[si]))); let nonwwav: []str = [driverwrapper, "run", nonsuffixlink]; runcommandenv(root, strings.concat("run-non-ww-", tags[si]), nonwwav, environments[si], (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 43); assert(out.stdout.len == 0 && out.stderr.len == 0 && !os.exists(readfile(pidrecords[si]))); let regularav: []str = [driverwrapper, "run", regular]; runcommandenv(root, strings.concat("run-regular-", tags[si]), regularav, environments[si], (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 39); assert(out.stdout.len == 0 && out.stderr.len == 0 && !os.exists(readfile(pidrecords[si]))); let logicalav: []str = [driverwrapper, "run", "-I", include, "domain.logical"]; runcommandenv(root, strings.concat("run-logical-", tags[si]), logicalav, environments[si], (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 41); assert(out.stdout.len == 0 && out.stderr.len == 0 && !os.exists(readfile(pidrecords[si]))); let buildwork: str = strings.concat(root, "/build-work-", tags[si]); let buildout: str = strings.concat(root, "/build-output-", tags[si]); mkdirall(buildwork); let buildav: []str = [driverwrapper, "build", "-w", buildwork, "-I", include, "-o", buildout, ordinarylink]; runcommandenv(root, strings.concat("build-same-spelling-", tags[si]), buildav, environments[si], (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(buildout)); let buildbytes: str = strings.dup(readfile(buildout)); let buildartifacts: str = strings.dup(artifacttreesnapshot(buildwork)); if (si == 0) { cbuildbytes = strings.dup(buildbytes); cbuildartifacts = strings.dup(buildartifacts); } else { assert(same(cbuildbytes, buildbytes) && same(cbuildartifacts, buildartifacts)); }; let builtav: []str = [buildout]; runcommand(root, strings.concat("built-control-", tags[si]), builtav, time.second, &out); expectexit(&out, 37); let testwork: str = strings.concat(root, "/test-work-", tags[si]); mkdirall(testwork); let testav: []str = [driverwrapper, "test", "-w", testwork, rawtest]; runcommandenv(root, strings.concat("raw-test-control-", tags[si]), testav, environments[si], (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0 && has(out.stdout, "raw_control ... ok\n")); if (si == 0) { cteststdout = strings.dup(out.stdout); } else { assert(same(cteststdout, out.stdout)); }; let testbinary: str = strings.concat(root, "/test-binary-", tags[si]); let compileav: []str = [driverwrapper, "test", "-c", "-o", testbinary, rawtest]; runcommandenv(root, strings.concat("raw-test-compile-", tags[si]), compileav, environments[si], (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(testbinary)); if (si == 0) { ctestbinary = strings.dup(readfile(testbinary)); } else { assert(same(ctestbinary, readfile(testbinary))); }; let assemblywork: str = strings.concat(root, "/assembly-work-", tags[si]); let assemblyout: str = strings.concat(root, "/assembly-output-", tags[si]); mkdirall(assemblywork); rewritefile(ctraces[si], ""); rewritefile(atraces[si], ""); rewritefile(ltraces[si], ""); let assemblyav: []str = [driverwrapper, "test", "-S", "-w", assemblywork, "-o", assemblyout, ordinarylink]; runcommandenv(root, strings.concat("directory-test-assembly-", tags[si]), assemblyav, environments[si], (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 2); assert(out.stdout.len == 0 && same(out.stderr, "ww test: -S needs a single test file\n") && !os.exists(assemblyout) && directoryisempty(assemblywork) && readfile(ctraces[si]).len == 0 && readfile(atraces[si]).len == 0 && readfile(ltraces[si]).len == 0); si += 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. // This observer keeps that source rule separate from dotted import identity. @test fn imports_precede_other_top_level_declarations() void = { let root: str = fresh(); let tree: str = strings.concat(root, "/tree"); let base: str = strings.concat(tree, "/domain/base"); let dep: str = strings.concat(tree, "/domain/dep"); let app: str = strings.concat(tree, "/domain/app"); let missinglate: str = strings.concat(tree, "/domain/missinglate"); let prodtest: str = strings.concat(tree, "/domain/prodtest"); let internaltest: str = strings.concat(tree, "/domain/internaltest"); let externaltest: str = strings.concat(tree, "/domain/externaltest"); let testonly: str = strings.concat(tree, "/domain/testonly"); let validtest: str = strings.concat(tree, "/domain/validtest"); mkdirall(base); mkdirall(dep); mkdirall(app); mkdirall(missinglate); mkdirall(prodtest); mkdirall(internaltest); mkdirall(externaltest); mkdirall(testonly); mkdirall(validtest); writefile(strings.concat(base, "/base.ww"), "package base;\nexport fn value() i32 = { return 40; };\n"); let depfile: str = strings.concat(dep, "/dep.ww"); let depvalid: str = strings.concat( "package renamed;\nimport domain.base;\n", "fn delta() i32 = { return 1; };\n", "export fn value() i32 = { return base.value() + delta(); };\n"); let deplate: str = strings.concat( "package renamed;\nfn delta() i32 = { return 1; };\n", "import domain.base;\n", "export fn value() i32 = { return base.value() + delta(); };\n"); writefile(depfile, depvalid); let appfile: str = strings.concat(app, "/main.ww"); let appvalid: str = strings.concat( "package main;\nimport domain.dep;\n", "fn local() i32 = { return 1; };\n", "fn main() i32 = { return renamed.value() + local(); };\n"); let applate: str = strings.concat( "package main;\nfn local() i32 = { return 1; };\n", "import domain.dep;\n", "fn main() i32 = { return renamed.value() + local(); };\n"); writefile(appfile, appvalid); // Platform exclusion precedes package/import parsing. Neither the wrong // declared name nor this late missing import may affect linux/amd64. writefile(strings.concat(app, "/ignored_windows.ww"), strings.concat( "package wrong;\nfn ignored() void = {};\n", "import missing.platform;\n")); writefile(strings.concat(missinglate, "/main.ww"), strings.concat( "package main;\nfn before() i32 = { return 0; };\n", "import missing.pkg;\nfn main() i32 = { return before(); };\n")); // Production, same-package test, external-test, and test-only sources all // pass through the same per-source ordering rule before product actions. writefile(strings.concat(prodtest, "/pkg.ww"), strings.concat( "package prodtest;\nfn local() i32 = { return 1; };\n", "import domain.dep;\n", "export fn value() i32 = { return local() + renamed.value(); };\n")); writefile(strings.concat(prodtest, "/pkg_test.ww"), "package prodtest;\n@test fn must_not_run() void = { abort(\"ran\"); };\n"); writefile(strings.concat(internaltest, "/pkg.ww"), "package internaltest;\nfn local() i32 = { return 1; };\n"); writefile(strings.concat(internaltest, "/pkg_test.ww"), strings.concat( "package internaltest;\nfn testlocal() i32 = { return 1; };\n", "import domain.dep;\n", "@test fn must_not_run() void = { abort(\"ran\"); };\n")); writefile(strings.concat(externaltest, "/pkg.ww"), "package externaltest;\nexport fn local() i32 = { return 1; };\n"); writefile(strings.concat(externaltest, "/pkg_test.ww"), strings.concat( "package externaltest_test;\nfn testlocal() i32 = { return 1; };\n", "import domain.dep;\n", "@test fn must_not_run() void = { abort(\"ran\"); };\n")); writefile(strings.concat(testonly, "/only_test.ww"), strings.concat( "package testonly;\nfn testlocal() i32 = { return 1; };\n", "import domain.dep;\n", "@test fn must_not_run() void = { abort(\"ran\"); };\n")); writefile(strings.concat(validtest, "/pkg.ww"), strings.concat( "package validtest;\nimport domain.dep;\n", "fn local() i32 = { return renamed.value(); };\n")); writefile(strings.concat(validtest, "/pkg_test.ww"), strings.concat( "package validtest;\nimport domain.base;\n", "@test fn import_first_runs() void = {", " assert(local() + base.value() == 81); };\n")); writefile(strings.concat(validtest, "/ignored_windows_test.ww"), strings.concat("package wrong;\nfn ignored() void = {};\n", "import missing.platform;\n")); let expected: str = "imports must appear before other declarations"; let sections: str = strings.concat(root, "/sections.ww"); writefile(sections, strings.concat( "package sections;\nfn first() void = {};\n", "import one;\nimport two;\n", "fn second() void = {};\nimport three;\n")); let compilers: []str = ["w6c", "w6c_ww"]; let directdiag: str = ""; let out: commandout; let i: i32 = 0; for (i < compilers.len) { let asmout: str = strings.concat(root, "/direct-", compilers[i], ".s"); let av: []str = [driver(compilers[i]), "-o", asmout, sections]; runcommand(root, strings.concat("import-order-direct-", compilers[i]), av, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && !os.exists(asmout)); assert(occurrences(out.stderr, expected) == 2); assert(same(primarydiagnostic(out.stderr), expected)); if (i == 0) { directdiag = strings.dup(out.stderr); } else { assert(same(directdiag, out.stderr)); }; i += 1; }; let compilerwrapper: str = strings.concat(root, "/import-w6c.sh"); let assemblerwrapper: str = strings.concat(root, "/import-w6a.sh"); let linkerwrapper: str = strings.concat(root, "/import-w6l.sh"); writeexecutable(compilerwrapper, strings.concat( "#!/bin/sh\nprintf 'compile\\n' >> \"$WW_IMPORT_CTRACE\"\n", "exec \"$WW_IMPORT_REAL_C\" \"$@\"\n")); writeexecutable(assemblerwrapper, strings.concat( "#!/bin/sh\nprintf 'assemble\\n' >> \"$WW_IMPORT_ATRACE\"\n", "exec \"$WW_IMPORT_REAL_A\" \"$@\"\n")); writeexecutable(linkerwrapper, strings.concat( "#!/bin/sh\nprintf 'link\\n' >> \"$WW_IMPORT_LTRACE\"\n", "exec \"$WW_IMPORT_REAL_L\" \"$@\"\n")); let stages: []str = ["ww", "ww_ww"]; let assemblers: []str = ["w6a", "w6a_ww"]; let linkers: []str = ["w6l", "w6l_ww"]; let tags: []str = ["c", "ww"]; let works: []str = [strings.concat(root, "/work-c"), strings.concat(root, "/work-ww")]; let outputs: []str = [strings.concat(root, "/app-c"), strings.concat(root, "/app-ww")]; let testworks: []str = [strings.concat(root, "/test-work-c"), strings.concat(root, "/test-work-ww")]; let testbins: []str = [strings.concat(root, "/valid-c.test"), strings.concat(root, "/valid-ww.test")]; let baseenv: []str = os.getenvs(); let appdiag: str = ""; let depdiag: str = ""; let missingdiag: str = ""; let validtestout: str = ""; let testdiags: []str = ["", "", "", ""]; let artifactrefs: []str = alloc([], 64u64)!; let testartifactrefs: []str = alloc([], 64u64)!; let appbinref: str = ""; let testbinref: str = ""; let actions: []str = ["domain.base", "domain.dep", "domain.app"]; let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"]; let initsuffixes: []str = [".init.unit.ww", ".init.s", ".init.o"]; let testactions: []str = ["domain.base", "domain.dep", "domain.validtest-internal-test", "domain.validtest-test-main"]; let testtargets: []str = ["domain.prodtest", "domain.internaltest", "domain.externaltest", "domain.testonly"]; i = 0; for (i < stages.len) { mkdirall(works[i]); mkdirall(testworks[i]); let ctrace: str = strings.concat(root, "/compile-", tags[i]); let atrace: str = strings.concat(root, "/assemble-", tags[i]); let ltrace: str = strings.concat(root, "/link-", tags[i]); writefile(ctrace, ""); writefile(atrace, ""); writefile(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_IMPORT_")) { 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_IMPORT_CTRACE=", ctrace)); append(env, strings.concat("WW_IMPORT_ATRACE=", atrace)); append(env, strings.concat("WW_IMPORT_LTRACE=", ltrace)); append(env, strings.concat("WW_IMPORT_REAL_C=", driver(compilers[i]))); append(env, strings.concat("WW_IMPORT_REAL_A=", driver(assemblers[i]))); append(env, strings.concat("WW_IMPORT_REAL_L=", driver(linkers[i]))); let buildav: []str = [driver(stages[i]), "build", "-I", tree, "-w", works[i], "-o", outputs[i], "domain.app"]; runcommandenv(root, strings.concat("import-order-cold-", tags[i]), buildav, env, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(readfile(ctrace).len != 0 && readfile(atrace).len != 0 && readfile(ltrace).len != 0); let built: str = strings.dup(readfile(outputs[i])); if (i == 0) { appbinref = strings.dup(built); } else { assert(same(appbinref, built)); }; let runav: []str = [outputs[i]]; runcommand(root, strings.concat("import-order-run-", tags[i]), runav, time.second, &out); expectexit(&out, 42); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(has(readfile(strings.concat(works[i], "/domain.dep.unit.ww")), "//ww:module-reset domain.dep\npackage renamed;")); // Save and compare every valid semantic output affected by this graph. let before: []str = alloc([], 32u64)!; let refi: i32 = 0; let ai: i32 = 0; for (ai < actions.len) { let si: i32 = 0; for (si < suffixes.len) { let bytes: str = strings.dup(readfile(strings.concat(works[i], "/", actions[ai], suffixes[si]))); append(before, bytes); if (i == 0) { append(artifactrefs, strings.dup(bytes)); } else { assert(same(artifactrefs[refi], bytes)); }; refi += 1; si += 1; }; ai += 1; }; let ii: i32 = 0; for (ii < initsuffixes.len) { let bytes: str = strings.dup(readfile(strings.concat(works[i], "/domain.app", initsuffixes[ii]))); append(before, bytes); if (i == 0) { append(artifactrefs, strings.dup(bytes)); } else { assert(same(artifactrefs[refi], bytes)); }; refi += 1; ii += 1; }; // A selected source becoming late rejects before every producer and // preserves the complete committed generation and public executable. rewritefile(appfile, applate); rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); runcommandenv(root, strings.concat("import-order-app-late-", tags[i]), buildav, env, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && same(primarydiagnostic(out.stderr), expected)); assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0 && same(built, readfile(outputs[i]))); if (i == 0) { appdiag = strings.dup(out.stderr); } else { assert(same(appdiag, out.stderr)); }; refi = 0; ai = 0; for (ai < actions.len) { let si: i32 = 0; for (si < suffixes.len) { assert(same(before[refi], readfile(strings.concat(works[i], "/", actions[ai], suffixes[si])))); refi += 1; si += 1; }; ai += 1; }; ii = 0; for (ii < initsuffixes.len) { assert(same(before[refi], readfile(strings.concat(works[i], "/domain.app", initsuffixes[ii])))); refi += 1; ii += 1; }; assert(!directoryhasnew(works[i]) && !os.exists(strings.concat(outputs[i], ".new")) && !os.exists(strings.concat(outputs[i], ".install")) && !directoryhasfragment(works[i], ".wwtxn.")); // Exact restoration reuses the committed producer bytes. rewritefile(appfile, appvalid); rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); runcommandenv(root, strings.concat("import-order-app-restored-", tags[i]), buildav, env, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && readfile(ctrace).len == 0 && readfile(atrace).len == 0 && same(built, readfile(outputs[i]))); // The same rule applies while loading an imported package; the root's // prior bytes and all graph identities remain unchanged. rewritefile(depfile, deplate); rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); runcommandenv(root, strings.concat("import-order-dep-late-", tags[i]), buildav, env, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && same(primarydiagnostic(out.stderr), expected) && has(out.stderr, "/domain/dep/dep.ww:")); assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0 && same(built, readfile(outputs[i]))); if (i == 0) { depdiag = strings.dup(out.stderr); } else { assert(same(depdiag, out.stderr)); }; rewritefile(depfile, depvalid); rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); runcommandenv(root, strings.concat("import-order-dep-restored-", tags[i]), buildav, env, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && readfile(ctrace).len == 0 && readfile(atrace).len == 0 && same(built, readfile(outputs[i]))); // Syntax ordering precedes import resolution, output creation, and cold // scratch even when the late import target does not exist. let misswork: str = strings.concat(root, "/missing-work-", tags[i]); let missout: str = strings.concat(root, "/missing-output-", tags[i]); mkdirall(misswork); let missav: []str = [driver(stages[i]), "build", "-I", tree, "-w", misswork, "-o", missout, "domain.missinglate"]; rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); runcommandenv(root, strings.concat("import-order-missing-", tags[i]), missav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && same(primarydiagnostic(out.stderr), expected) && !has(out.stderr, "cannot find package")); assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0 && directoryisempty(misswork) && !os.exists(missout) && !os.exists(strings.concat(missout, ".new")) && !os.exists(strings.concat(missout, ".sepwork"))); if (i == 0) { missingdiag = strings.dup(out.stderr); } else { assert(same(missingdiag, out.stderr)); }; let ti: i32 = 0; for (ti < testtargets.len) { let twork: str = strings.concat(root, "/invalid-test-work-", tags[i], "-", boundarypkgname(ti)); let tbin: str = strings.concat(root, "/invalid-test-bin-", tags[i], "-", boundarypkgname(ti)); mkdirall(twork); let tav: []str = [driver(stages[i]), "test", "-I", tree, "-w", twork, "-o", tbin, testtargets[ti]]; rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); runcommandenv(root, strings.concat("import-order-test-", tags[i], "-", boundarypkgname(ti)), tav, env, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stdout, "FAIL\n") && same(primarydiagnostic(out.stderr), expected)); assert(!has(out.stdout, "must_not_run") && !has(out.stdout, " discovered,") && !has(out.stderr, "ok ")); assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0 && directoryisempty(twork)); assert(!os.exists(tbin) && !os.exists(strings.concat(tbin, ".new")) && !os.exists(strings.concat(tbin, ".install")) && !os.exists(strings.concat(tbin, ".sepwork"))); if (i == 0) { testdiags[ti] = strings.dup(out.stderr); } else { assert(same(testdiags[ti], out.stderr)); }; ti += 1; }; // A valid import-first test still compiles to the same retained binary // and runs with the same bytes on both stages. Its excluded wrong-target // late import remains completely absent from loading. let validav: []str = [driver(stages[i]), "test", "-c", "-I", tree, "-w", testworks[i], "-o", testbins[i], "domain.validtest"]; runcommandenv(root, strings.concat("import-order-valid-test-", tags[i]), validav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); let testbytes: str = strings.dup(readfile(testbins[i])); if (i == 0) { testbinref = strings.dup(testbytes); } else { assert(same(testbinref, testbytes)); }; let testrunav: []str = [testbins[i]]; runcommand(root, strings.concat("import-order-valid-test-run-", tags[i]), testrunav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0 && has(out.stdout, "import_first_runs ... ok\n")); if (i == 0) { validtestout = strings.dup(out.stdout); } else { assert(same(validtestout, out.stdout)); }; refi = 0; ai = 0; for (ai < testactions.len) { let si: i32 = 0; for (si < suffixes.len) { let bytes: str = readfile(strings.concat(testworks[i], "/", testactions[ai], suffixes[si])); if (i == 0) { append(testartifactrefs, strings.dup(bytes)); } else { assert(same(testartifactrefs[refi], bytes)); }; refi += 1; si += 1; }; ai += 1; }; ii = 0; for (ii < initsuffixes.len) { let bytes: str = readfile(strings.concat(testworks[i], "/domain.validtest-test-main", initsuffixes[ii])); if (i == 0) { append(testartifactrefs, strings.dup(bytes)); } else { assert(same(testartifactrefs[refi], bytes)); }; refi += 1; ii += 1; }; assert(!directoryhasnew(works[i]) && !directoryhasnew(testworks[i]) && !directoryhasfragment(works[i], ".wwtxn.") && !directoryhasfragment(testworks[i], ".wwtxn.")); i += 1; }; // Parser state is request-local. A valid Cstage build and an invalid // WWstage build can overlap without changing either result or residue. let pcwork: str = strings.concat(root, "/parallel-c-work"); let pwwork: str = strings.concat(root, "/parallel-ww-work"); let pcout: str = strings.concat(root, "/parallel-c-output"); let pwout: str = strings.concat(root, "/parallel-ww-output"); mkdirall(pcwork); mkdirall(pwwork); let pcav: []str = [driver("ww"), "build", "-I", tree, "-w", pcwork, "-o", pcout, "domain.app"]; let pwav: []str = [driver("ww_ww"), "build", "-I", tree, "-w", pwwork, "-o", pwout, "domain.missinglate"]; let pc: exec.command; pc.path = pcav[0]; pc.argv = pcav; pc.env = os.getenvs(); 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), (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 = 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), (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 && pcp.result.termination == exec.termination.EXIT && pcp.result.code == 0); assert(pwp.result.errno == 0 && pwp.result.cleanuperrno == 0 && pwp.result.termination == exec.termination.EXIT && pwp.result.code == 1); assert(readfile(pc.stdoutpath).len == 0 && readfile(pc.stderrpath).len == 0); assert(readfile(pw.stdoutpath).len == 0 && same(primarydiagnostic(readfile(pw.stderrpath)), expected)); assert(same(appbinref, readfile(pcout)) && !os.exists(pwout) && directoryisempty(pwwork) && !directoryhasnew(pcwork) && !directoryhasfragment(pcwork, ".wwtxn.")); let pcrun: []str = [pcout]; runcommand(root, "import-order-parallel-run", pcrun, time.second, &out); expectexit(&out, 42); assert(!directoryhasfragment(root, ".wwtxn.") && !directoryhasfragment(root, ".install") && !directoryhasnew(root)); clean(root); }; @test fn list_mode_with_no_matches_emits_no_sentinel() void = { let root: str = fresh(); let alpha: str = strings.concat(root, "/alpha"); let beta: str = strings.concat(root, "/beta"); mkdirall(alpha); mkdirall(beta); writefile(strings.concat(alpha, "/alpha.ww"), strings.concat( "package list_alpha;\nimport os;\n", "fn init() void = { os.write(os.STDOUT_FILENO,", " \"alpha-init\\n\".ptr, 11u64); };\n")); writefile(strings.concat(alpha, "/alpha_internal_test.ww"), strings.concat( "package list_alpha;\n", "@test fn first() void = { abort(\"selected first\"); };\n", "@test fn second() void = { abort(\"selected second\"); };\n")); writefile(strings.concat(alpha, "/alpha_external_test.ww"), strings.concat( "package list_alpha_test;\n", "@test fn external() void = { abort(\"selected external\"); };\n")); writefile(strings.concat(beta, "/beta_test.ww"), strings.concat( "package list_beta_test;\nimport os;\n", "fn init() void = { os.write(os.STDOUT_FILENO,", " \"beta-init\\n\".ptr, 10u64); };\n", "@test fn only() void = { abort(\"selected only\"); };\n")); let stages: []str = ["ww", "ww_ww"]; let tags: []str = ["c", "ww"]; let works: []str = [strings.concat(root, "/work-c"), strings.concat(root, "/work-ww")]; let coldout: []str = ["", ""]; let colderror: []str = ["", ""]; let positiveout: []str = ["", ""]; let positiveerror: []str = ["", ""]; let retainedout: []str = ["", ""]; let retainederror: []str = ["", ""]; let manualout: []str = ["", ""]; let manualerror: []str = ["", ""]; let retained: []str = [strings.concat(root, "/retained-c.test"), strings.concat(root, "/retained-ww.test")]; let out: commandout; let i: i32 = 0; for (i < stages.len) { let emptyav: []str = [driver(stages[i]), "test", "-j", "2", "-w", works[i], "-list", "-run", "no-such-*", alpha, beta]; runcommand(root, strings.concat("list-empty-cold-", tags[i]), emptyav, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0); assert(occurrences(out.stdout, "alpha-init\n") == 1); assert(occurrences(out.stdout, "beta-init\n") == 1); assert(occurrences(out.stdout, "ok ") == 2); assert(!has(out.stdout, "testing: warning: no tests to run")); assert(!has(out.stdout, " [no tests to run]")); assert(!has(out.stdout, " discovered, ")); assert(!has(out.stdout, "list_alpha.first")); assert(!has(out.stdout, "list_alpha.second")); assert(!has(out.stdout, "list_alpha.external")); assert(!has(out.stdout, "list_beta.only")); coldout[i] = strings.dup(out.stdout); colderror[i] = strings.dup(out.stderr); runcommand(root, strings.concat("list-empty-warm-", tags[i]), emptyav, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(out.stdout, coldout[i])); assert(same(out.stderr, colderror[i])); assert(!directoryhasnew(works[i])); let positiveav: []str = [driver(stages[i]), "test", "-j", "2", "-w", works[i], "-list", alpha, beta]; runcommand(root, strings.concat("list-positive-", tags[i]), positiveav, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0); let first: i32 = pos(out.stdout, "list_alpha.first\n"); let second: i32 = pos(out.stdout, "list_alpha.second\n"); let external: i32 = pos(out.stdout, "list_alpha.external\n"); let only: i32 = pos(out.stdout, "list_beta.only\n"); assert(first >= 0 && first < second && second < external && external < only); assert(occurrences(out.stdout, "list_alpha.first\n") == 1); assert(occurrences(out.stdout, "list_alpha.second\n") == 1); assert(occurrences(out.stdout, "list_alpha.external\n") == 1); assert(occurrences(out.stdout, "list_beta.only\n") == 1); positiveout[i] = strings.dup(out.stdout); positiveerror[i] = strings.dup(out.stderr); let retainav: []str = [driver(stages[i]), "test", "-w", works[i], "-o", retained[i], "-list", "-run", "no-such-*", alpha]; runcommand(root, strings.concat("list-retained-", tags[i]), retainav, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0); assert(occurrences(out.stdout, "alpha-init\n") == 1); assert(occurrences(out.stdout, "ok ") == 1); assert(!has(out.stdout, "testing: warning: no tests to run")); assert(!has(out.stdout, " [no tests to run]")); assert(!has(out.stdout, "list_alpha.")); assert(os.exists(retained[i])); retainedout[i] = strings.dup(out.stdout); retainederror[i] = strings.dup(out.stderr); let manualav: []str = [retained[i], "-list", "no-such-*"]; runcommand(root, strings.concat("list-manual-", tags[i]), manualav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(out.stdout, "alpha-init\n")); assert(out.stderr.len == 0); manualout[i] = strings.dup(out.stdout); manualerror[i] = strings.dup(out.stderr); i += 1; }; assert(same(coldout[0], coldout[1])); assert(same(colderror[0], colderror[1])); assert(same(positiveout[0], positiveout[1])); assert(same(positiveerror[0], positiveerror[1])); assert(same(retainedout[0], retainedout[1])); assert(same(retainederror[0], retainederror[1])); assert(same(manualout[0], manualout[1])); assert(same(manualerror[0], manualerror[1])); assert(same(readfile(retained[0]), readfile(retained[1]))); clean(root); }; @test fn ordinary_no_match_uses_go_warning_and_result_suffix() void = { let root: str = fresh(); let alpha: str = strings.concat(root, "/alpha"); let beta: str = strings.concat(root, "/beta"); let none: str = strings.concat(root, "/none"); let broken: str = strings.concat(root, "/broken"); mkdirall(alpha); mkdirall(beta); mkdirall(none); mkdirall(broken); writefile(strings.concat(alpha, "/alpha.ww"), "package zero_alpha;\nfn value() i32 = { return 1; };\n"); writefile(strings.concat(alpha, "/alpha_test.ww"), strings.concat( "package zero_alpha;\nimport os;\n", "@test fn first() void = {};\n", "@test fn marker_text() void = {\n", " let text: str = \"not-a-marker testing: warning: no tests to run\\n\";\n", " os.write(os.STDOUT_FILENO, text.ptr, text.len: u64);\n", "};\n")); writefile(strings.concat(beta, "/beta.ww"), "package zero_beta;\nfn value() i32 = { return 2; };\n"); writefile(strings.concat(beta, "/beta_test.ww"), "package zero_beta;\n@test fn only() void = {};\n"); writefile(strings.concat(none, "/none.ww"), "package zero_none;\nfn value() i32 = { return 3; };\n"); writefile(strings.concat(broken, "/broken.ww"), "package zero_broken;\nfn value() missing_type = { return 4; };\n"); writefile(strings.concat(broken, "/broken_test.ww"), "package zero_broken;\n@test fn registered() void = {};\n"); let warning: str = "testing: warning: no tests to run\n"; let suffix: str = " [no tests to run]"; let stages: []str = ["ww", "ww_ww"]; let tags: []str = ["c", "ww"]; let works: []str = [strings.concat(root, "/work-c"), strings.concat(root, "/work-ww")]; let retained: []str = [strings.concat(root, "/retained-c.test"), strings.concat(root, "/retained-ww.test")]; let coldout: []str = ["", ""]; let colderror: []str = ["", ""]; let positiveout: []str = ["", ""]; let positiveerror: []str = ["", ""]; let listout: []str = ["", ""]; let listerror: []str = ["", ""]; let retainedout: []str = ["", ""]; let retainederror: []str = ["", ""]; let manualout: []str = ["", ""]; let manualerror: []str = ["", ""]; let failurediagnostic: []str = ["", ""]; let out: commandout; let i: i32 = 0; for (i < stages.len) { // Reverse request order and run two products concurrently. Result bytes // remain directory-sorted and each product owns its own marker/suffix. let emptyav: []str = [driver(stages[i]), "test", "-j", "2", "-w", works[i], "-run", "no-such-*", beta, alpha]; runcommand(root, strings.concat("ordinary-empty-cold-", tags[i]), emptyav, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0); assert(occurrences(out.stdout, warning) == 2); assert(!has(out.stdout, "[no matches]")); assert(occurrences(out.stdout, "2 discovered, 0 selected, 0 started, 0 completed\n") == 1); assert(occurrences(out.stdout, "1 discovered, 0 selected, 0 started, 0 completed\n") == 1); let alphaok: str = strings.concat("ok ", alpha, " [zero_alpha] [no tests to run]\n"); let betaok: str = strings.concat("ok ", beta, " [zero_beta] [no tests to run]\n"); let alphacount: i32 = pos(out.stdout, "2 discovered, 0 selected, 0 started, 0 completed\n"); let alphareport: i32 = pos(out.stdout, alphaok); let betacount: i32 = pos(out.stdout, "1 discovered, 0 selected, 0 started, 0 completed\n"); let betareport: i32 = pos(out.stdout, betaok); assert(alphacount >= 0 && alphacount < alphareport && alphareport < betacount && betacount < betareport); assert(occurrences(out.stdout, alphaok) == 1); assert(occurrences(out.stdout, betaok) == 1); coldout[i] = strings.dup(out.stdout); colderror[i] = strings.dup(out.stderr); runcommand(root, strings.concat("ordinary-empty-warm-", tags[i]), emptyav, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(out.stdout, coldout[i])); assert(same(out.stderr, colderror[i])); assert(!directoryhasnew(works[i])); // The same bytes inside a running test, but not at a line boundary, are // ordinary user output and must not annotate a successful package result. let positiveav: []str = [driver(stages[i]), "test", "-w", works[i], "-run", "marker_text", alpha]; runcommand(root, strings.concat("ordinary-positive-", tags[i]), positiveav, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0); assert(occurrences(out.stdout, warning) == 1); assert(has(out.stdout, strings.concat("not-a-marker ", warning))); assert(!has(out.stdout, suffix)); assert(has(out.stdout, strings.concat("ok ", alpha, " [zero_alpha]\n"))); positiveout[i] = strings.dup(out.stdout); positiveerror[i] = strings.dup(out.stderr); // The already-closed list behavior remains an early return: no warning, // accounting, old sentinel, or package suffix. let listav: []str = [driver(stages[i]), "test", "-w", works[i], "-list", "-run", "no-such-*", alpha]; runcommand(root, strings.concat("ordinary-list-control-", tags[i]), listav, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(out.stdout, strings.concat("ok ", alpha, " [zero_alpha]\n"))); assert(out.stderr.len == 0); assert(!has(out.stdout, warning) && !has(out.stdout, suffix)); assert(!has(out.stdout, " discovered, ")); listout[i] = strings.dup(out.stdout); listerror[i] = strings.dup(out.stderr); let noneav: []str = [driver(stages[i]), "test", "-w", works[i], none]; runcommand(root, strings.concat("ordinary-no-files-", tags[i]), noneav, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(out.stdout, strings.concat("? ", none, " [no test files]\n"))); assert(out.stderr.len == 0); assert(!has(out.stdout, warning) && !has(out.stdout, suffix)); // A successful zero-selection run may publish its retained test binary. let retainav: []str = [driver(stages[i]), "test", "-w", works[i], "-o", retained[i], "-run", "no-such-*", alpha]; runcommand(root, strings.concat("ordinary-retained-", tags[i]), retainav, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0); assert(occurrences(out.stdout, warning) == 1); assert(occurrences(out.stdout, alphaok) == 1); assert(os.exists(retained[i])); assert(!os.exists(strings.concat(retained[i], ".sepwork"))); let prior: str = strings.dup(readfile(retained[i])); retainedout[i] = strings.dup(out.stdout); retainederror[i] = strings.dup(out.stderr); // Direct execution exposes the harness-owned stream boundary: accounting // remains stdout while the exact pinned warning is stderr. let manualav: []str = [retained[i], "-package=zero_alpha", "no-such-*"]; runcommand(root, strings.concat("ordinary-manual-", tags[i]), manualav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(out.stdout, "2 discovered, 0 selected, 0 started, 0 completed\n")); assert(same(out.stderr, warning)); manualout[i] = strings.dup(out.stdout); manualerror[i] = strings.dup(out.stderr); // A producer failure wins before runtime and preserves a prior public // binary. Cold and repeated failure have no warning, ok line, or suffix. let failav: []str = [driver(stages[i]), "test", "-w", works[i], "-o", retained[i], "-run", "no-such-*", broken]; runcommand(root, strings.concat("ordinary-failure-cold-", tags[i]), failav, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stdout, "FAIL\n")); assert(out.stderr.len != 0); assert(!has(out.stdout, warning) && !has(out.stderr, warning)); assert(!has(out.stdout, suffix) && !has(out.stderr, suffix)); assert(!has(out.stdout, "ok ") && !has(out.stderr, "ok ")); assert(same(prior, readfile(retained[i]))); failurediagnostic[i] = strings.dup(primarydiagnostic(out.stderr)); let failstderr: str = strings.dup(out.stderr); runcommand(root, strings.concat("ordinary-failure-warm-", tags[i]), failav, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stdout, "FAIL\n")); assert(same(out.stderr, failstderr)); assert(same(prior, readfile(retained[i]))); assert(!has(out.stdout, warning) && !has(out.stderr, warning)); // Restored valid reuse still emits the success marker/suffix and never // rewrites an already-identical retained binary. runcommand(root, strings.concat("ordinary-restored-", tags[i]), retainav, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(out.stdout, retainedout[i])); assert(same(out.stderr, retainederror[i])); assert(same(prior, readfile(retained[i]))); assert(!directoryhasnew(works[i])); assert(!os.exists(strings.concat(retained[i], ".sepwork"))); i += 1; }; assert(same(coldout[0], coldout[1])); assert(same(colderror[0], colderror[1])); assert(same(positiveout[0], positiveout[1])); assert(same(positiveerror[0], positiveerror[1])); assert(same(listout[0], listout[1])); assert(same(listerror[0], listerror[1])); assert(same(retainedout[0], retainedout[1])); assert(same(retainederror[0], retainederror[1])); assert(same(manualout[0], manualout[1])); assert(same(manualerror[0], manualerror[1])); assert(same(failurediagnostic[0], failurediagnostic[1])); assert(same(readfile(retained[0]), readfile(retained[1]))); clean(root); }; // Directory-package execution context is one product-level runtime property. // This fixture keeps every source tree disposable while covering the variant, // dependency, environment, routing, concurrency, persistence, and failure // boundaries that could otherwise leak build-plan or caller process state. @test fn directory_test_execution_working_directory() void = { let root: str = fresh(); let caller: str = strings.concat(root, "/caller"); let tree: str = strings.concat(root, "/tree"); let treelink: str = strings.concat(root, "/tree-link"); let failtree: str = strings.concat(root, "/failtree"); mkdirall(caller); mkdirall(tree); mkdirall(failtree); cwdwritedata(caller, "caller"); writefile(strings.concat(caller, "/sentinel"), "parent-sentinel\n"); let stdinpath: str = strings.concat(root, "/stdin.txt"); let stdinbytes: []u8 = alloc([], 4096u64)!; let inputi: i32 = 0; for (inputi < 2048) { append(stdinbytes, 'X'); append(stdinbytes, '\n'); inputi += 1; }; writefile(stdinpath, strings.frombytes(stdinbytes)); let missingstdinout: str = strings.concat(root, "/missing-stdin.stdout"); let missingstdinerr: str = strings.concat(root, "/missing-stdin.stderr"); let missingstdinav: []str = ["/bin/true"]; let missingstdincmd: exec.command; missingstdincmd.path = missingstdinav[0]; missingstdincmd.argv = missingstdinav; missingstdincmd.env = os.getenvs(); missingstdincmd.dir = caller; missingstdincmd.stdinpath = strings.concat(root, "/missing-stdin"); missingstdincmd.stdoutpath = missingstdinout; missingstdincmd.stderrpath = missingstdinerr; missingstdincmd.deadline.sec = 0i64; missingstdincmd.deadline.nsec = 0i64; missingstdincmd.grace = 0i64: time.duration; let missingstdinresult: exec.result; exec.run(&missingstdincmd, &missingstdinresult); assert(missingstdinresult.termination == exec.termination.ERROR); assert(missingstdinresult.errno == 2 && missingstdinresult.cleanuperrno == 0); assert(!os.exists(missingstdinout) && !os.exists(missingstdinerr)); // Create in reverse lexical order; discovery and product emission may not // inherit filesystem or request order. let names: []str = ["probe", "plain", "p6", "p5", "p4", "p3", "p2", "p1", "testdep", "dep", "cmdno"]; let i: i32 = 0; for (i < names.len) { let dir: str = strings.concat(tree, "/", names[i]); assert(os.mkdir(dir, 448i32) == 0); cwdwritedata(dir, names[i]); i += 1; }; let probe: str = strings.concat(tree, "/probe"); let dep: str = strings.concat(tree, "/dep"); let testdep: str = strings.concat(tree, "/testdep"); let p1: str = strings.concat(tree, "/p1"); let p2: str = strings.concat(tree, "/p2"); let p3: str = strings.concat(tree, "/p3"); let p4: str = strings.concat(tree, "/p4"); let p5: str = strings.concat(tree, "/p5"); let p6: str = strings.concat(tree, "/p6"); let plain: str = strings.concat(tree, "/plain"); let cmdno: str = strings.concat(tree, "/cmdno"); let streamsource: str = strings.concat( "fn putfd(fd: i32, value: str) void = {\n", " match (os.writeall(fd, value.ptr, value.len: u64)) {\n", " case let n: i64 => assert(n == value.len: i64);\n", " case let e: os.oserror => abort(\"write failed\");\n", " };\n", "};\n", "fn put(value: str) void = { putfd(os.STDOUT_FILENO, value); };\n", "fn puterr(value: str) void = { putfd(os.STDERR_FILENO, value); };\n", "fn putcount(value: i32) void = {\n", " if (value == 0) { put(\"0\\n\"); } else { if (value == 1) {\n", " put(\"1\\n\"); } else { if (value == 2) { put(\"2\\n\");\n", " } else { put(\"many\\n\"); }; }; };\n", "};\n", "export fn streams(label: str, context: str) void = {\n", " put(strings.concat(\"STREAM \", label, \" out-1 cwd=\", context, \"\\n\"));\n", " puterr(strings.concat(\"STREAM \", label, \" err-1 cwd=\", context, \"\\n\"));\n", " put(strings.concat(\"STREAM \", label, \" out-2 cwd=\", context, \"\\n\"));\n", " puterr(strings.concat(\"STREAM \", label, \" err-2 cwd=\", context, \"\\n\"));\n", "};\n"); let envsource: str = strings.concat( " put(label); put(\" LC_ALL=\");\n match (os.getenv(\"LC_ALL\")) {\n case let value: str => put(value); case void => put(\"\");\n }; put(\"\\n\");\n put(label); put(\" TMPDIR=\");\n match (os.getenv(\"TMPDIR\")) {\n case let value: str => put(value); case void => put(\"\");\n }; put(\"\\n\");\n put(label); put(\" WW_CWD_ORIGINAL=\");\n match (os.getenv(\"WW_CWD_ORIGINAL\")) {\n case let value: str => put(value); case void => put(\"\");\n }; put(\"\\n\");\n put(label); put(\" WW_CWD_EMPTY=\");\n match (os.getenv(\"WW_CWD_EMPTY\")) {\n case let value: str => put(value); case void => put(\"\");\n }; put(\"\\n\");\n put(label); put(\" lc_all=\");\n match (os.getenv(\"lc_all\")) {\n case let value: str => put(value); case void => put(\"\");\n }; put(\"\\n\");\n", " let lccount: i32 = 0; let tmpcount: i32 = 0;\n let originalcount: i32 = 0; let emptyvaluecount: i32 = 0;\n let lowercount: i32 = 0; let malformedcount: i32 = 0;\n let emptyentrycount: i32 = 0;\n i = 0; for (i < env.len) {\n if (strings.hasprefix(env[i], \"LC_ALL=\")) { lccount += 1; };\n if (strings.hasprefix(env[i], \"TMPDIR=\")) { tmpcount += 1; };\n if (strings.hasprefix(env[i], \"WW_CWD_ORIGINAL=\")) { originalcount += 1; };\n if (strings.hasprefix(env[i], \"WW_CWD_EMPTY=\")) { emptyvaluecount += 1; };\n if (strings.hasprefix(env[i], \"lc_all=\")) { lowercount += 1; };\n if (strings.compare(env[i], \"WW_CWD_MALFORMED\") == 0) { malformedcount += 1; };\n if (env[i].len == 0) { emptyentrycount += 1; };\n i += 1;\n };\n", " put(label); put(\" PWD-count=\"); putcount(count);\n put(label); put(\" PWD-last=\");\n if (last == env.len - 1) { put(\"yes\\n\"); } else { put(\"no\\n\"); };\n put(label); put(\" PATH-count=\"); putcount(pathcount);\n", " put(label); put(\" LC_ALL-count=\"); putcount(lccount);\n put(label); put(\" TMPDIR-count=\"); putcount(tmpcount);\n put(label); put(\" WW_CWD_ORIGINAL-count=\"); putcount(originalcount);\n put(label); put(\" WW_CWD_EMPTY-count=\"); putcount(emptyvaluecount);\n put(label); put(\" lc_all-count=\"); putcount(lowercount);\n put(label); put(\" malformed-count=\"); putcount(malformedcount);\n put(label); put(\" empty-entry-count=\"); putcount(emptyentrycount);\n"); writefile(strings.concat(probe, "/probe.ww"), strings.concat( "package probe;\n", "import os;\n", "import strings;\n", streamsource, "fn emitfile(label: str, key: str, path: str) void = {\n", " put(label); put(\" \" ); put(key); put(\"=\");\n", " let fd: i32 = os.open(path, os.flag.RDONLY, 0i32);\n", " if (fd < 0) { put(\"\\n\"); return; };\n", " let size: i64 = -1i64;\n", " match (os.filesize(fd)) {\n", " case let value: i64 => size = value;\n", " case let e: os.oserror => void;\n", " };\n", " if (size < 0i64) { os.close(fd); put(\"\\n\"); return; };\n", " let bytes: []u8 = alloc([], size: u64)!; bytes.len = size: i32;\n", " match (os.readall(fd, bytes.ptr, size: u64)) {\n", " case let n: i64 => assert(n == size);\n", " case let e: os.oserror => abort(\"read failed\");\n", " }; os.close(fd);\n", " let body: str; body.ptr = bytes.ptr; body.len = bytes.len; put(body);\n", " if (body.len == 0 || body[body.len - 1] != '\\n') { put(\"\\n\"); };\n", "};\n", "export fn run(label: str) void = {\n let buf: [4096]u8;\n let n: i64 = os.getcwd(&buf[0], size([4096]u8)); assert(n > 1i64);\n let cwd: str; cwd.ptr = &buf[0]; cwd.len = (n - 1i64): i32;\n streams(label, cwd);\n put(label); put(\" cwd=\"); put(cwd); put(\"\\n\");\n put(label); put(\" PWD=\");\n match (os.getenv(\"PWD\")) {\n case let value: str => put(value); case void => put(\"\");\n }; put(\"\\n\");\n put(label); put(\" PATH=\");\n match (os.getenv(\"PATH\")) {\n case let value: str => put(value); case void => put(\"\");\n }; put(\"\\n\");\n let env: []str = os.getenvs(); let count: i32 = 0; let pathcount: i32 = 0; let last: i32 = -1;\n let i: i32 = 0; for (i < env.len) {\n if (strings.hasprefix(env[i], \"PWD=\")) { count += 1; last = i; };\n if (strings.hasprefix(env[i], \"PATH=\")) { pathcount += 1; };\n i += 1;\n };\n", envsource, " emitfile(label, \"data\", \"data.txt\");\n", " emitfile(label, \"testdata\", \"testdata/input.txt\");\n", " let input: [1]u8;\n let inputn: i64 = os.read(os.STDIN_FILENO, &input[0], 1u64);\n put(label); put(\" stdin=\");\n if (inputn == 0i64) { put(\"eof\\n\"); } else {\n if (inputn == 1i64) { put(\"data\\n\"); } else { put(\"\\n\"); };\n };\n", " let fd: i32 = os.open(\"created.txt\", os.flag.WRONLY |\n", " os.flag.CREATE | os.flag.TRUNC, 384i32);\n", " put(label); put(\" create=\");\n", " if (fd < 0) { put(\"\\n\"); } else {\n", " match (os.writeall(fd, label.ptr, label.len: u64)) {\n", " case let wrote: i64 => assert(wrote == label.len: i64);\n", " case let e: os.oserror => abort(\"create write failed\");\n", " }; assert(os.close(fd) == 0); put(\"ok\\n\");\n", " };\n", "};\n")); writefile(strings.concat(dep, "/dep.ww"), strings.concat( "package dep;\nimport probe;\n", "export fn value() i32 = { return 7; };\n", "fn init() void = { probe.run(\"dep-init\"); };\n")); writefile(strings.concat(dep, "/dep_test.ww"), "package dep;\nimport probe;\n@test fn cwd() void = { probe.run(\"dep-test\"); };\n"); writefile(strings.concat(testdep, "/testdep.ww"), strings.concat( "package testdep;\nimport probe;\n", "export fn value() i32 = { return 8; };\n", "fn init() void = { probe.run(\"testdep-init\"); };\n")); writefile(strings.concat(testdep, "/testdep_test.ww"), strings.concat( "package testdep;\nimport probe;\n", "@test fn cwd() void = { probe.run(\"testdep-test\"); };\n")); writefile(strings.concat(p1, "/p1.ww"), "package p1;\nimport _ dep;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(p1, "/p1_test.ww"), "package p1;\nimport probe;\n@test fn cwd() void = { probe.run(\"p1-internal\"); };\n"); writefile(strings.concat(p2, "/p2.ww"), "package p2;\nimport _ dep;\nexport fn value() i32 = { return 2; };\n"); writefile(strings.concat(p2, "/p2_test.ww"), strings.concat( "package p2_test;\nimport p2;\nimport probe;\n", "@test fn cwd() void = { assert(p2.value() == 2); ", "probe.run(\"p2-external\"); };\n")); writefile(strings.concat(p3, "/p3.ww"), "package p3;\nimport _ dep;\nexport fn value() i32 = { return 3; };\n"); writefile(strings.concat(p3, "/internal_test.ww"), strings.concat( "package p3;\nimport _ testdep;\nimport probe;\n", "export fn helper() i32 = { return 33; };\n", "@test fn cwd_internal() void = { probe.run(\"p3-internal\"); };\n")); writefile(strings.concat(p3, "/external_test.ww"), strings.concat( "package p3_test;\nimport p3;\nimport probe;\n", "@test fn cwd_external() void = { assert(p3.helper() == 33); ", "probe.run(\"p3-external\"); };\n")); writefile(strings.concat(p4, "/internal_test.ww"), "package p4;\nimport probe;\n@test fn cwd() void = { probe.run(\"p4-internal-only\"); };\n"); writefile(strings.concat(p5, "/external_test.ww"), "package p5_test;\nimport probe;\n@test fn cwd() void = { probe.run(\"p5-external-only\"); };\n"); writefile(strings.concat(p6, "/internal_test.ww"), strings.concat( "package p6;\nimport probe;\nexport fn helper() i32 = { return 66; };\n", "@test fn cwd_internal() void = { probe.run(\"p6-internal-only\"); };\n")); writefile(strings.concat(p6, "/external_test.ww"), strings.concat( "package p6_test;\nimport p6;\nimport probe;\n", "@test fn cwd_external() void = { assert(p6.helper() == 66); ", "probe.run(\"p6-external-only\"); };\n")); writefile(strings.concat(plain, "/plain.ww"), "package plain;\nimport probe;\nfn init() void = { probe.run(\"plain-init\"); };\n"); writefile(strings.concat(cmdno, "/main.ww"), strings.concat( "package main;\nimport probe;\n", "fn init() void = { probe.run(\"cmdno-init\"); };\n", "fn main() void = { probe.run(\"cmdno-main\"); };\n")); let failnames: []str = ["reject", "p9", "p8", "p7"]; i = 0; for (i < failnames.len) { let dir: str = strings.concat(failtree, "/", failnames[i]); assert(os.mkdir(dir, 448i32) == 0); cwdwritedata(dir, failnames[i]); i += 1; }; let p7: str = strings.concat(failtree, "/p7"); let p8: str = strings.concat(failtree, "/p8"); let p9: str = strings.concat(failtree, "/p9"); let reject: str = strings.concat(failtree, "/reject"); writefile(strings.concat(p7, "/p7_test.ww"), strings.concat( "package p7;\nimport probe;\n@test fn fails() void = {\n", " probe.run(\"p7-fail\"); assert(false);\n};\n")); writefile(strings.concat(p8, "/p8_test.ww"), strings.concat( "package p8;\nimport probe;\nimport time;\n@test fn timesout() void = {\n", " probe.run(\"p8-timeout\");\n", " time.sleep((2i64 * (time.second: i64)): time.duration, ", "time.clock.monotonic);\n};\n")); writefile(strings.concat(p9, "/p9_test.ww"), strings.concat( "package p9;\nimport os;\nimport probe;\n@test fn signals() void = {\n", " probe.run(\"p9-signal\"); os.kill(os.getpid(), os.SIGTERM);\n};\n")); writefile(strings.concat(reject, "/reject.ww"), "package reject;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(reject, "/bad_test.ww"), "package unrelated;\n@test fn unreachable() void = { assert(false); };\n"); let out: commandout; let lnav: []str = ["/bin/ln", "-s", tree, treelink]; runcommand(root, "cwd-link", lnav, time.second, &out); expectexit(&out, 0); // The first environment has hundreds of unrelated entries after two PWD // entries. The product must dynamically clone it, remove both PWD entries, // and append exactly one authoritative PWD last. let cbase: []str = cwdtestenv("ww"); let cenv: []str = alloc([], (cbase.len + 300): u64)!; i = 0; for (i < cbase.len) { append(cenv, cbase[i]); i += 1; }; i = 0; for (i < 300) { append(cenv, strings.concat("WW_CWD_PADDING_", boundarypkgname(i), "=abcdefghijklmnopqrstuvwxyz0123456789")); i += 1; }; let wenv: []str = cwdtestenv("ww_ww"); let forward: []str = [driver("ww"), "test", "-j", "1", "-I", tree, dep, testdep, p1, p2, p3, p4, p5, p6]; let reverse: []str = [driver("ww_ww"), "test", "-j", "4", "-I", tree, p6, p5, p4, p3, p2, p1, testdep, dep]; let outc: commandout; let outw: commandout; runcommandinputenvdir(root, "cwd-multi-c", forward, cenv, caller, stdinpath, (120i64 * (time.second: i64)): time.duration, &outc); runcommandinputenvdir(root, "cwd-multi-ww", reverse, wenv, caller, stdinpath, (120i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 0); expectexit(&outw, 0); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr) && outc.stderr.len == 0); cwdassertpackage(outc.stdout, "dep-init", dep, "dep-data", "dep-testdata"); cwdassertpackage(outc.stdout, "dep-test", dep, "dep-data", "dep-testdata"); cwdassertpackage(outc.stdout, "testdep-init", testdep, "testdep-data", "testdep-testdata"); cwdassertpackage(outc.stdout, "testdep-test", testdep, "testdep-data", "testdep-testdata"); cwdassertpackage(outc.stdout, "dep-init", p1, "p1-data", "p1-testdata"); cwdassertpackage(outc.stdout, "p1-internal", p1, "p1-data", "p1-testdata"); let multiout: str = strings.dup(outc.stdout); let ran: []str = [dep, testdep, p1, p2, p3, p4, p5, p6]; i = 0; for (i < ran.len) { assert(os.exists(strings.concat(ran[i], "/created.txt"))); i += 1; }; assert(!os.exists(strings.concat(caller, "/created.txt"))); assert(same(readfile(strings.concat(caller, "/sentinel")), "parent-sentinel\n")); // The official Go script exercises both an absent and an explicitly empty // caller PATH. Each produces only the selected toolchain directory, and the // two stages expose identical process bytes. assert(os.remove(strings.concat(p4, "/created.txt")) == 0); let absentbase: []str = cwdtestenv("ww"); let absentenv: []str = alloc([], absentbase.len: u64)!; i = 0; for (i < absentbase.len) { if (!strings.hasprefix(absentbase[i], "PATH=")) { append(absentenv, absentbase[i]); }; i += 1; }; let absentav: []str = [driver("ww"), "test", "-I", tree, p4]; runcommandinputenvdir(root, "path-absent-c", absentav, absentenv, caller, stdinpath, (60i64 * (time.second: i64)): time.duration, &outc); expectexit(&outc, 0); cwdassertrecord(outc.stdout, "p4-internal-only", p4, p4, "1", "yes", "p4-data", "p4-testdata", "eof"); cwdassertdirectoryenv(outc.stdout, "p4-internal-only"); cwdassertpath(outc.stdout, "p4-internal-only", strings.concat(repo(), "/out/bin"), "1"); assert(os.remove(strings.concat(p4, "/created.txt")) == 0); let emptybase: []str = cwdtestenv("ww_ww"); let emptyenv: []str = alloc([], (emptybase.len + 1): u64)!; i = 0; for (i < emptybase.len) { if (!strings.hasprefix(emptybase[i], "PATH=")) { append(emptyenv, emptybase[i]); }; i += 1; }; append(emptyenv, "PATH="); let emptyav: []str = [driver("ww_ww"), "test", "-I", tree, p4]; runcommandinputenvdir(root, "path-empty-ww", emptyav, emptyenv, caller, stdinpath, (60i64 * (time.second: i64)): time.duration, &outw); expectexit(&outw, 0); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); assert(os.remove(strings.concat(p4, "/created.txt")) == 0); // Filtering, listing, and a no-match filter all execute the same product // binary. Package initialization therefore has the product cwd even when no // selected test body runs. let filterav: []str = [driver("ww"), "test", "-run", "cwd_external", "-I", tree, p3]; runcommandinputenvdir(root, "cwd-filter", filterav, cenv, caller, stdinpath, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); cwdassertpackage(out.stdout, "dep-init", p3, "p3-data", "p3-testdata"); cwdassertpackage(out.stdout, "testdep-init", p3, "p3-data", "p3-testdata"); cwdassertpackage(out.stdout, "p3-external", p3, "p3-data", "p3-testdata"); assert(!has(out.stdout, "p3-internal cwd=")); let listav: []str = [driver("ww_ww"), "test", "-list", "-I", tree, p3]; runcommandinputenvdir(root, "cwd-list", listav, wenv, caller, stdinpath, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); cwdassertpackage(out.stdout, "dep-init", p3, "p3-data", "p3-testdata"); cwdassertpackage(out.stdout, "testdep-init", p3, "p3-data", "p3-testdata"); assert(has(out.stdout, "p3.cwd_internal\n")); assert(has(out.stdout, "p3.cwd_external\n")); assert(!has(out.stdout, "p3-internal cwd=")); assert(!has(out.stdout, "p3-external cwd=")); let nomatchav: []str = [driver("ww"), "test", "-run", "no-such-test", "-I", tree, p3]; runcommandinputenvdir(root, "cwd-no-match", nomatchav, cenv, caller, stdinpath, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); cwdassertpackage(out.stdout, "dep-init", p3, "p3-data", "p3-testdata"); cwdassertpackage(out.stdout, "testdep-init", p3, "p3-data", "p3-testdata"); assert(has(out.stdout, "testing: warning: no tests to run\n")); assert(has(out.stdout, strings.concat("ok ", p3, " [p3] [no tests to run]\n"))); assert(!has(out.stdout, "p3-internal cwd=")); assert(!has(out.stdout, "p3-external cwd=")); let failc: []str = [driver("ww"), "test", "-I", tree, "-I", failtree, p7]; let failw: []str = [driver("ww_ww"), "test", "-I", tree, "-I", failtree, p7]; runcommandinputenvdir(root, "cwd-fail-c", failc, cenv, caller, stdinpath, (60i64 * (time.second: i64)): time.duration, &outc); runcommandinputenvdir(root, "cwd-fail-ww", failw, wenv, caller, stdinpath, (60i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 1); expectexit(&outw, 1); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); cwdassertpackage(outc.stdout, "p7-fail", p7, "p7-data", "p7-testdata"); assert(outc.stderr.len == 0); assert(has(outc.stdout, strings.concat("FAIL ", p7, " [p7] (test exit 1)\n"))); assert(strings.hassuffix(outc.stdout, "\nFAIL\n")); let timeoutc: []str = [driver("ww"), "test", "-timeout-ms=50", "-I", tree, "-I", failtree, p8]; let timeoutw: []str = [driver("ww_ww"), "test", "-timeout-ms=50", "-I", tree, "-I", failtree, p8]; runcommandinputenvdir(root, "cwd-timeout-c", timeoutc, cenv, caller, stdinpath, (60i64 * (time.second: i64)): time.duration, &outc); runcommandinputenvdir(root, "cwd-timeout-ww", timeoutw, wenv, caller, stdinpath, (60i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 1); expectexit(&outw, 1); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); cwdassertpackage(outc.stdout, "p8-timeout", p8, "p8-data", "p8-testdata"); assert(has(outc.stdout, "FAIL(timeout)\n")); assert(outc.stderr.len == 0); assert(has(outc.stdout, strings.concat("FAIL ", p8, " [p8] (test exit 1)\n"))); assert(strings.hassuffix(outc.stdout, "\nFAIL\n")); let signalc: []str = [driver("ww"), "test", "-I", tree, "-I", failtree, p9]; let signalw: []str = [driver("ww_ww"), "test", "-I", tree, "-I", failtree, p9]; runcommandinputenvdir(root, "cwd-signal-c", signalc, cenv, caller, stdinpath, (60i64 * (time.second: i64)): time.duration, &outc); runcommandinputenvdir(root, "cwd-signal-ww", signalw, wenv, caller, stdinpath, (60i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 1); expectexit(&outw, 1); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); cwdassertpackage(outc.stdout, "p9-signal", p9, "p9-data", "p9-testdata"); assert(has(outc.stdout, "FAIL (signal 15)\n")); assert(outc.stderr.len == 0); assert(has(outc.stdout, strings.concat("FAIL ", p9, " [p9] (test exit 1)\n"))); assert(strings.hassuffix(outc.stdout, "\nFAIL\n")); // Source-class rejection precedes every captured build or test child, so a // nonempty caller input cannot create a partial persistent generation. let rejectcwork: str = strings.concat(root, "/stdin-reject-c"); let rejectwwork: str = strings.concat(root, "/stdin-reject-ww"); assert(os.mkdir(rejectcwork, 448i32) == 0); assert(os.mkdir(rejectwwork, 448i32) == 0); let rejectcav: []str = [driver("ww"), "test", "-w", rejectcwork, "-I", failtree, reject]; let rejectwav: []str = [driver("ww_ww"), "test", "-w", rejectwwork, "-I", failtree, reject]; runcommandinputenvdir(root, "stdin-reject-c", rejectcav, cenv, caller, stdinpath, (60i64 * (time.second: i64)): time.duration, &outc); runcommandinputenvdir(root, "stdin-reject-ww", rejectwav, wenv, caller, stdinpath, (60i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 1); expectexit(&outw, 1); assert(same(outc.stdout, "FAIL\n") && same(outw.stdout, "FAIL\n")); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stderr, "test package must match production package or _test\n")); assert(!has(outc.stdout, " PATH=") && !has(outw.stdout, " PATH=")); assert(!has(outc.stdout, " LC_ALL=") && !has(outw.stdout, " LC_ALL=")); assert(directoryisempty(rejectcwork)); assert(directoryisempty(rejectwwork)); // Packages without selected test sources and `ww build` never start a test // process, even if production initialization would make that visible. let notestav: []str = [driver("ww_ww"), "test", "-I", tree, plain, cmdno]; runcommandinputenvdir(root, "cwd-no-tests", notestav, wenv, caller, stdinpath, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(!has(out.stdout, "plain-init")); assert(!has(out.stdout, "cmdno-init")); assert(!has(out.stdout, "cmdno-main")); assert(!has(out.stdout, " PATH=")); assert(!has(out.stdout, " LC_ALL=") && !has(out.stdout, " TMPDIR=")); assert(!os.exists(strings.concat(plain, "/created.txt"))); assert(!os.exists(strings.concat(cmdno, "/created.txt"))); let commandbin: str = strings.concat(root, "/cmdno.bin"); let buildav: []str = [driver("ww"), "build", "-I", tree, "-o", commandbin, cmdno]; runcommandinputenvdir(root, "cwd-build", buildav, cenv, caller, stdinpath, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(os.exists(commandbin)); assert(!has(out.stdout, "cmdno-init") && !has(out.stdout, "cmdno-main")); assert(!has(out.stdout, " PATH=")); assert(!has(out.stdout, " LC_ALL=") && !has(out.stdout, " TMPDIR=")); assert(!os.exists(strings.concat(cmdno, "/created.txt"))); // Compile-only paths do not run. A published binary has no embedded cwd or // wrapper: direct invocation keeps the caller's cwd and duplicate PWDs. assert(os.remove(strings.concat(p2, "/created.txt")) == 0); let defaultcompile: []str = [driver("ww"), "test", "-c", "-I", tree, p2]; runcommandinputenvdir(root, "cwd-compile-default", defaultcompile, cenv, caller, stdinpath, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let defaultbin: str = strings.concat(caller, "/p2.test"); assert(os.exists(defaultbin)); assert(!os.exists(strings.concat(p2, "/p2.test"))); assert(!os.exists(strings.concat(p2, "/created.txt"))); assert(!has(out.stdout, "dep-init") && !has(out.stdout, "p2-external")); assert(!has(out.stdout, " PATH=")); assert(!has(out.stdout, " LC_ALL=") && !has(out.stdout, " TMPDIR=")); assert(os.remove(defaultbin) == 0); assert(os.remove(strings.concat(p1, "/created.txt")) == 0); let p1cbin: str = strings.concat(root, "/p1-c.test"); let p1wbin: str = strings.concat(root, "/p1-ww.test"); let compilec: []str = [driver("ww"), "test", "-c", "-o", p1cbin, "-I", tree, p1]; let compilew: []str = [driver("ww_ww"), "test", "-c", "-o", p1wbin, "-I", tree, p1]; runcommandinputenvdir(root, "cwd-compile-c", compilec, cenv, caller, stdinpath, (90i64 * (time.second: i64)): time.duration, &outc); runcommandinputenvdir(root, "cwd-compile-ww", compilew, wenv, caller, stdinpath, (90i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 0); expectexit(&outw, 0); assert(!os.exists(strings.concat(p1, "/created.txt"))); assert(same(readfile(p1cbin), readfile(p1wbin))); assert(!has(outc.stdout, "dep-init") && !has(outw.stdout, "dep-init")); assert(!has(outc.stdout, " PATH=") && !has(outw.stdout, " PATH=")); assert(!has(outc.stdout, " LC_ALL=") && !has(outw.stdout, " LC_ALL=")); let directbin: []str = [p1cbin, "-package=p1"]; runcommandinputenvdir(root, "cwd-published-direct", directbin, cenv, caller, stdinpath, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); cwdassertrecord(out.stdout, "dep-init", caller, "/ww-cwd-inherited-first", "2", "no", "caller-data", "caller-testdata", "data"); cwdassertrecord(out.stdout, "p1-internal", caller, "/ww-cwd-inherited-first", "2", "no", "caller-data", "caller-testdata", "data"); cwdassertcallerenv(out.stdout, "dep-init"); cwdassertcallerenv(out.stdout, "p1-internal"); cwdassertcallerpath(out.stdout, "dep-init"); cwdassertcallerpath(out.stdout, "p1-internal"); cwdassertsplitstreams(out.stdout, out.stderr, "dep-init", caller); cwdassertsplitstreams(out.stdout, out.stderr, "p1-internal", caller); assert(os.exists(strings.concat(caller, "/created.txt"))); assert(os.remove(strings.concat(caller, "/created.txt")) == 0); // Raw single-file compatibility retains its caller cwd/PWD/stdin context, // but it is still a test process and therefore receives the selected // toolchain directory first in PATH. let rawc: []str = [driver("ww"), "test", "-I", tree, strings.concat(p4, "/internal_test.ww")]; let raww: []str = [driver("ww_ww"), "test", "-I", tree, strings.concat(p4, "/internal_test.ww")]; runcommandinputenvdir(root, "cwd-raw-c", rawc, cbase, caller, stdinpath, (60i64 * (time.second: i64)): time.duration, &outc); assert(os.remove(strings.concat(caller, "/created.txt")) == 0); runcommandinputenvdir(root, "cwd-raw-ww", raww, wenv, caller, stdinpath, (60i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 0); expectexit(&outw, 0); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); cwdassertrecord(outc.stdout, "p4-internal-only", caller, "/ww-cwd-inherited-first", "2", "yes", "caller-data", "caller-testdata", "data"); cwdassertcallerenv(outc.stdout, "p4-internal-only"); cwdasserttestpath(outc.stdout, "p4-internal-only"); cwdassertsplitstreams(outc.stdout, outc.stderr, "p4-internal-only", caller); assert(os.remove(strings.concat(caller, "/created.txt")) == 0); // A retained running test receives the same environment before guarded // install. A later runtime failure observes PATH but cannot replace the // successful binary; both stages preserve identical prior bytes. let retained: str = strings.concat(root, "/path-retained.test"); let retainc: []str = [driver("ww"), "test", "-o", retained, "-I", tree, p1]; let retainw: []str = [driver("ww_ww"), "test", "-o", retained, "-I", tree, p1]; runcommandinputenvdir(root, "path-retain-c", retainc, cenv, caller, stdinpath, (90i64 * (time.second: i64)): time.duration, &outc); expectexit(&outc, 0); cwdassertpackage(outc.stdout, "dep-init", p1, "p1-data", "p1-testdata"); cwdassertpackage(outc.stdout, "p1-internal", p1, "p1-data", "p1-testdata"); let retainedbytes: str = strings.dup(readfile(retained)); runcommandinputenvdir(root, "path-retain-ww", retainw, wenv, caller, stdinpath, (90i64 * (time.second: i64)): time.duration, &outw); expectexit(&outw, 0); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); assert(same(retainedbytes, readfile(retained))); let retainfailc: []str = [driver("ww"), "test", "-o", retained, "-I", tree, "-I", failtree, p7]; let retainfailw: []str = [driver("ww_ww"), "test", "-o", retained, "-I", tree, "-I", failtree, p7]; runcommandinputenvdir(root, "path-retain-fail-c", retainfailc, cenv, caller, stdinpath, (90i64 * (time.second: i64)): time.duration, &outc); expectexit(&outc, 1); cwdassertpackage(outc.stdout, "p7-fail", p7, "p7-data", "p7-testdata"); assert(strings.hassuffix(outc.stdout, "\nFAIL\n")); assert(same(retainedbytes, readfile(retained))); runcommandinputenvdir(root, "path-retain-fail-ww", retainfailw, wenv, caller, stdinpath, (90i64 * (time.second: i64)): time.duration, &outw); expectexit(&outw, 1); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); assert(same(retainedbytes, readfile(retained))); assert(!os.exists(strings.concat(retained, ".new"))); assert(!directoryhasfragment(root, "path-retained.test.wwtxn.")); cwdassertpackage(multiout, "dep-init", p2, "p2-data", "p2-testdata"); cwdassertpackage(multiout, "p2-external", p2, "p2-data", "p2-testdata"); cwdassertpackage(multiout, "dep-init", p3, "p3-data", "p3-testdata"); cwdassertpackage(multiout, "testdep-init", p3, "p3-data", "p3-testdata"); cwdassertpackage(multiout, "p3-internal", p3, "p3-data", "p3-testdata"); cwdassertpackage(multiout, "p3-external", p3, "p3-data", "p3-testdata"); cwdassertpackage(multiout, "p4-internal-only", p4, "p4-data", "p4-testdata"); cwdassertpackage(multiout, "p5-external-only", p5, "p5-data", "p5-testdata"); cwdassertpackage(multiout, "p6-internal-only", p6, "p6-data", "p6-testdata"); cwdassertpackage(multiout, "p6-external-only", p6, "p6-data", "p6-testdata"); let realrec: []str = [driver("ww"), "test", "-j", "1", "-I", tree, strings.concat(tree, "/...")]; let linkrec: []str = [driver("ww_ww"), "test", "-j", "4", "-I", "../tree-link", "../tree-link/..."]; runcommandinputenvdir(root, "cwd-recursive-real", realrec, cenv, caller, stdinpath, (120i64 * (time.second: i64)): time.duration, &outc); runcommandinputenvdir(root, "cwd-recursive-link", linkrec, wenv, caller, stdinpath, (120i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 0); expectexit(&outw, 0); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr) && outc.stderr.len == 0); assert(has(outc.stdout, strings.concat("? ", plain, " [no test files]\n"))); assert(has(outc.stdout, strings.concat("? ", cmdno, " [no test files]\n"))); assert(!has(outc.stdout, "plain-init")); assert(!has(outc.stdout, "cmdno-init")); assert(!has(outc.stdout, "cmdno-main")); cwdassertpackage(outc.stdout, "p6-external-only", p6, "p6-data", "p6-testdata"); let direct: []str = [driver("ww"), "test", "-I", tree, p1]; let equivalent: []str = [driver("ww_ww"), "test", "-j", "3", "-I", "../tree-link", "../tree/p1", p1, "../tree/./p1/../p1", "../tree-link/p1"]; runcommandinputenvdir(root, "cwd-direct", direct, cenv, caller, stdinpath, (60i64 * (time.second: i64)): time.duration, &outc); runcommandinputenvdir(root, "cwd-equivalent", equivalent, wenv, caller, stdinpath, (60i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 0); expectexit(&outw, 0); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); assert(occurrences(outc.stdout, strings.concat("ok ", p1, " [p1]\n")) == 1); cwdassertpackage(outc.stdout, "dep-init", p1, "p1-data", "p1-testdata"); cwdassertpackage(outc.stdout, "p1-internal", p1, "p1-data", "p1-testdata"); // Tool wrappers observe the established build-tool context. Only the // generated product binary gets the package cwd; compiler, assembler, and // linker cwd/argv/locale/TMPDIR remain owned by the build plan. let tooltrace: str = strings.concat(root, "/cwd-tools.trace"); let cw: str = strings.concat(root, "/cwd-w6c.sh"); let aw: str = strings.concat(root, "/cwd-w6a.sh"); let lw: str = strings.concat(root, "/cwd-w6l.sh"); writefile(tooltrace, ""); writeexecutable(cw, strings.concat( "#!/bin/sh\n", "printf 'compiler cwd=' >> \"$WW_CWD_TOOL_TRACE\"\n", "/bin/pwd >> \"$WW_CWD_TOOL_TRACE\"\n", "printf 'compiler env=LC_ALL:%s TMPDIR:%s PATH:%s\\n' \"$LC_ALL\" ", "\"$TMPDIR\" \"$PATH\" >> \"$WW_CWD_TOOL_TRACE\"\n", "if IFS= read -r WW_CWD_STDIN; then\n", " printf 'compiler stdin=data\\n' >> \"$WW_CWD_TOOL_TRACE\"\n", "else printf 'compiler stdin=eof\\n' >> \"$WW_CWD_TOOL_TRACE\"; fi\n", "printf 'compiler argv' >> \"$WW_CWD_TOOL_TRACE\"\n", "for arg in \"$@\"; do printf ' <%s>' \"$arg\" >> ", "\"$WW_CWD_TOOL_TRACE\"; done\n", "printf '\\n' >> \"$WW_CWD_TOOL_TRACE\"\n", "exec \"$WW_CWD_REAL_W6C\" \"$@\"\n")); writeexecutable(aw, strings.concat( "#!/bin/sh\n", "printf 'assembler cwd=' >> \"$WW_CWD_TOOL_TRACE\"\n", "/bin/pwd >> \"$WW_CWD_TOOL_TRACE\"\n", "printf 'assembler env=LC_ALL:%s TMPDIR:%s PATH:%s\\n' \"$LC_ALL\" ", "\"$TMPDIR\" \"$PATH\" >> \"$WW_CWD_TOOL_TRACE\"\n", "if IFS= read -r WW_CWD_STDIN; then\n", " printf 'assembler stdin=data\\n' >> \"$WW_CWD_TOOL_TRACE\"\n", "else printf 'assembler stdin=eof\\n' >> \"$WW_CWD_TOOL_TRACE\"; fi\n", "printf 'assembler argv' >> \"$WW_CWD_TOOL_TRACE\"\n", "for arg in \"$@\"; do printf ' <%s>' \"$arg\" >> ", "\"$WW_CWD_TOOL_TRACE\"; done\n", "printf '\\n' >> \"$WW_CWD_TOOL_TRACE\"\n", "exec \"$WW_CWD_REAL_W6A\" \"$@\"\n")); writeexecutable(lw, strings.concat( "#!/bin/sh\n", "printf 'linker cwd=' >> \"$WW_CWD_TOOL_TRACE\"\n", "/bin/pwd >> \"$WW_CWD_TOOL_TRACE\"\n", "printf 'linker env=LC_ALL:%s TMPDIR:%s PATH:%s\\n' \"$LC_ALL\" ", "\"$TMPDIR\" \"$PATH\" >> \"$WW_CWD_TOOL_TRACE\"\n", "if IFS= read -r WW_CWD_STDIN; then\n", " printf 'linker stdin=data\\n' >> \"$WW_CWD_TOOL_TRACE\"\n", "else printf 'linker stdin=eof\\n' >> \"$WW_CWD_TOOL_TRACE\"; fi\n", "printf 'linker argv' >> \"$WW_CWD_TOOL_TRACE\"\n", "for arg in \"$@\"; do printf ' <%s>' \"$arg\" >> ", "\"$WW_CWD_TOOL_TRACE\"; done\n", "printf '\\n' >> \"$WW_CWD_TOOL_TRACE\"\n", "exec \"$WW_CWD_REAL_W6L\" \"$@\"\n")); let tracebase: []str = cwdtestenv("ww"); let traceenv: []str = alloc([], (tracebase.len + 7): u64)!; i = 0; for (i < tracebase.len) { if (!strings.hasprefix(tracebase[i], "WW_W6C=") && !strings.hasprefix(tracebase[i], "WW_W6A=") && !strings.hasprefix(tracebase[i], "WW_W6L=")) { append(traceenv, tracebase[i]); }; i += 1; }; append(traceenv, strings.concat("WW_W6C=", cw)); append(traceenv, strings.concat("WW_W6A=", aw)); append(traceenv, strings.concat("WW_W6L=", lw)); append(traceenv, strings.concat("WW_CWD_REAL_W6C=", driver("w6c"))); append(traceenv, strings.concat("WW_CWD_REAL_W6A=", driver("w6a"))); append(traceenv, strings.concat("WW_CWD_REAL_W6L=", driver("w6l"))); append(traceenv, strings.concat("WW_CWD_TOOL_TRACE=", tooltrace)); let work: str = strings.concat(root, "/cwd-work"); assert(os.mkdir(work, 448i32) == 0); let persistav: []str = [driver("ww"), "test", "-w", work, "-I", tree, p1]; runcommandinputenvdir(root, "cwd-persist-cold", persistav, traceenv, caller, stdinpath, (120i64 * (time.second: i64)): time.duration, &outc); expectexit(&outc, 0); cwdassertpackage(outc.stdout, "dep-init", p1, "p1-data", "p1-testdata"); cwdassertpackage(outc.stdout, "p1-internal", p1, "p1-data", "p1-testdata"); let coldtrace: str = readfile(tooltrace); assert(has(coldtrace, strings.concat("compiler cwd=", caller, "\n"))); assert(has(coldtrace, strings.concat("assembler cwd=", caller, "\n"))); assert(has(coldtrace, strings.concat("linker cwd=", caller, "\n"))); assert(!has(coldtrace, strings.concat("cwd=", p1, "\n"))); assert(has(coldtrace, "compiler env=LC_ALL:C TMPDIR:/tmp/")); assert(has(coldtrace, "assembler env=LC_ALL:C TMPDIR:/tmp/")); assert(has(coldtrace, "linker env=LC_ALL:C TMPDIR:/tmp/")); assert(has(coldtrace, " PATH:/ww-path-inherited-last\n")); assert(!has(coldtrace, strings.concat(" PATH:", repo(), "/out/bin:"))); assert(has(coldtrace, "compiler stdin=eof\n")); assert(has(coldtrace, "assembler stdin=eof\n")); assert(has(coldtrace, "linker stdin=eof\n")); assert(!has(coldtrace, "stdin=data\n")); assert(has(coldtrace, "compiler argv <")); assert(has(coldtrace, ".unit.new>")); assert(has(coldtrace, "assembler argv <")); assert(has(coldtrace, "linker argv <")); assert(has(coldtrace, ".a.new>")); let artifactpaths: []str = [ strings.concat(work, "/p1-internal-test.unit.ww"), strings.concat(work, "/p1-internal-test.wwi"), strings.concat(work, "/p1-internal-test.s"), strings.concat(work, "/p1-internal-test.o"), strings.concat(work, "/p1-internal-test.a"), strings.concat(work, "/p1-test-main.unit.ww"), strings.concat(work, "/p1-test-main.init.unit.ww"), strings.concat(work, "/p1-test-main.wwi"), strings.concat(work, "/p1-test-main.s"), strings.concat(work, "/p1-test-main.init.s"), strings.concat(work, "/p1-test-main.o"), strings.concat(work, "/p1-test-main.init.o"), strings.concat(work, "/p1-test-main.a"), strings.concat(work, "/dep.wwi"), strings.concat(work, "/dep.a"), strings.concat(work, "/probe.wwi"), strings.concat(work, "/probe.a") ]; let artifacts: []str = alloc([], artifactpaths.len: u64)!; i = 0; for (i < artifactpaths.len) { append(artifacts, strings.dup(readfile(artifactpaths[i]))); i += 1; }; rewritefile(tooltrace, ""); runcommandinputenvdir(root, "cwd-persist-warm", persistav, traceenv, caller, stdinpath, (120i64 * (time.second: i64)): time.duration, &outw); expectexit(&outw, 0); assert(same(outc.stdout, outw.stdout)); let warmtrace: str = readfile(tooltrace); assert(!has(warmtrace, "compiler cwd=")); assert(!has(warmtrace, "assembler cwd=")); assert(occurrences(warmtrace, "linker cwd=") == 1); assert(has(warmtrace, strings.concat("linker cwd=", caller, "\n"))); assert(has(warmtrace, "linker stdin=eof\n")); assert(!has(warmtrace, "stdin=data\n")); // Non-source data does not enter graph/action/storage bytes. The established // warm final relink remains one invocation, while the next uncached runtime // observes the new relative data immediately. rewritefile(strings.concat(p1, "/data.txt"), "p1-data-v2\n"); rewritefile(strings.concat(p1, "/testdata/input.txt"), "p1-testdata-v2\n"); let changedstdin: []u8 = alloc([], 4096u64)!; inputi = 0; for (inputi < 2048) { append(changedstdin, 'Y'); append(changedstdin, '\n'); inputi += 1; }; rewritefile(stdinpath, strings.frombytes(changedstdin)); rewritefile(tooltrace, ""); runcommandinputenvdir(root, "cwd-persist-data", persistav, traceenv, caller, stdinpath, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); cwdassertpackage(out.stdout, "dep-init", p1, "p1-data-v2", "p1-testdata-v2"); cwdassertpackage(out.stdout, "p1-internal", p1, "p1-data-v2", "p1-testdata-v2"); let datatrace: str = readfile(tooltrace); assert(!has(datatrace, "compiler cwd=")); assert(!has(datatrace, "assembler cwd=")); assert(occurrences(datatrace, "linker cwd=") == 1); assert(has(datatrace, strings.concat("linker cwd=", caller, "\n"))); assert(has(datatrace, "linker stdin=eof\n")); assert(!has(datatrace, "stdin=data\n")); i = 0; for (i < artifactpaths.len) { assert(same(artifacts[i], readfile(artifactpaths[i]))); i += 1; }; assert(!directoryhasnew(work)); assert(!os.exists(strings.concat(work, "/p1.result"))); assert(!os.exists(strings.concat(work, "/p1.output"))); assert(!os.exists(strings.concat(work, "/p1.status"))); assert(!os.exists(strings.concat(work, "/p1.voucher"))); let p1after: str = strings.concat(root, "/p1-after-data.test"); let afterav: []str = [driver("ww_ww"), "test", "-c", "-o", p1after, "-I", tree, p1]; runcommandinputenvdir(root, "cwd-after-data-binary", afterav, wenv, caller, stdinpath, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(readfile(p1cbin), readfile(p1after))); // The builder wrapper creates a deterministic post-build/pre-run boundary. // Removing one owned source directory there must be a child setup error; // the sibling still runs from its own directory and both stages agree. let hidewrapper: str = strings.concat(root, "/build-then-hide.sh"); writeexecutable(hidewrapper, strings.concat( "#!/bin/sh\n", "\"$WW_CWD_REAL_DRIVER\" \"$@\"\n", "status=$?\n", "if [ \"$status\" -eq 0 ]; then\n", " /bin/mv -- \"$WW_CWD_HIDE_DIR\" \"$WW_CWD_HIDE_DIR.gone\" || exit 125\n", " printf 'wrapper-hidden=%s\\n' \"$WW_CWD_HIDE_DIR\" >&2\n", "fi\n", "exit \"$status\"\n")); let hidestages: []str = ["ww", "ww_ww"]; let hidestdout: []str = ["", ""]; let hidestderr: []str = ["", ""]; i = 0; for (i < hidestages.len) { let base: []str = cwdtestenv(hidestages[i]); let hideenv: []str = alloc([], (base.len + 2): u64)!; let j: i32 = 0; for (j < base.len) { append(hideenv, base[j]); j += 1; }; append(hideenv, strings.concat("WW_CWD_REAL_DRIVER=", driver(hidestages[i]))); append(hideenv, strings.concat("WW_CWD_HIDE_DIR=", p1)); let hideav: []str = [driver("wwtest"), "package", "--ww-driver", "../build-then-hide.sh", "-j", "2", "-I", tree, p1, p2]; runcommandinputenvdir(root, strings.concat("cwd-hide-", hidestages[i]), hideav, hideenv, caller, stdinpath, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, strings.concat("wrapper-hidden=", p1, "\n"))); assert(has(out.stdout, strings.concat("FAIL ", p1, " [p1] (test harness error 2)\n"))); assert(!has(out.stdout, "p1-internal cwd=")); cwdassertrecord(out.stdout, "dep-init", p2, p2, "1", "yes", "p2-data", "p2-testdata", "eof"); cwdassertrecord(out.stdout, "p2-external", p2, p2, "1", "yes", "p2-data", "p2-testdata", "eof"); cwdassertdirectoryenv(out.stdout, "dep-init"); cwdassertdirectoryenv(out.stdout, "p2-external"); cwdassertpath(out.stdout, "dep-init", strings.concat(root, ":/ww-path-inherited-first"), "1"); cwdassertpath(out.stdout, "p2-external", strings.concat(root, ":/ww-path-inherited-first"), "1"); cwdassertmergedstreams(out.stdout, "dep-init", p2); cwdassertmergedstreams(out.stdout, "p2-external", p2); assert(os.rename(strings.concat(p1, ".gone"), p1) == 0); assert(!os.exists(strings.concat(p1, ".gone"))); hidestdout[i] = strings.dup(out.stdout); hidestderr[i] = strings.dup(out.stderr); i += 1; }; assert(same(hidestdout[0], hidestdout[1])); assert(same(hidestderr[0], hidestderr[1])); assert(!directoryhasnew(work)); assert(same(readfile(strings.concat(caller, "/sentinel")), "parent-sentinel\n")); assert(!os.exists(strings.concat(caller, "/created.txt"))); clean(root); }; @test fn directory_test_trailer_starts_on_new_line() void = { let root: str = fresh(); let pass: str = strings.concat(root, "/pass"); let fail: str = strings.concat(root, "/fail"); let complete: str = strings.concat(root, "/complete"); let empty: str = strings.concat(root, "/empty"); let signal: str = strings.concat(root, "/signal"); mkdirall(pass); mkdirall(fail); mkdirall(complete); mkdirall(empty); mkdirall(signal); writefile(strings.concat(pass, "/partial_test.ww"), strings.concat( "package trailer_pass_test;\nimport os;\n", "fn init() void = { os.write(os.STDOUT_FILENO,", " \"partial-success\".ptr, 15u64); os.exit(0); };\n", "@test fn unreachable() void = { void; };\n")); writefile(strings.concat(fail, "/partial_test.ww"), strings.concat( "package trailer_fail_test;\nimport os;\n", "fn init() void = { os.write(os.STDERR_FILENO,", " \"partial-failure\".ptr, 15u64); os.exit(7); };\n", "@test fn unreachable() void = { void; };\n")); writefile(strings.concat(complete, "/complete_test.ww"), strings.concat( "package trailer_complete_test;\nimport os;\n", "fn init() void = { os.write(os.STDOUT_FILENO,", " \"complete\\n\".ptr, 9u64); os.exit(0); };\n", "@test fn unreachable() void = { void; };\n")); writefile(strings.concat(empty, "/empty_test.ww"), strings.concat( "package trailer_empty_test;\nimport os;\n", "fn init() void = { os.exit(0); };\n", "@test fn unreachable() void = { void; };\n")); writefile(strings.concat(signal, "/partial_test.ww"), strings.concat( "package trailer_signal_test;\nimport os;\n", "fn init() void = { os.write(os.STDOUT_FILENO,", " \"partial-signal\".ptr, 14u64);", " os.kill(os.getpid(), os.SIGTERM); };\n", "@test fn unreachable() void = { void; };\n")); let stages: []str = ["ww", "ww_ww"]; let tags: []str = ["c", "ww"]; let multiout: []str = ["", ""]; let multierr: []str = ["", ""]; let retained: []str = ["", ""]; let out: commandout; let i: i32 = 0; for (i < stages.len) { let mav: []str = [driver(stages[i]), "test", "-j", "5", "-run", "unreachable", pass, fail, complete, empty, signal]; runcommand(root, strings.concat("trailer-multi-", tags[i]), mav, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stderr.len == 0); assert(strings.hassuffix(out.stdout, "\nFAIL\n")); assert(has(out.stdout, "partial-success\nok ")); assert(!has(out.stdout, "partial-successok ")); assert(has(out.stdout, "partial-failure\nFAIL ")); assert(!has(out.stdout, "partial-failureFAIL ")); assert(has(out.stdout, "partial-signal\nFAIL ")); assert(!has(out.stdout, "partial-signalFAIL ")); assert(has(out.stdout, "complete\nok ")); assert(!has(out.stdout, "complete\n\nok ")); multiout[i] = strings.dup(out.stdout); multierr[i] = strings.dup(out.stderr); let eav: []str = [driver(stages[i]), "test", empty]; runcommand(root, strings.concat("trailer-empty-", tags[i]), eav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0); assert(pos(out.stdout, "ok ") == 0); let lav: []str = [driver(stages[i]), "test", "-list", pass]; runcommand(root, strings.concat("trailer-list-", tags[i]), lav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0); assert(has(out.stdout, "partial-success\nok ")); retained[i] = strings.concat(root, "/retained-", tags[i]); let rav: []str = [driver(stages[i]), "test", "-o", retained[i], pass]; runcommand(root, strings.concat("trailer-retained-", tags[i]), rav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0); assert(has(out.stdout, "partial-success\nok ")); assert(os.exists(retained[i])); let runav: []str = [retained[i]]; runcommand(root, strings.concat("trailer-manual-", tags[i]), runav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(out.stdout, "partial-success")); assert(out.stderr.len == 0); let rawav: []str = [driver(stages[i]), "test", strings.concat(pass, "/partial_test.ww")]; runcommand(root, strings.concat("trailer-raw-", tags[i]), rawav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(out.stdout, "partial-success")); assert(out.stderr.len == 0); i += 1; }; assert(same(multiout[0], multiout[1])); assert(same(multierr[0], multierr[1])); assert(same(readfile(retained[0]), readfile(retained[1]))); clean(root); }; @test fn explicit_test_failure_has_final_status() void = { let root: str = fresh(); let fail: str = strings.concat(root, "/fail"); let pass: str = strings.concat(root, "/pass"); let setup: str = strings.concat(root, "/setup"); mkdirall(fail); mkdirall(pass); mkdirall(setup); let failfile: str = strings.concat(fail, "/fail_test.ww"); writefile(failfile, strings.concat( "package final_fail;\n", "@test fn fails() void = { assert(false); };\n")); writefile(strings.concat(pass, "/pass_test.ww"), strings.concat( "package final_pass;\n", "@test fn passes() void = { assert(true); };\n")); writefile(strings.concat(setup, "/bad_test.ww"), strings.concat( "package final_setup;\nimport nowhere;\n", "@test fn unreachable() void = { void; };\n")); let stages: []str = ["ww", "ww_ww"]; let tags: []str = ["c", "ww"]; let multiout: []str = ["", ""]; let multierr: []str = ["", ""]; let rawout: []str = ["", ""]; let setuperr: []str = ["", ""]; let out: commandout; let i: i32 = 0; for (i < stages.len) { let multiav: []str = [driver(stages[i]), "test", "-j", "2", fail, pass]; runcommand(root, strings.concat("final-multi-", tags[i]), multiav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stderr.len == 0); assert(strings.hassuffix(out.stdout, "\nFAIL\n")); assert(occurrences(out.stdout, "\nFAIL\n") == 1); assert(has(out.stdout, strings.concat("ok ", pass, " [final_pass]\nFAIL\n"))); multiout[i] = strings.dup(out.stdout); multierr[i] = strings.dup(out.stderr); let filterav: []str = [driver(stages[i]), "test", "-run", "fails", fail]; runcommand(root, strings.concat("final-filter-", tags[i]), filterav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stderr.len == 0); assert(strings.hassuffix(out.stdout, "\nFAIL\n")); let listav: []str = [driver(stages[i]), "test", "-list", setup]; runcommand(root, strings.concat("final-list-", tags[i]), listav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stdout, "FAIL\n")); assert(has(out.stderr, "cannot find package nowhere\n")); let rawav: []str = [driver(stages[i]), "test", failfile]; runcommand(root, strings.concat("final-raw-", tags[i]), rawav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stderr.len == 0); assert(strings.hassuffix(out.stdout, "\nFAIL\n")); assert(!has(out.stdout, strings.concat("FAIL ", fail, " ["))); rawout[i] = strings.dup(out.stdout); let setupav: []str = [driver(stages[i]), "test", setup]; runcommand(root, strings.concat("final-setup-", tags[i]), setupav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stdout, "FAIL\n")); assert(has(out.stderr, "cannot find package nowhere\n")); assert(has(out.stderr, strings.concat("FAIL ", setup, " [final_setup] (build exit 1)\n"))); setuperr[i] = strings.dup(out.stderr); let missing: str = strings.concat(root, "/missing"); let missingav: []str = [driver(stages[i]), "test", missing]; runcommand(root, strings.concat("final-missing-", tags[i]), missingav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stdout, "FAIL\n")); assert(same(out.stderr, strings.concat("ww test: cannot find ", missing, "\n"))); let implicitav: []str = [driver(stages[i]), "test"]; runcommanddir(root, strings.concat("final-implicit-", tags[i]), fail, implicitav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stderr.len == 0); assert(!strings.hassuffix(out.stdout, "\nFAIL\n")); let compileav: []str = [driver(stages[i]), "test", "-c", setup]; runcommand(root, strings.concat("final-compile-", tags[i]), compileav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0); assert(has(out.stderr, "cannot find package nowhere\n")); let successav: []str = [driver(stages[i]), "test", pass]; runcommand(root, strings.concat("final-success-", tags[i]), successav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0); assert(!strings.hassuffix(out.stdout, "\nFAIL\n")); let retained: str = strings.concat(root, "/retained-", tags[i], ".test"); writefile(retained, "prior-test-binary\n"); let retainav: []str = [driver(stages[i]), "test", "-o", retained, fail]; runcommand(root, strings.concat("final-retained-", tags[i]), retainav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stderr.len == 0); assert(strings.hassuffix(out.stdout, "\nFAIL\n")); assert(same(readfile(retained), "prior-test-binary\n")); assert(!os.exists(strings.concat(retained, ".new"))); assert(!directoryhasfragment(root, ".wwtxn.")); i += 1; }; assert(same(multiout[0], multiout[1])); assert(same(multierr[0], multierr[1])); assert(same(rawout[0], rawout[1])); assert(same(setuperr[0], setuperr[1])); assert(same(readfile(strings.concat(root, "/retained-c.test")), readfile(strings.concat(root, "/retained-ww.test")))); assert(!directoryhasnew(root)); clean(root); }; @test fn imported_dependency_tests_do_not_leak() void = { let root: str = fresh(); let base: str = packagepath("dependency"); let av: []str = [driver("ww"), "test", "-I", base, strings.concat(base, "/root")]; let out: commandout; runcommand(root, "dependency", av, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "root.imported_production_only ... ok\n")); assert(!has(out.stdout, "imported_dependency_test_must_not_leak")); assert(has(out.stdout, "1 discovered, 1 selected, 1 started, 1 completed\n")); clean(root); }; // Go's contract: only *_test.ww sources are test sources; a line-leading // @test anywhere else fails classification loudly rather than run or // drop silently. @test fn noncanonical_attest_rejected() void = { let root: str = fresh(); let pkgdir: str = strings.concat(root, "/noncanon"); assert(os.mkdir(pkgdir, 448i32) == 0); writefile(strings.concat(pkgdir, "/noncanon.ww"), strings.concat( "package noncanon;\n", "@test\n/* lexer whitespace is legal here */\n", "fn hidden() void = { assert(false); };\n")); let stages: []str = ["ww", "ww_ww"]; let out: commandout; let i: i32 = 0; for (i < stages.len) { let av: []str = [driver(stages[i]), "test", pkgdir]; runcommand(root, strings.concat("noncanon-", stages[i]), av, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, "@test declaration outside *_test.ww")); assert(!has(out.stdout, "hidden")); i += 1; }; clean(root); }; @test fn empty_and_invalid_package_classes() void = { let root: str = fresh(); let stages: []str = ["ww", "ww_ww"]; let out: commandout; let nofiles: str = strings.concat("? ", packagepath("no_tests"), " [no test files]\n"); let si: i32 = 0; for (si < stages.len) { let av: []str = [driver(stages[si]), "test", packagepath("no_tests")]; runcommand(root, strings.concat("notests-explicit-", stages[si]), av, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(out.stdout, nofiles)); assert(out.stderr.len == 0); assert(!has(out.stdout, " ... ok")); let implicit: []str = [driver(stages[si]), "test"]; runcommanddir(root, strings.concat("notests-implicit-", stages[si]), packagepath("no_tests"), implicit, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(out.stdout, nofiles)); assert(out.stderr.len == 0); si += 1; }; let bad: []str = [driver("ww"), "test", packagepath("bad_external")]; runcommand(root, "badexternal", bad, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, "test package must match production package or _test")); let empty: str = strings.concat(root, "/empty"); assert(os.mkdir(empty, 448i32) == 0); let emptyav: []str = [driver("ww"), "test", empty]; runcommand(root, "emptydir", emptyav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, "directory contains no WW package sources")); let invalid: str = strings.concat(root, "/invalid-no-tests"); assert(os.mkdir(invalid, 448i32) == 0); writefile(strings.concat(invalid, "/invalid.ww"), "package invalid;\nimport nowhere;\n"); let invalidav: []str = [driver("ww"), "test", invalid]; runcommand(root, "invalid-no-tests", invalidav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, "cannot find package nowhere")); assert(!has(out.stdout, "[no test files]")); // A test file is part of its variant even when it only supplies helpers. // Its imports and package clause must therefore reach the shared loader; // the coordinator cannot use textual @test presence as a source filter. let helperbad: str = strings.concat(root, "/helperbad"); assert(os.mkdir(helperbad, 448i32) == 0); writefile(strings.concat(helperbad, "/helperbad.ww"), "package helperbad;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(helperbad, "/helper_test.ww"), "package helperbad_test;\nimport nowhere;\nfn helper() void = { };\n"); si = 0; for (si < stages.len) { let helperav: []str = [driver(stages[si]), "test", helperbad]; runcommand(root, strings.concat("helper-only-", stages[si]), helperav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, "cannot find package nowhere")); si += 1; }; let helperwrong: str = strings.concat(root, "/helperwrong"); assert(os.mkdir(helperwrong, 448i32) == 0); writefile(strings.concat(helperwrong, "/helperwrong.ww"), "package helperwrong;\nfn value() void = { };\n"); writefile(strings.concat(helperwrong, "/wrong_test.ww"), "package unrelated;\nfn helper() void = { };\n"); let wrongav: []str = [driver("ww"), "test", helperwrong]; runcommand(root, "helper-wrong-package", wrongav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, "test package must match production package or _test")); // Package declarations are validation/kind data, not fixed driver storage. // A 256-byte selector must reach both stages intact and run under the same // reversible outside-root identity instead of preserving the old slot cap. let longdir: str = strings.concat(root, "/long-package"); assert(os.mkdir(longdir, 448i32) == 0); let chunk: str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; let longname: str = strings.concat(chunk, chunk, chunk, chunk, chunk, chunk, chunk, chunk); assert(longname.len == 256); writefile(strings.concat(longdir, "/long.ww"), strings.concat("package ", longname, ";\nfn value() void = { };\n")); writefile(strings.concat(longdir, "/long_test.ww"), strings.concat( "package ", longname, ";\n", "@test fn long_package_runs() void = { value(); };\n")); let longc: []str = [driver("ww"), "test", longdir]; let longw: []str = [driver("ww_ww"), "test", longdir]; let outc: commandout; let outw: commandout; runcommand(root, "long-package-c", longc, (30i64 * (time.second: i64)): time.duration, &outc); runcommand(root, "long-package-ww", longw, (30i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 0); expectexit(&outw, 0); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); assert(outc.stderr.len == 0); assert(has(outc.stdout, strings.concat(longname, ".long_package_runs ... ok\n"))); let multiline: str = strings.concat(root, "/multiline-test"); assert(os.mkdir(multiline, 448i32) == 0); writefile(strings.concat(multiline, "/multiline.ww"), "package multiline;\nfn value() i32 = { return 42; };\n"); writefile(strings.concat(multiline, "/multiline_test.ww"), strings.concat("package multiline;\n@test\n", "/* lexer whitespace between attribute and declaration */\n", "fn actually_runs() void = { assert(value() == 42); };\n")); let multilineav: []str = [driver("ww"), "test", multiline]; runcommand(root, "multiline-test", multilineav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "multiline.actually_runs ... ok\n")); assert(!has(out.stdout, "[no test files]")); let commented: str = strings.concat(root, "/commented-test"); assert(os.mkdir(commented, 448i32) == 0); writefile(strings.concat(commented, "/commented.ww"), "package commented;\nfn value() i32 = { return 1; };\n"); writefile(strings.concat(commented, "/commented_test.ww"), strings.concat( "package commented;\n/*\n", "@test fn phantom() void = { assert(false); };\n", "*/\nfn helper() void = { };\n")); let commentedav: []str = [driver("ww"), "test", commented]; runcommand(root, "commented-test", commentedav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "ok ")); assert(!has(out.stdout, " [no test files]\n")); assert(!has(out.stdout, "phantom")); // Go's complete empty-package matrix: production-only, internal-only, // external-only, combined, test-only same, test-only external, and honest // mixed test-only directories are each one directory-owned result. let prodonly: str = strings.concat(root, "/shapeprod"); let internalonly: str = strings.concat(root, "/shapeinternal"); let externalonly: str = strings.concat(root, "/shapeexternal"); let both: str = strings.concat(root, "/shapeboth"); let testonlysame: str = strings.concat(root, "/tosame"); let testonlyexternal: str = strings.concat(root, "/toexternal"); let testonlymixed: str = strings.concat(root, "/tomixed"); let shapes: []str = [prodonly, internalonly, externalonly, both, testonlysame, testonlyexternal, testonlymixed]; let shi: i32 = 0; for (shi < shapes.len) { assert(os.mkdir(shapes[shi], 448i32) == 0); shi += 1; }; writefile(strings.concat(prodonly, "/prod.ww"), "package shapeprod;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(internalonly, "/prod.ww"), "package shapeinternal;\nfn value() i32 = { return 1; };\n"); writefile(strings.concat(internalonly, "/same_test.ww"), strings.concat( "package shapeinternal;\n", "@test fn internal_runs() void = { assert(value() == 1); };\n")); writefile(strings.concat(externalonly, "/prod.ww"), "package shapeexternal;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(externalonly, "/external_test.ww"), strings.concat( "package shapeexternal_test;\nimport shapeexternal;\n", "@test fn external_runs() void = { assert(shapeexternal.value() == 1); };\n")); writefile(strings.concat(both, "/prod.ww"), "package shapeboth;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(both, "/same_test.ww"), strings.concat( "package shapeboth;\n", "export fn helper() i32 = { return 2; };\n", "@test fn same_runs() void = { assert(value() == 1); };\n")); writefile(strings.concat(both, "/external_test.ww"), strings.concat( "package shapeboth_test;\nimport shapeboth;\n", "@test fn external_runs() void = { assert(shapeboth.helper() == 2); };\n")); writefile(strings.concat(testonlysame, "/same_test.ww"), "package tosame;\n@test fn same_only() void = { assert(true); };\n"); writefile(strings.concat(testonlyexternal, "/external_test.ww"), "package toexternal_test;\n@test fn external_only() void = { assert(true); };\n"); writefile(strings.concat(testonlymixed, "/same_test.ww"), strings.concat( "package tomixed;\nexport fn helper() i32 = { return 3; };\n", "@test fn same_test_only() void = { assert(helper() == 3); };\n")); writefile(strings.concat(testonlymixed, "/external_test.ww"), strings.concat( "package tomixed_test;\nimport tomixed;\n", "@test fn external_test_only() void = { assert(tomixed.helper() == 3); };\n")); let shaperefs: []str = ["", "", "", "", "", "", ""]; shi = 0; for (shi < shapes.len) { let stagei: i32 = 0; for (stagei < stages.len) { let shapeav: []str = [driver(stages[stagei]), "test", "-I", root, shapes[shi]]; runcommand(root, strings.concat("shape-", stages[stagei], "-", boundarypkgname(shi)), shapeav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); if (stagei == 0) { shaperefs[shi] = strings.dup(out.stdout); } else { assert(same(shaperefs[shi], out.stdout)); }; assert(out.stderr.len == 0); stagei += 1; }; shi += 1; }; assert(has(shaperefs[0], " [no test files]\n")); assert(occurrences(shaperefs[0], strings.concat( "? ", prodonly, " [no test files]\n")) == 1); assert(has(shaperefs[1], "shapeinternal.internal_runs ... ok\n")); assert(has(shaperefs[2], "shapeexternal.external_runs ... ok\n")); assert(occurrences(shaperefs[3], " ... ok\n") == 2); assert(has(shaperefs[4], "tosame.same_only ... ok\n")); assert(has(shaperefs[5], "toexternal.external_only ... ok\n")); assert(has(shaperefs[6], "tomixed.same_test_only ... ok\n")); assert(has(shaperefs[6], "tomixed.external_test_only ... ok\n")); let shapefamilies: []str = ["shapeprod", "shapeinternal", "shapeexternal", "shapeboth", "tosame", "toexternal", "tomixed"]; shi = 1; for (shi < shapes.len) { assert(occurrences(shaperefs[shi], strings.concat( "ok ", shapes[shi], " [", shapefamilies[shi], "]\n")) == 1); shi += 1; }; // The five non-combined real-test shapes have one structural product in // both stages. The combined case is proved in full by the graph observer. let shapecompilerwrapper: str = strings.concat(root, "/shape-w6c-wrapper.sh"); let shapelinkerwrapper: str = strings.concat(root, "/shape-w6l-wrapper.sh"); writeexecutable(shapecompilerwrapper, strings.concat( "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_SHAPE_COMPILER_TRACE\"\n", "exec \"$WW_SHAPE_REAL_COMPILER\" \"$@\"\n")); writeexecutable(shapelinkerwrapper, strings.concat( "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_SHAPE_LINKER_TRACE\"\n", "exec \"$WW_SHAPE_REAL_LINKER\" \"$@\"\n")); let shapeindices: []i32 = [1, 2, 4, 5, 6]; let shapemains: []str = ["shapeinternal-test-main", "shapeexternal-test-main", "tosame-test-main", "toexternal-test-main", "tomixed-test-main"]; let shapeordinary: []str = ["shapeinternal", "shapeexternal", "tosame", "toexternal", "tomixed"]; let shapeordinarypresent: []bool = [false, true, false, false, false]; let shapeinternal: []str = ["shapeinternal-internal-test", "shapeexternal-internal-test", "tosame-internal-test", "toexternal-internal-test", "tomixed-internal-test"]; let shapeinternalpresent: []bool = [true, false, true, false, true]; let shapeexternal: []str = ["shapeinternal_test-external-test", "shapeexternal_test-external-test", "tosame_test-external-test", "toexternal_test-external-test", "tomixed_test-external-test"]; let shapeexternalpresent: []bool = [false, true, false, true, true]; let shapetargetfirst: []str = ["shapeinternal", "shapeexternal", "tosame", "toexternal_test", "tomixed"]; let shapetargetsecond: []str = ["", "shapeexternal_test", "", "", "tomixed_test"]; let shapeproductionselector: []str = ["shapeinternal", "shapeexternal", "-", "-", "-"]; let shapeinternalselector: []str = ["shapeinternal", "-", "tosame", "-", "tomixed"]; let shapeexternalselector: []str = ["-", "shapeexternal_test", "-", "toexternal_test", "tomixed_test"]; let shapecompilerrefs: []str = ["", "", "", "", ""]; let shapelinkerrefs: []str = ["", "", "", "", ""]; let shapemainrefs: []str = ["", "", "", "", ""]; let shapebinrefs: []str = ["", "", "", "", ""]; let shapecompilers: []str = ["w6c", "w6c_ww"]; let shapelinkers: []str = ["w6l", "w6l_ww"]; let shapebaseenv: []str = os.getenvs(); let sj: i32 = 0; for (sj < shapeindices.len) { let index: i32 = shapeindices[sj]; let shapebin: str = strings.concat(root, "/shape-product-", boundarypkgname(sj), ".test"); let shapework: str = strings.concat(shapebin, ".sepwork"); let shapestatus: str = strings.concat(shapebin, ".status"); let ctracepath: str = strings.concat(root, "/shape-compiler-", boundarypkgname(sj), ".trace"); let ltracepath: str = strings.concat(root, "/shape-linker-", boundarypkgname(sj), ".trace"); writefile(ctracepath, ""); writefile(ltracepath, ""); let stagei: i32 = 0; for (stagei < stages.len) { assert(os.mkdir(shapework, 448i32) == 0); if (stagei != 0) { rewritefile(ctracepath, ""); rewritefile(ltracepath, ""); }; let env: []str = alloc([], (shapebaseenv.len + 6): u64)!; let ei: i32 = 0; for (ei < shapebaseenv.len) { if (!strings.hasprefix(shapebaseenv[ei], "WW_W6C=") && !strings.hasprefix(shapebaseenv[ei], "WW_W6L=") && !strings.hasprefix(shapebaseenv[ei], "WW_SHAPE_COMPILER_TRACE=") && !strings.hasprefix(shapebaseenv[ei], "WW_SHAPE_LINKER_TRACE=") && !strings.hasprefix(shapebaseenv[ei], "WW_SHAPE_REAL_COMPILER=") && !strings.hasprefix(shapebaseenv[ei], "WW_SHAPE_REAL_LINKER=")) { append(env, shapebaseenv[ei]); }; ei += 1; }; append(env, strings.concat("WW_W6C=", shapecompilerwrapper)); append(env, strings.concat("WW_W6L=", shapelinkerwrapper)); append(env, strings.concat("WW_SHAPE_COMPILER_TRACE=", ctracepath)); append(env, strings.concat("WW_SHAPE_LINKER_TRACE=", ltracepath)); append(env, strings.concat("WW_SHAPE_REAL_COMPILER=", driver(shapecompilers[stagei]))); append(env, strings.concat("WW_SHAPE_REAL_LINKER=", driver(shapelinkers[stagei]))); let shapeav: []str = [driver(stages[stagei]), "test", "-c", "-w", shapework, "-I", root, "--ww-package-test", "test", shapefamilies[index], shapeproductionselector[sj], shapeinternalselector[sj], shapeexternalselector[sj], shapes[index], shapebin, "-", shapestatus, shapes[index]]; runcommandenv(root, strings.concat("shape-structure-", stages[stagei], "-", boundarypkgname(sj)), shapeav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0); assert(os.exists(shapebin)); assert(!os.exists(strings.concat(shapes[index], "/", shapefamilies[index], ".test"))); let ctrace: str = readfile(ctracepath); let ltrace: str = readfile(ltracepath); assert(occurrences(ctrace, "-test-main.unit.new") == 1); let mainline: str = linecontaining(ctrace, strings.concat("/", shapemains[sj], ".unit.new")); let targetcount: i32 = 1; if (shapetargetsecond[sj].len != 0) { targetcount = 2; }; assert(occurrences(mainline, "--test-target-package") == targetcount); assert(has(mainline, strings.concat("--test-target-package ", shapetargetfirst[sj], " "))); if (shapetargetsecond[sj].len != 0) { assert(pos(mainline, strings.concat("--test-target-package ", shapetargetfirst[sj], " ")) < pos(mainline, strings.concat( "--test-target-package ", shapetargetsecond[sj], " "))); }; assert(occurrences(ltrace, "\n") == 1); let linkline: str = linecontaining(ltrace, strings.concat("-o ", shapebin, ".new ")); assert(occurrences(linkline, "-test-main.a") == 1); assert(has(linkline, strings.concat("/", shapemains[sj], ".a"))); assert(!has(linkline, ".wwi")); assert(os.exists(strings.concat(shapework, "/", shapemains[sj], ".unit.ww"))); let actionnames: []str = [shapeordinary[sj], shapeinternal[sj], shapeexternal[sj]]; let actionpresent: []bool = [shapeordinarypresent[sj], shapeinternalpresent[sj], shapeexternalpresent[sj]]; let actioni: i32 = 0; for (actioni < actionnames.len) { assert(os.exists(strings.concat(shapework, "/", actionnames[actioni], ".unit.ww")) == actionpresent[actioni]); assert(has(linkline, strings.concat("/", actionnames[actioni], ".a")) == actionpresent[actioni]); actioni += 1; }; let nc: str = normalizedtrace(ctrace, strings.concat(shapework, "/"), shapebin); let nl: str = normalizedtrace(ltrace, strings.concat(shapework, "/"), shapebin); let mainbytes: str = readfile(strings.concat(shapework, "/", shapemains[sj], ".unit.ww")); let binbytes: str = readfile(shapebin); if (stagei == 0) { shapecompilerrefs[sj] = nc; shapelinkerrefs[sj] = nl; shapemainrefs[sj] = mainbytes; shapebinrefs[sj] = binbytes; } else { assert(same(shapecompilerrefs[sj], nc)); assert(same(shapelinkerrefs[sj], nl)); assert(same(shapemainrefs[sj], mainbytes)); assert(same(shapebinrefs[sj], binbytes)); }; clean(shapework); clean(shapebin); clean(shapestatus); stagei += 1; }; sj += 1; }; clean(root); }; @test fn symlink_package_root_reuses_canonical_directory() void = { let root: str = fresh(); let real: str = deeppunctuationpath(root, "symlink target"); let link: str = strings.concat(root, "/link"); let wrapper: str = strings.concat(root, "/compiler wrapper.sh"); mkdirall(real); writefile(strings.concat(real, "/real.ww"), "package real;\nexport fn value() int = { return 1; };\n"); writefile(strings.concat(real, "/real_test.ww"), "package real;\n@test fn value_is_one() void = { assert(value() == 1); };\n"); let lav: []str = ["/bin/ln", "-s", real, link]; let out: commandout; runcommand(root, "link", lav, time.second, &out); expectexit(&out, 0); writeexecutable(wrapper, strings.concat( "#!/bin/sh\n", "printf 'BEGIN' >> \"$WW_SYMLINK_COMPILER_TRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_SYMLINK_COMPILER_TRACE\"; done\n", "printf '\\n' >> \"$WW_SYMLINK_COMPILER_TRACE\"\n", "exec \"$WW_SYMLINK_REAL_COMPILER\" \"$@\"\n")); let actionid: str = localidentity(real, "real"); assert(actionid.len > 255); let action: str = packagestoragekey(actionid, real, 1, 0); assert(action.len + 9 <= 255); let stages: []str = ["ww", "ww_ww"]; let compilers: []str = ["w6c", "w6c_ww"]; let references: []str = ["", "", ""]; let baseenv: []str = os.getenvs(); let si: i32 = 0; for (si < stages.len) { let work: str = strings.concat(root, "/work-", stages[si]); let trace: str = strings.concat(root, "/trace-", stages[si]); assert(os.mkdir(work, 448i32) == 0); writefile(trace, ""); let env: []str = alloc([], (baseenv.len + 3): u64)!; let ei: i32 = 0; for (ei < baseenv.len) { if (!strings.hasprefix(baseenv[ei], "WW_W6C=") && !strings.hasprefix(baseenv[ei], "WW_SYMLINK_COMPILER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_SYMLINK_REAL_COMPILER=")) { append(env, baseenv[ei]); }; ei += 1; }; append(env, strings.concat("WW_W6C=", wrapper)); append(env, strings.concat("WW_SYMLINK_COMPILER_TRACE=", trace)); append(env, strings.concat("WW_SYMLINK_REAL_COMPILER=", driver(compilers[si]))); let av: []str = [driver(stages[si]), "test", "-w", work, real]; runcommandenv(root, strings.concat("real-cold-", stages[si]), av, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "real.value_is_one ... ok\n")); let coldtrace: str = readfile(trace); assert(coldtrace.len > 0); assert(has(coldtrace, strings.concat("<--import><", actionid, "><"))); let key: str = work; assert(os.exists(key)); let suffixes: []str = [".unit.ww", ".wwi", ".a"]; let xi: i32 = 0; for (xi < suffixes.len) { let artifact: str = readfile(strings.concat(key, "/", action, suffixes[xi])); if (xi == 0) { assert(has(artifact, strings.concat("//ww:module-reset ", actionid, "\n"))); } else { if (xi == 1) { assert(strings.hasprefix(artifact, strings.concat("//ww:module ", actionid, "\n"))); }; }; if (si == 0) { references[xi] = strings.dup(artifact); } else { assert(same(references[xi], artifact)); }; xi += 1; }; let linkav: []str = [driver(stages[si]), "test", "-w", work, link]; runcommandenv(root, strings.concat("symlink-warm-", stages[si]), linkav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "real.value_is_one ... ok\n")); assert(same(coldtrace, readfile(trace))); xi = 0; for (xi < suffixes.len) { assert(same(references[xi], readfile(strings.concat(key, "/", action, suffixes[xi])))); xi += 1; }; let relative: str = strings.concat(".", strings.sub(real, root.len, real.len)); let relativeav: []str = [driver(stages[si]), "test", "-w", work, relative]; runcommandenvdir(root, strings.concat("relative-warm-", stages[si]), relativeav, env, root, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "real.value_is_one ... ok\n")); assert(same(coldtrace, readfile(trace))); xi = 0; for (xi < suffixes.len) { assert(same(references[xi], readfile(strings.concat(key, "/", action, suffixes[xi])))); xi += 1; }; si += 1; }; clean(root); }; @test fn pass_skip_and_expected_abort() void = { let root: str = fresh(); let av: []str = [driver("ww"), "test", packagepath("cases/pass")]; let out: commandout; runcommand(root, "pass", av, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "pass.alpha_pass ... ok\n")); assert(has(out.stdout, "pass.beta_skip ... SKIP: synthetic unavailable feature\n")); assert(has(out.stdout, "pass.gamma_expected_abort ... ok\n")); assert(has(out.stdout, "pass.delta_pass ... ok\n")); assert(has(out.stdout, "3 passed, 0 failed, 1 skipped, 0 harness errors\n")); assert(has(out.stdout, "4 discovered, 4 selected, 4 started, 4 completed\n")); clean(root); }; @test fn failures_are_distinct_and_clamped() void = { let root: str = fresh(); let out: commandout; let av: []str = [driver("ww"), "test", packagepath("cases/assert_failure")]; runcommand(root, "assert", av, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stdout, strings.concat( "assert_failure.assertion_failure ... synthetic assertion failure", "FAIL (exit 1)\n"))); assert(out.stderr.len == 0); let nonzeroav: []str = [driver("ww"), "test", packagepath("cases/nonzero")]; runcommand(root, "nonzero", nonzeroav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stdout, "nonzero.deliberate_nonzero_exit ... FAIL (exit 7)\n")); assert(out.stderr.len == 0); let prematureav: []str = [driver("ww"), "test", packagepath("cases/premature")]; runcommand(root, "premature", prematureav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stdout, "premature.clean_exit_without_completion ... HARNESS (incomplete result)\n")); assert(out.stderr.len == 0); let signalav: []str = [driver("ww"), "test", packagepath("cases/signal")]; runcommand(root, "signal", signalav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stdout, "signal.signal_is_not_exit ... FAIL (signal 15)\n")); assert(out.stderr.len == 0); let multiav: []str = [driver("ww"), "test", packagepath("cases/multi_fail")]; runcommand(root, "multifail", multiav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stdout, "1 passed, 2 failed, 0 skipped, 0 harness errors\n")); assert(has(out.stdout, "3 discovered, 3 selected, 3 started, 3 completed\n")); assert(out.stderr.len == 0); clean(root); }; @test fn timeout_and_descendant_cleanup() void = { let root: str = fresh(); let av: []str = [driver("ww"), "test", "-timeout-ms=100", packagepath("cases/timeout")]; let out: commandout; runcommand(root, "timeout", av, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stdout, "timeout.timeout_clears_term_resistant_descendant ... FAIL(timeout)\n")); assert(has(out.stdout, "1 discovered, 1 selected, 1 started, 1 completed\n")); let descendantav: []str = [driver("ww"), "test", packagepath("cases/descendant")]; runcommand(root, "descendant", descendantav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "descendant.normal_return_clears_descendant ... ok\n")); assert(!has(out.stdout, "HARNESS")); clean(root); }; @test fn cstage_wwstage_behavior_and_assembly_match() void = { let root: str = fresh(); let route: str = strings.concat(root, "/route"); assert(os.mkdir(route, 448i32) == 0); writefile(strings.concat(route, "/route.ww"), "package route;\nexport fn value() int = { return 7; };\n"); writefile(strings.concat(route, "/a_test.ww"), "package route;\n@test fn white() void = { assert(value() == 7); };\n"); writefile(strings.concat(route, "/z_test.ww"), "package route_test;\nimport route;\n@test fn external() void = { assert(route.value() == 7); };\n"); let outc: commandout; let outw: commandout; let listc: []str = [driver("ww"), "test", "-I", root, "-list", route]; let listw: []str = [driver("ww_ww"), "test", "-I", root, "-list", route]; runcommand(root, "list-c", listc, (30i64 * (time.second: i64)): time.duration, &outc); runcommand(root, "list-ww", listw, (30i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 0); expectexit(&outw, 0); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); let misusec: []str = [driver("ww"), "test", "-run"]; let misusew: []str = [driver("ww_ww"), "test", "-run"]; runcommand(root, "misuse-c", misusec, time.second, &outc); runcommand(root, "misuse-ww", misusew, time.second, &outw); expectexit(&outc, 2); expectexit(&outw, 2); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stderr, "ww test: -run needs an argument\n")); let timeoutc: []str = [driver("ww"), "test", "-timeout-ms=4000000", "-I", root, route]; let timeoutw: []str = [driver("ww_ww"), "test", "-timeout-ms=4000000", "-I", root, route]; runcommand(root, "timeout-misuse-c", timeoutc, time.second, &outc); runcommand(root, "timeout-misuse-ww", timeoutw, time.second, &outw); expectexit(&outc, 2); expectexit(&outw, 2); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stderr, "usage: wwtest package")); let cwork: str = strings.concat(root, "/compile-c-work"); let wwork: str = strings.concat(root, "/compile-ww-work"); let cbinarypath: str = strings.concat(root, "/route-c.test"); let wbinarypath: str = strings.concat(root, "/route-ww.test"); let cc: []str = [driver("ww"), "test", "-w", cwork, "-o", cbinarypath, "-I", root, route]; runcommand(root, "compile-c", cc, (30i64 * (time.second: i64)): time.duration, &outc); expectexit(&outc, 0); assert(has(outc.stdout, "route.white ... ok\n")); assert(has(outc.stdout, "route.external ... ok\n")); let cwhite: str = readfile(strings.concat(cwork, "/route-internal-test.s")); let cexternal: str = readfile(strings.concat(cwork, "/route_test-external-test.s")); let cmain: str = readfile(strings.concat(cwork, "/route-test-main.s")); let cbinary: str = readfile(cbinarypath); let wc: []str = [driver("ww_ww"), "test", "-w", wwork, "-o", wbinarypath, "-I", root, route]; runcommand(root, "compile-ww", wc, (30i64 * (time.second: i64)): time.duration, &outw); expectexit(&outw, 0); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); assert(same(cwhite, readfile(strings.concat(wwork, "/route-internal-test.s")))); assert(same(cexternal, readfile(strings.concat(wwork, "/route_test-external-test.s")))); assert(same(cmain, readfile(strings.concat(wwork, "/route-test-main.s")))); assert(same(cbinary, readfile(wbinarypath))); clean(root); }; @test fn directory_package_graph_variants() void = { let root: str = fresh(); let implementation: str = strings.concat(root, "/implementation"); let api: str = strings.concat(root, "/api"); let samedep: str = strings.concat(root, "/__same"); let external: str = strings.concat(root, "/__external"); let bridge: str = strings.concat(root, "/bridge"); let pkg: str = strings.concat(root, "/pkg"); let dirs: []str = [implementation, api, samedep, external, bridge, pkg]; let di: i32 = 0; for (di < dirs.len) { assert(os.mkdir(dirs[di], 448i32) == 0); di += 1; }; writefile(strings.concat(implementation, "/implementation.ww"), strings.concat("package implementation;\n", "// IMPLEMENTATION_SOURCE\n", "export fn value() i32 = { return 41; };\n")); writefile(strings.concat(api, "/api.ww"), strings.concat( "package api;\nimport implementation;\n", "// API_SOURCE\n", "export fn value() i32 = { return implementation.value(); };\n")); writefile(strings.concat(api, "/ignored_test.ww"), strings.concat( "package api;\n", "TEST_DEPENDENCY_MUST_NOT_COMPILE\n")); writefile(strings.concat(samedep, "/same.ww"), "package __same;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(external, "/external.ww"), "package __external;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(pkg, "/a.ww"), strings.concat( "package pkg;\nimport api;\n", "// PACKAGE_PRODUCTION_A\n", "export fn value() i32 = { return api.value(); };\n")); writefile(strings.concat(pkg, "/z.ww"), strings.concat( "package pkg;\n", "// PACKAGE_PRODUCTION_Z\n", "fn secret() i32 = { return 6; };\n", "export fn second() i32 = { return 9; };\n")); writefile(strings.concat(bridge, "/bridge.ww"), strings.concat( "package bridge;\nimport pkg;\n", "// TRANSITIVE_TEST_IMPORTER\n", "export fn helper() i32 = { return pkg.internal_helper(); };\n")); let samebody: str = strings.concat( "package pkg;\nimport __same;\n", "// SAME_TEST_SOURCE\n", "export fn internal_helper() i32 = { return 6; };\n", "@test fn same_graph() void = {\n", " assert(value() + __same.value() == 42);\n", " assert(secret() == 6);\n", "};\n"); let samebodychanged: str = strings.concat( "package pkg;\nimport __same;\n", "// SAME_TEST_SOURCE_BODY_CHANGED\n", "export fn internal_helper() i32 = { return 7; };\n", "@test fn same_graph() void = {\n", " assert(value() + __same.value() == 42);\n", " assert(secret() == 6);\n", "};\n"); let samebodyexported: str = strings.concat( "package pkg;\nimport __same;\n", "// SAME_TEST_SOURCE_EXPORT_CHANGED\n", "export fn internal_helper() i32 = { return 7; };\n", "export fn second_helper() i32 = { return 8; };\n", "@test fn same_graph() void = {\n", " assert(value() + __same.value() == 42);\n", " assert(secret() == 6);\n", "};\n"); let externalbody: str = strings.concat( "package pkg_test;\nimport __external;\nimport bridge;\nimport pkg;\n", "// EXTERNAL_TEST_SOURCE\n", "@test fn external_graph() void = {\n", " assert(pkg.value() + __external.value() == 42);\n", " assert(pkg.second() == 9);\n", " assert(pkg.internal_helper() >= 6);\n", " assert(bridge.helper() >= 6);\n", "};\n"); let externalbodychanged: str = strings.concat( "package pkg_test;\nimport __external;\nimport bridge;\nimport pkg;\n", "// EXTERNAL_TEST_SOURCE_BODY_CHANGED\n", "@test fn external_graph() void = {\n", " assert(pkg.value() + __external.value() == 42);\n", " assert(pkg.second() == 9);\n", " assert(pkg.internal_helper() >= 7);\n", " assert(bridge.helper() >= 7);\n", "};\n"); let externalbodyreadded: str = strings.concat( "package pkg_test;\nimport __external;\nimport bridge;\nimport pkg;\n", "// EXTERNAL_TEST_SOURCE_READDED\n", "@test fn external_graph() void = {\n", " assert(pkg.value() + __external.value() == 42);\n", " assert(pkg.second() == 9);\n", " assert(pkg.internal_helper() >= 7);\n", " assert(bridge.helper() >= 7);\n", " assert(__external.value() == 1);\n", "};\n"); let samefile: str = strings.concat(pkg, "/same_test.ww"); let externalfile: str = strings.concat(pkg, "/external_test.ww"); writefile(samefile, samebody); writefile(externalfile, externalbody); // Ordinary builds neither parse nor load any test-only source or dependency. let prodout: str = strings.concat(root, "/production.a"); let prodav: []str = [driver("ww"), "build", "-I", root, "-o", prodout, pkg]; let out: commandout; runcommand(root, "production-build", prodav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let prodwork: str = strings.concat(prodout, ".sepwork/"); let produnit: str = readfile(strings.concat(prodwork, "pkg.unit.ww")); assert(has(produnit, "PACKAGE_PRODUCTION_A")); assert(has(produnit, "PACKAGE_PRODUCTION_Z")); assert(!has(produnit, "SAME_TEST_SOURCE")); assert(!has(produnit, "internal_helper")); assert(!os.exists(strings.concat(prodwork, "__same.unit.ww"))); assert(!os.exists(strings.concat(prodwork, "__external.unit.ww"))); assert(!os.exists(strings.concat(prodwork, "bridge.unit.ww"))); assert(!has(readfile(strings.concat(prodwork, "api.unit.ww")), "TEST_DEPENDENCY_MUST_NOT_COMPILE")); let compilerwrapper: str = strings.concat(root, "/trace-w6c.sh"); let assemblerwrapper: str = strings.concat(root, "/trace-w6a.sh"); let linkerwrapper: str = strings.concat(root, "/trace-w6l.sh"); writeexecutable(compilerwrapper, strings.concat( "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_GRAPH_COMPILER_TRACE\"\n", "exec \"$WW_GRAPH_REAL_COMPILER\" \"$@\"\n")); writeexecutable(assemblerwrapper, strings.concat( "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_GRAPH_ASSEMBLER_TRACE\"\n", "exec \"$WW_GRAPH_REAL_ASSEMBLER\" \"$@\"\n")); writeexecutable(linkerwrapper, strings.concat( "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_GRAPH_LINKER_TRACE\"\n", "exec \"$WW_GRAPH_REAL_LINKER\" \"$@\"\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 works: []str = [strings.concat(root, "/c-work"), strings.concat(root, "/ww-work")]; let bins: []str = [strings.concat(root, "/c-pkg.test"), strings.concat(root, "/ww-pkg.test")]; let statuses: []str = [strings.concat(root, "/c-pkg.status"), strings.concat(root, "/ww-pkg.status")]; let compilertraces: []str = [strings.concat(root, "/c-w6c.trace"), strings.concat(root, "/ww-w6c.trace")]; let assemblertraces: []str = [strings.concat(root, "/c-w6a.trace"), strings.concat(root, "/ww-w6a.trace")]; let linkertraces: []str = [strings.concat(root, "/c-w6l.trace"), strings.concat(root, "/ww-w6l.trace")]; let bridgeowner: str = strings.concat("pkg#", pkg); let bridgeaction: str = packagestoragekeyfor( "bridge", bridge, 4, 0, bridgeowner); assert(strings.hasprefix(bridgeaction, "__wwpkg.v4.r0.h")); let actions: []str = ["implementation", "api", "__same", "__external", bridgeaction, "test", "pkg-internal-test", "pkg_test-external-test", "pkg-test-main"]; let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"]; let references: []str = alloc([], (actions.len * suffixes.len): u64)!; let ai: i32 = 0; for (ai < actions.len * suffixes.len) { append(references, ""); ai += 1; }; let referencebin: str = ""; let referenceout: str = ""; let referenceerr: str = ""; let referencecompiler: str = ""; let referenceassembler: str = ""; let referencelinker: str = ""; let bodycompiler: str = ""; let bodyassembler: str = ""; let bodylinker: str = ""; let externalcompiler: str = ""; let externalassembler: str = ""; let externallinker: str = ""; let exportcompiler: str = ""; let exportassembler: str = ""; let exportlinker: str = ""; let exportbridge: []str = ["", "", "", "", ""]; let removecompiler: str = ""; let removeassembler: str = ""; let removelinker: str = ""; let removebin: str = ""; let removeout: str = ""; let removeerr: str = ""; let readdcompiler: str = ""; let readdassembler: str = ""; let readdlinker: str = ""; let readdbin: str = ""; let readdout: str = ""; let readderr: str = ""; let baseenv: []str = os.getenvs(); let si: i32 = 0; for (si < stages.len) { assert(os.mkdir(works[si], 448i32) == 0); writefile(compilertraces[si], ""); writefile(assemblertraces[si], ""); writefile(linkertraces[si], ""); let env: []str = alloc([], (baseenv.len + 9): 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_GRAPH_COMPILER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_GRAPH_ASSEMBLER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_GRAPH_LINKER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_GRAPH_REAL_COMPILER=") && !strings.hasprefix(baseenv[ei], "WW_GRAPH_REAL_ASSEMBLER=") && !strings.hasprefix(baseenv[ei], "WW_GRAPH_REAL_LINKER=")) { 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_GRAPH_COMPILER_TRACE=", compilertraces[si])); append(env, strings.concat("WW_GRAPH_ASSEMBLER_TRACE=", assemblertraces[si])); append(env, strings.concat("WW_GRAPH_LINKER_TRACE=", linkertraces[si])); append(env, strings.concat("WW_GRAPH_REAL_COMPILER=", driver(compilers[si]))); append(env, strings.concat("WW_GRAPH_REAL_ASSEMBLER=", driver(assemblers[si]))); append(env, strings.concat("WW_GRAPH_REAL_LINKER=", driver(linkers[si]))); let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si], "-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg", "pkg_test", pkg, bins[si], "-", statuses[si], pkg]; runcommandenv(root, strings.concat("graph-cold-", stages[si]), av, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(os.exists(bins[si])); assert(same(readfile(statuses[si]), "ok\n")); assert(!os.exists(strings.concat(pkg, "/pkg_test.test"))); assert(!os.exists(strings.concat(works[si], "/pkg.unit.ww"))); assert(!os.exists(strings.concat(works[si], "/bridge.unit.ww"))); let ptestunit: str = readfile(strings.concat(works[si], "/pkg-internal-test.unit.ww")); let pxtestunit: str = readfile(strings.concat(works[si], "/pkg_test-external-test.unit.ww")); let mainunit: str = readfile(strings.concat(works[si], "/pkg-test-main.unit.ww")); assert(has(ptestunit, "PACKAGE_PRODUCTION_A")); assert(has(ptestunit, "PACKAGE_PRODUCTION_Z")); assert(has(ptestunit, "SAME_TEST_SOURCE")); assert(has(ptestunit, "export fn internal_helper")); assert(!has(ptestunit, "EXTERNAL_TEST_SOURCE")); assert(has(pxtestunit, "EXTERNAL_TEST_SOURCE")); assert(!has(pxtestunit, "PACKAGE_PRODUCTION_A")); assert(!has(pxtestunit, "SAME_TEST_SOURCE")); assert(strings.hasprefix(mainunit, strings.concat( "//ww:module-reset __wwtestmain.pkg.main\n", "package main;\nimport pkg;\nimport pkg_test;\nimport test;\n"))); let ctrace: str = readfile(compilertraces[si]); let atrace: str = readfile(assemblertraces[si]); let ltrace: str = readfile(linkertraces[si]); assert(occurrences(ctrace, "pkg-internal-test.unit.new") == 1); assert(occurrences(ctrace, "pkg_test-external-test.unit.new") == 1); assert(occurrences(ctrace, "pkg-test-main.unit.new") == 1); assert(occurrences(ctrace, "--test-target-package pkg --test-target-package pkg_test") == 1); assert(!has(ctrace, "--package-init-symbol __ww..pkg.p.pkg.v0.r0.init")); let ptestcompile: str = linecontaining(ctrace, "pkg-internal-test.unit.new"); assert(has(ptestcompile, "--package-init-symbol __ww..pkg.p.pkg.v1.r0.init")); assert(pos(ptestcompile, "--import __same ") < pos(ptestcompile, "--import api ")); let bridgecompile: str = linecontaining(ctrace, "--package-init-symbol __ww..pkg.p.bridge.v4.r0.init"); assert(has(bridgecompile, "--import pkg ")); assert(has(bridgecompile, "/pkg-internal-test.wwi.new")); assert(!has(ctrace, "--package-init-symbol __ww..pkg.p.bridge.v0.r0.init")); let externalcompile: str = linecontaining(ctrace, "pkg_test-external-test.unit.new"); assert(pos(externalcompile, "--import __external ") < pos(externalcompile, "--import bridge ")); assert(pos(externalcompile, "--import bridge ") < pos(externalcompile, "--import pkg ")); assert(has(externalcompile, "/pkg-internal-test.wwi.new")); assert(!has(externalcompile, "/pkg.wwi")); let maincompile: str = linecontaining(ctrace, "pkg-test-main.unit.new"); assert(has(maincompile, strings.concat( "-T --entry --test-support-module test ", "--test-target-package pkg --test-target-package pkg_test ", "--package-init-symbol ", "__ww..pkg.p.__wwtestmain.pkg.main.v3.r2.init "))); assert(pos(maincompile, "--import pkg ") < pos(maincompile, "--import pkg_test ")); assert(pos(maincompile, "--import pkg_test ") < pos(maincompile, "--import test ")); let workprefix: str = strings.concat(works[si], "/"); assert(same(ptestcompile, strings.concat( "--test-package --package-init-symbol ", "__ww..pkg.p.pkg.v1.r0.init -c --import __same ", workprefix, "__same.wwi.new --import api ", workprefix, "api.wwi.new -I ", workprefix, "pkg-internal-test.wwi.new -o ", workprefix, "pkg-internal-test.s.new ", workprefix, "pkg-internal-test.unit.new"))); assert(same(bridgecompile, strings.concat( "--package-init-symbol __ww..pkg.p.bridge.v4.r0.init -c ", "--import pkg ", workprefix, "pkg-internal-test.wwi.new -I ", workprefix, bridgeaction, ".wwi.new -o ", workprefix, bridgeaction, ".s.new ", workprefix, bridgeaction, ".unit.new"))); assert(same(externalcompile, strings.concat( "--test-package --package-init-symbol ", "__ww..pkg.p.pkg_test.v2.r0.init -c --import __external ", workprefix, "__external.wwi.new --import bridge ", workprefix, bridgeaction, ".wwi.new --import pkg ", workprefix, "pkg-internal-test.wwi.new -I ", workprefix, "pkg_test-external-test.wwi.new -o ", workprefix, "pkg_test-external-test.s.new ", workprefix, "pkg_test-external-test.unit.new"))); assert(same(maincompile, strings.concat( "-T --entry --test-support-module test --test-target-package pkg ", "--test-target-package pkg_test --package-init-symbol ", "__ww..pkg.p.__wwtestmain.pkg.main.v3.r2.init ", "--init-dispatch-symbol __ww..dispatch -c --import pkg ", workprefix, "pkg-internal-test.wwi.new --import pkg_test ", workprefix, "pkg_test-external-test.wwi.new --import test ", workprefix, "test.wwi.new -I ", workprefix, "pkg-test-main.wwi.new -o ", workprefix, "pkg-test-main.s.new ", workprefix, "pkg-test-main.unit.new"))); let supportcompile: str = linecontaining(ctrace, "--test-support-module test --package-init-symbol"); assert(same(supportcompile, strings.concat( "--test-support-module test --package-init-symbol ", "__ww..pkg.p.test.v0.r0.init -c --import fnmatch ", workprefix, "fnmatch.wwi.new --import os ", workprefix, "os.wwi.new --import time ", workprefix, "time.wwi.new -I ", workprefix, "test.wwi.new -o ", workprefix, "test.s.new ", workprefix, "test.unit.new"))); assert(same(linecontaining(atrace, "/pkg-internal-test.o.new"), strings.concat("-o ", works[si], "/pkg-internal-test.o.new ", works[si], "/pkg-internal-test.s.new"))); assert(same(linecontaining(atrace, "/pkg-test-main.init.o.new"), strings.concat("-o ", works[si], "/pkg-test-main.init.o.new ", works[si], "/pkg-test-main.init.s.new"))); let exactassembled: []str = [bridgeaction, "pkg_test-external-test", "pkg-test-main", "test"]; let exacti: i32 = 0; for (exacti < exactassembled.len) { assert(same(linecontaining(atrace, strings.concat("/", exactassembled[exacti], ".o.new")), strings.concat("-o ", workprefix, exactassembled[exacti], ".o.new ", workprefix, exactassembled[exacti], ".s.new"))); exacti += 1; }; assert(occurrences(ltrace, "\n") == 1); let link: str = linecontaining(ltrace, strings.concat("-o ", bins[si], ".new ")); assert(has(link, "/pkg-test-main.a.new")); assert(has(link, "/pkg-internal-test.a.new")); assert(has(link, "/pkg_test-external-test.a.new")); assert(has(link, "/__same.a.new")); assert(has(link, "/__external.a.new")); assert(has(link, "/api.a.new")); assert(has(link, "/implementation.a.new")); assert(has(link, "/test.a.new")); assert(has(link, strings.concat("/", bridgeaction, ".a.new"))); assert(!has(link, strings.concat(works[si], "/pkg.a"))); assert(!has(link, ".wwi")); assert(same(link, strings.concat("-o ", bins[si], ".new ", workprefix, "pkg-test-main.a.new ", workprefix, "test.a.new ", workprefix, "fnmatch.a.new ", workprefix, "ascii.a.new ", workprefix, "strings.a.new ", workprefix, "os.a.new ", workprefix, "time.a.new ", workprefix, "encoding.utf8.a.new ", workprefix, "bytes.a.new ", workprefix, "types.a.new ", workprefix, "pkg_test-external-test.a.new ", workprefix, bridgeaction, ".a.new ", workprefix, "__external.a.new ", workprefix, "pkg-internal-test.a.new ", workprefix, "api.a.new ", workprefix, "implementation.a.new ", workprefix, "__same.a.new ", repo(), "/out/bin/../lib/libwwrt.a"))); ai = 0; for (ai < actions.len) { let archive: str = readfile(strings.concat(works[si], "/", actions[ai], ".a")); checkactionarchive(archive, strings.compare(actions[ai], "pkg-test-main") == 0); let xi: i32 = 0; for (xi < suffixes.len) { let bytes: str = readfile(strings.concat(works[si], "/", actions[ai], suffixes[xi])); let index: i32 = ai * suffixes.len + xi; if (si == 0) { references[index] = strings.dup(bytes); } else { assert(same(references[index], bytes)); }; xi += 1; }; ai += 1; }; let normalizedcompiler: str = normalizedtrace(ctrace, strings.concat(works[si], "/"), bins[si]); let normalizedassembler: str = normalizedtrace(atrace, strings.concat(works[si], "/"), bins[si]); let normalizedlinker: str = normalizedtrace(ltrace, strings.concat(works[si], "/"), bins[si]); if (si == 0) { referencebin = readfile(bins[si]); referenceout = strings.dup(out.stdout); referenceerr = strings.dup(out.stderr); referencecompiler = normalizedcompiler; referenceassembler = normalizedassembler; referencelinker = normalizedlinker; } else { assert(same(referencebin, readfile(bins[si]))); assert(same(referenceout, out.stdout)); assert(same(referenceerr, out.stderr)); assert(same(referencecompiler, normalizedcompiler)); assert(same(referenceassembler, normalizedassembler)); assert(same(referencelinker, normalizedlinker)); }; let runav: []str = [bins[si]]; runcommand(root, strings.concat("graph-run-", stages[si]), runav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(occurrences(out.stdout, "same_graph ... ok\n") == 1); assert(occurrences(out.stdout, "external_graph ... ok\n") == 1); // Warm action reuse is exact; linking republishes the requested output. rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); runcommandenv(root, strings.concat("graph-warm-", stages[si]), av, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(readfile(compilertraces[si]).len == 0); assert(readfile(assemblertraces[si]).len == 0); assert(occurrences(readfile(linkertraces[si]), "\n") == 1); si += 1; }; // An internal helper body edit recompiles only ptest; its stable export // stops reverse compilation of pxtest, the transitive clone, and pmain. rewritefile(samefile, samebodychanged); si = 0; for (si < stages.len) { let oldptestwwi: str = readfile(strings.concat(works[si], "/pkg-internal-test.wwi")); let oldptesta: str = readfile(strings.concat(works[si], "/pkg-internal-test.a")); let oldpxtestwwi: str = readfile(strings.concat(works[si], "/pkg_test-external-test.wwi")); let oldpxtesta: str = readfile(strings.concat(works[si], "/pkg_test-external-test.a")); let oldmainwwi: str = readfile(strings.concat(works[si], "/pkg-test-main.wwi")); let oldmaina: str = readfile(strings.concat(works[si], "/pkg-test-main.a")); rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); let env: []str = os.getenvs(); let tracedenv: []str = alloc([], (env.len + 9): u64)!; let ei: i32 = 0; for (ei < env.len) { if (!strings.hasprefix(env[ei], "WW_W6C=") && !strings.hasprefix(env[ei], "WW_W6A=") && !strings.hasprefix(env[ei], "WW_W6L=")) { append(tracedenv, env[ei]); }; ei += 1; }; append(tracedenv, strings.concat("WW_W6C=", compilerwrapper)); append(tracedenv, strings.concat("WW_W6A=", assemblerwrapper)); append(tracedenv, strings.concat("WW_W6L=", linkerwrapper)); append(tracedenv, strings.concat("WW_GRAPH_COMPILER_TRACE=", compilertraces[si])); append(tracedenv, strings.concat("WW_GRAPH_ASSEMBLER_TRACE=", assemblertraces[si])); append(tracedenv, strings.concat("WW_GRAPH_LINKER_TRACE=", linkertraces[si])); append(tracedenv, strings.concat("WW_GRAPH_REAL_COMPILER=", driver(compilers[si]))); append(tracedenv, strings.concat("WW_GRAPH_REAL_ASSEMBLER=", driver(assemblers[si]))); append(tracedenv, strings.concat("WW_GRAPH_REAL_LINKER=", driver(linkers[si]))); let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si], "-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg", "pkg_test", pkg, bins[si], "-", statuses[si], pkg]; runcommandenv(root, strings.concat("graph-body-", stages[si]), av, tracedenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let ct: str = readfile(compilertraces[si]); let at: str = readfile(assemblertraces[si]); let lt: str = readfile(linkertraces[si]); assert(occurrences(ct, "\n") == 1); assert(has(ct, "pkg-internal-test.unit.new")); assert(!has(ct, "pkg_test-external-test.unit.new")); assert(!has(ct, "pkg-test-main.unit.new")); assert(!has(ct, ".bridge.v4.r0.init")); assert(occurrences(at, "\n") == 1); assert(has(at, "pkg-internal-test.o.new")); assert(occurrences(lt, "\n") == 1); assert(same(oldptestwwi, readfile(strings.concat(works[si], "/pkg-internal-test.wwi")))); assert(!same(oldptesta, readfile(strings.concat(works[si], "/pkg-internal-test.a")))); assert(same(oldpxtestwwi, readfile(strings.concat(works[si], "/pkg_test-external-test.wwi")))); assert(same(oldpxtesta, readfile(strings.concat(works[si], "/pkg_test-external-test.a")))); assert(same(oldmainwwi, readfile(strings.concat(works[si], "/pkg-test-main.wwi")))); assert(same(oldmaina, readfile(strings.concat(works[si], "/pkg-test-main.a")))); let nc: str = normalizedtrace(ct, strings.concat(works[si], "/"), bins[si]); let na: str = normalizedtrace(at, strings.concat(works[si], "/"), bins[si]); let nl: str = normalizedtrace(lt, strings.concat(works[si], "/"), bins[si]); if (si == 0) { bodycompiler = nc; bodyassembler = na; bodylinker = nl; } else { assert(same(bodycompiler, nc)); assert(same(bodyassembler, na)); assert(same(bodylinker, nl)); assert(same(readfile(bins[0]), readfile(bins[1]))); }; si += 1; }; // An external test body edit is confined to pxtest when its export is stable. rewritefile(externalfile, externalbodychanged); si = 0; for (si < stages.len) { rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); let env: []str = os.getenvs(); let tracedenv: []str = alloc([], (env.len + 9): u64)!; let ei: i32 = 0; for (ei < env.len) { if (!strings.hasprefix(env[ei], "WW_W6C=") && !strings.hasprefix(env[ei], "WW_W6A=") && !strings.hasprefix(env[ei], "WW_W6L=")) { append(tracedenv, env[ei]); }; ei += 1; }; append(tracedenv, strings.concat("WW_W6C=", compilerwrapper)); append(tracedenv, strings.concat("WW_W6A=", assemblerwrapper)); append(tracedenv, strings.concat("WW_W6L=", linkerwrapper)); append(tracedenv, strings.concat("WW_GRAPH_COMPILER_TRACE=", compilertraces[si])); append(tracedenv, strings.concat("WW_GRAPH_ASSEMBLER_TRACE=", assemblertraces[si])); append(tracedenv, strings.concat("WW_GRAPH_LINKER_TRACE=", linkertraces[si])); append(tracedenv, strings.concat("WW_GRAPH_REAL_COMPILER=", driver(compilers[si]))); append(tracedenv, strings.concat("WW_GRAPH_REAL_ASSEMBLER=", driver(assemblers[si]))); append(tracedenv, strings.concat("WW_GRAPH_REAL_LINKER=", driver(linkers[si]))); let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si], "-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg", "pkg_test", pkg, bins[si], "-", statuses[si], pkg]; runcommandenv(root, strings.concat("graph-external-", stages[si]), av, tracedenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let ct: str = readfile(compilertraces[si]); let at: str = readfile(assemblertraces[si]); let lt: str = readfile(linkertraces[si]); assert(occurrences(ct, "\n") == 1); assert(has(ct, "pkg_test-external-test.unit.new")); assert(!has(ct, "pkg-internal-test.unit.new")); assert(!has(ct, "pkg-test-main.unit.new")); assert(!has(ct, ".bridge.v4.r0.init")); assert(occurrences(at, "\n") == 1); assert(has(at, "pkg_test-external-test.o.new")); assert(occurrences(lt, "\n") == 1); let nc: str = normalizedtrace(ct, strings.concat(works[si], "/"), bins[si]); let na: str = normalizedtrace(at, strings.concat(works[si], "/"), bins[si]); let nl: str = normalizedtrace(lt, strings.concat(works[si], "/"), bins[si]); if (si == 0) { externalcompiler = nc; externalassembler = na; externallinker = nl; } else { assert(same(externalcompiler, nc)); assert(same(externalassembler, na)); assert(same(externallinker, nl)); assert(same(readfile(bins[0]), readfile(bins[1]))); }; si += 1; }; // An internal exported-helper interface edit reaches the variant-4 // importer, pxtest, and pmain, but still never introduces ordinary p. rewritefile(samefile, samebodyexported); si = 0; for (si < stages.len) { let oldwwi: str = readfile(strings.concat(works[si], "/pkg-internal-test.wwi")); rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); let env: []str = os.getenvs(); let tracedenv: []str = alloc([], (env.len + 9): u64)!; let ei: i32 = 0; for (ei < env.len) { if (!strings.hasprefix(env[ei], "WW_W6C=") && !strings.hasprefix(env[ei], "WW_W6A=") && !strings.hasprefix(env[ei], "WW_W6L=")) { append(tracedenv, env[ei]); }; ei += 1; }; append(tracedenv, strings.concat("WW_W6C=", compilerwrapper)); append(tracedenv, strings.concat("WW_W6A=", assemblerwrapper)); append(tracedenv, strings.concat("WW_W6L=", linkerwrapper)); append(tracedenv, strings.concat("WW_GRAPH_COMPILER_TRACE=", compilertraces[si])); append(tracedenv, strings.concat("WW_GRAPH_ASSEMBLER_TRACE=", assemblertraces[si])); append(tracedenv, strings.concat("WW_GRAPH_LINKER_TRACE=", linkertraces[si])); append(tracedenv, strings.concat("WW_GRAPH_REAL_COMPILER=", driver(compilers[si]))); append(tracedenv, strings.concat("WW_GRAPH_REAL_ASSEMBLER=", driver(assemblers[si]))); append(tracedenv, strings.concat("WW_GRAPH_REAL_LINKER=", driver(linkers[si]))); let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si], "-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg", "pkg_test", pkg, bins[si], "-", statuses[si], pkg]; runcommandenv(root, strings.concat("graph-export-", stages[si]), av, tracedenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let ct: str = readfile(compilertraces[si]); let at: str = readfile(assemblertraces[si]); let lt: str = readfile(linkertraces[si]); assert(!same(oldwwi, readfile(strings.concat(works[si], "/pkg-internal-test.wwi")))); assert(occurrences(ct, "pkg-internal-test.unit.new") == 1); assert(occurrences(ct, "--package-init-symbol __ww..pkg.p.bridge.v4.r0.init") == 1); assert(occurrences(ct, "pkg_test-external-test.unit.new") == 1); assert(occurrences(ct, "pkg-test-main.unit.new") == 1); assert(!has(ct, "--package-init-symbol __ww..pkg.p.pkg.v0.r0.init")); assert(occurrences(lt, "\n") == 1); let nc: str = normalizedtrace(ct, strings.concat(works[si], "/"), bins[si]); let na: str = normalizedtrace(at, strings.concat(works[si], "/"), bins[si]); let nl: str = normalizedtrace(lt, strings.concat(works[si], "/"), bins[si]); if (si == 0) { exportcompiler = nc; exportassembler = na; exportlinker = nl; } else { assert(same(exportcompiler, nc)); assert(same(exportassembler, na)); assert(same(exportlinker, nl)); assert(same(readfile(bins[0]), readfile(bins[1]))); }; let xi: i32 = 0; for (xi < suffixes.len) { let bytes: str = readfile(strings.concat(works[si], "/", bridgeaction, suffixes[xi])); if (si == 0) { exportbridge[xi] = strings.dup(bytes); } else { assert(same(exportbridge[xi], bytes)); }; xi += 1; }; let runav: []str = [bins[si]]; runcommand(root, strings.concat("graph-export-run-", stages[si]), runav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(occurrences(out.stdout, "same_graph ... ok\n") == 1); assert(occurrences(out.stdout, "external_graph ... ok\n") == 1); si += 1; }; // Removing the external variant changes only the directory main's target // set. The now-unreachable pxtest and transitive clone remain intact in the // persistent store, while the linked process runs the internal target once. assert(os.remove(externalfile) == 0); si = 0; for (si < stages.len) { let oldptest: str = readfile(strings.concat(works[si], "/pkg-internal-test.a")); let oldpxtest: str = readfile(strings.concat(works[si], "/pkg_test-external-test.a")); let oldbridge: str = readfile(strings.concat(works[si], "/", bridgeaction, ".a")); rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); let env: []str = alloc([], (baseenv.len + 9): 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_GRAPH_COMPILER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_GRAPH_ASSEMBLER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_GRAPH_LINKER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_GRAPH_REAL_COMPILER=") && !strings.hasprefix(baseenv[ei], "WW_GRAPH_REAL_ASSEMBLER=") && !strings.hasprefix(baseenv[ei], "WW_GRAPH_REAL_LINKER=")) { 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_GRAPH_COMPILER_TRACE=", compilertraces[si])); append(env, strings.concat("WW_GRAPH_ASSEMBLER_TRACE=", assemblertraces[si])); append(env, strings.concat("WW_GRAPH_LINKER_TRACE=", linkertraces[si])); append(env, strings.concat("WW_GRAPH_REAL_COMPILER=", driver(compilers[si]))); append(env, strings.concat("WW_GRAPH_REAL_ASSEMBLER=", driver(assemblers[si]))); append(env, strings.concat("WW_GRAPH_REAL_LINKER=", driver(linkers[si]))); let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si], "-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg", "-", pkg, bins[si], "-", statuses[si], pkg]; runcommandenv(root, strings.concat("graph-remove-external-", stages[si]), av, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(same(readfile(statuses[si]), "ok\n")); let ct: str = readfile(compilertraces[si]); let at: str = readfile(assemblertraces[si]); let lt: str = readfile(linkertraces[si]); assert(occurrences(ct, "\n") == 1); assert(has(ct, "pkg-test-main.unit.new")); assert(!has(ct, "pkg-internal-test.unit.new")); assert(!has(ct, "pkg_test-external-test.unit.new")); assert(!has(ct, strings.concat("/", bridgeaction, ".unit.new"))); let maincompile: str = linecontaining(ct, "pkg-test-main.unit.new"); assert(occurrences(maincompile, "--test-target-package") == 1); assert(has(maincompile, "--test-target-package pkg ")); assert(!has(maincompile, "--test-target-package pkg_test ")); assert(occurrences(at, "\n") == 2); assert(has(at, "/pkg-test-main.o.new")); assert(has(at, "/pkg-test-main.init.o.new")); assert(occurrences(lt, "\n") == 1); let link: str = linecontaining(lt, strings.concat("-o ", bins[si], ".new ")); assert(has(link, "/pkg-test-main.a.new")); assert(has(link, "/pkg-internal-test.a")); assert(!has(link, "/pkg_test-external-test.a")); assert(!has(link, strings.concat("/", bridgeaction, ".a"))); assert(!has(link, "/__external.a")); assert(same(oldptest, readfile(strings.concat(works[si], "/pkg-internal-test.a")))); assert(same(oldpxtest, readfile(strings.concat(works[si], "/pkg_test-external-test.a")))); assert(same(oldbridge, readfile(strings.concat(works[si], "/", bridgeaction, ".a")))); let runav: []str = [bins[si]]; runcommand(root, strings.concat("graph-remove-run-", stages[si]), runav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(occurrences(out.stdout, "same_graph ... ok\n") == 1); assert(!has(out.stdout, "external_graph ... ok\n")); let nc: str = normalizedtrace(ct, strings.concat(works[si], "/"), bins[si]); let na: str = normalizedtrace(at, strings.concat(works[si], "/"), bins[si]); let nl: str = normalizedtrace(lt, strings.concat(works[si], "/"), bins[si]); if (si == 0) { removecompiler = nc; removeassembler = na; removelinker = nl; removebin = readfile(bins[si]); removeout = strings.dup(out.stdout); removeerr = strings.dup(out.stderr); } else { assert(same(removecompiler, nc)); assert(same(removeassembler, na)); assert(same(removelinker, nl)); assert(same(removebin, readfile(bins[si]))); assert(same(removeout, out.stdout)); assert(same(removeerr, out.stderr)); }; si += 1; }; // Re-adding the external variant with a body-only edit rebuilds pxtest and // the target-set main. Its stable export stops at the unchanged ptest and // transitive clone, whose complete persisted bytes remain identical. writefile(externalfile, externalbodyreadded); si = 0; for (si < stages.len) { let oldptest: str = readfile(strings.concat(works[si], "/pkg-internal-test.a")); let oldpxtestwwi: str = readfile(strings.concat(works[si], "/pkg_test-external-test.wwi")); let oldpxtesta: str = readfile(strings.concat(works[si], "/pkg_test-external-test.a")); let oldbridge: []str = ["", "", "", "", ""]; let xi: i32 = 0; for (xi < suffixes.len) { oldbridge[xi] = readfile(strings.concat(works[si], "/", bridgeaction, suffixes[xi])); xi += 1; }; rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); let env: []str = alloc([], (baseenv.len + 9): 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_GRAPH_COMPILER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_GRAPH_ASSEMBLER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_GRAPH_LINKER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_GRAPH_REAL_COMPILER=") && !strings.hasprefix(baseenv[ei], "WW_GRAPH_REAL_ASSEMBLER=") && !strings.hasprefix(baseenv[ei], "WW_GRAPH_REAL_LINKER=")) { 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_GRAPH_COMPILER_TRACE=", compilertraces[si])); append(env, strings.concat("WW_GRAPH_ASSEMBLER_TRACE=", assemblertraces[si])); append(env, strings.concat("WW_GRAPH_LINKER_TRACE=", linkertraces[si])); append(env, strings.concat("WW_GRAPH_REAL_COMPILER=", driver(compilers[si]))); append(env, strings.concat("WW_GRAPH_REAL_ASSEMBLER=", driver(assemblers[si]))); append(env, strings.concat("WW_GRAPH_REAL_LINKER=", driver(linkers[si]))); let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si], "-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg", "pkg_test", pkg, bins[si], "-", statuses[si], pkg]; runcommandenv(root, strings.concat("graph-readd-external-", stages[si]), av, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); let ct: str = readfile(compilertraces[si]); let at: str = readfile(assemblertraces[si]); let lt: str = readfile(linkertraces[si]); assert(occurrences(ct, "\n") == 2); assert(occurrences(ct, "pkg_test-external-test.unit.new") == 1); assert(occurrences(ct, "pkg-test-main.unit.new") == 1); assert(!has(ct, "pkg-internal-test.unit.new")); assert(!has(ct, strings.concat("/", bridgeaction, ".unit.new"))); let maincompile: str = linecontaining(ct, "pkg-test-main.unit.new"); assert(occurrences(maincompile, "--test-target-package") == 2); assert(pos(maincompile, "--test-target-package pkg ") < pos(maincompile, "--test-target-package pkg_test ")); let externalcompile: str = linecontaining(ct, "pkg_test-external-test.unit.new"); assert(has(externalcompile, "/pkg-internal-test.wwi")); assert(has(externalcompile, strings.concat("/", bridgeaction, ".wwi"))); assert(occurrences(at, "\n") == 3); assert(occurrences(lt, "\n") == 1); let link: str = linecontaining(lt, strings.concat("-o ", bins[si], ".new ")); assert(has(link, "/pkg-test-main.a.new")); assert(has(link, "/pkg_test-external-test.a.new")); assert(has(link, "/pkg-internal-test.a")); assert(has(link, strings.concat("/", bridgeaction, ".a"))); assert(same(oldptest, readfile(strings.concat(works[si], "/pkg-internal-test.a")))); assert(same(oldpxtestwwi, readfile(strings.concat(works[si], "/pkg_test-external-test.wwi")))); assert(!same(oldpxtesta, readfile(strings.concat(works[si], "/pkg_test-external-test.a")))); xi = 0; for (xi < suffixes.len) { assert(same(oldbridge[xi], readfile(strings.concat(works[si], "/", bridgeaction, suffixes[xi])))); xi += 1; }; let runav: []str = [bins[si]]; runcommand(root, strings.concat("graph-readd-run-", stages[si]), runav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(occurrences(out.stdout, "same_graph ... ok\n") == 1); assert(occurrences(out.stdout, "external_graph ... ok\n") == 1); let nc: str = normalizedtrace(ct, strings.concat(works[si], "/"), bins[si]); let na: str = normalizedtrace(at, strings.concat(works[si], "/"), bins[si]); let nl: str = normalizedtrace(lt, strings.concat(works[si], "/"), bins[si]); if (si == 0) { readdcompiler = nc; readdassembler = na; readdlinker = nl; readdbin = readfile(bins[si]); readdout = strings.dup(out.stdout); readderr = strings.dup(out.stderr); } else { assert(same(readdcompiler, nc)); assert(same(readdassembler, na)); assert(same(readdlinker, nl)); assert(same(readdbin, readfile(bins[si]))); assert(same(readdout, out.stdout)); assert(same(readderr, out.stderr)); }; si += 1; }; // The public coordinator accepts one explicit output for the combined // directory in both stages because variants are actions, not products. let directbin: str = strings.concat(root, "/direct.test"); let directbytes: str = ""; let directout: str = ""; let directerr: str = ""; si = 0; for (si < stages.len) { let directav: []str = [driver(stages[si]), "test", "-c", "-o", directbin, "-I", root, pkg]; runcommand(root, strings.concat("graph-direct-product-", stages[si]), directav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0); assert(os.exists(directbin)); if (si == 0) { directbytes = readfile(directbin); directout = strings.dup(out.stdout); directerr = strings.dup(out.stderr); } else { assert(same(directbytes, readfile(directbin))); assert(same(directout, out.stdout)); assert(same(directerr, out.stderr)); }; clean(strings.concat(directbin, ".sepwork")); clean(directbin); si += 1; }; clean(root); }; @test fn multi_directory_shared_package_plan() void = { let root: str = fresh(); let suite: str = strings.concat(root, "/suite"); let leaf: str = strings.concat(root, "/leaf"); let common: str = strings.concat(root, "/common"); let alpha: str = strings.concat(suite, "/alpha"); let beta: str = strings.concat(suite, "/beta"); let gamma: str = strings.concat(suite, "/gamma"); let alphasame: str = strings.concat(suite, "/_alpha_same"); let alphaexternal: str = strings.concat(suite, "/_alpha_external"); let betasame: str = strings.concat(suite, "/_beta_same"); let betaexternal: str = strings.concat(suite, "/_beta_external"); let dirs: []str = [suite, leaf, common, alpha, beta, gamma, alphasame, alphaexternal, betasame, betaexternal]; let di: i32 = 0; for (di < dirs.len) { assert(os.mkdir(dirs[di], 448i32) == 0); di += 1; }; writefile(strings.concat(leaf, "/leaf.ww"), strings.concat( "package leaf;\n// MULTIDIR_LEAF\n", "export fn value() i32 = { return 40; };\n")); writefile(strings.concat(common, "/common.ww"), strings.concat( "package common;\nimport leaf;\n// MULTIDIR_COMMON\n", "export fn value() i32 = { return leaf.value() + 1; };\n")); writefile(strings.concat(alphasame, "/dep.ww"), "package _alpha_same;\nexport fn value() i32 = { return 0; };\n"); writefile(strings.concat(alphaexternal, "/dep.ww"), "package _alpha_external;\nexport fn value() i32 = { return 0; };\n"); writefile(strings.concat(betasame, "/dep.ww"), "package _beta_same;\nexport fn value() i32 = { return 0; };\n"); writefile(strings.concat(betaexternal, "/dep.ww"), "package _beta_external;\nexport fn value() i32 = { return 0; };\n"); writefile(strings.concat(alpha, "/a.ww"), strings.concat( "package alpha;\nimport common;\n// MULTIDIR_ALPHA\n", "export fn value() i32 = { return common.value() + 1; };\n")); writefile(strings.concat(alpha, "/same_test.ww"), strings.concat( "package alpha;\nimport _alpha_same;\n// MULTIDIR_ALPHA_SAME\n", "export fn test_helper() i32 = { return 1; };\n", "@test fn alpha_same_runs() void = {\n", " assert(value() + _alpha_same.value() == 42);\n", "};\n")); writefile(strings.concat(alpha, "/external_test.ww"), strings.concat( "package alpha_test;\nimport _alpha_external;\nimport alpha;\n", "// MULTIDIR_ALPHA_EXTERNAL\n", "@test fn alpha_external_runs() void = {\n", " assert(alpha.value() + _alpha_external.value() == 42);\n", " assert(alpha.test_helper() == 1);\n", "};\n")); writefile(strings.concat(gamma, "/gamma.ww"), strings.concat( "package gamma;\n// MULTIDIR_GAMMA\n", "export fn value() i32 = { return 0; };\n")); writefile(strings.concat(beta, "/beta.ww"), strings.concat( "package beta;\nimport alpha;\nimport gamma;\n// MULTIDIR_BETA\n", "export fn value() i32 = { return alpha.value() + gamma.value(); };\n")); writefile(strings.concat(beta, "/same_test.ww"), strings.concat( "package beta;\nimport _beta_same;\n// MULTIDIR_BETA_SAME\n", "export fn test_helper() i32 = { return 2; };\n", "@test fn beta_same_runs() void = {\n", " assert(value() + _beta_same.value() == 42);\n", "};\n")); writefile(strings.concat(beta, "/external_test.ww"), strings.concat( "package beta_test;\nimport _beta_external;\nimport beta;\n", "// MULTIDIR_BETA_EXTERNAL\n", "@test fn beta_external_runs() void = {\n", " assert(beta.value() + _beta_external.value() == 42);\n", " assert(beta.test_helper() == 2);\n", "};\n")); let compilerwrapper: str = strings.concat(root, "/trace-w6c.sh"); let assemblerwrapper: str = strings.concat(root, "/trace-w6a.sh"); let linkerwrapper: str = strings.concat(root, "/trace-w6l.sh"); writeexecutable(compilerwrapper, strings.concat( "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_MULTI_COMPILER_TRACE\"\n", "exec \"$WW_MULTI_REAL_COMPILER\" \"$@\"\n")); writeexecutable(assemblerwrapper, strings.concat( "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_MULTI_ASSEMBLER_TRACE\"\n", "exec \"$WW_MULTI_REAL_ASSEMBLER\" \"$@\"\n")); writeexecutable(linkerwrapper, strings.concat( "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_MULTI_LINKER_TRACE\"\n", "exec \"$WW_MULTI_REAL_LINKER\" \"$@\"\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 works: []str = [strings.concat(root, "/c-work"), strings.concat(root, "/ww-work")]; let compilertraces: []str = [strings.concat(root, "/c-w6c.trace"), strings.concat(root, "/ww-w6c.trace")]; let assemblertraces: []str = [strings.concat(root, "/c-w6a.trace"), strings.concat(root, "/ww-w6a.trace")]; let linkertraces: []str = [strings.concat(root, "/c-w6l.trace"), strings.concat(root, "/ww-w6l.trace")]; let bins: []str = [strings.concat(root, "/alpha-product.test"), strings.concat(root, "/beta-product.test")]; let statuses: []str = [strings.concat(root, "/alpha.status"), strings.concat(root, "/beta.status"), strings.concat(root, "/gamma.status")]; let gammabin: str = strings.concat(root, "/gamma-product.test"); let actions: []str = ["leaf", "common", "alpha", "gamma", "_alpha_same", "_alpha_external", "_beta_same", "_beta_external", "test", "alpha-internal-test", "alpha_test-external-test", "alpha-test-main", "beta-internal-test", "beta_test-external-test", "beta-test-main"]; let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"]; let references: []str = alloc([], (actions.len * suffixes.len): u64)!; let ri: i32 = 0; for (ri < actions.len * suffixes.len) { append(references, ""); ri += 1; }; let referencealpha: str = ""; let referencebeta: str = ""; let referencecompiler: str = ""; let referenceassembler: str = ""; let referencelinker: str = ""; let baseenv: []str = os.getenvs(); let out: commandout; let si: i32 = 0; for (si < stages.len) { assert(os.mkdir(works[si], 448i32) == 0); writefile(compilertraces[si], ""); writefile(assemblertraces[si], ""); writefile(linkertraces[si], ""); let env: []str = alloc([], (baseenv.len + 9): 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_MULTI_COMPILER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_MULTI_ASSEMBLER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_MULTI_LINKER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_MULTI_REAL_COMPILER=") && !strings.hasprefix(baseenv[ei], "WW_MULTI_REAL_ASSEMBLER=") && !strings.hasprefix(baseenv[ei], "WW_MULTI_REAL_LINKER=")) { 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_MULTI_COMPILER_TRACE=", compilertraces[si])); append(env, strings.concat("WW_MULTI_ASSEMBLER_TRACE=", assemblertraces[si])); append(env, strings.concat("WW_MULTI_LINKER_TRACE=", linkertraces[si])); append(env, strings.concat("WW_MULTI_REAL_COMPILER=", driver(compilers[si]))); append(env, strings.concat("WW_MULTI_REAL_ASSEMBLER=", driver(assemblers[si]))); append(env, strings.concat("WW_MULTI_REAL_LINKER=", driver(linkers[si]))); let forward: []str = [driver(stages[si]), "test", "-c", "-w", works[si], "-I", suite, "-I", root, "--ww-package-test", "test", "alpha", "alpha", "alpha", "alpha_test", alpha, bins[0], "-", statuses[0], "--ww-package-test", "test", "beta", "beta", "beta", "beta_test", beta, bins[1], "-", statuses[1], "--ww-package-test", "test", "gamma", "gamma", "-", "-", gamma, gammabin, "-", statuses[2], alpha]; let reverse: []str = [driver(stages[si]), "test", "-c", "-w", works[si], "-I", suite, "-I", root, "--ww-package-test", "test", "gamma", "gamma", "-", "-", gamma, gammabin, "-", statuses[2], "--ww-package-test", "test", "beta", "beta", "beta", "beta_test", beta, bins[1], "-", statuses[1], "--ww-package-test", "test", "alpha", "alpha", "alpha", "alpha_test", alpha, bins[0], "-", statuses[0], alpha]; if (si == 0) { runcommandenv(root, "multi-forward-c", forward, env, (180i64 * (time.second: i64)): time.duration, &out); } else { runcommandenv(root, "multi-reverse-ww", reverse, env, (180i64 * (time.second: i64)): time.duration, &out); }; expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(os.exists(bins[0]) && os.exists(bins[1])); assert(!os.exists(gammabin)); assert(same(readfile(statuses[0]), "ok\n")); assert(same(readfile(statuses[1]), "ok\n")); assert(same(readfile(statuses[2]), "ok\n")); assert(!os.exists(strings.concat(works[si], "/beta.unit.ww"))); assert(!os.exists(strings.concat(works[si], "/gamma-main.unit.ww"))); assert(!os.exists(strings.concat(works[si], "/alpha-internal-test-main.unit.ww"))); assert(!os.exists(strings.concat(works[si], "/alpha_test-external-test-main.unit.ww"))); let ctrace: str = readfile(compilertraces[si]); let atrace: str = readfile(assemblertraces[si]); let ltrace: str = readfile(linkertraces[si]); assert(occurrences(ctrace, "/alpha.unit.new") == 1); assert(occurrences(ctrace, "/gamma.unit.new") == 1); assert(occurrences(ctrace, "/beta.unit.new") == 0); assert(occurrences(ctrace, "/alpha-internal-test.unit.new") == 1); assert(occurrences(ctrace, "/alpha_test-external-test.unit.new") == 1); assert(occurrences(ctrace, "/alpha-test-main.unit.new") == 1); assert(occurrences(ctrace, "/beta-internal-test.unit.new") == 1); assert(occurrences(ctrace, "/beta_test-external-test.unit.new") == 1); assert(occurrences(ctrace, "/beta-test-main.unit.new") == 1); assert(occurrences(ctrace, "--test-target-package") == 4); let alphamain: str = linecontaining(ctrace, "/alpha-test-main.unit.new"); assert(occurrences(alphamain, "--test-target-package") == 2); assert(pos(alphamain, "--test-target-package alpha ") < pos(alphamain, "--test-target-package alpha_test ")); let betamain: str = linecontaining(ctrace, "/beta-test-main.unit.new"); assert(occurrences(betamain, "--test-target-package") == 2); assert(pos(betamain, "--test-target-package beta ") < pos(betamain, "--test-target-package beta_test ")); assert(occurrences(ltrace, "\n") == 2); let alink: str = linecontaining(ltrace, strings.concat("-o ", bins[0], ".new ")); assert(has(alink, "/alpha-test-main.a.new")); assert(has(alink, "/alpha-internal-test.a.new")); assert(has(alink, "/alpha_test-external-test.a.new")); assert(!has(alink, strings.concat(works[si], "/alpha.a"))); assert(!has(alink, "/beta-internal-test.a.new")); assert(!has(alink, "/gamma.a.new")); assert(!has(alink, ".wwi")); let blink: str = linecontaining(ltrace, strings.concat("-o ", bins[1], ".new ")); assert(has(blink, "/beta-test-main.a.new")); assert(has(blink, "/beta-internal-test.a.new")); assert(has(blink, "/beta_test-external-test.a.new")); assert(!has(blink, strings.concat(works[si], "/beta.a"))); assert(has(blink, strings.concat(works[si], "/alpha.a.new"))); assert(has(blink, strings.concat(works[si], "/gamma.a.new"))); assert(!has(blink, "/alpha-internal-test.a.new")); assert(!has(blink, "/alpha_test-external-test.a.new")); assert(!has(blink, ".wwi")); let runav: []str = [bins[0]]; runcommand(root, strings.concat("multi-alpha-run-", stages[si]), runav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(occurrences(out.stdout, "alpha_same_runs ... ok\n") == 1); assert(occurrences(out.stdout, "alpha_external_runs ... ok\n") == 1); let betarunav: []str = [bins[1]]; runcommand(root, strings.concat("multi-beta-run-", stages[si]), betarunav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(occurrences(out.stdout, "beta_same_runs ... ok\n") == 1); assert(occurrences(out.stdout, "beta_external_runs ... ok\n") == 1); ri = 0; for (ri < actions.len) { let xi: i32 = 0; for (xi < suffixes.len) { let bytes: str = readfile(strings.concat(works[si], "/", actions[ri], suffixes[xi])); let index: i32 = ri * suffixes.len + xi; if (si == 0) { references[index] = strings.dup(bytes); } else { assert(same(references[index], bytes)); }; xi += 1; }; ri += 1; }; let nc: str = normalizedtrace(ctrace, strings.concat(works[si], "/"), bins[0]); let na: str = normalizedtrace(atrace, strings.concat(works[si], "/"), bins[0]); let nl: str = normalizedtrace(ltrace, strings.concat(works[si], "/"), bins[0]); if (si == 0) { referencealpha = readfile(bins[0]); referencebeta = readfile(bins[1]); referencecompiler = nc; referenceassembler = na; referencelinker = nl; } else { assert(same(referencealpha, readfile(bins[0]))); assert(same(referencebeta, readfile(bins[1]))); assert(same(referencecompiler, nc)); assert(same(referenceassembler, na)); assert(same(referencelinker, nl)); }; // Every unchanged action is warm-reused. Only the two requested // directory binaries are linked again; gamma remains compile-only. // The opposite descriptor order is warm in each stage, so product // order cannot enter action identity or invalidation. let warmalpha: str = readfile(bins[0]); let warmbeta: str = readfile(bins[1]); rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); if (si == 0) { runcommandenv(root, "multi-warm-reverse-c", reverse, env, (180i64 * (time.second: i64)): time.duration, &out); } else { runcommandenv(root, "multi-warm-forward-ww", forward, env, (180i64 * (time.second: i64)): time.duration, &out); }; expectexit(&out, 0); assert(readfile(compilertraces[si]).len == 0); assert(readfile(assemblertraces[si]).len == 0); assert(occurrences(readfile(linkertraces[si]), "\n") == 2); assert(same(warmalpha, readfile(bins[0]))); assert(same(warmbeta, readfile(bins[1]))); si += 1; }; // Public root order is presentation-only. Default -c destinations are in // the invocation cwd, and the selected no-test package publishes nothing. let publicalpha: str = strings.concat(root, "/alpha.test"); let publicbeta: str = strings.concat(root, "/beta.test"); let publicgamma: str = strings.concat(root, "/gamma.test"); let publicout: str = ""; let publicerr: str = ""; let publicalphabytes: str = ""; let publicbetabytes: str = ""; let publiclabels: []str = ["c-forward", "c-reverse", "ww-forward", "ww-reverse"]; let pi: i32 = 0; for (pi < 4) { let publicdriver: str = driver("ww"); if (pi >= 2) { publicdriver = driver("ww_ww"); }; let publicav: []str = alloc([], 16u64)!; append(publicav, publicdriver); append(publicav, "test"); append(publicav, "-c"); append(publicav, "-j"); append(publicav, "1"); append(publicav, "-I"); append(publicav, suite); append(publicav, "-I"); append(publicav, root); if (pi % 2 == 0) { append(publicav, alpha); append(publicav, beta); append(publicav, gamma); } else { append(publicav, gamma); append(publicav, beta); append(publicav, alpha); }; runcommanddir(root, strings.concat("multi-public-order-", publiclabels[pi]), root, publicav, (180i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(occurrences(out.stdout, "built ") == 0); assert(has(out.stdout, strings.concat( "? ", gamma, " [no test files]\n"))); assert(os.exists(publicalpha) && os.exists(publicbeta)); assert(!os.exists(publicgamma)); assert(!os.exists(strings.concat(alpha, "/alpha_test.test"))); assert(!os.exists(strings.concat(beta, "/beta_test.test"))); if (pi == 0) { publicout = strings.dup(out.stdout); publicerr = strings.dup(out.stderr); publicalphabytes = readfile(publicalpha); publicbetabytes = readfile(publicbeta); } else { assert(same(publicout, out.stdout)); assert(same(publicerr, out.stderr)); assert(same(publicalphabytes, readfile(publicalpha))); assert(same(publicbetabytes, readfile(publicbeta))); }; clean(publicalpha); clean(publicbeta); clean(publicgamma); pi += 1; }; clean(root); }; @test fn shared_package_resolution_context_conflict_is_stable() void = { let root: str = fresh(); let suite: str = strings.concat(root, "/suite"); let a: str = strings.concat(suite, "/a"); let b: str = strings.concat(suite, "/b"); let good: str = strings.concat(suite, "/good"); let aleaf: str = strings.concat(a, "/_leaf"); let bleaf: str = strings.concat(b, "/_leaf"); let common: str = strings.concat(root, "/common"); let dirs: []str = [suite, a, b, good, aleaf, bleaf, common]; let di: i32 = 0; for (di < dirs.len) { assert(os.mkdir(dirs[di], 448i32) == 0); di += 1; }; writefile(strings.concat(aleaf, "/_leaf.ww"), "package _leaf;\nexport fn value() i32 = { return 20; };\n"); writefile(strings.concat(bleaf, "/_leaf.ww"), "package _leaf;\nexport fn value() i32 = { return 21; };\n"); writefile(strings.concat(common, "/common.ww"), strings.concat( "package common;\nimport _leaf;\n", "export fn value() i32 = { return _leaf.value(); };\n")); writefile(strings.concat(a, "/a.ww"), "package a;\nimport common;\nfn value() i32 = { return common.value(); };\n"); writefile(strings.concat(a, "/a_test.ww"), "package a;\n@test fn a_context() void = { assert(value() == 20); };\n"); writefile(strings.concat(b, "/b.ww"), "package b;\nimport common;\nfn value() i32 = { return common.value(); };\n"); writefile(strings.concat(b, "/b_test.ww"), "package b;\n@test fn b_context() void = { assert(value() == 21); };\n"); writefile(strings.concat(good, "/good.ww"), "package good;\nfn value() i32 = { return 42; };\n"); writefile(strings.concat(good, "/good_test.ww"), "package good;\n@test fn unrelated_runs() void = { assert(value() == 42); };\n"); let tooltrace: str = strings.concat(root, "/context-tool.trace"); let compilerwrapper: str = strings.concat(root, "/context-w6c.sh"); let linkerwrapper: str = strings.concat(root, "/context-w6l.sh"); writefile(tooltrace, ""); writeexecutable(compilerwrapper, strings.concat( "#!/bin/sh\nprintf 'compile\\n' >> \"$WW_CONTEXT_TRACE\"\n", "exec \"$WW_CONTEXT_W6C\" \"$@\"\n")); writeexecutable(linkerwrapper, strings.concat( "#!/bin/sh\nprintf 'link\\n' >> \"$WW_CONTEXT_TRACE\"\n", "exec \"$WW_CONTEXT_W6L\" \"$@\"\n")); let baseenv: []str = os.getenvs(); let traceenv: []str = alloc([], (baseenv.len + 5): u64)!; let ei: i32 = 0; for (ei < baseenv.len) { if (!strings.hasprefix(baseenv[ei], "WW_W6C=") && !strings.hasprefix(baseenv[ei], "WW_W6L=") && !strings.hasprefix(baseenv[ei], "WW_CONTEXT_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_CONTEXT_W6C=") && !strings.hasprefix(baseenv[ei], "WW_CONTEXT_W6L=")) { append(traceenv, baseenv[ei]); }; ei += 1; }; append(traceenv, strings.concat("WW_W6C=", compilerwrapper)); append(traceenv, strings.concat("WW_W6L=", linkerwrapper)); append(traceenv, strings.concat("WW_CONTEXT_TRACE=", tooltrace)); append(traceenv, strings.concat("WW_CONTEXT_W6C=", driver("w6c"))); append(traceenv, strings.concat("WW_CONTEXT_W6L=", driver("w6l"))); let stages: []str = ["ww", "ww_ww"]; let jobs: []str = ["1", "4"]; let diagnostics: []str = ["", ""]; let referenceout: str = ""; let out: commandout; let i: i32 = 0; for (i < stages.len) { let av: []str = [driver(stages[i]), "test", "-j", jobs[i], "-I", root, strings.concat(suite, "/...")]; let stageenv: []str = traceenv; if (i == 1) { let wwenv: []str = alloc([], (baseenv.len + 5): u64)!; stageenv = wwenv; ei = 0; for (ei < baseenv.len) { if (!strings.hasprefix(baseenv[ei], "WW_W6C=") && !strings.hasprefix(baseenv[ei], "WW_W6L=") && !strings.hasprefix(baseenv[ei], "WW_CONTEXT_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_CONTEXT_W6C=") && !strings.hasprefix(baseenv[ei], "WW_CONTEXT_W6L=")) { append(stageenv, baseenv[ei]); }; ei += 1; }; append(stageenv, strings.concat("WW_W6C=", compilerwrapper)); append(stageenv, strings.concat("WW_W6L=", linkerwrapper)); append(stageenv, strings.concat("WW_CONTEXT_TRACE=", tooltrace)); append(stageenv, strings.concat("WW_CONTEXT_W6C=", driver("w6c_ww"))); append(stageenv, strings.concat("WW_CONTEXT_W6L=", driver("w6l_ww"))); }; runcommandenv(root, strings.concat("context-conflict-", stages[i]), av, stageenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, strings.concat( "ww: package _leaf resolves to directories ", aleaf, " and ", bleaf, "\n"))); assert(has(out.stderr, strings.concat("FAIL ", a, " [a] (build exit 1)\n"))); assert(has(out.stderr, strings.concat("FAIL ", b, " [b] (build exit 1)\n"))); assert(has(out.stderr, strings.concat("FAIL ", good, " [good] (build exit 1)\n"))); assert(!has(out.stdout, "unrelated_runs")); diagnostics[i] = strings.dup(primarydiagnostic(out.stderr)); if (i == 0) { referenceout = strings.dup(out.stdout); } else { assert(same(referenceout, out.stdout)); }; i += 1; }; assert(same(diagnostics[0], diagnostics[1])); assert(readfile(tooltrace).len == 0); clean(root); }; @test fn mixed_named_test_and_toolchain_test_actions_do_not_collide() void = { let root: str = fresh(); let suite: str = strings.concat(root, "/suite"); let named: str = strings.concat(suite, "/test"); let consumer: str = strings.concat(suite, "/zconsumer"); assert(os.mkdir(suite, 448i32) == 0); assert(os.mkdir(named, 448i32) == 0); assert(os.mkdir(consumer, 448i32) == 0); writefile(strings.concat(named, "/named.ww"), strings.concat( "package test;\n// MIXED_USER_TEST_PRODUCTION\n", "fn hidden() i32 = { return 19; };\n", "export fn value() i32 = { return 42; };\n")); writefile(strings.concat(named, "/same_test.ww"), strings.concat( "package test;\n@test fn named_same_runs() void = {", " assert(hidden() == 19); };\n")); writefile(strings.concat(named, "/external_test.ww"), strings.concat( "package test_test;\nimport test;\n", "@test fn named_external_runs() void = {", " assert(test.value() == 42); };\n")); writefile(strings.concat(consumer, "/consumer.ww"), strings.concat( "package zconsumer;\n", "// MIXED_GENERATED_TOOLCHAIN_TEST_SUPPORT\n", "export fn value() i32 = { return 42; };\n")); writefile(strings.concat(consumer, "/same_test.ww"), strings.concat( "package zconsumer;\n@test fn consumer_same_runs() void = {", " assert(value() == 42); };\n")); writefile(strings.concat(consumer, "/external_test.ww"), strings.concat( "package zconsumer_test;\nimport zconsumer;\n", "@test fn consumer_external_runs() void = {", " assert(zconsumer.value() == 42); };\n")); let linktrace: str = strings.concat(root, "/mixed-link.trace"); let linkwrapper: str = strings.concat(root, "/mixed-w6l.sh"); writefile(linktrace, ""); writeexecutable(linkwrapper, strings.concat( "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_MIXED_LINK_TRACE\"\n", "exec \"$WW_MIXED_W6L\" \"$@\"\n")); let baseenv: []str = os.getenvs(); let linkenv: []str = alloc([], (baseenv.len + 3): u64)!; let ei: i32 = 0; for (ei < baseenv.len) { if (!strings.hasprefix(baseenv[ei], "WW_W6L=") && !strings.hasprefix(baseenv[ei], "WW_MIXED_LINK_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_MIXED_W6L=")) { append(linkenv, baseenv[ei]); }; ei += 1; }; append(linkenv, strings.concat("WW_W6L=", linkwrapper)); append(linkenv, strings.concat("WW_MIXED_LINK_TRACE=", linktrace)); append(linkenv, strings.concat("WW_MIXED_W6L=", driver("w6l"))); let bins: []str = [strings.concat(named, "/test.test"), strings.concat(consumer, "/zconsumer.test")]; let statuses: []str = [strings.concat(root, "/mixed-test.status"), strings.concat(root, "/mixed-zconsumer.status")]; let keys: []str = ["test-internal-test", "test_test-external-test", "test-test-main", "zconsumer-internal-test", "zconsumer_test-external-test", "zconsumer-test-main"]; let workroot: str = strings.concat(bins[0], ".sepwork"); let work: str = strings.concat(workroot, "/"); let referenceunits: []str = ["", "", "", "", "", ""]; let stages: []str = ["ww", "ww_ww"]; let out: commandout; let si: i32 = 0; for (si < stages.len) { assert(os.mkdir(workroot, 448i32) == 0); let av: []str = [driver(stages[si]), "test", "-c", "-w", workroot, "-I", suite, "--ww-package-test", "test", "test", "test", "test", "test_test", named, bins[0], "-", statuses[0], "--ww-package-test", "test", "zconsumer", "zconsumer", "zconsumer", "zconsumer_test", consumer, bins[1], "-", statuses[1], named]; if (si == 0) { runcommandenv(root, "mixed-actions-c", av, linkenv, (120i64 * (time.second: i64)): time.duration, &out); } else { runcommand(root, "mixed-actions-ww", av, (120i64 * (time.second: i64)): time.duration, &out); }; expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); let namedprod: str = readfile(strings.concat(work, "test-internal-test.unit.ww")); let support: str = readfile(strings.concat(work, "__wwtest.unit.ww")); assert(has(namedprod, "MIXED_USER_TEST_PRODUCTION")); assert(has(namedprod, "//ww:module-reset test\n")); assert(has(support, "//ww:module-reset __wwtest\n")); let suffixes: []str = [".unit.ww", ".wwi", ".a"]; let actions: []str = ["__wwtest", "test-internal-test", "test_test-external-test", "test-test-main", "zconsumer-internal-test", "zconsumer_test-external-test", "zconsumer-test-main"]; let ai: i32 = 0; for (ai < actions.len) { let xi: i32 = 0; for (xi < suffixes.len) { assert(os.exists(strings.concat(work, actions[ai], suffixes[xi]))); xi += 1; }; ai += 1; }; let ri: i32 = 0; for (ri < keys.len) { let unit: str = readfile(strings.concat(work, keys[ri], ".unit.ww")); assert(!has(unit, "//ww:module ")); if (si == 0) { referenceunits[ri] = strings.dup(unit); } else { assert(same(referenceunits[ri], unit)); }; ri += 1; }; let namedrun: []str = [bins[0]]; runcommand(root, strings.concat("mixed-run-", stages[si], "-named"), namedrun, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "named_same_runs ... ok\n")); assert(has(out.stdout, "named_external_runs ... ok\n")); let consumerrun: []str = [bins[1]]; runcommand(root, strings.concat("mixed-run-", stages[si], "-consumer"), consumerrun, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "consumer_same_runs ... ok\n")); assert(has(out.stdout, "consumer_external_runs ... ok\n")); if (si == 0) { let links: str = readfile(linktrace); assert(occurrences(links, "\n") == 2); let namedlink: str = linecontaining(links, strings.concat("-o ", bins[0], ".new ")); let consumerlink: str = linecontaining(links, strings.concat("-o ", bins[1], ".new ")); assert(has(namedlink, strings.concat(work, "__wwtest.a"))); assert(has(namedlink, strings.concat(work, "test-test-main.a"))); assert(has(namedlink, strings.concat(work, "test-internal-test.a"))); assert(has(namedlink, strings.concat(work, "test_test-external-test.a"))); assert(!has(namedlink, strings.concat(work, "test.a"))); assert(has(consumerlink, strings.concat(work, "__wwtest.a"))); assert(has(consumerlink, strings.concat(work, "zconsumer-test-main.a"))); assert(has(consumerlink, strings.concat(work, "zconsumer-internal-test.a"))); assert(has(consumerlink, strings.concat(work, "zconsumer_test-external-test.a"))); assert(!has(consumerlink, strings.concat(work, "test.a"))); }; if (si == 0) { clean(workroot); let bi: i32 = 0; for (bi < bins.len) { clean(bins[bi]); bi += 1; }; }; si += 1; }; clean(root); }; @test fn long_shared_link_closure_is_complete() void = { let root: str = fresh(); let d1: str = strings.concat(root, "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); let d2: str = strings.concat(d1, "/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"); let d3: str = strings.concat(d2, "/cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"); let target: str = strings.concat(d3, "/target"); let chainroot: str = strings.concat(d3, "/chain"); assert(os.mkdir(d1, 448i32) == 0); assert(os.mkdir(d2, 448i32) == 0); assert(os.mkdir(d3, 448i32) == 0); assert(os.mkdir(target, 448i32) == 0); assert(os.mkdir(chainroot, 448i32) == 0); let packagecount: i32 = 300; let i: i32 = 0; for (i < packagecount) { let name: str = boundarypkgname(i); let dir: str = strings.concat(root, "/", name); assert(os.mkdir(dir, 448i32) == 0); let a: str = ""; if (i + 1 < packagecount) { let next: str = boundarypkgname(i + 1); a = strings.concat("package ", name, ";\nimport ", next, ";\n// OWNER_A_", name, "\nexport fn value() i32 = { return ", next, ".value(); };\n"); } else { a = strings.concat("package ", name, ";\n// OWNER_A_", name, "\nexport fn value() i32 = { return 42; };\n"); }; let z: str = strings.concat("package ", name, ";\n// OWNER_Z_", name, "\nfn private_value() i32 = { return value(); };\n"); writefile(strings.concat(dir, "/a.ww"), a); writefile(strings.concat(dir, "/z.ww"), z); i += 1; }; let roota: str = "package main;\n// ROOT_A\n"; i = 0; for (i < packagecount) { if (i != 150) { roota = strings.concat(roota, "import ", boundarypkgname(i), ";\n"); }; i += 1; }; i = 0; for (i < packagecount) { if (i != 150) { let name: str = boundarypkgname(i); roota = strings.concat(roota, "fn use_", name, "() i32 = { return ", name, ".value(); };\n"); }; i += 1; }; let rootz: str = strings.concat("package main;\nimport p150;\n", "// ROOT_Z\nfn main() i32 = { return p150.value() - 42; };\n"); writefile(strings.concat(target, "/a.ww"), roota); writefile(strings.concat(target, "/z.ww"), rootz); let chainsource: str = strings.concat("package main;\nimport p000;\n", "// CHAIN_ROOT\nfn main() i32 = { return p000.value() - 42; };\n"); writefile(strings.concat(chainroot, "/main.ww"), chainsource); let rootaction: str = "target"; let chainaction: str = "chain"; let compilerwrapper: str = strings.concat(root, "/boundary-w6c.sh"); let assemblerwrapper: str = strings.concat(root, "/boundary-w6a.sh"); let linkerwrapper: str = strings.concat(root, "/boundary-w6l.sh"); writeexecutable(compilerwrapper, strings.concat( "#!/bin/sh\nprintf 'BEGIN' >> \"$WW_BOUNDARY_COMPILER_TRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_BOUNDARY_COMPILER_TRACE\"; done\n", "printf '\\n' >> \"$WW_BOUNDARY_COMPILER_TRACE\"\n", "exec \"$WW_BOUNDARY_W6C\" \"$@\"\n")); writeexecutable(assemblerwrapper, strings.concat( "#!/bin/sh\nprintf 'BEGIN' >> \"$WW_BOUNDARY_ASSEMBLER_TRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_BOUNDARY_ASSEMBLER_TRACE\"; done\n", "printf '\\n' >> \"$WW_BOUNDARY_ASSEMBLER_TRACE\"\n", "exec \"$WW_BOUNDARY_W6A\" \"$@\"\n")); writeexecutable(linkerwrapper, strings.concat( "#!/bin/sh\nprintf 'BEGIN' >> \"$WW_BOUNDARY_LINKER_TRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_BOUNDARY_LINKER_TRACE\"; done\n", "printf '\\n' >> \"$WW_BOUNDARY_LINKER_TRACE\"\n", "exec \"$WW_BOUNDARY_W6L\" \"$@\"\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 works: []str = [strings.concat(root, "/boundary-c-work"), strings.concat(root, "/boundary-ww-work")]; let bins: []str = [strings.concat(root, "/boundary-c-bin"), strings.concat(root, "/boundary-ww-bin")]; let chainbins: []str = [strings.concat(root, "/boundary-c-chain-bin"), strings.concat(root, "/boundary-ww-chain-bin")]; let compilertraces: []str = [strings.concat(root, "/boundary-c-compiler"), strings.concat(root, "/boundary-ww-compiler")]; let assemblertraces: []str = [strings.concat(root, "/boundary-c-assembler"), strings.concat(root, "/boundary-ww-assembler")]; let linkertraces: []str = [strings.concat(root, "/boundary-c-linker"), strings.concat(root, "/boundary-ww-linker")]; let baseenv: []str = os.getenvs(); let environments: [][]str = alloc([], stages.len: u64)!; let out: commandout; let si: i32 = 0; for (si < stages.len) { assert(os.mkdir(works[si], 448i32) == 0); writefile(compilertraces[si], ""); writefile(assemblertraces[si], ""); writefile(linkertraces[si], ""); let env: []str = alloc([], (baseenv.len + 11): 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_BOUNDARY_COMPILER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_BOUNDARY_ASSEMBLER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_BOUNDARY_LINKER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_BOUNDARY_W6C=") && !strings.hasprefix(baseenv[ei], "WW_BOUNDARY_W6A=") && !strings.hasprefix(baseenv[ei], "WW_BOUNDARY_W6L=") && !strings.hasprefix(baseenv[ei], "WW_LIB=") && !strings.hasprefix(baseenv[ei], "WW_SRCLIB=")) { 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_BOUNDARY_COMPILER_TRACE=", compilertraces[si])); append(env, strings.concat("WW_BOUNDARY_ASSEMBLER_TRACE=", assemblertraces[si])); append(env, strings.concat("WW_BOUNDARY_LINKER_TRACE=", linkertraces[si])); append(env, strings.concat("WW_BOUNDARY_W6C=", driver(compilers[si]))); append(env, strings.concat("WW_BOUNDARY_W6A=", driver(assemblers[si]))); append(env, strings.concat("WW_BOUNDARY_W6L=", driver(linkers[si]))); append(env, strings.concat("WW_LIB=", repo(), "/out/bin/../lib")); append(env, strings.concat("WW_SRCLIB=", repo(), "/lib")); append(environments, env); let av: []str = [driver(stages[si]), "build", "-w", works[si], "-I", d3, "-I", root, "-o", bins[si], target]; runcommandenv(root, strings.concat("boundary-cold-", stages[si]), av, env, (600i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); let runav: []str = [bins[si]]; runcommand(root, strings.concat("boundary-run-", stages[si]), runav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); let ctrace: str = readfile(compilertraces[si]); let atrace: str = readfile(assemblertraces[si]); let ltrace: str = readfile(linkertraces[si]); assert(occurrences(ctrace, "\n") == packagecount + 1); assert(occurrences(atrace, "\n") == packagecount + 2); assert(occurrences(ltrace, "\n") == 1); let rootline: str = linecontaining(ctrace, strings.concat("/", rootaction, ".unit.new")); assert(occurrences(rootline, "<--import>") == packagecount); let expectedroot: str = strings.concat( "BEGIN<--entry><--package-init-symbol><__ww..pkg.p.", rootaction, ".v0.r0.init><--init-dispatch-symbol>", "<__ww..dispatch><-c>"); i = 0; for (i < packagecount) { let name: str = boundarypkgname(i); expectedroot = strings.concat(expectedroot, "<--import><", name, "><", works[si], "/", name, ".wwi.new>"); assert(occurrences(rootline, strings.concat("<", name, "><", works[si], "/", name, ".wwi.new>")) == 1); i += 1; }; expectedroot = strings.concat(expectedroot, "<-I><", works[si], "/", rootaction, ".wwi.new><-o><", works[si], "/", rootaction, ".s.new><", works[si], "/", rootaction, ".unit.new>"); assert(same(rootline, expectedroot)); i = 0; for (i < packagecount) { let name: str = boundarypkgname(i); let expectedcompile: str = strings.concat( "BEGIN<--package-init-symbol><__ww..pkg.p.", name, ".v0.r0.init><-c>"); if (i + 1 < packagecount) { let next: str = boundarypkgname(i + 1); expectedcompile = strings.concat(expectedcompile, "<--import><", next, "><", works[si], "/", next, ".wwi.new>"); }; expectedcompile = strings.concat(expectedcompile, "<-I><", works[si], "/", name, ".wwi.new><-o><", works[si], "/", name, ".s.new><", works[si], "/", name, ".unit.new>"); assert(same(linecontaining(ctrace, strings.concat("/", name, ".unit.new")), expectedcompile)); let expectedassemble: str = strings.concat("BEGIN<-o><", works[si], "/", name, ".o.new><", works[si], "/", name, ".s.new>"); assert(same(linecontaining(atrace, strings.concat("/", name, ".o.new")), expectedassemble)); assert(occurrences(ltrace, strings.concat("<", works[si], "/", name, ".a.new>")) == 1); assert(os.exists(strings.concat(works[si], "/", name, ".wwi"))); assert(os.exists(strings.concat(works[si], "/", name, ".s"))); assert(os.exists(strings.concat(works[si], "/", name, ".o"))); assert(os.exists(strings.concat(works[si], "/", name, ".a"))); let a: str = ""; if (i + 1 < packagecount) { let next: str = boundarypkgname(i + 1); a = strings.concat("package ", name, ";\nimport ", next, ";\n// OWNER_A_", name, "\nexport fn value() i32 = { return ", next, ".value(); };\n"); } else { a = strings.concat("package ", name, ";\n// OWNER_A_", name, "\nexport fn value() i32 = { return 42; };\n"); }; let z: str = strings.concat("package ", name, ";\n// OWNER_Z_", name, "\nfn private_value() i32 = { return value(); };\n"); let unit: str = readfile(strings.concat(works[si], "/", name, ".unit.ww")); assert(strings.hasprefix(unit, strings.concat("//ww:module-reset ", name, "\n", a, "\n//ww:module-reset ", name, "\n", z, "\n"))); if (i + 1 < packagecount) { assert(has(unit, strings.concat("//ww:direct-export ", boundarypkgname(i + 1), " "))); }; assert(strings.hasprefix(readfile(strings.concat(works[si], "/", name, ".wwi")), strings.concat("//ww:module ", name, "\n"))); i += 1; }; assert(occurrences(ltrace, strings.concat("<", works[si], "/", rootaction, ".a.new>")) == 1); assert(!has(ltrace, ".wwi>")); let expectedlink: str = strings.concat("BEGIN<-o><", bins[si], ".new><", works[si], "/", rootaction, ".a.new>"); i = 0; for (i < packagecount) { expectedlink = strings.concat(expectedlink, "<", works[si], "/", boundarypkgname(i), ".a.new>"); i += 1; }; expectedlink = strings.concat(expectedlink, "<", repo(), "/out/bin/../lib/libwwrt.a>\n"); assert(same(ltrace, expectedlink)); let rootunit: str = readfile(strings.concat(works[si], "/", rootaction, ".unit.ww")); assert(strings.hasprefix(rootunit, strings.concat("//ww:module-reset target\n", roota, "\n//ww:module-reset target\n", rootz, "\n"))); assert(occurrences(rootunit, "//ww:direct-export p") == packagecount); let rootassemble: str = strings.concat("BEGIN<-o><", works[si], "/", rootaction, ".o.new><", works[si], "/", rootaction, ".s.new>"); assert(same(linecontaining(atrace, strings.concat("/", rootaction, ".o.new")), rootassemble)); let dispatchassemble: str = strings.concat("BEGIN<-o><", works[si], "/", rootaction, ".init.o.new><", works[si], "/", rootaction, ".init.s.new>"); assert(same(linecontaining(atrace, strings.concat("/", rootaction, ".init.o.new")), dispatchassemble)); // Repeating the same semantic request in the persistent work root may // relink the requested binary, but must not compile or assemble any action. let cbefore: i32 = ctrace.len; let abefore: i32 = atrace.len; runcommandenv(root, strings.concat("boundary-warm-", stages[si]), av, env, (600i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(readfile(compilertraces[si]).len == cbefore); assert(readfile(assemblertraces[si]).len == abefore); // A second root importing only p000 proves that link closure expansion, // not the wide root's direct import list, reaches every archive beyond 256. let chaincbefore: i32 = readfile(compilertraces[si]).len; let chainabefore: i32 = readfile(assemblertraces[si]).len; let chainlbefore: i32 = readfile(linkertraces[si]).len; let chainav: []str = [driver(stages[si]), "build", "-w", works[si], "-I", d3, "-I", root, "-o", chainbins[si], chainroot]; runcommandenv(root, strings.concat("boundary-chain-", stages[si]), chainav, env, (600i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); let chainctrace: str = readfile(compilertraces[si]); let chainatrace: str = readfile(assemblertraces[si]); let chainltrace: str = readfile(linkertraces[si]); let cdelta: str = strings.sub(chainctrace, chaincbefore, chainctrace.len); let adelta: str = strings.sub(chainatrace, chainabefore, chainatrace.len); let ldelta: str = strings.sub(chainltrace, chainlbefore, chainltrace.len); assert(occurrences(cdelta, "\n") == 1); assert(occurrences(adelta, "\n") == 2); assert(occurrences(ldelta, "\n") == 1); assert(same(cdelta, strings.concat( "BEGIN<--entry><--package-init-symbol><__ww..pkg.p.", chainaction, ".v0.r0.init><--init-dispatch-symbol>", "<__ww..dispatch><-c><--import><", works[si], "/p000.wwi><-I><", works[si], "/", chainaction, ".wwi.new><-o><", works[si], "/", chainaction, ".s.new><", works[si], "/", chainaction, ".unit.new>\n"))); assert(same(adelta, strings.concat("BEGIN<-o><", works[si], "/", chainaction, ".o.new><", works[si], "/", chainaction, ".s.new>\n", "BEGIN<-o><", works[si], "/", chainaction, ".init.o.new><", works[si], "/", chainaction, ".init.s.new>\n"))); let expectedchainlink: str = strings.concat("BEGIN<-o><", chainbins[si], ".new><", works[si], "/", chainaction, ".a.new>"); i = 0; for (i < packagecount) { expectedchainlink = strings.concat(expectedchainlink, "<", works[si], "/", boundarypkgname(i), ".a>"); i += 1; }; expectedchainlink = strings.concat(expectedchainlink, "<", repo(), "/out/bin/../lib/libwwrt.a>\n"); assert(same(ldelta, expectedchainlink)); assert(!has(ldelta, ".wwi>")); let chainunit: str = readfile(strings.concat(works[si], "/", chainaction, ".unit.ww")); assert(strings.hasprefix(chainunit, strings.concat( "//ww:module-reset chain\n", chainsource, "\n"))); assert(has(chainunit, "//ww:direct-export p000 ")); let chainrun: []str = [chainbins[si]]; runcommand(root, strings.concat("boundary-chain-run-", stages[si]), chainrun, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); si += 1; }; let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"]; i = 0; for (i < packagecount) { let name: str = boundarypkgname(i); let xi: i32 = 0; for (xi < suffixes.len) { assert(same(readfile(strings.concat(works[0], "/", name, suffixes[xi])), readfile(strings.concat(works[1], "/", name, suffixes[xi])))); xi += 1; }; i += 1; }; let xi: i32 = 0; for (xi < suffixes.len) { assert(same(readfile(strings.concat(works[0], "/", rootaction, suffixes[xi])), readfile(strings.concat(works[1], "/", rootaction, suffixes[xi])))); xi += 1; }; assert(same(readfile(bins[0]), readfile(bins[1]))); xi = 0; for (xi < suffixes.len) { assert(same(readfile(strings.concat(works[0], "/", chainaction, suffixes[xi])), readfile(strings.concat(works[1], "/", chainaction, suffixes[xi])))); xi += 1; }; assert(same(readfile(chainbins[0]), readfile(chainbins[1]))); // p257 is inserted beyond the former 256-action boundary. A changed // export recompiles that package, its direct importer p256, and the root // (which directly imports every package), then stops at p255 because the // regenerated p256 export is byte-identical. let changed: str = strings.concat(root, "/p257/a.ww"); rewritefile(changed, strings.concat( "package p257;\nimport p258;\n// OWNER_A_p257_CHANGED\n", "export fn value() i32 = { return p258.value(); };\n", "export fn changed() i32 = { return 257; };\n")); si = 0; for (si < stages.len) { let before: str = readfile(compilertraces[si]); let av: []str = [driver(stages[si]), "build", "-w", works[si], "-I", d3, "-I", root, "-o", bins[si], target]; runcommandenv(root, strings.concat("boundary-change-", stages[si]), av, environments[si], (600i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); let after: str = readfile(compilertraces[si]); let delta: str = strings.sub(after, before.len, after.len); assert(occurrences(delta, "\n") == 3); assert(occurrences(delta, "/p257.unit.new") == 1); assert(occurrences(delta, "/p256.unit.new") == 1); assert(occurrences(delta, strings.concat("/", rootaction, ".unit.new")) == 1); assert(!has(delta, "/p255.unit.new")); assert(!has(delta, "/p258.unit.new")); let runav: []str = [bins[si]]; runcommand(root, strings.concat("boundary-change-run-", stages[si]), runav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); si += 1; }; assert(same(readfile(strings.concat(works[0], "/p257.wwi")), readfile(strings.concat(works[1], "/p257.wwi")))); assert(same(readfile(bins[0]), readfile(bins[1]))); // Close the 300-node chain into a cycle. Both graph implementations must // emit the complete deterministic path before compiler, assembler, or linker. rewritefile(strings.concat(root, "/p299/a.ww"), strings.concat( "package p299;\nimport p000;\n// OWNER_A_p299_CYCLE\n", "export fn value() i32 = { return p000.value(); };\n")); let wantcycle: str = "ww: dependency cycle: "; i = 0; for (i < packagecount) { if (i != 0) { wantcycle = strings.concat(wantcycle, " -> "); }; wantcycle = strings.concat(wantcycle, boundarypkgname(i)); i += 1; }; wantcycle = strings.concat(wantcycle, " -> p000\n"); let referencecycle: str = ""; si = 0; for (si < stages.len) { rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); let cyclework: str = strings.concat(root, "/cycle-work-", stages[si]); assert(os.mkdir(cyclework, 448i32) == 0); let cyclebin: str = strings.concat(root, "/cycle-bin-", stages[si]); let av: []str = [driver(stages[si]), "build", "-w", cyclework, "-I", d3, "-I", root, "-o", cyclebin, target]; runcommandenv(root, strings.concat("boundary-cycle-", stages[si]), av, environments[si], (600i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0); assert(same(out.stderr, wantcycle)); assert(readfile(compilertraces[si]).len == 0); assert(readfile(assemblertraces[si]).len == 0); assert(readfile(linkertraces[si]).len == 0); if (si == 0) { referencecycle = strings.dup(out.stderr); } else { assert(same(referencecycle, out.stderr)); }; si += 1; }; clean(root); }; @test fn dynamic_package_universe_crosses_former_boundary() void = { let root: str = fresh(); let suite: str = strings.concat(root, "/suite"); assert(os.mkdir(suite, 448i32) == 0); let testcount: i32 = 52; let notestcount: i32 = 205; let productcount: i32 = testcount + notestcount; let actioncount: i32 = testcount * 3 + notestcount + 9; let assemblycount: i32 = actioncount + testcount; let i: i32 = 0; for (i < productcount) { let name: str = boundarypkgname(i); let dir: str = strings.concat(suite, "/", name); assert(os.mkdir(dir, 448i32) == 0); writefile(strings.concat(dir, "/prod.ww"), strings.concat( "package ", name, ";\n// DYNAMIC_PRODUCTION_", name, "\nexport fn value() i32 = { return 42; };\n")); if (i < testcount) { writefile(strings.concat(dir, "/same_test.ww"), strings.concat( "package ", name, ";\n// DYNAMIC_INTERNAL_", name, "\n@test fn same_value() void = { assert(value() == 42); };\n")); writefile(strings.concat(dir, "/external_test.ww"), strings.concat( "package ", name, "_test;\nimport ", name, ";\n// DYNAMIC_EXTERNAL_", name, "\n@test fn external_value() void = { assert(", name, ".value() == 42); };\n")); }; i += 1; }; let compilerwrapper: str = strings.concat(root, "/universe-w6c.sh"); let assemblerwrapper: str = strings.concat(root, "/universe-w6a.sh"); let linkerwrapper: str = strings.concat(root, "/universe-w6l.sh"); writeexecutable(compilerwrapper, strings.concat( "#!/bin/sh\nprintf '%s\\n' \"$*\" >> ", "\"$WW_UNIVERSE_COMPILER_TRACE\"\n", "exec \"$WW_UNIVERSE_W6C\" \"$@\"\n")); writeexecutable(assemblerwrapper, strings.concat( "#!/bin/sh\nprintf '%s\\n' \"$*\" >> ", "\"$WW_UNIVERSE_ASSEMBLER_TRACE\"\n", "exec \"$WW_UNIVERSE_W6A\" \"$@\"\n")); writeexecutable(linkerwrapper, strings.concat( "#!/bin/sh\nprintf '%s\\n' \"$*\" >> ", "\"$WW_UNIVERSE_LINKER_TRACE\"\n", "exec \"$WW_UNIVERSE_W6L\" \"$@\"\n")); let coordinatorproxy: str = strings.concat(root, "/universe-coordinator.sh"); let driverwrapper: str = strings.concat(root, "/universe-driver.sh"); writeexecutable(coordinatorproxy, strings.concat( "#!/bin/sh\n", "if [ \"$1\" != package ] || [ \"$2\" != --ww-driver ] || ", "[ \"$#\" -lt 3 ]; then exit 97; fi\n", "shift 3\nexec \"$WW_UNIVERSE_WWTEST\" package --ww-driver ", "\"$WW_UNIVERSE_DRIVER_WRAPPER\" \"$@\"\n")); writeexecutable(driverwrapper, strings.concat( "#!/bin/sh\ncount=0\n", "for arg in \"$@\"; do\n", " if [ \"$arg\" = --ww-package-test ]; then count=$((count + 1)); fi\n", "done\nprintf '%s\\n' \"$count\" >> ", "\"$WW_UNIVERSE_BUILDER_TRACE\"\n", "exec \"$WW_UNIVERSE_DRIVER\" \"$@\"\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 works: []str = [strings.concat(root, "/universe-c-work"), strings.concat(root, "/universe-ww-work")]; let compilertraces: []str = [strings.concat(root, "/universe-c-w6c"), strings.concat(root, "/universe-ww-w6c")]; let assemblertraces: []str = [strings.concat(root, "/universe-c-w6a"), strings.concat(root, "/universe-ww-w6a")]; let linkertraces: []str = [strings.concat(root, "/universe-c-w6l"), strings.concat(root, "/universe-ww-w6l")]; let buildertraces: []str = [strings.concat(root, "/universe-c-builder"), strings.concat(root, "/universe-ww-builder")]; let referencecompiler: str = ""; let referenceassembler: str = ""; let referencelinker: str = ""; let referencebinary: str = ""; let referenceartifacts: []str = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""]; let coordinatoroutputs: []str = ["", ""]; let baseenv: []str = os.getenvs(); let out: commandout; let si: i32 = 0; for (si < stages.len) { assert(os.mkdir(works[si], 448i32) == 0); writefile(compilertraces[si], ""); writefile(assemblertraces[si], ""); writefile(linkertraces[si], ""); writefile(buildertraces[si], ""); let env: []str = alloc([], (baseenv.len + 17): 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_UNIVERSE_COMPILER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_UNIVERSE_ASSEMBLER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_UNIVERSE_LINKER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_UNIVERSE_W6C=") && !strings.hasprefix(baseenv[ei], "WW_UNIVERSE_W6A=") && !strings.hasprefix(baseenv[ei], "WW_UNIVERSE_W6L=") && !strings.hasprefix(baseenv[ei], "WW_UNIVERSE_WWTEST=") && !strings.hasprefix(baseenv[ei], "WW_UNIVERSE_DRIVER_WRAPPER=") && !strings.hasprefix(baseenv[ei], "WW_UNIVERSE_DRIVER=") && !strings.hasprefix(baseenv[ei], "WW_UNIVERSE_BUILDER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_WWTEST=") && !strings.hasprefix(baseenv[ei], "WW_LIB=") && !strings.hasprefix(baseenv[ei], "WW_SRCLIB=")) { 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_UNIVERSE_COMPILER_TRACE=", compilertraces[si])); append(env, strings.concat("WW_UNIVERSE_ASSEMBLER_TRACE=", assemblertraces[si])); append(env, strings.concat("WW_UNIVERSE_LINKER_TRACE=", linkertraces[si])); append(env, strings.concat("WW_UNIVERSE_W6C=", driver(compilers[si]))); append(env, strings.concat("WW_UNIVERSE_W6A=", driver(assemblers[si]))); append(env, strings.concat("WW_UNIVERSE_W6L=", driver(linkers[si]))); append(env, strings.concat("WW_LIB=", repo(), "/out/bin/../lib")); append(env, strings.concat("WW_SRCLIB=", repo(), "/lib")); append(env, strings.concat("WW_WWTEST=", coordinatorproxy)); append(env, strings.concat("WW_UNIVERSE_WWTEST=", repo(), "/out/bin/wwtest")); append(env, strings.concat("WW_UNIVERSE_DRIVER_WRAPPER=", driverwrapper)); append(env, strings.concat("WW_UNIVERSE_DRIVER=", driver(stages[si]))); append(env, strings.concat("WW_UNIVERSE_BUILDER_TRACE=", buildertraces[si])); let av: []str = alloc([], (8 + productcount * 10): u64)!; append(av, driver(stages[si])); append(av, "test"); append(av, "-c"); append(av, "-w"); append(av, works[si]); append(av, "-I"); append(av, suite); if (si == 0) { i = 0; for (i < productcount) { let name: str = boundarypkgname(i); let dir: str = strings.concat(suite, "/", name); append(av, "--ww-package-test"); append(av, "test"); append(av, name); append(av, name); if (i < testcount) { append(av, name); append(av, strings.concat(name, "_test")); } else { append(av, "-"); append(av, "-"); }; append(av, dir); append(av, strings.concat(root, "/product-", name, ".test")); append(av, "-"); append(av, strings.concat(root, "/product-", name, ".status")); i += 1; }; } else { i = productcount - 1; for (i >= 0) { let name: str = boundarypkgname(i); let dir: str = strings.concat(suite, "/", name); append(av, "--ww-package-test"); append(av, "test"); append(av, name); append(av, name); if (i < testcount) { append(av, name); append(av, strings.concat(name, "_test")); } else { append(av, "-"); append(av, "-"); }; append(av, dir); append(av, strings.concat(root, "/product-", name, ".test")); append(av, "-"); append(av, strings.concat(root, "/product-", name, ".status")); i -= 1; }; }; append(av, strings.concat(suite, "/p000")); assert(av.len == 8 + productcount * 10); runcommandenv(root, strings.concat("dynamic-universe-", stages[si]), av, env, (1200i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); let ctrace: str = readfile(compilertraces[si]); let atrace: str = readfile(assemblertraces[si]); let ltrace: str = readfile(linkertraces[si]); assert(occurrences(ctrace, "\n") == actioncount); assert(occurrences(atrace, "\n") == assemblycount); assert(occurrences(ltrace, "\n") == testcount); assert(!has(ltrace, ".wwi")); assert(occurrences(ctrace, "/test.unit.new") == 1); i = 0; for (i < productcount) { let name: str = boundarypkgname(i); if (i < testcount) { assert(occurrences(ctrace, strings.concat("/", name, "-internal-test.unit.new")) == 1); assert(occurrences(ctrace, strings.concat("/", name, "_test-external-test.unit.new")) == 1); assert(occurrences(ctrace, strings.concat("/", name, "-test-main.unit.new")) == 1); assert(occurrences(ctrace, strings.concat("/", name, ".unit.new")) == 0); assert(os.exists(strings.concat(root, "/product-", name, ".test"))); } else { assert(occurrences(ctrace, strings.concat("/", name, ".unit.new")) == 1); assert(occurrences(ctrace, strings.concat("/", name, "-test-main.unit.new")) == 0); assert(!os.exists(strings.concat(root, "/product-", name, ".test"))); }; assert(same(readfile(strings.concat(root, "/product-", name, ".status")), "ok\n")); i += 1; }; let samplemain: str = linecontaining(ctrace, "/p051-test-main.unit.new"); assert(has(samplemain, "--test-target-package p051 --test-target-package p051_test")); let sampleexternal: str = linecontaining(ctrace, "/p051_test-external-test.unit.new"); assert(has(sampleexternal, strings.concat("--import p051 ", works[si], "/p051-internal-test.wwi.new"))); let samplelink: str = linecontaining(ltrace, strings.concat("-o ", root, "/product-p051.test.new ")); assert(has(samplelink, "/p051-test-main.a.new")); assert(has(samplelink, "/p051-internal-test.a.new")); assert(has(samplelink, "/p051_test-external-test.a.new")); assert(!has(samplelink, strings.concat(works[si], "/p051.a"))); assert(!has(samplelink, ".wwi")); let runav: []str = [strings.concat(root, "/product-p051.test")]; runcommand(root, strings.concat("dynamic-run-", stages[si]), runav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "same_value ... ok\n")); assert(has(out.stdout, "external_value ... ok\n")); let samples: []str = ["p051-internal-test", "p051_test-external-test", "p051-test-main", "p256"]; let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"]; let sri: i32 = 0; for (sri < samples.len) { let xi: i32 = 0; for (xi < suffixes.len) { let bytes: str = readfile(strings.concat(works[si], "/", samples[sri], suffixes[xi])); let index: i32 = sri * suffixes.len + xi; if (si == 0) { referenceartifacts[index] = strings.dup(bytes); } else { assert(same(referenceartifacts[index], bytes)); }; xi += 1; }; sri += 1; }; let impossible: str = strings.concat(root, "/not-an-output"); let nc: str = normalizedtrace(ctrace, strings.concat(works[si], "/"), impossible); let na: str = normalizedtrace(atrace, strings.concat(works[si], "/"), impossible); let nl: str = normalizedtrace(ltrace, strings.concat(works[si], "/"), impossible); if (si == 0) { referencecompiler = nc; referenceassembler = na; referencelinker = nl; referencebinary = readfile(strings.concat(root, "/product-p051.test")); } else { assert(same(referencecompiler, nc)); assert(same(referenceassembler, na)); assert(same(referencelinker, nl)); assert(same(referencebinary, readfile(strings.concat(root, "/product-p051.test")))); }; // The complete >256 action universe is reused on an unchanged request. rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); runcommandenv(root, strings.concat("dynamic-warm-", stages[si]), av, env, (1200i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(readfile(compilertraces[si]).len == 0); assert(readfile(assemblertraces[si]).len == 0); assert(occurrences(readfile(linkertraces[si]), "\n") == testcount); // The public recursive coordinator emits the same 257 canonical // directory products in one delegated request, runs 52 binaries, and // takes the no-real-run path for all remaining directories. rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); let coordinatorav: []str = [driver(stages[si]), "test", "-j", "1", "-w", works[si], "-I", suite, strings.concat(suite, "/...")]; runcommandenv(root, strings.concat("dynamic-coordinator-", stages[si]), coordinatorav, env, (1200i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0); coordinatoroutputs[si] = strings.dup(out.stdout); assert(readfile(compilertraces[si]).len == 0); assert(readfile(assemblertraces[si]).len == 0); assert(occurrences(readfile(linkertraces[si]), "\n") == testcount); assert(same(readfile(buildertraces[si]), "257\n")); assert(occurrences(out.stdout, "ok ") == testcount); assert(occurrences(out.stdout, "? ") == notestcount); assert(has(out.stdout, strings.concat("ok ", suite, "/p051 [p051]\n"))); assert(has(out.stdout, strings.concat("? ", suite, "/p256 [no test files]\n"))); let beforecompiler: i32 = readfile(compilertraces[si]).len; runcommandenv(root, strings.concat("dynamic-coordinator-warm-", stages[si]), coordinatorav, env, (1200i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(coordinatoroutputs[si], out.stdout)); assert(readfile(compilertraces[si]).len == beforecompiler); assert(same(readfile(buildertraces[si]), "257\n257\n")); si += 1; }; assert(same(coordinatoroutputs[0], coordinatoroutputs[1])); clean(root); }; @test fn multi_directory_test_failures_are_isolated() void = { let root: str = fresh(); let suite: str = strings.concat(root, "/suite"); let alpha: str = strings.concat(suite, "/alpha"); let beta: str = strings.concat(suite, "/beta"); assert(os.mkdir(suite, 448i32) == 0); assert(os.mkdir(alpha, 448i32) == 0); assert(os.mkdir(beta, 448i32) == 0); writefile(strings.concat(alpha, "/alpha.ww"), strings.concat( "package alpha;\n", "fn hidden() i32 = { return 41; };\n", "export fn shown() i32 = { return 41; };\n")); writefile(strings.concat(alpha, "/same_test.ww"), strings.concat( "package alpha;\n", "@test fn alpha_same_survives() void = {", " assert(hidden() == 41); };\n")); writefile(strings.concat(alpha, "/external_test.ww"), strings.concat( "package alpha_test;\nimport alpha;\n", "@test fn alpha_external_fails() void = {", " assert(alpha.hidden() == 41); };\n")); writefile(strings.concat(beta, "/beta.ww"), strings.concat( "package beta;\n", "fn hidden() i32 = { return 42; };\n", "export fn shown() i32 = { return 42; };\n")); writefile(strings.concat(beta, "/same_test.ww"), strings.concat( "package beta;\n", "@test fn beta_same_survives() void = {", " assert(hidden() == 42); };\n")); writefile(strings.concat(beta, "/external_test.ww"), strings.concat( "package beta_test;\nimport beta;\n", "@test fn beta_external_survives() void = {", " assert(beta.shown() == 42); };\n")); let stages: []str = ["ww", "ww_ww"]; let jobs: []str = ["1", "4"]; let diagnostics: []str = ["", ""]; let referenceout: str = ""; let out: commandout; let i: i32 = 0; for (i < stages.len) { let av: []str = [driver(stages[i]), "test", "-j", jobs[i], "-I", root, strings.concat(suite, "/...")]; runcommand(root, strings.concat("multidir-failure-", stages[i]), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stdout, "FAIL\n")); assert(has(out.stderr, "has no exported declaration 'hidden'")); assert(has(out.stderr, strings.concat("FAIL ", alpha, " [alpha] (build exit 1)\n"))); assert(has(out.stderr, strings.concat("FAIL ", beta, " [beta] (build exit 1)\n"))); assert(occurrences(out.stderr, strings.concat("FAIL ", alpha)) == 1); assert(occurrences(out.stderr, strings.concat("FAIL ", beta)) == 1); assert(!has(out.stderr, "[alpha_test]")); assert(!has(out.stderr, "[beta_test]")); let alphapos: i32 = pos(out.stderr, strings.concat("FAIL ", alpha, " [alpha] (build exit 1)\n")); let betapos: i32 = pos(out.stderr, strings.concat("FAIL ", beta, " [beta] (build exit 1)\n")); assert(alphapos >= 0 && alphapos < betapos); diagnostics[i] = strings.dup(primarydiagnostic(out.stderr)); if (i == 0) { referenceout = strings.dup(out.stdout); } else { assert(same(referenceout, out.stdout)); }; i += 1; }; assert(same(diagnostics[0], diagnostics[1])); clean(root); }; // Roots remain independently diagnosed and may be staged in parallel, but one // failing product rejects publication and execution of the entire request. @test fn sibling_test_variant_failures_are_isolated() void = { let root: str = fresh(); let badexternal: str = strings.concat(root, "/badexternal"); let badsame: str = strings.concat(root, "/badsame"); assert(os.mkdir(badexternal, 448i32) == 0); assert(os.mkdir(badsame, 448i32) == 0); writefile(strings.concat(badexternal, "/badexternal.ww"), strings.concat( "package badexternal;\n", "fn hidden() i32 = { return 41; };\n", "export fn shown() i32 = { return 41; };\n")); writefile(strings.concat(badexternal, "/same_test.ww"), strings.concat( "package badexternal;\n", "@test fn valid_same_runs() void = { assert(hidden() == 41); };\n")); writefile(strings.concat(badexternal, "/external_test.ww"), strings.concat( "package badexternal_test;\nimport badexternal;\n", "@test fn invalid_external() void = {\n", " assert(badexternal.hidden() == 41);\n};\n")); writefile(strings.concat(badsame, "/badsame.ww"), strings.concat( "package badsame;\n", "export fn shown() i32 = { return 42; };\n")); writefile(strings.concat(badsame, "/same_test.ww"), strings.concat( "package badsame;\n", "@test fn invalid_same() void = { assert(missing() == 0); };\n")); writefile(strings.concat(badsame, "/external_test.ww"), strings.concat( "package badsame_test;\nimport badsame;\n", "@test fn valid_external_runs() void = {\n", " assert(badsame.shown() == 42);\n};\n")); let stages: []str = ["ww", "ww_ww"]; let tags: []str = ["c", "ww"]; let externaldiagnostics: []str = ["", ""]; let samediagnostics: []str = ["", ""]; let out: commandout; let i: i32 = 0; for (i < stages.len) { let externalav: []str = [driver(stages[i]), "test", "-I", root, badexternal]; runcommand(root, strings.concat("isolated-external-", tags[i]), externalav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stdout, "FAIL\n")); assert(has(out.stderr, "has no exported declaration 'hidden'")); assert(has(out.stderr, strings.concat("FAIL ", badexternal, " [badexternal] (build exit 1)\n"))); assert(occurrences(out.stderr, strings.concat("FAIL ", badexternal)) == 1); assert(!has(out.stderr, "[badexternal_test]")); externaldiagnostics[i] = strings.dup(primarydiagnostic(out.stderr)); let sameav: []str = [driver(stages[i]), "test", "-I", root, badsame]; runcommand(root, strings.concat("isolated-same-", tags[i]), sameav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stdout, "FAIL\n")); assert(has(out.stderr, "undefined: missing")); assert(has(out.stderr, strings.concat("FAIL ", badsame, " [badsame] (build exit 1)\n"))); assert(occurrences(out.stderr, strings.concat("FAIL ", badsame)) == 1); assert(!has(out.stderr, "[badsame_test]")); samediagnostics[i] = strings.dup(primarydiagnostic(out.stderr)); i += 1; }; assert(same(externaldiagnostics[0], externaldiagnostics[1])); assert(same(samediagnostics[0], samediagnostics[1])); // A persistent request is one publication transaction. A rejection leaves // no reusable generation, so every staged action is reconsidered next time. let warmroot: str = strings.concat(root, "/warm-partial"); assert(os.mkdir(warmroot, 448i32) == 0); let trace: str = strings.concat(root, "/warm-partial.trace"); let wrapper: str = strings.concat(root, "/warm-partial-w6c.sh"); writefile(trace, ""); writeexecutable(wrapper, strings.concat( "#!/bin/sh\n", "printf '%s\\n' \"$*\" >> \"$WW_PACKAGE_COMPILER_TRACE\"\n", "exec \"$WW_PACKAGE_W6C\" \"$@\"\n")); let baseenv: []str = os.getenvs(); let traceenv: []str = alloc([], (baseenv.len + 3): u64)!; let ei: i32 = 0; for (ei < baseenv.len) { if (!strings.hasprefix(baseenv[ei], "WW_W6C=") && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_COMPILER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6C=")) { append(traceenv, baseenv[ei]); }; ei += 1; }; append(traceenv, strings.concat("WW_W6C=", wrapper)); append(traceenv, strings.concat("WW_PACKAGE_COMPILER_TRACE=", trace)); append(traceenv, strings.concat("WW_PACKAGE_W6C=", driver("w6c"))); let warmav: []str = [driver("ww"), "test", "-j", "4", "-w", warmroot, "-I", root, badexternal]; runcommandenv(root, "isolated-warm-first", warmav, traceenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); let firsttrace: str = readfile(trace); assert(occurrences(firsttrace, "/badexternal-internal-test.unit.new") == 1); assert(occurrences(firsttrace, "/badexternal_test-external-test.unit.new") == 1); runcommandenv(root, "isolated-warm-reuse", warmav, traceenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); let secondtrace: str = readfile(trace); assert(occurrences(secondtrace, "/badexternal-internal-test.unit.new") == 2); assert(occurrences(secondtrace, "/badexternal_test-external-test.unit.new") == 2); assert(!os.exists(strings.concat(warmroot, "/badexternal-internal-test.unit.ww"))); assert(!os.exists(strings.concat(warmroot, "/badexternal_test-external-test.unit.ww"))); assert(!os.exists(strings.concat(warmroot, "/.wwtool.stamp"))); let wwtracebin: str = strings.concat(repo(), "/out/partial-trace-", workescape(root)); assert(os.mkdir(wwtracebin, 448i32) == 0); let wwdriver: str = strings.concat(wwtracebin, "/ww_ww"); let wwtrace: str = strings.concat(root, "/warm-partial-ww.trace"); let wwwarmroot: str = strings.concat(root, "/warm-partial-ww"); assert(os.mkdir(wwwarmroot, 448i32) == 0); writefile(wwtrace, ""); writeexecutable(wwdriver, readfile(driver("ww_ww"))); writeexecutable(strings.concat(wwtracebin, "/wwtest"), readfile(driver("wwtest"))); writeexecutable(strings.concat(wwtracebin, "/w6c_ww"), strings.concat( "#!/bin/sh\n", "printf '%s\\n' \"$*\" >> \"$WW_PACKAGE_WW_COMPILER_TRACE\"\n", "exec \"$WW_PACKAGE_W6C_WW\" \"$@\"\n")); writeexecutable(strings.concat(wwtracebin, "/w6a_ww"), readfile(driver("w6a_ww"))); writeexecutable(strings.concat(wwtracebin, "/w6l_ww"), readfile(driver("w6l_ww"))); let wwenv: []str = alloc([], (baseenv.len + 2): u64)!; ei = 0; for (ei < baseenv.len) { if (!strings.hasprefix(baseenv[ei], "WW_PACKAGE_WW_COMPILER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6C_WW=")) { append(wwenv, baseenv[ei]); }; ei += 1; }; append(wwenv, strings.concat("WW_PACKAGE_WW_COMPILER_TRACE=", wwtrace)); append(wwenv, strings.concat("WW_PACKAGE_W6C_WW=", driver("w6c_ww"))); let wwwarmav: []str = [wwdriver, "test", "-j", "4", "-w", wwwarmroot, "-I", root, badexternal]; runcommandenv(root, "isolated-warm-ww-first", wwwarmav, wwenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); let wwfirst: str = readfile(wwtrace); assert(occurrences(wwfirst, "/badexternal-internal-test.unit.new") == 1); assert(occurrences(wwfirst, "/badexternal_test-external-test.unit.new") == 1); runcommandenv(root, "isolated-warm-ww-reuse", wwwarmav, wwenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); let wwsecond: str = readfile(wwtrace); assert(occurrences(wwsecond, "/badexternal-internal-test.unit.new") == 2); assert(occurrences(wwsecond, "/badexternal_test-external-test.unit.new") == 2); assert(!os.exists(strings.concat(wwwarmroot, "/badexternal-internal-test.unit.ww"))); assert(!os.exists(strings.concat(wwwarmroot, "/badexternal_test-external-test.unit.ww"))); assert(!os.exists(strings.concat(wwwarmroot, "/.wwtool.stamp"))); clean(wwtracebin); // Late failures are request-wide transactions. Start from a committed // combined product, stage a changed dependency first, and then fail each // family action or the linker twice. Every retry must reconsider the same // work and the committed generation must remain byte-identical. let latedep: str = strings.concat(root, "/late_dep"); let latebridge: str = strings.concat(root, "/late_bridge"); let late: str = strings.concat(root, "/late"); assert(os.mkdir(latedep, 448i32) == 0); assert(os.mkdir(latebridge, 448i32) == 0); assert(os.mkdir(late, 448i32) == 0); let depfile: str = strings.concat(latedep, "/dep.ww"); let latefile: str = strings.concat(late, "/late.ww"); let latebridgefile: str = strings.concat(latebridge, "/bridge.ww"); let latesamefile: str = strings.concat(late, "/same_test.ww"); let lateexternalfile: str = strings.concat(late, "/external_test.ww"); let depbase: str = strings.concat("package late_dep;\n", "export fn value() i32 = { return 40; };\n"); let depchanged: str = strings.concat("package late_dep;\n", "export fn value() i32 = { return 39 + 1; };\n"); let samebase: str = strings.concat("package late;\n", "export fn internal_helper() i32 = { return 41; };\n", "@test fn internal_base() void = { assert(value() == 41); };\n"); let samechanged: str = strings.concat("package late;\n", "export fn internal_helper() i32 = { return 40 + 1; };\n", "@test fn internal_base() void = { assert(value() == 41); };\n"); let externalbase: str = strings.concat( "package late_test;\nimport late;\nimport late_bridge;\n", "@test fn external_base() void = {\n", " assert(late.internal_helper() == 41);\n", " assert(late_bridge.helper() == 41);\n};\n"); let externalchanged: str = strings.concat( "package late_test;\nimport late;\nimport late_bridge;\n", "@test fn external_base() void = {\n", " assert(late.internal_helper() >= 41);\n", " assert(late_bridge.helper() >= 41);\n};\n"); let externalmainchanged: str = strings.concat( "package late_test;\nimport late;\nimport late_bridge;\n", "@test fn external_renamed() void = {\n", " assert(late.internal_helper() == 41);\n", " assert(late_bridge.helper() == 41);\n};\n"); writefile(depfile, depbase); writefile(latefile, strings.concat("package late;\nimport late_dep;\n", "export fn value() i32 = { return late_dep.value() + 1; };\n")); writefile(latebridgefile, strings.concat( "package late_bridge;\nimport late;\n", "export fn helper() i32 = { return late.internal_helper(); };\n")); writefile(latesamefile, samebase); writefile(lateexternalfile, externalbase); let latecompilerwrapper: str = strings.concat(root, "/late-w6c.sh"); let lateassemblerwrapper: str = strings.concat(root, "/late-w6a.sh"); let latelinkerwrapper: str = strings.concat(root, "/late-w6l.sh"); writeexecutable(latecompilerwrapper, strings.concat( "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_LATE_COMPILER_TRACE\"\n", "if test -n \"$WW_LATE_FAIL_FRAGMENT\"; then\n", " for arg do case \"$arg\" in *\"$WW_LATE_FAIL_FRAGMENT\"*)\n", " printf 'injected w6c failure: %s\\n' ", "\"$WW_LATE_FAIL_FRAGMENT\" >&2\n exit 97;; esac; done\nfi\n", "exec \"$WW_LATE_REAL_COMPILER\" \"$@\"\n")); writeexecutable(lateassemblerwrapper, strings.concat( "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_LATE_ASSEMBLER_TRACE\"\n", "exec \"$WW_LATE_REAL_ASSEMBLER\" \"$@\"\n")); writeexecutable(latelinkerwrapper, strings.concat( "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_LATE_LINKER_TRACE\"\n", "if test \"$WW_LATE_FAIL_LINK\" = 1; then\n", " printf 'injected w6l failure\\n' >&2\n exit 97\nfi\n", "exec \"$WW_LATE_REAL_LINKER\" \"$@\"\n")); let lateworks: []str = [strings.concat(root, "/late-c-work"), strings.concat(root, "/late-ww-work")]; let latebins: []str = [strings.concat(root, "/late-c.test"), strings.concat(root, "/late-ww.test")]; let latestatuses: []str = [strings.concat(root, "/late-c.status"), strings.concat(root, "/late-ww.status")]; let latecompilers: []str = ["w6c", "w6c_ww"]; let lateassemblers: []str = ["w6a", "w6a_ww"]; let latelinkers: []str = ["w6l", "w6l_ww"]; let latebridgeowner: str = strings.concat("late#", late); let latebridgeaction: str = packagestoragekeyfor( "late_bridge", latebridge, 4, 0, latebridgeowner); let laterelative: []str = [".wwtool.ww", ".wwtool.w6c", ".wwtool.w6a", ".wwtool.stamp", "late_dep.unit.ww", "late_dep.wwi", "late_dep.s", "late_dep.o", "late_dep.a", strings.concat(latebridgeaction, ".unit.ww"), strings.concat(latebridgeaction, ".wwi"), strings.concat(latebridgeaction, ".s"), strings.concat(latebridgeaction, ".o"), strings.concat(latebridgeaction, ".a"), "late-internal-test.unit.ww", "late-internal-test.wwi", "late-internal-test.s", "late-internal-test.o", "late-internal-test.a", "late_test-external-test.unit.ww", "late_test-external-test.wwi", "late_test-external-test.s", "late_test-external-test.o", "late_test-external-test.a", "late-test-main.unit.ww", "late-test-main.wwi", "late-test-main.s", "late-test-main.o", "late-test-main.a", "late-test-main.init.unit.ww", "late-test-main.init.s", "late-test-main.init.o", "test.unit.ww", "test.wwi", "test.s", "test.o", "test.a"]; let latesnapshots: []str = alloc([], (stages.len * laterelative.len): u64)!; let snapshoti: i32 = 0; for (snapshoti < stages.len * laterelative.len) { append(latesnapshots, ""); snapshoti += 1; }; let latebinrefs: []str = ["", ""]; let latestatusrefs: []str = ["", ""]; let lateseedbaseenv: []str = os.getenvs(); i = 0; for (i < stages.len) { assert(os.mkdir(lateworks[i], 448i32) == 0); let seedctrace: str = strings.concat(root, "/late-seed-", tags[i], "-w6c.trace"); let seedatrace: str = strings.concat(root, "/late-seed-", tags[i], "-w6a.trace"); let seedltrace: str = strings.concat(root, "/late-seed-", tags[i], "-w6l.trace"); writefile(seedctrace, ""); writefile(seedatrace, ""); writefile(seedltrace, ""); let seedenv: []str = alloc([], (lateseedbaseenv.len + 11): u64)!; ei = 0; for (ei < lateseedbaseenv.len) { if (!strings.hasprefix(lateseedbaseenv[ei], "WW_W6C=") && !strings.hasprefix(lateseedbaseenv[ei], "WW_W6A=") && !strings.hasprefix(lateseedbaseenv[ei], "WW_W6L=") && !strings.hasprefix(lateseedbaseenv[ei], "WW_LATE_COMPILER_TRACE=") && !strings.hasprefix(lateseedbaseenv[ei], "WW_LATE_ASSEMBLER_TRACE=") && !strings.hasprefix(lateseedbaseenv[ei], "WW_LATE_LINKER_TRACE=") && !strings.hasprefix(lateseedbaseenv[ei], "WW_LATE_REAL_COMPILER=") && !strings.hasprefix(lateseedbaseenv[ei], "WW_LATE_REAL_ASSEMBLER=") && !strings.hasprefix(lateseedbaseenv[ei], "WW_LATE_REAL_LINKER=") && !strings.hasprefix(lateseedbaseenv[ei], "WW_LATE_FAIL_FRAGMENT=") && !strings.hasprefix(lateseedbaseenv[ei], "WW_LATE_FAIL_LINK=")) { append(seedenv, lateseedbaseenv[ei]); }; ei += 1; }; append(seedenv, strings.concat("WW_W6C=", latecompilerwrapper)); append(seedenv, strings.concat("WW_W6A=", lateassemblerwrapper)); append(seedenv, strings.concat("WW_W6L=", latelinkerwrapper)); append(seedenv, strings.concat("WW_LATE_COMPILER_TRACE=", seedctrace)); append(seedenv, strings.concat("WW_LATE_ASSEMBLER_TRACE=", seedatrace)); append(seedenv, strings.concat("WW_LATE_LINKER_TRACE=", seedltrace)); append(seedenv, strings.concat("WW_LATE_REAL_COMPILER=", driver(latecompilers[i]))); append(seedenv, strings.concat("WW_LATE_REAL_ASSEMBLER=", driver(lateassemblers[i]))); append(seedenv, strings.concat("WW_LATE_REAL_LINKER=", driver(latelinkers[i]))); append(seedenv, "WW_LATE_FAIL_FRAGMENT="); append(seedenv, "WW_LATE_FAIL_LINK=0"); let seedav: []str = [driver(stages[i]), "test", "-c", "-w", lateworks[i], "-I", root, "--ww-package-test", "test", "late", "late", "late", "late_test", late, latebins[i], "-", latestatuses[i], late]; runcommandenv(root, strings.concat("late-seed-", tags[i]), seedav, seedenv, (180i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(same(readfile(latestatuses[i]), "ok\n")); let runav: []str = [latebins[i]]; runcommand(root, strings.concat("late-seed-run-", tags[i]), runav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(occurrences(out.stdout, "internal_base ... ok\n") == 1); assert(occurrences(out.stdout, "external_base ... ok\n") == 1); latebinrefs[i] = readfile(latebins[i]); latestatusrefs[i] = readfile(latestatuses[i]); snapshoti = 0; for (snapshoti < laterelative.len) { latesnapshots[i * laterelative.len + snapshoti] = readfile( strings.concat(lateworks[i], "/", laterelative[snapshoti])); snapshoti += 1; }; i += 1; }; assert(same(latebinrefs[0], latebinrefs[1])); let phaselabels: []str = ["internal", "external", "main", "link"]; let phasefragments: []str = ["/late-internal-test.unit.new", "/late_test-external-test.unit.new", "/late-test-main.unit.new", ""]; let phaseowners: []str = ["late", "late_test", "__wwtestmain.late.main", ""]; let phasecompiles: []i32 = [2, 2, 3, 2]; let phaseassembles: []i32 = [1, 1, 2, 2]; let phaselinks: []i32 = [0, 0, 0, 1]; let phasectrefs: []str = ["", "", "", ""]; let phaseatrefs: []str = ["", "", "", ""]; let phaseltrefs: []str = ["", "", "", ""]; let phasediagnosticrefs: []str = ["", "", "", ""]; let lateactions: []str = ["late_dep", latebridgeaction, "late-internal-test", "late_test-external-test", "late-test-main", "test"]; let latestagesuffixes: []str = [".unit.new", ".wwi.new", ".s.new", ".o.new", ".a.new", ".init.unit.new", ".init.s.new", ".init.o.new"]; let latebaseenv: []str = os.getenvs(); let phasei: i32 = 0; for (phasei < phaselabels.len) { rewritefile(depfile, depchanged); if (phasei == 0 || phasei == 3) { rewritefile(latesamefile, samechanged); } else { rewritefile(latesamefile, samebase); }; if (phasei == 1) { rewritefile(lateexternalfile, externalchanged); } else { if (phasei == 2) { rewritefile(lateexternalfile, externalmainchanged); } else { rewritefile(lateexternalfile, externalbase); }; }; i = 0; for (i < stages.len) { let retry: i32 = 0; for (retry < 2) { let retrylabel: str = "first"; if (retry != 0) { retrylabel = "second"; }; let ctracepath: str = strings.concat(root, "/late-", phaselabels[phasei], "-", tags[i], "-", retrylabel, "-w6c.trace"); let atracepath: str = strings.concat(root, "/late-", phaselabels[phasei], "-", tags[i], "-", retrylabel, "-w6a.trace"); let ltracepath: str = strings.concat(root, "/late-", phaselabels[phasei], "-", tags[i], "-", retrylabel, "-w6l.trace"); writefile(ctracepath, ""); writefile(atracepath, ""); writefile(ltracepath, ""); let attemptbin: str = strings.concat(root, "/late-attempt-", phaselabels[phasei], "-", tags[i], "-", retrylabel, ".test"); let attemptstatus: str = strings.concat(root, "/late-attempt-", phaselabels[phasei], "-", tags[i], "-", retrylabel, ".status"); let env: []str = alloc([], (latebaseenv.len + 11): u64)!; ei = 0; for (ei < latebaseenv.len) { if (!strings.hasprefix(latebaseenv[ei], "WW_W6C=") && !strings.hasprefix(latebaseenv[ei], "WW_W6A=") && !strings.hasprefix(latebaseenv[ei], "WW_W6L=") && !strings.hasprefix(latebaseenv[ei], "WW_LATE_COMPILER_TRACE=") && !strings.hasprefix(latebaseenv[ei], "WW_LATE_ASSEMBLER_TRACE=") && !strings.hasprefix(latebaseenv[ei], "WW_LATE_LINKER_TRACE=") && !strings.hasprefix(latebaseenv[ei], "WW_LATE_REAL_COMPILER=") && !strings.hasprefix(latebaseenv[ei], "WW_LATE_REAL_ASSEMBLER=") && !strings.hasprefix(latebaseenv[ei], "WW_LATE_REAL_LINKER=") && !strings.hasprefix(latebaseenv[ei], "WW_LATE_FAIL_FRAGMENT=") && !strings.hasprefix(latebaseenv[ei], "WW_LATE_FAIL_LINK=")) { append(env, latebaseenv[ei]); }; ei += 1; }; append(env, strings.concat("WW_W6C=", latecompilerwrapper)); append(env, strings.concat("WW_W6A=", lateassemblerwrapper)); append(env, strings.concat("WW_W6L=", latelinkerwrapper)); append(env, strings.concat("WW_LATE_COMPILER_TRACE=", ctracepath)); append(env, strings.concat("WW_LATE_ASSEMBLER_TRACE=", atracepath)); append(env, strings.concat("WW_LATE_LINKER_TRACE=", ltracepath)); append(env, strings.concat("WW_LATE_REAL_COMPILER=", driver(latecompilers[i]))); append(env, strings.concat("WW_LATE_REAL_ASSEMBLER=", driver(lateassemblers[i]))); append(env, strings.concat("WW_LATE_REAL_LINKER=", driver(latelinkers[i]))); append(env, strings.concat("WW_LATE_FAIL_FRAGMENT=", phasefragments[phasei])); let faillink: str = "0"; if (phasei == 3) { faillink = "1"; }; append(env, strings.concat("WW_LATE_FAIL_LINK=", faillink)); let failav: []str = [driver(stages[i]), "test", "-c", "-w", lateworks[i], "-I", root, "--ww-package-test", "test", "late", "late", "late", "late_test", late, attemptbin, "-", attemptstatus, late]; runcommandenv(root, strings.concat("late-failure-", phaselabels[phasei], "-", tags[i], "-", retrylabel), failav, env, (180i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0); if (phasei == 3) { assert(has(out.stderr, "injected w6l failure\n")); assert(has(out.stderr, "ww: w6l failed\n")); } else { assert(has(out.stderr, strings.concat( "injected w6c failure: ", phasefragments[phasei], "\n"))); assert(has(out.stderr, strings.concat( "ww: w6c failed for ", phaseowners[phasei], "\n"))); }; assert(!os.exists(attemptbin)); assert(!os.exists(strings.concat(attemptbin, ".new"))); assert(!os.exists(strings.concat(attemptbin, ".result"))); assert(!os.exists(strings.concat(attemptbin, ".result.new"))); assert(!os.exists(attemptstatus)); assert(!os.exists(strings.concat(attemptstatus, ".new"))); let ct: str = readfile(ctracepath); let at: str = readfile(atracepath); let lt: str = readfile(ltracepath); assert(occurrences(ct, "\n") == phasecompiles[phasei]); assert(occurrences(at, "\n") == phaseassembles[phasei]); assert(occurrences(lt, "\n") == phaselinks[phasei]); assert(pos(ct, "/late_dep.unit.new") >= 0); if (phasei != 3) { assert(pos(ct, "/late_dep.unit.new") < pos(ct, phasefragments[phasei])); }; assert(!directoryhasnew(lateworks[i])); let actioni: i32 = 0; for (actioni < lateactions.len) { let suffixi: i32 = 0; for (suffixi < latestagesuffixes.len) { assert(!os.exists(strings.concat(lateworks[i], "/", lateactions[actioni], latestagesuffixes[suffixi]))); suffixi += 1; }; actioni += 1; }; snapshoti = 0; for (snapshoti < laterelative.len) { assert(same(latesnapshots[i * laterelative.len + snapshoti], readfile(strings.concat(lateworks[i], "/", laterelative[snapshoti])))); snapshoti += 1; }; assert(same(latebinrefs[i], readfile(latebins[i]))); assert(same(latestatusrefs[i], readfile(latestatuses[i]))); let nc: str = normalizedtrace(ct, strings.concat(lateworks[i], "/"), attemptbin); let na: str = normalizedtrace(at, strings.concat(lateworks[i], "/"), attemptbin); let nl: str = normalizedtrace(lt, strings.concat(lateworks[i], "/"), attemptbin); let nd: str = normalizedtrace(out.stderr, strings.concat(lateworks[i], "/"), attemptbin); if (i == 0 && retry == 0) { phasectrefs[phasei] = nc; phaseatrefs[phasei] = na; phaseltrefs[phasei] = nl; phasediagnosticrefs[phasei] = nd; } else { assert(same(phasectrefs[phasei], nc)); assert(same(phaseatrefs[phasei], na)); assert(same(phaseltrefs[phasei], nl)); assert(same(phasediagnosticrefs[phasei], nd)); }; retry += 1; }; i += 1; }; phasei += 1; }; clean(root); }; // The compiler-generated runner uses a reserved graph qualifier rather than // claiming the user-visible import path `test`. A real multi-file production // package named test can therefore be the normal direct dependency of its // external test variant in both bootstrap stages. @test fn production_package_named_test_remains_available() void = { let root: str = fresh(); let pkg: str = strings.concat(root, "/test"); assert(os.mkdir(pkg, 448i32) == 0); writefile(strings.concat(pkg, "/a.ww"), strings.concat( "package test;\n", "// USER_TEST_PACKAGE_A\n", "export fn first() i32 = { return 19; };\n")); writefile(strings.concat(pkg, "/z.ww"), strings.concat( "package test;\n", "// USER_TEST_PACKAGE_Z\n", "export fn second() i32 = { return 23; };\n")); writefile(strings.concat(pkg, "/external_test.ww"), strings.concat( "package test_test;\nimport test;\n", "// USER_TEST_EXTERNAL\n", "@test fn package_name_is_not_reserved() void = {\n", " assert(test.first() + test.second() == 42);\n};\n")); let bin: str = strings.concat(root, "/named-test.bin"); let workroot: str = strings.concat(bin, ".sepwork"); let work: str = strings.concat(workroot, "/"); let stages: []str = ["ww", "ww_ww"]; let referencebin: str = ""; let referenceroot: str = ""; let referenceprod: str = ""; let referencesupport: str = ""; let out: commandout; let i: i32 = 0; for (i < stages.len) { let av: []str = [driver(stages[i]), "test", "-w", workroot, "-I", root, "-o", bin, pkg]; runcommand(root, strings.concat("named-test-build-", stages[i]), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let rootunit: str = readfile(strings.concat(work, "test_test-external-test.unit.ww")); let produnit: str = readfile(strings.concat(work, "test.unit.ww")); let supportunit: str = readfile(strings.concat(work, "__wwtest.unit.ww")); assert(!has(rootunit, "//ww:module ")); assert(has(rootunit, "USER_TEST_EXTERNAL")); assert(!has(rootunit, "USER_TEST_PACKAGE_A")); assert(!has(rootunit, "USER_TEST_PACKAGE_Z")); assert(has(produnit, "USER_TEST_PACKAGE_A")); assert(has(produnit, "USER_TEST_PACKAGE_Z")); assert(has(supportunit, "//ww:module-reset __wwtest\n")); assert(has(supportunit, "package test;\n")); assert(os.exists(strings.concat(work, "test.wwi"))); assert(os.exists(strings.concat(work, "test.a"))); assert(!os.exists(strings.concat(work, "__ww-test-000-external-production.unit.ww"))); assert(os.exists(strings.concat(work, "__wwtest.wwi"))); assert(os.exists(strings.concat(work, "__wwtest.a"))); let runav: []str = [bin]; runcommand(root, strings.concat("named-test-run-", stages[i]), runav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "package_name_is_not_reserved ... ok\n")); if (i == 0) { referencebin = readfile(bin); referenceroot = strings.dup(rootunit); referenceprod = strings.dup(produnit); referencesupport = strings.dup(supportunit); } else { assert(same(referencebin, readfile(bin))); assert(same(referenceroot, rootunit)); assert(same(referenceprod, produnit)); assert(same(referencesupport, supportunit)); }; if (i + 1 < stages.len) { clean(workroot); clean(bin); }; i += 1; }; clean(root); }; @test fn internal_package_import_visibility() void = { let root: str = fresh(); let source: str = strings.concat(root, "/host/internal/source"); let runtime: str = strings.concat(root, "/runtime"); let tools: str = strings.concat(root, "/tools"); let domain: str = strings.concat(source, "/domain"); let leaf: str = strings.concat(domain, "/leaf"); let secret: str = strings.concat(domain, "/internal/secret"); let client: str = strings.concat(domain, "/client"); let outsider: str = strings.concat(source, "/outsider"); let prefixclient: str = strings.concat(source, "/domainx/client"); let deep: str = strings.concat(domain, "/internal/outer/internal/deep"); let deepclient: str = strings.concat(domain, "/internal/outer/client"); let nestedoutsider: str = strings.concat(domain, "/nestedclient"); let internalx: str = strings.concat(domain, "/internalx/visible"); let internalxclient: str = strings.concat(source, "/internalxclient"); let testclient: str = strings.concat(domain, "/testclient"); let outsame: str = strings.concat(source, "/outsame"); let outexternal: str = strings.concat(source, "/outexternal"); let physical: str = strings.concat(root, "/physical/domain"); let linkedsecret: str = strings.concat(physical, "/internal/secret"); let linkedclient: str = strings.concat(physical, "/client"); let rawallowed: str = strings.concat(source, "/alias/rawclient.ww"); let rawalias: str = strings.concat(domain, "/rawalias.ww"); let rawescape: str = strings.concat(domain, "/rawescape.ww"); let aliasinternal: str = strings.concat(source, "/alias/internal"); let aliasclient: str = strings.concat(source, "/alias/client"); let aliasownedclient: str = strings.concat(source, "/alias/owned-client"); let aliasoutsider: str = strings.concat(source, "/alias/outsider"); let shapedomain: str = strings.concat(source, "/shape/domain"); let shapeinternal: str = strings.concat(shapedomain, "/internal"); let shapeclient: str = strings.concat(shapedomain, "/client"); let shapeoutsider: str = strings.concat(source, "/shape/outsider"); let shapevault: str = strings.concat(root, "/vault"); mkdirall(leaf); mkdirall(secret); mkdirall(client); mkdirall(outsider); mkdirall(prefixclient); mkdirall(deep); mkdirall(deepclient); mkdirall(nestedoutsider); mkdirall(internalx); mkdirall(internalxclient); mkdirall(testclient); mkdirall(outsame); mkdirall(outexternal); mkdirall(linkedsecret); mkdirall(linkedclient); mkdirall(aliasinternal); mkdirall(aliasownedclient); mkdirall(shapeinternal); mkdirall(shapeclient); mkdirall(shapeoutsider); mkdirall(shapevault); mkdirall(runtime); mkdirall(tools); writefile(strings.concat(runtime, "/libwwrt.a"), readfile(strings.concat(repo(), "/out/lib/libwwrt.a"))); let leafbody: str = strings.concat( "package leaf;\n", "export fn value() i32 = { return 20; };\n"); let secreta: str = strings.concat( "package secret;\n", "import domain.leaf;\n", "export fn value() i32 = { return leaf.value() + 22; };\n"); let secretz: str = strings.concat( "package secret;\n", "fn private_value() i32 = { return 99; };\n"); let clientbody: str = strings.concat( "package main;\n", "import domain.internal.secret;\n", "fn main() i32 = { return secret.value(); };\n"); writefile(strings.concat(leaf, "/leaf.ww"), leafbody); writefile(strings.concat(secret, "/a.ww"), secreta); writefile(strings.concat(secret, "/z.ww"), secretz); writefile(strings.concat(client, "/main.ww"), clientbody); writefile(strings.concat(outsider, "/main.ww"), strings.concat( "package main;\nimport domain.internal.secret;\n", "fn main() i32 = { return secret.value(); };\n")); writefile(strings.concat(prefixclient, "/main.ww"), strings.concat( "package main;\nimport domain.internal.secret;\n", "fn main() i32 = { return secret.value(); };\n")); writefile(strings.concat(deep, "/deep.ww"), strings.concat( "package deep;\n", "export fn value() i32 = { return 7; };\n")); writefile(strings.concat(deepclient, "/main.ww"), strings.concat( "package main;\n", "import domain.internal.outer.internal.deep;\n", "fn main() i32 = { return deep.value(); };\n")); writefile(strings.concat(nestedoutsider, "/main.ww"), strings.concat( "package main;\n", "import domain.internal.outer.internal.deep;\n", "fn main() i32 = { return deep.value(); };\n")); writefile(strings.concat(internalx, "/visible.ww"), strings.concat( "package visible;\n", "export fn value() i32 = { return 9; };\n")); writefile(strings.concat(internalxclient, "/main.ww"), strings.concat( "package main;\nimport domain.internalx.visible;\n", "fn main() i32 = { return visible.value(); };\n")); writefile(strings.concat(linkedsecret, "/secret.ww"), strings.concat( "package secret;\n", "export fn value() i32 = { return 31; };\n")); writefile(strings.concat(linkedclient, "/main.ww"), strings.concat( "package main;\nimport alias.internal.secret;\n", "fn main() i32 = { return secret.value(); };\n")); writefile(rawallowed, strings.concat( "package main;\nimport alias.internal.secret;\n", "fn main() i32 = { return secret.value(); };\n")); assert(os.symlink(rawallowed, rawalias) == 0); assert(os.symlink(strings.concat(outsider, "/main.ww"), rawescape) == 0); assert(os.symlink(linkedsecret, strings.concat(aliasinternal, "/secret")) == 0); writefile(strings.concat(aliasownedclient, "/main.ww"), strings.concat( "package main;\nimport alias.internal.secret;\n", "fn main() i32 = { return secret.value(); };\n")); assert(os.symlink(aliasownedclient, aliasclient) == 0); assert(os.symlink(linkedclient, aliasoutsider) == 0); writefile(strings.concat(shapevault, "/secret.ww"), strings.concat( "package secret;\n", "export fn value() i32 = { return 17; };\n")); assert(os.symlink(shapevault, strings.concat(shapeinternal, "/secret")) == 0); writefile(strings.concat(shapeclient, "/main.ww"), strings.concat( "package main;\nimport shape.domain.internal.secret;\n", "fn main() i32 = { return secret.value(); };\n")); writefile(strings.concat(shapeoutsider, "/main.ww"), strings.concat( "package main;\nimport shape.domain.internal.secret;\n", "fn main() i32 = { return secret.value(); };\n")); writefile(strings.concat(testclient, "/testclient.ww"), strings.concat( "package testclient;\n", "export fn value() i32 = { return 1; };\n")); writefile(strings.concat(testclient, "/same_test.ww"), strings.concat( "package testclient;\nimport domain.internal.secret;\n", "@test fn allowed_same() void = {\n", " assert(secret.value() == 42);\n};\n")); writefile(strings.concat(testclient, "/external_test.ww"), strings.concat( "package testclient_test;\nimport domain.internal.secret;\n", "import domain.testclient;\n", "@test fn allowed_external() void = {\n", " assert(secret.value() + testclient.value() == 43);\n};\n")); writefile(strings.concat(outsame, "/outsame.ww"), "package outsame;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(outsame, "/same_test.ww"), strings.concat( "package outsame;\nimport domain.internal.secret;\n", "@test fn forbidden_same() void = { assert(false); };\n")); writefile(strings.concat(outexternal, "/outexternal.ww"), "package outexternal;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(outexternal, "/external_test.ww"), strings.concat( "package outexternal_test;\nimport domain.internal.secret;\n", "@test fn forbidden_external() void = { assert(false); };\n")); let compilerwrapper: str = strings.concat(tools, "/w6c.sh"); let assemblerwrapper: str = strings.concat(tools, "/w6a.sh"); let linkerwrapper: str = strings.concat(tools, "/w6l.sh"); writeexecutable(compilerwrapper, strings.concat( "#!/bin/sh\nprintf 'BEGIN' >> \"$WW_INTERNAL_COMPILER_TRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_INTERNAL_COMPILER_TRACE\"; done\n", "printf '\\n' >> \"$WW_INTERNAL_COMPILER_TRACE\"\n", "exec \"$WW_INTERNAL_REAL_COMPILER\" \"$@\"\n")); writeexecutable(assemblerwrapper, strings.concat( "#!/bin/sh\nprintf 'BEGIN' >> \"$WW_INTERNAL_ASSEMBLER_TRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_INTERNAL_ASSEMBLER_TRACE\"; done\n", "printf '\\n' >> \"$WW_INTERNAL_ASSEMBLER_TRACE\"\n", "exec \"$WW_INTERNAL_REAL_ASSEMBLER\" \"$@\"\n")); writeexecutable(linkerwrapper, strings.concat( "#!/bin/sh\nprintf 'BEGIN' >> \"$WW_INTERNAL_LINKER_TRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_INTERNAL_LINKER_TRACE\"; done\n", "printf '\\n' >> \"$WW_INTERNAL_LINKER_TRACE\"\n", "exec \"$WW_INTERNAL_REAL_LINKER\" \"$@\"\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 labels: []str = ["0", "1", "2", "3", "4"]; let works: []str = [strings.concat(root, "/c-work"), strings.concat(root, "/ww-work")]; let bins: []str = [strings.concat(root, "/c-bin"), strings.concat(root, "/ww-bin")]; let compilertraces: []str = [strings.concat(root, "/c-compiler"), strings.concat(root, "/ww-compiler")]; let assemblertraces: []str = [strings.concat(root, "/c-assembler"), strings.concat(root, "/ww-assembler")]; let linkertraces: []str = [strings.concat(root, "/c-linker"), strings.concat(root, "/ww-linker")]; let artifacts: []str = ["domain.leaf", "domain.internal.secret", "domain.client"]; let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"]; let references: []str = alloc([], (artifacts.len * suffixes.len): u64)!; let ri: i32 = 0; for (ri < artifacts.len * suffixes.len) { append(references, ""); ri += 1; }; let referencebin: str = ""; let referencecompiler: str = ""; let referenceassembler: str = ""; let referencelinker: str = ""; let referencediagnostic: str = ""; let referencetestout: str = ""; let referencetesterr: str = ""; let referencetestartifacts: []str = alloc([], 9u64)!; ri = 0; for (ri < 9) { append(referencetestartifacts, ""); ri += 1; }; let allowedreferences: []str = ["", "", "", ""]; let testrejectreferences: []str = ["", ""]; let orderreference: str = ""; let baseenv: []str = os.getenvs(); let si: i32 = 0; for (si < stages.len) { assert(os.mkdir(works[si], 448i32) == 0); writefile(compilertraces[si], ""); writefile(assemblertraces[si], ""); writefile(linkertraces[si], ""); let env: []str = alloc([], (baseenv.len + 11): 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_INTERNAL_COMPILER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_INTERNAL_ASSEMBLER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_INTERNAL_LINKER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_INTERNAL_REAL_COMPILER=") && !strings.hasprefix(baseenv[ei], "WW_INTERNAL_REAL_ASSEMBLER=") && !strings.hasprefix(baseenv[ei], "WW_INTERNAL_REAL_LINKER=") && !strings.hasprefix(baseenv[ei], "WW_LIB=")) { 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_INTERNAL_COMPILER_TRACE=", compilertraces[si])); append(env, strings.concat("WW_INTERNAL_ASSEMBLER_TRACE=", assemblertraces[si])); append(env, strings.concat("WW_INTERNAL_LINKER_TRACE=", linkertraces[si])); append(env, strings.concat("WW_INTERNAL_REAL_COMPILER=", driver(compilers[si]))); append(env, strings.concat("WW_INTERNAL_REAL_ASSEMBLER=", driver(assemblers[si]))); append(env, strings.concat("WW_INTERNAL_REAL_LINKER=", driver(linkers[si]))); append(env, strings.concat("WW_LIB=", runtime)); let av: []str = [driver(stages[si]), "build", "-w", works[si], "-I", source, "-o", bins[si], client]; let out: commandout; runcommandenv(root, strings.concat("internal-cold-", stages[si]), av, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); let wantsecret: str = strings.concat( "//ww:module-reset domain.internal.secret\n", secreta, "\n//ww:module-reset domain.internal.secret\n", secretz, "\n"); let secretunit: str = readfile(strings.concat(works[si], "/domain.internal.secret.unit.ww")); assert(strings.hasprefix(secretunit, wantsecret)); assert(has(secretunit, "//ww:direct-export domain.leaf ")); let ai: i32 = 0; for (ai < artifacts.len) { let xi: i32 = 0; for (xi < suffixes.len) { let bytes: str = readfile(strings.concat(works[si], "/", artifacts[ai], suffixes[xi])); let index: i32 = ai * suffixes.len + xi; if (si == 0) { references[index] = strings.dup(bytes); } else { assert(same(references[index], bytes)); }; xi += 1; }; ai += 1; }; let ctrace: str = readfile(compilertraces[si]); let atrace: str = readfile(assemblertraces[si]); let ltrace: str = readfile(linkertraces[si]); assert(occurrences(ctrace, "\n") == 3); assert(occurrences(atrace, "\n") == 4); assert(occurrences(ltrace, "\n") == 1); let dispatchline: str = strings.concat("BEGIN<-o><", works[si], "/domain.client.init.o.new><", works[si], "/domain.client.init.s.new>"); assert(same(linecontaining(atrace, "/domain.client.init.o.new"), dispatchline)); let secretline: str = linecontaining(ctrace, "domain.internal.secret.unit.new"); assert(occurrences(secretline, "domain.leaf.wwi") == 1); assert(!has(secretline, "domain.client.wwi")); let clientline: str = linecontaining(ctrace, "domain.client.unit.new"); assert(occurrences(clientline, "domain.internal.secret.wwi") == 1); assert(!has(clientline, "domain.leaf.wwi")); assert(occurrences(ltrace, strings.concat(works[si], "/domain.client.a")) == 1); assert(occurrences(ltrace, strings.concat(works[si], "/domain.internal.secret.a")) == 1); assert(occurrences(ltrace, strings.concat(works[si], "/domain.leaf.a")) == 1); assert(!has(ltrace, ".wwi")); let normalizedcompiler: str = normalizedtrace(ctrace, strings.concat(works[si], "/"), bins[si]); let normalizedassembler: str = normalizedtrace(atrace, strings.concat(works[si], "/"), bins[si]); let normalizedlinker: str = normalizedtrace(ltrace, strings.concat(works[si], "/"), bins[si]); if (si == 0) { referencecompiler = strings.dup(normalizedcompiler); referenceassembler = strings.dup(normalizedassembler); referencelinker = strings.dup(normalizedlinker); referencebin = strings.dup(readfile(bins[si])); } else { assert(same(referencecompiler, normalizedcompiler)); assert(same(referenceassembler, normalizedassembler)); assert(same(referencelinker, normalizedlinker)); assert(same(referencebin, readfile(bins[si]))); }; let runav: []str = [bins[si]]; runcommand(root, strings.concat("internal-run-", stages[si]), runav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 42); assert(out.stdout.len == 0 && out.stderr.len == 0); let voucher: str = readfile(strings.concat(works[si], "/domain.internal.secret.unit.ww")); let toolww: str = readfile(strings.concat(works[si], "/.wwtool.ww")); let toolc: str = readfile(strings.concat(works[si], "/.wwtool.w6c")); let toola: str = readfile(strings.concat(works[si], "/.wwtool.w6a")); let toolstamp: str = readfile(strings.concat(works[si], "/.wwtool.stamp")); rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); runcommandenv(root, strings.concat("internal-warm-", stages[si]), av, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(readfile(compilertraces[si]).len == 0); assert(readfile(assemblertraces[si]).len == 0); assert(occurrences(readfile(linkertraces[si]), "\n") == 1); assert(same(voucher, readfile(strings.concat(works[si], "/domain.internal.secret.unit.ww")))); rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); let badout: str = strings.concat(root, "/warm-forbidden-", stages[si]); let badav: []str = [driver(stages[si]), "build", "-w", works[si], "-I", source, "-o", badout, outsider]; runcommandenv(root, strings.concat("internal-warm-forbidden-", stages[si]), badav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); let wantdiag: str = strings.concat(outsider, "/main.ww:2:1: error: use of internal package ", "domain.internal.secret not allowed\n"); assert(same(out.stderr, wantdiag)); if (si == 0) { referencediagnostic = strings.dup(out.stderr); } else { assert(same(referencediagnostic, out.stderr)); }; assert(readfile(compilertraces[si]).len == 0); assert(readfile(assemblertraces[si]).len == 0); assert(readfile(linkertraces[si]).len == 0); assert(!os.exists(badout)); assert(!os.exists(strings.concat(works[si], "/outsider.unit.ww"))); assert(same(voucher, readfile(strings.concat(works[si], "/domain.internal.secret.unit.ww")))); assert(same(toolww, readfile(strings.concat(works[si], "/.wwtool.ww")))); assert(same(toolc, readfile(strings.concat(works[si], "/.wwtool.w6c")))); assert(same(toola, readfile(strings.concat(works[si], "/.wwtool.w6a")))); assert(same(toolstamp, readfile(strings.concat(works[si], "/.wwtool.stamp")))); let coldreject: str = strings.concat(root, "/cold-reject-", stages[si]); assert(os.mkdir(coldreject, 448i32) == 0); rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); let coldav: []str = [driver(stages[si]), "build", "-w", coldreject, "-I", source, "-o", strings.concat(coldreject, ".bin"), outsider]; runcommandenv(root, strings.concat("internal-cold-forbidden-", stages[si]), coldav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stderr, wantdiag)); assert(readfile(compilertraces[si]).len == 0); assert(readfile(assemblertraces[si]).len == 0); assert(readfile(linkertraces[si]).len == 0); assert(!os.exists(strings.concat(coldreject, "/.wwtool.ww"))); assert(!os.exists(strings.concat(coldreject, "/.wwtool.w6c"))); assert(!os.exists(strings.concat(coldreject, "/.wwtool.w6a"))); assert(!os.exists(strings.concat(coldreject, "/.wwtool.stamp"))); assert(!os.exists(strings.concat(coldreject, ".bin"))); assert(!os.exists(strings.concat(coldreject, "/domain.internal.secret.unit.ww"))); assert(!os.exists(strings.concat(coldreject, "/domain.internal.secret.a"))); assert(!os.exists(strings.concat(coldreject, "/outsider.unit.ww"))); let allowedtargets: []str = [deepclient, internalxclient, aliasclient, shapeclient]; let allowedcodes: []i32 = [7, 9, 31, 17]; let allowedi: i32 = 0; for (allowedi < allowedtargets.len) { let allowedout: str = strings.concat(root, "/allowed-", stages[si], "-", labels[allowedi]); let allowedav: []str = [driver(stages[si]), "build", "-I", source, "-o", allowedout, allowedtargets[allowedi]]; runcommandenv(root, strings.concat("internal-allowed-", stages[si], "-", labels[allowedi]), allowedav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let allowedrun: []str = [allowedout]; runcommand(root, strings.concat("internal-allowed-run-", stages[si], "-", labels[allowedi]), allowedrun, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, allowedcodes[allowedi]); if (si == 0) { allowedreferences[allowedi] = strings.dup(readfile(allowedout)); } else { assert(same(allowedreferences[allowedi], readfile(allowedout))); }; allowedi += 1; }; let rawout: str = strings.concat(root, "/raw-allowed-", stages[si]); let rawav: []str = [driver(stages[si]), "build", "-I", source, "-o", rawout, rawalias]; runcommandenv(root, strings.concat("internal-raw-allowed-", stages[si]), rawav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let rawrun: []str = [rawout]; runcommand(root, strings.concat("internal-raw-run-", stages[si]), rawrun, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 31); let directout: str = strings.concat(root, "/direct-", stages[si], ".a"); let directav: []str = [driver(stages[si]), "build", "-I", source, "-o", directout, secret]; runcommandenv(root, strings.concat("internal-direct-", stages[si]), directav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(references[1 * suffixes.len + 4], readfile(directout))); assert(same(references[1 * suffixes.len + 1], readfile(strings.concat(directout, ".wwi")))); let rejecttargets: []str = [prefixclient, nestedoutsider, aliasoutsider, rawescape, shapeoutsider]; let rejectimports: []str = ["domain.internal.secret", "domain.internal.outer.internal.deep", "alias.internal.secret", "domain.internal.secret", "shape.domain.internal.secret"]; let rejecti: i32 = 0; for (rejecti < rejecttargets.len) { let rejectwork: str = strings.concat(root, "/reject-", stages[si], "-", labels[rejecti]); assert(os.mkdir(rejectwork, 448i32) == 0); rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); let rejectav: []str = [driver(stages[si]), "build", "-w", rejectwork, "-I", source, "-o", strings.concat(rejectwork, ".bin"), rejecttargets[rejecti]]; runcommandenv(root, strings.concat("internal-reject-", stages[si], "-", labels[rejecti]), rejectav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); let rejectsource: str = strings.concat(rejecttargets[rejecti], "/main.ww"); if (rejecti == 3) { rejectsource = rawescape; }; let rejectdiag: str = strings.concat(rejectsource, ":2:1: error: use of internal package ", rejectimports[rejecti], " not allowed\n"); assert(same(out.stderr, rejectdiag)); assert(readfile(compilertraces[si]).len == 0); assert(readfile(assemblertraces[si]).len == 0); assert(readfile(linkertraces[si]).len == 0); assert(!os.exists(strings.concat(rejectwork, "/.wwtool.ww"))); assert(!os.exists(strings.concat(rejectwork, "/.wwtool.w6c"))); assert(!os.exists(strings.concat(rejectwork, "/.wwtool.w6a"))); assert(!os.exists(strings.concat(rejectwork, "/.wwtool.stamp"))); assert(!os.exists(strings.concat(rejectwork, ".bin"))); rejecti += 1; }; let testworkroot: str = strings.concat(root, "/test-work-", stages[si]); assert(os.mkdir(testworkroot, 448i32) == 0); rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); let testav: []str = [driver(stages[si]), "test", "-w", testworkroot, "-I", source, testclient]; runcommandenv(root, strings.concat("internal-test-allowed-", stages[si]), testav, env, (180i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "allowed_same ... ok\n")); assert(has(out.stdout, "allowed_external ... ok\n")); if (si == 0) { referencetestout = strings.dup(out.stdout); referencetesterr = strings.dup(out.stderr); } else { assert(same(referencetestout, out.stdout)); assert(same(referencetesterr, out.stderr)); }; let testwork: str = strings.concat(testworkroot, "/"); let testactions: []str = ["domain.testclient-internal-test", "domain.testclient_test-external-test", "domain.testclient-test-main"]; ai = 0; for (ai < testactions.len) { let testsuffixes: []str = [".unit.ww", ".wwi", ".a"]; let xi: i32 = 0; for (xi < testsuffixes.len) { let bytes: str = readfile(strings.concat(testwork, testactions[ai], testsuffixes[xi])); let index: i32 = ai * testsuffixes.len + xi; if (si == 0) { referencetestartifacts[index] = strings.dup(bytes); } else { assert(same(referencetestartifacts[index], bytes)); }; xi += 1; }; ai += 1; }; let testcompiletrace: str = readfile(compilertraces[si]); let sameline: str = linecontaining(testcompiletrace, "domain.testclient-internal-test.unit"); let externalline: str = linecontaining(testcompiletrace, "domain.testclient_test-external-test.unit"); assert(occurrences(sameline, "domain.internal.secret.wwi") == 1); assert(occurrences(externalline, "domain.internal.secret.wwi") == 1); assert(!has(sameline, "domain.leaf.wwi")); assert(!has(externalline, "domain.leaf.wwi")); assert(has(externalline, strings.concat("<--import>", "<", testwork, "domain.testclient-internal-test.wwi.new>"))); let mainline: str = linecontaining(testcompiletrace, "domain.testclient-test-main.unit"); assert(!has(mainline, "domain.internal.secret.wwi")); assert(occurrences(mainline, "<--test-target-package>") == 2); assert(pos(mainline, "<--test-target-package>") < pos(mainline, "<--test-target-package>")); assert(has(mainline, strings.concat("<--import><", testwork, "domain.testclient-internal-test.wwi.new>"))); assert(has(mainline, strings.concat( "<--import><", testwork, "domain.testclient_test-external-test.wwi.new>"))); assert(occurrences(testcompiletrace, "--test-package") == 2); assert(occurrences(testcompiletrace, "<-T><--entry><--test-support-module>") == 1); assert(occurrences(readfile(linkertraces[si]), "\n") == 1); assert(!has(readfile(linkertraces[si]), ".wwi")); let testrejects: []str = [outsame, outexternal]; let testfiles: []str = ["/same_test.ww", "/external_test.ww"]; let tri: i32 = 0; for (tri < testrejects.len) { let tw: str = strings.concat(root, "/test-reject-", stages[si], "-", labels[tri]); assert(os.mkdir(tw, 448i32) == 0); rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); let tav: []str = [driver(stages[si]), "test", "-w", tw, "-I", source, testrejects[tri]]; runcommandenv(root, strings.concat("internal-test-reject-", stages[si], "-", labels[tri]), tav, env, (180i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); let testdiag: str = strings.concat(testrejects[tri], testfiles[tri], ":2:1: error: use of internal package ", "domain.internal.secret not allowed\n"); assert(has(out.stderr, testdiag)); if (si == 0) { testrejectreferences[tri] = strings.dup(out.stderr); } else { assert(same(testrejectreferences[tri], out.stderr)); }; assert(readfile(compilertraces[si]).len == 0); assert(readfile(assemblertraces[si]).len == 0); assert(readfile(linkertraces[si]).len == 0); tri += 1; }; let orderwork: str = strings.concat(root, "/order-work-", stages[si]); assert(os.mkdir(orderwork, 448i32) == 0); let orderforward: []str = [driver(stages[si]), "test", "-c", "-w", orderwork, "-I", source, "--ww-package-test", "test", "main", "main", "-", "-", client, strings.concat(root, "/order-client-", stages[si]), "-", strings.concat(root, "/order-client-", stages[si], ".status"), "--ww-package-test", "test", "main", "main", "-", "-", outsider, strings.concat(root, "/order-outsider-", stages[si]), "-", strings.concat(root, "/order-outsider-", stages[si], ".status"), client]; let orderreverse: []str = [driver(stages[si]), "test", "-c", "-w", orderwork, "-I", source, "--ww-package-test", "test", "main", "main", "-", "-", outsider, strings.concat(root, "/order-outsider-", stages[si]), "-", strings.concat(root, "/order-outsider-", stages[si], ".status"), "--ww-package-test", "test", "main", "main", "-", "-", client, strings.concat(root, "/order-client-", stages[si]), "-", strings.concat(root, "/order-client-", stages[si], ".status"), client]; let orderrequests: [][]str = [orderforward, orderreverse]; let orderdiags: []str = ["", ""]; let oi: i32 = 0; for (oi < 2) { rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); runcommandenv(root, strings.concat("internal-order-", stages[si], "-", labels[oi]), orderrequests[oi], env, (180i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); orderdiags[oi] = strings.dup(out.stderr); assert(same(out.stderr, wantdiag)); assert(readfile(compilertraces[si]).len == 0); assert(readfile(assemblertraces[si]).len == 0); assert(readfile(linkertraces[si]).len == 0); oi += 1; }; assert(same(orderdiags[0], orderdiags[1])); if (si == 0) { orderreference = strings.dup(orderdiags[0]); } else { assert(same(orderreference, orderdiags[0])); }; si += 1; }; clean(root); }; @test fn exact_package_tool_argv_stage_parity() void = { let root: str = fresh(); let source: str = strings.concat(root, "/source tree"); let runtime: str = strings.concat(root, "/runtime library"); let baseidentity: str = longidentity("paritybase"); let leftidentity: str = longidentity("parityleft"); let rightidentity: str = longidentity("parityright"); let rootidentity: str = longidentity("paritycommand"); assert(baseidentity.len > 255 && rootidentity.len > 255); let base: str = strings.concat(source, "/", importrelative(baseidentity)); let left: str = strings.concat(source, "/", importrelative(leftidentity)); let right: str = strings.concat(source, "/", importrelative(rightidentity)); let target: str = strings.concat(source, "/", importrelative(rootidentity)); let tools: str = strings.concat(root, "/tool wrappers"); assert(os.mkdir(source, 448i32) == 0); assert(os.mkdir(runtime, 448i32) == 0); writefile(strings.concat(runtime, "/libwwrt.a"), readfile(strings.concat(repo(), "/out/lib/libwwrt.a"))); mkdirall(base); mkdirall(left); mkdirall(right); mkdirall(target); assert(os.mkdir(tools, 448i32) == 0); let basea: str = strings.concat( "package paritybase;\n", "export fn value() i32 = { return 20; };\n"); let basez: str = strings.concat( "package paritybase;\n", "fn private_value() i32 = { return 99; };\n"); let leftbody: str = strings.concat( "package parityleft;\n", "import ", baseidentity, ";\n", "export fn value() i32 = { return ", "paritybase.value(); };\n"); let leftz: str = strings.concat( "package parityleft;\n", "import ", baseidentity, ";\n", "export fn repeated_binding() i32 = { return ", "paritybase.value(); };\n"); let rightbody: str = strings.concat( "package parityright;\n", "import ", baseidentity, ";\n", "export fn value() i32 = { return ", "paritybase.value() + 1; };\n"); let rootbody: str = strings.concat( "package main;\n", "import ", rightidentity, ";\n", "import ", leftidentity, ";\n", "fn main() i32 = { return parityleft.value() + ", "parityright.value() + 1; };\n"); writefile(strings.concat(base, "/a.ww"), basea); writefile(strings.concat(base, "/z.ww"), basez); writefile(strings.concat(left, "/a.ww"), leftbody); writefile(strings.concat(left, "/z.ww"), leftz); writefile(strings.concat(right, "/right.ww"), rightbody); writefile(strings.concat(target, "/main.ww"), rootbody); let compilerwrapper: str = strings.concat(tools, "/w6c wrapper.sh"); let assemblerwrapper: str = strings.concat(tools, "/w6a wrapper.sh"); let linkerwrapper: str = strings.concat(tools, "/w6l wrapper.sh"); let failurewrapper: str = strings.concat(tools, "/failing w6c wrapper.sh"); writeexecutable(compilerwrapper, strings.concat( "#!/bin/sh\n", "printf 'BEGIN' >> \"$WW_ARGV_COMPILER_TRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_ARGV_COMPILER_TRACE\"; done\n", "printf '\\n' >> \"$WW_ARGV_COMPILER_TRACE\"\n", "exec \"$WW_ARGV_REAL_COMPILER\" \"$@\"\n")); writeexecutable(assemblerwrapper, strings.concat( "#!/bin/sh\n", "printf 'BEGIN' >> \"$WW_ARGV_ASSEMBLER_TRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_ARGV_ASSEMBLER_TRACE\"; done\n", "printf '\\n' >> \"$WW_ARGV_ASSEMBLER_TRACE\"\n", "exec \"$WW_ARGV_REAL_ASSEMBLER\" \"$@\"\n")); writeexecutable(linkerwrapper, strings.concat( "#!/bin/sh\n", "printf 'BEGIN' >> \"$WW_ARGV_LINKER_TRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_ARGV_LINKER_TRACE\"; done\n", "printf '\\n' >> \"$WW_ARGV_LINKER_TRACE\"\n", "exec \"$WW_ARGV_REAL_LINKER\" \"$@\"\n")); writeexecutable(failurewrapper, strings.concat( "#!/bin/sh\n", "printf 'BEGIN' >> \"$WW_ARGV_FAILURE_TRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_ARGV_FAILURE_TRACE\"; done\n", "printf '\\n' >> \"$WW_ARGV_FAILURE_TRACE\"\n", "owner=\n", "for arg in \"$@\"; do if [ \"$arg\" = ", "\"$WW_ARGV_MISSING_OWNER\" ]; then owner=1; fi; done\n", "if [ -n \"$owner\" ]; then\n", " mv -- \"$WW_ARGV_MISSING_EXPORT\" ", "\"$WW_ARGV_MISSING_EXPORT.saved\" || exit 24\n", " \"$WW_ARGV_REAL_COMPILER\" \"$@\"\n", " rc=$?\n", " mv -- \"$WW_ARGV_MISSING_EXPORT.saved\" ", "\"$WW_ARGV_MISSING_EXPORT\" || exit 25\n", " exit $rc\n", "fi\n", "exec \"$WW_ARGV_REAL_COMPILER\" \"$@\"\n")); let stages: []str = ["ww", "ww", "ww_ww", "ww_ww"]; let compilers: []str = ["w6c", "w6c", "w6c_ww", "w6c_ww"]; let assemblers: []str = ["w6a", "w6a", "w6a_ww", "w6a_ww"]; let linkers: []str = ["w6l", "w6l", "w6l_ww", "w6l_ww"]; let tags: []str = ["c1", "c2", "ww1", "ww2"]; let bin: str = strings.concat(root, "/published binary"); let workroot: str = strings.concat(bin, ".sepwork"); let work: str = strings.concat(workroot, "/"); let identities: []str = [baseidentity, leftidentity, rightidentity, rootidentity]; let artifacts: []str = [packagestoragekey(baseidentity, base, 0, 0), packagestoragekey(leftidentity, left, 0, 0), packagestoragekey(rightidentity, right, 0, 0), packagestoragekey(rootidentity, target, 0, 0)]; let unitartifacts: []str = artifacts; let aki: i32 = 0; for (aki < artifacts.len) { assert(strings.hasprefix(artifacts[aki], "__wwpkg.v0.r0.h")); assert(artifacts[aki].len <= 246); aki += 1; }; let wantunits: []str = [strings.concat( "//ww:module-reset ", baseidentity, "\n", basea, "\n//ww:module-reset ", baseidentity, "\n", basez, "\n"), strings.concat("//ww:module-reset ", leftidentity, "\n", leftbody, "\n//ww:module-reset ", leftidentity, "\n", leftz, "\n"), strings.concat("//ww:module-reset ", rightidentity, "\n", rightbody, "\n"), strings.concat("//ww:module-reset ", rootidentity, "\n", rootbody, "\n")]; let referenceunits: []str = ["", "", "", ""]; let referenceexports: []str = ["", "", "", ""]; let referencearchives: []str = ["", "", "", ""]; let referencebin: str = ""; let referencecompiler: str = ""; let referenceassembler: str = ""; let referencelinker: str = ""; let referenceout: str = ""; let referenceerr: str = ""; let referencefailure: str = ""; let referencecollision: str = ""; let baseenv: []str = os.getenvs(); let si: i32 = 0; for (si < stages.len) { let compilertrace: str = strings.concat(root, "/", tags[si], " compiler trace"); let assemblertrace: str = strings.concat(root, "/", tags[si], " assembler trace"); let linkertrace: str = strings.concat(root, "/", tags[si], " linker trace"); let failuretrace: str = strings.concat(root, "/", tags[si], " failure trace"); writefile(compilertrace, ""); writefile(assemblertrace, ""); writefile(linkertrace, ""); writefile(failuretrace, ""); let env: []str = alloc([], (baseenv.len + 11): u64)!; let ei: i32 = 0; for (ei < baseenv.len) { if (!strings.hasprefix(baseenv[ei], "WW_SRCLIB=") && !strings.hasprefix(baseenv[ei], "WW_LIB=") && !strings.hasprefix(baseenv[ei], "WW_W6C=") && !strings.hasprefix(baseenv[ei], "WW_W6A=") && !strings.hasprefix(baseenv[ei], "WW_W6L=") && !strings.hasprefix(baseenv[ei], "WW_ARGV_COMPILER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_ARGV_ASSEMBLER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_ARGV_LINKER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_ARGV_REAL_COMPILER=") && !strings.hasprefix(baseenv[ei], "WW_ARGV_REAL_ASSEMBLER=") && !strings.hasprefix(baseenv[ei], "WW_ARGV_REAL_LINKER=") && !strings.hasprefix(baseenv[ei], "WW_ARGV_FAILURE_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_ARGV_MISSING_EXPORT=") && !strings.hasprefix(baseenv[ei], "WW_ARGV_MISSING_OWNER=")) { append(env, baseenv[ei]); }; ei += 1; }; append(env, strings.concat("WW_SRCLIB=", source)); append(env, strings.concat("WW_LIB=", runtime)); 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_ARGV_COMPILER_TRACE=", compilertrace)); append(env, strings.concat("WW_ARGV_ASSEMBLER_TRACE=", assemblertrace)); append(env, strings.concat("WW_ARGV_LINKER_TRACE=", linkertrace)); append(env, strings.concat("WW_ARGV_REAL_COMPILER=", driver(compilers[si]))); append(env, strings.concat("WW_ARGV_REAL_ASSEMBLER=", driver(assemblers[si]))); append(env, strings.concat("WW_ARGV_REAL_LINKER=", driver(linkers[si]))); let request: str = target; if (si == 1 || si == 3) { request = rootidentity; }; let av: []str = [driver(stages[si]), "build", "-o", bin, request]; let out: commandout; runcommandenv(root, strings.concat("exact-argv-", tags[si]), av, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0); let ui: i32 = 0; for (ui < unitartifacts.len) { let unit: str = readfile(strings.concat(work, unitartifacts[ui], ".unit.ww")); assert(strings.hasprefix(unit, wantunits[ui])); if (ui == 1 || ui == 2) { assert(has(unit, strings.concat("//ww:direct-export ", baseidentity, " "))); } else { if (ui == 3) { assert(has(unit, strings.concat("//ww:direct-export ", leftidentity, " "))); assert(has(unit, strings.concat("//ww:direct-export ", rightidentity, " "))); }; }; assert(!has(unit, "//ww:module ")); if (si == 0) { referenceunits[ui] = strings.dup(unit); } else { assert(same(referenceunits[ui], unit)); }; ui += 1; }; let ai: i32 = 0; for (ai < artifacts.len) { let exportf: str = readfile(strings.concat(work, artifacts[ai], ".wwi")); let assembly: str = readfile(strings.concat(work, artifacts[ai], ".s")); let archive: str = readfile(strings.concat(work, artifacts[ai], ".a")); assert(strings.hasprefix(exportf, strings.concat("//ww:module ", identities[ai], "\n"))); if (ai < 3) { assert(has(assembly, strings.concat(identities[ai], ".value"))); }; if (ai == 3) { assert(has(exportf, "\npackage main;\n")); assert(!has(exportf, "\npackage paritycommand;\n")); }; if (si == 0) { referenceexports[ai] = strings.dup(exportf); referencearchives[ai] = strings.dup(archive); } else { assert(same(referenceexports[ai], exportf)); assert(same(referencearchives[ai], archive)); }; ai += 1; }; assert(os.exists(bin)); let ctrace: str = readfile(compilertrace); let atrace: str = readfile(assemblertrace); let ltrace: str = readfile(linkertrace); assert(occurrences(ctrace, "\n") == 4); let baseline: str = strings.concat( "BEGIN<--package-init-symbol><__ww..pkg.p.", baseidentity, ".v0.r0.init><-c><-I><", work, artifacts[0], ".wwi><-o><", work, artifacts[0], ".s><", work, artifacts[0], ".unit.ww>"); let leftline: str = strings.concat( "BEGIN<--package-init-symbol><__ww..pkg.p.", leftidentity, ".v0.r0.init><-c><--import>", "<", baseidentity, "><", work, artifacts[0], ".wwi><-I><", work, artifacts[1], ".wwi><-o><", work, artifacts[1], ".s><", work, artifacts[1], ".unit.ww>"); let rightline: str = strings.concat( "BEGIN<--package-init-symbol><__ww..pkg.p.", rightidentity, ".v0.r0.init><-c><--import>", "<", baseidentity, "><", work, artifacts[0], ".wwi><-I><", work, artifacts[2], ".wwi><-o><", work, artifacts[2], ".s><", work, artifacts[2], ".unit.ww>"); let rootline: str = strings.concat( "BEGIN<--entry><--package-init-symbol><__ww..pkg.p.", rootidentity, ".v0.r0.init><--init-dispatch-symbol>", "<__ww..dispatch><-c><--import>", "<", leftidentity, "><", work, artifacts[1], ".wwi><--import>", "<", rightidentity, "><", work, artifacts[2], ".wwi><-I><", work, artifacts[3], ".wwi><-o><", work, artifacts[3], ".s><", work, artifacts[3], ".unit.ww>"); assert(same(linecontaining(ctrace, strings.concat(artifacts[0], ".unit.ww")), baseline)); assert(same(linecontaining(ctrace, strings.concat(artifacts[1], ".unit.ww")), leftline)); assert(same(linecontaining(ctrace, strings.concat(artifacts[2], ".unit.ww")), rightline)); assert(same(linecontaining(ctrace, strings.concat(artifacts[3], ".unit.ww")), rootline)); assert(!has(rootline, strings.concat(artifacts[0], ".wwi"))); // Canonical owner and declared name stay independent even in a raw // package compile. --entry selects link-root code generation; it is not // permission to replace canonical owner with the declared name. let rejectwwi: str = strings.concat(root, "/reject-", tags[si], ".wwi"); let rejectasm: str = strings.concat(root, "/reject-", tags[si], ".s"); let rejectav: []str = [driver(compilers[si]), "-c", "--import", leftidentity, strings.concat(work, artifacts[1], ".wwi"), "--import", rightidentity, strings.concat(work, artifacts[2], ".wwi"), "-I", rejectwwi, "-o", rejectasm, strings.concat(work, artifacts[3], ".unit.ww")]; runcommand(root, strings.concat("command-without-entry-", tags[si]), rejectav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(os.exists(rejectwwi) && os.exists(rejectasm)); let ownerrejectwwi: str = strings.concat(root, "/owner-reject-", tags[si], ".wwi"); let ownerrejectasm: str = strings.concat(root, "/owner-reject-", tags[si], ".s"); let ownerrejectav: []str = [driver(compilers[si]), "-c", "--import", leftidentity, strings.concat(work, artifacts[0], ".wwi"), "-I", ownerrejectwwi, "-o", ownerrejectasm, strings.concat(work, artifacts[1], ".unit.ww")]; runcommand(root, strings.concat("export-owner-reject-", tags[si]), ownerrejectav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stderr, strings.concat("w6c: import ", leftidentity, ": export owner mismatch in ", work, artifacts[0], ".wwi\n"))); assert(occurrences(atrace, "\n") == 5); ai = 0; for (ai < artifacts.len) { let assemblerline: str = strings.concat("BEGIN<-o><", work, artifacts[ai], ".o><", work, artifacts[ai], ".s>"); assert(same(linecontaining(atrace, strings.concat(work, artifacts[ai], ".o")), assemblerline)); ai += 1; }; let dispatcherline: str = strings.concat("BEGIN<-o><", work, artifacts[3], ".init.o><", work, artifacts[3], ".init.s>"); assert(same(linecontaining(atrace, strings.concat(work, artifacts[3], ".init.o")), dispatcherline)); assert(occurrences(ltrace, "\n") == 1); assert(has(ltrace, strings.concat("BEGIN<-o><", bin, ".new><", work, artifacts[3], ".a>"))); ai = 0; for (ai < artifacts.len) { assert(occurrences(ltrace, strings.concat("<", work, artifacts[ai], ".a>")) == 1); ai += 1; }; assert(has(ltrace, strings.concat("<", runtime, "/libwwrt.a>"))); assert(!has(ltrace, strings.concat("<", repo(), "/out/lib/libwwrt.a>"))); assert(!has(ltrace, ".wwi>")); let runav: []str = [bin]; runcommand(root, strings.concat("exact-argv-run-", tags[si]), runav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 42); assert(out.stdout.len == 0 && out.stderr.len == 0); if (si == 0) { referencebin = strings.dup(readfile(bin)); referencecompiler = strings.dup(ctrace); referenceassembler = strings.dup(atrace); referencelinker = strings.dup(ltrace); referenceout = strings.dup(out.stdout); referenceerr = strings.dup(out.stderr); } else { assert(same(referencebin, readfile(bin))); assert(same(referencecompiler, ctrace)); assert(same(referenceassembler, atrace)); assert(same(referencelinker, ltrace)); assert(same(referenceout, out.stdout)); assert(same(referenceerr, out.stderr)); }; // Select the same long ordinary directory as both an explicit product // root and the dependency of another product in one command. Then build // it alone through logical and literal spellings. All routes must intern // one production action and emit the dependency-built bytes above. let combinedbase: str = strings.concat(root, "/combined-base-", tags[si]); let combinedleft: str = strings.concat(root, "/combined-left-", tags[si]); let combinedbasestatus: str = strings.concat(combinedbase, ".status"); let combinedleftstatus: str = strings.concat(combinedleft, ".status"); clean(compilertrace); writefile(compilertrace, ""); clean(assemblertrace); writefile(assemblertrace, ""); clean(linkertrace); writefile(linkertrace, ""); let combinedav: []str = [driver(stages[si]), "test", "-c", "-I", source, "--ww-package-build", "--ww-package-test", "build", "paritybase", "paritybase", "-", "-", base, combinedbase, "-", combinedbasestatus, "--ww-package-test", "build", "parityleft", "parityleft", "-", "-", left, combinedleft, "-", combinedleftstatus, left]; runcommandenv(root, strings.concat("long-combined-", tags[si]), combinedav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let combinedtrace: str = readfile(compilertrace); assert(occurrences(combinedtrace, strings.concat(artifacts[0], ".unit.ww>")) == 1); let combinedwork: str = strings.concat(combinedbase, ".sepwork/"); let combinedunit: str = readfile(strings.concat(combinedwork, artifacts[0], ".unit.ww")); let combinedwwi: str = readfile(strings.concat(combinedwork, artifacts[0], ".wwi")); let combinedarchive: str = readfile(strings.concat(combinedwork, artifacts[0], ".a")); assert(same(referenceunits[0], combinedunit)); assert(same(referenceexports[0], combinedwwi)); assert(same(referencearchives[0], combinedarchive)); let rootspellings: []str = [baseidentity, base]; let rootlabels: []str = ["logical", "literal"]; let ri: i32 = 0; for (ri < rootspellings.len) { let rootout: str = strings.concat(root, "/long-root-", rootlabels[ri], "-", tags[si], ".a"); clean(compilertrace); writefile(compilertrace, ""); let rootav: []str = [driver(stages[si]), "build", "-I", source, "-o", rootout, rootspellings[ri]]; runcommandenv(root, strings.concat("long-root-", rootlabels[ri], "-", tags[si]), rootav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(occurrences(readfile(compilertrace), strings.concat( artifacts[0], ".unit.ww>")) == 1); let rootwork: str = strings.concat(rootout, ".sepwork/"); assert(same(combinedunit, readfile(strings.concat(rootwork, artifacts[0], ".unit.ww")))); assert(same(combinedwwi, readfile(strings.concat(rootwork, artifacts[0], ".wwi")))); assert(same(combinedarchive, readfile(strings.concat(rootwork, artifacts[0], ".a")))); clean(strings.concat(rootout, ".sepwork")); clean(rootout); clean(strings.concat(rootout, ".wwi")); ri += 1; }; clean(strings.concat(combinedbase, ".sepwork")); clean(combinedbase); clean(combinedleft); clean(combinedbasestatus); clean(combinedleftstatus); let seedav: []str = [driver(stages[si]), "build", "-w", workroot, "-o", bin, request]; runcommandenv(root, strings.concat("persistent-seed-", tags[si]), seedav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(os.remove(compilertrace) == 0); assert(os.remove(assemblertrace) == 0); writefile(compilertrace, ""); writefile(assemblertrace, ""); let equivalent: str = rootidentity; if (same(request, rootidentity)) { equivalent = target; }; let warmav: []str = [driver(stages[si]), "build", "-w", workroot, "-o", bin, equivalent]; runcommandenv(root, strings.concat("persistent-equivalent-", tags[si]), warmav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(readfile(compilertrace).len == 0); assert(readfile(assemblertrace).len == 0); let baseafile: str = strings.concat(base, "/a.ww"); assert(os.remove(baseafile) == 0); writefile(baseafile, strings.concat(basea, "export fn marker() i32 = { return 7; };\n")); runcommandenv(root, strings.concat("persistent-export-", tags[si]), warmav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let changedtrace: str = readfile(compilertrace); assert(occurrences(changedtrace, "\n") == 3); assert(has(changedtrace, strings.concat(artifacts[0], ".unit.new"))); assert(has(changedtrace, strings.concat(artifacts[1], ".unit.new"))); assert(has(changedtrace, strings.concat(artifacts[2], ".unit.new"))); assert(!has(changedtrace, strings.concat(artifacts[3], ".unit.new"))); let badunit: str = strings.concat(work, artifacts[0], ".unit.ww"); assert(os.remove(badunit) == 0); writefile(badunit, "//ww:module-reset wrong.owner\n"); assert(os.remove(compilertrace) == 0); assert(os.remove(assemblertrace) == 0); assert(os.remove(linkertrace) == 0); writefile(compilertrace, ""); writefile(assemblertrace, ""); writefile(linkertrace, ""); let collisionav: []str = [driver(stages[si]), "build", "-w", workroot, "-o", bin, request]; runcommandenv(root, strings.concat("storage-owner-", tags[si]), collisionav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); let collisiondiag: str = strings.concat( "ww: package storage owner mismatch at ", artifacts[0], " for ", baseidentity, "\n"); assert(same(out.stderr, collisiondiag)); assert(readfile(compilertrace).len == 0); assert(readfile(assemblertrace).len == 0); assert(readfile(linkertrace).len == 0); if (si == 0) { referencecollision = strings.dup(out.stderr); } else { assert(same(referencecollision, out.stderr)); }; assert(os.remove(baseafile) == 0); writefile(baseafile, basea); clean(workroot); clean(bin); let failenv: []str = alloc([], (env.len + 4): u64)!; ei = 0; for (ei < env.len) { if (!strings.hasprefix(env[ei], "WW_W6C=") && !strings.hasprefix(env[ei], "WW_ARGV_FAILURE_TRACE=")) { append(failenv, env[ei]); }; ei += 1; }; append(failenv, strings.concat("WW_W6C=", failurewrapper)); append(failenv, strings.concat("WW_ARGV_FAILURE_TRACE=", failuretrace)); append(failenv, strings.concat("WW_ARGV_MISSING_EXPORT=", work, artifacts[0], ".wwi")); append(failenv, strings.concat("WW_ARGV_MISSING_OWNER=", work, artifacts[1], ".unit.ww")); runcommandenv(root, strings.concat("exact-argv-fail-", tags[si]), av, failenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); let wantfailure: str = strings.concat( "w6c: import ", baseidentity, ": cannot read ", work, artifacts[0], ".wwi\n", "ww: w6c failed for ", leftidentity, "\n"); assert(same(out.stderr, wantfailure)); assert(has(readfile(failuretrace), strings.concat( "<--import><", baseidentity, "><", work, artifacts[0], ".wwi>"))); if (si == 0) { referencefailure = strings.dup(out.stderr); } else { assert(same(referencefailure, out.stderr)); }; clean(workroot); if (os.exists(bin)) { clean(bin); }; si += 1; }; clean(root); }; @test fn command_package_test_variants_keep_canonical_identity() void = { let root: str = fresh(); let source: str = strings.concat(root, "/source"); let commandid: str = longidentity("cmdtool"); assert(commandid.len > 255); let target: str = strings.concat(source, "/", importrelative(commandid)); let importer: str = strings.concat(source, "/importer"); let wrapper: str = strings.concat(root, "/command-w6c.sh"); assert(os.mkdir(source, 448i32) == 0); mkdirall(target); assert(os.mkdir(importer, 448i32) == 0); writefile(strings.concat(target, "/main.ww"), strings.concat( "package main;\n", "export fn value() i32 = { return 41; };\n", "fn main() i32 = { return 0; };\n")); writefile(strings.concat(target, "/internal_test.ww"), strings.concat( "package main;\n", "@test fn command_internal() void = { assert(value() == 41); };\n")); writefile(strings.concat(target, "/external_test.ww"), strings.concat( "package main_test;\n", "import ", commandid, ";\n", "@test fn command_external() void = { ", "assert(main.value() == 41); };\n")); writefile(strings.concat(importer, "/importer.ww"), strings.concat( "package importer;\n", "import ", commandid, ";\n", "export fn value() i32 = { return main.value(); };\n")); writeexecutable(wrapper, strings.concat( "#!/bin/sh\n", "printf 'BEGIN' >> \"$WW_COMMAND_COMPILER_TRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_COMMAND_COMPILER_TRACE\"; done\n", "printf '\\n' >> \"$WW_COMMAND_COMPILER_TRACE\"\n", "exec \"$WW_COMMAND_REAL_COMPILER\" \"$@\"\n")); let stages: []str = ["ww", "ww_ww"]; let compilers: []str = ["w6c", "w6c_ww"]; let internalid: str = commandid; let externalid: str = strings.concat(commandid, "_test"); let mainid: str = strings.concat("__wwtestmain.", commandid, ".main"); let actionids: []str = [internalid, externalid, mainid]; let ordinaryartifact: str = packagestoragekey(commandid, target, 0, 0); let artifacts: []str = [packagestoragekey(internalid, target, 1, 0), packagestoragekey(externalid, target, 2, 0), packagestoragekey(mainid, strings.concat(target, "#directory-test-main"), 3, 2)]; let suffixes: []str = [".unit.ww", ".wwi", ".a"]; let reference: []str = ["", "", "", "", "", "", "", "", ""]; let baseenv: []str = os.getenvs(); let si: i32 = 0; for (si < stages.len) { let workroot: str = strings.concat(root, "/command-work-", stages[si]); assert(os.mkdir(workroot, 448i32) == 0); let work: str = strings.concat(workroot, "/"); let trace: str = strings.concat(root, "/", stages[si], ".trace"); writefile(trace, ""); let env: []str = alloc([], (baseenv.len + 3): u64)!; let ei: i32 = 0; for (ei < baseenv.len) { if (!strings.hasprefix(baseenv[ei], "WW_W6C=") && !strings.hasprefix(baseenv[ei], "WW_COMMAND_COMPILER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_COMMAND_REAL_COMPILER=")) { append(env, baseenv[ei]); }; ei += 1; }; append(env, strings.concat("WW_W6C=", wrapper)); append(env, strings.concat("WW_COMMAND_COMPILER_TRACE=", trace)); append(env, strings.concat("WW_COMMAND_REAL_COMPILER=", driver(compilers[si]))); let av: []str = [driver(stages[si]), "test", "-w", workroot, "-I", source, target]; let out: commandout; runcommandenv(root, strings.concat("command-variants-", stages[si]), av, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0); assert(has(out.stdout, "main.command_internal ... ok\n")); assert(has(out.stdout, "main.command_external ... ok\n")); let ctrace: str = readfile(trace); let ai: i32 = 0; for (ai < artifacts.len) { let line: str = linecontaining(ctrace, strings.concat(artifacts[ai], ".unit.new")); if (ai < 2) { assert(has(line, "<--command-package>")); } else { assert(has(line, "<-T><--entry>")); }; let sj: i32 = 0; for (sj < suffixes.len) { let body: str = readfile(strings.concat(work, artifacts[ai], suffixes[sj])); if (sj == 0) { assert(has(body, strings.concat("//ww:module-reset ", actionids[ai], "\n"))); } else { if (sj == 1) { assert(strings.hasprefix(body, strings.concat("//ww:module ", actionids[ai], "\n"))); }; }; let ri: i32 = ai * suffixes.len + sj; if (si == 0) { reference[ri] = strings.dup(body); } else { assert(same(reference[ri], body)); }; sj += 1; }; ai += 1; }; let externalline: str = linecontaining(ctrace, strings.concat(artifacts[1], ".unit.new")); assert(has(externalline, strings.concat("<--import><", commandid, "><", work, artifacts[0], ".wwi.new>"))); let mainline: str = linecontaining(ctrace, strings.concat(artifacts[2], ".unit.new")); assert(has(mainline, strings.concat("<--import><", internalid, "><", work, artifacts[0], ".wwi.new>"))); assert(has(mainline, strings.concat("<--import><", externalid, "><", work, artifacts[1], ".wwi.new>"))); assert(has(mainline, strings.concat( "<--test-target-package><", internalid, ">"))); assert(has(mainline, strings.concat( "<--test-target-package><", externalid, ">"))); assert(pos(mainline, strings.concat("<--test-target-package><", internalid, ">")) < pos(mainline, strings.concat( "<--test-target-package><", externalid, ">"))); assert(!os.exists(strings.concat(work, ordinaryartifact, ".unit.ww"))); assert(has(readfile(strings.concat(work, artifacts[0], ".s")), strings.concat(commandid, ".value"))); assert(has(readfile(strings.concat(work, artifacts[0], ".s")), strings.concat(commandid, ".command_internal"))); assert(has(readfile(strings.concat(work, artifacts[1], ".s")), strings.concat(externalid, ".command_external"))); let warmrefs: []str = alloc([], reference.len: u64)!; warmrefs.len = reference.len; ai = 0; for (ai < artifacts.len) { let sj: i32 = 0; for (sj < suffixes.len) { warmrefs[ai * suffixes.len + sj] = strings.dup(readfile( strings.concat(work, artifacts[ai], suffixes[sj]))); sj += 1; }; ai += 1; }; runcommandenv(root, strings.concat("command-warm-", stages[si]), av, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "main.command_internal ... ok\n")); assert(has(out.stdout, "main.command_external ... ok\n")); assert(same(ctrace, readfile(trace))); ai = 0; for (ai < artifacts.len) { let sj: i32 = 0; for (sj < suffixes.len) { assert(same(warmrefs[ai * suffixes.len + sj], readfile( strings.concat(work, artifacts[ai], suffixes[sj])))); sj += 1; }; ai += 1; }; // The same command action is not generally source-importable. It is // classified from its declaration so this Go-like diagnostic is stable, // and graph failure occurs before any compiler invocation. clean(trace); writefile(trace, ""); let importout: str = strings.concat(root, "/importer-", stages[si], ".a"); let importav: []str = [driver(stages[si]), "build", "-I", source, "-o", importout, importer]; runcommandenv(root, strings.concat("command-import-", stages[si]), importav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, strings.concat("package ", commandid, " is a program, not an importable package\n"))); assert(readfile(trace).len == 0); clean(workroot); si += 1; }; clean(root); }; @test fn package_graph_diagnostics_are_stable() void = { let root: str = fresh(); let missing: str = strings.concat(root, "/missing"); let cycle: str = strings.concat(root, "/cycle"); let left: str = strings.concat(root, "/left"); let right: str = strings.concat(root, "/right"); let conflict: str = strings.concat(root, "/conflict"); let privatepkg: str = strings.concat(root, "/privatepkg"); let identityroot: str = strings.concat(root, "/identityroot"); let identityactual: str = strings.concat(root, "/identityactual"); let identityalias: str = strings.concat(root, "/identityalias"); let reserved: str = strings.concat(root, "/reservedprobe"); let foldsource: str = strings.concat(root, "/foldsource"); let foldupper: str = strings.concat(foldsource, "/domain/Foo"); let foldlower: str = strings.concat(foldsource, "/domain/foo"); let folddirect: str = strings.concat(foldsource, "/cmd/direct"); let foldbackward: str = strings.concat(foldsource, "/cmd/backward"); let foldindirect: str = strings.concat(foldsource, "/cmd/indirect"); let foldleft: str = strings.concat(foldsource, "/parent/left"); let foldright: str = strings.concat(foldsource, "/parent/right"); let foldrepeat: str = strings.concat(foldsource, "/cmd/repeat"); let foldaliasroot: str = strings.concat(foldsource, "/aliasroot"); let foldaliasactual: str = strings.concat(foldsource, "/alias/Foo"); let foldalias: str = strings.concat(foldsource, "/alias/foo"); let foldrootleft: str = strings.concat(foldsource, "/roots/left"); let foldrootright: str = strings.concat(foldsource, "/roots/right"); let foldrecursive: str = strings.concat(root, "/foldrecursive"); let foldrecursiveupper: str = strings.concat(foldrecursive, "/domain/Foo"); let foldrecursivelower: str = strings.concat(foldrecursive, "/domain/foo"); assert(os.mkdir(missing, 448i32) == 0); assert(os.mkdir(cycle, 448i32) == 0); assert(os.mkdir(left, 448i32) == 0); assert(os.mkdir(right, 448i32) == 0); assert(os.mkdir(conflict, 448i32) == 0); assert(os.mkdir(privatepkg, 448i32) == 0); assert(os.mkdir(identityroot, 448i32) == 0); assert(os.mkdir(identityactual, 448i32) == 0); assert(os.mkdir(reserved, 448i32) == 0); mkdirall(foldupper); mkdirall(foldlower); mkdirall(folddirect); mkdirall(foldbackward); mkdirall(foldindirect); mkdirall(foldleft); mkdirall(foldright); mkdirall(foldrepeat); mkdirall(foldaliasroot); mkdirall(foldaliasactual); mkdirall(foldrootleft); mkdirall(foldrootright); mkdirall(foldrecursiveupper); mkdirall(foldrecursivelower); writefile(strings.concat(missing, "/missing.ww"), "package missing;\nfn local() void = { };\n"); writefile(strings.concat(missing, "/missing_test.ww"), strings.concat( "package missing;\nimport nowhere;\n", "@test fn missing_import() void = { };\n")); writefile(strings.concat(cycle, "/cycle.ww"), "package cycle;\nfn local() void = { };\n"); writefile(strings.concat(cycle, "/cycle_test.ww"), strings.concat( "package cycle;\nimport left;\n", "@test fn cycle_import() void = { };\n")); writefile(strings.concat(left, "/left.ww"), "package left;\nimport right;\nexport fn v() i32 = { return right.v(); };\n"); writefile(strings.concat(right, "/right.ww"), "package right;\nimport left;\nexport fn v() i32 = { return left.v(); };\n"); writefile(strings.concat(conflict, "/a.ww"), "package conflict;\nfn a() void = { };\n"); writefile(strings.concat(conflict, "/z.ww"), "package other;\nfn z() void = { };\n"); writefile(strings.concat(conflict, "/conflict_test.ww"), "package conflict;\n@test fn conflict_name() void = { };\n"); writefile(strings.concat(privatepkg, "/a.ww"), strings.concat( "package privatepkg;\n", "export fn shown() i32 = { return 1; };\n")); writefile(strings.concat(privatepkg, "/z.ww"), "package privatepkg;\nfn hidden() i32 = { return 2; };\n"); writefile(strings.concat(privatepkg, "/external_test.ww"), strings.concat( "package privatepkg_test;\nimport privatepkg;\n", "@test fn private_use() void = { assert(privatepkg.hidden() == 2); };\n")); writefile(strings.concat(identityactual, "/identityactual.ww"), "package identityactual;\nexport fn value() i32 = { return 1; };\n"); let linkout: commandout; let linkav: []str = ["/bin/ln", "-s", identityactual, identityalias]; runcommand(root, "identity-alias", linkav, time.second, &linkout); expectexit(&linkout, 0); writefile(strings.concat(identityroot, "/identityroot.ww"), strings.concat( "package identityroot;\nimport identityactual;\n", "import identityalias;\nexport fn value() i32 = { return 1; };\n")); writefile(strings.concat(reserved, "/reservedprobe.ww"), strings.concat( "package reservedprobe;\nimport __wwlocal.fake;\n", "export fn value() i32 = { return 1; };\n")); writefile(strings.concat(foldupper, "/pkg.ww"), "package upperdecl;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(foldlower, "/pkg.ww"), "package lowerdecl;\nexport fn value() i32 = { return 2; };\n"); // File creation and import order deliberately disagree with byte order. writefile(strings.concat(folddirect, "/z.ww"), strings.concat( "package main;\nimport Lower domain.foo;\n", "fn main() i32 = { return 0; };\n")); writefile(strings.concat(folddirect, "/a.ww"), "package main;\nimport Upper domain.Foo;\n"); writefile(strings.concat(foldbackward, "/a.ww"), "package main;\nimport Lower domain.foo;\n"); writefile(strings.concat(foldbackward, "/z.ww"), strings.concat( "package main;\nimport Upper domain.Foo;\n", "fn main() i32 = { return 0; };\n")); writefile(strings.concat(foldleft, "/pkg.ww"), strings.concat( "package left;\nimport _ domain.Foo;\n", "export fn value() i32 = { return 1; };\n")); writefile(strings.concat(foldright, "/pkg.ww"), strings.concat( "package right;\nimport _ domain.foo;\n", "export fn value() i32 = { return 2; };\n")); writefile(strings.concat(foldindirect, "/main.ww"), strings.concat( "package main;\nimport _ parent.left;\nimport _ parent.right;\n", "fn main() i32 = { return 0; };\n")); writefile(strings.concat(foldrepeat, "/a.ww"), "package main;\nimport _ domain.Foo;\n"); writefile(strings.concat(foldrepeat, "/z.ww"), strings.concat( "package main;\nimport _ domain.Foo;\n", "fn main() i32 = { return 0; };\n")); writefile(strings.concat(foldaliasactual, "/pkg.ww"), "package aliasdecl;\nexport fn value() i32 = { return 1; };\n"); assert(os.symlink(foldaliasactual, foldalias) == 0); writefile(strings.concat(foldaliasroot, "/main.ww"), strings.concat( "package main;\nimport _ alias.Foo;\nimport _ alias.foo;\n", "fn main() i32 = { return 0; };\n")); writefile(strings.concat(foldrootleft, "/pkg.ww"), "package leftroot;\nimport _ domain.Foo;\n"); writefile(strings.concat(foldrootright, "/pkg.ww"), "package rightroot;\nimport _ domain.foo;\n"); writefile(strings.concat(foldrecursiveupper, "/pkg.ww"), "package upper;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(foldrecursivelower, "/pkg.ww"), "package lower;\nexport fn value() i32 = { return 2; };\n"); rejectpackagestable(root, "missing", missing, "cannot find package nowhere"); rejectpackagestable(root, "cycle", cycle, "ww: dependency cycle:"); rejectpackagestable(root, "conflict", conflict, "conflicting package names conflict and other"); rejectpackagestable(root, "private", privatepkg, "package 'privatepkg' has no exported declaration 'hidden'"); let stages: []str = ["ww", "ww_ww"]; let tooltrace: str = strings.concat(root, "/identity-tool.trace"); let compilerwrapper: str = strings.concat(root, "/identity-w6c.sh"); let assemblerwrapper: str = strings.concat(root, "/identity-w6a.sh"); let linkerwrapper: str = strings.concat(root, "/identity-w6l.sh"); writefile(tooltrace, ""); writeexecutable(compilerwrapper, strings.concat( "#!/bin/sh\nprintf 'compile' >> \"$WW_IDENTITY_TRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_IDENTITY_TRACE\"; done\nprintf '\\n' >> ", "\"$WW_IDENTITY_TRACE\"\n", "exec \"$WW_IDENTITY_W6C\" \"$@\"\n")); writeexecutable(assemblerwrapper, strings.concat( "#!/bin/sh\nprintf 'assemble' >> \"$WW_IDENTITY_TRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_IDENTITY_TRACE\"; done\nprintf '\\n' >> ", "\"$WW_IDENTITY_TRACE\"\n", "exec \"$WW_IDENTITY_W6A\" \"$@\"\n")); writeexecutable(linkerwrapper, strings.concat( "#!/bin/sh\nprintf 'link' >> \"$WW_IDENTITY_TRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_IDENTITY_TRACE\"; done\nprintf '\\n' >> ", "\"$WW_IDENTITY_TRACE\"\n", "exec \"$WW_IDENTITY_W6L\" \"$@\"\n")); let baseenv: []str = os.getenvs(); let traceenv: []str = alloc([], (baseenv.len + 4): 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_IDENTITY_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_IDENTITY_W6C=") && !strings.hasprefix(baseenv[ei], "WW_IDENTITY_W6A=") && !strings.hasprefix(baseenv[ei], "WW_IDENTITY_W6L=")) { append(traceenv, baseenv[ei]); }; ei += 1; }; append(traceenv, strings.concat("WW_W6C=", compilerwrapper)); append(traceenv, strings.concat("WW_W6A=", assemblerwrapper)); append(traceenv, strings.concat("WW_W6L=", linkerwrapper)); append(traceenv, strings.concat("WW_IDENTITY_TRACE=", tooltrace)); let out: commandout; let i: i32 = 0; let identitydiagnostics: []str = ["", ""]; i = 0; for (i < stages.len) { let av: []str = [driver(stages[i]), "build", "-I", root, "-o", strings.concat(root, "/bad-identity-", stages[i], ".a"), identityroot]; runcommandenv(root, strings.concat("duplicate-directory-identity-", stages[i]), av, traceenv, (60i64 * (time.second: i64)): time.duration, &out); assert(out.termination == exec.termination.EXIT && out.code != 0); assert(has(out.stderr, strings.concat("ww: package directory ", identityactual, " has import identities identityactual and ", "identityalias\n"))); identitydiagnostics[i] = strings.dup(out.stderr); i += 1; }; assert(same(identitydiagnostics[0], identitydiagnostics[1])); // A canonical directory acquiring two import identities rejects before // compiler or linker invocation. The converse is covered by the shared // multi-context resolution test above. assert(readfile(tooltrace).len == 0); let foldexpected: str = "ww: case-insensitive import collision: \"domain.Foo\" and \"domain.foo\"\n"; let aliasexpected: str = "ww: case-insensitive import collision: \"alias.Foo\" and \"alias.foo\"\n"; let foldtargets: []str = [folddirect, foldbackward, foldindirect, foldaliasroot]; let foldlabels: []str = ["direct", "backward", "indirect", "one-dir"]; let foldwant: []str = [foldexpected, foldexpected, foldexpected, aliasexpected]; let compilers: []str = ["w6c", "w6c_ww"]; let assemblers: []str = ["w6a", "w6a_ww"]; let linkers: []str = ["w6l", "w6l_ww"]; let foldrefs: []str = ["", "", "", "", "", "", ""]; let repeatref: str = ""; i = 0; for (i < stages.len) { let foldenv: []str = alloc([], (traceenv.len + 3): u64)!; let tei: i32 = 0; for (tei < traceenv.len) { append(foldenv, traceenv[tei]); tei += 1; }; append(foldenv, strings.concat("WW_IDENTITY_W6C=", driver(compilers[i]))); append(foldenv, strings.concat("WW_IDENTITY_W6A=", driver(assemblers[i]))); append(foldenv, strings.concat("WW_IDENTITY_W6L=", driver(linkers[i]))); let fi: i32 = 0; for (fi < foldtargets.len) { rewritefile(tooltrace, ""); let work: str = strings.concat(root, "/fold-", foldlabels[fi], "-", stages[i], "-work"); assert(os.mkdir(work, 448i32) == 0); let output: str = strings.concat(root, "/fold-", foldlabels[fi], "-", stages[i]); let av: []str = [driver(stages[i]), "build", "-w", work, "-I", foldsource, "-o", output, foldtargets[fi]]; runcommandenv(root, strings.concat("case-fold-", foldlabels[fi], "-", stages[i]), av, foldenv, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && same(out.stderr, foldwant[fi])); assert(readfile(tooltrace).len == 0); assert(!os.exists(output)); assert(!os.exists(strings.concat(output, ".new"))); assert(!os.exists(strings.concat(output, ".sepwork"))); assert(!os.exists(strings.concat(work, "/.wwtool.stamp"))); assert(directoryisempty(work)); if (i == 0) { foldrefs[fi] = strings.dup(out.stderr); } else { assert(same(foldrefs[fi], out.stderr)); }; fi += 1; }; // The coordinator canonicalizes product order, so reversing roots cannot // choose a different representative or diagnostic. let rootfirst: []str = [foldrootleft, foldrootright]; let rootsecond: []str = [foldrootright, foldrootleft]; let oi: i32 = 0; for (oi < rootfirst.len) { rewritefile(tooltrace, ""); let work: str = strings.concat(root, "/fold-roots-", stages[i], "-", foldlabels[oi], "-work"); assert(os.mkdir(work, 448i32) == 0); let av: []str = [driver(stages[i]), "build", "-w", work, "-I", foldsource, rootfirst[oi], rootsecond[oi]]; runcommandenv(root, strings.concat("case-fold-roots-", stages[i], "-", foldlabels[oi]), av, foldenv, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && has(out.stderr, foldexpected)); assert(readfile(tooltrace).len == 0); assert(!os.exists(strings.concat(work, "/.wwtool.stamp"))); assert(directoryisempty(work)); let refindex: i32 = foldtargets.len + oi; if (i == 0) { foldrefs[refindex] = strings.dup(out.stderr); } else { assert(same(foldrefs[refindex], out.stderr)); }; oi += 1; }; assert(same(foldrefs[4], foldrefs[5])); rewritefile(tooltrace, ""); let recursivework: str = strings.concat(root, "/fold-recursive-", stages[i], "-work"); assert(os.mkdir(recursivework, 448i32) == 0); let recursiveav: []str = [driver(stages[i]), "build", "-w", recursivework, "-I", foldrecursive, strings.concat(foldrecursive, "/...")]; runcommandenv(root, strings.concat("case-fold-recursive-", stages[i]), recursiveav, foldenv, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && has(out.stderr, foldexpected)); assert(readfile(tooltrace).len == 0); assert(!os.exists(strings.concat(recursivework, "/.wwtool.stamp"))); assert(directoryisempty(recursivework)); if (i == 0) { foldrefs[6] = strings.dup(out.stderr); } else { assert(same(foldrefs[6], out.stderr)); }; // Exact repeated occurrences retain one canonical action and accepted // compiler input, rather than being merged by the folded key. rewritefile(tooltrace, ""); let repeatwork: str = strings.concat(root, "/fold-repeat-", stages[i], "-work"); let repeatout: str = strings.concat(root, "/fold-repeat-", stages[i]); assert(os.mkdir(repeatwork, 448i32) == 0); let repeatav: []str = [driver(stages[i]), "build", "-w", repeatwork, "-I", foldsource, "-o", repeatout, foldrepeat]; runcommandenv(root, strings.concat("case-fold-repeat-", stages[i]), repeatav, foldenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); let repeattrace: str = readfile(tooltrace); assert(occurrences(repeattrace, "/domain.Foo.unit.new>") == 1); let repeatline: str = linecontaining(repeattrace, "/cmd.repeat.unit.new>"); assert(occurrences(repeatline, "<--import>") == 1); assert(occurrences(repeatline, "/domain.Foo.wwi.new>") == 1); assert(os.exists(strings.concat(repeatwork, "/.wwtool.stamp"))); let repeatpaths: []str = ["/.wwtool.ww", "/.wwtool.w6c", "/.wwtool.w6a", "/.wwtool.stamp", "/cmd.repeat.unit.ww", "/cmd.repeat.wwi", "/cmd.repeat.s", "/cmd.repeat.o", "/cmd.repeat.a", "/cmd.repeat.init.unit.ww", "/cmd.repeat.init.s", "/cmd.repeat.init.o", "/domain.Foo.unit.ww", "/domain.Foo.wwi", "/domain.Foo.s", "/domain.Foo.o", "/domain.Foo.a"]; let repeatbytes: []str = alloc([], repeatpaths.len: u64)!; let rsi: i32 = 0; for (rsi < repeatpaths.len) { append(repeatbytes, strings.dup(readfile(strings.concat(repeatwork, repeatpaths[rsi])))); rsi += 1; }; let repeatoutput: str = strings.dup(readfile(repeatout)); if (i == 0) { repeatref = strings.dup(repeatoutput); } else { assert(same(repeatref, repeatoutput)); }; let repeatcollision: str = strings.concat(foldrepeat, "/m.ww"); writefile(repeatcollision, "package main;\nimport _ domain.foo;\n"); rewritefile(tooltrace, ""); runcommandenv(root, strings.concat("case-fold-repeat-added-", stages[i]), repeatav, foldenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && same(out.stderr, foldexpected)); assert(readfile(tooltrace).len == 0); rsi = 0; for (rsi < repeatpaths.len) { assert(same(repeatbytes[rsi], readfile(strings.concat(repeatwork, repeatpaths[rsi])))); rsi += 1; }; assert(!directoryhasnew(repeatwork)); assert(same(repeatoutput, readfile(repeatout))); assert(!os.exists(strings.concat(repeatout, ".new"))); assert(!os.exists(strings.concat(repeatout, ".sepwork"))); assert(!os.exists(strings.concat(repeatwork, "/domain.foo.unit.ww"))); assert(!os.exists(strings.concat(repeatwork, "/domain.foo.wwi"))); assert(!os.exists(strings.concat(repeatwork, "/domain.foo.a"))); assert(os.remove(repeatcollision) == 0); rewritefile(tooltrace, ""); runcommandenv(root, strings.concat("case-fold-repeat-removed-", stages[i]), repeatav, foldenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(occurrences(readfile(tooltrace), "compile") == 0); assert(occurrences(readfile(tooltrace), "assemble") == 0); assert(occurrences(readfile(tooltrace), "link") == 1); rsi = 0; for (rsi < repeatpaths.len) { assert(same(repeatbytes[rsi], readfile(strings.concat(repeatwork, repeatpaths[rsi])))); rsi += 1; }; assert(!directoryhasnew(repeatwork)); assert(same(repeatoutput, readfile(repeatout))); assert(!os.exists(strings.concat(repeatout, ".new"))); assert(!os.exists(strings.concat(repeatout, ".sepwork"))); i += 1; }; rewritefile(tooltrace, ""); let reserveddiagnostics: []str = ["", ""]; i = 0; for (i < stages.len) { let av: []str = [driver(stages[i]), "build", "-I", root, "-o", strings.concat(root, "/bad-reserved-", stages[i], ".a"), reserved]; runcommandenv(root, strings.concat("reserved-local-import-", stages[i]), av, traceenv, (60i64 * (time.second: i64)): time.duration, &out); assert(out.termination == exec.termination.EXIT && out.code != 0); assert(has(out.stderr, "package path __wwlocal.fake is reserved")); reserveddiagnostics[i] = strings.dup(primarydiagnostic(out.stderr)); i += 1; }; assert(same(reserveddiagnostics[0], reserveddiagnostics[1])); assert(readfile(tooltrace).len == 0); clean(root); }; @test fn platform_filename_source_selection() void = { let root: str = fresh(); let source: str = strings.concat(root, "/source"); let app: str = strings.concat(source, "/app"); let good: str = strings.concat(source, "/dep/good"); let pkg: str = strings.concat(source, "/pkg"); let testgood: str = strings.concat(source, "/test/good"); let wrongonly: str = strings.concat(source, "/wrongonly"); let notests: str = strings.concat(source, "/notests"); let internal: str = strings.concat(source, "/foreign/internal/secret"); let command: str = strings.concat(source, "/command"); let vendored: str = strings.concat(app, "/vendor/vendored"); let order: str = strings.concat(root, "/order"); let tools: str = strings.concat(root, "/tools"); let foldfiles: str = strings.concat(root, "/foldfiles"); let foldunicode: str = strings.concat(foldfiles, "/unicode"); let foldinvalid: str = strings.concat(foldfiles, "/invalid"); let foldnonormal: str = strings.concat(foldfiles, "/nonormal"); let foldinternal: str = strings.concat(foldfiles, "/internal"); let foldexternal: str = strings.concat(foldfiles, "/external"); let foldsameonly: str = strings.concat(foldfiles, "/sameonly"); let foldexternalonly: str = strings.concat(foldfiles, "/externalonly"); let foldmixed: str = strings.concat(foldfiles, "/mixed"); let foldcross: str = strings.concat(foldfiles, "/cross"); let foldcreationroot: str = strings.concat(root, "/fold-creation-order"); let foldcreation: str = strings.concat(foldcreationroot, "/prod"); mkdirall(app); mkdirall(good); mkdirall(pkg); mkdirall(testgood); mkdirall(wrongonly); mkdirall(notests); mkdirall(internal); mkdirall(command); mkdirall(vendored); mkdirall(order); mkdirall(tools); mkdirall(foldunicode); mkdirall(foldinvalid); mkdirall(foldnonormal); mkdirall(foldinternal); mkdirall(foldexternal); mkdirall(foldsameonly); mkdirall(foldexternalonly); mkdirall(foldmixed); mkdirall(foldcross); mkdirall(foldcreation); let good5: str = strings.concat("package good;\n", "export fn value() i32 = { return 5; };\n"); let good6: str = strings.concat("package good;\n", "export fn value() i32 = { return 6; };\n"); let good7: str = strings.concat("package good;\n", "export fn value() i32 = { return 7; };\n"); let appmain: str = strings.concat( "package main;\nimport dep.good;\n", "fn main() i32 = { return good.value() + wholelinux() + ", "afterdot() + arch() + reversed() + future() + osname() + pair(); };\n"); let wholelinux: str = "package main;\nfn wholelinux() i32 = { return 1; };\n"; let afterdot: str = "package main;\nfn afterdot() i32 = { return 1; };\n"; let arch: str = "package main;\nfn arch() i32 = { return 1; };\n"; let reversed: str = "package main;\nfn reversed() i32 = { return 1; };\n"; let future: str = "package main;\nfn future() i32 = { return 1; };\n"; let osname: str = strings.concat( "package main;\nimport _ dep.good;\n", "fn osname() i32 = { return 1; };\n"); let pair: str = "package main;\nfn pair() i32 = { return 1; };\n"; let ignoredparse: str = "this wrong-target source is deliberately malformed\n"; let ignoredparseedit: str = "a different malformed wrong-target edit {\n"; let ignoreddep: str = strings.concat( "package main;\nimport dep.wrong;\n", "fn wrong_dep() i32 = { return wrong.value(); };\n"); let ignoredinternal: str = strings.concat( "package main;\nimport foreign.internal.secret;\n", "fn wrong_internal() i32 = { return secret.value(); };\n"); let ignoredmain: str = strings.concat( "package main;\nimport command;\n", "fn wrong_main() i32 = { return 0; };\n"); let ignoredvendor: str = strings.concat( "package main;\nimport vendored;\n", "fn wrong_vendor() i32 = { return vendored.value(); };\n"); writefile(strings.concat(good, "/good.ww"), good5); writefile(strings.concat(app, "/main.ww"), appmain); writefile(strings.concat(app, "/linux.ww"), wholelinux); writefile(strings.concat(app, "/platform.extra_windows.ww"), afterdot); writefile(strings.concat(app, "/platform_amd64.ww"), arch); writefile(strings.concat(app, "/platform_amd64_linux.ww"), reversed); writefile(strings.concat(app, "/platform_future.ww"), future); writefile(strings.concat(app, "/platform_linux.ww"), osname); writefile(strings.concat(app, "/platform_linux_amd64.ww"), pair); writefile(strings.concat(app, "/badparse_windows.ww"), ignoredparse); writefile(strings.concat(app, "/baddep_arm64.ww"), ignoreddep); writefile(strings.concat(app, "/badinternal_windows_amd64.ww"), ignoredinternal); writefile(strings.concat(app, "/badmain_linux_arm64.ww"), ignoredmain); writefile(strings.concat(app, "/badvendor_windows.ww"), ignoredvendor); writefile(strings.concat(app, "/.Fold.ww"), ignoredparse); writefile(strings.concat(app, "/.fold.ww"), ignoredparseedit); writefile(strings.concat(app, "/_Fold.ww"), ignoredparse); writefile(strings.concat(app, "/_fold.ww"), ignoredparseedit); writefile(strings.concat(app, "/Fold_windows.ww"), ignoredparse); writefile(strings.concat(app, "/fold_windows.ww"), ignoredparseedit); writefile(strings.concat(internal, "/secret.ww"), strings.concat( "package secret;\n", "export fn value() i32 = { return 19; };\n")); writefile(strings.concat(command, "/main.ww"), "package main;\nfn main() i32 = { return 0; };\n"); writefile(strings.concat(vendored, "/vendored.ww"), strings.concat( "package vendored;\n", "export fn value() i32 = { return 23; };\n")); let pkgbase: str = strings.concat("package pkg;\n", "export fn value() i32 = { return 7; };\n"); let samepair: str = strings.concat( "package pkg;\nimport test.good;\n", "@test fn selected_internal_pair() void = { ", "assert(value() + good.value() == 18); };\n"); let samewhole: str = strings.concat( "package pkg;\n", "@test fn selected_whole_plan9_name() void = { assert(value() == 7); };\n"); let samereversed: str = strings.concat( "package pkg;\n", "@test fn selected_reversed_suffix() void = { assert(value() == 7); };\n"); let externalpair: str = strings.concat( "package pkg_test;\nimport pkg;\nimport test.good;\n", "@test fn selected_external_pair() void = { ", "assert(pkg.value() + good.value() == 18); };\n"); let externaldot: str = strings.concat( "package pkg_test;\nimport pkg;\n", "@test fn selected_external_after_dot() void = { ", "assert(pkg.value() == 7); };\n"); let wrongtest: str = strings.concat( "package pkg;\nimport test.wrong;\n", "@test fn wrong_target_test() void = { abort(\"wrong test ran\"); };\n"); let wrongtestedit: str = "this edited wrong-target test remains deliberately malformed\n"; writefile(strings.concat(pkg, "/base.ww"), pkgbase); writefile(strings.concat(pkg, "/internal_linux_amd64_test.ww"), samepair); writefile(strings.concat(pkg, "/plan9_test.ww"), samewhole); writefile(strings.concat(pkg, "/reverse_amd64_linux_test.ww"), samereversed); writefile(strings.concat(pkg, "/external_linux_amd64_test.ww"), externalpair); writefile(strings.concat(pkg, "/external.extra_windows_test.ww"), externaldot); writefile(strings.concat(pkg, "/internal_windows_amd64_test.ww"), wrongtest); writefile(strings.concat(pkg, "/external_linux_arm64_test.ww"), wrongtest); writefile(strings.concat(pkg, "/x_plan9_test.ww"), wrongtest); writefile(strings.concat(testgood, "/good.ww"), strings.concat( "package good;\n", "export fn value() i32 = { return 11; };\n")); writefile(strings.concat(wrongonly, "/only_windows.ww"), ignoredparse); writefile(strings.concat(notests, "/base.ww"), "package notests;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(notests, "/only_windows_test.ww"), ignoredparse); writefile(strings.concat(pkg, "/.Fold_test.ww"), ignoredparse); writefile(strings.concat(pkg, "/.fold_test.ww"), ignoredparseedit); writefile(strings.concat(pkg, "/_Fold_test.ww"), ignoredparse); writefile(strings.concat(pkg, "/_fold_test.ww"), ignoredparseedit); writefile(strings.concat(pkg, "/Fold_windows_test.ww"), ignoredparse); writefile(strings.concat(pkg, "/fold_windows_test.ww"), ignoredparseedit); writefile(strings.concat(foldunicode, "/K.ww"), "package main;\nfn latin() i32 = { return 1; };\n"); writefile(strings.concat(foldunicode, "/K.ww"), "package main;\nfn kelvin() i32 = { return 2; };\n"); writefile(strings.concat(foldunicode, "/main.ww"), "package main;\nfn main() i32 = { return 0; };\n"); let invalidupperbytes: []u8 = alloc([], 6u64)!; append(invalidupperbytes, 'A'); append(invalidupperbytes, '-'); append(invalidupperbytes, 255u8); append(invalidupperbytes, '.'); append(invalidupperbytes, 'w'); append(invalidupperbytes, 'w'); let invalidlowerbytes: []u8 = alloc([], 6u64)!; append(invalidlowerbytes, 'a'); append(invalidlowerbytes, '-'); append(invalidlowerbytes, 254u8); append(invalidlowerbytes, '.'); append(invalidlowerbytes, 'w'); append(invalidlowerbytes, 'w'); writefile(strings.concat(foldinvalid, "/", strings.frombytes(invalidupperbytes)), "package main;\nfn invalidupper() i32 = { return 1; };\n"); writefile(strings.concat(foldinvalid, "/", strings.frombytes(invalidlowerbytes)), "package main;\nfn invalidlower() i32 = { return 2; };\n"); writefile(strings.concat(foldinvalid, "/main.ww"), "package main;\nfn main() i32 = { return 0; };\n"); writefile(strings.concat(foldnonormal, "/É.ww"), "package main;\nfn composed() i32 = { return 1; };\n"); writefile(strings.concat(foldnonormal, "/É.ww"), "package main;\nfn decomposed() i32 = { return 2; };\n"); writefile(strings.concat(foldnonormal, "/main.ww"), "package main;\nfn main() i32 = { return 0; };\n"); writefile(strings.concat(foldinternal, "/base.ww"), "package sample;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(foldinternal, "/File_test.ww"), "package sample;\n@test fn upper() void = { assert(true); };\n"); writefile(strings.concat(foldinternal, "/file_test.ww"), "package sample;\n@test fn lower() void = { assert(true); };\n"); writefile(strings.concat(foldexternal, "/base.ww"), "package sample;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(foldexternal, "/File_test.ww"), "package sample_test;\n@test fn upper() void = { assert(true); };\n"); writefile(strings.concat(foldexternal, "/file_test.ww"), "package sample_test;\n@test fn lower() void = { assert(true); };\n"); writefile(strings.concat(foldsameonly, "/File_test.ww"), "package sample;\n@test fn upper() void = { assert(true); };\n"); writefile(strings.concat(foldsameonly, "/file_test.ww"), "package sample;\n@test fn lower() void = { assert(true); };\n"); writefile(strings.concat(foldexternalonly, "/File_test.ww"), "package sample_test;\n@test fn upper() void = { assert(true); };\n"); writefile(strings.concat(foldexternalonly, "/file_test.ww"), "package sample_test;\n@test fn lower() void = { assert(true); };\n"); writefile(strings.concat(foldmixed, "/File_test.ww"), "package sample;\n@test fn upper() void = { assert(true); };\n"); writefile(strings.concat(foldmixed, "/file_test.ww"), "package sample_test;\n@test fn lower() void = { assert(true); };\n"); writefile(strings.concat(foldcross, "/X_TEST.ww"), "package cross;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(foldcross, "/x_test.ww"), "package cross;\n@test fn lower() void = { assert(value() == 1); };\n"); // The lower spelling is created first to prove getdents creation order is // irrelevant to both direct and recursive collision diagnostics. writefile(strings.concat(foldcreation, "/file.ww"), "package main;\nfn lower() i32 = { return 1; };\n"); writefile(strings.concat(foldcreation, "/File.ww"), strings.concat( "package main;\nfn upper() i32 = { return 2; };\n", "fn main() i32 = { return 0; };\n")); // Creation order is deliberately the inverse of byte order. writefile(strings.concat(order, "/z_linux.ww"), "package order;\n@test fn z() void = { };\n"); writefile(strings.concat(order, "/a_linux.ww"), "package order;\n@test fn a() void = { };\n"); let compilerwrapper: str = strings.concat(tools, "/w6c.sh"); let assemblerwrapper: str = strings.concat(tools, "/w6a.sh"); let linkerwrapper: str = strings.concat(tools, "/w6l.sh"); writeexecutable(compilerwrapper, strings.concat( "#!/bin/sh\nprintf 'BEGIN' >> \"$WW_PLATFORM_COMPILER_TRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_PLATFORM_COMPILER_TRACE\"; done\n", "printf '\\n' >> \"$WW_PLATFORM_COMPILER_TRACE\"\n", "exec \"$WW_PLATFORM_REAL_COMPILER\" \"$@\"\n")); writeexecutable(assemblerwrapper, strings.concat( "#!/bin/sh\nprintf 'BEGIN' >> \"$WW_PLATFORM_ASSEMBLER_TRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_PLATFORM_ASSEMBLER_TRACE\"; done\n", "printf '\\n' >> \"$WW_PLATFORM_ASSEMBLER_TRACE\"\n", "exec \"$WW_PLATFORM_REAL_ASSEMBLER\" \"$@\"\n")); writeexecutable(linkerwrapper, strings.concat( "#!/bin/sh\nprintf 'BEGIN' >> \"$WW_PLATFORM_LINKER_TRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_PLATFORM_LINKER_TRACE\"; done\n", "printf '\\n' >> \"$WW_PLATFORM_LINKER_TRACE\"\n", "exec \"$WW_PLATFORM_REAL_LINKER\" \"$@\"\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 labels: []str = ["c", "ww"]; let artifactnames: []str = ["app.unit.ww", "app.wwi", "app.s", "app.o", "app.a", "app.init.unit.ww", "app.init.s", "app.init.o", "dep.good.unit.ww", "dep.good.wwi", "dep.good.s", "dep.good.o", "dep.good.a"]; let artifactrefs: []str = ["", "", "", "", "", "", "", "", "", "", "", "", ""]; let testartifactnames: []str = ["pkg-internal-test.unit.ww", "pkg-internal-test.wwi", "pkg-internal-test.a", "pkg_test-external-test.unit.ww", "pkg_test-external-test.wwi", "pkg_test-external-test.a", "pkg-test-main.unit.ww", "pkg-test-main.wwi", "pkg-test-main.a"]; let testartifactrefs: []str = ["", "", "", "", "", "", "", "", ""]; let binref: str = ""; let compilerref: str = ""; let assemblerref: str = ""; let linkerref: str = ""; let prodarchiveref: str = ""; let prodwwiref: str = ""; let testoutref: str = ""; let testerrref: str = ""; let faildiagref: str = ""; let recursiveoutref: str = ""; let recursiveerrref: str = ""; let foldfilerefs: []str = ["", "", "", "", "", "", "", ""]; let crossref: str = ""; let nonormalref: str = ""; let nonormalunitref: str = ""; let foldcreationref: str = ""; let foldcreationrecursiveref: str = ""; let baseenv: []str = os.getenvs(); let si: i32 = 0; for (si < stages.len) { rewritefile(strings.concat(good, "/good.ww"), good5); rewritefile(strings.concat(app, "/platform_future.ww"), future); rewritefile(strings.concat(app, "/badparse_windows.ww"), ignoredparse); rewritefile(strings.concat(pkg, "/internal_windows_amd64_test.ww"), wrongtest); let work: str = strings.concat(root, "/", labels[si], "-work"); let reversework: str = strings.concat(root, "/", labels[si], "-reverse-work"); let outdir: str = strings.concat(root, "/", labels[si], "-out"); let reverseout: str = strings.concat(root, "/", labels[si], "-reverse-out"); let compilertrace: str = strings.concat(root, "/", labels[si], "-compiler"); let assemblertrace: str = strings.concat(root, "/", labels[si], "-assembler"); let linkertrace: str = strings.concat(root, "/", labels[si], "-linker"); assert(os.mkdir(work, 448i32) == 0); assert(os.mkdir(reversework, 448i32) == 0); assert(os.mkdir(outdir, 448i32) == 0); assert(os.mkdir(reverseout, 448i32) == 0); writefile(compilertrace, ""); writefile(assemblertrace, ""); writefile(linkertrace, ""); let env: []str = alloc([], (baseenv.len + 9): 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_PLATFORM_COMPILER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_PLATFORM_ASSEMBLER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_PLATFORM_LINKER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_PLATFORM_REAL_COMPILER=") && !strings.hasprefix(baseenv[ei], "WW_PLATFORM_REAL_ASSEMBLER=") && !strings.hasprefix(baseenv[ei], "WW_PLATFORM_REAL_LINKER=")) { 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_PLATFORM_COMPILER_TRACE=", compilertrace)); append(env, strings.concat("WW_PLATFORM_ASSEMBLER_TRACE=", assemblertrace)); append(env, strings.concat("WW_PLATFORM_LINKER_TRACE=", linkertrace)); append(env, strings.concat("WW_PLATFORM_REAL_COMPILER=", driver(compilers[si]))); append(env, strings.concat("WW_PLATFORM_REAL_ASSEMBLER=", driver(assemblers[si]))); append(env, strings.concat("WW_PLATFORM_REAL_LINKER=", driver(linkers[si]))); let forward: []str = [driver(stages[si]), "build", "-w", work, "-I", source, "-o", strings.concat(outdir, "/"), app, good]; let reverse: []str = [driver(stages[si]), "build", "-w", reversework, "-I", source, "-o", strings.concat(reverseout, "/"), good, app]; let out: commandout; runcommandenv(root, strings.concat("platform-cold-", labels[si]), forward, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); let ctrace: str = readfile(compilertrace); let atrace: str = readfile(assemblertrace); let ltrace: str = readfile(linkertrace); assert(occurrences(ctrace, "\n") == 2); assert(occurrences(atrace, "\n") == 3); assert(occurrences(ltrace, "\n") == 1); let appline: str = linecontaining(ctrace, "/app.unit.new"); assert(occurrences(appline, "<--import>") == 1); assert(occurrences(appline, "/dep.good.wwi.new>") == 1); assert(!has(ctrace, "dep.wrong")); assert(!has(ctrace, "foreign.internal.secret")); assert(!has(ctrace, "vendored")); assert(!has(ctrace, "command.wwi")); assert(occurrences(ltrace, strings.concat(work, "/app.a.new")) == 1); assert(occurrences(ltrace, strings.concat(work, "/dep.good.a.new")) == 1); assert(!has(ltrace, ".wwi")); assert(!has(ltrace, "dep.wrong")); let unitprefix: str = strings.concat( "//ww:module-reset app\n", wholelinux, "\n", "//ww:module-reset app\n", appmain, "\n", "//ww:module-reset app\n", afterdot, "\n", "//ww:module-reset app\n", arch, "\n", "//ww:module-reset app\n", reversed, "\n", "//ww:module-reset app\n", future, "\n", "//ww:module-reset app\n", osname, "\n", "//ww:module-reset app\n", pair, "\n"); let appunit: str = readfile(strings.concat(work, "/app.unit.ww")); assert(strings.hasprefix(appunit, unitprefix)); assert(has(appunit, "//ww:direct-export dep.good ")); assert(!has(appunit, "wrong_dep")); assert(!has(appunit, "wrong_internal")); assert(!has(appunit, "wrong_main")); assert(!has(appunit, "wrong_vendor")); assert(!os.exists(strings.concat(work, "/dep.wrong.unit.ww"))); let bin: str = strings.concat(outdir, "/app"); let runav: []str = [bin]; runcommand(root, strings.concat("platform-run-", labels[si]), runav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 12); let ai: i32 = 0; for (ai < artifactnames.len) { let bytes: str = readfile(strings.concat(work, "/", artifactnames[ai])); if (si == 0) { artifactrefs[ai] = strings.dup(bytes); } else { assert(same(artifactrefs[ai], bytes)); }; ai += 1; }; let normalizedcompiler: str = normalizedtrace(ctrace, strings.concat(work, "/"), outdir); let normalizedassembler: str = normalizedtrace(atrace, strings.concat(work, "/"), outdir); let normalizedlinker: str = normalizedtrace(ltrace, strings.concat(work, "/"), outdir); if (si == 0) { compilerref = strings.dup(normalizedcompiler); assemblerref = strings.dup(normalizedassembler); linkerref = strings.dup(normalizedlinker); binref = strings.dup(readfile(bin)); } else { assert(same(compilerref, normalizedcompiler)); assert(same(assemblerref, normalizedassembler)); assert(same(linkerref, normalizedlinker)); assert(same(binref, readfile(bin))); }; rewritefile(compilertrace, ""); rewritefile(assemblertrace, ""); rewritefile(linkertrace, ""); runcommandenv(root, strings.concat("platform-reverse-", labels[si]), reverse, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(readfile(bin), readfile(strings.concat(reverseout, "/app")))); assert(same(normalizedcompiler, normalizedtrace(readfile(compilertrace), strings.concat(reversework, "/"), reverseout))); assert(same(normalizedassembler, normalizedtrace(readfile(assemblertrace), strings.concat(reversework, "/"), reverseout))); assert(same(normalizedlinker, normalizedtrace(readfile(linkertrace), strings.concat(reversework, "/"), reverseout))); ai = 0; for (ai < artifactnames.len) { assert(same(readfile(strings.concat(work, "/", artifactnames[ai])), readfile(strings.concat(reversework, "/", artifactnames[ai])))); ai += 1; }; let stableunit: str = strings.dup(readfile(strings.concat(work, "/app.unit.ww"))); let stablewwi: str = strings.dup(readfile(strings.concat(work, "/app.wwi"))); let stablearchive: str = strings.dup(readfile(strings.concat(work, "/app.a"))); let stabledepwwi: str = strings.dup(readfile(strings.concat(work, "/dep.good.wwi"))); let stablebin: str = strings.dup(readfile(bin)); let stablestamp: str = strings.dup(readfile(strings.concat(work, "/.wwtool.stamp"))); rewritefile(compilertrace, ""); rewritefile(assemblertrace, ""); rewritefile(linkertrace, ""); runcommandenv(root, strings.concat("platform-warm-", labels[si]), forward, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(readfile(compilertrace).len == 0); assert(readfile(assemblertrace).len == 0); assert(occurrences(readfile(linkertrace), "\n") == 1); rewritefile(strings.concat(app, "/badparse_windows.ww"), ignoredparseedit); rewritefile(compilertrace, ""); rewritefile(assemblertrace, ""); rewritefile(linkertrace, ""); runcommandenv(root, strings.concat("platform-ignored-edit-", labels[si]), forward, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(readfile(compilertrace).len == 0); assert(readfile(assemblertrace).len == 0); assert(occurrences(readfile(linkertrace), "\n") == 1); assert(same(stableunit, readfile(strings.concat(work, "/app.unit.ww")))); assert(same(stablewwi, readfile(strings.concat(work, "/app.wwi")))); assert(same(stablearchive, readfile(strings.concat(work, "/app.a")))); assert(same(stabledepwwi, readfile(strings.concat(work, "/dep.good.wwi")))); assert(same(stablebin, readfile(bin))); assert(same(stablestamp, readfile(strings.concat(work, "/.wwtool.stamp")))); rewritefile(strings.concat(good, "/good.ww"), good6); rewritefile(compilertrace, ""); rewritefile(assemblertrace, ""); rewritefile(linkertrace, ""); runcommandenv(root, strings.concat("platform-private-edit-", labels[si]), forward, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(occurrences(readfile(compilertrace), "\n") == 1); assert(has(readfile(compilertrace), "/dep.good.unit.new")); assert(!has(readfile(compilertrace), "/app.unit.new")); assert(occurrences(readfile(assemblertrace), "\n") == 1); assert(occurrences(readfile(linkertrace), "\n") == 1); assert(same(stabledepwwi, readfile(strings.concat(work, "/dep.good.wwi")))); assert(same(stableunit, readfile(strings.concat(work, "/app.unit.ww")))); assert(same(stablewwi, readfile(strings.concat(work, "/app.wwi")))); assert(same(stablearchive, readfile(strings.concat(work, "/app.a")))); assert(!same(stablebin, readfile(bin))); let runchanged: []str = [bin]; runcommand(root, strings.concat("platform-run-edited-", labels[si]), runchanged, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 13); let committeddepunit: str = strings.dup(readfile(strings.concat(work, "/dep.good.unit.ww"))); let committeddepa: str = strings.dup(readfile(strings.concat(work, "/dep.good.a"))); let committedbin: str = strings.dup(readfile(bin)); rewritefile(strings.concat(good, "/good.ww"), good7); rewritefile(strings.concat(app, "/platform_future.ww"), "package main;\nfn future() i32 = { return missing_name; };\n"); rewritefile(compilertrace, ""); rewritefile(assemblertrace, ""); rewritefile(linkertrace, ""); runcommandenv(root, strings.concat("platform-late-failure-", labels[si]), forward, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, "undefined: missing_name")); let normalizedfailure: str = normalizedtrace(out.stderr, strings.concat(work, "/"), bin); if (si == 0) { faildiagref = strings.dup(normalizedfailure); } else { assert(same(faildiagref, normalizedfailure)); }; assert(occurrences(readfile(compilertrace), "\n") == 2); assert(occurrences(readfile(assemblertrace), "\n") == 1); assert(readfile(linkertrace).len == 0); assert(same(committeddepunit, readfile(strings.concat(work, "/dep.good.unit.ww")))); assert(same(committeddepa, readfile(strings.concat(work, "/dep.good.a")))); assert(same(committedbin, readfile(bin))); assert(same(stablestamp, readfile(strings.concat(work, "/.wwtool.stamp")))); assert(!os.exists(strings.concat(work, "/dep.good.unit.new"))); assert(!os.exists(strings.concat(work, "/dep.good.wwi.new"))); assert(!os.exists(strings.concat(work, "/dep.good.a.new"))); assert(!os.exists(strings.concat(work, "/app.unit.new"))); assert(!os.exists(strings.concat(work, "/app.wwi.new"))); assert(!os.exists(strings.concat(work, "/app.a.new"))); rewritefile(strings.concat(good, "/good.ww"), good6); rewritefile(strings.concat(app, "/platform_future.ww"), future); rewritefile(compilertrace, ""); rewritefile(assemblertrace, ""); rewritefile(linkertrace, ""); runcommandenv(root, strings.concat("platform-restored-", labels[si]), forward, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(readfile(compilertrace).len == 0); assert(readfile(assemblertrace).len == 0); assert(occurrences(readfile(linkertrace), "\n") == 1); assert(same(committedbin, readfile(bin))); rewritefile(strings.concat(good, "/good.ww"), good5); rewritefile(strings.concat(app, "/badparse_windows.ww"), ignoredparse); let prodwork: str = strings.concat(root, "/", labels[si], "-prod-work"); let prodout: str = strings.concat(root, "/", labels[si], "-pkg.a"); assert(os.mkdir(prodwork, 448i32) == 0); rewritefile(compilertrace, ""); rewritefile(assemblertrace, ""); rewritefile(linkertrace, ""); let prodav: []str = [driver(stages[si]), "build", "-w", prodwork, "-I", source, "-o", prodout, pkg]; runcommandenv(root, strings.concat("platform-production-", labels[si]), prodav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(occurrences(readfile(compilertrace), "\n") == 1); assert(!has(readfile(compilertrace), "test.good")); assert(!has(readfile(strings.concat(prodwork, "/pkg.unit.ww")), "selected_internal")); if (si == 0) { prodarchiveref = strings.dup(readfile(prodout)); prodwwiref = strings.dup(readfile(strings.concat(prodout, ".wwi"))); } else { assert(same(prodarchiveref, readfile(prodout))); assert(same(prodwwiref, readfile(strings.concat(prodout, ".wwi")))); }; let testwork: str = strings.concat(root, "/", labels[si], "-test-work"); assert(os.mkdir(testwork, 448i32) == 0); rewritefile(compilertrace, ""); rewritefile(assemblertrace, ""); rewritefile(linkertrace, ""); let testav: []str = [driver(stages[si]), "test", "-w", testwork, "-I", source, pkg]; runcommandenv(root, strings.concat("platform-test-", labels[si]), testav, env, (180i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "selected_internal_pair ... ok\n")); assert(has(out.stdout, "selected_whole_plan9_name ... ok\n")); assert(has(out.stdout, "selected_reversed_suffix ... ok\n")); assert(has(out.stdout, "selected_external_pair ... ok\n")); assert(has(out.stdout, "selected_external_after_dot ... ok\n")); assert(!has(out.stdout, "wrong_target_test")); assert(!has(out.stderr, "wrong-target")); let testctrace: str = readfile(compilertrace); let testltrace: str = readfile(linkertrace); assert(!has(testctrace, "test.wrong")); let sameline: str = linecontaining(testctrace, "/pkg-internal-test.unit.new"); let externalline: str = linecontaining(testctrace, "/pkg_test-external-test.unit.new"); assert(occurrences(sameline, "<--import>") == 1); assert(occurrences(externalline, "<--import>") == 1); assert(occurrences(externalline, "<--import>") == 1); assert(has(externalline, strings.concat("<--import><", testwork, "/pkg-internal-test.wwi.new>"))); let mainline: str = linecontaining(testctrace, "/pkg-test-main.unit.new"); assert(occurrences(mainline, "<--test-target-package>") == 2); assert(pos(mainline, "<--test-target-package>") < pos(mainline, "<--test-target-package>")); assert(occurrences(testltrace, "\n") == 1); assert(has(testltrace, "/pkg-test-main.a.new>")); assert(has(testltrace, "/pkg-internal-test.a.new>")); assert(has(testltrace, "/pkg_test-external-test.a.new>")); assert(!has(testltrace, ".wwi")); assert(!os.exists(strings.concat(testwork, "/pkg.unit.ww"))); let sameunit: str = readfile(strings.concat(testwork, "/pkg-internal-test.unit.ww")); let externalunit: str = readfile(strings.concat(testwork, "/pkg_test-external-test.unit.ww")); assert(has(sameunit, "selected_internal_pair")); assert(has(sameunit, "selected_whole_plan9_name")); assert(has(sameunit, "selected_reversed_suffix")); assert(!has(sameunit, "wrong_target_test")); assert(has(externalunit, "selected_external_pair")); assert(has(externalunit, "selected_external_after_dot")); assert(!has(externalunit, "wrong_target_test")); ai = 0; for (ai < testartifactnames.len) { let bytes: str = readfile(strings.concat(testwork, "/", testartifactnames[ai])); if (si == 0) { testartifactrefs[ai] = strings.dup(bytes); } else { assert(same(testartifactrefs[ai], bytes)); }; ai += 1; }; if (si == 0) { testoutref = strings.dup(out.stdout); testerrref = strings.dup(out.stderr); } else { assert(same(testoutref, out.stdout)); assert(same(testerrref, out.stderr)); }; let stabletestunit: str = strings.dup(sameunit); rewritefile(strings.concat(pkg, "/internal_windows_amd64_test.ww"), wrongtestedit); rewritefile(compilertrace, ""); rewritefile(assemblertrace, ""); rewritefile(linkertrace, ""); runcommandenv(root, strings.concat("platform-test-warm-", labels[si]), testav, env, (180i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(testoutref, out.stdout)); assert(readfile(compilertrace).len == 0); assert(readfile(assemblertrace).len == 0); assert(occurrences(readfile(linkertrace), "\n") == 1); assert(same(stabletestunit, readfile(strings.concat(testwork, "/pkg-internal-test.unit.ww")))); rewritefile(strings.concat(pkg, "/internal_windows_amd64_test.ww"), wrongtest); let onlywork: str = strings.concat(root, "/", labels[si], "-only-work"); let onlyout: str = strings.concat(root, "/", labels[si], "-only-bin"); assert(os.mkdir(onlywork, 448i32) == 0); rewritefile(compilertrace, ""); rewritefile(assemblertrace, ""); rewritefile(linkertrace, ""); let onlyav: []str = [driver(stages[si]), "build", "-w", onlywork, "-o", onlyout, wrongonly]; runcommandenv(root, strings.concat("platform-only-", labels[si]), onlyav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stderr, strings.concat("ww: ", wrongonly, ": directory contains no WW package sources\n"))); assert(readfile(compilertrace).len == 0); assert(readfile(assemblertrace).len == 0); assert(readfile(linkertrace).len == 0); assert(!os.exists(onlyout)); assert(!os.exists(strings.concat(onlywork, "/.wwtool.stamp"))); let orderwork: str = strings.concat(root, "/", labels[si], "-order-work"); assert(os.mkdir(orderwork, 448i32) == 0); rewritefile(compilertrace, ""); rewritefile(assemblertrace, ""); rewritefile(linkertrace, ""); let orderav: []str = [driver(stages[si]), "build", "-w", orderwork, "-o", strings.concat(root, "/", labels[si], "-order.a"), order]; runcommandenv(root, strings.concat("platform-order-", labels[si]), orderav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stderr, strings.concat("ww: ", order, "/a_linux.ww: @test declaration outside *_test.ww\n"))); assert(readfile(compilertrace).len == 0); assert(readfile(assemblertrace).len == 0); assert(readfile(linkertrace).len == 0); assert(!os.exists(strings.concat(orderwork, "/.wwtool.stamp"))); let recursivework: str = strings.concat(root, "/", labels[si], "-recursive-work"); let recursiveout: str = strings.concat(root, "/", labels[si], "-recursive-out"); assert(os.mkdir(recursivework, 448i32) == 0); assert(os.mkdir(recursiveout, 448i32) == 0); rewritefile(compilertrace, ""); rewritefile(assemblertrace, ""); rewritefile(linkertrace, ""); let recursiveav: []str = [driver(stages[si]), "build", "-w", recursivework, "-I", source, "-o", strings.concat(recursiveout, "/"), strings.concat(source, "/...")]; runcommandenv(root, strings.concat("platform-recursive-build-", labels[si]), recursiveav, env, (180i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(!has(readfile(compilertrace), "wrongonly")); assert(!has(readfile(compilertrace), "dep.wrong")); let recbin: str = strings.concat(recursiveout, "/app"); let recrun: []str = [recbin]; runcommand(root, strings.concat("platform-recursive-run-", labels[si]), recrun, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 12); assert(same(binref, readfile(recbin))); let recursivetestwork: str = strings.concat(root, "/", labels[si], "-recursive-test-work"); assert(os.mkdir(recursivetestwork, 448i32) == 0); rewritefile(compilertrace, ""); rewritefile(assemblertrace, ""); rewritefile(linkertrace, ""); let recursivetestav: []str = [driver(stages[si]), "test", "-w", recursivetestwork, "-I", source, strings.concat(source, "/...")]; runcommandenv(root, strings.concat("platform-recursive-test-", labels[si]), recursivetestav, env, (240i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "selected_internal_pair ... ok\n")); assert(has(out.stdout, "selected_external_pair ... ok\n")); assert(has(out.stdout, strings.concat("? ", notests, " [no test files]\n"))); assert(!has(out.stdout, "wrong_target_test")); assert(!has(out.stderr, "only_windows")); assert(!has(readfile(compilertrace), "dep.wrong")); if (si == 0) { recursiveoutref = strings.dup(out.stdout); recursiveerrref = strings.dup(out.stderr); } else { assert(same(recursiveoutref, out.stdout)); assert(same(recursiveerrref, out.stderr)); }; // An upper-case TEST suffix is production source, while its lower-case // twin is test source. Ordinary build stays isolated; test selects both // categories into one directory-owned filename-collision scope. let crosswork: str = strings.concat(root, "/", labels[si], "-fold-cross-build-work"); let crossout: str = strings.concat(root, "/", labels[si], "-fold-cross.a"); assert(os.mkdir(crosswork, 448i32) == 0); rewritefile(compilertrace, ""); rewritefile(assemblertrace, ""); rewritefile(linkertrace, ""); let crossbuildav: []str = [driver(stages[si]), "build", "-w", crosswork, "-I", foldfiles, "-o", crossout, foldcross]; runcommandenv(root, strings.concat("fold-cross-build-", labels[si]), crossbuildav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(!has(readfile(strings.concat(crosswork, "/cross.unit.ww")), "@test fn lower")); if (si == 0) { crossref = strings.dup(readfile(crossout)); } else { assert(same(crossref, readfile(crossout))); }; let foldtargets: []str = [foldunicode, foldinvalid, foldinternal, foldexternal, foldsameonly, foldexternalonly, foldmixed, foldcross]; let foldmodes: []str = ["build", "build", "test", "test", "test", "test", "test", "test"]; let foldpackages: []str = ["", "", "sample", "sample", "sample", "sample", "sample", "cross"]; let foldlabels: []str = ["unicode", "invalid", "internal", "external", "same-only", "external-only", "mixed", "cross"]; let foldwants: []str = [ "ww: case-insensitive file name collision: \"K.ww\" and \"K.ww\"\n", "ww: case-insensitive file name collision: \"A-\\xff.ww\" and \"a-\\xfe.ww\"\n", "ww: case-insensitive file name collision: \"File_test.ww\" and \"file_test.ww\"\n", "ww: case-insensitive file name collision: \"File_test.ww\" and \"file_test.ww\"\n", "ww: case-insensitive file name collision: \"File_test.ww\" and \"file_test.ww\"\n", "ww: case-insensitive file name collision: \"File_test.ww\" and \"file_test.ww\"\n", "ww: case-insensitive file name collision: \"File_test.ww\" and \"file_test.ww\"\n", "ww: case-insensitive file name collision: \"X_TEST.ww\" and \"x_test.ww\"\n", ]; let fi: i32 = 0; for (fi < foldtargets.len) { let foldwork: str = strings.concat(root, "/", labels[si], "-fold-", foldlabels[fi], "-work"); let foldoutput: str = strings.concat(root, "/", labels[si], "-fold-", foldlabels[fi]); assert(os.mkdir(foldwork, 448i32) == 0); rewritefile(compilertrace, ""); rewritefile(assemblertrace, ""); rewritefile(linkertrace, ""); let foldbuildav: []str = [driver(stages[si]), "build", "-w", foldwork, "-I", foldfiles, "-o", foldoutput, foldtargets[fi]]; let foldtestav: []str = [driver(stages[si]), "test", "-w", foldwork, "-I", foldfiles, foldtargets[fi]]; let foldav: []str = foldtestav; if (same(foldmodes[fi], "build")) { foldav = foldbuildav; }; runcommandenv(root, strings.concat("filename-fold-", labels[si], "-", foldlabels[fi]), foldav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); let foldfullwant: str = foldwants[fi]; let foldstdoutwant: str = ""; if (same(foldmodes[fi], "test")) { foldfullwant = strings.concat(foldwants[fi], "FAIL ", foldtargets[fi], " [", foldpackages[fi], "] (build exit 1)\n"); foldstdoutwant = "FAIL\n"; }; assert(same(out.stdout, foldstdoutwant) && same(out.stderr, foldfullwant)); assert(readfile(compilertrace).len == 0); assert(readfile(assemblertrace).len == 0); assert(readfile(linkertrace).len == 0); assert(!os.exists(foldoutput)); assert(!os.exists(strings.concat(foldoutput, ".new"))); assert(!os.exists(strings.concat(foldoutput, ".sepwork"))); assert(!os.exists(strings.concat(foldwork, "/.wwtool.stamp"))); assert(directoryisempty(foldwork)); if (si == 0) { foldfilerefs[fi] = strings.dup(out.stderr); } else { assert(same(foldfilerefs[fi], out.stderr)); }; fi += 1; }; let creationmodes: []str = ["direct", "recursive"]; let creationfull: []str = ["", ""]; let creationwant: str = "ww: case-insensitive file name collision: \"File.ww\" and \"file.ww\"\n"; let ci: i32 = 0; for (ci < creationmodes.len) { let creationwork: str = strings.concat(root, "/", labels[si], "-fold-creation-", creationmodes[ci], "-work"); let creationout: str = strings.concat(root, "/", labels[si], "-fold-creation-", creationmodes[ci]); assert(os.mkdir(creationwork, 448i32) == 0); rewritefile(compilertrace, ""); rewritefile(assemblertrace, ""); rewritefile(linkertrace, ""); let directav: []str = [driver(stages[si]), "build", "-w", creationwork, "-I", foldcreationroot, "-o", creationout, foldcreation]; if (ci != 0) { assert(os.mkdir(creationout, 448i32) == 0); }; let recursiveav: []str = [driver(stages[si]), "build", "-w", creationwork, "-I", foldcreationroot, "-o", strings.concat(creationout, "/"), strings.concat(foldcreationroot, "/...")]; let creationav: []str = directav; if (ci != 0) { creationav = recursiveav; }; runcommandenv(root, strings.concat("filename-fold-creation-", labels[si], "-", creationmodes[ci]), creationav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0); // Build-output failures are emitted by the shared driver after its // load/check boundary; the coordinator adds no test-style FAIL line. assert(same(out.stderr, creationwant)); assert(readfile(compilertrace).len == 0); assert(readfile(assemblertrace).len == 0); assert(readfile(linkertrace).len == 0); if (ci == 0) { assert(!os.exists(creationout)); } else { assert(directoryisempty(creationout)); }; assert(!os.exists(strings.concat(creationout, ".new"))); assert(!os.exists(strings.concat(creationout, ".sepwork"))); assert(directoryisempty(creationwork)); creationfull[ci] = strings.dup(out.stderr); ci += 1; }; if (si == 0) { foldcreationref = strings.dup(creationfull[0]); foldcreationrecursiveref = strings.dup(creationfull[1]); } else { assert(same(foldcreationref, creationfull[0])); assert(same(foldcreationrecursiveref, creationfull[1])); }; // Canonically equivalent Unicode normalization forms remain distinct. let nonormalwork: str = strings.concat(root, "/", labels[si], "-fold-nonormal-work"); let nonormalout: str = strings.concat(root, "/", labels[si], "-fold-nonormal"); assert(os.mkdir(nonormalwork, 448i32) == 0); rewritefile(compilertrace, ""); rewritefile(assemblertrace, ""); rewritefile(linkertrace, ""); let nonormalav: []str = [driver(stages[si]), "build", "-w", nonormalwork, "-I", foldfiles, "-o", nonormalout, foldnonormal]; runcommandenv(root, strings.concat("filename-no-normalization-", labels[si]), nonormalav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); let nonormalunit: str = readfile(strings.concat(nonormalwork, "/nonormal.unit.ww")); assert(has(nonormalunit, "fn composed() i32")); assert(has(nonormalunit, "fn decomposed() i32")); if (si == 0) { nonormalref = strings.dup(readfile(nonormalout)); } else { assert(same(nonormalref, readfile(nonormalout))); }; if (si == 0) { nonormalunitref = strings.dup(nonormalunit); } else { assert(same(nonormalunitref, nonormalunit)); }; // A collision introduced after a committed generation is pre-tool and // cannot mutate any successful action, stamp, or published output. Its // removal restores exact warm reuse (only the normal relink remains). let warmname: str = strings.concat("warm", labels[si]); let warmdir: str = strings.concat(foldfiles, "/", warmname); mkdirall(warmdir); writefile(strings.concat(warmdir, "/File.ww"), strings.concat( "package main;\nfn upper() i32 = { return 1; };\n")); writefile(strings.concat(warmdir, "/main.ww"), strings.concat( "package main;\nfn main() i32 = { return upper() - 1; };\n")); let warmwork: str = strings.concat(root, "/", labels[si], "-fold-warm-work"); let warmout: str = strings.concat(root, "/", labels[si], "-fold-warm"); assert(os.mkdir(warmwork, 448i32) == 0); let warmav: []str = [driver(stages[si]), "build", "-w", warmwork, "-I", foldfiles, "-o", warmout, warmdir]; rewritefile(compilertrace, ""); rewritefile(assemblertrace, ""); rewritefile(linkertrace, ""); runcommandenv(root, strings.concat("filename-fold-warm-cold-", labels[si]), warmav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let warmsuffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a", ".init.unit.ww", ".init.s", ".init.o"]; let warmrefs: []str = ["", "", "", "", "", "", "", ""]; let wi: i32 = 0; for (wi < warmsuffixes.len) { warmrefs[wi] = strings.dup(readfile(strings.concat(warmwork, "/", warmname, warmsuffixes[wi]))); wi += 1; }; let warmbinref: str = strings.dup(readfile(warmout)); let warmstampref: str = strings.dup(readfile(strings.concat(warmwork, "/.wwtool.stamp"))); let warmtoolpaths: []str = ["/.wwtool.ww", "/.wwtool.w6c", "/.wwtool.w6a"]; let warmtoolrefs: []str = alloc([], warmtoolpaths.len: u64)!; wi = 0; for (wi < warmtoolpaths.len) { append(warmtoolrefs, strings.dup(readfile(strings.concat(warmwork, warmtoolpaths[wi])))); wi += 1; }; let collidingfile: str = strings.concat(warmdir, "/file.ww"); writefile(collidingfile, "package main;\nfn lower() i32 = { return 2; };\n"); rewritefile(compilertrace, ""); rewritefile(assemblertrace, ""); rewritefile(linkertrace, ""); runcommandenv(root, strings.concat("filename-fold-warm-added-", labels[si]), warmav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && same(out.stderr, "ww: case-insensitive file name collision: \"File.ww\" and \"file.ww\"\n")); assert(readfile(compilertrace).len == 0); assert(readfile(assemblertrace).len == 0); assert(readfile(linkertrace).len == 0); wi = 0; for (wi < warmsuffixes.len) { assert(same(warmrefs[wi], readfile(strings.concat(warmwork, "/", warmname, warmsuffixes[wi])))); wi += 1; }; wi = 0; for (wi < warmtoolpaths.len) { assert(same(warmtoolrefs[wi], readfile(strings.concat(warmwork, warmtoolpaths[wi])))); wi += 1; }; assert(!directoryhasnew(warmwork)); assert(same(warmbinref, readfile(warmout))); assert(!os.exists(strings.concat(warmout, ".new"))); assert(!os.exists(strings.concat(warmout, ".sepwork"))); assert(same(warmstampref, readfile(strings.concat(warmwork, "/.wwtool.stamp")))); assert(os.remove(collidingfile) == 0); rewritefile(compilertrace, ""); rewritefile(assemblertrace, ""); rewritefile(linkertrace, ""); runcommandenv(root, strings.concat("filename-fold-warm-removed-", labels[si]), warmav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(readfile(compilertrace).len == 0); assert(readfile(assemblertrace).len == 0); assert(occurrences(readfile(linkertrace), "\n") == 1); wi = 0; for (wi < warmsuffixes.len) { assert(same(warmrefs[wi], readfile(strings.concat(warmwork, "/", warmname, warmsuffixes[wi])))); wi += 1; }; wi = 0; for (wi < warmtoolpaths.len) { assert(same(warmtoolrefs[wi], readfile(strings.concat(warmwork, warmtoolpaths[wi])))); wi += 1; }; assert(!directoryhasnew(warmwork)); assert(same(warmbinref, readfile(warmout))); assert(!os.exists(strings.concat(warmout, ".new"))); assert(!os.exists(strings.concat(warmout, ".sepwork"))); assert(same(warmstampref, readfile(strings.concat(warmwork, "/.wwtool.stamp")))); si += 1; }; clean(root); }; // A package declared `main` may reserve the entry spelling only for a function. // This is the declaration-kind half of pinned Go's main rule; WW deliberately // retains its C/Hare-style argument/result entry ABI. The checker owns the // reject before assembly/linking, directory tests reject before execution, and // declared package name remains independent from dotted/physical identity. @test fn nonfunction_main_declarations_reject() void = { let root: str = fresh(); let shapes: []str = [ "let main: i32 = 0;\n", "const main: i32 = 0;\n", "def main: i32 = 0;\n", "type main = i32;\n", ]; let labels: []str = ["let", "const", "def", "type"]; let expected: str = "cannot declare main - must be func"; let i: i32 = 0; for (i < shapes.len) { let dir: str = strings.concat(root, "/invalid-", labels[i]); assert(os.mkdir(dir, 448i32) == 0); let src: str = strings.concat(dir, "/main.ww"); writefile(src, strings.concat("package main;\n", shapes[i])); let output: str = strings.concat(root, "/invalid-output-", labels[i]); let avc: []str = [driver("ww"), "build", "-o", output, dir]; let avw: []str = [driver("ww_ww"), "build", "-o", output, dir]; let outc: commandout; let outw: commandout; runcommand(root, strings.concat("invalid-main-c-", labels[i]), avc, (30i64 * (time.second: i64)): time.duration, &outc); runcommand(root, strings.concat("invalid-main-ww-", labels[i]), avw, (30i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 1); expectexit(&outw, 1); assert(outc.stdout.len == 0 && outw.stdout.len == 0); assert(same(primarydiagnostic(outc.stderr), expected)); assert(same(primarydiagnostic(outw.stderr), expected)); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stderr, "ww: w6c failed for ")); assert(!has(outc.stderr, "w6a") && !has(outc.stderr, "w6l")); assert(!os.exists(output)); assert(!os.exists(strings.concat(output, ".new"))); assert(!os.exists(strings.concat(output, ".install"))); assert(!os.exists(strings.concat(output, ".sepwork"))); assert(!directoryhasfragment(root, ".wwtxn.")); i += 1; }; // Direct compiler calls pin the true owner and byte-identical frontend // diagnostic independently from driver-generated unit paths. let directsrc: str = strings.concat(root, "/invalid-let/main.ww"); let directc: str = strings.concat(root, "/direct-c.s"); let directw: str = strings.concat(root, "/direct-ww.s"); let dcav: []str = [driver("w6c"), "-c", "--entry", "-o", directc, directsrc]; let dwav: []str = [driver("w6c_ww"), "-c", "--entry", "-o", directw, directsrc]; let directco: commandout; let directwo: commandout; runcommand(root, "invalid-main-direct-c", dcav, (30i64 * (time.second: i64)): time.duration, &directco); runcommand(root, "invalid-main-direct-ww", dwav, (30i64 * (time.second: i64)): time.duration, &directwo); expectexit(&directco, 1); expectexit(&directwo, 1); assert(directco.stdout.len == 0 && directwo.stdout.len == 0); assert(same(directco.stderr, directwo.stderr)); assert(same(primarydiagnostic(directco.stderr), expected)); assert(!os.exists(directc) && !os.exists(directw)); // The same malformed production package must not become a passing test // package merely because the dispatcher owns a separate synthetic entry. let testdir: str = strings.concat(root, "/invalid-test"); assert(os.mkdir(testdir, 448i32) == 0); writefile(strings.concat(testdir, "/main.ww"), "package main;\nlet main: i32 = 0;\n"); writefile(strings.concat(testdir, "/main_test.ww"), "package main;\n@test fn must_not_run() void = { abort(\"ran\"); };\n"); let testdiags: []str = ["", ""]; let stages: []str = ["ww", "ww_ww"]; let tags: []str = ["c", "ww"]; i = 0; for (i < stages.len) { let av: []str = [driver(stages[i]), "test", testdir]; let out: commandout; runcommand(root, strings.concat("invalid-main-test-", tags[i]), av, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stdout, "FAIL\n")); assert(!has(out.stdout, "must_not_run") && !has(out.stdout, " passed,")); assert(!has(out.stderr, "must_not_run") && !has(out.stderr, "ok ")); assert(has(out.stderr, expected)); assert(has(out.stderr, strings.concat("FAIL ", testdir))); testdiags[i] = strings.dup(primarydiagnostic(out.stderr)); i += 1; }; assert(same(testdiags[0], expected) && same(testdiags[0], testdiags[1])); // A warm semantic failure preserves every committed action byte, tool // voucher, and public binary, then exact source restoration reuses them. let warmdiags: []str = ["", ""]; i = 0; for (i < stages.len) { let warmdir: str = strings.concat(root, "/warm-", tags[i]); let work: str = strings.concat(root, "/warm-work-", tags[i]); let output: str = strings.concat(root, "/warm-output-", tags[i]); assert(os.mkdir(warmdir, 448i32) == 0); assert(os.mkdir(work, 448i32) == 0); let source: str = strings.concat(warmdir, "/main.ww"); let valid: str = "package main;\nfn main() i32 = { return 0; };\n"; writefile(source, valid); let av: []str = [driver(stages[i]), "build", "-w", work, "-o", output, warmdir]; let out: commandout; runcommand(root, strings.concat("invalid-main-warm-cold-", tags[i]), av, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); let action: str = localidentity(warmdir, "main"); let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a", ".init.unit.ww", ".init.s", ".init.o"]; let refs: []str = alloc([], suffixes.len: u64)!; let j: i32 = 0; for (j < suffixes.len) { append(refs, strings.dup(readfile(strings.concat(strings.concat(work, "/"), strings.concat(action, suffixes[j]))))); j += 1; }; let toolpaths: []str = ["/.wwtool.ww", "/.wwtool.w6c", "/.wwtool.w6a", "/.wwtool.stamp"]; let toolrefs: []str = alloc([], toolpaths.len: u64)!; j = 0; for (j < toolpaths.len) { append(toolrefs, strings.dup(readfile(strings.concat(work, toolpaths[j])))); j += 1; }; let binref: str = strings.dup(readfile(output)); rewritefile(source, "package main;\nlet main: i32 = 0;\n"); runcommand(root, strings.concat("invalid-main-warm-reject-", tags[i]), av, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && same(primarydiagnostic(out.stderr), expected)); warmdiags[i] = strings.dup(primarydiagnostic(out.stderr)); j = 0; for (j < suffixes.len) { assert(same(refs[j], readfile(strings.concat(strings.concat(work, "/"), strings.concat(action, suffixes[j]))))); j += 1; }; j = 0; for (j < toolpaths.len) { assert(same(toolrefs[j], readfile(strings.concat(work, toolpaths[j])))); j += 1; }; assert(same(binref, readfile(output))); assert(!directoryhasnew(work)); assert(!os.exists(strings.concat(output, ".new"))); assert(!os.exists(strings.concat(output, ".install"))); assert(!os.exists(strings.concat(output, ".sepwork"))); assert(!directoryhasfragment(root, ".wwtxn.")); rewritefile(source, valid); runcommand(root, strings.concat("invalid-main-warm-restored-", tags[i]), av, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(same(binref, readfile(output)) && !directoryhasnew(work)); i += 1; }; assert(same(warmdiags[0], expected) && same(warmdiags[0], warmdiags[1])); // The declared package name, never the dotted path leaf or physical // directory, owns the rule. A non-main package may export `main`, and a // command may import it while retaining WW's argument/result entry ABI. let tree: str = strings.concat(root, "/tree"); let dep: str = strings.concat(tree, "/domain/main"); let app: str = strings.concat(tree, "/app"); mkdirall(dep); assert(os.mkdir(app, 448i32) == 0); writefile(strings.concat(dep, "/dep.ww"), "package utility;\nexport let main: i32 = 7;\n"); writefile(strings.concat(app, "/main.ww"), strings.concat( "package main;\nimport domain.main;\n", "fn main(argc: i32, argv: **u8) i32 = { return utility.main - 7; };\n")); let binaries: []str = [strings.concat(root, "/identity-c"), strings.concat(root, "/identity-ww")]; i = 0; for (i < stages.len) { let av: []str = [driver(stages[i]), "build", "-I", tree, "-o", binaries[i], app]; let out: commandout; runcommand(root, strings.concat("main-name-boundary-", tags[i]), av, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); let runav: []str = [binaries[i], "one", "two"]; runcommand(root, strings.concat("main-name-boundary-run-", tags[i]), runav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); i += 1; }; assert(same(readfile(binaries[0]), readfile(binaries[1]))); let artifacts: []str = ["domain.main.unit.ww", "domain.main.wwi", "domain.main.s", "domain.main.o", "domain.main.a"]; i = 0; for (i < artifacts.len) { assert(same(readfile(strings.concat(strings.concat(binaries[0], ".sepwork/"), artifacts[i])), readfile(strings.concat(strings.concat(binaries[1], ".sepwork/"), artifacts[i])))); i += 1; }; // Importing any declared-main package is still the loader-owned error and // retains precedence over the declaration checker, even when that command // also contains the malformed declaration. let badcmd: str = strings.concat(tree, "/badcmd"); let client: str = strings.concat(tree, "/client"); assert(os.mkdir(badcmd, 448i32) == 0); assert(os.mkdir(client, 448i32) == 0); writefile(strings.concat(badcmd, "/main.ww"), "package main;\nlet main: i32 = 0;\n"); writefile(strings.concat(client, "/client.ww"), strings.concat( "package client;\nimport _ badcmd;\n", "export fn value() i32 = { return 7; };\n")); let importout: str = strings.concat(root, "/bad-import-output"); let importdiag: str = "ww: package badcmd is a program, not an importable package\n"; i = 0; for (i < stages.len) { let av: []str = [driver(stages[i]), "build", "-I", tree, "-o", importout, client]; let out: commandout; runcommand(root, strings.concat("bad-main-import-", tags[i]), av, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && same(out.stderr, importdiag)); assert(!has(out.stderr, expected)); assert(!os.exists(importout) && !os.exists(strings.concat(importout, ".sepwork"))); i += 1; }; clean(root); }; // Invalid @test attribute shapes reject at build with stable text on // BOTH frontends (the -T synth checker owns them; the fixture corpus // cannot reach -T, so these rows live here). Fragments only — the // stages' error-line prefixes legitimately differ (file:line:col vs // file:). @test fn invalid_test_shapes_reject() void = { let root: str = fresh(); let shapes: []str = [ "@test export fn t() void = { };", "@test fn t() void;", "@test fn t(x: i32) void = { };", "@test fn t() i32 = { return 0; };", "@test @test fn t() void = { };", ]; let frags: []str = [ "@test fn 't' cannot be exported", "@test fn 't' needs a body", "@test fn 't' must be fn() void", "@test fn 't' must be fn() void", "duplicate @test on fn 't'", ]; let digits: []str = ["0", "1", "2", "3", "4"]; let outc: commandout; let outw: commandout; let i: i32 = 0; for (i < shapes.len) { let dir: str = strings.concat(root, "/shape", digits[i]); assert(os.mkdir(dir, 448i32) == 0); let src: str = strings.concat(dir, "/bad_test.ww"); writefile(src, strings.concat("package main;\n", shapes[i], "\n")); let avc: []str = [driver("ww"), "test", src]; let avw: []str = [driver("ww_ww"), "test", src]; runcommand(root, strings.concat("shape-c", digits[i]), avc, (30i64 * (time.second: i64)): time.duration, &outc); runcommand(root, strings.concat("shape-ww", digits[i]), avw, (30i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 1); expectexit(&outw, 1); assert(has(outc.stderr, frags[i])); assert(has(outw.stderr, frags[i])); i += 1; }; clean(root); }; // Go's local-pattern parity: "..." walks below the prefix preceding its first // occurrence. Dot-, underscore-, and testdata-prefixed trees are excluded; // vendor is traversed but a wildcard cannot consume a non-terminal vendor. // Excluded sentinels fail so a wrong descent turns the whole run red. @test fn recursive_tree_discovery() void = { let root: str = fresh(); let tree: str = strings.concat(root, "/tree"); assert(os.mkdir(tree, 448i32) == 0); assert(os.mkdir(strings.concat(tree, "/alpha"), 448i32) == 0); assert(os.mkdir(strings.concat(tree, "/beta"), 448i32) == 0); assert(os.mkdir(strings.concat(tree, "/beta/inner"), 448i32) == 0); assert(os.mkdir(strings.concat(tree, "/gamma"), 448i32) == 0); assert(os.mkdir(strings.concat(tree, "/app"), 448i32) == 0); assert(os.mkdir(strings.concat(tree, "/app/vendor"), 448i32) == 0); assert(os.mkdir(strings.concat(tree, "/app/vendor/lib"), 448i32) == 0); assert(os.mkdir(strings.concat(tree, "/app/vendor/lib/math"), 448i32) == 0); assert(os.mkdir(strings.concat(tree, "/domain"), 448i32) == 0); assert(os.mkdir(strings.concat(tree, "/domain/app"), 448i32) == 0); assert(os.mkdir(strings.concat(tree, "/domain/vendor"), 448i32) == 0); assert(os.mkdir(strings.concat(tree, "/domain/vendor/dep"), 448i32) == 0); assert(os.mkdir(strings.concat(tree, "/.hidden"), 448i32) == 0); assert(os.mkdir(strings.concat(tree, "/.hidden/deep"), 448i32) == 0); assert(os.mkdir(strings.concat(tree, "/_skip"), 448i32) == 0); assert(os.mkdir(strings.concat(tree, "/testdata"), 448i32) == 0); assert(os.mkdir(strings.concat(tree, "/testdata/bad"), 448i32) == 0); assert(os.mkdir(strings.concat(tree, "/vendor"), 448i32) == 0); assert(os.mkdir(strings.concat(tree, "/vendor/bad"), 448i32) == 0); assert(os.mkdir(strings.concat(tree, "/vendor/bad/vendor"), 448i32) == 0); assert(os.mkdir(strings.concat(tree, "/vendor/bad/vendor/deep"), 448i32) == 0); assert(os.mkdir(strings.concat(tree, "/vendorx"), 448i32) == 0); writefile(strings.concat(tree, "/a.ww"), "package rootpkg;\nfn first() i32 = { return 1; };\n"); writefile(strings.concat(tree, "/root_test.ww"), strings.concat( "package rootpkg;\n", "@test fn treeroot() void = { assert(first() + last() == 3); };\n")); writefile(strings.concat(tree, "/z.ww"), "package rootpkg;\nfn last() i32 = { return 2; };\n"); writefile(strings.concat(tree, "/alpha/alpha.ww"), "package alpha;\nexport fn value() int = { return 7; };\n"); writefile(strings.concat(tree, "/alpha/alpha_test.ww"), strings.concat("package alpha_test;\nimport alpha;\n", "@test fn treealpha() void = { assert(alpha.value() == 7);", " assert(alpha.linked() == 11); };\n")); writefile(strings.concat(tree, "/beta/inner/inner_test.ww"), "package inner;\n@test fn treeinner() void = { assert(1 == 1); };\n"); writefile(strings.concat(tree, "/gamma/gamma.ww"), "package gamma;\nexport fn g() int = { return 1; };\n"); writefile(strings.concat(tree, "/app/app.ww"), strings.concat( "package app;\nimport lib.math;\n", "export fn value() int = { return math.value(); };\n")); writefile(strings.concat(tree, "/app/app_test.ww"), strings.concat( "package app;\n@test fn vendored_dependency() void = {", " assert(value() == 17); };\n")); writefile(strings.concat(tree, "/app/vendor/lib/math/math.ww"), "package math;\nexport fn value() int = { return 17; };\n"); writefile(strings.concat(tree, "/domain/app/app.ww"), strings.concat( "package app;\nimport dep;\n", "fn value() int = { return dep.value(); };\n")); writefile(strings.concat(tree, "/domain/app/app_test.ww"), strings.concat( "package app;\n@test fn ancestor_vendor() void = {", " assert(value() == 29); };\n")); writefile(strings.concat(tree, "/domain/vendor/dep/dep.ww"), "package dep;\nexport fn value() int = { return 29; };\n"); writefile(strings.concat(tree, "/gamma/.ignored.ww"), "this hidden source must not make package loading fail\n"); writefile(strings.concat(tree, "/gamma/_ignored.ww"), "this underscore source must not make package loading fail\n"); writefile(strings.concat(tree, "/.hidden/hid_test.ww"), "package hid;\n@test fn hidden_direct() void = { assert(true); };\n"); writefile(strings.concat(tree, "/.hidden/deep/bad.ww"), "this excluded malformed directory must not be selected\n"); writefile(strings.concat(tree, "/_skip/skip_test.ww"), "package _skip;\n@test fn underscore_direct() void = { assert(true); };\n"); writefile(strings.concat(tree, "/testdata/bad/bad_test.ww"), "package bad;\n@test fn testdata_direct() void = { assert(true); };\n"); writefile(strings.concat(tree, "/vendor/vendor.ww"), "package vendor;\nexport fn value() int = { return 1; };\n"); writefile(strings.concat(tree, "/vendor/bad/bad.ww"), "package bad;\nexport fn value() int = { return 2; };\n"); writefile(strings.concat(tree, "/vendor/bad/bad_test.ww"), "package bad;\n@test fn explicit_vendor() void = { assert(value() == 2); };\n"); writefile(strings.concat(tree, "/vendor/bad/vendor/deep/deep_test.ww"), "package deep;\n@test fn nested_vendor_must_not_run() void = { assert(false); };\n"); writefile(strings.concat(tree, "/vendorx/vendorx.ww"), "package vendorx;\nexport fn value() int = { return 3; };\n"); let outside: str = strings.concat(root, "/outside"); let alias: str = strings.concat(root, "/tree-alias"); assert(os.mkdir(outside, 448i32) == 0); writefile(strings.concat(outside, "/outside.ww"), "package outside;\nexport fn value() int = { return 23; };\n"); writefile(strings.concat(outside, "/outside_test.ww"), strings.concat( "package outside;\n@test fn explicit_root_symlink() void = {", " assert(value() == 23); };\n")); let linkout: commandout; let linkedsource: str = strings.concat(root, "/alpha-linked-source"); writefile(linkedsource, "package alpha;\nexport fn linked() int = { return 11; };\n"); let linkedav: []str = ["/bin/ln", "-s", linkedsource, strings.concat(tree, "/alpha/linked.ww")]; runcommand(root, "recursive-source-link", linkedav, time.second, &linkout); expectexit(&linkout, 0); let linkeddirectory: str = strings.concat(root, "/source-directory"); assert(os.mkdir(linkeddirectory, 448i32) == 0); let linkeddirav: []str = ["/bin/ln", "-s", linkeddirectory, strings.concat(tree, "/alpha/directory.ww")]; runcommand(root, "recursive-source-directory-link", linkeddirav, time.second, &linkout); expectexit(&linkout, 0); let aliasav: []str = ["/bin/ln", "-s", tree, alias]; runcommand(root, "recursive-root-alias", aliasav, time.second, &linkout); expectexit(&linkout, 0); let escapeav: []str = ["/bin/ln", "-s", outside, strings.concat(tree, "/escape")]; runcommand(root, "recursive-child-escape", escapeav, time.second, &linkout); expectexit(&linkout, 0); let cycleav: []str = ["/bin/ln", "-s", tree, strings.concat(tree, "/cycle")]; runcommand(root, "recursive-child-cycle", cycleav, time.second, &linkout); expectexit(&linkout, 0); let spec: str = strings.concat(tree, "/..."); let outc: commandout; let outw: commandout; let treec: []str = [driver("ww"), "test", "-I", tree, spec]; let treew: []str = [driver("ww_ww"), "test", "-I", tree, spec]; runcommand(root, "tree-c", treec, (30i64 * (time.second: i64)): time.duration, &outc); runcommand(root, "tree-ww", treew, (30i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 0); expectexit(&outw, 0); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); let seq: str = strings.dup(outc.stdout); // A directory -o publishes every selected command by directory basename. // Non-command production roots remain graph roots but have no named output. let commands: str = strings.concat(root, "/commands"); let commandalpha: str = strings.concat(commands, "/cmd-alpha"); let commandzeta: str = strings.concat(commands, "/cmd-zeta"); let commandlib: str = strings.concat(commands, "/library"); assert(os.mkdir(commands, 448i32) == 0); assert(os.mkdir(commandalpha, 448i32) == 0); assert(os.mkdir(commandzeta, 448i32) == 0); assert(os.mkdir(commandlib, 448i32) == 0); writefile(strings.concat(commandalpha, "/main.ww"), strings.concat( "package main;\nimport library;\n", "fn main() i32 = { return library.value(); };\n")); writefile(strings.concat(commandzeta, "/main.ww"), "package main;\nfn main() i32 = { return 0; };\n"); writefile(strings.concat(commandlib, "/library.ww"), "package library;\nexport fn value() i32 = { return 0; };\n"); let commandalias: str = strings.concat(root, "/commands-alias"); let commandaliasav: []str = ["/bin/ln", "-s", commands, commandalias]; runcommand(root, "recursive-command-alias", commandaliasav, time.second, &linkout); expectexit(&linkout, 0); let dashselector: str = "-dash-command"; let dashselectorpath: str = strings.concat(root, "/", dashselector); let dashselectorav: []str = ["/bin/ln", "-s", commandalpha, dashselectorpath]; runcommand(root, "recursive-dash-selector", dashselectorav, time.second, &linkout); expectexit(&linkout, 0); let commandalphaid: str = localidentity(commandalpha, "main"); let commandzetaid: str = localidentity(commandzeta, "main"); let binc: str = strings.concat(root, "/bin-c"); let binw: str = strings.concat(root, "/bin-ww"); let reversebinc: str = strings.concat(root, "/reverse-bin-c"); let reversebinw: str = strings.concat(root, "/reverse-bin-ww"); assert(os.mkdir(binc, 448i32) == 0); assert(os.mkdir(binw, 448i32) == 0); let commandspec: str = strings.concat(commands, "/..."); let commandaliasspec: str = strings.concat(commandalias, "/..."); let commandworks: []str = [strings.concat(root, "/command-work-c"), strings.concat(root, "/command-work-ww")]; let commandreverseworks: []str = [strings.concat(root, "/command-reverse-work-c"), strings.concat(root, "/command-reverse-work-ww")]; let commandbins: []str = [binc, binw]; let commandreversebins: []str = [reversebinc, reversebinw]; let commandreverseoutputs: []str = [strings.concat(reversebinc, "/"), strings.concat(reversebinw, "/")]; let commandcompilertraces: []str = [strings.concat(root, "/command-compiler-c"), strings.concat(root, "/command-compiler-ww")]; let commandassemblertraces: []str = [strings.concat(root, "/command-assembler-c"), strings.concat(root, "/command-assembler-ww")]; let commandlinkertraces: []str = [strings.concat(root, "/command-linker-c"), strings.concat(root, "/command-linker-ww")]; let commandasmworks: []str = [strings.concat(root, "/asm-work-c"), strings.concat(root, "/asm-work-ww")]; let commandtools: str = strings.concat(root, "/command-tools"); assert(os.mkdir(commandtools, 448i32) == 0); let commandcompilerwrapper: str = strings.concat(commandtools, "/w6c.sh"); let commandassemblerwrapper: str = strings.concat(commandtools, "/w6a.sh"); let commandlinkerwrapper: str = strings.concat(commandtools, "/w6l.sh"); writeexecutable(commandcompilerwrapper, strings.concat( "#!/bin/sh\nprintf 'BEGIN' >> \"$WW_PATTERN_COMPILER_TRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_PATTERN_COMPILER_TRACE\"; done\n", "printf '\\n' >> \"$WW_PATTERN_COMPILER_TRACE\"\n", "exec \"$WW_PATTERN_REAL_COMPILER\" \"$@\"\n")); writeexecutable(commandassemblerwrapper, strings.concat( "#!/bin/sh\nprintf 'BEGIN' >> \"$WW_PATTERN_ASSEMBLER_TRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_PATTERN_ASSEMBLER_TRACE\"; done\n", "printf '\\n' >> \"$WW_PATTERN_ASSEMBLER_TRACE\"\n", "exec \"$WW_PATTERN_REAL_ASSEMBLER\" \"$@\"\n")); writeexecutable(commandlinkerwrapper, strings.concat( "#!/bin/sh\nprintf 'BEGIN' >> \"$WW_PATTERN_LINKER_TRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_PATTERN_LINKER_TRACE\"; done\n", "printf '\\n' >> \"$WW_PATTERN_LINKER_TRACE\"\n", "exec \"$WW_PATTERN_REAL_LINKER\" \"$@\"\n")); let collisions: str = strings.concat(root, "/collisions"); let collisionleft: str = strings.concat(collisions, "/left"); let collisionright: str = strings.concat(collisions, "/right"); let collisionleftcmd: str = strings.concat(collisionleft, "/same"); let collisionrightcmd: str = strings.concat(collisionright, "/same"); assert(os.mkdir(collisions, 448i32) == 0); assert(os.mkdir(collisionleft, 448i32) == 0); assert(os.mkdir(collisionright, 448i32) == 0); assert(os.mkdir(collisionleftcmd, 448i32) == 0); assert(os.mkdir(collisionrightcmd, 448i32) == 0); writefile(strings.concat(collisionleftcmd, "/main.ww"), "package main;\nfn main() i32 = { return 0; };\n"); writefile(strings.concat(collisionrightcmd, "/main.ww"), "package main;\nfn main() i32 = { return 0; };\n"); let collisionspec: str = strings.concat(collisions, "/..."); let collisionout: str = strings.concat(root, "/collision-bin"); assert(os.mkdir(collisionout, 448i32) == 0); let collisionworks: []str = [strings.concat(root, "/collision-work-c"), strings.concat(root, "/collision-work-ww")]; let commandstages: []str = ["ww", "ww_ww"]; let commandcompilers: []str = ["w6c", "w6c_ww"]; let commandassemblers: []str = ["w6a", "w6a_ww"]; let commandlinkers: []str = ["w6l", "w6l_ww"]; let commandbaseenv: []str = os.getenvs(); let referencecommandcompiler: str = ""; let referencecommandassembler: str = ""; let referencecommandlinker: str = ""; let referenceasmcompiler: str = ""; let referenceasmrejectdiag: str = ""; let referencecollisiondiag: str = ""; let referencefanoutpathdiag: str = ""; let referencescratchpathdiag: str = ""; let referenceworkpathdiag: str = ""; // Raw -o/-w spellings still fit the public PATH_MAX contract. The // directory fan-out, cold scratch, and persistent tool-record spellings // derived from them do not, and must be rejected before any producer. let longoutputbytes: []u8 = alloc([], os.PATH_MAX: u64)!; let longpathi: i32 = 0; for (longpathi < root.len) { append(longoutputbytes, root[longpathi]); longpathi += 1; }; append(longoutputbytes, '/': u8); for (longoutputbytes.len < os.PATH_MAX - 2) { append(longoutputbytes, 'o': u8); }; append(longoutputbytes, '/': u8); assert(longoutputbytes.len == os.PATH_MAX - 1); let longoutputdir: str = strings.frombytes(longoutputbytes); let longscratchbytes: []u8 = alloc([], os.PATH_MAX: u64)!; longpathi = 0; for (longpathi < root.len) { append(longscratchbytes, root[longpathi]); longpathi += 1; }; append(longscratchbytes, '/': u8); for (longscratchbytes.len < os.PATH_MAX - 1) { append(longscratchbytes, 's': u8); }; assert(longscratchbytes.len == os.PATH_MAX - 1); let longscratchout: str = strings.frombytes(longscratchbytes); let longworkbytes: []u8 = alloc([], os.PATH_MAX: u64)!; longpathi = 0; for (longpathi < root.len) { append(longworkbytes, root[longpathi]); longpathi += 1; }; for (longworkbytes.len < os.PATH_MAX - 8) { append(longworkbytes, '/': u8); let segmentbytes: i32 = 0; for (segmentbytes < 120 && longworkbytes.len < os.PATH_MAX - 8) { append(longworkbytes, 'w': u8); segmentbytes += 1; }; }; let longworkdir: str = strings.frombytes(longworkbytes); mkdirall(longworkdir); let commandworkpaths: []str = ["", ""]; let commandstage: i32 = 0; for (commandstage < commandstages.len) { writefile(commandcompilertraces[commandstage], ""); writefile(commandassemblertraces[commandstage], ""); writefile(commandlinkertraces[commandstage], ""); let commandenv: []str = alloc([], (commandbaseenv.len + 9): u64)!; let commandei: i32 = 0; for (commandei < commandbaseenv.len) { if (!strings.hasprefix(commandbaseenv[commandei], "WW_W6C=") && !strings.hasprefix(commandbaseenv[commandei], "WW_W6A=") && !strings.hasprefix(commandbaseenv[commandei], "WW_W6L=") && !strings.hasprefix(commandbaseenv[commandei], "WW_PATTERN_COMPILER_TRACE=") && !strings.hasprefix(commandbaseenv[commandei], "WW_PATTERN_ASSEMBLER_TRACE=") && !strings.hasprefix(commandbaseenv[commandei], "WW_PATTERN_LINKER_TRACE=") && !strings.hasprefix(commandbaseenv[commandei], "WW_PATTERN_REAL_COMPILER=") && !strings.hasprefix(commandbaseenv[commandei], "WW_PATTERN_REAL_ASSEMBLER=") && !strings.hasprefix(commandbaseenv[commandei], "WW_PATTERN_REAL_LINKER=")) { append(commandenv, commandbaseenv[commandei]); }; commandei += 1; }; append(commandenv, strings.concat("WW_W6C=", commandcompilerwrapper)); append(commandenv, strings.concat("WW_W6A=", commandassemblerwrapper)); append(commandenv, strings.concat("WW_W6L=", commandlinkerwrapper)); append(commandenv, strings.concat("WW_PATTERN_COMPILER_TRACE=", commandcompilertraces[commandstage])); append(commandenv, strings.concat("WW_PATTERN_ASSEMBLER_TRACE=", commandassemblertraces[commandstage])); append(commandenv, strings.concat("WW_PATTERN_LINKER_TRACE=", commandlinkertraces[commandstage])); append(commandenv, strings.concat("WW_PATTERN_REAL_COMPILER=", driver(commandcompilers[commandstage]))); append(commandenv, strings.concat("WW_PATTERN_REAL_ASSEMBLER=", driver(commandassemblers[commandstage]))); append(commandenv, strings.concat("WW_PATTERN_REAL_LINKER=", driver(commandlinkers[commandstage]))); let commandforward: []str = [driver(commandstages[commandstage]), "build", "-w", commandworks[commandstage], "-I", commands, "-L", strings.concat(repo(), "/out/lib"), "-lwcc", "-o", commandbins[commandstage], commandspec, commandaliasspec, commandspec, commandalpha]; let commandreverse: []str = [driver(commandstages[commandstage]), "build", "-w", commandreverseworks[commandstage], "-I", commands, "-L", strings.concat(repo(), "/out/lib"), "-lwcc", "-o", commandreverseoutputs[commandstage], commandalpha, commandspec, commandaliasspec, commandspec]; if (commandstage == 0) { runcommandenv(root, "tree-command-output-c", commandforward, commandenv, (60i64 * (time.second: i64)): time.duration, &outc); expectexit(&outc, 0); } else { runcommandenv(root, "tree-command-output-ww", commandforward, commandenv, (60i64 * (time.second: i64)): time.duration, &outw); expectexit(&outw, 0); }; let commandstdout: str; let commandstderr: str; if (commandstage == 0) { commandstdout = strings.dup(outc.stdout); commandstderr = strings.dup(outc.stderr); } else { commandstdout = strings.dup(outw.stdout); commandstderr = strings.dup(outw.stderr); }; let commandalphabin: str = strings.dup(readfile(strings.concat( commandbins[commandstage], "/cmd-alpha"))); let commandzetabin: str = strings.dup(readfile(strings.concat( commandbins[commandstage], "/cmd-zeta"))); let commandwork: str = strings.concat(commandworks[commandstage], "/"); commandworkpaths[commandstage] = strings.dup(commandwork); let commandctrace: str = readfile(commandcompilertraces[commandstage]); let commandatrace: str = readfile(commandassemblertraces[commandstage]); let commandltrace: str = readfile(commandlinkertraces[commandstage]); assert(occurrences(commandctrace, "\n") == 3); assert(occurrences(commandatrace, "\n") == 5); assert(occurrences(commandltrace, "\n") == 2); let commandalphacompile: str = linecontaining(commandctrace, strings.concat(commandalphaid, ".unit.new")); assert(occurrences(commandalphacompile, strings.concat( "<--import><", commandwork, "library.wwi.new>")) == 1); assert(!has(commandalphacompile, "cmd-zeta.wwi")); let alphaassembly: str = strings.concat("BEGIN<-o><", commandwork, commandalphaid, ".init.o.new><", commandwork, commandalphaid, ".init.s.new>"); let zetaassembly: str = strings.concat("BEGIN<-o><", commandwork, commandzetaid, ".init.o.new><", commandwork, commandzetaid, ".init.s.new>"); assert(same(linecontaining(commandatrace, strings.concat(commandalphaid, ".init.o.new")), alphaassembly)); assert(same(linecontaining(commandatrace, strings.concat(commandzetaid, ".init.o.new")), zetaassembly)); let commandalphalink: str = linecontaining(commandltrace, strings.concat("<-o><", commandbins[commandstage], "/cmd-alpha.new>")); let commandzetalink: str = linecontaining(commandltrace, strings.concat("<-o><", commandbins[commandstage], "/cmd-zeta.new>")); assert(occurrences(commandalphalink, strings.concat("<", commandwork, commandalphaid, ".a.new>")) == 1); assert(occurrences(commandalphalink, strings.concat("<", commandwork, "library.a.new>")) == 1); assert(!has(commandalphalink, strings.concat(commandzetaid, ".a"))); assert(!has(commandalphalink, ".wwi>")); assert(has(commandalphalink, strings.concat("<-L><", repo(), "/out/lib><-l>"))); assert(has(commandzetalink, strings.concat("<", commandwork, commandzetaid, ".a.new>"))); assert(!has(commandzetalink, "library.a")); assert(!has(commandzetalink, ".wwi>")); let normalizedcommandcompiler: str = normalizedtrace(commandctrace, commandwork, commandbins[commandstage]); let normalizedcommandassembler: str = normalizedtrace(commandatrace, commandwork, commandbins[commandstage]); let normalizedcommandlinker: str = normalizedtrace(commandltrace, commandwork, commandbins[commandstage]); if (commandstage == 0) { referencecommandcompiler = strings.dup(normalizedcommandcompiler); referencecommandassembler = strings.dup(normalizedcommandassembler); referencecommandlinker = strings.dup(normalizedcommandlinker); } else { assert(same(referencecommandcompiler, normalizedcommandcompiler)); assert(same(referencecommandassembler, normalizedcommandassembler)); assert(same(referencecommandlinker, normalizedcommandlinker)); }; // Reverse the identical request multiset into an independent cold // work/output root inside this stage. Canonical product ordering leaves // diagnostics, normalized tool argv, artifacts, and binaries fixed. rewritefile(commandcompilertraces[commandstage], ""); rewritefile(commandassemblertraces[commandstage], ""); rewritefile(commandlinkertraces[commandstage], ""); if (commandstage == 0) { runcommandenv(root, "tree-command-reverse-c", commandreverse, commandenv, (60i64 * (time.second: i64)): time.duration, &outc); expectexit(&outc, 0); assert(same(commandstdout, outc.stdout)); assert(same(commandstderr, outc.stderr)); } else { runcommandenv(root, "tree-command-reverse-ww", commandreverse, commandenv, (60i64 * (time.second: i64)): time.duration, &outw); expectexit(&outw, 0); assert(same(commandstdout, outw.stdout)); assert(same(commandstderr, outw.stderr)); }; let reversework: str = strings.concat( commandreverseworks[commandstage], "/"); let reversectrace: str = readfile(commandcompilertraces[commandstage]); let reverseatrace: str = readfile(commandassemblertraces[commandstage]); let reverseltrace: str = readfile(commandlinkertraces[commandstage]); assert(same(normalizedcommandcompiler, normalizedtrace(reversectrace, reversework, commandreversebins[commandstage]))); assert(same(normalizedcommandassembler, normalizedtrace(reverseatrace, reversework, commandreversebins[commandstage]))); assert(same(normalizedcommandlinker, normalizedtrace(reverseltrace, reversework, commandreversebins[commandstage]))); let reverseactions: []str = [commandalphaid, commandzetaid, "library"]; let reversesuffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"]; let rai: i32 = 0; for (rai < reverseactions.len) { let rsi: i32 = 0; for (rsi < reversesuffixes.len) { assert(same(readfile(strings.concat(commandwork, reverseactions[rai], reversesuffixes[rsi])), readfile(strings.concat(reversework, reverseactions[rai], reversesuffixes[rsi])))); rsi += 1; }; rai += 1; }; assert(same(commandalphabin, readfile(strings.concat( commandreversebins[commandstage], "/cmd-alpha")))); assert(same(commandzetabin, readfile(strings.concat( commandreversebins[commandstage], "/cmd-zeta")))); // Return to the forward store: a warm equivalent request invokes no // compiler or assembler before the direct-form reuse check below. rewritefile(commandcompilertraces[commandstage], ""); rewritefile(commandassemblertraces[commandstage], ""); rewritefile(commandlinkertraces[commandstage], ""); if (commandstage == 0) { runcommandenv(root, "tree-command-warm-c", commandforward, commandenv, (60i64 * (time.second: i64)): time.duration, &outc); expectexit(&outc, 0); } else { runcommandenv(root, "tree-command-warm-ww", commandforward, commandenv, (60i64 * (time.second: i64)): time.duration, &outw); expectexit(&outw, 0); }; assert(readfile(commandcompilertraces[commandstage]).len == 0); assert(readfile(commandassemblertraces[commandstage]).len == 0); let directbin: str = strings.concat(root, "/direct-", commandstages[commandstage]); let directav: []str = [driver(commandstages[commandstage]), "build", "-w", commandworks[commandstage], "-I", commands, "-L", strings.concat(repo(), "/out/lib"), "-lwcc", "-o", directbin, commandalpha]; if (commandstage == 0) { runcommandenv(root, "tree-command-direct-c", directav, commandenv, (60i64 * (time.second: i64)): time.duration, &outc); expectexit(&outc, 0); } else { runcommandenv(root, "tree-command-direct-ww", directav, commandenv, (60i64 * (time.second: i64)): time.duration, &outw); expectexit(&outw, 0); }; assert(readfile(commandcompilertraces[commandstage]).len == 0); assert(readfile(commandassemblertraces[commandstage]).len == 0); assert(same(commandalphabin, readfile(directbin))); rewritefile(commandcompilertraces[commandstage], ""); rewritefile(commandassemblertraces[commandstage], ""); rewritefile(commandlinkertraces[commandstage], ""); let commandasm: []str = [driver(commandstages[commandstage]), "build", "-S", "-w", commandasmworks[commandstage], "-I", commands, commandspec, commandaliasspec, commandalpha]; if (commandstage == 0) { runcommandenv(root, "tree-command-asm-c", commandasm, commandenv, (60i64 * (time.second: i64)): time.duration, &outc); expectexit(&outc, 0); } else { runcommandenv(root, "tree-command-asm-ww", commandasm, commandenv, (60i64 * (time.second: i64)): time.duration, &outw); expectexit(&outw, 0); }; let commandasmwork: str = strings.concat(commandasmworks[commandstage], "/"); let commandasmtrace: str = readfile(commandcompilertraces[commandstage]); assert(occurrences(commandasmtrace, "\n") == 3); assert(readfile(commandassemblertraces[commandstage]).len == 0); assert(readfile(commandlinkertraces[commandstage]).len == 0); assert(os.exists(strings.concat(commandasmwork, commandalphaid, ".s"))); assert(os.exists(strings.concat(commandasmwork, commandzetaid, ".s"))); assert(os.exists(strings.concat(commandasmwork, "library.s"))); let normalizedasmcompiler: str = normalizedtrace(commandasmtrace, commandasmwork, commandbins[commandstage]); if (commandstage == 0) { referenceasmcompiler = strings.dup(normalizedasmcompiler); } else { assert(same(referenceasmcompiler, normalizedasmcompiler)); }; if (commandstage == 0) { runcommandenv(root, "tree-command-asm-warm-c", commandasm, commandenv, (60i64 * (time.second: i64)): time.duration, &outc); expectexit(&outc, 0); } else { runcommandenv(root, "tree-command-asm-warm-ww", commandasm, commandenv, (60i64 * (time.second: i64)): time.duration, &outw); expectexit(&outw, 0); }; assert(same(commandasmtrace, readfile(commandcompilertraces[commandstage]))); rewritefile(commandcompilertraces[commandstage], ""); rewritefile(commandassemblertraces[commandstage], ""); rewritefile(commandlinkertraces[commandstage], ""); let commandasmnowork: []str = [driver(commandstages[commandstage]), "build", "-S", "-I", commands, commandspec]; if (commandstage == 0) { runcommandenv(root, "tree-command-asm-no-work-c", commandasmnowork, commandenv, time.second, &outc); expectexit(&outc, 2); referenceasmrejectdiag = strings.dup(outc.stderr); } else { runcommandenv(root, "tree-command-asm-no-work-ww", commandasmnowork, commandenv, time.second, &outw); expectexit(&outw, 2); assert(same(referenceasmrejectdiag, outw.stderr)); }; assert(has(referenceasmrejectdiag, "wwtest package: recursive -S needs -w\n")); assert(readfile(commandcompilertraces[commandstage]).len == 0); assert(readfile(commandassemblertraces[commandstage]).len == 0); assert(readfile(commandlinkertraces[commandstage]).len == 0); rewritefile(commandcompilertraces[commandstage], ""); rewritefile(commandassemblertraces[commandstage], ""); rewritefile(commandlinkertraces[commandstage], ""); let collisionav: []str = [driver(commandstages[commandstage]), "build", "-w", collisionworks[commandstage], "-o", collisionout, collisionspec]; if (commandstage == 0) { runcommandenv(root, "tree-collision-c", collisionav, commandenv, time.second, &outc); expectexit(&outc, 1); referencecollisiondiag = strings.dup(outc.stderr); } else { runcommandenv(root, "tree-collision-ww", collisionav, commandenv, time.second, &outw); expectexit(&outw, 1); assert(same(referencecollisiondiag, outw.stderr)); }; assert(readfile(commandcompilertraces[commandstage]).len == 0); assert(readfile(commandassemblertraces[commandstage]).len == 0); assert(readfile(commandlinkertraces[commandstage]).len == 0); assert(!os.exists(collisionworks[commandstage])); rewritefile(commandcompilertraces[commandstage], ""); rewritefile(commandassemblertraces[commandstage], ""); rewritefile(commandlinkertraces[commandstage], ""); let fanoutpathav: []str = [driver(commandstages[commandstage]), "build", "-o", longoutputdir, "-I", commands, commandspec]; if (commandstage == 0) { runcommandenv(root, "tree-fanout-path-c", fanoutpathav, commandenv, time.second, &outc); expectexit(&outc, 1); referencefanoutpathdiag = strings.dup(outc.stderr); } else { runcommandenv(root, "tree-fanout-path-ww", fanoutpathav, commandenv, time.second, &outw); expectexit(&outw, 1); assert(same(referencefanoutpathdiag, outw.stderr)); }; assert(has(referencefanoutpathdiag, "ww: command output path is too long\n")); assert(readfile(commandcompilertraces[commandstage]).len == 0); assert(readfile(commandassemblertraces[commandstage]).len == 0); assert(readfile(commandlinkertraces[commandstage]).len == 0); let scratchpathav: []str = [driver(commandstages[commandstage]), "build", "-o", longscratchout, "-I", commands, commandalpha]; if (commandstage == 0) { runcommandenv(root, "tree-scratch-path-c", scratchpathav, commandenv, time.second, &outc); expectexit(&outc, 1); referencescratchpathdiag = strings.dup(outc.stderr); } else { runcommandenv(root, "tree-scratch-path-ww", scratchpathav, commandenv, time.second, &outw); expectexit(&outw, 1); assert(same(referencescratchpathdiag, outw.stderr)); }; assert(has(referencescratchpathdiag, "ww: scratch path is too long\n")); assert(readfile(commandcompilertraces[commandstage]).len == 0); assert(readfile(commandassemblertraces[commandstage]).len == 0); assert(readfile(commandlinkertraces[commandstage]).len == 0); let workpathav: []str = [driver(commandstages[commandstage]), "build", "-w", longworkdir, "-I", commands, commandalpha]; if (commandstage == 0) { runcommandenv(root, "tree-work-path-c", workpathav, commandenv, time.second, &outc); expectexit(&outc, 1); referenceworkpathdiag = strings.dup(outc.stderr); } else { runcommandenv(root, "tree-work-path-ww", workpathav, commandenv, time.second, &outw); expectexit(&outw, 1); assert(same(referenceworkpathdiag, outw.stderr)); }; assert(has(referenceworkpathdiag, "ww: workdir path is too long\n")); assert(readfile(commandcompilertraces[commandstage]).len == 0); assert(readfile(commandassemblertraces[commandstage]).len == 0); assert(readfile(commandlinkertraces[commandstage]).len == 0); commandstage += 1; }; assert(has(referencecollisiondiag, "ww: multiple commands produce output basename \"same\"")); assert(!os.exists(strings.concat(collisionout, "/same"))); let commandactions: []str = [commandalphaid, commandzetaid, "library"]; let commandsuffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"]; let commandaction: i32 = 0; for (commandaction < commandactions.len) { let commandsuffix: i32 = 0; for (commandsuffix < commandsuffixes.len) { assert(same(readfile(strings.concat(commandworkpaths[0], commandactions[commandaction], commandsuffixes[commandsuffix])), readfile(strings.concat(commandworkpaths[1], commandactions[commandaction], commandsuffixes[commandsuffix])))); commandsuffix += 1; }; commandaction += 1; }; assert(os.exists(strings.concat(binc, "/cmd-alpha"))); assert(os.exists(strings.concat(binc, "/cmd-zeta"))); assert(os.exists(strings.concat(binw, "/cmd-alpha"))); assert(os.exists(strings.concat(binw, "/cmd-zeta"))); assert(!os.exists(strings.concat(binc, "/library"))); assert(!os.exists(strings.concat(binw, "/library"))); assert(same(readfile(strings.concat(binc, "/cmd-alpha")), readfile(strings.concat(binw, "/cmd-alpha")))); assert(same(readfile(strings.concat(binc, "/cmd-zeta")), readfile(strings.concat(binw, "/cmd-zeta")))); // `--` ends build option parsing and every following argument remains a // package selector. The coordinator receives and builds that direct root. let dashworks: []str = [strings.concat(root, "/dash-work-c"), strings.concat(root, "/dash-work-ww")]; let dashbins: []str = [strings.concat(root, "/dash-bin-c"), strings.concat(root, "/dash-bin-ww")]; let dashtrailworks: []str = [strings.concat(root, "/dash-trail-work-c"), strings.concat(root, "/dash-trail-work-ww")]; let dashtrailbins: []str = [strings.concat(root, "/dash-trail-bin-c"), strings.concat(root, "/dash-trail-bin-ww")]; let referencedashstdout: str = ""; let referencedashstderr: str = ""; let dashstage: i32 = 0; for (dashstage < commandstages.len) { let dashav: []str = [driver(commandstages[dashstage]), "build", "-w", dashworks[dashstage], "-I", commands, "-o", dashbins[dashstage], "--", dashselector]; if (dashstage == 0) { runcommanddir(root, "tree-dash-c", root, dashav, (30i64 * (time.second: i64)): time.duration, &outc); expectexit(&outc, 0); referencedashstdout = strings.dup(outc.stdout); referencedashstderr = strings.dup(outc.stderr); } else { runcommanddir(root, "tree-dash-ww", root, dashav, (30i64 * (time.second: i64)): time.duration, &outw); expectexit(&outw, 0); assert(same(referencedashstdout, outw.stdout)); assert(same(referencedashstderr, outw.stderr)); }; assert(os.exists(strings.concat(dashworks[dashstage], "/", commandalphaid, ".unit.ww"))); // Once the first positional has stopped flag parsing, a later `--` is // another package argument, not a second terminator. let dashtrailav: []str = [driver(commandstages[dashstage]), "build", "-w", dashtrailworks[dashstage], "-I", commands, "-o", dashtrailbins[dashstage], commandalpha, "--"]; if (dashstage == 0) { runcommand(root, "tree-dash-trailing-c", dashtrailav, time.second, &outc); expectexit(&outc, 1); assert(has(outc.stderr, "--: cannot canonicalize package directory\n")); } else { runcommand(root, "tree-dash-trailing-ww", dashtrailav, time.second, &outw); expectexit(&outw, 1); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); assert(has(outw.stderr, "--: cannot canonicalize package directory\n")); }; assert(!os.exists(dashtrailworks[dashstage])); assert(!os.exists(dashtrailbins[dashstage])); dashstage += 1; }; assert(same(readfile(dashbins[0]), readfile(dashbins[1]))); let commandrun: []str = [strings.concat(binc, "/cmd-alpha")]; runcommand(root, "tree-command-run", commandrun, time.second, &outc); expectexit(&outc, 0); let nomainpattern: str = strings.concat(commandlib, "/..."); let nomainc: []str = [driver("ww"), "build", "-o", binc, nomainpattern]; let nomainw: []str = [driver("ww_ww"), "build", "-o", binw, nomainpattern]; runcommand(root, "tree-no-main-c", nomainc, time.second, &outc); runcommand(root, "tree-no-main-ww", nomainw, time.second, &outw); expectexit(&outc, 1); expectexit(&outw, 1); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stderr, "ww: no main packages to build\n")); assert(has(seq, strings.concat("ok ", tree, " [rootpkg]\n"))); assert(has(seq, strings.concat("ok ", tree, "/alpha [alpha]\n"))); assert(has(seq, strings.concat("ok ", tree, "/app [app]\n"))); assert(has(seq, strings.concat("ok ", tree, "/beta/inner [inner]\n"))); assert(has(seq, strings.concat("ok ", tree, "/domain/app [app]\n"))); assert(has(seq, strings.concat("? ", tree, "/gamma [no test files]\n"))); assert(has(seq, strings.concat("? ", tree, "/vendor [no test files]\n"))); assert(has(seq, strings.concat("? ", tree, "/vendorx [no test files]\n"))); let rootreport: i32 = pos(seq, strings.concat("ok ", tree, " [rootpkg]\n")); let alphareport: i32 = pos(seq, strings.concat("ok ", tree, "/alpha [alpha]\n")); let appreport: i32 = pos(seq, strings.concat("ok ", tree, "/app [app]\n")); let innerreport: i32 = pos(seq, strings.concat("ok ", tree, "/beta/inner [inner]\n")); let domainreport: i32 = pos(seq, strings.concat("ok ", tree, "/domain/app [app]\n")); let gammareport: i32 = pos(seq, strings.concat("? ", tree, "/gamma [no test files]\n")); let vendorreport: i32 = pos(seq, strings.concat("? ", tree, "/vendor [no test files]\n")); let vendorxreport: i32 = pos(seq, strings.concat("? ", tree, "/vendorx [no test files]\n")); assert(rootreport >= 0 && rootreport < alphareport && alphareport < appreport && appreport < innerreport && innerreport < domainreport && domainreport < gammareport && gammareport < vendorreport && vendorreport < vendorxreport); assert(occurrences(seq, strings.concat(tree, " [")) == 1); assert(occurrences(seq, strings.concat("ok ", tree, "/alpha [alpha]\n")) == 1); assert(occurrences(seq, strings.concat("ok ", tree, "/app [app]\n")) == 1); assert(occurrences(seq, strings.concat("ok ", tree, "/beta/inner [inner]\n")) == 1); assert(occurrences(seq, strings.concat("ok ", tree, "/domain/app [app]\n")) == 1); assert(occurrences(seq, strings.concat("? ", tree, "/gamma [no test files]\n")) == 1); assert(occurrences(seq, strings.concat("? ", tree, "/vendor [no test files]\n")) == 1); assert(occurrences(seq, strings.concat("? ", tree, "/vendorx [no test files]\n")) == 1); assert(!has(seq, ".hidden")); assert(!has(seq, "_skip")); assert(!has(seq, "/testdata")); assert(!has(seq, "/vendor/bad")); assert(!has(seq, "/app/vendor/lib/math")); assert(!has(seq, outside)); let buildworkc: str = strings.concat(root, "/build-work-c"); let buildworkw: str = strings.concat(root, "/build-work-ww"); let buildc: []str = [driver("ww"), "build", "-w", buildworkc, "-I", tree, spec]; let buildw: []str = [driver("ww_ww"), "build", "-w", buildworkw, "-I", tree, spec]; runcommand(root, "tree-build-c", buildc, (30i64 * (time.second: i64)): time.duration, &outc); runcommand(root, "tree-build-ww", buildw, (30i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 0); expectexit(&outw, 0); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); let buildactions: []str = [localidentity(tree, "rootpkg"), "alpha", "app", "domain.app", "gamma", "vendor", "vendorx"]; let bai: i32 = 0; for (bai < buildactions.len) { let cunit: str = readfile(strings.concat(buildworkc, "/", buildactions[bai], ".unit.ww")); assert(same(cunit, readfile(strings.concat(buildworkw, "/", buildactions[bai], ".unit.ww")))); bai += 1; }; assert(!os.exists(strings.concat(buildworkc, "/beta.inner.unit.ww"))); assert(!os.exists(strings.concat(buildworkw, "/beta.inner.unit.ww"))); assert(!os.exists(strings.concat(buildworkc, "/alpha-internal-test.unit.ww"))); assert(!os.exists(strings.concat(buildworkw, "/alpha-internal-test.unit.ww"))); // Concurrent scheduling must not reorder the emitted byte stream. let j4: []str = [driver("ww"), "test", "-j", "4", "-I", tree, spec]; runcommand(root, "tree-j4", j4, (30i64 * (time.second: i64)): time.duration, &outc); expectexit(&outc, 0); assert(same(outc.stdout, seq)); let j0: []str = [driver("ww"), "test", "-j", "0", spec]; runcommand(root, "tree-j0", j0, time.second, &outc); expectexit(&outc, 2); assert(has(outc.stderr, "usage: wwtest package")); let overlapc: []str = [driver("ww"), "test", "-I", tree, strings.concat(tree, "/alpha"), spec, spec, strings.concat(alias, "/...")]; let overlapcreverse: []str = [driver("ww"), "test", "-I", tree, strings.concat(alias, "/..."), spec, spec, strings.concat(tree, "/alpha")]; let overlapw: []str = [driver("ww_ww"), "test", "-I", tree, strings.concat(alias, "/..."), spec, spec, strings.concat(tree, "/alpha")]; let overlapwreverse: []str = [driver("ww_ww"), "test", "-I", tree, strings.concat(tree, "/alpha"), spec, spec, strings.concat(alias, "/...")]; runcommand(root, "tree-overlap-c", overlapc, (30i64 * (time.second: i64)): time.duration, &outc); runcommand(root, "tree-overlap-ww", overlapw, (30i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 0); expectexit(&outw, 0); assert(same(outc.stdout, seq)); assert(same(outw.stdout, seq)); assert(same(outc.stderr, outw.stderr)); let overlapstderr: str = strings.dup(outc.stderr); runcommand(root, "tree-overlap-reverse-c", overlapcreverse, (30i64 * (time.second: i64)): time.duration, &outc); runcommand(root, "tree-overlap-reverse-ww", overlapwreverse, (30i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 0); expectexit(&outw, 0); assert(same(outc.stdout, seq)); assert(same(outw.stdout, seq)); assert(same(outc.stderr, overlapstderr)); assert(same(outw.stderr, overlapstderr)); // The first ellipsis need not be a path element. With cwd as the implicit // traversal root, matching uses cleaned relative candidates rather than an // accidental "./" prefix. let middlec: []str = [driver("ww"), "test", "-I", tree, "tr.../alpha"]; let middlew: []str = [driver("ww_ww"), "test", "-I", tree, "tr.../alpha"]; runcommanddir(root, "tree-middle-pattern-c", root, middlec, (30i64 * (time.second: i64)): time.duration, &outc); runcommanddir(root, "tree-middle-pattern-ww", root, middlew, (30i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 0); expectexit(&outw, 0); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stdout, strings.concat("ok ", tree, "/alpha [alpha]\n"))); assert(!has(outc.stdout, strings.concat("ok ", tree, " [rootpkg]\n"))); // The recursive exclusions are traversal rules, not bans on direct roots. let directc: []str = [driver("ww"), "test", "-I", tree, strings.concat(tree, "/.hidden"), strings.concat(tree, "/_skip"), strings.concat(tree, "/testdata/bad")]; let directw: []str = [driver("ww_ww"), "test", "-I", tree, strings.concat(tree, "/testdata/bad"), strings.concat(tree, "/_skip"), strings.concat(tree, "/.hidden")]; runcommand(root, "tree-direct-exclusions-c", directc, (30i64 * (time.second: i64)): time.duration, &outc); runcommand(root, "tree-direct-exclusions-ww", directw, (30i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 0); expectexit(&outw, 0); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stdout, strings.concat("ok ", tree, "/.hidden [hid]\n"))); assert(has(outc.stdout, strings.concat("ok ", tree, "/_skip [_skip]\n"))); assert(has(outc.stdout, strings.concat("ok ", tree, "/testdata/bad [bad]\n"))); // A generic wildcard selects a code-bearing terminal vendor directory but // cannot consume it. Rooting the pattern at vendor selects its descendants; // a second non-terminal vendor remains a barrier. let vendorspec: str = strings.concat(tree, "/vendor/..."); let vendorc: []str = [driver("ww"), "test", "-I", tree, vendorspec]; let vendorw: []str = [driver("ww_ww"), "test", "-I", tree, vendorspec]; runcommand(root, "tree-explicit-vendor-c", vendorc, (30i64 * (time.second: i64)): time.duration, &outc); runcommand(root, "tree-explicit-vendor-ww", vendorw, (30i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 0); expectexit(&outw, 0); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stdout, strings.concat("? ", tree, "/vendor [no test files]\n"))); assert(has(outc.stdout, strings.concat("ok ", tree, "/vendor/bad [bad]\n"))); assert(!has(outc.stdout, "/vendor/bad/vendor/deep")); let vendordirectc: []str = [driver("ww"), "test", "-I", tree, strings.concat(tree, "/app/vendor/lib/math")]; let vendordirectw: []str = [driver("ww_ww"), "test", "-I", tree, strings.concat(tree, "/app/vendor/lib/math")]; runcommand(root, "tree-direct-below-vendor-c", vendordirectc, (30i64 * (time.second: i64)): time.duration, &outc); runcommand(root, "tree-direct-below-vendor-ww", vendordirectw, (30i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 0); expectexit(&outw, 0); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stdout, strings.concat("? ", tree, "/app/vendor/lib/math [no test files]\n"))); let vendorroots: []str = [driver("ww"), "test", "-I", tree, strings.concat(tree, "/app/vendor/...")]; runcommand(root, "tree-pattern-below-vendor", vendorroots, (30i64 * (time.second: i64)): time.duration, &outc); expectexit(&outc, 0); assert(has(outc.stdout, strings.concat("? ", tree, "/app/vendor/lib/math [no test files]\n"))); // A child symlink is never traversed. The same symlink is followed when it // is itself the explicit traversal root, then canonical output names it once. let escapec: []str = [driver("ww"), "test", "-I", root, strings.concat(tree, "/escape/...")]; let escapew: []str = [driver("ww_ww"), "test", "-I", root, strings.concat(tree, "/escape/...")]; runcommand(root, "tree-explicit-symlink-root-c", escapec, (30i64 * (time.second: i64)): time.duration, &outc); runcommand(root, "tree-explicit-symlink-root-ww", escapew, (30i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 0); expectexit(&outw, 0); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stdout, strings.concat("ok ", outside, " [outside]\n"))); let oc: []str = [driver("ww"), "test", "-o", strings.concat(root, "/stem"), spec]; let ow: []str = [driver("ww_ww"), "test", "-o", strings.concat(root, "/stem"), spec]; runcommand(root, "tree-o-c", oc, (30i64 * (time.second: i64)): time.duration, &outc); runcommand(root, "tree-o-ww", ow, (30i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 1); expectexit(&outw, 1); assert(outc.stdout.len == 0 && outw.stdout.len == 0); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stderr, "with multiple packages, -o must refer to a directory or /dev/null")); assert(!os.exists(strings.concat(root, "/stem"))); // -w on a package target forwards to the coordinator: one shared // persistent driver workdir for the complete selected request, // reuse owned by the driver's content-identity contract. The // byte stream must match the plain run, cold and warm. let wdroot: str = strings.concat(root, "/wd"); assert(os.mkdir(wdroot, 448i32) == 0); let wdc: []str = [driver("ww"), "test", "-w", wdroot, "-I", tree, spec]; let wdw: []str = [driver("ww_ww"), "test", "-w", wdroot, "-I", tree, spec]; runcommand(root, "tree-w-c", wdc, (30i64 * (time.second: i64)): time.duration, &outc); let wdkey: str = wdroot; let expandedmath: str = "app.vendor.lib.math"; let expandedancestor: str = "domain.vendor.dep"; assert(os.exists(strings.concat(wdkey, "/", expandedmath, ".unit.ww"))); assert(os.exists(strings.concat(wdkey, "/", expandedancestor, ".unit.ww"))); assert(!os.exists(strings.concat(wdkey, "/math.unit.ww"))); let appunit: str = readfile(strings.concat(wdkey, "/app-internal-test.unit.ww")); assert(has(appunit, strings.concat("//ww:import-map lib.math ", expandedmath, " "))); assert(has(readfile(strings.concat(wdkey, "/", expandedmath, ".unit.ww")), strings.concat("//ww:module-reset ", expandedmath, "\n"))); let ancestorunit: str = readfile(strings.concat(wdkey, "/domain.app-internal-test.unit.ww")); assert(has(ancestorunit, strings.concat("//ww:import-map dep ", expandedancestor, " "))); assert(has(readfile(strings.concat(wdkey, "/", expandedancestor, ".unit.ww")), strings.concat("//ww:module-reset ", expandedancestor, "\n"))); let alphaunit: str = readfile(strings.concat(wdkey, "/alpha.unit.ww")); assert(pos(alphaunit, "export fn value()") < pos(alphaunit, "export fn linked()")); assert(same(readfile(strings.concat(wdkey, "/.wwtool.stamp")), "ww workdir fmt 19 mode test asm 0\n")); let directorymains: []str = [strings.concat(localidentity(tree, "rootpkg"), "-test-main"), "alpha-test-main", "app-test-main", "beta.inner-test-main", "domain.app-test-main"]; let dmi: i32 = 0; for (dmi < directorymains.len) { assert(os.exists(strings.concat(wdkey, "/", directorymains[dmi], ".unit.ww"))); dmi += 1; }; let notestactions: []str = ["gamma", "vendor", "vendorx"]; let nti: i32 = 0; for (nti < notestactions.len) { assert(os.exists(strings.concat(wdkey, "/", notestactions[nti], ".unit.ww"))); assert(!os.exists(strings.concat(wdkey, "/", notestactions[nti], "-test-main.unit.ww"))); nti += 1; }; runcommand(root, "tree-w-ww", wdw, (30i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 0); expectexit(&outw, 0); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stdout, seq)); runcommand(root, "tree-w-warm", wdc, (30i64 * (time.second: i64)): time.duration, &outc); expectexit(&outc, 0); assert(same(outc.stdout, seq)); // The retained executable is separate from the persistent action store, so // -c can suppress execution while reusing the same complete graph. let wcbin: str = strings.concat(root, "/tree-wc-c.test"); let wwbin: str = strings.concat(root, "/tree-wc-ww.test"); let wcc: []str = [driver("ww"), "test", "-c", "-w", wdroot, "-o", wcbin, "-I", tree, strings.concat(tree, "/alpha")]; let wcw: []str = [driver("ww_ww"), "test", "-c", "-w", wdroot, "-o", wwbin, "-I", tree, strings.concat(tree, "/alpha")]; runcommand(root, "tree-wc-c", wcc, (30i64 * (time.second: i64)): time.duration, &outc); runcommand(root, "tree-wc-ww", wcw, (30i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 0); expectexit(&outw, 0); assert(outc.stdout.len == 0 && outc.stderr.len == 0); assert(outw.stdout.len == 0 && outw.stderr.len == 0); assert(os.exists(wcbin) && os.exists(wwbin)); assert(same(readfile(wcbin), readfile(wwbin))); assert(same(outc.stderr, outw.stderr)); clean(wcbin); clean(wwbin); let bare: str = strings.concat(root, "/bare"); let barez: str = strings.concat(root, "/zz-bare"); assert(os.mkdir(bare, 448i32) == 0); assert(os.mkdir(strings.concat(bare, "/sub"), 448i32) == 0); assert(os.mkdir(barez, 448i32) == 0); let barec: []str = [driver("ww"), "test", strings.concat(bare, "/...")]; let barew: []str = [driver("ww_ww"), "test", strings.concat(bare, "/...")]; runcommand(root, "tree-bare-c", barec, (10i64 * (time.second: i64)): time.duration, &outc); runcommand(root, "tree-bare-ww", barew, (10i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 1); expectexit(&outw, 1); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stderr, strings.concat("ww: warning: \"", bare, "/...\" matched no packages\n"))); assert(has(outc.stderr, "ww test: no packages to test\n")); let baredirectc: []str = [driver("ww"), "test", bare]; let baredirectw: []str = [driver("ww_ww"), "test", bare]; runcommand(root, "tree-bare-direct-c", baredirectc, time.second, &outc); runcommand(root, "tree-bare-direct-ww", baredirectw, time.second, &outw); expectexit(&outc, 1); expectexit(&outw, 1); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stderr, "directory contains no WW package sources\n")); let quotedpattern: str = strings.concat(root, "/no\"..."); let quotedc: []str = [driver("ww"), "test", quotedpattern]; let quotedw: []str = [driver("ww_ww"), "test", quotedpattern]; runcommand(root, "tree-quoted-unmatched-c", quotedc, time.second, &outc); runcommand(root, "tree-quoted-unmatched-ww", quotedw, time.second, &outw); expectexit(&outc, 1); expectexit(&outw, 1); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stderr, strings.concat("ww: warning: \"", root, "/no\\\"...\" matched no packages\n"))); // strconv.Quote escapes valid but non-printing Unicode, not only malformed // bytes and ASCII controls. U+2028 is a stable discriminator. let nonprintingbytes: []u8 = alloc([], (root.len + 8): u64)!; let qi: i32 = 0; for (qi < root.len) { append(nonprintingbytes, root[qi]); qi += 1; }; append(nonprintingbytes, '/': u8); append(nonprintingbytes, 'n': u8); append(nonprintingbytes, 0xe2u8); append(nonprintingbytes, 0x80u8); append(nonprintingbytes, 0xa8u8); append(nonprintingbytes, '.': u8); append(nonprintingbytes, '.': u8); append(nonprintingbytes, '.': u8); let nonprintingpattern: str = strings.frombytes(nonprintingbytes); let nonprintingc: []str = [driver("ww"), "test", nonprintingpattern]; let nonprintingw: []str = [driver("ww_ww"), "test", nonprintingpattern]; runcommand(root, "tree-nonprinting-unmatched-c", nonprintingc, time.second, &outc); runcommand(root, "tree-nonprinting-unmatched-ww", nonprintingw, time.second, &outw); expectexit(&outc, 1); expectexit(&outw, 1); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stderr, "\\u2028...\" matched no packages\n")); let mixedc: []str = [driver("ww"), "test", "-I", tree, strings.concat(barez, "/..."), spec, strings.concat(bare, "/...")]; let mixedw: []str = [driver("ww_ww"), "test", "-I", tree, strings.concat(bare, "/..."), spec, strings.concat(barez, "/...")]; runcommand(root, "tree-unmatched-order-c", mixedc, (30i64 * (time.second: i64)): time.duration, &outc); runcommand(root, "tree-unmatched-order-ww", mixedw, (30i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 0); expectexit(&outw, 0); assert(same(outc.stdout, seq)); assert(same(outw.stdout, seq)); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stderr, strings.concat("ww: warning: \"", bare, "/...\" matched no packages\n"))); let barewarning: str = strings.concat("ww: warning: \"", bare, "/...\" matched no packages\n"); let barezwarning: str = strings.concat("ww: warning: \"", barez, "/...\" matched no packages\n"); assert(pos(outc.stderr, barewarning) < pos(outc.stderr, barezwarning)); // Pinned pkgpattern rejects invalid UTF-8 patterns before regexp // construction, so they match nothing and receive the ordinary quoted // warning. Candidate names still use regexp's DecodeRune behavior. let invalidpatternbytes: []u8 = alloc([], (root.len + 6): u64)!; let mpi: i32 = 0; for (mpi < root.len) { append(invalidpatternbytes, root[mpi]); mpi += 1; }; append(invalidpatternbytes, '/': u8); append(invalidpatternbytes, '.': u8); append(invalidpatternbytes, '.': u8); append(invalidpatternbytes, '.': u8); append(invalidpatternbytes, 0xffu8); let invalidpattern: str = strings.frombytes(invalidpatternbytes); let invalidpatternc: []str = [driver("ww"), "test", invalidpattern]; let invalidpatternw: []str = [driver("ww_ww"), "test", invalidpattern]; runcommand(root, "tree-invalid-pattern-c", invalidpatternc, time.second, &outc); runcommand(root, "tree-invalid-pattern-ww", invalidpatternw, time.second, &outw); expectexit(&outc, 1); expectexit(&outw, 1); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stderr, "...\\xff\" matched no packages\n")); // U+FFFD in a valid pattern matches a malformed candidate filename byte as // regexp does, while the wildcard below does not consume a newline. let utfbase: str = strings.concat(root, "/utf-names"); assert(os.mkdir(utfbase, 448i32) == 0); let invalidnamebytes: []u8 = alloc([], (utfbase.len + 2): u64)!; mpi = 0; for (mpi < utfbase.len) { append(invalidnamebytes, utfbase[mpi]); mpi += 1; }; append(invalidnamebytes, '/': u8); append(invalidnamebytes, 0xffu8); let invalidname: str = strings.frombytes(invalidnamebytes); assert(os.mkdir(invalidname, 448i32) == 0); writefile(strings.concat(invalidname, "/case_test.ww"), strings.concat( "package invalidname;\n@test fn replacement_match() void = {", " assert(true); };\n")); let replacementbytes: []u8 = alloc([], (utfbase.len + 8): u64)!; mpi = 0; for (mpi < utfbase.len) { append(replacementbytes, utfbase[mpi]); mpi += 1; }; append(replacementbytes, '/': u8); append(replacementbytes, 0xefu8); append(replacementbytes, 0xbfu8); append(replacementbytes, 0xbdu8); append(replacementbytes, '.': u8); append(replacementbytes, '.': u8); append(replacementbytes, '.': u8); let replacementpattern: str = strings.frombytes(replacementbytes); let replacementc: []str = [driver("ww"), "test", replacementpattern]; let replacementw: []str = [driver("ww_ww"), "test", replacementpattern]; runcommand(root, "tree-replacement-pattern-c", replacementc, (30i64 * (time.second: i64)): time.duration, &outc); runcommand(root, "tree-replacement-pattern-ww", replacementw, (30i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 0); expectexit(&outw, 0); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stdout, "invalidname.replacement_match ... ok\n")); let newlinebase: str = strings.concat(root, "/newline-names"); assert(os.mkdir(newlinebase, 448i32) == 0); let newlinedir: str = strings.concat(newlinebase, "/line\nbreak"); assert(os.mkdir(newlinedir, 448i32) == 0); writefile(strings.concat(newlinedir, "/case_test.ww"), "package newline;\n@test fn must_not_match() void = { assert(false); };\n"); let newlinepattern: str = strings.concat(newlinebase, "/line...break"); let newlinec: []str = [driver("ww"), "test", newlinepattern]; let newlinew: []str = [driver("ww_ww"), "test", newlinepattern]; runcommand(root, "tree-newline-pattern-c", newlinec, time.second, &outc); runcommand(root, "tree-newline-pattern-ww", newlinew, time.second, &outw); expectexit(&outc, 1); expectexit(&outw, 1); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stderr, "line...break\" matched no packages\n")); let barebuildc: []str = [driver("ww"), "build", strings.concat(bare, "/...")]; let barebuildw: []str = [driver("ww_ww"), "build", strings.concat(bare, "/...")]; runcommand(root, "tree-empty-build-c", barebuildc, time.second, &outc); runcommand(root, "tree-empty-build-ww", barebuildw, time.second, &outw); expectexit(&outc, 0); expectexit(&outw, 0); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stderr, "matched no packages\n")); // A wildcard recognizes a test-only directory, so it is not unmatched; // build then omits it while test keeps its isolated variant. Direct build // remains an explicit unusable-package error. let testonly: str = strings.concat(tree, "/beta/..."); let testonlyc: []str = [driver("ww"), "build", "-I", tree, testonly]; let testonlyw: []str = [driver("ww_ww"), "build", "-I", tree, testonly]; runcommand(root, "tree-test-only-build-c", testonlyc, time.second, &outc); runcommand(root, "tree-test-only-build-ww", testonlyw, time.second, &outw); expectexit(&outc, 0); expectexit(&outw, 0); assert(outc.stdout.len == 0 && outc.stderr.len == 0); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); let testdirectc: []str = [driver("ww"), "build", strings.concat(tree, "/beta/inner")]; let testdirectw: []str = [driver("ww_ww"), "build", strings.concat(tree, "/beta/inner")]; runcommand(root, "tree-test-only-direct-c", testdirectc, time.second, &outc); runcommand(root, "tree-test-only-direct-ww", testdirectw, time.second, &outw); expectexit(&outc, 1); expectexit(&outw, 1); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stderr, "directory contains no WW package sources\n")); let malformed: str = strings.concat(root, "/malformed"); let malformedgood: str = strings.concat(malformed, "/good"); let malformedbad: str = strings.concat(malformed, "/bad"); assert(os.mkdir(malformed, 448i32) == 0); assert(os.mkdir(malformedgood, 448i32) == 0); assert(os.mkdir(malformedbad, 448i32) == 0); writefile(strings.concat(malformedgood, "/good.ww"), "package good;\nexport fn value() int = { return 1; };\n"); let loop: str = strings.concat(root, "/loop"); let loopav: []str = ["/bin/ln", "-s", loop, loop]; runcommand(root, "recursive-root-loop", loopav, time.second, &linkout); expectexit(&linkout, 0); let rejecttrace: str = strings.concat(root, "/pattern-reject.trace"); let rejectcompiler: str = strings.concat(root, "/pattern-reject-w6c.sh"); writefile(rejecttrace, ""); writeexecutable(rejectcompiler, strings.concat( "#!/bin/sh\nprintf 'compiler\\n' >> \"$WW_PATTERN_REJECT_TRACE\"\n", "exit 99\n")); let baseenv: []str = os.getenvs(); let rejectenv: []str = alloc([], (baseenv.len + 2): u64)!; let ei: i32 = 0; for (ei < baseenv.len) { if (!strings.hasprefix(baseenv[ei], "WW_W6C=") && !strings.hasprefix(baseenv[ei], "WW_PATTERN_REJECT_TRACE=")) { append(rejectenv, baseenv[ei]); }; ei += 1; }; append(rejectenv, strings.concat("WW_W6C=", rejectcompiler)); append(rejectenv, strings.concat("WW_PATTERN_REJECT_TRACE=", rejecttrace)); let rejectspec: str = strings.concat(malformed, "/..."); let warmworkc: str = strings.concat(root, "/warm-reject-work-c"); let warmworkw: str = strings.concat(root, "/warm-reject-work-ww"); let warmc: []str = [driver("ww"), "build", "-w", warmworkc, "-I", malformed, rejectspec]; let warmw: []str = [driver("ww_ww"), "build", "-w", warmworkw, "-I", malformed, rejectspec]; // Seed genuine committed package, voucher, and tool state before making a // second selected directory malformed. runcommand(root, "tree-malformed-seed-c", warmc, (30i64 * (time.second: i64)): time.duration, &outc); runcommand(root, "tree-malformed-seed-ww", warmw, (30i64 * (time.second: i64)): time.duration, &outw); expectexit(&outc, 0); expectexit(&outw, 0); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); let warmsuffixes: []str = ["/good.unit.ww", "/good.wwi", "/good.s", "/good.o", "/good.a", "/.wwtool.stamp", "/.wwtool.ww", "/.wwtool.w6c", "/.wwtool.w6a"]; let warmcstate: []str = alloc([], warmsuffixes.len: u64)!; let warmwstate: []str = alloc([], warmsuffixes.len: u64)!; let wi: i32 = 0; for (wi < warmsuffixes.len) { append(warmcstate, readfile(strings.concat(warmworkc, warmsuffixes[wi]))); append(warmwstate, readfile(strings.concat(warmworkw, warmsuffixes[wi]))); wi += 1; }; writefile(strings.concat(warmworkc, "/marker"), "c-warm\n"); writefile(strings.concat(warmworkw, "/marker"), "ww-warm\n"); writefile(strings.concat(malformedbad, "/bad.ww"), "this selected source has no package clause\n"); let rejectworkc: str = strings.concat(root, "/cold-reject-work-c"); let rejectworkw: str = strings.concat(root, "/cold-reject-work-ww"); let rejectc: []str = [driver("ww"), "build", "-w", rejectworkc, "-I", malformed, rejectspec]; let rejectw: []str = [driver("ww_ww"), "build", "-w", rejectworkw, "-I", malformed, rejectspec]; runcommandenv(root, "tree-malformed-c", rejectc, rejectenv, time.second, &outc); runcommandenv(root, "tree-malformed-ww", rejectw, rejectenv, time.second, &outw); expectexit(&outc, 1); expectexit(&outw, 1); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stderr, "invalid or missing package clause\n")); assert(readfile(rejecttrace).len == 0); assert(!os.exists(rejectworkc) && !os.exists(rejectworkw)); // Warm rejection preserves all previously committed package, voucher, // tool, and caller-owned marker bytes while still reaching no producer. runcommandenv(root, "tree-malformed-warm-c", warmc, rejectenv, time.second, &outc); runcommandenv(root, "tree-malformed-warm-ww", warmw, rejectenv, time.second, &outw); expectexit(&outc, 1); expectexit(&outw, 1); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stderr, "invalid or missing package clause\n")); assert(readfile(rejecttrace).len == 0); assert(same(readfile(strings.concat(warmworkc, "/marker")), "c-warm\n")); assert(same(readfile(strings.concat(warmworkw, "/marker")), "ww-warm\n")); wi = 0; for (wi < warmsuffixes.len) { assert(same(warmcstate[wi], readfile(strings.concat(warmworkc, warmsuffixes[wi])))); assert(same(warmwstate[wi], readfile(strings.concat(warmworkw, warmsuffixes[wi])))); wi += 1; }; // A valid package clause that fails during source-import graph loading is // also rejected before private work/output directories are materialized. let graphbad: str = strings.concat(root, "/graph-bad"); let graphapp: str = strings.concat(graphbad, "/app"); assert(os.mkdir(graphbad, 448i32) == 0); assert(os.mkdir(graphapp, 448i32) == 0); writefile(strings.concat(graphapp, "/main.ww"), "package main;\nimport nowhere;\nfn main() void = { };\n"); let graphspec: str = strings.concat(graphbad, "/..."); let graphworkc: str = strings.concat(root, "/graph-work-c"); let graphworkw: str = strings.concat(root, "/graph-work-ww"); let graphoutc: str = strings.concat(root, "/graph-out-c/"); let graphoutw: str = strings.concat(root, "/graph-out-ww/"); let graphc: []str = [driver("ww"), "build", "-w", graphworkc, "-o", graphoutc, "-I", graphbad, graphspec]; let graphw: []str = [driver("ww_ww"), "build", "-w", graphworkw, "-o", graphoutw, "-I", graphbad, graphspec]; runcommandenv(root, "tree-graph-reject-c", graphc, rejectenv, time.second, &outc); runcommandenv(root, "tree-graph-reject-ww", graphw, rejectenv, time.second, &outw); expectexit(&outc, 1); expectexit(&outw, 1); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stderr, "cannot find package nowhere\n")); assert(readfile(rejecttrace).len == 0); assert(!os.exists(graphworkc) && !os.exists(graphworkw)); assert(!os.exists(graphoutc) && !os.exists(graphoutw)); // If output creation succeeds but a later workdir prefix is unusable, the // driver reclaims only the output prefixes it just minted. Both stages make // the same pre-producer failure and leave the caller tree unchanged. let setupblock: str = strings.concat(root, "/setup-block"); let setupwork: str = strings.concat(setupblock, "/child"); let setupoutroot: str = strings.concat(root, "/setup-output"); let setuppreserved: str = strings.concat(root, "/setup-preserved"); assert(os.mkdir(setuppreserved, 448i32) == 0); let setupout: str = strings.concat(setupoutroot, "/../setup-preserved/nested/"); writefile(setupblock, "not a directory\n"); let setupc: []str = [driver("ww"), "build", "-w", setupwork, "-o", setupout, "-I", commands, commandspec]; let setupw: []str = [driver("ww_ww"), "build", "-w", setupwork, "-o", setupout, "-I", commands, commandspec]; runcommandenv(root, "tree-setup-rollback-c", setupc, rejectenv, time.second, &outc); expectexit(&outc, 1); assert(has(outc.stderr, strings.concat("ww: workdir ", setupwork, " is not a directory\n"))); assert(readfile(rejecttrace).len == 0); assert(!os.exists(setupoutroot)); assert(os.exists(setuppreserved)); assert(!os.exists(strings.concat(setuppreserved, "/nested"))); assert(!os.exists(setupwork)); assert(same(readfile(setupblock), "not a directory\n")); runcommandenv(root, "tree-setup-rollback-ww", setupw, rejectenv, time.second, &outw); expectexit(&outw, 1); assert(same(outc.stderr, outw.stderr)); assert(readfile(rejecttrace).len == 0); assert(!os.exists(setupoutroot)); assert(os.exists(setuppreserved)); assert(!os.exists(strings.concat(setuppreserved, "/nested"))); assert(!os.exists(setupwork)); assert(same(readfile(setupblock), "not a directory\n")); let loopc: []str = [driver("ww"), "test", strings.concat(loop, "/...")]; let loopw: []str = [driver("ww_ww"), "test", strings.concat(loop, "/...")]; runcommandenv(root, "tree-loop-c", loopc, rejectenv, time.second, &outc); runcommandenv(root, "tree-loop-ww", loopw, rejectenv, time.second, &outw); expectexit(&outc, 1); expectexit(&outw, 1); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stderr, "cannot canonicalize package directory\n")); assert(readfile(rejecttrace).len == 0); clean(root); }; @test fn persistent_package_request_workdir_is_stable() void = { let root: str = fresh(); let tree: str = deeppunctuationpath(root, "request tree"); assert(tree.len > 255); let adir: str = strings.concat(tree, "/a"); let bdir: str = strings.concat(tree, "/b"); let a: str = strings.concat(adir, "/foo"); let b: str = strings.concat(bdir, "/foo"); let workc: str = strings.concat(root, "/work-c"); let workww: str = strings.concat(root, "/work-ww"); mkdirall(tree); assert(os.mkdir(adir, 448i32) == 0); assert(os.mkdir(bdir, 448i32) == 0); assert(os.mkdir(a, 448i32) == 0); assert(os.mkdir(b, 448i32) == 0); assert(os.mkdir(workc, 448i32) == 0); assert(os.mkdir(workww, 448i32) == 0); writefile(strings.concat(a, "/foo.ww"), "package foo;\nfn value() i32 = { return 1; };\n"); writefile(strings.concat(a, "/foo_test.ww"), "package foo;\n@test fn first() void = { assert(value() == 1); };\n"); writefile(strings.concat(b, "/foo.ww"), "package foo;\nfn value() i32 = { return 2; };\n"); writefile(strings.concat(b, "/foo_test.ww"), "package foo;\n@test fn second() void = { assert(value() == 2); };\n"); let av: []str = [driver("ww"), "test", "-j", "2", "-w", workc, "-I", tree, strings.concat(tree, "/...")]; let out: commandout; runcommand(root, "workkey-cold", av, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let cold: str = strings.dup(out.stdout); assert(has(cold, strings.concat("ok ", a, " [foo]\n"))); assert(has(cold, strings.concat("ok ", b, " [foo]\n"))); let ckey: str = workc; assert(os.exists(ckey)); let aaction: str = "a.foo-internal-test"; let baction: str = "b.foo-internal-test"; assert(os.exists(strings.concat(ckey, "/", aaction, ".unit.ww"))); assert(os.exists(strings.concat(ckey, "/", baction, ".unit.ww"))); let aunit: str = readfile(strings.concat(ckey, "/", aaction, ".unit.ww")); let bunit: str = readfile(strings.concat(ckey, "/", baction, ".unit.ww")); let awwi: str = readfile(strings.concat(ckey, "/", aaction, ".wwi")); let bwwi: str = readfile(strings.concat(ckey, "/", baction, ".wwi")); let aa: str = readfile(strings.concat(ckey, "/", aaction, ".a")); let ba: str = readfile(strings.concat(ckey, "/", baction, ".a")); assert(has(aunit, "return 1") && !has(aunit, "return 2")); assert(has(bunit, "return 2") && !has(bunit, "return 1")); runcommand(root, "workkey-warm", av, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(cold, out.stdout)); let wwav: []str = [driver("ww_ww"), "test", "-j", "1", "-w", workww, "-I", tree, strings.concat(tree, "/...")]; runcommand(root, "workkey-ww", wwav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(cold, out.stdout)); let wwkey: str = workww; assert(same(aunit, readfile(strings.concat(wwkey, "/", aaction, ".unit.ww")))); assert(same(bunit, readfile(strings.concat(wwkey, "/", baction, ".unit.ww")))); assert(same(awwi, readfile(strings.concat(wwkey, "/", aaction, ".wwi")))); assert(same(bwwi, readfile(strings.concat(wwkey, "/", baction, ".wwi")))); assert(same(aa, readfile(strings.concat(wwkey, "/", aaction, ".a")))); assert(same(ba, readfile(strings.concat(wwkey, "/", baction, ".a")))); // The shared semantic-action store and warm action set are unchanged by an // equivalent recursive-root spelling. let equivalentav: []str = [driver("ww_ww"), "test", "-j", "2", "-w", workww, "-I", tree, strings.concat(tree, "/./...")]; runcommand(root, "workkey-equivalent", equivalentav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, strings.concat("ok ", a, " [foo]\n"))); assert(has(out.stdout, strings.concat("ok ", b, " [foo]\n"))); assert(os.exists(wwkey)); assert(same(aunit, readfile(strings.concat(wwkey, "/", aaction, ".unit.ww")))); assert(same(bunit, readfile(strings.concat(wwkey, "/", baction, ".unit.ww")))); assert(same(awwi, readfile(strings.concat(wwkey, "/", aaction, ".wwi")))); assert(same(bwwi, readfile(strings.concat(wwkey, "/", baction, ".wwi")))); assert(same(aa, readfile(strings.concat(wwkey, "/", aaction, ".a")))); assert(same(ba, readfile(strings.concat(wwkey, "/", baction, ".a")))); // Outside every active import root, equal declaration leaves remain two // deterministic command-local identities derived from canonical dirs. let localleft: str = deeppunctuationpath(root, "local left"); let localright: str = deeppunctuationpath(root, "local right"); mkdirall(localleft); mkdirall(localright); writefile(strings.concat(localleft, "/foo.ww"), "package foo;\nexport fn value() i32 = { return 11; };\n"); writefile(strings.concat(localright, "/foo.ww"), "package foo;\nexport fn value() i32 = { return 22; };\n"); let localids: []str = [localidentity(localleft, "foo"), localidentity(localright, "foo")]; assert(!same(localids[0], localids[1])); assert(localids[0].len > 255 && localids[1].len > 255); let localstorage: []str = [packagestoragekey(localids[0], localleft, 0, 0), packagestoragekey(localids[1], localright, 0, 0)]; assert(!same(localstorage[0], localstorage[1])); let stages: []str = ["ww", "ww_ww"]; let localrefs: []str = ["", "", "", "", "", ""]; let si: i32 = 0; for (si < stages.len) { let leftout: str = strings.concat(root, "/local-", stages[si], "-left.test"); let rightout: str = strings.concat(root, "/local-", stages[si], "-right.test"); let lav: []str = [driver(stages[si]), "test", "-c", "--ww-package-build", "--ww-package-test", "build", "foo", "foo", "-", "-", localleft, leftout, "-", strings.concat(root, "/local-", stages[si], "-left.status"), "--ww-package-test", "build", "foo", "foo", "-", "-", localright, rightout, "-", strings.concat(root, "/local-", stages[si], "-right.status"), localleft]; runcommand(root, strings.concat("local-command-union-", stages[si]), lav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let localwork: str = strings.concat(leftout, ".sepwork/"); let li: i32 = 0; for (li < localids.len) { let suffixes: []str = [".unit.ww", ".wwi", ".a"]; let xi: i32 = 0; for (xi < suffixes.len) { let artifact: str = readfile(strings.concat(localwork, localstorage[li], suffixes[xi])); if (xi == 0) { assert(has(artifact, strings.concat("//ww:module-reset ", localids[li], "\n"))); } else { if (xi == 1) { assert(strings.hasprefix(artifact, strings.concat( "//ww:module ", localids[li], "\n"))); }; }; let refi: i32 = li * 3 + xi; if (si == 0) { localrefs[refi] = strings.dup(artifact); } else { assert(same(localrefs[refi], artifact)); }; xi += 1; }; li += 1; }; si += 1; }; clean(root); }; fn runtimepath(relative: str) str = { return strings.concat(repo(), "/test/package/runtime/", relative); }; // `documentation` is a source-loader reservation, not a package identity. // Keep this matrix here rather than splitting it between command tests: both // drivers and the shared package coordinator must make the same decision // before a body, import edge, action, or public artifact exists. fn documentationrecord(out: *commandout, code: i32, records: *[]str) void = { expectexit(out, code); append(*records, strings.dup(out.stdout)); append(*records, strings.dup(out.stderr)); }; fn documentationrecordssame(a: []str, b: []str) void = { assert(a.len == b.len); let i: i32 = 0; for (i < a.len) { assert(same(a[i], b[i])); i += 1; }; }; fn documentationclean(root: str) void = { assert(!directoryhasnew(root) && !directoryhasfragment(root, ".wwtxn.") && !directoryhasfragment(root, ".install") && !directoryhasfragment(root, ".sepwork") && !wrongsuffixpathfragment(root, ".new")); }; @test fn package_documentation_is_omitted_before_body_or_actions() void = { let root: str = fresh(); let docs: str = strings.concat(root, "/docs"); let controls: str = strings.concat(root, "/controls"); let bomdir: str = strings.concat(root, "/bom"); let trivia: str = strings.concat(root, "/trivia"); let rawidir: str = strings.concat(root, "/raw-i"); let rawitree: str = strings.concat(root, "/raw-i-tree"); let literal: str = strings.concat(root, "/literal"); let include: str = strings.concat(root, "/include"); let logicaldir: str = strings.concat(include, "/logic"); let logicaltestdir: str = strings.concat(include, "/logicaltest"); let tree: str = strings.concat(root, "/tree"); let doconlytree: str = strings.concat(root, "/doconlytree"); let doconlydocs: str = strings.concat(doconlytree, "/docs"); let treedocs: str = strings.concat(tree, "/docs"); let treemain: str = strings.concat(tree, "/main"); let mixed: str = strings.concat(root, "/mixed"); let control: str = strings.concat(root, "/control"); let warm: str = strings.concat(root, "/warm"); let exact: str = strings.concat(root, "/exact"); let importer: str = strings.concat(root, "/importer"); let importerone: str = strings.concat(root, "/importer-one"); let docdep: str = strings.concat(include, "/docdep"); mkdirall(docs); mkdirall(controls); mkdirall(bomdir); mkdirall(trivia); mkdirall(rawidir); mkdirall(rawitree); mkdirall(literal); mkdirall(logicaldir); mkdirall(logicaltestdir); mkdirall(treedocs); mkdirall(treemain); mkdirall(doconlydocs); mkdirall(mixed); mkdirall(control); mkdirall(warm); mkdirall(exact); mkdirall(importer); mkdirall(importerone); mkdirall(docdep); let doc: str = strings.concat(docs, "/doc.ww"); let doclink: str = strings.concat(root, "/doclink.ww"); let logical: str = strings.concat(logicaldir, "/doc.ww"); let rawi: str = strings.concat(rawidir, "/rawi.ww"); let rawitreefile: str = strings.concat(rawitree, "/rawi.ww"); let badheader: str = strings.concat(controls, "/badheader.ww"); let badpackage: str = strings.concat(controls, "/badpackage.ww"); let bom: str = strings.concat(bomdir, "/bom.ww"); let triviafile: str = strings.concat(trivia, "/trivia.ww"); let literaltest: str = strings.concat(literal, "/literal_test.ww"); let logicaltest: str = strings.concat(logicaltestdir, "/provider_test.ww"); let mixdoc: str = strings.concat(mixed, "/doc.ww"); let warmfile: str = strings.concat(warm, "/main.ww"); let maintext: str = "package main;\nfn main() i32 = { return 31; };\n"; let doctext: str = strings.concat("// leading trivia\npackage documentation;\n", "this is deliberately malformed ordinary body;\n", "import absent.after.boundary;\nfn main() i32 = { return 99; };\n"); writefile(doc, doctext); writefile(logical, doctext); assert(os.symlink(doc, doclink) == 0); writefile(rawi, "package documentation;\ni this_is_not_import;\n"); writefile(badheader, "package documentation;\nimport ;\n"); writefile(badpackage, "package ;\n"); putbomfile(bom, "// bom and comment\npackage documentation;\n", "this malformed body is past the header;\n", false); writefile(triviafile, strings.concat("//ww:module ignored.header\n", "package /* comment between clause and name */ documentation;\n", "this malformed body is past the header;\n")); writefile(rawitreefile, "package documentation;\ni this_is_not_import;\n"); writefile(literaltest, "package documentation;\n"); writefile(logicaltest, doctext); writefile(mixdoc, doctext); writefile(strings.concat(mixed, "/main.ww"), maintext); writefile(strings.concat(control, "/main.ww"), maintext); writefile(warmfile, maintext); writefile(strings.concat(treedocs, "/doc.ww"), doctext); writefile(strings.concat(doconlydocs, "/doc.ww"), doctext); writefile(strings.concat(treemain, "/main.ww"), maintext); writefile(strings.concat(treemain, "/main_test.ww"), "package main;\n@test fn retained() void = { assert(true); };\n"); writefile(strings.concat(docdep, "/doc.ww"), doctext); writefile(strings.concat(importer, "/user.ww"), strings.concat( "package user;\nimport docdep;\n", "export fn value() i32 = { return docdep.value(); };\n")); writefile(strings.concat(include, "/docone.ww"), doctext); writefile(strings.concat(importerone, "/user.ww"), strings.concat( "package userone;\nimport docone;\n", "export fn value() i32 = { return docone.value(); };\n")); writefile(strings.concat(exact, "/x.ww"), "package documentationx;\nimport absent.exact;\n"); writefile(strings.concat(exact, "/t.ww"), "package documentation_test;\nimport absent.exact;\n"); let stages: []str = ["ww", "ww_ww"]; let tags: []str = ["c", "ww"]; let compilers: []str = ["w6c", "w6c_ww"]; let assemblers: []str = ["w6a", "w6a_ww"]; let linkers: []str = ["w6l", "w6l_ww"]; let baseenv: []str = os.getenvs(); let records: []str = alloc([], 96u64)!; let cross: []str = alloc([], 96u64)!; let mixedcross: str = ""; let mixedsemanticcross: str = ""; let warmcross: str = ""; let out: commandout; let si: i32 = 0; for (si < stages.len) { records.len = 0; let work: str = strings.concat(root, "/docs-work-", tags[si]); let rawwork: str = strings.concat(root, "/raw-work-", tags[si]); let mixwork: str = strings.concat(root, "/mixed-work-", tags[si]); let ctlwork: str = strings.concat(root, "/control-work-", tags[si]); let outputdir: str = strings.concat(root, "/outputs-", tags[si]); let mixedout: str = strings.concat(root, "/mixed-", tags[si]); let controlout: str = strings.concat(root, "/control-", tags[si]); let trace: str = strings.concat(root, "/tools-", tags[si], ".trace"); let cwrap: str = strings.concat(root, "/w6c-", tags[si], ".sh"); let awrap: str = strings.concat(root, "/w6a-", tags[si], ".sh"); let lwrap: str = strings.concat(root, "/w6l-", tags[si], ".sh"); mkdirall(work); mkdirall(rawwork); mkdirall(mixwork); mkdirall(ctlwork); mkdirall(outputdir); writefile(trace, ""); writeexecutable(cwrap, "#!/bin/sh\nprintf C >> \"$WW_DOC_TRACE\"\nexec \"$WW_DOC_REAL_C\" \"$@\"\n"); writeexecutable(awrap, "#!/bin/sh\nprintf A >> \"$WW_DOC_TRACE\"\nexec \"$WW_DOC_REAL_A\" \"$@\"\n"); writeexecutable(lwrap, "#!/bin/sh\nprintf L >> \"$WW_DOC_TRACE\"\nexec \"$WW_DOC_REAL_L\" \"$@\"\n"); let env: []str = alloc([], (baseenv.len + 6): 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_DOC_")) { append(env, baseenv[ei]); }; ei += 1; }; append(env, strings.concat("WW_W6C=", cwrap)); append(env, strings.concat("WW_W6A=", awrap)); append(env, strings.concat("WW_W6L=", lwrap)); append(env, strings.concat("WW_DOC_REAL_C=", driver(compilers[si]))); append(env, strings.concat("WW_DOC_REAL_A=", driver(assemblers[si]))); append(env, strings.concat("WW_DOC_REAL_L=", driver(linkers[si]))); append(env, strings.concat("WW_DOC_TRACE=", trace)); let directerr: str = strings.concat("ww: ", docs, ": directory contains no WW package sources\n"); let namederr: str = strings.concat("ww: ", docs, ": directory contains no WW package sources\n"); let coordinatorerr: str = strings.concat("wwtest package: ", docs, ": directory contains no WW package sources\n"); let before: str = treesnapshot(work); let av: []str = [driver(stages[si]), "build", "-w", work, docs]; runcommandenv(root, strings.concat("documentation-build-", tags[si]), av, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(out.stdout.len == 0 && same(out.stderr, directerr) && same(before, treesnapshot(work)) && readfile(trace).len == 0); let namedav: []str = [driver(stages[si]), "build", "-w", work, doc]; runcommandenv(root, strings.concat("documentation-named-", tags[si]), namedav, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(out.stdout.len == 0 && same(out.stderr, namederr) && same(before, treesnapshot(work)) && readfile(trace).len == 0); let linkav: []str = [driver(stages[si]), "build", "-w", work, doclink]; runcommandenv(root, strings.concat("documentation-named-link-", tags[si]), linkav, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(out.stdout.len == 0 && same(out.stderr, strings.concat("ww: ", root, ": directory contains no WW package sources\n")) && same(before, treesnapshot(work)) && readfile(trace).len == 0); let logicalav: []str = [driver(stages[si]), "build", "-w", work, "-I", include, "logic.doc"]; runcommandenv(root, strings.concat("documentation-logical-", tags[si]), logicalav, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(out.stdout.len == 0 && same(out.stderr, strings.concat("ww: ", logicaldir, ": directory contains no WW package sources\n"))); let logicaltestav: []str = [driver(stages[si]), "build", "-w", work, "-I", include, "logicaltest.provider_test"]; runcommandenv(root, strings.concat("documentation-logical-test-", tags[si]), logicaltestav, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(out.stdout.len == 0 && same(out.stderr, strings.concat("ww: ", logicaltestdir, ": directory contains no WW package sources\n"))); let coordav: []str = [driver(stages[si]), "build", "-o", outputdir, docs]; runcommandenv(root, strings.concat("documentation-coordinator-", tags[si]), coordav, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(out.stdout.len == 0 && same(out.stderr, coordinatorerr) && !directoryhasnew(outputdir) && readfile(trace).len == 0); let runav: []str = [driver(stages[si]), "run", docs]; runcommandenv(root, strings.concat("documentation-run-dir-", tags[si]), runav, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(out.stdout.len == 0 && same(out.stderr, directerr) && readfile(trace).len == 0); let runnamed: []str = [driver(stages[si]), "run", doc]; runcommandenv(root, strings.concat("documentation-run-file-", tags[si]), runnamed, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(out.stdout.len == 0 && same(out.stderr, namederr) && readfile(trace).len == 0); let runlogical: []str = [driver(stages[si]), "run", "-I", include, "logic.doc"]; runcommandenv(root, strings.concat("documentation-run-logical-", tags[si]), runlogical, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(out.stdout.len == 0 && same(out.stderr, strings.concat("ww: ", logicaldir, ": directory contains no WW package sources\n"))); let literalplain: []str = [driver(stages[si]), "build", literaltest]; runcommandenv(root, strings.concat("documentation-literal-plain-", tags[si]), literalplain, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 0, &records); assert(out.stdout.len == 0 && out.stderr.len == 0); let literalnull: []str = [driver(stages[si]), "build", "-o", "/dev/null", literaltest]; runcommandenv(root, strings.concat("documentation-literal-null-", tags[si]), literalnull, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 0, &records); assert(out.stdout.len == 0 && out.stderr.len == 0); let literalfile: str = strings.concat(root, "/literal-file-", tags[si]); let literalfileav: []str = [driver(stages[si]), "build", "-o", literalfile, literaltest]; runcommandenv(root, strings.concat("documentation-literal-file-", tags[si]), literalfileav, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(out.stdout.len == 0 && same(out.stderr, "ww: no packages to build\n") && !os.exists(literalfile)); let literaloutdir: str = strings.concat(root, "/literal-output-", tags[si]); mkdirall(literaloutdir); let literaldirav: []str = [driver(stages[si]), "build", "-o", literaloutdir, literaltest]; runcommandenv(root, strings.concat("documentation-literal-dir-", tags[si]), literaldirav, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(out.stdout.len == 0 && same(out.stderr, "ww: no main packages to build\n") && !directoryhasnew(literaloutdir)); let rawbefore: str = treesnapshot(rawwork); let rawtest: []str = [driver(stages[si]), "test", "-w", rawwork, doc]; runcommandenv(root, strings.concat("documentation-test-raw-", tags[si]), rawtest, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(same(out.stdout, "FAIL\n") && same(out.stderr, namederr) && same(rawbefore, treesnapshot(rawwork)) && readfile(trace).len == 0); let logicalraw: []str = [driver(stages[si]), "test", "-I", include, "logic.doc"]; runcommandenv(root, strings.concat("documentation-test-logical-", tags[si]), logicalraw, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(same(out.stdout, "FAIL\n") && same(out.stderr, strings.concat("ww: ", logicaldir, ": directory contains no WW package sources\n"))); let rawcompile: []str = [driver(stages[si]), "test", "-c", "-w", rawwork, doc]; runcommandenv(root, strings.concat("documentation-test-c-", tags[si]), rawcompile, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(out.stdout.len == 0 && same(out.stderr, namederr)); let rawasmout: str = strings.concat(root, "/raw-asm-", tags[si]); let rawasm: []str = [driver(stages[si]), "test", "-S", "-w", rawwork, "-o", rawasmout, doc]; runcommandenv(root, strings.concat("documentation-test-s-", tags[si]), rawasm, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(out.stdout.len == 0 && same(out.stderr, namederr) && !os.exists(rawasmout)); let dirtest: []str = [driver(stages[si]), "test", docs]; runcommandenv(root, strings.concat("documentation-test-dir-", tags[si]), dirtest, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(same(out.stdout, "FAIL\n") && same(out.stderr, coordinatorerr)); let dircompile: []str = [driver(stages[si]), "test", "-c", docs]; runcommandenv(root, strings.concat("documentation-test-dir-c-", tags[si]), dircompile, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(out.stdout.len == 0 && same(out.stderr, coordinatorerr)); let dirasm: []str = [driver(stages[si]), "test", "-S", docs]; runcommandenv(root, strings.concat("documentation-test-dir-s-", tags[si]), dirasm, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 2, &records); assert(out.stdout.len == 0 && same(out.stderr, "ww test: -S needs -o\n")); let dirasmout: str = strings.concat(root, "/dir-asm-", tags[si]); let dirasmwithoutput: []str = [driver(stages[si]), "test", "-S", "-o", dirasmout, docs]; runcommandenv(root, strings.concat("documentation-test-dir-s-output-", tags[si]), dirasmwithoutput, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 2, &records); assert(out.stdout.len == 0 && same(out.stderr, "ww test: -S needs a single test file\n")); let spec: str = strings.concat(doconlytree, "/..."); let recbuild: []str = [driver(stages[si]), "build", spec]; runcommandenv(root, strings.concat("documentation-rec-build-", tags[si]), recbuild, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 0, &records); assert(out.stdout.len == 0 && same(out.stderr, strings.concat("ww: warning: \"", spec, "\" matched no packages\n"))); let recbuildtwice: []str = [driver(stages[si]), "build", spec, spec]; runcommandenv(root, strings.concat("documentation-rec-build-twice-", tags[si]), recbuildtwice, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 0, &records); assert(out.stdout.len == 0 && same(out.stderr, strings.concat("ww: warning: \"", spec, "\" matched no packages\nww: warning: \"", spec, "\" matched no packages\n"))); let rectest: []str = [driver(stages[si]), "test", spec]; runcommandenv(root, strings.concat("documentation-rec-test-", tags[si]), rectest, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(out.stdout.len == 0 && same(out.stderr, strings.concat("ww: warning: \"", spec, "\" matched no packages\nww test: no packages to test\n"))); let rectesttwice: []str = [driver(stages[si]), "test", spec, spec]; runcommandenv(root, strings.concat("documentation-rec-test-twice-", tags[si]), rectesttwice, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(out.stdout.len == 0 && same(out.stderr, strings.concat("ww: warning: \"", spec, "\" matched no packages\nww: warning: \"", spec, "\" matched no packages\nww test: no packages to test\n"))); let mixedspec: str = strings.concat(tree, "/..."); let mixedrecbuild: []str = [driver(stages[si]), "build", "-o", "/dev/null", mixedspec]; runcommandenv(root, strings.concat("documentation-rec-mixed-build-", tags[si]), mixedrecbuild, env, (60i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 0, &records); assert(out.stdout.len == 0 && out.stderr.len == 0); let mixedrectest: []str = [driver(stages[si]), "test", mixedspec]; runcommandenv(root, strings.concat("documentation-rec-mixed-test-", tags[si]), mixedrectest, env, (60i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 0, &records); assert(out.stderr.len == 0 && has(out.stdout, "retained ... ok\n") && !has(out.stdout, "documentation")); rewritefile(trace, ""); let rawiav: []str = [driver(stages[si]), "build", rawi]; runcommandenv(root, strings.concat("documentation-raw-i-", tags[si]), rawiav, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(out.stdout.len == 0 && same(out.stderr, strings.concat(rawi, ":2:1: error: expected top-level decl\n")) && readfile(trace).len == 0); let rawirun: []str = [driver(stages[si]), "run", rawi]; runcommandenv(root, strings.concat("documentation-raw-i-run-", tags[si]), rawirun, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(out.stdout.len == 0 && same(out.stderr, strings.concat(rawi, ":2:1: error: expected top-level decl\n"))); let rawitest: []str = [driver(stages[si]), "test", rawi]; runcommandenv(root, strings.concat("documentation-raw-i-test-", tags[si]), rawitest, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(same(out.stdout, "FAIL\n") && same(out.stderr, strings.concat(rawi, ":2:1: error: expected top-level decl\n"))); let rawicompile: []str = [driver(stages[si]), "test", "-c", rawi]; runcommandenv(root, strings.concat("documentation-raw-i-c-", tags[si]), rawicompile, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(out.stdout.len == 0 && same(out.stderr, strings.concat(rawi, ":2:1: error: expected top-level decl\n"))); let rawiasmout: str = strings.concat(root, "/raw-i-asm-", tags[si]); let rawiasm: []str = [driver(stages[si]), "test", "-S", "-o", rawiasmout, rawi]; runcommandenv(root, strings.concat("documentation-raw-i-s-", tags[si]), rawiasm, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(out.stdout.len == 0 && same(out.stderr, strings.concat(rawi, ":2:1: error: expected top-level decl\n")) && !os.exists(rawiasmout)); let rawidirectorytest: []str = [driver(stages[si]), "test", rawidir]; runcommandenv(root, strings.concat("documentation-raw-i-dir-test-", tags[si]), rawidirectorytest, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(same(out.stdout, "FAIL\n") && same(out.stderr, strings.concat(rawi, ":2:1: error: expected top-level decl\n"))); let rawispec: str = strings.concat(rawitree, "/..."); let rawirectest: []str = [driver(stages[si]), "test", rawispec]; runcommandenv(root, strings.concat("documentation-raw-i-rec-test-", tags[si]), rawirectest, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(same(out.stdout, "FAIL\n") && same(out.stderr, strings.concat(rawitreefile, ":2:1: error: expected top-level decl\n"))); let headerav: []str = [driver(stages[si]), "build", badheader]; runcommandenv(root, strings.concat("documentation-header-", tags[si]), headerav, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(out.stdout.len == 0 && same(out.stderr, strings.concat(badheader, ":2:8: error: expected identifier, got ;\n"))); let packageav: []str = [driver(stages[si]), "build", badpackage]; runcommandenv(root, strings.concat("documentation-package-", tags[si]), packageav, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(out.stdout.len == 0 && same(out.stderr, strings.concat(badpackage, ":1:9: error: invalid or missing package clause\n"))); let bomav: []str = [driver(stages[si]), "build", bom]; runcommandenv(root, strings.concat("documentation-bom-", tags[si]), bomav, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(out.stdout.len == 0 && same(out.stderr, strings.concat("ww: ", bomdir, ": directory contains no WW package sources\n"))); let triviaav: []str = [driver(stages[si]), "build", triviafile]; runcommandenv(root, strings.concat("documentation-trivia-", tags[si]), triviaav, env, (30i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 1, &records); assert(out.stdout.len == 0 && same(out.stderr, strings.concat("ww: ", trivia, ": directory contains no WW package sources\n"))); let exactav: []str = [driver(stages[si]), "build", strings.concat(exact, "/x.ww")]; runcommandenv(root, strings.concat("documentation-exact-x-", tags[si]), exactav, env, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(!has(out.stderr, "directory contains no WW package sources")); let exacttav: []str = [driver(stages[si]), "build", strings.concat(exact, "/t.ww")]; runcommandenv(root, strings.concat("documentation-exact-t-", tags[si]), exacttav, env, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(!has(out.stderr, "directory contains no WW package sources")); let importav: []str = [driver(stages[si]), "build", "-I", include, importer]; runcommandenv(root, strings.concat("documentation-imported-", tags[si]), importav, env, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(!os.exists(strings.concat(root, "/imported-", tags[si])) && readfile(trace).len == 0); let importoneav: []str = [driver(stages[si]), "build", "-I", include, importerone]; runcommandenv(root, strings.concat("documentation-imported-one-", tags[si]), importoneav, env, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, ":2:1: error: cannot find package docone\n") && !has(out.stderr, "directory contains no WW package sources") && readfile(trace).len == 0); let mixav: []str = [driver(stages[si]), "build", "-w", mixwork, "-o", mixedout, mixed]; let ctlav: []str = [driver(stages[si]), "build", "-w", ctlwork, "-o", controlout, control]; runcommandenv(root, strings.concat("documentation-mixed-cold-", tags[si]), mixav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(mixedout)); runcommandenv(root, strings.concat("documentation-control-cold-", tags[si]), ctlav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && same(readfile(mixedout), readfile(controlout))); let mixedbytes: str = strings.dup(readfile(mixedout)); let mixedtree: str = strings.dup(artifacttreesnapshot(mixwork)); let mixedsemantic: str = strings.dup(wrongsuffixsemantictreesnapshot(mixwork)); if (si == 0) { mixedcross = strings.dup(mixedbytes); mixedsemanticcross = strings.dup(mixedsemantic); } else { assert(same(mixedcross, mixedbytes) && same(mixedsemanticcross, mixedsemantic)); }; let mixabsent: str = strings.concat(mixdoc, ".absent"); assert(os.rename(mixdoc, mixabsent) == 0); rewritefile(trace, ""); runcommandenv(root, strings.concat("documentation-mixed-absent-", tags[si]), mixav, env, (60i64 * (time.second: i64)): time.duration, &out); documentationrecord(&out, 0, &records); assert(out.stdout.len == 0 && out.stderr.len == 0 && same(mixedbytes, readfile(mixedout)) && same(mixedtree, artifacttreesnapshot(mixwork)) && same(mixedsemantic, wrongsuffixsemantictreesnapshot(mixwork)) && same(readfile(trace), "L")); assert(os.rename(mixabsent, mixdoc) == 0); rewritefile(trace, ""); rewritefile(mixdoc, strings.concat(doctext, "still ignored\n")); runcommandenv(root, strings.concat("documentation-mixed-warm-", tags[si]), mixav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && same(mixedbytes, readfile(mixedout)) && same(mixedtree, artifacttreesnapshot(mixwork)) && same(readfile(trace), "L")); 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, warm]; runcommandenv(root, strings.concat("documentation-warm-cold-", tags[si]), warmav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); let warmbytes: str = strings.dup(readfile(warmout)); let warmtree: str = strings.dup(artifacttreesnapshot(warmwork)); if (si == 0) { warmcross = strings.dup(warmbytes); } else { assert(same(warmcross, warmbytes)); }; rewritefile(trace, ""); rewritefile(warmfile, doctext); runcommandenv(root, strings.concat("documentation-warm-docs-", tags[si]), warmav, env, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && same(out.stderr, strings.concat("ww: ", warm, ": directory contains no WW package sources\n")) && same(warmbytes, readfile(warmout)) && same(warmtree, artifacttreesnapshot(warmwork)) && readfile(trace).len == 0); rewritefile(warmfile, "package documentation;\nimport ;\n"); runcommandenv(root, strings.concat("documentation-warm-header-", tags[si]), warmav, env, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && same(out.stderr, strings.concat(warmfile, ":2:8: error: expected identifier, got ;\n")) && same(warmbytes, readfile(warmout)) && same(warmtree, artifacttreesnapshot(warmwork)) && readfile(trace).len == 0); rewritefile(warmfile, maintext); documentationclean(root); if (si == 0) { let ri: i32 = 0; for (ri < records.len) { append(cross, strings.dup(records[ri])); ri += 1; }; } else { documentationrecordssame(cross, records); }; si += 1; }; // Header classification has no shared action identity or lock: simultaneous // Cstage/WWstage requests may read the same source but must leave separate // caller-owned work roots untouched. let cwork: str = strings.concat(root, "/parallel-c-work"); let wwork: str = strings.concat(root, "/parallel-ww-work"); mkdirall(cwork); mkdirall(wwork); let cbefore: str = treesnapshot(cwork); let wbefore: str = treesnapshot(wwork); let cav: []str = [driver("ww"), "build", "-w", cwork, docs]; let wav: []str = [driver("ww_ww"), "build", "-w", wwork, docs]; let cc: exec.command; cc.path = cav[0]; cc.argv = cav; cc.env = os.getenvs(); cc.dir = repo(); cc.stdoutpath = strings.concat(root, "/documentation-parallel-c.stdout"); cc.stderrpath = strings.concat(root, "/documentation-parallel-c.stderr"); cc.deadline = time.add(time.now(time.clock.monotonic), (30i64 * (time.second: i64)): time.duration); cc.grace = (100i64 * (time.millisecond: i64)): time.duration; let wc: exec.command; wc.path = wav[0]; wc.argv = wav; wc.env = os.getenvs(); wc.dir = repo(); wc.stdoutpath = strings.concat(root, "/documentation-parallel-ww.stdout"); wc.stderrpath = strings.concat(root, "/documentation-parallel-ww.stderr"); wc.deadline = time.add(time.now(time.clock.monotonic), (30i64 * (time.second: i64)): time.duration); wc.grace = (100i64 * (time.millisecond: i64)): time.duration; let cp: exec.process; let wp: exec.process; exec.start(&cp, &cc); exec.start(&wp, &wc); let cdone: bool = false; let wdone: bool = false; for (!cdone || !wdone) { if (!cdone) { cdone = exec.poll(&cp); }; if (!wdone) { wdone = exec.poll(&wp); }; if (!cdone || !wdone) { time.sleep(time.millisecond, time.clock.monotonic); }; }; let parallelerr: str = strings.concat("ww: ", docs, ": directory contains no WW package sources\n"); assert(cp.result.errno == 0 && cp.result.cleanuperrno == 0 && cp.result.termination == exec.termination.EXIT && cp.result.code == 1 && wp.result.errno == 0 && wp.result.cleanuperrno == 0 && wp.result.termination == exec.termination.EXIT && wp.result.code == 1 && readfile(cc.stdoutpath).len == 0 && readfile(wc.stdoutpath).len == 0 && same(readfile(cc.stderrpath), parallelerr) && same(readfile(wc.stderrpath), parallelerr) && same(cbefore, treesnapshot(cwork)) && same(wbefore, treesnapshot(wwork))); documentationclean(root); clean(root); }; // The single-FILE `ww test ` route wraps the same in-binary test // runtime as the directory-package route (cases/*), but through the // driver's single-file leg. One row per outcome class keeps that leg's // wiring honest; the runtime-internal classifications themselves are // owned by the cases/* tests above. @test fn singlefile_runtime_outcomes() void = { let root: str = fresh(); let out: commandout; let successav: []str = [driver("ww"), "test", runtimepath("success_test.ww")]; runcommand(root, "sf-success", successav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "alpha_pass ... ok\n")); assert(has(out.stdout, "beta_skip ... SKIP: synthetic unavailable feature\n")); assert(has(out.stdout, "gamma_expected_abort ... ok\n")); assert(has(out.stdout, "delta_pass ... ok\n")); assert(has(out.stdout, "3 passed, 0 failed, 1 skipped, 0 harness errors\n")); assert(has(out.stdout, "4 discovered, 4 selected, 4 started, 4 completed\n")); let failav: []str = [driver("ww"), "test", runtimepath("fail_test.ww")]; runcommand(root, "sf-fail", failav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stdout, "assertion_failure ... FAIL (exit 1)\n")); let multiav: []str = [driver("ww"), "test", runtimepath("multiple_fail_test.ww")]; runcommand(root, "sf-multi", multiav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stdout, "0 passed, 2 failed, 0 skipped, 0 harness errors\n")); let unmetav: []str = [driver("ww"), "test", runtimepath("expected_abort_return_test.ww")]; runcommand(root, "sf-unmet", unmetav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stdout, "expected_abort_must_happen ... FAIL (expected abort)\n")); let premav: []str = [driver("ww"), "test", runtimepath("premature_exit_test.ww")]; runcommand(root, "sf-premature", premav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stdout, "clean_exit_without_completion ... HARNESS (incomplete result)\n")); let sigav: []str = [driver("ww"), "test", runtimepath("signal_test.ww")]; runcommand(root, "sf-signal", sigav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stdout, "signal_is_not_exit ... FAIL (signal 15)\n")); // Record-and-continue past every fault class: the runner reports // hardware faults with their literal signal numbers and still // reaches the trailing test (retired 911_attest_record). let recav: []str = [driver("ww"), "test", runtimepath("record_continue_test.ww")]; runcommand(root, "sf-record", recav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stdout, "rec_pass_a ... ok\n")); assert(has(out.stdout, "rec_assert ... FAIL (exit 1)\n")); assert(has(out.stdout, "rec_segv ... FAIL (signal 11)\n")); assert(has(out.stdout, "rec_div0 ... FAIL (signal 8)\n")); assert(has(out.stdout, "rec_pass_b ... ok\n")); assert(has(out.stdout, "2 passed, 3 failed, 0 skipped, 0 harness errors\n")); assert(has(out.stdout, "5 discovered, 5 selected, 5 started, 5 completed\n")); clean(root); }; // Explicit linker search/library flags are retained by the package builder // and reach both native linkers in their accepted argv forms. This is a // real manifest-free build/run check, not a parser-only simulation. @test fn linker_flag_stage_parity() void = { let root: str = fresh(); let pkg: str = strings.concat(root, "/mainpkg"); let extra: str = strings.concat(root, "/extra"); let linktrace: str = strings.concat(root, "/link.trace"); let cwrapper: str = strings.concat(root, "/trace-w6l.sh"); assert(os.mkdir(pkg, 448i32) == 0); assert(os.mkdir(extra, 448i32) == 0); writefile(linktrace, ""); writeexecutable(cwrapper, strings.concat( "#!/bin/sh\n", "printf '%s\\n' \"$*\" >> \"$WW_LINK_FLAG_TRACE\"\n", "exec \"$WW_LINK_FLAG_W6L\" \"$@\"\n")); writefile(strings.concat(pkg, "/main.ww"), "package main;\nfn main() void = {};\n"); writefile(strings.concat(extra, "/extra.ww"), "package extra;\nexport fn marker() i32 = { return 7; };\n"); let extralib: str = strings.concat(root, "/libextra.a"); let libav: []str = [driver("ww"), "build", "-o", extralib, extra]; let out: commandout; runcommand(root, "link-flags-library", libav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let cbin: str = strings.concat(root, "/c-main"); let wwbin: str = strings.concat(root, "/ww-main"); let cav: []str = [driver("ww"), "build", "-L", root, "-l", "extra", "-o", cbin, pkg]; let baseenv: []str = os.getenvs(); let cenv: []str = alloc([], (baseenv.len + 3): u64)!; let ei: i32 = 0; for (ei < baseenv.len) { if (!strings.hasprefix(baseenv[ei], "WW_W6L=") && !strings.hasprefix(baseenv[ei], "WW_LINK_FLAG_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_LINK_FLAG_W6L=")) { append(cenv, baseenv[ei]); }; ei += 1; }; append(cenv, strings.concat("WW_W6L=", cwrapper)); append(cenv, strings.concat("WW_LINK_FLAG_TRACE=", linktrace)); append(cenv, strings.concat("WW_LINK_FLAG_W6L=", driver("w6l"))); runcommandenv(root, "link-flags-c", cav, cenv, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let ctraced: str = readfile(linktrace); assert(has(ctraced, strings.concat(" -L ", root, " -l extra"))); // Keep this row on default sibling discovery: a copied driver beside a // tracing linker and exact compiler/assembler siblings proves WWstage uses // the same separate flag/value linker argv contract as Cstage. let tracedir: str = strings.concat(repo(), "/out/link-flag-trace-", workescape(root)); assert(os.mkdir(tracedir, 448i32) == 0); let tracedriver: str = strings.concat(tracedir, "/ww_ww"); writeexecutable(tracedriver, readfile(driver("ww_ww"))); writeexecutable(strings.concat(tracedir, "/w6c_ww"), readfile(driver("w6c_ww"))); writeexecutable(strings.concat(tracedir, "/w6a_ww"), readfile(driver("w6a_ww"))); writeexecutable(strings.concat(tracedir, "/w6l_ww"), strings.concat( "#!/bin/sh\n", "printf '%s\\n' \"$*\" >> \"$WW_LINK_FLAG_TRACE\"\n", "exec \"$WW_LINK_FLAG_W6L_WW\" \"$@\"\n")); let wenv: []str = alloc([], (baseenv.len + 2): u64)!; ei = 0; for (ei < baseenv.len) { if (!strings.hasprefix(baseenv[ei], "WW_LINK_FLAG_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_LINK_FLAG_W6L_WW=")) { append(wenv, baseenv[ei]); }; ei += 1; }; append(wenv, strings.concat("WW_LINK_FLAG_TRACE=", linktrace)); append(wenv, strings.concat("WW_LINK_FLAG_W6L_WW=", driver("w6l_ww"))); let twav: []str = [tracedriver, "build", "-L", root, "-l", "extra", "-o", wwbin, pkg]; runcommandenv(root, "link-flags-ww", twav, wenv, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let wtraced: str = readfile(linktrace); let wwdelta: str = strings.sub(wtraced, ctraced.len, wtraced.len); assert(has(wwdelta, strings.concat(" -L ", root, " -l extra"))); assert(!has(wwdelta, strings.concat(" -L", root))); assert(!has(wwdelta, " -lextra")); assert(same(readfile(cbin), readfile(wwbin))); let crun: []str = [cbin]; let wrun: []str = [wwbin]; runcommand(root, "link-flags-c-run", crun, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); runcommand(root, "link-flags-ww-run", wrun, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); clean(tracedir); clean(root); }; // The published test binary's OWN command surface: repeated glob args, // -list (with and without a glob), and -timeout-ms. Only a direct // invocation of the `-c` artifact proves these; every driver route // parses its flags before the binary sees them. @test fn published_binary_cli() void = { let root: str = fresh(); let out: commandout; let stem: str = strings.concat(root, "/rtsuccess"); let cav: []str = [driver("ww"), "test", "-c", "-o", stem, runtimepath("success_test.ww")]; runcommand(root, "publish", cav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let gav: []str = [stem, "alpha*", "delta*"]; runcommand(root, "globs", gav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "alpha_pass ... ok\n")); assert(has(out.stdout, "delta_pass ... ok\n")); assert(has(out.stdout, "2 passed, 0 failed, 0 skipped, 0 harness errors\n")); assert(has(out.stdout, "4 discovered, 2 selected, 2 started, 2 completed\n")); let lav: []str = [stem, "-list"]; runcommand(root, "list", lav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(out.stdout, "alpha_pass\nbeta_skip\ngamma_expected_abort\ndelta_pass\n")); let lgav: []str = [stem, "-list", "gamma*"]; runcommand(root, "list-glob", lgav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(out.stdout, "gamma_expected_abort\n")); let tav: []str = [stem, "-timeout-ms=50", "alpha*"]; runcommand(root, "cli-timeout", tav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "1 passed, 0 failed, 0 skipped, 0 harness errors\n")); clean(root); }; // Direct-binary timeout classification and descendant handling. The // runcommand deadline bounds the no-hang claims: a runner waiting on a // SIGTERM-blocking or process-group-escaped pipe writer would trip it. @test fn direct_binary_timeout_and_descendants() void = { let root: str = fresh(); let out: commandout; let tstem: str = strings.concat(root, "/rttimeout"); let tcav: []str = [driver("ww"), "test", "-c", "-o", tstem, runtimepath("timeout_test.ww")]; runcommand(root, "publish-timeout", tcav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let trun: []str = [tstem, "-timeout-ms=50"]; runcommand(root, "run-timeout", trun, (20i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stdout, "hang_times_out ... FAIL(timeout)\n")); let lstem: str = strings.concat(root, "/rtlinger"); let lcav: []str = [driver("ww"), "test", "-c", "-o", lstem, runtimepath("lingering_descendant_test.ww")]; runcommand(root, "publish-linger", lcav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let lrun: []str = [lstem, "-timeout-ms=250"]; runcommand(root, "run-linger", lrun, (20i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "returning_test_clears_descendant ... ok\n")); let estem: str = strings.concat(root, "/rtescaped"); let ecav: []str = [driver("ww"), "test", "-c", "-o", estem, runtimepath("escaped_descendant_test.ww")]; runcommand(root, "publish-escaped", ecav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let erun: []str = [estem, "-timeout-ms=250"]; runcommand(root, "run-escaped", erun, (20i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "escaped_writer_does_not_hold_runner ... ok\n")); clean(root); }; @test fn vendor_directory_import_resolution() void = { let root: str = fresh(); let source: str = strings.concat(root, "/source"); let runtime: str = strings.concat(root, "/runtime"); let tools: str = strings.concat(root, "/tools"); let leaf: str = strings.concat(source, "/lib/leaf"); let nearest: str = strings.concat(source, "/domain/app/vendor/lib/math"); let outer: str = strings.concat(source, "/domain/vendor/lib/math"); let rootvendor: str = strings.concat(source, "/vendor/lib/math"); let ordinary: str = strings.concat(source, "/lib/math"); let client: str = strings.concat(source, "/domain/app/client"); let one: str = strings.concat(source, "/domain/app/one"); let two: str = strings.concat(source, "/domain/app/two"); let outerclient: str = strings.concat(source, "/domain/other/client"); let rootclient: str = strings.concat(source, "/other/client"); let emptyclient: str = strings.concat(source, "/domain/empty/client"); let testonlyclient: str = strings.concat(source, "/domain/testonly/client"); let bareclient: str = strings.concat(source, "/domain/bare/client"); let outsider: str = strings.concat(source, "/outsider"); let prefixclient: str = strings.concat(source, "/domainx/client"); let directclient: str = strings.concat(source, "/domain/app/direct"); let directhijack: str = strings.concat(directclient, "/vendor/domain/app/vendor/lib/math"); let nested: str = strings.concat(source, "/nested/owner/vendor/outer/vendor/lib/math"); let nestedclient: str = strings.concat(source, "/nested/owner/vendor/outer/client"); let ordinaryclient: str = strings.concat(source, "/ordinaryclient"); let vendorx: str = strings.concat(source, "/vendorx/lib/math"); let terminalvendor: str = strings.concat(source, "/terminal/vendor"); let physicalclient: str = strings.concat(source, "/domain/app/physicalclient"); let aliasclient: str = strings.concat(source, "/aliasclient"); let physicaldirect: str = strings.concat(source, "/domain/app/physicaldirect"); let aliasdirect: str = strings.concat(source, "/aliasdirect"); let escapephysical: str = strings.concat(root, "/escape/client"); let escapeclient: str = strings.concat(source, "/domain/app/escape"); let shapeclient: str = strings.concat(source, "/shape/owner/client"); let shapevendor: str = strings.concat(source, "/shape/owner/vendor/lib/math"); let shapevault: str = strings.concat(root, "/vault/deep/different"); let testclient: str = strings.concat(source, "/domain/app/testclient"); let testbad: str = strings.concat(source, "/testbad"); let supportcase: str = strings.concat(source, "/supportcase"); let toolsource: str = strings.concat(root, "/toolsource"); let toolsupport: str = strings.concat(toolsource, "/test"); let toolvendor: str = strings.concat(toolsource, "/test/vendor/fnmatch"); let vendortest: str = strings.concat(source, "/literal/owner/vendor/lib/math"); let nestedvendortest: str = strings.concat(vendortest, "/vendor/lib/math"); let alpha: str = strings.concat(source, "/alpha/client"); let beta: str = strings.concat(source, "/beta/client"); let alphavendor: str = strings.concat(alpha, "/vendor/lib/math"); let betavendor: str = strings.concat(beta, "/vendor/lib/math"); let pairvault: str = strings.concat(root, "/pair-vault/math"); let boundary: str = strings.concat(root, "/boundary"); let active: str = strings.concat(boundary, "/active"); let boundaryclient: str = strings.concat(active, "/domain/app"); let boundaryordinary: str = strings.concat(active, "/lib/math"); let boundaryabove: str = strings.concat(boundary, "/vendor/lib/math"); let cleanroot: str = strings.concat(root, "/clean"); let cleanclient: str = strings.concat(cleanroot, "/domain/app"); let cleanordinary: str = strings.concat(cleanroot, "/lib/math"); let foldvendorupper: str = strings.concat(source, "/domain/fold/vendor/lib/Foo"); let foldvendorlower: str = strings.concat(source, "/domain/fold/vendor/lib/foo"); let foldvendorleft: str = strings.concat(source, "/domain/fold/left"); let foldvendorright: str = strings.concat(source, "/domain/fold/right"); let foldvendorclient: str = strings.concat(source, "/domain/fold/client"); let foldvendorreverseclient: str = strings.concat(source, "/domain/fold/reverseclient"); let foldaliasvendorlib: str = strings.concat(source, "/domain/foldalias/vendor/lib"); let foldaliasvendorupper: str = strings.concat(foldaliasvendorlib, "/Foo"); let foldaliasvendorlower: str = strings.concat(foldaliasvendorlib, "/foo"); let foldaliasvendorleft: str = strings.concat(source, "/domain/foldalias/left"); let foldaliasvendorright: str = strings.concat(source, "/domain/foldalias/right"); let foldaliasvendorclient: str = strings.concat(source, "/domain/foldalias/client"); let foldaliasvendorvault: str = strings.concat(root, "/fold-vendor-vault"); let dirs: []str = [leaf, nearest, outer, rootvendor, ordinary, client, one, two, outerclient, rootclient, emptyclient, strings.concat(emptyclient, "/vendor/lib/math/fake.ww"), testonlyclient, strings.concat(testonlyclient, "/vendor/lib/math"), bareclient, strings.concat(bareclient, "/vendor/lib/math"), outsider, prefixclient, directclient, directhijack, nested, nestedclient, ordinaryclient, vendorx, terminalvendor, physicalclient, physicaldirect, escapephysical, shapeclient, strings.concat(source, "/shape/owner/vendor/lib"), shapevault, testclient, testbad, supportcase, toolsupport, toolvendor, vendortest, nestedvendortest, alpha, beta, strings.concat(alpha, "/vendor/lib"), strings.concat(beta, "/vendor/lib"), pairvault, boundaryclient, boundaryordinary, boundaryabove, cleanclient, cleanordinary, foldvendorupper, foldvendorlower, foldvendorleft, foldvendorright, foldvendorclient, foldvendorreverseclient, foldaliasvendorlib, foldaliasvendorleft, foldaliasvendorright, foldaliasvendorclient, foldaliasvendorvault, runtime, tools]; let di: i32 = 0; for (di < dirs.len) { mkdirall(dirs[di]); di += 1; }; writefile(strings.concat(runtime, "/libwwrt.a"), readfile(strings.concat(repo(), "/out/lib/libwwrt.a"))); writefile(strings.concat(leaf, "/leaf.ww"), strings.concat( "package leaf;\n", "export fn value() i32 = { return 1; };\n")); let neara: str = strings.concat( "package math;\nimport lib.leaf;\n", "export fn value() i32 = { return leaf.value() + 10; };\n"); let nearz: str = strings.concat( "package math;\n", "fn private_value() i32 = { return 99; };\n"); writefile(strings.concat(nearest, "/a.ww"), neara); writefile(strings.concat(nearest, "/z.ww"), nearz); writefile(strings.concat(outer, "/math.ww"), "package math;\nexport fn value() i32 = { return 22; };\n"); writefile(strings.concat(rootvendor, "/math.ww"), "package math;\nexport fn value() i32 = { return 33; };\n"); writefile(strings.concat(ordinary, "/math.ww"), "package math;\nexport fn value() i32 = { return 44; };\n"); let mainbody: str = strings.concat( "package main;\nimport lib.math;\n", "fn main() i32 = { return math.value(); };\n"); writefile(strings.concat(client, "/main.ww"), mainbody); writefile(strings.concat(one, "/main.ww"), mainbody); writefile(strings.concat(two, "/main.ww"), mainbody); writefile(strings.concat(outerclient, "/main.ww"), mainbody); writefile(strings.concat(rootclient, "/main.ww"), mainbody); writefile(strings.concat(emptyclient, "/main.ww"), mainbody); writefile(strings.concat(testonlyclient, "/main.ww"), mainbody); writefile(strings.concat(testonlyclient, "/vendor/lib/math/only_test.ww"), "package math;\n@test fn only() void = { assert(true); };\n"); writefile(strings.concat(bareclient, "/main.ww"), mainbody); writefile(strings.concat(bareclient, "/vendor/lib/math/.ww"), "package math;\n"); let expanded: str = "domain.app.vendor.lib.math"; let directbody: str = strings.concat( "package main;\nimport ", expanded, ";\n", "fn main() i32 = { return math.value(); };\n"); writefile(strings.concat(outsider, "/main.ww"), directbody); writefile(strings.concat(prefixclient, "/main.ww"), directbody); writefile(strings.concat(directclient, "/main.ww"), directbody); writefile(strings.concat(directhijack, "/math.ww"), "package math;\nexport fn value() i32 = { return 99; };\n"); writefile(strings.concat(nested, "/math.ww"), "package math;\nexport fn value() i32 = { return 77; };\n"); writefile(strings.concat(nestedclient, "/main.ww"), mainbody); writefile(strings.concat(vendorx, "/math.ww"), "package math;\nexport fn value() i32 = { return 4; };\n"); writefile(strings.concat(terminalvendor, "/vendor.ww"), "package vendor;\nexport fn value() i32 = { return 8; };\n"); writefile(strings.concat(ordinaryclient, "/main.ww"), strings.concat( "package main;\nimport vendorx.lib.math;\nimport terminal.vendor;\n", "fn main() i32 = { return math.value() + vendor.value(); };\n")); writefile(strings.concat(physicalclient, "/main.ww"), mainbody); assert(os.symlink(physicalclient, aliasclient) == 0); writefile(strings.concat(physicaldirect, "/main.ww"), directbody); assert(os.symlink(physicaldirect, aliasdirect) == 0); writefile(strings.concat(escapephysical, "/main.ww"), mainbody); assert(os.symlink(escapephysical, escapeclient) == 0); writefile(strings.concat(shapevault, "/math.ww"), "package math;\nexport fn value() i32 = { return 17; };\n"); assert(os.symlink(shapevault, shapevendor) == 0); writefile(strings.concat(shapeclient, "/main.ww"), mainbody); writefile(strings.concat(testclient, "/testclient.ww"), strings.concat( "package testclient;\nimport lib.math;\n", "export fn value() i32 = { return math.value(); };\n")); writefile(strings.concat(testclient, "/same_test.ww"), strings.concat( "package testclient;\nimport lib.math;\n", "@test fn same_vendor() void = { assert(math.value() == 11); };\n")); writefile(strings.concat(testclient, "/external_test.ww"), strings.concat( "package testclient_test;\nimport lib.math;\n", "import domain.app.testclient;\n", "@test fn external_vendor() void = {\n", " assert(math.value() + testclient.value() == 22);\n};\n")); writefile(strings.concat(testbad, "/testbad.ww"), "package testbad;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(testbad, "/same_test.ww"), strings.concat( "package testbad;\nimport ", expanded, ";\n", "@test fn forbidden_vendor() void = { assert(false); };\n")); writefile(strings.concat(supportcase, "/supportcase.ww"), "package supportcase;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(supportcase, "/supportcase_test.ww"), "package supportcase;\n@test fn support_runs() void = { assert(value() == 1); };\n"); writefile(strings.concat(toolsupport, "/run.ww"), readfile(strings.concat(repo(), "/lib/test/run.ww"))); writefile(strings.concat(toolvendor, "/fnmatch.ww"), readfile(strings.concat(repo(), "/lib/fnmatch/fnmatch.ww"))); let tooldeps: []str = ["ascii", "bytes", "encoding", "os", "rt", "strings", "time", "types"]; di = 0; for (di < tooldeps.len) { assert(os.symlink(strings.concat(repo(), "/lib/", tooldeps[di]), strings.concat(toolsource, "/", tooldeps[di])) == 0); di += 1; }; writefile(strings.concat(vendortest, "/math.ww"), "package math;\nexport fn value() i32 = { return 7; };\n"); writefile(strings.concat(vendortest, "/math_test.ww"), strings.concat( "package math_test;\nimport lib.math;\n", "@test fn nested_external_vendor() void = {\n", " assert(math.value() == 9);\n};\n")); writefile(strings.concat(nestedvendortest, "/math.ww"), "package math;\nexport fn value() i32 = { return 9; };\n"); writefile(strings.concat(pairvault, "/math.ww"), "package math;\nexport fn value() i32 = { return 61; };\n"); assert(os.symlink(pairvault, alphavendor) == 0); assert(os.symlink(pairvault, betavendor) == 0); writefile(strings.concat(alpha, "/main.ww"), mainbody); writefile(strings.concat(beta, "/main.ww"), mainbody); writefile(strings.concat(boundaryclient, "/main.ww"), mainbody); writefile(strings.concat(boundaryordinary, "/math.ww"), "package math;\nexport fn value() i32 = { return 55; };\n"); writefile(strings.concat(boundaryabove, "/math.ww"), "package math;\nexport fn value() i32 = { return 66; };\n"); writefile(strings.concat(cleanclient, "/main.ww"), mainbody); writefile(strings.concat(cleanordinary, "/math.ww"), "package math;\nexport fn value() i32 = { return 44; };\n"); writefile(strings.concat(foldvendorupper, "/pkg.ww"), "package upper;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(foldvendorlower, "/pkg.ww"), "package lower;\nexport fn value() i32 = { return 2; };\n"); writefile(strings.concat(foldvendorleft, "/pkg.ww"), strings.concat( "package left;\nimport _ lib.Foo;\n", "export fn value() i32 = { return 1; };\n")); writefile(strings.concat(foldvendorright, "/pkg.ww"), strings.concat( "package right;\nimport _ lib.foo;\n", "export fn value() i32 = { return 2; };\n")); writefile(strings.concat(foldvendorclient, "/main.ww"), strings.concat( "package main;\nimport _ domain.fold.left;\n", "import _ domain.fold.right;\nfn main() i32 = { return 0; };\n")); writefile(strings.concat(foldvendorreverseclient, "/main.ww"), strings.concat( "package main;\nimport _ domain.fold.right;\n", "import _ domain.fold.left;\nfn main() i32 = { return 0; };\n")); writefile(strings.concat(foldaliasvendorvault, "/pkg.ww"), "package shared;\nexport fn value() i32 = { return 1; };\n"); assert(os.symlink(foldaliasvendorvault, foldaliasvendorupper) == 0); assert(os.symlink(foldaliasvendorvault, foldaliasvendorlower) == 0); writefile(strings.concat(foldaliasvendorleft, "/pkg.ww"), strings.concat( "package left;\nimport _ lib.Foo;\n", "export fn value() i32 = { return 1; };\n")); writefile(strings.concat(foldaliasvendorright, "/pkg.ww"), strings.concat( "package right;\nimport _ lib.foo;\n", "export fn value() i32 = { return 2; };\n")); writefile(strings.concat(foldaliasvendorclient, "/main.ww"), strings.concat( "package main;\nimport _ domain.foldalias.left;\n", "import _ domain.foldalias.right;\nfn main() i32 = { return 0; };\n")); let compilerwrapper: str = strings.concat(tools, "/w6c.sh"); let assemblerwrapper: str = strings.concat(tools, "/w6a.sh"); let linkerwrapper: str = strings.concat(tools, "/w6l.sh"); writeexecutable(compilerwrapper, strings.concat( "#!/bin/sh\nprintf 'BEGIN' >> \"$WW_VENDOR_COMPILER_TRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_VENDOR_COMPILER_TRACE\"; done\n", "printf '\\n' >> \"$WW_VENDOR_COMPILER_TRACE\"\n", "exec \"$WW_VENDOR_REAL_COMPILER\" \"$@\"\n")); writeexecutable(assemblerwrapper, strings.concat( "#!/bin/sh\nprintf 'BEGIN' >> \"$WW_VENDOR_ASSEMBLER_TRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_VENDOR_ASSEMBLER_TRACE\"; done\n", "printf '\\n' >> \"$WW_VENDOR_ASSEMBLER_TRACE\"\n", "exec \"$WW_VENDOR_REAL_ASSEMBLER\" \"$@\"\n")); writeexecutable(linkerwrapper, strings.concat( "#!/bin/sh\nprintf 'BEGIN' >> \"$WW_VENDOR_LINKER_TRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_VENDOR_LINKER_TRACE\"; done\n", "printf '\\n' >> \"$WW_VENDOR_LINKER_TRACE\"\n", "exec \"$WW_VENDOR_REAL_LINKER\" \"$@\"\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 works: []str = [strings.concat(root, "/c-work"), strings.concat(root, "/ww-work")]; let bins: []str = [strings.concat(root, "/c-bin"), strings.concat(root, "/ww-bin")]; let compilertraces: []str = [strings.concat(root, "/c-compiler"), strings.concat(root, "/ww-compiler")]; let assemblertraces: []str = [strings.concat(root, "/c-assembler"), strings.concat(root, "/ww-assembler")]; let linkertraces: []str = [strings.concat(root, "/c-linker"), strings.concat(root, "/ww-linker")]; let coreartifacts: []str = ["lib.leaf", expanded, "domain.app.client"]; let coresuffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"]; let references: []str = alloc([], (coreartifacts.len * coresuffixes.len): u64)!; let ri: i32 = 0; for (ri < coreartifacts.len * coresuffixes.len) { append(references, ""); ri += 1; }; let referencebin: str = ""; let referencecompiler: str = ""; let referenceassembler: str = ""; let referencelinker: str = ""; let referencedirectdiag: str = ""; let referenceoutsidediag: str = ""; let combinedrefs: []str = ["", "", "", "", "", ""]; let referencetestout: str = ""; let referencetesterr: str = ""; let testrefs: []str = ["", "", "", "", "", ""]; let referencesupportout: str = ""; let supportrefs: []str = ["", "", ""]; let referenceorderdiag: str = ""; let referenceescapeddiag: str = ""; let referencealiasdirectdiag: str = ""; let referenceprefixdiag: str = ""; let candidate_diag_refs: []str = ["", ""]; let foldvendorrefs: []str = ["", "", ""]; let foldvendormultiref: str = ""; let supplementalrefs: []str = ["", "", "", "", "", "", "", "", ""]; let referencevendortestout: str = ""; let referencevendortesterr: str = ""; let referencetestbaddiag: str = ""; let baseenv: []str = os.getenvs(); let si: i32 = 0; for (si < stages.len) { assert(os.mkdir(works[si], 448i32) == 0); writefile(compilertraces[si], ""); writefile(assemblertraces[si], ""); writefile(linkertraces[si], ""); let env: []str = alloc([], (baseenv.len + 11): 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_LIB=") && !strings.hasprefix(baseenv[ei], "WW_SRCLIB=") && !strings.hasprefix(baseenv[ei], "WW_VENDOR_COMPILER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_VENDOR_ASSEMBLER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_VENDOR_LINKER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_VENDOR_REAL_COMPILER=") && !strings.hasprefix(baseenv[ei], "WW_VENDOR_REAL_ASSEMBLER=") && !strings.hasprefix(baseenv[ei], "WW_VENDOR_REAL_LINKER=")) { 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_LIB=", runtime)); append(env, strings.concat("WW_VENDOR_COMPILER_TRACE=", compilertraces[si])); append(env, strings.concat("WW_VENDOR_ASSEMBLER_TRACE=", assemblertraces[si])); append(env, strings.concat("WW_VENDOR_LINKER_TRACE=", linkertraces[si])); append(env, strings.concat("WW_VENDOR_REAL_COMPILER=", driver(compilers[si]))); append(env, strings.concat("WW_VENDOR_REAL_ASSEMBLER=", driver(assemblers[si]))); append(env, strings.concat("WW_VENDOR_REAL_LINKER=", driver(linkers[si]))); let out: commandout; let foldtargets: []str = [foldvendorclient, foldvendorreverseclient, foldaliasvendorclient]; let foldlabels: []str = ["distinct", "reverse-imports", "one-dir"]; let foldwants: []str = [ "ww: case-insensitive import collision: \"domain.fold.vendor.lib.Foo\" and \"domain.fold.vendor.lib.foo\"\n", "ww: case-insensitive import collision: \"domain.fold.vendor.lib.Foo\" and \"domain.fold.vendor.lib.foo\"\n", "ww: case-insensitive import collision: \"domain.foldalias.vendor.lib.Foo\" and \"domain.foldalias.vendor.lib.foo\"\n", ]; let fi: i32 = 0; for (fi < foldtargets.len) { rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); let foldwork: str = strings.concat(root, "/vendor-fold-", foldlabels[fi], "-", stages[si], "-work"); let foldout: str = strings.concat(root, "/vendor-fold-", foldlabels[fi], "-", stages[si]); assert(os.mkdir(foldwork, 448i32) == 0); let foldav: []str = [driver(stages[si]), "build", "-w", foldwork, "-I", source, "-o", foldout, foldtargets[fi]]; runcommandenv(root, strings.concat("vendor-fold-", foldlabels[fi], "-", stages[si]), foldav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && same(out.stderr, foldwants[fi])); assert(readfile(compilertraces[si]).len == 0); assert(readfile(assemblertraces[si]).len == 0); assert(readfile(linkertraces[si]).len == 0); assert(!os.exists(foldout)); assert(!os.exists(strings.concat(foldout, ".new"))); assert(!os.exists(strings.concat(foldout, ".sepwork"))); assert(!os.exists(strings.concat(foldwork, "/.wwtool.stamp"))); assert(directoryisempty(foldwork)); if (si == 0) { foldvendorrefs[fi] = strings.dup(out.stderr); } else { assert(same(foldvendorrefs[fi], out.stderr)); }; fi += 1; }; assert(same(foldvendorrefs[0], foldvendorrefs[1])); // A command-global multi-product request is one rejection transaction. // Reversing its descriptors cannot publish the unrelated product or // mutate either pre-existing status. let foldmultiwork: str = strings.concat(root, "/vendor-fold-multi-", stages[si], "-work"); assert(os.mkdir(foldmultiwork, 448i32) == 0); let foldallowedout: str = strings.concat(root, "/vendor-fold-allowed-", stages[si]); let foldcollisionout: str = strings.concat(root, "/vendor-fold-collision-", stages[si]); let foldallowedstatus: str = strings.concat(foldallowedout, ".status"); let foldcollisionstatus: str = strings.concat(foldcollisionout, ".status"); writefile(foldallowedstatus, "sentinel-allowed\n"); writefile(foldcollisionstatus, "sentinel-collision\n"); let foldforward: []str = [driver(stages[si]), "test", "-c", "-w", foldmultiwork, "-I", source, "--ww-package-build", "--ww-package-test", "build", "main", "main", "-", "-", client, foldallowedout, "-", foldallowedstatus, "--ww-package-test", "build", "main", "main", "-", "-", foldvendorclient, foldcollisionout, "-", foldcollisionstatus, client]; let foldreverse: []str = [driver(stages[si]), "test", "-c", "-w", foldmultiwork, "-I", source, "--ww-package-build", "--ww-package-test", "build", "main", "main", "-", "-", foldvendorclient, foldcollisionout, "-", foldcollisionstatus, "--ww-package-test", "build", "main", "main", "-", "-", client, foldallowedout, "-", foldallowedstatus, client]; let foldrequests: [][]str = [foldforward, foldreverse]; let foldmultidiags: []str = ["", ""]; let foldorderlabels: []str = ["forward", "reverse"]; let foi: i32 = 0; for (foi < foldrequests.len) { rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); runcommandenv(root, strings.concat("vendor-fold-multi-", stages[si], "-", foldorderlabels[foi]), foldrequests[foi], env, (180i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && same(out.stderr, foldwants[0])); foldmultidiags[foi] = strings.dup(out.stderr); assert(readfile(compilertraces[si]).len == 0); assert(readfile(assemblertraces[si]).len == 0); assert(readfile(linkertraces[si]).len == 0); assert(same(readfile(foldallowedstatus), "sentinel-allowed\n")); assert(same(readfile(foldcollisionstatus), "sentinel-collision\n")); assert(!os.exists(strings.concat(foldallowedstatus, ".new"))); assert(!os.exists(strings.concat(foldcollisionstatus, ".new"))); assert(!os.exists(foldallowedout)); assert(!os.exists(strings.concat(foldallowedout, ".new"))); assert(!os.exists(strings.concat(foldallowedout, ".sepwork"))); assert(!os.exists(strings.concat(foldallowedout, ".result"))); assert(!os.exists(strings.concat(foldallowedout, ".result.new"))); assert(!os.exists(foldcollisionout)); assert(!os.exists(strings.concat(foldcollisionout, ".new"))); assert(!os.exists(strings.concat(foldcollisionout, ".sepwork"))); assert(!os.exists(strings.concat(foldcollisionout, ".result"))); assert(!os.exists(strings.concat(foldcollisionout, ".result.new"))); assert(directoryisempty(foldmultiwork)); foi += 1; }; assert(same(foldmultidiags[0], foldmultidiags[1])); if (si == 0) { foldvendormultiref = strings.dup(foldmultidiags[0]); } else { assert(same(foldvendormultiref, foldmultidiags[0])); }; let av: []str = [driver(stages[si]), "build", "-w", works[si], "-I", source, "-o", bins[si], client]; runcommandenv(root, strings.concat("vendor-cold-", stages[si]), av, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); let wantnear: str = strings.concat( "//ww:module-reset ", expanded, "\n", neara, "\n", "//ww:module-reset ", expanded, "\n", nearz, "\n", "//ww:vendor-dir ", hexbytes(nearest), "\n"); let nearunit: str = readfile(strings.concat(works[si], "/", expanded, ".unit.ww")); assert(strings.hasprefix(nearunit, wantnear)); assert(has(nearunit, "//ww:direct-export lib.leaf ")); let clientunit: str = readfile(strings.concat(works[si], "/domain.app.client.unit.ww")); assert(has(clientunit, strings.concat("//ww:import-map lib.math ", expanded, " ", hexbytes(nearest), "\n"))); let ai: i32 = 0; for (ai < coreartifacts.len) { let xi: i32 = 0; for (xi < coresuffixes.len) { let bytes: str = readfile(strings.concat(works[si], "/", coreartifacts[ai], coresuffixes[xi])); let index: i32 = ai * coresuffixes.len + xi; if (si == 0) { references[index] = strings.dup(bytes); } else { assert(same(references[index], bytes)); }; xi += 1; }; ai += 1; }; let ctrace: str = readfile(compilertraces[si]); let atrace: str = readfile(assemblertraces[si]); let ltrace: str = readfile(linkertraces[si]); assert(occurrences(ctrace, "\n") == 3); assert(occurrences(atrace, "\n") == 4); assert(occurrences(ltrace, "\n") == 1); assert(has(ltrace, strings.concat("BEGIN<-o><", bins[si], ".new>"))); let dispatchline: str = strings.concat("BEGIN<-o><", works[si], "/domain.app.client.init.o.new><", works[si], "/domain.app.client.init.s.new>"); assert(same(linecontaining(atrace, "/domain.app.client.init.o.new"), dispatchline)); let nearline: str = linecontaining(ctrace, strings.concat(expanded, ".unit.new")); assert(occurrences(nearline, "<--import>") == 1); assert(!has(nearline, "domain.app.client.wwi")); let clientline: str = linecontaining(ctrace, "domain.app.client.unit.new"); assert(occurrences(clientline, strings.concat("<--import><", expanded, "><", works[si], "/", expanded, ".wwi.new>")) == 1); assert(occurrences(clientline, strings.concat( "<--import-map><", expanded, ">")) == 1); assert(!has(clientline, "lib.leaf.wwi")); assert(occurrences(ltrace, strings.concat("<", works[si], "/domain.app.client.a.new>")) == 1); assert(occurrences(ltrace, strings.concat("<", works[si], "/", expanded, ".a.new>")) == 1); assert(occurrences(ltrace, strings.concat("<", works[si], "/lib.leaf.a.new>")) == 1); assert(!has(ltrace, ".wwi>")); let nc: str = normalizedtrace(ctrace, strings.concat(works[si], "/"), bins[si]); let na: str = normalizedtrace(atrace, strings.concat(works[si], "/"), bins[si]); let nl: str = normalizedtrace(ltrace, strings.concat(works[si], "/"), bins[si]); if (si == 0) { referencecompiler = strings.dup(nc); referenceassembler = strings.dup(na); referencelinker = strings.dup(nl); referencebin = strings.dup(readfile(bins[si])); } else { assert(same(referencecompiler, nc)); assert(same(referenceassembler, na)); assert(same(referencelinker, nl)); assert(same(referencebin, readfile(bins[si]))); }; let runav: []str = [bins[si]]; runcommand(root, strings.concat("vendor-run-", stages[si]), runav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 11); let voucher: str = readfile(strings.concat(works[si], "/", expanded, ".unit.ww")); let toolww: str = readfile(strings.concat(works[si], "/.wwtool.ww")); let toolc: str = readfile(strings.concat(works[si], "/.wwtool.w6c")); let toola: str = readfile(strings.concat(works[si], "/.wwtool.w6a")); let stamp: str = readfile(strings.concat(works[si], "/.wwtool.stamp")); rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); runcommandenv(root, strings.concat("vendor-warm-", stages[si]), av, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(readfile(compilertraces[si]).len == 0); assert(readfile(assemblertraces[si]).len == 0); assert(occurrences(readfile(linkertraces[si]), "\n") == 1); rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); let badout: str = strings.concat(root, "/warm-bad-", stages[si]); let badav: []str = [driver(stages[si]), "build", "-w", works[si], "-I", source, "-o", badout, outsider]; runcommandenv(root, strings.concat("vendor-warm-bad-", stages[si]), badav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); let outsidediag: str = strings.concat(outsider, "/main.ww:2:1: error: use of vendored package not allowed\n"); assert(same(out.stderr, outsidediag)); if (si == 0) { referenceoutsidediag = strings.dup(out.stderr); } else { assert(same(referenceoutsidediag, out.stderr)); }; assert(readfile(compilertraces[si]).len == 0); assert(readfile(assemblertraces[si]).len == 0); assert(readfile(linkertraces[si]).len == 0); assert(!os.exists(badout)); assert(same(voucher, readfile(strings.concat(works[si], "/", expanded, ".unit.ww")))); assert(same(toolww, readfile(strings.concat(works[si], "/.wwtool.ww")))); assert(same(toolc, readfile(strings.concat(works[si], "/.wwtool.w6c")))); assert(same(toola, readfile(strings.concat(works[si], "/.wwtool.w6a")))); assert(same(stamp, readfile(strings.concat(works[si], "/.wwtool.stamp")))); ai = 0; for (ai < coreartifacts.len) { let xi: i32 = 0; for (xi < coresuffixes.len) { let index: i32 = ai * coresuffixes.len + xi; assert(same(references[index], readfile(strings.concat( works[si], "/", coreartifacts[ai], coresuffixes[xi])))); xi += 1; }; ai += 1; }; let coldout: str = strings.concat(root, "/cold-bad-", stages[si]); let coldav: []str = [driver(stages[si]), "build", "-I", source, "-o", coldout, outsider]; runcommandenv(root, strings.concat("vendor-cold-bad-", stages[si]), coldav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stderr, outsidediag)); assert(readfile(compilertraces[si]).len == 0); assert(readfile(assemblertraces[si]).len == 0); assert(readfile(linkertraces[si]).len == 0); assert(!os.exists(coldout)); assert(!os.exists(strings.concat(coldout, ".sepwork"))); let directout: str = strings.concat(root, "/direct-bad-", stages[si]); let directav: []str = [driver(stages[si]), "build", "-I", source, "-o", directout, directclient]; runcommandenv(root, strings.concat("vendor-direct-bad-", stages[si]), directav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); let directdiag: str = strings.concat(directclient, "/main.ww:2:1: error: ", expanded, " must be imported as lib.math\n"); assert(same(out.stderr, directdiag)); if (si == 0) { referencedirectdiag = strings.dup(out.stderr); } else { assert(same(referencedirectdiag, out.stderr)); }; assert(readfile(compilertraces[si]).len == 0); assert(readfile(assemblertraces[si]).len == 0); assert(readfile(linkertraces[si]).len == 0); assert(!os.exists(directout)); assert(!os.exists(strings.concat(directout, ".sepwork"))); // The lexical importer is outside domain/app, but the symlink target is // physically inside it. Visibility therefore passes and the later // effective-spelling diagnostic proves physical importer containment. let aliasdirectout: str = strings.concat(root, "/alias-direct-bad-", stages[si]); let aliasdirectav: []str = [driver(stages[si]), "build", "-I", source, "-o", aliasdirectout, aliasdirect]; runcommandenv(root, strings.concat("vendor-alias-direct-bad-", stages[si]), aliasdirectav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); let aliasdirectdiag: str = strings.concat(aliasdirect, "/main.ww:2:1: error: ", expanded, " must be imported as lib.math\n"); assert(same(out.stderr, aliasdirectdiag)); if (si == 0) { referencealiasdirectdiag = strings.dup(out.stderr); } else { assert(same(referencealiasdirectdiag, out.stderr)); }; assert(readfile(compilertraces[si]).len == 0); assert(readfile(assemblertraces[si]).len == 0); assert(readfile(linkertraces[si]).len == 0); assert(!os.exists(aliasdirectout)); assert(!os.exists(strings.concat(aliasdirectout, ".sepwork"))); // Remaining resolution rows reuse the stage workdir, but every output // remains independently executable and compared across stages below. let targets: []str = [outerclient, rootclient, emptyclient, boundaryclient, cleanclient, nestedclient, ordinaryclient, aliasclient, shapeclient]; let roots: []str = [source, source, source, active, cleanroot, source, source, source, source]; let codes: []i32 = [22, 33, 22, 55, 44, 77, 12, 33, 17]; let labels: []str = ["outer", "root", "empty", "boundary", "ordinary", "nested", "names", "alias", "shape"]; let ti: i32 = 0; for (ti < targets.len) { let output: str = strings.concat(root, "/", labels[ti], "-", stages[si]); let tav: []str = [driver(stages[si]), "build", "-w", works[si], "-I", roots[ti], "-o", output, targets[ti]]; runcommandenv(root, strings.concat("vendor-", labels[ti], "-", stages[si]), tav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let rr: []str = [output]; runcommand(root, strings.concat("vendor-run-", labels[ti], "-", stages[si]), rr, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, codes[ti]); if (si == 0) { supplementalrefs[ti] = strings.dup(readfile(output)); } else { assert(same(supplementalrefs[ti], readfile(output))); }; ti += 1; }; let escapedout: str = strings.concat(root, "/escape-bad-", stages[si]); let escapedav: []str = [driver(stages[si]), "build", "-I", source, "-o", escapedout, escapeclient]; runcommandenv(root, strings.concat("vendor-escape-bad-", stages[si]), escapedav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, "use of vendored package not allowed\n")); if (si == 0) { referenceescapeddiag = strings.dup(out.stderr); } else { assert(same(referenceescapeddiag, out.stderr)); }; let prefixout: str = strings.concat(root, "/prefix-bad-", stages[si]); let prefixav: []str = [driver(stages[si]), "build", "-I", source, "-o", prefixout, prefixclient]; runcommandenv(root, strings.concat("vendor-prefix-bad-", stages[si]), prefixav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, "use of vendored package not allowed\n")); if (si == 0) { referenceprefixdiag = strings.dup(out.stderr); } else { assert(same(referenceprefixdiag, out.stderr)); }; let candidateclients: []str = [testonlyclient, bareclient]; let candidatefiles: []str = ["only_test.ww", ".ww"]; let ci: i32 = 0; for (ci < candidateclients.len) { let candidateout: str = strings.concat(root, "/candidate-bad-", stages[si], "-", candidatefiles[ci]); let cav: []str = [driver(stages[si]), "build", "-I", source, "-o", candidateout, candidateclients[ci]]; runcommandenv(root, strings.concat("vendor-candidate-bad-", stages[si], "-", candidatefiles[ci]), cav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, "/vendor/lib/math: directory contains no WW package sources\n")); assert(!os.exists(strings.concat(candidateout, ".sepwork"))); if (si == 0) { candidate_diag_refs[ci] = strings.dup(out.stderr); } else { assert(same(candidate_diag_refs[ci], out.stderr)); }; ci += 1; }; let selected: str = strings.concat(root, "/selected-", stages[si], ".a"); let selectedav: []str = [driver(stages[si]), "build", "-I", source, "-o", selected, nearest]; runcommandenv(root, strings.concat("vendor-selected-", stages[si]), selectedav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(references[1 * coresuffixes.len + 4], readfile(selected))); assert(same(references[1 * coresuffixes.len + 1], readfile(strings.concat(selected, ".wwi")))); // One command-global union shares one vendored action between three // importers, keeps an outer physical copy distinct, and also keeps two // expanded identities distinct when their lexical vendor routes converge // through symlinks on one physical directory. rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); let combinedwork: str = strings.concat(root, "/combined-work-", stages[si]); assert(os.mkdir(combinedwork, 448i32) == 0); let combinedtargets: []str = [client, one, two, outerclient, alpha, beta, pairvault]; let combinedpackages: []str = ["main", "main", "main", "main", "main", "main", "math"]; let combinedcodes: []i32 = [11, 11, 11, 22, 61, 61]; let combinedoutputs: []str = [ strings.concat(root, "/combined-client-", stages[si]), strings.concat(root, "/combined-one-", stages[si]), strings.concat(root, "/combined-two-", stages[si]), strings.concat(root, "/combined-outer-", stages[si]), strings.concat(root, "/combined-alpha-", stages[si]), strings.concat(root, "/combined-beta-", stages[si]), strings.concat(root, "/combined-local-", stages[si])]; let combinedstatuses: []str = [ strings.concat(combinedoutputs[0], ".status"), strings.concat(combinedoutputs[1], ".status"), strings.concat(combinedoutputs[2], ".status"), strings.concat(combinedoutputs[3], ".status"), strings.concat(combinedoutputs[4], ".status"), strings.concat(combinedoutputs[5], ".status"), strings.concat(combinedoutputs[6], ".status")]; let combinedav: []str = alloc([], (9 + combinedtargets.len * 10): u64)!; append(combinedav, driver(stages[si])); append(combinedav, "test"); append(combinedav, "-c"); append(combinedav, "-w"); append(combinedav, combinedwork); append(combinedav, "-I"); append(combinedav, source); append(combinedav, "--ww-package-build"); let ui: i32 = 0; for (ui < combinedtargets.len) { append(combinedav, "--ww-package-test"); append(combinedav, "build"); append(combinedav, combinedpackages[ui]); append(combinedav, combinedpackages[ui]); append(combinedav, "-"); append(combinedav, "-"); append(combinedav, combinedtargets[ui]); append(combinedav, combinedoutputs[ui]); append(combinedav, "-"); append(combinedav, combinedstatuses[ui]); ui += 1; }; append(combinedav, client); runcommandenv(root, strings.concat("vendor-combined-", stages[si]), combinedav, env, (180i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); let combinedtrace: str = readfile(compilertraces[si]); assert(occurrences(combinedtrace, strings.concat("/", expanded, ".unit.new")) == 1); assert(occurrences(combinedtrace, "/domain.vendor.lib.math.unit.new") == 1); assert(occurrences(combinedtrace, "/alpha.client.vendor.lib.math.unit.new") == 1); assert(occurrences(combinedtrace, "/beta.client.vendor.lib.math.unit.new") == 1); assert(has(combinedtrace, localidentity(pairvault, "math"))); assert(occurrences(combinedtrace, "/lib.leaf.unit.new") == 1); let combinedidentities: []str = [expanded, "alpha.client.vendor.lib.math", "beta.client.vendor.lib.math"]; let combinedsuffixes: []str = [".wwi", ".a"]; ui = 0; for (ui < combinedidentities.len) { let xi: i32 = 0; for (xi < combinedsuffixes.len) { let bytes: str = readfile(strings.concat(combinedwork, "/", combinedidentities[ui], combinedsuffixes[xi])); let index: i32 = ui * combinedsuffixes.len + xi; if (si == 0) { combinedrefs[index] = strings.dup(bytes); } else { assert(same(combinedrefs[index], bytes)); }; xi += 1; }; ui += 1; }; assert(os.exists(strings.concat(combinedwork, "/domain.vendor.lib.math.wwi"))); assert(os.exists(strings.concat(combinedwork, "/domain.vendor.lib.math.a"))); ui = 0; for (ui < combinedcodes.len) { let cav: []str = [combinedoutputs[ui]]; runcommand(root, strings.concat("vendor-combined-run-", stages[si], "-", labels[ui]), cav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, combinedcodes[ui]); ui += 1; }; assert(!os.exists(combinedoutputs[6])); assert(same(readfile(combinedstatuses[6]), "ok\n")); // A target interned by the allowed product is still checked at the // forbidden source edge. Reversing product descriptors changes neither // the diagnostic nor any status/tool state. let orderwork: str = strings.concat(root, "/vendor-order-work-", stages[si]); assert(os.mkdir(orderwork, 448i32) == 0); let orderallowed: str = strings.concat(root, "/vendor-order-allowed-", stages[si]); let orderforbidden: str = strings.concat(root, "/vendor-order-forbidden-", stages[si]); let orderallowedstatus: str = strings.concat(orderallowed, ".status"); let orderforbiddenstatus: str = strings.concat(orderforbidden, ".status"); writefile(orderallowedstatus, "sentinel-allowed\n"); writefile(orderforbiddenstatus, "sentinel-forbidden\n"); let orderforward: []str = [driver(stages[si]), "test", "-c", "-w", orderwork, "-I", source, "--ww-package-build", "--ww-package-test", "build", "main", "main", "-", "-", client, orderallowed, "-", orderallowedstatus, "--ww-package-test", "build", "main", "main", "-", "-", outsider, orderforbidden, "-", orderforbiddenstatus, client]; let orderreverse: []str = [driver(stages[si]), "test", "-c", "-w", orderwork, "-I", source, "--ww-package-build", "--ww-package-test", "build", "main", "main", "-", "-", outsider, orderforbidden, "-", orderforbiddenstatus, "--ww-package-test", "build", "main", "main", "-", "-", client, orderallowed, "-", orderallowedstatus, client]; let orderrequests: [][]str = [orderforward, orderreverse]; let orderdiags: []str = ["", ""]; let oi: i32 = 0; for (oi < orderrequests.len) { rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); runcommandenv(root, strings.concat("vendor-order-", stages[si], "-", labels[oi]), orderrequests[oi], env, (180i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stderr, outsidediag)); orderdiags[oi] = strings.dup(out.stderr); assert(readfile(compilertraces[si]).len == 0); assert(readfile(assemblertraces[si]).len == 0); assert(readfile(linkertraces[si]).len == 0); assert(same(readfile(orderallowedstatus), "sentinel-allowed\n")); assert(same(readfile(orderforbiddenstatus), "sentinel-forbidden\n")); assert(!os.exists(strings.concat(orderwork, "/.wwtool.stamp"))); assert(!os.exists(orderallowed) && !os.exists(orderforbidden)); oi += 1; }; assert(same(orderdiags[0], orderdiags[1])); if (si == 0) { referenceorderdiag = strings.dup(orderdiags[0]); } else { assert(same(referenceorderdiag, orderdiags[0])); }; // Production, same-package test, and external test sources all retain // their short spelling while consuming the one expanded direct export. let testworkroot: str = strings.concat(root, "/vendor-test-work-", stages[si]); assert(os.mkdir(testworkroot, 448i32) == 0); rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); let testav: []str = [driver(stages[si]), "test", "-w", testworkroot, "-I", source, testclient]; runcommandenv(root, strings.concat("vendor-tests-", stages[si]), testav, env, (180i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "same_vendor ... ok\n")); assert(has(out.stdout, "external_vendor ... ok\n")); if (si == 0) { referencetestout = strings.dup(out.stdout); referencetesterr = strings.dup(out.stderr); } else { assert(same(referencetestout, out.stdout)); assert(same(referencetesterr, out.stderr)); }; let testwork: str = strings.concat(testworkroot, "/"); let testactions: []str = ["domain.app.testclient-internal-test", "domain.app.testclient_test-external-test"]; let testsuffixes: []str = [".unit.ww", ".wwi", ".a"]; ui = 0; for (ui < testactions.len) { let xi: i32 = 0; for (xi < testsuffixes.len) { let bytes: str = readfile(strings.concat(testwork, testactions[ui], testsuffixes[xi])); let index: i32 = ui * testsuffixes.len + xi; if (si == 0) { testrefs[index] = strings.dup(bytes); } else { assert(same(testrefs[index], bytes)); }; xi += 1; }; assert(has(readfile(strings.concat(testwork, testactions[ui], ".unit.ww")), strings.concat("//ww:import-map lib.math ", expanded, " ", hexbytes(nearest), "\n"))); ui += 1; }; let testtrace: str = readfile(compilertraces[si]); assert(occurrences(testtrace, strings.concat("/", expanded, ".unit.new")) == 1); let internalline: str = linecontaining(testtrace, "domain.app.testclient-internal-test.unit.new"); let externalline: str = linecontaining(testtrace, "domain.app.testclient_test-external-test.unit.new"); assert(occurrences(internalline, strings.concat("<--import><", expanded, "><", testwork, expanded, ".wwi.new>")) == 1); assert(occurrences(externalline, strings.concat("<--import><", expanded, "><", testwork, expanded, ".wwi.new>")) == 1); assert(occurrences(internalline, strings.concat( "<--import-map><", expanded, ">")) == 1); assert(occurrences(externalline, strings.concat( "<--import-map><", expanded, ">")) == 1); assert(occurrences(externalline, strings.concat( "<--import><", testwork, "domain.app.testclient-internal-test.wwi.new>")) == 1); assert(!has(internalline, "lib.leaf.wwi")); assert(!has(externalline, "lib.leaf.wwi")); let mainline: str = linecontaining(testtrace, "domain.app.testclient-test-main.unit.new"); assert(!has(mainline, expanded)); assert(occurrences(mainline, "<--test-target-package>") == 2); assert(pos(mainline, "<--test-target-package>") < pos(mainline, "<--test-target-package>")); assert(has(mainline, strings.concat( "<--import><", testwork, "domain.app.testclient-internal-test.wwi.new>"))); assert(has(mainline, strings.concat( "<--import><", testwork, "domain.app.testclient_test-external-test.wwi.new>"))); let testlinktrace: str = readfile(linkertraces[si]); assert(occurrences(testlinktrace, "\n") == 1); assert(has(testlinktrace, "/domain.app.testclient-test-main.a.new>")); assert(!has(testlinktrace, ".wwi>")); rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); runcommandenv(root, strings.concat("vendor-tests-warm-", stages[si]), testav, env, (180i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(referencetestout, out.stdout)); assert(same(referencetesterr, out.stderr)); assert(readfile(compilertraces[si]).len == 0); assert(readfile(assemblertraces[si]).len == 0); assert(occurrences(readfile(linkertraces[si]), "\n") == 1); // A directly selected vendored test root is legal, and its external // source still performs the ordinary nearest-vendor search first. rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); let vendortestav: []str = [driver(stages[si]), "test", "-I", source, vendortest]; runcommandenv(root, strings.concat("vendor-literal-test-", stages[si]), vendortestav, env, (180i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "nested_external_vendor ... ok\n")); if (si == 0) { referencevendortestout = strings.dup(out.stdout); referencevendortesterr = strings.dup(out.stderr); } else { assert(same(referencevendortestout, out.stdout)); assert(same(referencevendortesterr, out.stderr)); }; assert(has(readfile(compilertraces[si]), "literal.owner.vendor.lib.math.vendor.lib.math.unit.ww")); // A cold forbidden public test request is rejected before any // persistent tool or package state is written. let testbadworkroot: str = strings.concat(root, "/vendor-test-bad-work-", stages[si]); assert(os.mkdir(testbadworkroot, 448i32) == 0); rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); let testbadav: []str = [driver(stages[si]), "test", "-w", testbadworkroot, "-I", source, testbad]; runcommandenv(root, strings.concat("vendor-test-bad-", stages[si]), testbadav, env, (180i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); let testbaddiag: str = strings.concat(testbad, "/same_test.ww:2:1: error: use of vendored package not allowed\n"); assert(has(out.stderr, testbaddiag)); if (si == 0) { referencetestbaddiag = strings.dup(out.stderr); } else { assert(same(referencetestbaddiag, out.stderr)); }; assert(!os.exists(strings.concat(testbadworkroot, "/.wwtool.stamp"))); assert(readfile(compilertraces[si]).len == 0); assert(readfile(assemblertraces[si]).len == 0); assert(readfile(linkertraces[si]).len == 0); // The coordinator's synthetic support/main edges stay synthetic, while // imports written in the real test-support source use this same resolver. let supportenv: []str = alloc([], (env.len + 1): u64)!; ei = 0; for (ei < env.len) { append(supportenv, env[ei]); ei += 1; }; append(supportenv, strings.concat("WW_SRCLIB=", toolsource)); let supportworkroot: str = strings.concat(root, "/vendor-support-work-", stages[si]); assert(os.mkdir(supportworkroot, 448i32) == 0); rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], ""); rewritefile(linkertraces[si], ""); let supportav: []str = [driver(stages[si]), "test", "-w", supportworkroot, "-I", source, supportcase]; runcommandenv(root, strings.concat("vendor-support-", stages[si]), supportav, supportenv, (240i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "support_runs ... ok\n")); assert(out.stderr.len == 0); if (si == 0) { referencesupportout = strings.dup(out.stdout); } else { assert(same(referencesupportout, out.stdout)); }; let supportwork: str = strings.concat(supportworkroot, "/"); let supportunit: str = readfile(strings.concat(supportwork, "test.unit.ww")); assert(has(supportunit, strings.concat("//ww:import-map fnmatch ", "test.vendor.fnmatch ", hexbytes(toolvendor), "\n"))); assert(has(readfile(strings.concat(supportwork, "test.vendor.fnmatch.unit.ww")), strings.concat( "//ww:vendor-dir ", hexbytes(toolvendor), "\n"))); let supporttrace: str = readfile(compilertraces[si]); let supportline: str = linecontaining(supporttrace, "/test.unit.new"); assert(occurrences(supportline, "<--import>") == 1); assert(occurrences(supportline, "<--import-map>") == 1); assert(!has(supportline, "/ascii.wwi>")); assert(occurrences(supporttrace, "/test.vendor.fnmatch.unit.new") == 1); let supportmainline: str = linecontaining(supporttrace, "supportcase-test-main.unit.new"); assert(!has(supportmainline, "test.vendor.fnmatch")); assert(!has(readfile(linkertraces[si]), ".wwi>")); let supportsuffixes: []str = [".unit.ww", ".wwi", ".a"]; ui = 0; for (ui < supportsuffixes.len) { let bytes: str = readfile(strings.concat(supportwork, "test.vendor.fnmatch", supportsuffixes[ui])); if (si == 0) { supportrefs[ui] = strings.dup(bytes); } else { assert(same(supportrefs[ui], bytes)); }; ui += 1; }; si += 1; }; clean(root); }; // The package-level -o contract: -c -o names the single package's // artifact in place of the fixed .test stem; -o without -c // has nothing to name (runs execute from the temp root); one name // cannot fan out over multiple packages (Go's `go test -o` rule). @test fn declared_name_identity_and_file_import_scope() void = { let root: str = fresh(); let source: str = strings.concat(root, "/source"); let tools: str = strings.concat(root, "/tools"); let codec: str = strings.concat(source, "/acme/codec"); let bridge: str = strings.concat(source, "/acme/bridge"); let direct: str = strings.concat(source, "/cmd/direct"); let app: str = strings.concat(source, "/cmd/app"); let scopeone: str = strings.concat(source, "/scope/one"); let scopetwo: str = strings.concat(source, "/scope/two"); let scoped: str = strings.concat(source, "/cmd/scoped"); let repeated: str = strings.concat(source, "/cmd/repeated"); let commitcodec: str = strings.concat(source, "/commit/codec"); let commitapp: str = strings.concat(source, "/cmd/commit"); let leafbad: str = strings.concat(source, "/cmd/leafbad"); let leak: str = strings.concat(source, "/cmd/leak"); let duplicate: str = strings.concat(source, "/cmd/duplicate"); let collision: str = strings.concat(source, "/cmd/collision"); let crosscollision: str = strings.concat(source, "/cmd/crosscollision"); let conflict: str = strings.concat(source, "/conflict/pkg"); let leafmain: str = strings.concat(source, "/domain/main"); let program: str = strings.concat(source, "/domain/program"); let leafmainuser: str = strings.concat(source, "/cmd/leafmain"); let programuser: str = strings.concat(source, "/cmd/programuser"); let testcodec: str = strings.concat(source, "/testpkg/codec"); let testhelper: str = strings.concat(source, "/testonly/helper"); let vendored: str = strings.concat(source, "/vend/vendor/short/codec"); let vendorclient: str = strings.concat(source, "/vend/client"); let dirs: []str = [source, tools, codec, bridge, direct, app, scopeone, scopetwo, scoped, repeated, commitcodec, commitapp, leafbad, leak, duplicate, collision, crosscollision, conflict, leafmain, program, leafmainuser, programuser, testcodec, testhelper, vendored, vendorclient]; let di: i32 = 0; for (di < dirs.len) { mkdirall(dirs[di]); di += 1; }; let wirebody: str = strings.concat( "package wire;\n", "export type Thing = i32;\n", "export fn value() i32 = { return 42; };\n"); let cablebody: str = strings.concat( "package cable;\n", "export type Thing = i32;\n", "export fn value() i32 = { return 42; };\n"); let codecfile: str = strings.concat(codec, "/codec.ww"); writefile(codecfile, wirebody); let commitcodecfile: str = strings.concat(commitcodec, "/codec.ww"); let commitappfile: str = strings.concat(commitapp, "/main.ww"); writefile(commitcodecfile, wirebody); writefile(commitappfile, strings.concat( "package main;\nimport commit.codec;\n", "fn main() i32 = { return wire.value(); };\n")); let bridgefile: str = strings.concat(bridge, "/bridge.ww"); let stablebridgebody: str = strings.concat( "package bridge;\nimport stable acme.codec;\n", "export type BridgedThing = stable.Thing;\n", "export fn bridged() i32 = { return stable.value(); };\n"); let steadybridgebody: str = strings.concat( "package bridge;\nimport steady acme.codec;\n", "export type BridgedThing = steady.Thing;\n", "export fn bridged() i32 = { return steady.value(); };\n"); writefile(bridgefile, stablebridgebody); writefile(strings.concat(direct, "/main.ww"), strings.concat( "package main;\nimport acme.codec;\n", "fn main() i32 = { return wire.value(); };\n")); writefile(strings.concat(app, "/main.ww"), strings.concat( "package main;\nimport acme.bridge;\n", "fn main() i32 = { return bridge.bridged(); };\n")); writefile(strings.concat(scopeone, "/one.ww"), "package same;\nexport fn value() i32 = { return 20; };\n"); writefile(strings.concat(scopetwo, "/two.ww"), "package same;\nexport fn value() i32 = { return 22; };\n"); writefile(strings.concat(scoped, "/a.ww"), strings.concat( "package main;\nimport shared scope.one;\n", "fn left() i32 = { return shared.value(); };\n")); writefile(strings.concat(scoped, "/b.ww"), strings.concat( "package main;\nimport shared scope.two;\n", "fn right() i32 = { return shared.value(); };\n")); writefile(strings.concat(scoped, "/main.ww"), "package main;\nfn main() i32 = { return left() + right(); };\n"); writefile(strings.concat(repeated, "/a.ww"), strings.concat( "package main;\nimport stable acme.codec;\n", "fn first() i32 = { return stable.value(); };\n")); writefile(strings.concat(repeated, "/b.ww"), strings.concat( "package main;\nimport acme.codec;\n", "fn second() i32 = { return wire.value(); };\n")); writefile(strings.concat(repeated, "/main.ww"), strings.concat( "package main;\n", "fn main() i32 = { return first() - second(); };\n")); writefile(strings.concat(leafbad, "/main.ww"), strings.concat( "package main;\nimport acme.codec;\n", "fn main() i32 = { return codec.value(); };\n")); writefile(strings.concat(leak, "/a.ww"), strings.concat( "package main;\nimport acme.codec;\n", "fn anchor() i32 = { return 1; };\n")); writefile(strings.concat(leak, "/b.ww"), "package main;\nfn leaked() i32 = { return wire.value(); };\n"); writefile(strings.concat(leak, "/main.ww"), "package main;\nfn main() i32 = { return leaked(); };\n"); writefile(strings.concat(duplicate, "/main.ww"), strings.concat( "package main;\nimport scope.one;\nimport scope.two;\n", "fn main() i32 = { return same.value(); };\n")); writefile(strings.concat(collision, "/main.ww"), strings.concat( "package main;\nimport scope.one;\n", "fn same() i32 = { return 0; };\n", "fn main() i32 = { return same(); };\n")); writefile(strings.concat(crosscollision, "/a.ww"), strings.concat( "package main;\nimport scope.one;\n", "fn fromdep() i32 = { return same.value(); };\n")); writefile(strings.concat(crosscollision, "/b.ww"), strings.concat( "package main;\nfn same() i32 = { return 0; };\n", "fn main() i32 = { return fromdep(); };\n")); writefile(strings.concat(conflict, "/a.ww"), "package first;\nexport fn a() i32 = { return 1; };\n"); writefile(strings.concat(conflict, "/b.ww"), "package second;\nexport fn b() i32 = { return 2; };\n"); writefile(strings.concat(leafmain, "/utility.ww"), "package utility;\nexport fn value() i32 = { return 7; };\n"); writefile(strings.concat(program, "/program.ww"), "package main;\nexport fn value() i32 = { return 9; };\n"); writefile(strings.concat(leafmainuser, "/main.ww"), strings.concat( "package main;\nimport domain.main;\n", "fn main() i32 = { return utility.value(); };\n")); writefile(strings.concat(programuser, "/main.ww"), strings.concat( "package main;\nimport stable domain.program;\n", "fn main() i32 = { return stable.value(); };\n")); writefile(strings.concat(testhelper, "/helper.ww"), "package helper;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(testcodec, "/codec.ww"), wirebody); writefile(strings.concat(testcodec, "/internal_test.ww"), strings.concat( "package wire;\nimport assist testonly.helper;\n", "@test fn internal_name_uses_declaration() void = {\n", " assert(value() + assist.value() == 43);\n};\n")); writefile(strings.concat(testcodec, "/external_test.ww"), strings.concat( "package wire_test;\nimport prod testpkg.codec;\n", "@test fn external_name_uses_declaration() void = {\n", " assert(prod.value() == 42);\n};\n")); writefile(strings.concat(vendored, "/codec.ww"), "package cable;\nexport fn value() i32 = { return 13; };\n"); writefile(strings.concat(vendorclient, "/a.ww"), strings.concat( "package main;\nimport stable short.codec;\n", "fn vendora() i32 = { return stable.value(); };\n")); writefile(strings.concat(vendorclient, "/b.ww"), strings.concat( "package main;\nimport short.codec;\n", "fn vendorb() i32 = { return cable.value(); };\n")); writefile(strings.concat(vendorclient, "/main.ww"), "package main;\nfn main() i32 = { return vendora(); };\n"); let compilerwrapper: str = strings.concat(tools, "/w6c.sh"); writeexecutable(compilerwrapper, strings.concat( "#!/bin/sh\n", "printf 'BEGIN' >> \"$WW_NAME_COMPILER_TRACE\"\n", "for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ", "\"$WW_NAME_COMPILER_TRACE\"; done\n", "printf '\\n' >> \"$WW_NAME_COMPILER_TRACE\"\n", "exec \"$WW_NAME_REAL_COMPILER\" \"$@\"\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 works: []str = [strings.concat(root, "/c-work"), strings.concat(root, "/ww-work")]; let traces: []str = [strings.concat(root, "/c-compiler"), strings.concat(root, "/ww-compiler")]; let directbins: []str = [strings.concat(root, "/c-direct"), strings.concat(root, "/ww-direct")]; let appbins: []str = [strings.concat(root, "/c-app"), strings.concat(root, "/ww-app")]; let scopedbins: []str = [strings.concat(root, "/c-scoped"), strings.concat(root, "/ww-scoped")]; let repeatedbins: []str = [strings.concat(root, "/c-repeated"), strings.concat(root, "/ww-repeated")]; let commitworks: []str = [strings.concat(root, "/c-commit-work"), strings.concat(root, "/ww-commit-work")]; let commitcoldbins: []str = [strings.concat(root, "/c-commit-cold"), strings.concat(root, "/ww-commit-cold")]; let commitrejectbins: []str = [strings.concat(root, "/c-commit-reject"), strings.concat(root, "/ww-commit-reject")]; let commitfixedbins: []str = [strings.concat(root, "/c-commit-fixed"), strings.concat(root, "/ww-commit-fixed")]; let leafmainbins: []str = [strings.concat(root, "/c-leafmain"), strings.concat(root, "/ww-leafmain")]; let vendorbins: []str = [strings.concat(root, "/c-vendor"), strings.concat(root, "/ww-vendor")]; let actions: []str = ["acme.codec", "acme.bridge", "cmd.direct", "cmd.app", "scope.one", "scope.two", "cmd.scoped", "cmd.repeated", "domain.main", "cmd.leafmain", "vend.vendor.short.codec", "vend.client"]; let suffixes: []str = [".unit.ww", ".wwi", ".a"]; let artifactrefs: []str = alloc([], (actions.len * suffixes.len): u64)!; let ari: i32 = 0; for (ari < actions.len * suffixes.len) { append(artifactrefs, ""); ari += 1; }; let binaryrefs: []str = ["", "", "", "", "", ""]; let diagnosticrefs: []str = ["", "", "", "", "", ""]; let directtraceref: str = ""; let scopedtraceref: str = ""; let repeatedtraceref: str = ""; let changedtraceref: str = ""; let aliastraceref: str = ""; let commitbindingref: str = ""; let commitbinref: str = ""; let initialwwiref: str = ""; let testoutref: str = ""; let baseenv: []str = os.getenvs(); let si: i32 = 0; for (si < stages.len) { rewritefile(codecfile, wirebody); rewritefile(bridgefile, stablebridgebody); rewritefile(commitcodecfile, wirebody); rewritefile(commitappfile, strings.concat( "package main;\nimport commit.codec;\n", "fn main() i32 = { return wire.value(); };\n")); assert(os.mkdir(works[si], 448i32) == 0); assert(os.mkdir(commitworks[si], 448i32) == 0); writefile(traces[si], ""); let env: []str = alloc([], (baseenv.len + 7): 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_SRCLIB=") && !strings.hasprefix(baseenv[ei], "WW_NAME_COMPILER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_NAME_REAL_COMPILER=")) { append(env, baseenv[ei]); }; ei += 1; }; append(env, strings.concat("WW_W6C=", compilerwrapper)); append(env, strings.concat("WW_W6A=", driver(assemblers[si]))); append(env, strings.concat("WW_W6L=", driver(linkers[si]))); append(env, strings.concat("WW_SRCLIB=", repo(), "/lib")); append(env, strings.concat("WW_NAME_COMPILER_TRACE=", traces[si])); append(env, strings.concat("WW_NAME_REAL_COMPILER=", driver(compilers[si]))); // A directly selected library keeps canonical action/artifact identity // even though its declaration is unrelated to the path leaf. let out: commandout; let selectav: []str = [driver(stages[si]), "build", "-w", works[si], "-I", source, codec]; runcommandenv(root, strings.concat("name-select-", stages[si]), selectav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(os.exists(strings.concat(works[si], "/acme.codec.wwi"))); assert(!os.exists(strings.concat(works[si], "/wire.wwi"))); let initialwwi: str = readfile(strings.concat(works[si], "/acme.codec.wwi")); assert(has(initialwwi, "//ww:module acme.codec\npackage wire;\n")); if (si == 0) { initialwwiref = strings.dup(initialwwi); } else { assert(same(initialwwiref, initialwwi)); }; rewritefile(traces[si], ""); let directav: []str = [driver(stages[si]), "build", "-w", works[si], "-I", source, "-o", directbins[si], direct]; runcommandenv(root, strings.concat("name-direct-", stages[si]), directav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); let directtrace: str = readfile(traces[si]); let directline: str = linecontaining(directtrace, "cmd.direct.unit.new"); assert(occurrences(directline, "<--import>") == 1); assert(!has(directline, "wire.wwi")); let ndirect: str = normalizedtrace(directtrace, strings.concat(works[si], "/"), directbins[si]); if (si == 0) { directtraceref = strings.dup(ndirect); } else { assert(same(directtraceref, ndirect)); }; let runav: []str = [directbins[si]]; runcommand(root, strings.concat("name-direct-run-", stages[si]), runav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 42); // The package and command form one publication transaction. A changed // declared name is visible to the importer through the staged .wwi, but // its resulting semantic rejection must preserve the complete prior // generation and force the next request to reconsider both actions. let commitcoldav: []str = [driver(stages[si]), "build", "-w", commitworks[si], "-I", source, "-o", commitcoldbins[si], commitapp]; runcommandenv(root, strings.concat("name-commit-cold-", stages[si]), commitcoldav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let commitrun: []str = [commitcoldbins[si]]; runcommand(root, strings.concat("name-commit-cold-run-", stages[si]), commitrun, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 42); let commitpaths: []str = ["/.wwtool.ww", "/.wwtool.w6c", "/.wwtool.w6a", "/.wwtool.stamp", "/commit.codec.unit.ww", "/commit.codec.wwi", "/commit.codec.s", "/commit.codec.o", "/commit.codec.a", "/cmd.commit.unit.ww", "/cmd.commit.wwi", "/cmd.commit.s", "/cmd.commit.o", "/cmd.commit.a", "/cmd.commit.init.unit.ww", "/cmd.commit.init.s", "/cmd.commit.init.o"]; let commitsnapshots: []str = alloc([], commitpaths.len: u64)!; let cpi: i32 = 0; for (cpi < commitpaths.len) { append(commitsnapshots, readfile(strings.concat(commitworks[si], commitpaths[cpi]))); cpi += 1; }; let commitcoldbytes: str = readfile(commitcoldbins[si]); rewritefile(commitcodecfile, cablebody); let commitrejectav: []str = [driver(stages[si]), "build", "-w", commitworks[si], "-I", source, "-o", commitrejectbins[si], commitapp]; let partialsuffixes: []str = [".unit.new", ".wwi.new", ".s.new", ".o.new", ".a.new", ".init.unit.new", ".init.s.new", ".init.o.new"]; let rejectedactions: []str = ["commit.codec", "cmd.commit"]; let attemptlabels: []str = ["0", "1"]; let attempt: i32 = 0; for (attempt < 2) { rewritefile(traces[si], ""); runcommandenv(root, strings.concat("name-commit-reject-", stages[si], "-", attemptlabels[attempt]), commitrejectav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, "imported as cable and not used")); assert(has(out.stderr, "undefined: wire")); assert(has(out.stderr, "ww: w6c failed for cmd.commit\n")); let ncommitbinding: str = normalizedtrace(out.stderr, strings.concat(commitworks[si], "/"), commitrejectbins[si]); if (si == 0 && attempt == 0) { commitbindingref = strings.dup(ncommitbinding); } else { assert(same(commitbindingref, ncommitbinding)); }; let rejecttrace: str = readfile(traces[si]); assert(occurrences(rejecttrace, "/commit.codec.unit.new") == 1); assert(occurrences(rejecttrace, "/cmd.commit.unit.new") == 1); cpi = 0; for (cpi < commitpaths.len) { assert(same(commitsnapshots[cpi], readfile(strings.concat( commitworks[si], commitpaths[cpi])))); cpi += 1; }; assert(same(commitcoldbytes, readfile(commitcoldbins[si]))); let rai: i32 = 0; for (rai < rejectedactions.len) { let psi: i32 = 0; for (psi < partialsuffixes.len) { assert(!os.exists(strings.concat(commitworks[si], "/", rejectedactions[rai], partialsuffixes[psi]))); psi += 1; }; rai += 1; }; assert(!os.exists(commitrejectbins[si])); assert(!os.exists(strings.concat(commitrejectbins[si], ".new"))); attempt += 1; }; rewritefile(commitappfile, strings.concat( "package main;\nimport commit.codec;\n", "fn main() i32 = { return cable.value(); };\n")); let commitfixedav: []str = [driver(stages[si]), "build", "-w", commitworks[si], "-I", source, "-o", commitfixedbins[si], commitapp]; runcommandenv(root, strings.concat("name-commit-fixed-", stages[si]), commitfixedav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let commitfixedrun: []str = [commitfixedbins[si]]; runcommand(root, strings.concat("name-commit-fixed-run-", stages[si]), commitfixedrun, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 42); assert(has(readfile(strings.concat(commitworks[si], "/commit.codec.wwi")), "//ww:module commit.codec\npackage cable;\n")); assert(same(commitcoldbytes, readfile(commitcoldbins[si]))); let commitbinbytes: str = readfile(commitfixedbins[si]); if (si == 0) { commitbinref = strings.dup(commitbinbytes); } else { assert(same(commitbinref, commitbinbytes)); }; // The same declared qualifier belongs to each importing source file, // not to the package-wide namespace. rewritefile(traces[si], ""); let scopedav: []str = [driver(stages[si]), "build", "-w", works[si], "-I", source, "-o", scopedbins[si], scoped]; runcommandenv(root, strings.concat("name-scoped-", stages[si]), scopedav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let scopedtrace: str = readfile(traces[si]); let scopedline: str = linecontaining(scopedtrace, "cmd.scoped.unit.new"); assert(occurrences(scopedline, "<--import>") == 1); assert(occurrences(scopedline, "<--import>") == 1); let nscoped: str = normalizedtrace(scopedtrace, strings.concat(works[si], "/"), scopedbins[si]); if (si == 0) { scopedtraceref = strings.dup(nscoped); } else { assert(same(scopedtraceref, nscoped)); }; let scopedrun: []str = [scopedbins[si]]; runcommand(root, strings.concat("name-scoped-run-", stages[si]), scopedrun, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 42); // Two per-file binding records for one canonical package still form // exactly one direct graph edge and one compiler input. rewritefile(traces[si], ""); let repeatedav: []str = [driver(stages[si]), "build", "-w", works[si], "-I", source, "-o", repeatedbins[si], repeated]; runcommandenv(root, strings.concat("name-repeated-", stages[si]), repeatedav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let repeatedunit: str = readfile(strings.concat(works[si], "/cmd.repeated.unit.ww")); assert(occurrences(repeatedunit, "import stable acme.codec;") == 1); assert(occurrences(repeatedunit, "import acme.codec;") == 1); let repeatedtrace: str = readfile(traces[si]); let repeatedline: str = linecontaining(repeatedtrace, "cmd.repeated.unit.new"); assert(occurrences(repeatedline, "<--import>") == 1); assert(occurrences(repeatedtrace, "acme.codec.unit.new") == 0); let nrepeated: str = normalizedtrace(repeatedtrace, strings.concat(works[si], "/"), repeatedbins[si]); if (si == 0) { repeatedtraceref = strings.dup(nrepeated); } else { assert(same(repeatedtraceref, nrepeated)); }; let repeatedrun: []str = [repeatedbins[si]]; runcommand(root, strings.concat("name-repeated-run-", stages[si]), repeatedrun, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); // A path ending in main remains importable when its declaration is not // main. A different path declaring main is rejected by loaded name. let leafmainav: []str = [driver(stages[si]), "build", "-w", works[si], "-I", source, "-o", leafmainbins[si], leafmainuser]; runcommandenv(root, strings.concat("name-leafmain-", stages[si]), leafmainav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let leafmainrun: []str = [leafmainbins[si]]; runcommand(root, strings.concat("name-leafmain-run-", stages[si]), leafmainrun, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 7); // Compiler-owned binding failures are deterministic across stages and // never commit an action voucher/artifact or publish a binary. let rejectwork: str = strings.concat(root, "/reject-work-", stages[si]); assert(os.mkdir(rejectwork, 448i32) == 0); let rejecttargets: []str = [leafbad, leak, duplicate, collision, crosscollision, programuser]; let rejectactions: []str = ["cmd.leafbad", "cmd.leak", "cmd.duplicate", "cmd.collision", "cmd.crosscollision", "cmd.programuser"]; let rejectneedles: []str = ["undefined: codec", "undefined: wire", "same redeclared in this block", "same already declared through import of package same", "same already declared through import of package same", "package domain.program is a program, not an importable package"]; let rj: i32 = 0; for (rj < rejecttargets.len) { let badbin: str = strings.concat(root, "/reject-bin-", stages[si], "-", rejectactions[rj]); let badav: []str = [driver(stages[si]), "build", "-w", rejectwork, "-I", source, "-o", badbin, rejecttargets[rj]]; runcommandenv(root, strings.concat("name-reject-", stages[si], "-", rejectactions[rj]), badav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, rejectneedles[rj])); let normalized: str = normalizedtrace(out.stderr, strings.concat(rejectwork, "/"), badbin); if (rj == 2) { assert(same(normalized, strings.concat( "$WORK/cmd.duplicate.unit.new:4:1: error: same redeclared in this block\n", "\t$WORK/cmd.duplicate.unit.new:3:1: other declaration of same\n", "$WORK/cmd.duplicate.unit.new:4:1: error: \"scope.two\" imported as same and not used\n", "ww: w6c failed for cmd.duplicate\n"))); }; if (rj == 3) { assert(same(normalized, strings.concat( "$WORK/cmd.collision.unit.new:3:1: error: \"scope.one\" imported as same and not used\n", "$WORK/cmd.collision.unit.new:4:1: error: same already declared through import of package same (\"scope.one\")\n", "\t$WORK/cmd.collision.unit.new:3:1: other declaration of same\n", "$WORK/cmd.collision.unit.new:5:26: error: use of package same not in selector\n", "ww: w6c failed for cmd.collision\n"))); }; if (si == 0) { diagnosticrefs[rj] = strings.dup(normalized); } else { assert(same(diagnosticrefs[rj], normalized)); }; assert(!os.exists(badbin)); let committed: []str = [".unit.ww", ".wwi", ".s", ".o", ".a", ".unit.new", ".wwi.new", ".s.new", ".o.new", ".a.new"]; let ci: i32 = 0; for (ci < committed.len) { assert(!os.exists(strings.concat(rejectwork, "/", rejectactions[rj], committed[ci]))); ci += 1; }; rj += 1; }; let conflictav: []str = [driver(stages[si]), "build", "-w", rejectwork, "-I", source, conflict]; runcommandenv(root, strings.concat("name-conflict-", stages[si]), conflictav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, "conflicting package names")); assert(!os.exists(strings.concat(rejectwork, "/conflict.pkg.unit.ww"))); // Production/internal/external naming uses the declaration, while every // action and generated-main artifact retains canonical variant identity. let testwork: str = strings.concat(root, "/test-work-", stages[si]); assert(os.mkdir(testwork, 448i32) == 0); rewritefile(traces[si], ""); let testav: []str = [driver(stages[si]), "test", "-w", testwork, "-I", source, testcodec]; runcommandenv(root, strings.concat("name-test-", stages[si]), testav, env, (240i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "internal_name_uses_declaration ... ok\n")); assert(has(out.stdout, "external_name_uses_declaration ... ok\n")); assert(out.stderr.len == 0); if (si == 0) { testoutref = strings.dup(out.stdout); } else { assert(same(testoutref, out.stdout)); }; assert(has(out.stdout, strings.concat("ok ", testcodec, " [wire]\n"))); assert(!os.exists(strings.concat(testwork, "/testpkg.codec.unit.ww"))); let internalunit: str = readfile(strings.concat(testwork, "/testpkg.codec-internal-test.unit.ww")); let externalunit: str = readfile(strings.concat(testwork, "/testpkg.codec_test-external-test.unit.ww")); assert(has(internalunit, "export type Thing = i32;")); assert(has(internalunit, "export fn value()")); assert(has(internalunit, "import assist testonly.helper;")); assert(!has(externalunit, "testonly.helper")); assert(has(externalunit, "package wire_test;")); assert(has(externalunit, "//ww:direct-export testpkg.codec ")); assert(os.exists(strings.concat(testwork, "/testpkg.codec-test-main.a"))); assert(!os.exists(strings.concat(testwork, "/testpkg.codec-internal-test-main.a"))); assert(!os.exists(strings.concat(testwork, "/testpkg.codec_test-external-test-main.a"))); assert(os.exists(strings.concat(testwork, "/test.a"))); let testtrace: str = readfile(traces[si]); let externalline: str = linecontaining(testtrace, "/testpkg.codec_test-external-test.unit.new"); assert(has(externalline, strings.concat( "<--import><", testwork, "/testpkg.codec-internal-test.wwi.new>"))); let mainline: str = linecontaining(testtrace, "/testpkg.codec-test-main.unit.new"); assert(occurrences(mainline, "<--test-target-package>") == 2); assert(pos(mainline, "<--test-target-package>") < pos(mainline, "<--test-target-package>")); let mainunit: str = readfile(strings.concat(testwork, "/testpkg.codec-test-main.unit.ww")); assert(has(mainunit, "import testpkg.codec;")); assert(has(mainunit, "import testpkg.codec_test;")); assert(has(mainunit, "import test;")); assert(!has(mainunit, "testonly.helper")); let recursivebuildwork: str = strings.concat(root, "/recursive-build-", stages[si]); assert(os.mkdir(recursivebuildwork, 448i32) == 0); let recursivebuildav: []str = [driver(stages[si]), "build", "-w", recursivebuildwork, "-I", source, strings.concat(source, "/testpkg/...")]; runcommandenv(root, strings.concat("name-recursive-build-", stages[si]), recursivebuildav, env, (180i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(os.exists(strings.concat(recursivebuildwork, "/testpkg.codec.wwi"))); let recursivetestwork: str = strings.concat(root, "/recursive-test-", stages[si]); assert(os.mkdir(recursivetestwork, 448i32) == 0); let recursivetestav: []str = [driver(stages[si]), "test", "-w", recursivetestwork, "-I", source, strings.concat(source, "/testpkg/...")]; runcommandenv(root, strings.concat("name-recursive-test-", stages[si]), recursivetestav, env, (240i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "external_name_uses_declaration ... ok\n")); // Vendor expansion owns the action/map identity; the loaded declaration // independently supplies cable as the source-visible qualifier. rewritefile(traces[si], ""); let vendorav: []str = [driver(stages[si]), "build", "-w", works[si], "-I", source, "-o", vendorbins[si], vendorclient]; runcommandenv(root, strings.concat("name-vendor-", stages[si]), vendorav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let vendorunit: str = readfile(strings.concat(works[si], "/vend.client.unit.ww")); assert(occurrences(vendorunit, "import stable short.codec;") == 1); assert(occurrences(vendorunit, "import short.codec;") == 1); assert(has(vendorunit, strings.concat( "//ww:import-map short.codec vend.vendor.short.codec ", hexbytes(vendored), "\n"))); assert(occurrences(vendorunit, "//ww:import-map short.codec vend.vendor.short.codec ") == 1); let vendortrace: str = readfile(traces[si]); let vendorline: str = linecontaining(vendortrace, "vend.client.unit.new"); assert(occurrences(vendorline, "<--import>") == 1); assert(occurrences(vendorline, strings.concat( "<--import-map>")) == 1); let vendorwwi: str = readfile(strings.concat(works[si], "/vend.vendor.short.codec.wwi")); assert(has(vendorwwi, strings.concat( "//ww:module vend.vendor.short.codec\npackage cable;\n"))); assert(!os.exists(strings.concat(works[si], "/short.codec.a"))); let vendorrun: []str = [vendorbins[si]]; runcommand(root, strings.concat("name-vendor-run-", stages[si]), vendorrun, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 13); // A stable explicit alias survives a dependency declared-name change. // Canonicalized exported type references keep the bridge interface // unchanged, so reconsideration stops after the direct importer. rewritefile(traces[si], ""); let appav: []str = [driver(stages[si]), "build", "-w", works[si], "-I", source, "-o", appbins[si], app]; runcommandenv(root, strings.concat("name-app-cold-", stages[si]), appav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let apprun: []str = [appbins[si]]; runcommand(root, strings.concat("name-app-run-", stages[si]), apprun, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 42); let stablebridgewwi: str = readfile(strings.concat(works[si], "/acme.bridge.wwi")); assert(has(stablebridgewwi, "import __wwi_61636d652e636f646563 acme.codec;\n")); assert(has(stablebridgewwi, "export type BridgedThing = __wwi_61636d652e636f646563.Thing;\n")); assert(!has(stablebridgewwi, "stable.Thing")); rewritefile(traces[si], ""); runcommandenv(root, strings.concat("name-app-warm-", stages[si]), appav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(readfile(traces[si]).len == 0); rewritefile(codecfile, cablebody); rewritefile(traces[si], ""); runcommandenv(root, strings.concat("name-app-renamed-", stages[si]), appav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let changedtrace: str = readfile(traces[si]); assert(occurrences(changedtrace, "\n") == 2); assert(occurrences(changedtrace, "/acme.codec.unit.new") == 1); assert(occurrences(changedtrace, "/acme.bridge.unit.new") == 1); assert(!has(changedtrace, "/cmd.app.unit.new")); let nchanged: str = normalizedtrace(changedtrace, strings.concat(works[si], "/"), appbins[si]); if (si == 0) { changedtraceref = strings.dup(nchanged); } else { assert(same(changedtraceref, nchanged)); }; let renamedwwi: str = readfile(strings.concat(works[si], "/acme.codec.wwi")); assert(has(renamedwwi, "//ww:module acme.codec\npackage cable;\n")); assert(!has(renamedwwi, "package wire;")); assert(same(stablebridgewwi, readfile(strings.concat(works[si], "/acme.bridge.wwi")))); runcommand(root, strings.concat("name-app-renamed-run-", stages[si]), apprun, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 42); // Editing only the local alias rebuilds its owning source action. It // cannot rename canonical exports or force the downstream command. rewritefile(bridgefile, steadybridgebody); rewritefile(traces[si], ""); runcommandenv(root, strings.concat("name-app-alias-edit-", stages[si]), appav, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let aliastrace: str = readfile(traces[si]); assert(occurrences(aliastrace, "\n") == 1); assert(occurrences(aliastrace, "/acme.bridge.unit.new") == 1); assert(!has(aliastrace, "/acme.codec.unit.new")); assert(!has(aliastrace, "/cmd.app.unit.new")); assert(same(stablebridgewwi, readfile(strings.concat(works[si], "/acme.bridge.wwi")))); let nalias: str = normalizedtrace(aliastrace, strings.concat(works[si], "/"), appbins[si]); if (si == 0) { aliastraceref = strings.dup(nalias); } else { assert(same(aliastraceref, nalias)); }; runcommand(root, strings.concat("name-app-alias-edit-run-", stages[si]), apprun, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 42); // New semantic artifacts and binaries are byte-identical across the // independently cold Cstage and WWstage work roots. ari = 0; for (ari < actions.len) { let xi: i32 = 0; for (xi < suffixes.len) { let index: i32 = ari * suffixes.len + xi; let bytes: str = readfile(strings.concat(works[si], "/", actions[ari], suffixes[xi])); if (si == 0) { artifactrefs[index] = strings.dup(bytes); } else { assert(same(artifactrefs[index], bytes)); }; xi += 1; }; ari += 1; }; let stagebins: []str = [directbins[si], appbins[si], scopedbins[si], repeatedbins[si], leafmainbins[si], vendorbins[si]]; let bini: i32 = 0; for (bini < stagebins.len) { let bytes: str = readfile(stagebins[bini]); if (si == 0) { binaryrefs[bini] = strings.dup(bytes); } else { assert(same(binaryrefs[bini], bytes)); }; bini += 1; }; si += 1; }; clean(root); }; @test fn explicit_import_alias_binding_modes() void = { let root: str = fresh(); let source: str = strings.concat(root, "/source"); let one: str = strings.concat(source, "/pkg/one"); let two: str = strings.concat(source, "/pkg/two"); let positives: []str = ["default", "alias", "reuse", "repeatok", "repeatedfiles", "blankalias", "blankrepeat", "blankdefault", "blankexplicit", "blankfiles"]; let negatives: []str = ["barefn", "baretype", "baredef", "bareconst", "barevar", "aliasbad", "aliasleak", "repeatdup", "aliasdup", "defaultalias", "declcollision", "unusedalias", "blankqual", "blankbare", "blankordinaryunused"]; let dirs: []str = [one, two]; let i: i32 = 0; for (i < positives.len) { append(dirs, strings.concat(source, "/cmd/", positives[i])); i += 1; }; i = 0; for (i < negatives.len) { append(dirs, strings.concat(source, "/cmd/", negatives[i])); i += 1; }; i = 0; for (i < dirs.len) { mkdirall(dirs[i]); i += 1; }; writefile(strings.concat(one, "/one.ww"), strings.concat( "package wire;\n", "export fn value() i32 = { return 11; };\n", "export type Thing = i32;\n", "export def DEFINED: i32 = 13;\n", "export const CONSTANT: i32 = 17;\n", "export let VARIABLE: i32 = 19;\n")); writefile(strings.concat(two, "/two.ww"), "package cable;\nexport fn value() i32 = { return 29; };\n"); let defaultbody: str = strings.concat( "package main;\nimport pkg.one;\n", "fn typed(v: wire.Thing) i32 = { return v: i32; };\n", "fn main() i32 = { return wire.value() + wire.DEFINED + ", "wire.CONSTANT + wire.VARIABLE + typed(23); };\n"); writefile(strings.concat(source, "/cmd/default/main.ww"), defaultbody); writefile(strings.concat(source, "/cmd/alias/main.ww"), strings.concat( "package main;\nimport stable pkg.one;\n", "fn typed(v: stable.Thing) i32 = { return v: i32; };\n", "fn main() i32 = { return stable.value() + stable.DEFINED + ", "stable.CONSTANT + stable.VARIABLE + typed(23); };\n")); writefile(strings.concat(source, "/cmd/reuse/a.ww"), strings.concat( "package main;\nimport shared pkg.one;\n", "fn left() i32 = { return shared.value(); };\n")); writefile(strings.concat(source, "/cmd/reuse/b.ww"), strings.concat( "package main;\nimport shared pkg.two;\n", "fn right() i32 = { return shared.value(); };\n")); writefile(strings.concat(source, "/cmd/reuse/main.ww"), "package main;\nfn main() i32 = { return left() + right(); };\n"); writefile(strings.concat(source, "/cmd/repeatok/main.ww"), strings.concat( "package main;\nimport pkg.one;\nimport first pkg.one;\n", "import second pkg.one;\n", "fn main() i32 = { return wire.value() + first.value() + ", "second.value(); };\n")); writefile(strings.concat(source, "/cmd/repeatedfiles/a.ww"), strings.concat( "package main;\nimport left pkg.one;\n", "fn first() i32 = { return left.value(); };\n")); writefile(strings.concat(source, "/cmd/repeatedfiles/b.ww"), strings.concat( "package main;\nimport right pkg.one;\n", "fn second() i32 = { return right.value(); };\n")); writefile(strings.concat(source, "/cmd/repeatedfiles/main.ww"), "package main;\nfn main() i32 = { return first() + second(); };\n"); writefile(strings.concat(source, "/cmd/blankalias/main.ww"), "package main;\nimport _ pkg.one;\nfn main() i32 = { return 0; };\n"); writefile(strings.concat(source, "/cmd/blankrepeat/main.ww"), strings.concat("package main;\nimport _ pkg.one;\n", "import _ pkg.one;\nfn main() i32 = { return 0; };\n")); writefile(strings.concat(source, "/cmd/blankdefault/main.ww"), strings.concat("package main;\nimport _ pkg.one;\nimport pkg.one;\n", "fn main() i32 = { return wire.value(); };\n")); writefile(strings.concat(source, "/cmd/blankexplicit/main.ww"), strings.concat("package main;\nimport _ pkg.one;\n", "import stable pkg.one;\n", "fn main() i32 = { return stable.value(); };\n")); writefile(strings.concat(source, "/cmd/blankfiles/a.ww"), "package main;\nimport _ pkg.one;\n"); writefile(strings.concat(source, "/cmd/blankfiles/b.ww"), "package main;\nimport _ pkg.one;\n"); writefile(strings.concat(source, "/cmd/blankfiles/main.ww"), "package main;\nfn main() i32 = { return 0; };\n"); let bareexprs: []str = ["value()", "take(7)", "DEFINED", "CONSTANT", "VARIABLE"]; let barenames: []str = ["barefn", "baretype", "baredef", "bareconst", "barevar"]; i = 0; for (i < barenames.len) { let pre: str = ""; if (i == 1) { pre = "fn take(v: Thing) i32 = { return v: i32; };\n"; }; writefile(strings.concat(source, "/cmd/", barenames[i], "/main.ww"), strings.concat("package main;\nimport pkg.one;\n", pre, "fn main() i32 = { return ", bareexprs[i], "; };\n")); i += 1; }; writefile(strings.concat(source, "/cmd/aliasbad/main.ww"), strings.concat( "package main;\nimport stable pkg.one;\n", "fn bad(v: wire.Thing) i32 = { return v: i32; };\n", "fn declared() i32 = { return wire.value(); };\n", "fn leaf() i32 = { return one.value(); };\n", "fn bare() i32 = { return value(); };\n", "fn main() i32 = { return stable.value(); };\n")); writefile(strings.concat(source, "/cmd/aliasleak/a.ww"), strings.concat( "package main;\nimport stable pkg.one;\n", "fn anchor() i32 = { return stable.value(); };\n")); writefile(strings.concat(source, "/cmd/aliasleak/b.ww"), "package main;\nfn leaked() i32 = { return stable.value(); };\n"); writefile(strings.concat(source, "/cmd/aliasleak/main.ww"), "package main;\nfn main() i32 = { return leaked(); };\n"); writefile(strings.concat(source, "/cmd/repeatdup/main.ww"), strings.concat( "package main;\nimport pkg.one;\nimport pkg.one;\n", "fn main() i32 = { return wire.value(); };\n")); writefile(strings.concat(source, "/cmd/aliasdup/main.ww"), strings.concat( "package main;\nimport stable pkg.one;\nimport stable pkg.two;\n", "fn main() i32 = { return stable.value(); };\n")); writefile(strings.concat(source, "/cmd/defaultalias/main.ww"), strings.concat( "package main;\nimport pkg.one;\nimport wire pkg.two;\n", "fn main() i32 = { return wire.value(); };\n")); writefile(strings.concat(source, "/cmd/declcollision/main.ww"), strings.concat( "package main;\nimport stable pkg.one;\n", "fn stable() i32 = { return 0; };\n", "fn main() i32 = { return stable(); };\n")); writefile(strings.concat(source, "/cmd/unusedalias/main.ww"), "package main;\nimport stable pkg.one;\nfn main() i32 = { return 0; };\n"); writefile(strings.concat(source, "/cmd/blankqual/main.ww"), strings.concat("package main;\nimport _ pkg.one;\n", "fn main() i32 = { return wire.value(); };\n")); writefile(strings.concat(source, "/cmd/blankbare/main.ww"), strings.concat("package main;\nimport _ pkg.one;\n", "fn main() i32 = { return value(); };\n")); writefile(strings.concat(source, "/cmd/blankordinaryunused/main.ww"), strings.concat("package main;\nimport _ pkg.one;\nimport pkg.one;\n", "fn main() i32 = { return 0; };\n")); let stages: []str = ["ww", "ww_ww"]; let expected: []i32 = [83, 83, 40, 33, 22, 0, 0, 11, 11, 0]; let binaryrefs: []str = ["", "", "", "", "", "", "", "", "", ""]; let diagrefs: []str = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", ""]; let needles: []str = ["undefined: value", "unknown type 'Thing'", "undefined: DEFINED", "undefined: CONSTANT", "undefined: VARIABLE", "unknown type 'wire.Thing'", "undefined: stable", "wire redeclared in this block", "stable redeclared in this block", "wire redeclared in this block", "stable already declared through import of package stable", "\"pkg.one\" imported as stable and not used", "undefined: wire", "undefined: value", "\"pkg.one\" imported as wire and not used"]; let exactdiags: []str = [ strings.concat( "$WORK/cmd.barefn.unit.new:3:1: error: \"pkg.one\" imported as wire and not used\n", "$WORK/cmd.barefn.unit.new:4:26: error: undefined: value\n", "ww: w6c failed for cmd.barefn\n"), strings.concat( "$WORK/cmd.baretype.unit.new:3:1: error: \"pkg.one\" imported as wire and not used\n", "$WORK/cmd.baretype.unit.new:4:12: error: unknown type 'Thing'\n", "ww: w6c failed for cmd.baretype\n"), strings.concat( "$WORK/cmd.baredef.unit.new:3:1: error: \"pkg.one\" imported as wire and not used\n", "$WORK/cmd.baredef.unit.new:4:26: error: undefined: DEFINED\n", "ww: w6c failed for cmd.baredef\n"), strings.concat( "$WORK/cmd.bareconst.unit.new:3:1: error: \"pkg.one\" imported as wire and not used\n", "$WORK/cmd.bareconst.unit.new:4:26: error: undefined: CONSTANT\n", "ww: w6c failed for cmd.bareconst\n"), strings.concat( "$WORK/cmd.barevar.unit.new:3:1: error: \"pkg.one\" imported as wire and not used\n", "$WORK/cmd.barevar.unit.new:4:26: error: undefined: VARIABLE\n", "ww: w6c failed for cmd.barevar\n"), "", "", strings.concat( "$WORK/cmd.repeatdup.unit.new:4:1: error: wire redeclared in this block\n", "\t$WORK/cmd.repeatdup.unit.new:3:1: other declaration of wire\n", "$WORK/cmd.repeatdup.unit.new:4:1: error: \"pkg.one\" imported as wire and not used\n", "ww: w6c failed for cmd.repeatdup\n"), strings.concat( "$WORK/cmd.aliasdup.unit.new:4:1: error: stable redeclared in this block\n", "\t$WORK/cmd.aliasdup.unit.new:3:1: other declaration of stable\n", "$WORK/cmd.aliasdup.unit.new:4:1: error: \"pkg.two\" imported as stable and not used\n", "ww: w6c failed for cmd.aliasdup\n"), strings.concat( "$WORK/cmd.defaultalias.unit.new:4:1: error: wire redeclared in this block\n", "\t$WORK/cmd.defaultalias.unit.new:3:1: other declaration of wire\n", "$WORK/cmd.defaultalias.unit.new:4:1: error: \"pkg.two\" imported as wire and not used\n", "ww: w6c failed for cmd.defaultalias\n"), strings.concat( "$WORK/cmd.declcollision.unit.new:3:1: error: \"pkg.one\" imported as stable and not used\n", "$WORK/cmd.declcollision.unit.new:4:1: error: stable already declared through import of package stable (\"pkg.one\")\n", "\t$WORK/cmd.declcollision.unit.new:3:1: other declaration of stable\n", "$WORK/cmd.declcollision.unit.new:5:26: error: use of package stable not in selector\n", "ww: w6c failed for cmd.declcollision\n"), strings.concat( "$WORK/cmd.unusedalias.unit.new:3:1: error: \"pkg.one\" imported as stable and not used\n", "ww: w6c failed for cmd.unusedalias\n"), "", "", "", ]; let si: i32 = 0; for (si < stages.len) { let work: str = strings.concat(root, "/work-", stages[si]); let rejectwork: str = strings.concat(root, "/reject-", stages[si]); mkdirall(work); mkdirall(rejectwork); i = 0; for (i < positives.len) { let target: str = strings.concat(source, "/cmd/", positives[i]); let binary: str = strings.concat(root, "/", stages[si], "-", positives[i]); let av: []str = [driver(stages[si]), "build", "-w", work, "-I", source, "-o", binary, target]; let out: commandout; runcommand(root, strings.concat("alias-positive-", stages[si], "-", positives[i]), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let runav: []str = [binary]; runcommand(root, strings.concat("alias-positive-run-", stages[si], "-", positives[i]), runav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, expected[i]); let bytes: str = readfile(binary); if (si == 0) { binaryrefs[i] = strings.dup(bytes); } else { assert(same(binaryrefs[i], bytes)); }; i += 1; }; let aliasunit: str = readfile(strings.concat(work, "/cmd.alias.unit.ww")); assert(has(aliasunit, "import stable pkg.one;")); let aliaswwi: str = readfile(strings.concat(work, "/cmd.alias.wwi")); assert(has(aliaswwi, "import __wwi_706b672e6f6e65 pkg.one;")); assert(!has(aliaswwi, "import stable")); assert(os.exists(strings.concat(work, "/pkg.one.a"))); assert(!os.exists(strings.concat(work, "/stable.a"))); let repeatunit: str = readfile(strings.concat(work, "/cmd.repeatok.unit.ww")); assert(occurrences(repeatunit, "pkg.one;") == 3); let repeatedunit: str = readfile(strings.concat(work, "/cmd.repeatedfiles.unit.ww")); assert(occurrences(repeatedunit, "pkg.one;") == 2); let blankunit: str = readfile(strings.concat(work, "/cmd.blankalias.unit.ww")); assert(has(blankunit, "import _ pkg.one;")); let blankwwi: str = readfile(strings.concat(work, "/cmd.blankalias.wwi")); assert(!has(blankwwi, "import _") && !has(blankwwi, "__ww..")); let blankrepeat: str = readfile(strings.concat(work, "/cmd.blankrepeat.unit.ww")); assert(occurrences(blankrepeat, "import _ pkg.one;") == 2); assert(occurrences(blankrepeat, "//ww:direct-export pkg.one ") == 1); let blankfiles: str = readfile(strings.concat(work, "/cmd.blankfiles.unit.ww")); assert(occurrences(blankfiles, "import _ pkg.one;") == 2); assert(occurrences(blankfiles, "//ww:direct-export pkg.one ") == 1); let blankdispatch: str = readfile(strings.concat(work, "/cmd.blankdefault.init.unit.ww")); assert(occurrences(blankdispatch, "//ww:init-call __ww..pkg.p.pkg.one.v0.r0.init") == 1); i = 0; for (i < negatives.len) { let target: str = strings.concat(source, "/cmd/", negatives[i]); let binary: str = strings.concat(root, "/reject-bin-", stages[si], "-", negatives[i]); let av: []str = [driver(stages[si]), "build", "-w", rejectwork, "-I", source, "-o", binary, target]; let out: commandout; runcommand(root, strings.concat("alias-negative-", stages[si], "-", negatives[i]), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, needles[i])); if (i == 5) { assert(has(out.stderr, "undefined: wire")); assert(has(out.stderr, "undefined: one")); assert(has(out.stderr, "undefined: value")); }; if (i < 5) { let unusedpos: i32 = pos(out.stderr, "\"pkg.one\" imported as wire and not used"); let namepos: i32 = pos(out.stderr, needles[i]); assert(unusedpos >= 0 && namepos > unusedpos); }; if (i == 7 || i == 8 || i == 9) { let redeclared: i32 = pos(out.stderr, "redeclared in this block"); let unused: i32 = pos(out.stderr, "imported as"); assert(redeclared >= 0 && unused > redeclared); }; let normalized: str = normalizedtrace(out.stderr, strings.concat(rejectwork, "/"), binary); if (exactdiags[i].len > 0) { assert(same(normalized, exactdiags[i])); }; if (si == 0) { diagrefs[i] = strings.dup(normalized); } else { assert(same(diagrefs[i], normalized)); }; assert(!os.exists(binary)); let action: str = strings.concat("cmd.", negatives[i]); let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a", ".unit.new", ".wwi.new", ".s.new", ".o.new", ".a.new"]; let j: i32 = 0; for (j < suffixes.len) { assert(!os.exists(strings.concat(rejectwork, "/", action, suffixes[j]))); j += 1; }; i += 1; }; si += 1; }; clean(root); }; @test fn output_flag_exact_name_and_value_semantics() void = { let root: str = fresh(); let source: str = strings.concat(root, "/source"); let multi_a: str = strings.concat(source, "/alpha"); let multi_b: str = strings.concat(source, "/beta"); let tests: str = strings.concat(source, "/tests"); let outputs: str = strings.concat(root, "/outputs"); mkdirall(source); mkdirall(multi_a); mkdirall(multi_b); mkdirall(tests); mkdirall(outputs); let single: str = strings.concat(source, "/single.ww"); writefile(single, "package main;\nfn main() i32 = { return 29; };\n"); writefile(strings.concat(multi_a, "/main.ww"), "package main;\nfn main() i32 = { return 31; };\n"); writefile(strings.concat(multi_b, "/main.ww"), "package main;\nfn main() i32 = { return 37; };\n"); writefile(strings.concat(tests, "/tests.ww"), "package exactflags;\nfn value() i32 = { return 41; };\n"); writefile(strings.concat(tests, "/tests_test.ww"), strings.concat( "package exactflags;\n", "@test fn exact() void = { assert(value() == 41); };\n")); let rawtest: str = strings.concat(source, "/raw_test.ww"); writefile(rawtest, strings.concat( "package exactraw;\n", "@test fn raw_exact() void = { assert(true); };\n")); let compilerwrapper: str = strings.concat(root, "/output-flag-w6c.sh"); writeexecutable(compilerwrapper, strings.concat( "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_OUTPUT_FLAG_TRACE\"\n", "exec \"$WW_OUTPUT_FLAG_REAL_C\" \"$@\"\n")); let stages: []str = ["ww", "ww_ww"]; let compilers: []str = ["w6c", "w6c_ww"]; let tags: []str = ["c", "ww"]; let forms: []str = ["one-separate", "one-equals", "two-separate", "two-equals"]; let buildref: str = ""; let testref: str = ""; let alpharef: str = ""; let betaref: str = ""; let rawref: str = ""; let rejectrefs: []str = ["", "", "", "", ""]; let coordinatorrejectref: str = ""; let buildmissingref: str = ""; let testmissingref: str = ""; let stopref: str = ""; let baseenv: []str = os.getenvs(); let out: commandout; let si: i32 = 0; for (si < stages.len) { let trace: str = strings.concat(root, "/output-flag-", tags[si], ".trace"); writefile(trace, ""); let env: []str = alloc([], (baseenv.len + 3): u64)!; let ei: i32 = 0; for (ei < baseenv.len) { if (!strings.hasprefix(baseenv[ei], "WW_W6C=") && !strings.hasprefix(baseenv[ei], "WW_OUTPUT_FLAG_")) { append(env, baseenv[ei]); }; ei += 1; }; append(env, strings.concat("WW_W6C=", compilerwrapper)); append(env, strings.concat("WW_OUTPUT_FLAG_REAL_C=", driver(compilers[si]))); append(env, strings.concat("WW_OUTPUT_FLAG_TRACE=", trace)); let ignoredrunout: str = strings.concat(outputs, "/ignored-run-", tags[si]); let runcontrol: []str = [driver(stages[si]), "run", "-I", source, strings.concat("-o", ignoredrunout), single]; runcommand(root, strings.concat("output-flag-run-control-", tags[si]), runcontrol, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 29); assert(out.stdout.len == 0 && out.stderr.len == 0 && !os.exists(ignoredrunout)); // Exact output names change only publication. Distinct accepted // spellings retain the same action and executable bytes. let buildwork: str = strings.concat(root, "/build-work-", tags[si]); mkdirall(buildwork); let fi: i32 = 0; for (fi < forms.len) { let binary: str = strings.concat(outputs, "/build-", tags[si], "-", forms[fi], "=tail"); let av: []str = [driver(stages[si]), "build", "-w", buildwork, "-I", source]; if (fi == 0) { append(av, "-o"); append(av, binary); } else if (fi == 1) { append(av, strings.concat("-o=", binary)); } else if (fi == 2) { append(av, "--o"); append(av, binary); } else { append(av, strings.concat("--o=", binary)); }; append(av, single); rewritefile(trace, ""); runcommandenv(root, strings.concat("output-flag-build-", tags[si], "-", forms[fi]), av, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(binary)); if (fi == 0) { assert(readfile(trace).len != 0); } else { assert(readfile(trace).len == 0); }; let bytes: str = readfile(binary); if (buildref.len == 0) { buildref = strings.dup(bytes); } else { assert(same(buildref, bytes)); }; let runav: []str = [binary]; runcommand(root, strings.concat("output-flag-build-run-", tags[si], "-", forms[fi]), runav, time.second, &out); expectexit(&out, 29); assert(out.stdout.len == 0 && out.stderr.len == 0); fi += 1; }; // Repeated string flags are last-wins. A final empty value returns to // default naming and cannot create a file or scratch tree named '='. let stale: str = strings.concat(outputs, "/stale-build-", tags[si]); let winner: str = strings.concat(outputs, "/winner-build-", tags[si], "=last"); let lastav: []str = [driver(stages[si]), "build", "-w", buildwork, "-I", source, "-o", stale, strings.concat("--o=", winner), single]; rewritefile(trace, ""); runcommandenv(root, strings.concat("output-flag-last-", tags[si]), lastav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && !os.exists(stale) && same(buildref, readfile(winner)) && readfile(trace).len == 0); let defaultbin: str = strings.concat(root, "/single"); let emptyav: []str = [driver(stages[si]), "build", "-w", buildwork, "-I", source, "-o", stale, "-o=", single]; rewritefile(trace, ""); runcommandenvdir(root, strings.concat("output-flag-empty-", tags[si]), emptyav, env, root, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && !os.exists(stale) && same(buildref, readfile(defaultbin)) && readfile(trace).len == 0 && !os.exists(strings.concat(root, "/=")) && !os.exists(strings.concat(root, "/=.sepwork"))); assert(os.remove(defaultbin) == 0); clean(strings.concat(defaultbin, ".sepwork")); // Build parsing stops at its first operand: a later flag-like value is // package input, not a second output option. let early: str = strings.concat(outputs, "/early"); let late: str = strings.concat(outputs, "/late"); let stopav: []str = [driver(stages[si]), "build", "-I", source, "-o", early, single, strings.concat("--o=", late)]; runcommand(root, strings.concat("output-flag-stop-", tags[si]), stopav, (60i64 * (time.second: i64)): time.duration, &out); assert(out.termination == exec.termination.EXIT && out.code != 0); assert(out.stdout.len == 0 && !has(out.stderr, "unknown flag") && !os.exists(early) && !os.exists(late)); if (si == 0) { stopref = strings.dup(out.stderr); } else { assert(same(stopref, out.stderr)); }; // Joined names are unknown before loading, work creation, or output. let ri: i32 = 0; for (ri < 2) { let badout: str = strings.concat(outputs, "/bad-build-", tags[si], "-", forms[ri]); let badwork: str = strings.concat(root, "/bad-build-work-", tags[si], "-", forms[ri]); let prefix: str = "-o"; if (ri != 0) { prefix = "--o"; }; let joined: str = strings.concat(prefix, badout); let badav: []str = [driver(stages[si]), "build", "-w", badwork, "-I", source, joined, single]; runcommand(root, strings.concat("output-flag-reject-build-", tags[si], "-", forms[ri]), badav, time.second, &out); expectexit(&out, 2); assert(out.stdout.len == 0 && same(out.stderr, "ww build: unknown flag\n") && !os.exists(badout) && !os.exists(badwork)); if (si == 0) { rejectrefs[ri] = strings.dup(out.stderr); } else { assert(same(rejectrefs[ri], out.stderr)); }; ri += 1; }; let buildmissingwork: str = strings.concat(root, "/missing-build-work-", tags[si]); let buildmissing: []str = [driver(stages[si]), "build", "-w", buildmissingwork, "-I", source, "--o"]; runcommand(root, strings.concat("output-flag-missing-build-", tags[si]), buildmissing, time.second, &out); expectexit(&out, 2); assert(out.stdout.len == 0 && same(out.stderr, "ww build: -o needs an argument\n") && !os.exists(buildmissingwork)); if (si == 0) { buildmissingref = strings.dup(out.stderr); } else { assert(same(buildmissingref, out.stderr)); }; // Two roots exercise coordinator output planning. The second spelling // reuses the first request's compiled actions, and final empty performs // the established no-public-output multi-build. let multiwork: str = strings.concat(root, "/multi-work-", tags[si]); mkdirall(multiwork); let multione: str = strings.concat(root, "/multi-one-", tags[si], "/"); let multiav: []str = [driver(stages[si]), "build", "-w", multiwork, "-I", source, strings.concat("--o=", multione), multi_a, multi_b]; rewritefile(trace, ""); runcommandenvdir(root, strings.concat("output-flag-multi-one-", tags[si]), multiav, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && readfile(trace).len != 0); let alphaone: str = strings.concat(multione, "alpha"); let betaone: str = strings.concat(multione, "beta"); if (alpharef.len == 0) { alpharef = readfile(alphaone); betaref = readfile(betaone); } else { assert(same(alpharef, readfile(alphaone)) && same(betaref, readfile(betaone))); }; let alpharun: []str = [alphaone]; runcommand(root, strings.concat("output-flag-alpha-run-", tags[si]), alpharun, time.second, &out); expectexit(&out, 31); assert(out.stdout.len == 0 && out.stderr.len == 0); let betarun: []str = [betaone]; runcommand(root, strings.concat("output-flag-beta-run-", tags[si]), betarun, time.second, &out); expectexit(&out, 37); assert(out.stdout.len == 0 && out.stderr.len == 0); let multitwo: str = strings.concat(root, "/multi-two-", tags[si], "/"); let warmav: []str = [driver(stages[si]), "build", "-w", multiwork, "-I", source, "-o", multitwo, multi_a, multi_b]; rewritefile(trace, ""); runcommandenvdir(root, strings.concat("output-flag-multi-warm-", tags[si]), warmav, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && readfile(trace).len == 0 && same(alpharef, readfile(strings.concat(multitwo, "alpha"))) && same(betaref, readfile(strings.concat(multitwo, "beta")))); let unusedmulti: str = strings.concat(root, "/multi-unused-", tags[si], "/"); let multiempty: []str = [driver(stages[si]), "build", "-w", multiwork, "-I", source, "-o", unusedmulti, "--o=", multi_a, multi_b]; rewritefile(trace, ""); runcommandenvdir(root, strings.concat("output-flag-multi-empty-", tags[si]), multiempty, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && readfile(trace).len == 0 && !os.exists(unusedmulti) && !os.exists(strings.concat(root, "/alpha")) && !os.exists(strings.concat(root, "/beta"))); let multibadout: str = strings.concat(outputs, "/bad-multi-", tags[si]); let multibadwork: str = strings.concat(root, "/bad-multi-work-", tags[si]); let multibad: []str = [driver(stages[si]), "build", "-w", multibadwork, "-I", source, strings.concat("--o", multibadout), multi_a, multi_b]; runcommand(root, strings.concat("output-flag-reject-multi-", tags[si]), multibad, time.second, &out); expectexit(&out, 2); assert(out.stdout.len == 0 && same(out.stderr, "ww build: unknown flag\n") && !os.exists(multibadout) && !os.exists(multibadwork)); if (si == 0) { rejectrefs[4] = strings.dup(out.stderr); } else { assert(same(rejectrefs[4], out.stderr)); }; let directbadout: str = strings.concat(outputs, "/bad-direct-coordinator-", tags[si]); let directbadwork: str = strings.concat(root, "/bad-direct-coordinator-work-", tags[si]); let directbad: []str = [driver("wwtest"), "package", "--ww-operation", "build", "--ww-driver", driver(stages[si]), "-w", directbadwork, "-I", source, strings.concat("-o", directbadout), multi_a, multi_b]; runcommand(root, strings.concat("output-flag-reject-coordinator-", tags[si]), directbad, time.second, &out); expectexit(&out, 2); assert(out.stdout.len == 0 && has(out.stderr, "usage: wwtest package") && !os.exists(directbadout) && !os.exists(directbadwork)); if (si == 0) { coordinatorrejectref = strings.dup(out.stderr); } else { assert(same(coordinatorrejectref, out.stderr)); }; // Compile-only directory testing accepts the same four spellings. Its // final empty option may follow the package and restores the default. let testwork: str = strings.concat(root, "/test-work-", tags[si]); mkdirall(testwork); fi = 0; for (fi < forms.len) { let binary: str = strings.concat(outputs, "/test-", tags[si], "-", forms[fi], "=tail"); let av: []str = [driver(stages[si]), "test", "-c", "-w", testwork, "-I", source]; if (fi == 0) { append(av, "-o"); append(av, binary); } else if (fi == 1) { append(av, strings.concat("-o=", binary)); } else if (fi == 2) { append(av, "--o"); append(av, binary); } else { append(av, strings.concat("--o=", binary)); }; append(av, tests); rewritefile(trace, ""); runcommandenv(root, strings.concat("output-flag-test-", tags[si], "-", forms[fi]), av, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(binary)); if (fi == 0) { assert(readfile(trace).len != 0); } else { assert(readfile(trace).len == 0); }; let bytes: str = readfile(binary); if (testref.len == 0) { testref = strings.dup(bytes); } else { assert(same(testref, bytes)); }; let runav: []str = [binary]; runcommand(root, strings.concat("output-flag-test-run-", tags[si], "-", forms[fi]), runav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0 && has(out.stdout, "exact ... ok\n")); fi += 1; }; let staletest: str = strings.concat(outputs, "/stale-test-", tags[si]); let runningretained: str = strings.concat(outputs, "/running-test-", tags[si], "=tail"); let runningretainedav: []str = [driver(stages[si]), "test", "-w", testwork, "-I", source, strings.concat("--o=", runningretained), tests]; rewritefile(trace, ""); runcommandenvdir(root, strings.concat("output-flag-test-running-retained-", tags[si]), runningretainedav, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0 && has(out.stdout, "exact ... ok\n") && same(testref, readfile(runningretained)) && readfile(trace).len == 0); let defaulttest: str = strings.concat(root, "/tests.test"); let testempty: []str = [driver(stages[si]), "test", "-c", "-w", testwork, "-I", source, "-o", staletest, tests, "--o", ""]; rewritefile(trace, ""); runcommandenvdir(root, strings.concat("output-flag-test-empty-", tags[si]), testempty, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && !os.exists(staletest) && same(testref, readfile(defaulttest)) && readfile(trace).len == 0 && !os.exists(strings.concat(root, "/="))); assert(os.remove(defaulttest) == 0); let runningstale: str = strings.concat(outputs, "/stale-running-test-", tags[si]); let runningempty: []str = [driver(stages[si]), "test", "-w", testwork, "-I", source, "-o", runningstale, tests, "-o="]; rewritefile(trace, ""); runcommandenvdir(root, strings.concat("output-flag-test-running-empty-", tags[si]), runningempty, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0 && has(out.stdout, "exact ... ok\n") && !os.exists(runningstale) && readfile(trace).len == 0); // The raw single-file route owns output state in the public driver, so // final empty must normalize before its retain/copy decision too. let rawwork: str = strings.concat(root, "/raw-work-", tags[si]); mkdirall(rawwork); let rawretained: str = strings.concat(outputs, "/raw-test-", tags[si], "=tail"); let rawretainedav: []str = [driver(stages[si]), "test", "-w", rawwork, strings.concat("--o=", rawretained), rawtest]; runcommandenvdir(root, strings.concat("output-flag-raw-retained-", tags[si]), rawretainedav, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0 && has(out.stdout, "raw_exact ... ok\n") && os.exists(rawretained)); let rawpublishedbytes: str = readfile(rawretained); if (rawref.len == 0) { rawref = strings.dup(rawpublishedbytes); } else { assert(same(rawref, rawpublishedbytes)); }; let rawstale: str = strings.concat(outputs, "/stale-raw-", tags[si]); let rawempty: []str = [driver(stages[si]), "test", "-c", "-w", rawwork, "-o", rawstale, "--o=", rawtest]; runcommandenvdir(root, strings.concat("output-flag-raw-empty-", tags[si]), rawempty, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let rawbinary: str = strings.concat(rawwork, "/main"); assert(out.stdout.len == 0 && out.stderr.len == 0 && !os.exists(rawstale) && os.exists(rawbinary) && !os.exists(strings.concat(root, "/="))); let rawbytes: str = readfile(rawbinary); assert(same(rawref, rawbytes)); ri = 0; for (ri < 2) { let badout: str = strings.concat(outputs, "/bad-test-", tags[si], "-", forms[ri]); let badwork: str = strings.concat(root, "/bad-test-work-", tags[si], "-", forms[ri]); let prefix: str = "-o"; if (ri != 0) { prefix = "--o"; }; let joined: str = strings.concat(prefix, badout); let badav: []str = [driver(stages[si]), "test", "-c", "-w", badwork, "-I", source, joined, tests]; runcommand(root, strings.concat("output-flag-reject-test-", tags[si], "-", forms[ri]), badav, time.second, &out); expectexit(&out, 2); assert(out.stdout.len == 0 && same(out.stderr, "ww test: unknown flag\n") && !os.exists(badout) && !os.exists(badwork)); if (si == 0) { rejectrefs[2 + ri] = strings.dup(out.stderr); } else { assert(same(rejectrefs[2 + ri], out.stderr)); }; ri += 1; }; let testmissingwork: str = strings.concat(root, "/missing-test-work-", tags[si]); let testmissing: []str = [driver(stages[si]), "test", "-c", "-w", testmissingwork, "-I", source, "--o"]; runcommand(root, strings.concat("output-flag-missing-test-", tags[si]), testmissing, time.second, &out); expectexit(&out, 2); assert(out.stdout.len == 0 && same(out.stderr, "ww test: -o needs an argument\n") && !os.exists(testmissingwork)); if (si == 0) { testmissingref = strings.dup(out.stderr); } else { assert(same(testmissingref, out.stderr)); }; let cleanpaths: []str = [root, outputs, source, multi_a, multi_b, tests, buildwork, multiwork, testwork, rawwork]; let ci: i32 = 0; for (ci < cleanpaths.len) { assert(!directoryhasnew(cleanpaths[ci]) && !directoryhasfragment(cleanpaths[ci], ".install") && !directoryhasfragment(cleanpaths[ci], ".wwtxn.")); ci += 1; }; si += 1; }; assert(!os.exists(strings.concat(root, "/=")) && !os.exists(strings.concat(root, "/=.sepwork"))); clean(root); }; @test fn compile_artifact_naming() void = { let root: str = fresh(); let pdir: str = strings.concat(root, "/pkg"); assert(os.mkdir(pdir, 493i32) == 0); writefile(strings.concat(pdir, "/pkg.ww"), "package family;\nexport fn v() i32 = { return 7; };\n"); writefile(strings.concat(pdir, "/same_test.ww"), strings.concat( "package family;\nimport os;\n", "@test fn seven() void = { let av: []str = os.args();", " if (av.len != 0) { os.write(os.STDOUT_FILENO, av[0].ptr,", " av[0].len: u64); os.write(os.STDOUT_FILENO, \"\\n\".ptr, 1u64); };", " assert(v() == 7); };\n")); let adir: str = strings.concat(root, "/a"); assert(os.mkdir(adir, 493i32) == 0); writefile(strings.concat(adir, "/a.ww"), "package other;\nexport fn v() i32 = { return 1; };\n"); writefile(strings.concat(adir, "/a_test.ww"), "package other;\n@test fn one() void = { assert(v() == 1); };\n"); let none: str = strings.concat(root, "/znone"); assert(os.mkdir(none, 493i32) == 0); writefile(strings.concat(none, "/none.ww"), "package renamed;\nexport fn v() i32 = { return 9; };\n"); // '_' roots are excluded from recursive package discovery. let outdir: str = strings.concat(root, "/_out"); assert(os.mkdir(outdir, 493i32) == 0); let out: commandout; let drvs: []str = ["ww", "ww_ww"]; let namedbytes: str = ""; let defaultbytes: str = ""; let fanabytes: str = ""; let fanpkgbytes: str = ""; let i: i32 = 0; for (i < 2) { let named: str = strings.concat(outdir, "/out_", drvs[i], ".bin"); let coav: []str = [driver(drvs[i]), "test", "-c", "-I", root, "-o", named, pdir]; runcommand(root, strings.concat("nameco_", drvs[i]), coav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let fi: os.filestat; match (os.stat(&fi, named)) { case void => void; case let e: os.oserror => abort("-c -o artifact missing"); }; assert(((fi.mode: u32) & 73u32) != 0u32); assert(!has(out.stdout, "seven ... ")); match (os.stat(&fi, strings.concat(pdir, "/pkg.test"))) { case void => abort("-c -o still published the fixed stem"); case let e: os.oserror => void; }; if (i == 0) { namedbytes = readfile(named); } else { assert(same(namedbytes, readfile(named))); }; let runav: []str = [named]; runcommand(root, strings.concat("namerun_", drvs[i]), runav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "seven ... ")); let runnamed: str = strings.concat(outdir, "/run_", drvs[i], ".bin"); let nocav: []str = [driver(drvs[i]), "test", "-I", root, "-o", runnamed, pdir]; runcommand(root, strings.concat("noc_", drvs[i]), nocav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0 && os.exists(runnamed)); assert(has(out.stdout, "seven ... ")); assert(has(out.stdout, "/package.test\n")); assert(!has(out.stdout, runnamed)); let defaultbin: str = strings.concat(root, "/pkg.test"); let defaultav: []str = [driver(drvs[i]), "test", "-c", "-I", root, pdir]; runcommanddir(root, strings.concat("default_", drvs[i]), root, defaultav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(os.exists(defaultbin)); assert(!os.exists(strings.concat(root, "/family.test"))); assert(!os.exists(strings.concat(pdir, "/family.test"))); if (i == 0) { defaultbytes = readfile(defaultbin); } else { assert(same(defaultbytes, readfile(defaultbin))); }; clean(defaultbin); let fanroot: str = strings.concat(root, "/_fan_", drvs[i]); let fanarg: str = strings.concat(fanroot, "/nested/"); let fanav: []str = [driver(drvs[i]), "test", "-c", "-j", "2", "-I", root, "-o", fanarg, strings.concat(root, "/...")]; runcommand(root, strings.concat("fan_", drvs[i]), fanav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let fanabin: str = strings.concat(fanarg, "a.test"); let fanpkg: str = strings.concat(fanarg, "pkg.test"); assert(os.exists(fanabin) && os.exists(fanpkg)); assert(!os.exists(strings.concat(fanarg, "znone.test"))); assert(has(out.stdout, strings.concat("? ", none, " [no test files]\n"))); if (i == 0) { fanabytes = readfile(fanabin); fanpkgbytes = readfile(fanpkg); } else { assert(same(fanabytes, readfile(fanabin))); assert(same(fanpkgbytes, readfile(fanpkg))); }; let badout: str = strings.concat(outdir, "/multi-file-", drvs[i]); let mulav: []str = [driver(drvs[i]), "test", "-c", "-o", badout, strings.concat(root, "/...")]; runcommand(root, strings.concat("multi_", drvs[i]), mulav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, "with multiple packages, -o must refer to a directory or /dev/null")); assert(!os.exists(badout)); let nullcompile: []str = [driver(drvs[i]), "test", "-c", "-I", root, "-o", "/dev/null", pdir]; runcommand(root, strings.concat("null-compile_", drvs[i]), nullcompile, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(!has(out.stdout, "seven ... ")); assert(!os.exists("/dev/null.new")); let nullrun: []str = [driver(drvs[i]), "test", "-I", root, "-o", "/dev/null", pdir]; runcommand(root, strings.concat("null-run_", drvs[i]), nullrun, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "seven ... ")); i += 1; }; clean(root); }; @test fn test_binary_publication_transaction() void = { let root: str = fresh(); let suite: str = strings.concat(root, "/suite"); let leftsame: str = strings.concat(suite, "/left/same"); let rightsame: str = strings.concat(suite, "/right/same"); let alpha: str = strings.concat(suite, "/alpha"); let beta: str = strings.concat(suite, "/beta"); let failed: str = strings.concat(suite, "/failed"); let none: str = strings.concat(suite, "/none"); let persist: str = strings.concat(suite, "/persist"); mkdirall(leftsame); mkdirall(rightsame); mkdirall(alpha); mkdirall(beta); mkdirall(failed); mkdirall(none); mkdirall(persist); writefile(strings.concat(leftsame, "/same.ww"), "package declared_left;\nfn value() i32 = { return 1; };\n"); writefile(strings.concat(leftsame, "/same_test.ww"), strings.concat( "package declared_left;\n", "@test fn left() void = { assert(value() == 1); };\n")); writefile(strings.concat(rightsame, "/same.ww"), "package declared_right;\nfn value() i32 = { return 2; };\n"); writefile(strings.concat(rightsame, "/same_test.ww"), strings.concat( "package declared_right;\n", "@test fn right() void = { assert(value() == 2); };\n")); writefile(strings.concat(alpha, "/alpha.ww"), "package alpha_decl;\nfn value() i32 = { return 3; };\n"); writefile(strings.concat(alpha, "/alpha_test.ww"), strings.concat( "package alpha_decl;\n", "@test fn alpha_ok() void = { assert(value() == 3); };\n")); writefile(strings.concat(beta, "/beta.ww"), "package beta_decl;\nfn value() i32 = { return 4; };\n"); writefile(strings.concat(beta, "/beta_test.ww"), strings.concat( "package beta_decl;\n", "@test fn beta_ok() void = { assert(value() == 4); };\n")); writefile(strings.concat(failed, "/failed.ww"), "package failed_decl;\nfn value() i32 = { return 5; };\n"); writefile(strings.concat(failed, "/failed_test.ww"), strings.concat( "package failed_decl;\n", "@test fn runtime_failure() void = { assert(false); };\n")); writefile(strings.concat(none, "/none.ww"), "package none_decl;\nfn value() i32 = { return 6; };\n"); let persisttest: str = strings.concat(persist, "/persist_test.ww"); let persistbase: str = strings.concat( "package persist_decl;\n", "@test fn persistent_base() void = { assert(true); };\n"); let persistchanged: str = strings.concat( "package persist_decl;\n", "@test fn persistent_changed() void = { assert(1 + 1 == 2); };\n"); writefile(strings.concat(persist, "/persist.ww"), "package persist_decl;\nfn value() i32 = { return 7; };\n"); writefile(persisttest, persistbase); let compilerwrapper: str = strings.concat(root, "/publish-w6c.sh"); let linkerwrapper: str = strings.concat(root, "/publish-w6l.sh"); writeexecutable(compilerwrapper, strings.concat( "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_PUBLISH_CTRACE\"\n", "exec \"$WW_PUBLISH_REAL_C\" \"$@\"\n")); writeexecutable(linkerwrapper, strings.concat( "#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_PUBLISH_LTRACE\"\n", "if test -n \"$WW_PUBLISH_FAIL\"; then\n", " for arg do case \"$arg\" in *\"$WW_PUBLISH_FAIL\"*)\n", " printf 'injected publication linker failure\\n' >&2\n", " exit 97;; esac; done\nfi\n", "exec \"$WW_PUBLISH_REAL_L\" \"$@\"\n")); let stages: []str = ["ww", "ww_ww"]; let compilers: []str = ["w6c", "w6c_ww"]; let linkers: []str = ["w6l", "w6l_ww"]; let tags: []str = ["c", "ww"]; let duplicatediag: str = ""; let rollbackdiag: str = ""; let missingdiag: str = ""; let occupieddiag: str = ""; let persistbasebytes: str = ""; let persistchangedbytes: str = ""; let baseenv: []str = os.getenvs(); let out: commandout; let si: i32 = 0; for (si < stages.len) { let ctrace: str = strings.concat(root, "/publish-", tags[si], "-w6c.trace"); let ltrace: str = strings.concat(root, "/publish-", tags[si], "-w6l.trace"); writefile(ctrace, ""); writefile(ltrace, ""); let env: []str = alloc([], (baseenv.len + 7): u64)!; let ei: i32 = 0; for (ei < baseenv.len) { if (!strings.hasprefix(baseenv[ei], "WW_W6C=") && !strings.hasprefix(baseenv[ei], "WW_W6L=") && !strings.hasprefix(baseenv[ei], "WW_PUBLISH_CTRACE=") && !strings.hasprefix(baseenv[ei], "WW_PUBLISH_LTRACE=") && !strings.hasprefix(baseenv[ei], "WW_PUBLISH_REAL_C=") && !strings.hasprefix(baseenv[ei], "WW_PUBLISH_REAL_L=") && !strings.hasprefix(baseenv[ei], "WW_PUBLISH_FAIL=")) { append(env, baseenv[ei]); }; ei += 1; }; append(env, strings.concat("WW_W6C=", compilerwrapper)); append(env, strings.concat("WW_W6L=", linkerwrapper)); append(env, strings.concat("WW_PUBLISH_CTRACE=", ctrace)); append(env, strings.concat("WW_PUBLISH_LTRACE=", ltrace)); append(env, strings.concat("WW_PUBLISH_REAL_C=", driver(compilers[si]))); append(env, strings.concat("WW_PUBLISH_REAL_L=", driver(linkers[si]))); append(env, "WW_PUBLISH_FAIL="); // Equal import leaves are rejected before tools or output creation, // independent of their unequal declared package names. let duplicateout: str = strings.concat(root, "/duplicate-output/"); let duplicateav: []str = [driver(stages[si]), "test", "-c", "-j", "2", "-I", suite, "-o", duplicateout, leftsame, rightsame]; runcommandenvdir(root, strings.concat("publish-duplicate-", tags[si]), duplicateav, env, root, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0); assert(has(out.stderr, "ww test: cannot write test binary same.test for multiple packages:\n")); assert(has(out.stderr, strings.concat(leftsame, "\n"))); assert(has(out.stderr, strings.concat(rightsame, "\n"))); assert(readfile(ctrace).len == 0 && readfile(ltrace).len == 0); assert(!os.exists(duplicateout)); if (si == 0) { duplicatediag = strings.dup(out.stderr); } else { assert(same(duplicatediag, out.stderr)); }; // /dev/null is Go's exact multi-package discard exception: both // products build, neither publishes, and duplicate names are harmless. let nullav: []str = [driver(stages[si]), "test", "-c", "-j", "2", "-I", suite, "-o", "/dev/null", leftsame, rightsame]; runcommandenvdir(root, strings.concat("publish-null-", tags[si]), nullav, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(readfile(ctrace).len != 0); assert(occurrences(readfile(ltrace), "\n") == 2); assert(!os.exists("/dev/null.new")); // An occupied retained-binary stage is a pre-tool rejection. The // caller's old destination and occupied stage both remain untouched. let occupied: str = strings.concat(root, "/occupied"); if (si == 0) { assert(os.mkdir(occupied, 448i32) == 0); writefile(strings.concat(occupied, "/alpha.test"), "old-alpha\n"); writefile(strings.concat(occupied, "/alpha.test.new"), "occupied-stage\n"); }; rewritefile(ctrace, ""); rewritefile(ltrace, ""); let occupiedav: []str = [driver(stages[si]), "test", "-c", "-I", suite, "-o", strings.concat(occupied, "/"), alpha]; runcommandenvdir(root, strings.concat("publish-occupied-", tags[si]), occupiedav, env, root, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, strings.concat( "ww: product staging path already exists: ", occupied, "/alpha.test.new\n"))); assert(same(readfile(strings.concat(occupied, "/alpha.test")), "old-alpha\n")); assert(same(readfile(strings.concat(occupied, "/alpha.test.new")), "occupied-stage\n")); assert(readfile(ctrace).len == 0 && readfile(ltrace).len == 0); if (si == 0) { occupieddiag = strings.dup(out.stderr); } else { assert(same(occupieddiag, out.stderr)); }; // Link failure after an earlier sibling has staged a retained copy // rejects the whole request and preserves every old destination. let rollback: str = strings.concat(root, "/rollback-", tags[si]); assert(os.mkdir(rollback, 448i32) == 0); writefile(strings.concat(rollback, "/alpha.test"), "alpha-sentinel\n"); writefile(strings.concat(rollback, "/beta.test"), "beta-sentinel\n"); rewritefile(ctrace, ""); rewritefile(ltrace, ""); env[env.len - 1] = "WW_PUBLISH_FAIL=beta-test-main"; let rollbackav: []str = [driver(stages[si]), "test", "-c", "-j", "1", "-I", suite, "-o", strings.concat(rollback, "/"), alpha, beta]; runcommandenvdir(root, strings.concat("publish-rollback-", tags[si]), rollbackav, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, "injected publication linker failure\n")); assert(has(out.stderr, "ww: w6l failed\n")); assert(same(readfile(strings.concat(rollback, "/alpha.test")), "alpha-sentinel\n")); assert(same(readfile(strings.concat(rollback, "/beta.test")), "beta-sentinel\n")); assert(!directoryhasnew(rollback)); assert(occurrences(readfile(ltrace), "\n") == 2); if (si == 0) { rollbackdiag = strings.dup(out.stderr); } else { assert(same(rollbackdiag, out.stderr)); }; // Output directories minted for a rejected request are rolled back. let missingroot: str = strings.concat(root, "/missing-", tags[si]); let missingout: str = strings.concat(missingroot, "/nested/"); rewritefile(ctrace, ""); rewritefile(ltrace, ""); env[env.len - 1] = "WW_PUBLISH_FAIL=alpha-test-main"; let missingav: []str = [driver(stages[si]), "test", "-c", "-j", "1", "-I", suite, "-o", missingout, alpha]; runcommandenvdir(root, strings.concat("publish-missing-", tags[si]), missingav, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(!os.exists(missingroot)); assert(occurrences(readfile(ltrace), "\n") == 1); if (si == 0) { missingdiag = strings.dup(out.stderr); } else { assert(same(missingdiag, out.stderr)); }; // A package with no tests never claims a binary or creates a requested // output hierarchy, although its production package is still checked. let noneroot: str = strings.concat(root, "/none-output-", tags[si]); let noneav: []str = [driver(stages[si]), "test", "-c", "-I", suite, "-o", strings.concat(noneroot, "/nested/"), none]; env[env.len - 1] = "WW_PUBLISH_FAIL="; runcommandenvdir(root, strings.concat("publish-none-", tags[si]), noneav, env, root, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, strings.concat("? ", none, " [no test files]\n"))); assert(out.stderr.len == 0 && !os.exists(noneroot)); // Pinned builderTest makes the install action depend on the run action. // A failing run therefore preserves a prior destination and never enters // the retained-output overwrite guard. let failurebin: str = strings.concat(root, "/failure-", tags[si], ".test"); writefile(failurebin, "failure-sentinel\n"); let failureav: []str = [driver(stages[si]), "test", "-I", suite, "-o", failurebin, failed]; runcommandenvdir(root, strings.concat("publish-runtime-", tags[si]), failureav, env, root, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(readfile(failurebin), "failure-sentinel\n")); assert(!os.exists(strings.concat(failurebin, ".new"))); assert(has(out.stdout, "failed_decl.runtime_failure ... FAIL (exit 1)\n")); assert(out.stderr.len == 0); // -w keeps semantic actions persistent while -c and -o remain execution // and presentation policy. Compile-only can cold-build and invalidate; // an unchanged running request reuses those actions and the same bytes. rewritefile(persisttest, persistbase); let persistwork: str = strings.concat(root, "/persist-work-", tags[si]); let persistcompilebin: str = strings.concat(root, "/persist-compile-", tags[si], ".test"); let persistbin: str = strings.concat(root, "/persist-run-", tags[si], ".test"); rewritefile(ctrace, ""); rewritefile(ltrace, ""); let persistcompileav: []str = [driver(stages[si]), "test", "-c", "-w", persistwork, "-I", suite, "-o", persistcompilebin, persist]; let persistav: []str = [driver(stages[si]), "test", "-w", persistwork, "-I", suite, "-o", persistbin, persist]; runcommandenvdir(root, strings.concat("publish-persist-cold-c-", tags[si]), persistcompileav, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(readfile(ctrace).len != 0); let basebytes: str = readfile(persistcompilebin); if (si == 0) { persistbasebytes = strings.dup(basebytes); } else { assert(same(persistbasebytes, basebytes)); }; rewritefile(ctrace, ""); rewritefile(ltrace, ""); runcommandenvdir(root, strings.concat("publish-persist-warm-run-", tags[si]), persistav, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "persistent_base ... ok\n")); assert(readfile(ctrace).len == 0); assert(same(basebytes, readfile(persistbin))); rewritefile(persisttest, persistchanged); rewritefile(ctrace, ""); rewritefile(ltrace, ""); runcommandenvdir(root, strings.concat("publish-persist-change-c-", tags[si]), persistcompileav, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(readfile(ctrace).len != 0); let changedbytes: str = readfile(persistcompilebin); assert(!same(basebytes, changedbytes)); if (si == 0) { persistchangedbytes = strings.dup(changedbytes); } else { assert(same(persistchangedbytes, changedbytes)); }; rewritefile(ctrace, ""); rewritefile(ltrace, ""); runcommandenvdir(root, strings.concat("publish-persist-change-run-", tags[si]), persistav, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "persistent_changed ... ok\n")); assert(readfile(ctrace).len == 0); assert(same(changedbytes, readfile(persistbin))); assert(!os.exists(strings.concat(persistcompilebin, ".new"))); assert(!os.exists(strings.concat(persistbin, ".new"))); assert(!directoryhasnew(persistwork)); si += 1; }; assert(os.remove(strings.concat(root, "/occupied/alpha.test.new")) == 0); clean(root); }; // Go 1.26.5's BuildInstallFunc protects caller data at the install boundary: // after producers, and after a successful run for retained running tests. // Exercise that one predicate through direct, raw, package, and test routes. @test fn public_output_overwrite_safety() void = { let root: str = fresh(); let source: str = strings.concat(root, "/source"); let dep: str = strings.concat(source, "/lib/dep"); let app: str = strings.concat(source, "/cmd/app"); let bad: str = strings.concat(source, "/cmd/bad"); let checked: str = strings.concat(source, "/pkg/check"); let failed: str = strings.concat(source, "/pkg/failed"); let invocation: str = strings.concat(root, "/invoke"); mkdirall(dep); mkdirall(app); mkdirall(bad); mkdirall(checked); mkdirall(failed); mkdirall(invocation); writefile(strings.concat(dep, "/dep.ww"), strings.concat( "package renamed;\n", "export fn value() int = { return 9; };\n")); let appsource: str = strings.concat(app, "/main.ww"); let appbase: str = strings.concat( "package main;\n", "import stable lib.dep;\n", "export fn main() int = { return stable.value(); };\n"); let appchanged: str = strings.concat( "package main;\n", "import stable lib.dep;\n", "export fn main() int = { return stable.value() + 1; };\n"); writefile(appsource, appbase); writefile(strings.concat(bad, "/main.ww"), strings.concat( "package main;\n", "import missing nowhere.present;\n", "export fn main() int = { return missing.value(); };\n")); writefile(strings.concat(checked, "/check.ww"), "package checked;\nfn value() int = { return 4; };\n"); writefile(strings.concat(checked, "/check_test.ww"), strings.concat( "package checked;\n", "@test fn guarded_run() void = { assert(value() == 4); };\n")); writefile(strings.concat(failed, "/failed_test.ww"), strings.concat( "package failed;\n", "@test fn failed_run() void = { assert(false); };\n")); let raw: str = strings.concat(root, "/raw.ww"); writefile(raw, "package main;\nexport fn main() int = { return 3; };\n"); let linkerwrapper: str = strings.concat(root, "/overwrite-w6l.sh"); writeexecutable(linkerwrapper, strings.concat( "#!/bin/sh\n", "printf 'link\\n' >> \"$WW_OVERWRITE_TRACE\"\n", "exec \"$WW_OVERWRITE_REAL_L\" \"$@\"\n")); let drivers: []str = ["ww", "ww_ww"]; let linkers: []str = ["w6l", "w6l_ww"]; let tags: []str = ["c", "ww"]; let commandout: str = strings.concat(root, "/command.bin"); let rawout: str = strings.concat(root, "/raw.bin"); let defaultout: str = strings.concat(invocation, "/app"); let libout: str = strings.concat(root, "/library.a"); let testbin: str = strings.concat(root, "/check.test"); let symlinktarget: str = strings.concat(root, "/symlink-target"); let symlinkout: str = strings.concat(root, "/symlink.bin"); let fifoout: str = strings.concat(root, "/fifo.bin"); writefile(symlinktarget, "symlink-sentinel\n"); assert(os.symlink(symlinktarget, symlinkout) == 0); let builddir: str = strings.concat(root, "/build-output"); let buildchild: str = strings.concat(builddir, "/app"); let testdir: str = strings.concat(root, "/test-output"); let testchild: str = strings.concat(testdir, "/check.test"); mkdirall(buildchild); mkdirall(testchild); writefile(strings.concat(buildchild, "/marker"), "build-directory\n"); writefile(strings.concat(testchild, "/marker"), "test-directory\n"); let commanddiag: str = ""; let rawdiag: str = ""; let defaultdiag: str = ""; let symlinkdiag: str = ""; let builddirdiag: str = ""; let librarydiag: str = ""; let libraryarchivediag: str = ""; let testcompilediag: str = ""; let testrundiag: str = ""; let testdirdiag: str = ""; let loaddiag: str = ""; let commandbytes: str = ""; let librarybytes: str = ""; let interfacebytes: str = ""; let testbytes: str = ""; let unitbytes: str = ""; let baseenv: []str = os.getenvs(); let out: commandout; let si: i32 = 0; for (si < drivers.len) { let trace: str = strings.concat(root, "/overwrite-", tags[si], ".trace"); writefile(trace, ""); let env: []str = alloc([], (baseenv.len + 3): u64)!; let ei: i32 = 0; for (ei < baseenv.len) { if (!strings.hasprefix(baseenv[ei], "WW_W6L=") && !strings.hasprefix(baseenv[ei], "WW_OVERWRITE_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_OVERWRITE_REAL_L=")) { append(env, baseenv[ei]); }; ei += 1; }; append(env, strings.concat("WW_W6L=", linkerwrapper)); append(env, strings.concat("WW_OVERWRITE_TRACE=", trace)); append(env, strings.concat("WW_OVERWRITE_REAL_L=", driver(linkers[si]))); let work: str = strings.concat(root, "/command-work-", tags[si]); let badwork: str = strings.concat(root, "/bad-work-", tags[si]); let rawwork: str = strings.concat(root, "/raw-work-", tags[si]); let defaultwork: str = strings.concat(root, "/default-work-", tags[si]); let symlinkwork: str = strings.concat(root, "/symlink-work-", tags[si]); let fifowork: str = strings.concat(root, "/fifo-work-", tags[si]); let dirwork: str = strings.concat(root, "/dir-work-", tags[si]); let libwork: str = strings.concat(root, "/lib-work-", tags[si]); let testwork: str = strings.concat(root, "/test-work-", tags[si]); let testdirwork: str = strings.concat(root, "/test-dir-work-", tags[si]); mkdirall(work); mkdirall(badwork); mkdirall(rawwork); mkdirall(defaultwork); mkdirall(symlinkwork); mkdirall(fifowork); mkdirall(dirwork); mkdirall(libwork); mkdirall(testwork); mkdirall(testdirwork); // A nonempty caller file rejects only after the linker ran, and a cold // persistent generation remains unpublished. if (os.exists(commandout)) { assert(os.remove(commandout) == 0); }; writefile(commandout, "caller-command\n"); rewritefile(trace, ""); let commandav: []str = [driver(drivers[si]), "build", "-w", work, "-I", source, "-o", commandout, "cmd.app"]; runcommandenv(root, strings.concat("overwrite-command-", tags[si]), commandav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, strings.concat("ww: build output \"", commandout, "\" already exists and is not an object file\n"))); assert(same(readfile(commandout), "caller-command\n")); assert(permissionmode(commandout) == 384u32); assert(readfile(trace).len != 0 && directoryisempty(work)); assert(!os.exists(strings.concat(commandout, ".new")) && !directoryhasfragment(root, "command.bin.wwtxn.")); if (si == 0) { commanddiag = strings.dup(out.stderr); } else { assert(same(commanddiag, out.stderr)); }; // Package/import failure keeps load precedence and starts no linker. rewritefile(trace, ""); let badav: []str = [driver(drivers[si]), "build", "-w", badwork, "-I", source, "-o", commandout, "cmd.bad"]; runcommandenv(root, strings.concat("overwrite-load-", tags[si]), badav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, "cannot find package nowhere.present")); assert(!has(out.stderr, "already exists and is not an object file")); assert(readfile(trace).len == 0 && directoryisempty(badwork)); assert(same(readfile(commandout), "caller-command\n")); if (si == 0) { loaddiag = strings.dup(out.stderr); } else { assert(same(loaddiag, out.stderr)); }; // Empty reservations and recognized ELF outputs are replaceable. A // rejected invalidation preserves the prior persistent generation. rewritefile(commandout, ""); rewritefile(trace, ""); runcommandenv(root, strings.concat("overwrite-empty-", tags[si]), commandav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let built: str = readfile(commandout); assert(strings.hasprefix(built, "\x7fELF")); let unit: str = readfile(strings.concat(work, "/cmd.app.unit.ww")); if (si == 0) { commandbytes = strings.dup(built); unitbytes = strings.dup(unit); } else { assert(same(commandbytes, built) && same(unitbytes, unit)); }; rewritefile(appsource, appchanged); rewritefile(commandout, "caller-after-change\n"); rewritefile(trace, ""); runcommandenv(root, strings.concat("overwrite-invalidated-", tags[si]), commandav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(readfile(trace).len != 0); assert(same(readfile(commandout), "caller-after-change\n")); assert(same(readfile(strings.concat(work, "/cmd.app.unit.ww")), unit)); rewritefile(appsource, appbase); rewritefile(commandout, built); runcommandenv(root, strings.concat("overwrite-elf-", tags[si]), commandav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let runav: []str = [commandout]; runcommand(root, strings.concat("overwrite-command-run-", tags[si]), runav, (10i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 9); // Default, raw, symlink-following, and output-directory child routes // use the same late guard and never strand rollback backups. if (os.exists(defaultout)) { rewritefile(defaultout, "default-sentinel\n"); } else { writefile(defaultout, "default-sentinel\n"); }; let defaultav: []str = [driver(drivers[si]), "build", "-w", defaultwork, "-I", source, "cmd.app"]; runcommandenvdir(root, strings.concat("overwrite-default-", tags[si]), defaultav, env, invocation, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(readfile(defaultout), "default-sentinel\n")); if (si == 0) { defaultdiag = strings.dup(out.stderr); } else { assert(same(defaultdiag, out.stderr)); }; if (os.exists(rawout)) { rewritefile(rawout, "raw-sentinel\n"); } else { writefile(rawout, "raw-sentinel\n"); }; let rawav: []str = [driver(drivers[si]), "build", "-w", rawwork, "-o", rawout, raw]; runcommandenv(root, strings.concat("overwrite-raw-", tags[si]), rawav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(readfile(rawout), "raw-sentinel\n")); if (si == 0) { rawdiag = strings.dup(out.stderr); } else { assert(same(rawdiag, out.stderr)); }; let symlinkav: []str = [driver(drivers[si]), "build", "-w", symlinkwork, "-I", source, "-o", symlinkout, "cmd.app"]; runcommandenv(root, strings.concat("overwrite-symlink-", tags[si]), symlinkav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(readfile(symlinkout), "symlink-sentinel\n")); if (si == 0) { symlinkdiag = strings.dup(out.stderr); } else { assert(same(symlinkdiag, out.stderr)); }; if (os.exists(fifoout)) { assert(os.remove(fifoout) == 0); }; let fifoav: []str = ["/usr/bin/mkfifo", fifoout]; runcommand(root, strings.concat("overwrite-mkfifo-", tags[si]), fifoav, (10i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let fifobuildav: []str = [driver(drivers[si]), "build", "-w", fifowork, "-I", source, "-o", fifoout, "cmd.app"]; runcommandenv(root, strings.concat("overwrite-fifo-", tags[si]), fifobuildav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(strings.hasprefix(readfile(fifoout), "\x7fELF")); let dirav: []str = [driver(drivers[si]), "build", "-w", dirwork, "-I", source, "-o", strings.concat(builddir, "/"), "cmd.app"]; runcommandenv(root, strings.concat("overwrite-build-dir-", tags[si]), dirav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, "already exists and is a directory")); assert(same(readfile(strings.concat(buildchild, "/marker")), "build-directory\n")); assert(!directoryhasfragment(builddir, ".wwtxn.")); if (si == 0) { builddirdiag = strings.dup(out.stderr); } else { assert(same(builddirdiag, out.stderr)); }; // Archive and WW-interface outputs each participate in the guard. An // ordinary repeat accepts both recognized prior outputs; arbitrary // interface or archive text rejects without changing either member. if (os.exists(libout)) { assert(os.remove(libout) == 0); }; if (os.exists(strings.concat(libout, ".wwi"))) { assert(os.remove(strings.concat(libout, ".wwi")) == 0); }; let libav: []str = [driver(drivers[si]), "build", "-w", libwork, "-I", source, "-o", libout, "lib.dep"]; runcommandenv(root, strings.concat("overwrite-library-new-", tags[si]), libav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let archive: str = readfile(libout); let iface: str = readfile(strings.concat(libout, ".wwi")); assert(strings.hasprefix(archive, "!\n")); assert(strings.hasprefix(iface, "//ww:module ")); runcommandenv(root, strings.concat("overwrite-library-repeat-", tags[si]), libav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(archive, readfile(libout)) && same(iface, readfile(strings.concat(libout, ".wwi")))); if (si == 0) { librarybytes = strings.dup(archive); interfacebytes = strings.dup(iface); } else { assert(same(librarybytes, archive) && same(interfacebytes, iface)); }; rewritefile(strings.concat(libout, ".wwi"), "caller-interface\n"); runcommandenv(root, strings.concat("overwrite-library-reject-", tags[si]), libav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(readfile(libout), archive)); assert(same(readfile(strings.concat(libout, ".wwi")), "caller-interface\n")); if (si == 0) { librarydiag = strings.dup(out.stderr); } else { assert(same(librarydiag, out.stderr)); }; rewritefile(libout, "caller-archive\n"); rewritefile(strings.concat(libout, ".wwi"), iface); runcommandenv(root, strings.concat("overwrite-library-archive-", tags[si]), libav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(readfile(libout), "caller-archive\n")); assert(same(readfile(strings.concat(libout, ".wwi")), iface)); if (si == 0) { libraryarchivediag = strings.dup(out.stderr); } else { assert(same(libraryarchivediag, out.stderr)); }; // Compile-only installs check after linking. Running retention executes // the private test first; a successful run then checks, while a failed // run never installs or diagnoses the caller destination. if (os.exists(testbin)) { rewritefile(testbin, "caller-test\n"); } else { writefile(testbin, "caller-test\n"); }; let testcompileav: []str = [driver(drivers[si]), "test", "-c", "-w", testwork, "-I", source, "-o", testbin, "pkg.check"]; runcommandenv(root, strings.concat("overwrite-test-c-", tags[si]), testcompileav, env, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(readfile(testbin), "caller-test\n")); if (si == 0) { testcompilediag = strings.dup(out.stderr); } else { assert(same(testcompilediag, out.stderr)); }; rewritefile(testbin, ""); runcommandenv(root, strings.concat("overwrite-test-empty-", tags[si]), testcompileav, env, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let compiledtest: str = readfile(testbin); assert(strings.hasprefix(compiledtest, "\x7fELF")); if (si == 0) { testbytes = strings.dup(compiledtest); } else { assert(same(testbytes, compiledtest)); }; rewritefile(testbin, "caller-running-test\n"); let testrunav: []str = [driver(drivers[si]), "test", "-w", testwork, "-I", source, "-o", testbin, "pkg.check"]; runcommandenv(root, strings.concat("overwrite-test-run-", tags[si]), testrunav, env, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stdout, "checked.guarded_run ... ok\n")); assert(same(readfile(testbin), "caller-running-test\n")); if (si == 0) { testrundiag = strings.dup(out.stderr); } else { assert(same(testrundiag, out.stderr)); }; rewritefile(testbin, compiledtest); runcommandenv(root, strings.concat("overwrite-test-elf-", tags[si]), testrunav, env, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "checked.guarded_run ... ok\n")); let beforefailure: str = readfile(testbin); let failav: []str = [driver(drivers[si]), "test", "-w", testwork, "-I", source, "-o", testbin, "pkg.failed"]; runcommandenv(root, strings.concat("overwrite-test-fail-", tags[si]), failav, env, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stdout, "failed.failed_run ... FAIL (exit 1)\n")); assert(out.stderr.len == 0 && same(readfile(testbin), beforefailure)); assert(!os.exists(strings.concat(testbin, ".new")) && !os.exists(strings.concat(testbin, ".install"))); let testdirav: []str = [driver(drivers[si]), "test", "-c", "-w", testdirwork, "-I", source, "-o", strings.concat(testdir, "/"), "pkg.check"]; runcommandenv(root, strings.concat("overwrite-test-dir-", tags[si]), testdirav, env, (90i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, "already exists and is a directory")); assert(same(readfile(strings.concat(testchild, "/marker")), "test-directory\n")); assert(!directoryhasfragment(testdir, ".wwtxn.")); if (si == 0) { testdirdiag = strings.dup(out.stderr); } else { assert(same(testdirdiag, out.stderr)); }; // Assembly and exact-null routes have no public install action. let asmout: str = strings.concat(root, "/assembly-", tags[si]); writefile(asmout, "assembly-sentinel\n"); let asmav: []str = [driver(drivers[si]), "build", "-S", "-w", work, "-I", source, "-o", asmout, "cmd.app"]; runcommandenv(root, strings.concat("overwrite-asm-", tags[si]), asmav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(readfile(asmout), "assembly-sentinel\n")); let nullav: []str = [driver(drivers[si]), "build", "-I", source, "-o", "/dev/null", "cmd.app"]; runcommandenv(root, strings.concat("overwrite-null-", tags[si]), nullav, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); si += 1; }; clean(root); }; // Empty build patterns select the current directory. A literal dot is // loading syntax, not the caller-visible output name: command publication and // cold scratch use the selected directory leaf without making that physical // leaf package or import identity. @test fn current_directory_build_default_output() void = { let root: str = fresh(); let command: str = strings.concat(root, "/current-command"); let library: str = strings.concat(root, "/current-library"); let bad: str = strings.concat(root, "/bad-current"); mkdirall(command); mkdirall(library); mkdirall(bad); let commandfile: str = strings.concat(command, "/main.ww"); let original: str = "package main;\nfn main() i32 = { return 23; };\n"; let changed: str = "package main;\nfn main() i32 = { return 24; };\n"; writefile(commandfile, original); writefile(strings.concat(library, "/library.ww"), strings.concat( "package declared_library;\n", "export fn value() i32 = { return 7; };\n")); writefile(strings.concat(bad, "/main.ww"), strings.concat( "package main;\nimport missing.current;\n", "fn main() i32 = { return missing.current.value(); };\n")); let compilerwrapper: str = strings.concat(root, "/current-w6c.sh"); writeexecutable(compilerwrapper, strings.concat( "#!/bin/sh\nprintf 'compile\\n' >> \"$WW_CURRENT_TRACE\"\n", "if test \"$WW_CURRENT_FAIL\" = 1; then\n", " printf 'injected current-directory compiler failure\\n' >&2\n", " exit 97\nfi\nexec \"$WW_CURRENT_REAL_C\" \"$@\"\n")); let stages: []str = ["ww", "ww_ww"]; let compilers: []str = ["w6c", "w6c_ww"]; let tags: []str = ["c", "ww"]; let spellings: []str = ["", ".", "./"]; let spellingtags: []str = ["bare", "dot", "slash"]; let baseenv: []str = os.getenvs(); let commandref: str = ""; let changedref: str = ""; let missingref: str = ""; let failref: str = ""; let collisionref: str = ""; let out: commandout; let si: i32 = 0; for (si < stages.len) { rewritefile(commandfile, original); let defaultbin: str = strings.concat(command, "/current-command"); let defaultscratch: str = strings.concat(defaultbin, ".sepwork"); let legacyscratch: str = strings.concat(command, "/..sepwork"); // Bare, dot, and dot-slash spellings select the same package and // produce the same directory-named executable and cold artifacts. let pi: i32 = 0; for (pi < spellings.len) { let av: []str = [driver(stages[si]), "build"]; if (spellings[pi].len != 0) { append(av, spellings[pi]); }; runcommanddir(root, strings.concat("current-", tags[si], "-", spellingtags[pi]), command, av, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(os.exists(defaultbin) && os.exists(defaultscratch)); assert(!os.exists(legacyscratch)); assert((permissionmode(defaultbin) & 73u32) == 73u32); let built: str = readfile(defaultbin); if (commandref.len == 0) { commandref = strings.dup(built); } else { assert(same(commandref, built)); }; let runav: []str = [defaultbin]; runcommand(root, strings.concat("current-run-", tags[si], "-", spellingtags[pi]), runav, time.second, &out); expectexit(&out, 23); assert(!directoryhasnew(command) && !directoryhasfragment(command, ".install") && !directoryhasfragment(command, ".wwtxn.")); assert(os.remove(defaultbin) == 0); clean(defaultscratch); pi += 1; }; // Explicit output and exact null output keep their established // disposition and never create the default current-directory name. let explicitout: str = strings.concat(root, "/explicit-", tags[si]); let explicitav: []str = [driver(stages[si]), "build", "-o", explicitout]; runcommanddir(root, strings.concat("current-explicit-", tags[si]), command, explicitav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(same(commandref, readfile(explicitout)) && !os.exists(defaultbin) && !os.exists(defaultscratch)); assert(os.remove(explicitout) == 0); clean(strings.concat(explicitout, ".sepwork")); let nullav: []str = [driver(stages[si]), "build", "-o", "/dev/null"]; runcommanddir(root, strings.concat("current-null-", tags[si]), command, nullav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(!os.exists(defaultbin) && !os.exists(defaultscratch) && !os.exists(legacyscratch)); // A non-main current-directory build compiles and archives privately, // but neither links nor publishes a directory-named file. let librarybin: str = strings.concat(library, "/current-library"); let libraryscratch: str = strings.concat(librarybin, ".sepwork"); let librarylegacy: str = strings.concat(library, "/..sepwork"); let libraryav: []str = [driver(stages[si]), "build"]; runcommanddir(root, strings.concat("current-library-", tags[si]), library, libraryav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(!os.exists(librarybin) && os.exists(libraryscratch) && !os.exists(librarylegacy)); assert(!directoryhasnew(library) && !directoryhasfragment(library, ".wwtxn.")); clean(libraryscratch); // Import loading retains precedence over output planning. Failure // creates neither the corrected output nor either scratch spelling. let badbin: str = strings.concat(bad, "/bad-current"); let badav: []str = [driver(stages[si]), "build"]; runcommanddir(root, strings.concat("current-missing-", tags[si]), bad, badav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && has(out.stderr, "cannot find package missing.current\n")); assert(!os.exists(badbin) && !os.exists(strings.concat(badbin, ".sepwork")) && !os.exists(strings.concat(bad, "/..sepwork"))); if (si == 0) { missingref = strings.dup(out.stderr); } else { assert(same(missingref, out.stderr)); }; // Persistent cold/warm reuse keeps the same output. An invalidated // producer failure preserves both that prior output and clean public // state; a later successful invalidation publishes the changed binary. let work: str = strings.concat(root, "/current-work-", tags[si]); let trace: str = strings.concat(root, "/current-trace-", tags[si]); mkdirall(work); writefile(trace, ""); let env: []str = alloc([], (baseenv.len + 4): u64)!; let ei: i32 = 0; for (ei < baseenv.len) { if (!strings.hasprefix(baseenv[ei], "WW_W6C=") && !strings.hasprefix(baseenv[ei], "WW_CURRENT_")) { append(env, baseenv[ei]); }; ei += 1; }; append(env, strings.concat("WW_W6C=", compilerwrapper)); append(env, strings.concat("WW_CURRENT_REAL_C=", driver(compilers[si]))); append(env, strings.concat("WW_CURRENT_TRACE=", trace)); let failindex: i32 = env.len; append(env, "WW_CURRENT_FAIL="); let persistav: []str = [driver(stages[si]), "build", "-w", work]; runcommandenvdir(root, strings.concat("current-cold-", tags[si]), persistav, env, command, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && readfile(trace).len != 0); let prior: str = readfile(defaultbin); assert(same(commandref, prior)); rewritefile(trace, ""); runcommandenvdir(root, strings.concat("current-warm-", tags[si]), persistav, env, command, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && readfile(trace).len == 0 && same(prior, readfile(defaultbin))); rewritefile(commandfile, changed); rewritefile(trace, ""); env[failindex] = "WW_CURRENT_FAIL=1"; runcommandenvdir(root, strings.concat("current-fail-", tags[si]), persistav, env, command, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(readfile(trace).len != 0 && has(out.stderr, "injected current-directory compiler failure\n") && has(out.stderr, "ww: w6c failed for ") && same(prior, readfile(defaultbin))); assert(!directoryhasnew(command) && !directoryhasnew(work) && !directoryhasfragment(command, ".install") && !directoryhasfragment(command, ".wwtxn.") && !directoryhasfragment(work, ".wwtxn.")); if (si == 0) { failref = strings.dup(out.stderr); } else { assert(same(failref, out.stderr)); }; env[failindex] = "WW_CURRENT_FAIL="; rewritefile(trace, ""); runcommandenvdir(root, strings.concat("current-changed-", tags[si]), persistav, env, command, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && readfile(trace).len != 0); let changedbytes: str = readfile(defaultbin); assert(!same(prior, changedbytes)); if (changedref.len == 0) { changedref = strings.dup(changedbytes); } else { assert(same(changedref, changedbytes)); }; let changedrun: []str = [defaultbin]; runcommand(root, strings.concat("current-changed-run-", tags[si]), changedrun, time.second, &out); expectexit(&out, 24); assert(os.remove(defaultbin) == 0); clean(work); rewritefile(commandfile, original); // A genuine destination-directory collision still rejects at output // preflight, now naming the derived leaf rather than the dot selector. mkdirall(defaultbin); runcommanddir(root, strings.concat("current-collision-", tags[si]), command, libraryav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && same(out.stderr, strings.concat( "ww: build output \"current-command\" already exists and is a directory\n"))); assert(!os.exists(defaultscratch) && !os.exists(legacyscratch)); if (si == 0) { collisionref = strings.dup(out.stderr); } else { assert(same(collisionref, out.stderr)); }; clean(defaultbin); si += 1; }; assert(!directoryhasnew(root) && !directoryhasfragment(root, ".wwtxn.")); clean(root); }; // A single directory root used to bypass the package coordinator and treat // every non-null -o spelling as one file. Exercise the Go output-directory // branch at that dispatch boundary while the established graph transaction // remains responsible for actions, publication, and rollback. @test fn single_root_build_output_directory() 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 library: str = strings.concat(source, "/library"); let bad: str = strings.concat(source, "/bad"); let badlibrary: str = strings.concat(source, "/badlibrary"); let duplicateone: str = strings.concat(source, "/duplicate-one/tool"); let duplicatetwo: str = strings.concat(source, "/duplicate-two/tool"); let duplicatethree: str = strings.concat(source, "/duplicate-three/tool"); mkdirall(base); mkdirall(alpha); mkdirall(library); mkdirall(bad); mkdirall(badlibrary); mkdirall(duplicateone); mkdirall(duplicatetwo); mkdirall(duplicatethree); 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(alpha, "/main_test.ww"), "package main;\n@test fn valid() void = { assert(true); };\n"); writefile(strings.concat(library, "/library.ww"), strings.concat( "package renamed;\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")); writefile(strings.concat(badlibrary, "/library.ww"), strings.concat( "package renamed;\nimport _ missing.pkg;\n", "export fn value() i32 = { return 0; };\n")); writefile(strings.concat(duplicateone, "/main.ww"), "package main;\nfn main() i32 = { return 12; };\n"); writefile(strings.concat(duplicatetwo, "/main.ww"), strings.concat( "package main;\nimport _ missing.pkg;\n", "fn main() i32 = { return 13; };\n")); writefile(strings.concat(duplicatethree, "/main.ww"), "package main;\nfn main() i32 = { return 14; };\n"); let logical: str = strings.concat(source, "/logical"); assert(os.symlink(alpha, logical) == 0); let raw: str = strings.concat(source, "/raw.ww"); writefile(raw, "package main;\nfn main() i32 = { return 9; };\n"); let rawlibrary: str = strings.concat(source, "/rawlibrary.ww"); writefile(rawlibrary, "package renamed;\nexport fn value() i32 = { return 10; };\n"); let rawbad: str = strings.concat(source, "/rawbad.ww"); writefile(rawbad, strings.concat( "package main;\nimport _ missing.pkg;\n", "fn main() i32 = { return 11; };\n")); let longoutputbytes: []u8 = alloc([], os.PATH_MAX: u64)!; let longi: i32 = 0; for (longi < root.len) { append(longoutputbytes, root[longi]); longi += 1; }; append(longoutputbytes, '/': u8); for (longoutputbytes.len < os.PATH_MAX - 2) { append(longoutputbytes, 'o': u8); }; append(longoutputbytes, '/': u8); assert(longoutputbytes.len == os.PATH_MAX - 1); let longoutputdir: str = strings.frombytes(longoutputbytes); let compilerwrapper: str = strings.concat(root, "/output-dir-w6c.sh"); let assemblerwrapper: str = strings.concat(root, "/output-dir-w6a.sh"); let linkerwrapper: str = strings.concat(root, "/output-dir-w6l.sh"); writeexecutable(compilerwrapper, strings.concat( "#!/bin/sh\nprintf 'compile %s\\n' \"$*\" >> \"$WW_ODIR_CTRACE\"\n", "if test \"$WW_ODIR_COMPILE_MODE\" = fail; then\n", " printf 'injected output-directory compiler failure\\n' >&2\n", " exit 96\nfi\nexec \"$WW_ODIR_REAL_C\" \"$@\"\n")); writeexecutable(assemblerwrapper, strings.concat( "#!/bin/sh\nprintf 'assemble %s\\n' \"$*\" >> \"$WW_ODIR_ATRACE\"\n", "exec \"$WW_ODIR_REAL_A\" \"$@\"\n")); writeexecutable(linkerwrapper, strings.concat( "#!/bin/sh\nprintf 'link %s\\n' \"$*\" >> \"$WW_ODIR_LTRACE\"\n", "case \"$WW_ODIR_LINK_MODE\" in\n", " fail) printf 'injected output-directory linker failure\\n' >&2;", " exit 97;;\n", " signal) kill -TERM \"$$\";;\nesac\n", "exec \"$WW_ODIR_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 umasklauncher: str = driver("package-umaskexec"); let baseenv: []str = os.getenvs(); let routebinref: str = ""; let rawbinref: str = ""; let testbinref: str = ""; let changedbinref: str = ""; let asmref: str = ""; let nomainref: str = ""; let importref: str = ""; let implicitref: str = ""; let implicitrawref: str = ""; let implicitimportref: str = ""; let compilerfailref: str = ""; let linkerfailref: str = ""; let linkersignalref: str = ""; let rawrollbackref: str = ""; let rawnonmainref: str = ""; let rawlongref: str = ""; let rawlongimportref: str = ""; let dirlongref: str = ""; let dirlongimportref: str = ""; let mixedimportref: str = ""; let nomainimportref: str = ""; let duplicateimportref: str = ""; let duplicateref: str = ""; let recursiveimplicitref: str = ""; let recursiveimplicitimportref: str = ""; let coldctraceref: str = ""; let coldatraceref: str = ""; let coldltraceref: str = ""; let warmltraceref: str = ""; let changedctraceref: str = ""; let changedatraceref: str = ""; let changedltraceref: str = ""; let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a", ".init.unit.ww", ".init.s", ".init.o"]; let artifactrefs: []str = ["", "", "", "", "", "", "", ""]; let out: commandout; let si: i32 = 0; for (si < stages.len) { rewritefile(alphafile, alphaoriginal); let ctrace: str = strings.concat(root, "/", tags[si], "-odir-c.trace"); let atrace: str = strings.concat(root, "/", tags[si], "-odir-a.trace"); let ltrace: str = strings.concat(root, "/", tags[si], "-odir-l.trace"); writefile(ctrace, ""); writefile(atrace, ""); writefile(ltrace, ""); let env: []str = alloc([], (baseenv.len + 11): 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_ODIR_")) { 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_ODIR_CTRACE=", ctrace)); append(env, strings.concat("WW_ODIR_ATRACE=", atrace)); append(env, strings.concat("WW_ODIR_LTRACE=", ltrace)); append(env, strings.concat("WW_ODIR_REAL_C=", driver(compilers[si]))); append(env, strings.concat("WW_ODIR_REAL_A=", driver(assemblers[si]))); append(env, strings.concat("WW_ODIR_REAL_L=", driver(linkers[si]))); let compilemodeindex: i32 = env.len; append(env, "WW_ODIR_COMPILE_MODE="); let linkmodeindex: i32 = env.len; append(env, "WW_ODIR_LINK_MODE="); // An existing directory without a trailing slash remains a directory. // The literal root's path leaf, not its declared name or source file, // names the command; unrelated directory contents survive publication. let existing: str = strings.concat(root, "/existing-", tags[si]); assert(os.mkdir(existing, 448i32) == 0); writefile(strings.concat(existing, "/sentinel"), "existing-sentinel\n"); let existingav: []str = [driver(stages[si]), "build", "-I", source, "-o", existing, alpha]; runcommandenvdir(root, strings.concat("odir-existing-", tags[si]), existingav, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(same(readfile(strings.concat(existing, "/sentinel")), "existing-sentinel\n")); let existingbin: str = strings.concat(existing, "/alpha"); assert(os.exists(existingbin)); let routebytes: str = readfile(existingbin); if (si == 0) { routebinref = strings.dup(routebytes); } else { assert(same(routebinref, routebytes)); }; let runav: []str = [existingbin]; runcommand(root, strings.concat("odir-existing-run-", tags[si]), runav, time.second, &out); expectexit(&out, 4); assert(!directoryhasnew(existing)); // A missing trailing-slash hierarchy is created, and logical package // resolution retains the same command identity and bytes. let missingroot: str = strings.concat(root, "/missing-", tags[si]); let missingout: str = strings.concat(missingroot, "/nested/"); let missingav: []str = [driver(stages[si]), "build", "-I", source, "-o", missingout, "alpha"]; runcommandenvdir(root, strings.concat("odir-missing-", tags[si]), missingav, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(same(routebytes, readfile(strings.concat(missingout, "alpha")))); // A contextual request's final import component is presentation // metadata. Canonicalizing a symlink target must not leak the physical // directory leaf into explicit or implicit command names. let logicalout: str = strings.concat(root, "/logical-output-", tags[si]); assert(os.mkdir(logicalout, 448i32) == 0); let logicalav: []str = [driver(stages[si]), "build", "-I", source, "-o", logicalout, "logical"]; runcommandenvdir(root, strings.concat("odir-logical-", tags[si]), logicalav, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(routebytes, readfile(strings.concat(logicalout, "/logical")))); assert(!os.exists(strings.concat(logicalout, "/alpha"))); let logicalcaller: str = strings.concat(root, "/logical-caller-", tags[si]); let logicalwork: str = strings.concat(root, "/logical-work-", tags[si]); mkdirall(logicalcaller); mkdirall(logicalwork); let logicaldefaultav: []str = [driver(stages[si]), "build", "-w", logicalwork, "-I", source, "logical"]; runcommandenvdir(root, strings.concat("odir-logical-default-", tags[si]), logicaldefaultav, env, logicalcaller, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(routebytes, readfile(strings.concat(logicalcaller, "/logical")))); assert(!os.exists(strings.concat(logicalcaller, "/alpha"))); // BuildInstallFunc creates a missing caller output parent from 0777; // the launcher fixes umask at 000 so every minted prefix is observable. let modeparent: str = strings.concat(root, "/mode-parent-", tags[si]); let modeout: str = strings.concat(modeparent, "/nested/"); let modeav: []str = [umasklauncher, "000", driver(stages[si]), "build", "-I", source, "-o", modeout, "alpha"]; runcommandenvdir(root, strings.concat("odir-mode-", tags[si]), modeav, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(permissionmode(modeparent) == 511u32); assert(permissionmode(strings.concat(modeparent, "/nested")) == 511u32); assert(permissionmode(strings.concat(modeout, "alpha")) == 511u32); assert(same(routebytes, readfile(strings.concat(modeout, "alpha")))); // stat follows a symlinked output directory. With no explicit root the // default dot is inserted into the delegated request exactly once. let symlinktarget: str = strings.concat(root, "/symlink-target-", tags[si]); let symlinkout: str = strings.concat(root, "/symlink-out-", tags[si]); assert(os.mkdir(symlinktarget, 448i32) == 0); writefile(strings.concat(symlinktarget, "/sentinel"), "symlink-sentinel\n"); assert(os.symlink(symlinktarget, symlinkout) == 0); let defaultav: []str = [driver(stages[si]), "build", "-I", source, "-o", symlinkout]; runcommandenvdir(root, strings.concat("odir-default-", tags[si]), defaultav, env, alpha, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(same(readfile(strings.concat(symlinktarget, "/sentinel")), "symlink-sentinel\n")); assert(same(routebytes, readfile(strings.concat(symlinkout, "/alpha")))); // Raw command-line files remain driver-owned. Existing and missing // output directories both derive the source basename without .ww. let rawexisting: str = strings.concat(root, "/raw-existing-", tags[si]); assert(os.mkdir(rawexisting, 448i32) == 0); writefile(strings.concat(rawexisting, "/sentinel"), "raw-sentinel\n"); let rawexistingav: []str = [driver(stages[si]), "build", "-o", rawexisting, raw]; runcommandenvdir(root, strings.concat("odir-raw-existing-", tags[si]), rawexistingav, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let rawbin: str = strings.concat(rawexisting, "/raw"); let rawbytes: str = readfile(rawbin); if (si == 0) { rawbinref = strings.dup(rawbytes); } else { assert(same(rawbinref, rawbytes)); }; let rawrun: []str = [rawbin]; runcommand(root, strings.concat("odir-raw-run-", tags[si]), rawrun, time.second, &out); expectexit(&out, 9); assert(same(readfile(strings.concat(rawexisting, "/sentinel")), "raw-sentinel\n")); let rawmissingroot: str = strings.concat(root, "/raw-missing-", tags[si]); let rawmissingout: str = strings.concat(rawmissingroot, "/nested/"); let rawmissingav: []str = [driver(stages[si]), "build", "-o", rawmissingout, raw]; runcommandenvdir(root, strings.concat("odir-raw-missing-", tags[si]), rawmissingav, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(rawbytes, readfile(strings.concat(rawmissingout, "raw")))); // The directory form requests command actions, not an archive name. // A raw non-main root therefore rejects after loading and before tools or // caller-directory creation, just like a directory-package root. rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); let rawlibroot: str = strings.concat(root, "/raw-library-", tags[si]); let rawlibout: str = strings.concat(rawlibroot, "/nested/"); let rawlibav: []str = [driver(stages[si]), "build", "-o", rawlibout, rawlibrary]; runcommandenvdir(root, strings.concat("odir-raw-library-", tags[si]), rawlibav, env, root, time.second, &out); expectexit(&out, 1); assert(same(out.stderr, "ww: no main packages to build\n")); assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); assert(!os.exists(rawlibroot)); if (si == 0) { rawnonmainref = strings.dup(out.stderr); } else { assert(same(rawnonmainref, out.stderr)); }; // Raw destination joining is output preflight, not loading. A bad import // therefore wins over the derived PATH_MAX failure; a valid command then // reports that path error before tools or directory creation. let rawlongbadav: []str = [driver(stages[si]), "build", "-o", longoutputdir, rawbad]; runcommandenvdir(root, strings.concat("odir-raw-long-import-", tags[si]), rawlongbadav, env, root, time.second, &out); expectexit(&out, 1); assert(has(out.stderr, "cannot find package missing.pkg\n")); assert(!has(out.stderr, "command output path is too long")); assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); if (si == 0) { rawlongimportref = strings.dup(out.stderr); } else { assert(same(rawlongimportref, out.stderr)); }; let rawlongav: []str = [driver(stages[si]), "build", "-o", longoutputdir, raw]; runcommandenvdir(root, strings.concat("odir-raw-long-", tags[si]), rawlongav, env, root, time.second, &out); expectexit(&out, 1); assert(same(out.stderr, "ww: command output path is too long\n")); assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); if (si == 0) { rawlongref = strings.dup(out.stderr); } else { assert(same(rawlongref, out.stderr)); }; // The coordinator also defers its derived command destination until the // shared driver has loaded every import. The valid twin pins the retained // PATH_MAX rejection after the same no-tool boundary. rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); let dirlongbadav: []str = [driver(stages[si]), "build", "-I", source, "-o", longoutputdir, bad]; runcommandenvdir(root, strings.concat("odir-directory-long-import-", tags[si]), dirlongbadav, env, root, time.second, &out); expectexit(&out, 1); assert(has(out.stderr, "cannot find package missing.pkg\n")); assert(!has(out.stderr, "command output path is too long")); assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); if (si == 0) { dirlongimportref = strings.dup(out.stderr); } else { assert(same(dirlongimportref, out.stderr)); }; let dirlongav: []str = [driver(stages[si]), "build", "-I", source, "-o", longoutputdir, alpha]; runcommandenvdir(root, strings.concat("odir-directory-long-", tags[si]), dirlongav, env, root, time.second, &out); expectexit(&out, 1); assert(same(out.stderr, "ww: command output path is too long\n")); assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); if (si == 0) { dirlongref = strings.dup(out.stderr); } else { assert(same(dirlongref, out.stderr)); }; // Test retains its already-established directory semantics; dispatching // build through the coordinator must not disturb the adjacent branch. let testout: str = strings.concat(root, "/test-output-", tags[si]); assert(os.mkdir(testout, 448i32) == 0); let testav: []str = [driver(stages[si]), "test", "-c", "-I", source, "-o", testout, alpha]; runcommandenvdir(root, strings.concat("odir-test-control-", tags[si]), testav, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let testbin: str = strings.concat(testout, "/alpha.test"); let testbytes: str = readfile(testbin); if (si == 0) { testbinref = strings.dup(testbytes); } else { assert(same(testbinref, testbytes)); }; // Repeated command roots deduplicate. A mixed independent non-main root // is loaded but omitted from the command-only action list; dependencies // of the retained command still build normally. rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); let mixedout: str = strings.concat(root, "/mixed-", tags[si]); let mixedwork: str = strings.concat(root, "/mixed-work-", tags[si]); assert(os.mkdir(mixedout, 448i32) == 0); let mixedav: []str = [driver(stages[si]), "build", "-w", mixedwork, "-I", source, "-o", mixedout, alpha, alpha, library]; runcommandenvdir(root, strings.concat("odir-mixed-", tags[si]), mixedav, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(os.exists(strings.concat(mixedout, "/alpha"))); assert(!os.exists(strings.concat(mixedout, "/library"))); assert(occurrences(readfile(ctrace), "\n") == 2); assert(occurrences(readfile(atrace), "\n") == 3); assert(occurrences(readfile(ltrace), "\n") == 1); assert(!has(readfile(ctrace), "library.unit.ww")); assert(!os.exists(strings.concat(mixedwork, "/library.a"))); // Filtering occurs only after loading every selected root. A structural // import failure in an otherwise skipped non-main sibling therefore // outranks actions and output mutation. rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); let mixedbadout: str = strings.concat(root, "/mixed-bad-", tags[si]); assert(os.mkdir(mixedbadout, 448i32) == 0); writefile(strings.concat(mixedbadout, "/sentinel"), "mixed-bad-sentinel\n"); let mixedbadav: []str = [driver(stages[si]), "build", "-I", source, "-o", mixedbadout, alpha, badlibrary]; runcommandenvdir(root, strings.concat("odir-mixed-bad-", tags[si]), mixedbadav, env, root, time.second, &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); assert(same(readfile(strings.concat(mixedbadout, "/sentinel")), "mixed-bad-sentinel\n")); if (si == 0) { mixedimportref = strings.dup(out.stderr); } else { assert(same(mixedimportref, out.stderr)); }; // Duplicate caller-visible basenames are output preflight too. A load // failure in either selected command wins; two valid commands then reject // the ambiguous publication without invoking any producer. let duplicateout: str = strings.concat(root, "/duplicate-output"); if (si == 0) { assert(os.mkdir(duplicateout, 448i32) == 0); writefile(strings.concat(duplicateout, "/sentinel"), "duplicate-sentinel\n"); }; rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); let duplicatebadav: []str = [driver(stages[si]), "build", "-I", source, "-o", duplicateout, duplicateone, duplicatetwo]; runcommandenvdir(root, strings.concat("odir-duplicate-import-", tags[si]), duplicatebadav, env, root, time.second, &out); expectexit(&out, 1); assert(has(out.stderr, "cannot find package missing.pkg\n")); assert(!has(out.stderr, "multiple commands produce output basename")); assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); assert(same(readfile(strings.concat(duplicateout, "/sentinel")), "duplicate-sentinel\n")); if (si == 0) { duplicateimportref = strings.dup(out.stderr); } else { assert(same(duplicateimportref, out.stderr)); }; let duplicateav: []str = [driver(stages[si]), "build", "-I", source, "-o", duplicateout, duplicateone, duplicatethree]; runcommandenvdir(root, strings.concat("odir-duplicate-", tags[si]), duplicateav, env, root, time.second, &out); expectexit(&out, 1); assert(same(out.stderr, strings.concat( "ww: multiple commands produce output basename \"tool\" in directory \"", duplicateout, "\"\n"))); assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); assert(same(readfile(strings.concat(duplicateout, "/sentinel")), "duplicate-sentinel\n")); assert(!directoryhasnew(duplicateout)); if (si == 0) { duplicateref = strings.dup(out.stderr); } else { assert(same(duplicateref, out.stderr)); }; // No-main and import failures are loading/planning rejections. They run // no tools, preserve existing contents, and do not mint missing output. rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); let badnonmainout: str = strings.concat(root, "/bad-nonmain-", tags[si]); assert(os.mkdir(badnonmainout, 448i32) == 0); writefile(strings.concat(badnonmainout, "/sentinel"), "bad-nonmain-sentinel\n"); let badnonmainav: []str = [driver(stages[si]), "build", "-I", source, "-o", badnonmainout, badlibrary]; runcommandenvdir(root, strings.concat("odir-nonmain-import-", tags[si]), badnonmainav, env, root, time.second, &out); expectexit(&out, 1); assert(has(out.stderr, "cannot find package missing.pkg\n")); assert(!has(out.stderr, "no main packages to build")); assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); assert(same(readfile(strings.concat(badnonmainout, "/sentinel")), "bad-nonmain-sentinel\n")); if (si == 0) { nomainimportref = strings.dup(out.stderr); } else { assert(same(nomainimportref, out.stderr)); }; let nonmainout: str = strings.concat(root, "/nonmain-", tags[si]); assert(os.mkdir(nonmainout, 448i32) == 0); writefile(strings.concat(nonmainout, "/sentinel"), "nonmain-sentinel\n"); let nonmainav: []str = [driver(stages[si]), "build", "-I", source, "-o", nonmainout, library]; runcommandenvdir(root, strings.concat("odir-nonmain-", tags[si]), nonmainav, env, root, time.second, &out); expectexit(&out, 1); assert(same(out.stderr, "ww: no main packages to build\n")); assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); assert(same(readfile(strings.concat(nonmainout, "/sentinel")), "nonmain-sentinel\n")); if (si == 0) { nomainref = strings.dup(out.stderr); } else { assert(same(nomainref, out.stderr)); }; let badroot: str = strings.concat(root, "/bad-output-", tags[si]); let badout: str = strings.concat(badroot, "/nested/"); let badav: []str = [driver(stages[si]), "build", "-I", source, "-o", badout, bad]; runcommandenvdir(root, strings.concat("odir-import-", tags[si]), badav, env, root, time.second, &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); assert(!os.exists(badroot)); if (si == 0) { importref = strings.dup(out.stderr); } else { assert(same(importref, out.stderr)); }; // The same pinned branch runs after a lone command's default output // name is synthesized. An existing directory is then a load-complete // rejection, while a non-main package has no default public output. let implicitcaller: str = strings.concat(root, "/implicit-caller-", tags[si]); let implicitout: str = strings.concat(implicitcaller, "/alpha"); mkdirall(implicitout); writefile(strings.concat(implicitout, "/sentinel"), "implicit-sentinel\n"); rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); let implicitav: []str = [driver(stages[si]), "build", "-I", source, alpha]; runcommandenvdir(root, strings.concat("odir-implicit-", tags[si]), implicitav, env, implicitcaller, time.second, &out); expectexit(&out, 1); assert(same(out.stderr, "ww: build output \"alpha\" already exists and is a directory\n")); assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); assert(same(readfile(strings.concat(implicitout, "/sentinel")), "implicit-sentinel\n")); if (si == 0) { implicitref = strings.dup(out.stderr); } else { assert(same(implicitref, out.stderr)); }; let implicitrawcaller: str = strings.concat(root, "/implicit-raw-caller-", tags[si]); let implicitrawout: str = strings.concat(implicitrawcaller, "/raw"); mkdirall(implicitrawout); writefile(strings.concat(implicitrawout, "/sentinel"), "implicit-raw-sentinel\n"); let implicitrawav: []str = [driver(stages[si]), "build", raw]; runcommandenvdir(root, strings.concat("odir-implicit-raw-", tags[si]), implicitrawav, env, implicitrawcaller, time.second, &out); expectexit(&out, 1); assert(same(out.stderr, "ww: build output \"raw\" already exists and is a directory\n")); assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); assert(same(readfile(strings.concat(implicitrawout, "/sentinel")), "implicit-raw-sentinel\n")); if (si == 0) { implicitrawref = strings.dup(out.stderr); } else { assert(same(implicitrawref, out.stderr)); }; let implicitbadcaller: str = strings.concat(root, "/implicit-bad-caller-", tags[si]); let implicitbadout: str = strings.concat(implicitbadcaller, "/bad"); mkdirall(implicitbadout); let implicitbadav: []str = [driver(stages[si]), "build", "-I", source, bad]; runcommandenvdir(root, strings.concat("odir-implicit-bad-", tags[si]), implicitbadav, env, implicitbadcaller, time.second, &out); expectexit(&out, 1); assert(has(out.stderr, "cannot find package missing.pkg\n")); assert(!has(out.stderr, "already exists and is a directory")); assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); if (si == 0) { implicitimportref = strings.dup(out.stderr); } else { assert(same(implicitimportref, out.stderr)); }; // Recursive selection reaches the same load-before-output boundary. Its // implicit basename collision is deferred until all selected imports pass. let recursivebadcaller: str = strings.concat(root, "/recursive-bad-caller-", tags[si]); let recursivebadout: str = strings.concat(recursivebadcaller, "/bad"); mkdirall(recursivebadout); writefile(strings.concat(recursivebadout, "/sentinel"), "recursive-bad-sentinel\n"); rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); let recursivebadav: []str = [driver(stages[si]), "build", "-I", source, strings.concat(bad, "/...")]; runcommandenvdir(root, strings.concat("odir-recursive-import-", tags[si]), recursivebadav, env, recursivebadcaller, time.second, &out); expectexit(&out, 1); assert(has(out.stderr, "cannot find package missing.pkg\n")); assert(!has(out.stderr, "already exists and is a directory")); assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); assert(same(readfile(strings.concat(recursivebadout, "/sentinel")), "recursive-bad-sentinel\n")); if (si == 0) { recursiveimplicitimportref = strings.dup(out.stderr); } else { assert(same(recursiveimplicitimportref, out.stderr)); }; let recursivecaller: str = strings.concat(root, "/recursive-caller-", tags[si]); let recursiveout: str = strings.concat(recursivecaller, "/alpha"); mkdirall(recursiveout); writefile(strings.concat(recursiveout, "/sentinel"), "recursive-sentinel\n"); let recursiveav: []str = [driver(stages[si]), "build", "-I", source, strings.concat(alpha, "/...")]; runcommandenvdir(root, strings.concat("odir-recursive-", tags[si]), recursiveav, env, recursivecaller, time.second, &out); expectexit(&out, 1); assert(same(out.stderr, "ww: build output \"alpha\" already exists and is a directory\n")); assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); assert(same(readfile(strings.concat(recursiveout, "/sentinel")), "recursive-sentinel\n")); assert(!directoryhasnew(recursiveout)); if (si == 0) { recursiveimplicitref = strings.dup(out.stderr); } else { assert(same(recursiveimplicitref, out.stderr)); }; let implicitlibcaller: str = strings.concat(root, "/implicit-library-caller-", tags[si]); let implicitlibout: str = strings.concat(implicitlibcaller, "/library"); let implicitlibwork: str = strings.concat(root, "/implicit-library-work-", tags[si]); mkdirall(implicitlibout); mkdirall(implicitlibwork); writefile(strings.concat(implicitlibout, "/sentinel"), "implicit-library-sentinel\n"); rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); let implicitlibav: []str = [driver(stages[si]), "build", "-w", implicitlibwork, "-I", source, library]; runcommandenvdir(root, strings.concat("odir-implicit-library-", tags[si]), implicitlibav, env, implicitlibcaller, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(occurrences(readfile(ctrace), "\n") == 1); assert(occurrences(readfile(atrace), "\n") == 1); assert(readfile(ltrace).len == 0); assert(same(readfile(strings.concat(implicitlibout, "/sentinel")), "implicit-library-sentinel\n")); assert(!os.exists(strings.concat(implicitlibout, "/library.a"))); // -S remains action-only. With the coordinator-owned -w tree it emits // semantic assembly but no command publication beneath -o. rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); let asmout: str = strings.concat(root, "/asm-output-", tags[si]); let asmwork: str = strings.concat(root, "/asm-work-", tags[si]); assert(os.mkdir(asmout, 448i32) == 0); writefile(strings.concat(asmout, "/sentinel"), "asm-sentinel\n"); let asmav: []str = [driver(stages[si]), "build", "-S", "-w", asmwork, "-I", source, "-o", asmout, "alpha"]; runcommandenvdir(root, strings.concat("odir-asm-", tags[si]), asmav, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(readfile(ctrace).len != 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); assert(!os.exists(strings.concat(asmout, "/alpha"))); assert(same(readfile(strings.concat(asmout, "/sentinel")), "asm-sentinel\n")); let asmbytes: str = readfile(strings.concat(asmwork, "/alpha.s")); if (si == 0) { asmref = strings.dup(asmbytes); } else { assert(same(asmref, asmbytes)); }; // WW's -S boundary stops before Go's install action. Directory form still // selects command actions, but publication-only path, collision, default, // and parent-creation checks are inapplicable. let asmmissingroot: str = strings.concat(root, "/asm-missing-", tags[si]); let asmmissingout: str = strings.concat(asmmissingroot, "/nested/"); let asmmissingav: []str = [driver(stages[si]), "build", "-S", "-w", asmwork, "-I", source, "-o", asmmissingout, alpha]; rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); runcommandenvdir(root, strings.concat("odir-asm-missing-", tags[si]), asmmissingav, env, root, time.second, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(!os.exists(asmmissingroot)); assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); let asmlongav: []str = [driver(stages[si]), "build", "-S", "-w", asmwork, "-I", source, "-o", longoutputdir, alpha]; runcommandenvdir(root, strings.concat("odir-asm-long-", tags[si]), asmlongav, env, root, time.second, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); let asmduplicatework: str = strings.concat(root, "/asm-duplicate-work-", tags[si]); let asmduplicateav: []str = [driver(stages[si]), "build", "-S", "-w", asmduplicatework, "-I", source, "-o", duplicateout, duplicateone, duplicatethree]; runcommandenvdir(root, strings.concat("odir-asm-duplicate-", tags[si]), asmduplicateav, 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(readfile(atrace).len == 0 && readfile(ltrace).len == 0); assert(same(readfile(strings.concat(duplicateout, "/sentinel")), "duplicate-sentinel\n")); rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); let asmdefaultav: []str = [driver(stages[si]), "build", "-S", "-w", asmwork, "-I", source, alpha]; runcommandenvdir(root, strings.concat("odir-asm-default-", tags[si]), asmdefaultav, env, implicitcaller, time.second, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); assert(same(readfile(strings.concat(implicitout, "/sentinel")), "implicit-sentinel\n")); let asmnonmainroot: str = strings.concat(root, "/asm-nonmain-", tags[si]); let asmnonmainout: str = strings.concat(asmnonmainroot, "/nested/"); let asmnonmainav: []str = [driver(stages[si]), "build", "-S", "-w", asmwork, "-I", source, "-o", asmnonmainout, library]; runcommandenvdir(root, strings.concat("odir-asm-nonmain-", tags[si]), asmnonmainav, env, root, time.second, &out); expectexit(&out, 1); assert(same(out.stderr, "ww: no main packages to build\n")); assert(!os.exists(asmnonmainroot)); assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0 && readfile(ltrace).len == 0); // Persistent action storage is independent of output disposition. Cold // and invalidated requests compile normally; warm commands still relink. rewritefile(alphafile, alphaoriginal); let work: str = strings.concat(root, "/persist-work-", tags[si]); let persistout: str = strings.concat(root, "/persist-output-", tags[si]); assert(os.mkdir(persistout, 448i32) == 0); writefile(strings.concat(persistout, "/sentinel"), "persist-sentinel\n"); let persistav: []str = [driver(stages[si]), "build", "-w", work, "-I", source, "-o", persistout, "alpha"]; rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); runcommandenvdir(root, strings.concat("odir-cold-", tags[si]), persistav, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(occurrences(readfile(ctrace), "\n") == 2); assert(occurrences(readfile(atrace), "\n") == 3); assert(occurrences(readfile(ltrace), "\n") == 1); let persistbin: str = strings.concat(persistout, "/alpha"); let coldbytes: str = readfile(persistbin); let normalizedc: str = normalizedtrace(readfile(ctrace), strings.concat(work, "/"), strings.concat(persistout, "/")); let normalizeda: str = normalizedtrace(readfile(atrace), strings.concat(work, "/"), strings.concat(persistout, "/")); let normalizedl: str = normalizedtrace(readfile(ltrace), strings.concat(work, "/"), strings.concat(persistout, "/")); if (si == 0) { coldctraceref = strings.dup(normalizedc); coldatraceref = strings.dup(normalizeda); coldltraceref = strings.dup(normalizedl); } else { assert(same(coldctraceref, normalizedc)); assert(same(coldatraceref, normalizeda)); assert(same(coldltraceref, normalizedl)); }; rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); runcommandenvdir(root, strings.concat("odir-warm-", tags[si]), persistav, 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(persistbin))); let warmtrace: str = normalizedtrace(readfile(ltrace), strings.concat(work, "/"), strings.concat(persistout, "/")); if (si == 0) { warmltraceref = strings.dup(warmtrace); } else { assert(same(warmltraceref, warmtrace)); }; rewritefile(alphafile, alphachanged); rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); runcommandenvdir(root, strings.concat("odir-invalidated-", tags[si]), persistav, 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); let changedbytes: str = readfile(persistbin); assert(!same(coldbytes, changedbytes)); if (si == 0) { changedbinref = strings.dup(changedbytes); } else { assert(same(changedbinref, changedbytes)); }; let persistrunav: []str = [persistbin]; runcommand(root, strings.concat("odir-invalidated-run-", tags[si]), persistrunav, time.second, &out); expectexit(&out, 5); normalizedc = normalizedtrace(readfile(ctrace), strings.concat(work, "/"), strings.concat(persistout, "/")); normalizeda = normalizedtrace(readfile(atrace), strings.concat(work, "/"), strings.concat(persistout, "/")); normalizedl = normalizedtrace(readfile(ltrace), strings.concat(work, "/"), strings.concat(persistout, "/")); if (si == 0) { changedctraceref = strings.dup(normalizedc); changedatraceref = strings.dup(normalizeda); changedltraceref = strings.dup(normalizedl); } else { assert(same(changedctraceref, normalizedc)); assert(same(changedatraceref, normalizeda)); assert(same(changedltraceref, normalizedl)); }; let ai: i32 = 0; for (ai < suffixes.len) { let artifact: str = readfile(strings.concat(work, "/alpha", suffixes[ai])); if (si == 0) { artifactrefs[ai] = strings.dup(artifact); } else { assert(same(artifactrefs[ai], artifact)); }; ai += 1; }; assert((permissionmode(persistbin) & 73u32) != 0u32); assert(!os.exists(strings.concat(persistout, "/alpha.sepwork"))); // Compiler failure, linker failure, and linker interruption cannot // replace the prior command or commit an invalid persistent generation. let kept: []str = alloc([], suffixes.len: u64)!; ai = 0; for (ai < suffixes.len) { append(kept, strings.dup(readfile(strings.concat(work, "/alpha", suffixes[ai])))); ai += 1; }; rewritefile(alphafile, alphafailing); env[compilemodeindex] = "WW_ODIR_COMPILE_MODE=fail"; rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); runcommandenvdir(root, strings.concat("odir-compiler-fail-", tags[si]), persistav, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, "injected output-directory compiler failure\n")); assert(has(out.stderr, "ww: w6c failed for alpha\n")); assert(readfile(atrace).len == 0 && readfile(ltrace).len == 0); assert(same(changedbytes, readfile(persistbin))); if (si == 0) { compilerfailref = strings.dup(out.stderr); } else { assert(same(compilerfailref, out.stderr)); }; env[compilemodeindex] = "WW_ODIR_COMPILE_MODE="; env[linkmodeindex] = "WW_ODIR_LINK_MODE=fail"; rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); runcommandenvdir(root, strings.concat("odir-linker-fail-", tags[si]), persistav, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, "injected output-directory linker failure\n")); assert(has(out.stderr, "ww: w6l failed\n")); assert(same(changedbytes, readfile(persistbin))); if (si == 0) { linkerfailref = strings.dup(out.stderr); } else { assert(same(linkerfailref, out.stderr)); }; env[linkmodeindex] = "WW_ODIR_LINK_MODE=signal"; rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, ""); runcommandenvdir(root, strings.concat("odir-linker-signal-", tags[si]), persistav, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, "ww: w6l failed\n")); assert(same(changedbytes, readfile(persistbin))); if (si == 0) { linkersignalref = strings.dup(out.stderr); } else { assert(same(linkersignalref, out.stderr)); }; ai = 0; for (ai < suffixes.len) { assert(same(kept[ai], readfile(strings.concat(work, "/alpha", suffixes[ai])))); ai += 1; }; assert(!directoryhasnew(work) && !directoryhasnew(persistout)); assert(!directoryhasfragment(work, ".wwtxn.")); assert(!directoryhasfragment(persistout, ".wwtxn.")); assert(same(readfile(strings.concat(persistout, "/sentinel")), "persist-sentinel\n")); // The raw direct route uses the same creation ledger. A late failure // removes every directory minted for this request and leaves no stage. env[linkmodeindex] = "WW_ODIR_LINK_MODE=fail"; let rawfailroot: str = strings.concat(root, "/raw-fail-", tags[si]); let rawfailout: str = strings.concat(rawfailroot, "/nested/"); let rawfailav: []str = [driver(stages[si]), "build", "-o", rawfailout, raw]; runcommandenvdir(root, strings.concat("odir-raw-fail-", tags[si]), rawfailav, env, root, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, "injected output-directory linker failure\n")); assert(!os.exists(rawfailroot)); if (si == 0) { rawrollbackref = strings.dup(out.stderr); } else { assert(same(rawrollbackref, out.stderr)); }; env[linkmodeindex] = "WW_ODIR_LINK_MODE="; assert(!directoryhasfragment(root, ".wwtxn.")); si += 1; }; // Distinct Cstage and WWstage requests may build simultaneously without // sharing output, work, stage, transaction, or process ownership. rewritefile(alphafile, alphaoriginal); let concurrentcout: str = strings.concat(root, "/concurrent-c-output"); let concurrentwout: str = strings.concat(root, "/concurrent-ww-output"); let concurrentcwork: str = strings.concat(root, "/concurrent-c-work"); let concurrentwwork: str = strings.concat(root, "/concurrent-ww-work"); mkdirall(concurrentcout); mkdirall(concurrentwout); writefile(strings.concat(concurrentcout, "/sentinel"), "c-sentinel\n"); writefile(strings.concat(concurrentwout, "/sentinel"), "ww-sentinel\n"); let cav: []str = [driver("ww"), "build", "-w", concurrentcwork, "-I", source, "-o", concurrentcout, "alpha"]; let wav: []str = [driver("ww_ww"), "build", "-w", concurrentwwork, "-I", source, "-o", concurrentwout, "alpha"]; let cc: exec.command; cc.path = cav[0]; cc.argv = cav; cc.env = os.getenvs(); cc.dir = root; cc.stdoutpath = strings.concat(root, "/concurrent-c.stdout"); cc.stderrpath = strings.concat(root, "/concurrent-c.stderr"); cc.deadline = time.add(time.now(time.clock.monotonic), (120i64 * (time.second: i64)): time.duration); cc.grace = (100i64 * (time.millisecond: i64)): time.duration; let wc: exec.command; wc.path = wav[0]; wc.argv = wav; wc.env = os.getenvs(); wc.dir = root; wc.stdoutpath = strings.concat(root, "/concurrent-ww.stdout"); wc.stderrpath = strings.concat(root, "/concurrent-ww.stderr"); wc.deadline = cc.deadline; wc.grace = cc.grace; let cp: exec.process; let wp: exec.process; exec.start(&cp, &cc); exec.start(&wp, &wc); let cdone: bool = false; let wdone: bool = false; for (!cdone || !wdone) { if (!cdone) { cdone = exec.poll(&cp); }; if (!wdone) { wdone = exec.poll(&wp); }; if (!cdone || !wdone) { time.sleep(time.millisecond, time.clock.monotonic); }; }; assert(cp.result.errno == 0 && cp.result.cleanuperrno == 0); assert(wp.result.errno == 0 && wp.result.cleanuperrno == 0); assert(cp.result.termination == exec.termination.EXIT && cp.result.code == 0); assert(wp.result.termination == exec.termination.EXIT && wp.result.code == 0); assert(readfile(cc.stdoutpath).len == 0 && readfile(cc.stderrpath).len == 0); assert(readfile(wc.stdoutpath).len == 0 && readfile(wc.stderrpath).len == 0); assert(same(readfile(strings.concat(concurrentcout, "/alpha")), readfile(strings.concat(concurrentwout, "/alpha")))); assert(same(readfile(strings.concat(concurrentcout, "/sentinel")), "c-sentinel\n")); assert(same(readfile(strings.concat(concurrentwout, "/sentinel")), "ww-sentinel\n")); assert(!directoryhasnew(concurrentcout) && !directoryhasnew(concurrentwout)); assert(!directoryhasnew(concurrentcwork) && !directoryhasnew(concurrentwwork)); assert(!directoryhasfragment(root, ".wwtxn.")); clean(root); }; @test fn build_output_permissions_follow_umask() void = { let root: str = fresh(); let source: str = strings.concat(root, "/source"); let alpha: str = strings.concat(source, "/alpha"); let fan: str = strings.concat(source, "/fan"); let fanalpha: str = strings.concat(fan, "/alpha"); let fanbeta: str = strings.concat(fan, "/beta"); let fanlib: str = strings.concat(fan, "/library"); let library: str = strings.concat(source, "/library"); let check: str = strings.concat(source, "/check"); mkdirall(alpha); mkdirall(fanalpha); mkdirall(fanbeta); mkdirall(fanlib); mkdirall(library); mkdirall(check); let alphafile: str = strings.concat(alpha, "/main.ww"); let alphabase: str = strings.concat( "package main;\n", "fn main() i32 = { return 21; };\n"); let alphachanged: str = strings.concat( "package main;\n", "fn main() i32 = { return 22; };\n"); writefile(alphafile, alphabase); writefile(strings.concat(fanalpha, "/main.ww"), "package main;\nfn main() i32 = { return 31; };\n"); writefile(strings.concat(fanbeta, "/main.ww"), "package main;\nfn main() i32 = { return 32; };\n"); writefile(strings.concat(fanlib, "/library.ww"), "package library;\nexport fn value() i32 = { return 33; };\n"); writefile(strings.concat(library, "/library.ww"), "package library;\nexport fn value() i32 = { return 41; };\n"); let raw: str = strings.concat(source, "/raw.ww"); writefile(raw, "package main;\nfn main() i32 = { return 23; };\n"); writefile(strings.concat(check, "/check.ww"), "package check;\nfn value() i32 = { return 1; };\n"); writefile(strings.concat(check, "/check_test.ww"), strings.concat( "package check;\n", "@test fn retained() void = { assert(value() == 1); };\n")); let launcher: str = driver("package-umaskexec"); let stages: []str = ["ww", "ww_ww"]; let tags: []str = ["c", "ww"]; let linkers: []str = ["w6l", "w6l_ww"]; let coldref: str = ""; let changedref: str = ""; let rawref: str = ""; let defaultref: str = ""; let fanalpharef: str = ""; let fanbetaref: str = ""; let archiverefs: []str = ["", ""]; let testref: str = ""; let faildiagref: str = ""; let occupieddiagref: str = ""; let linkerwrapper: str = strings.concat(root, "/mode-w6l.sh"); writeexecutable(linkerwrapper, strings.concat( "#!/bin/sh\n", "for arg do case \"$arg\" in */beta.new)\n", " printf 'injected build-mode linker failure\\n' >&2\n", " exit 97;; esac; done\n", "exec \"$WW_MODE_REAL_LINKER\" \"$@\"\n")); let baseenv: []str = os.getenvs(); let si: i32 = 0; for (si < stages.len) { let work: str = strings.concat(root, "/work-", tags[si]); mkdirall(work); let binary: str = strings.concat(root, "/command-", tags[si]); let coldav: []str = [launcher, "000", driver(stages[si]), "build", "-w", work, "-I", source, "-o", binary, alpha]; let out: commandout; runcommand(root, strings.concat("mode-cold-", tags[si]), coldav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(permissionmode(binary) == 511u32); assert(!os.exists(strings.concat(binary, ".new"))); assert(!os.exists(strings.concat(binary, "-go-tmp-umask"))); let coldbytes: str = readfile(binary); let coldarchive: str = readfile(strings.concat(work, "/alpha.a")); if (si == 0) { coldref = strings.dup(coldbytes); } else { assert(same(coldref, coldbytes)); }; let runav: []str = [binary]; runcommand(root, strings.concat("mode-cold-run-", tags[si]), runav, time.second, &out); expectexit(&out, 21); // Umask is request-local publication metadata: the warm graph stays // byte-stable while replacing the same output with the current mode. let warmav: []str = [launcher, "077", driver(stages[si]), "build", "-w", work, "-I", source, "-o", binary, alpha]; runcommand(root, strings.concat("mode-warm-", tags[si]), warmav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(permissionmode(binary) == 448u32); assert(same(coldbytes, readfile(binary))); assert(same(coldarchive, readfile(strings.concat(work, "/alpha.a")))); assert(!directoryhasnew(work)); // Source invalidation changes the command bytes, not the creation rule. rewritefile(alphafile, alphachanged); let changedav: []str = [launcher, "027", driver(stages[si]), "build", "-w", work, "-I", source, "-o", binary, alpha]; runcommand(root, strings.concat("mode-changed-", tags[si]), changedav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(permissionmode(binary) == 488u32); let changedbytes: str = readfile(binary); assert(!same(coldbytes, changedbytes)); if (si == 0) { changedref = strings.dup(changedbytes); } else { assert(same(changedref, changedbytes)); }; let changedrun: []str = [binary]; runcommand(root, strings.concat("mode-changed-run-", tags[si]), changedrun, time.second, &out); expectexit(&out, 22); rewritefile(alphafile, alphabase); // A default command output and an explicit raw-file output use the same // link-install permission, independent of their naming route. let defaultdir: str = strings.concat(root, "/default-", tags[si]); let defaultwork: str = strings.concat(root, "/default-work-", tags[si]); mkdirall(defaultdir); mkdirall(defaultwork); let defaultav: []str = [launcher, "000", driver(stages[si]), "build", "-w", defaultwork, "-I", source, alpha]; runcommanddir(root, strings.concat("mode-default-", tags[si]), defaultdir, defaultav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let defaultbin: str = strings.concat(defaultdir, "/alpha"); assert(permissionmode(defaultbin) == 511u32); let defaultbytes: str = readfile(defaultbin); if (si == 0) { defaultref = strings.dup(defaultbytes); } else { assert(same(defaultref, defaultbytes)); }; let rawbin: str = strings.concat(root, "/raw-", tags[si]); let rawav: []str = [launcher, "007", driver(stages[si]), "build", "-o", rawbin, raw]; runcommand(root, strings.concat("mode-raw-", tags[si]), rawav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(permissionmode(rawbin) == 504u32); let rawbytes: str = readfile(rawbin); if (si == 0) { rawref = strings.dup(rawbytes); } else { assert(same(rawref, rawbytes)); }; // Directory fan-out creates each command independently, skips the // library product, and leaves no request-private publication residue. let fanout: str = strings.concat(root, "/fanout-", tags[si]); let fanwork: str = strings.concat(root, "/fanwork-", tags[si]); mkdirall(fanout); mkdirall(fanwork); let fanav: []str = [launcher, "027", driver(stages[si]), "build", "-w", fanwork, "-I", source, "-o", fanout, strings.concat(fan, "/...")]; runcommand(root, strings.concat("mode-fan-", tags[si]), fanav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let fanalphabin: str = strings.concat(fanout, "/alpha"); let fanbetabin: str = strings.concat(fanout, "/beta"); assert(permissionmode(fanalphabin) == 488u32); assert(permissionmode(fanbetabin) == 488u32); assert(!os.exists(strings.concat(fanout, "/library"))); assert(!directoryhasnew(fanout) && !directoryhasnew(fanwork)); let fanalphabytes: str = readfile(fanalphabin); let fanbetabytes: str = readfile(fanbetabin); if (si == 0) { fanalpharef = strings.dup(fanalphabytes); fanbetaref = strings.dup(fanbetabytes); } else { assert(same(fanalpharef, fanalphabytes)); assert(same(fanbetaref, fanbetabytes)); }; // The non-link branch of BuildInstallFunc uses 0666. WW's required // adjacent interface follows the archive's data-file permission. let libwork: str = strings.concat(root, "/libwork-", tags[si]); mkdirall(libwork); let archive: str = strings.concat(root, "/library-", tags[si], ".a"); let libav: []str = [launcher, "000", driver(stages[si]), "build", "-w", libwork, "-I", source, "-o", archive, library]; runcommand(root, strings.concat("mode-library-", tags[si]), libav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(permissionmode(archive) == 438u32); assert(permissionmode(strings.concat(archive, ".wwi")) == 438u32); archiverefs[si] = strings.dup(readfile(archive)); let libwarm: []str = [launcher, "077", driver(stages[si]), "build", "-w", libwork, "-I", source, "-o", archive, library]; runcommand(root, strings.concat("mode-library-warm-", tags[si]), libwarm, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(permissionmode(archive) == 384u32); assert(permissionmode(strings.concat(archive, ".wwi")) == 384u32); assert(same(archiverefs[si], readfile(archive))); assert(!os.exists(strings.concat(archive, ".new"))); assert(!os.exists(strings.concat(archive, ".wwi.new"))); // Test-binary retention is a separate publication branch, but the // ordinary linked-executable rule remains identical and byte-stable. let testbin: str = strings.concat(root, "/check-", tags[si], ".test"); let testav: []str = [launcher, "000", driver(stages[si]), "test", "-c", "-I", source, "-o", testbin, check]; runcommand(root, strings.concat("mode-test-", tags[si]), testav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(permissionmode(testbin) == 511u32); let testbytes: str = readfile(testbin); if (si == 0) { testref = strings.dup(testbytes); } else { assert(same(testref, testbytes)); }; let testwarm: []str = [launcher, "077", driver(stages[si]), "test", "-c", "-I", source, "-o", testbin, check]; runcommand(root, strings.concat("mode-test-warm-", tags[si]), testwarm, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(permissionmode(testbin) == 448u32); assert(same(testbytes, readfile(testbin))); assert(!os.exists(strings.concat(testbin, ".new"))); // Assembly-only builds do not publish an inode to which the permission // contract could apply. let asmdir: str = strings.concat(root, "/asm-dir-", tags[si]); let asmwork: str = strings.concat(root, "/asm-work-", tags[si]); mkdirall(asmdir); mkdirall(asmwork); let asmav: []str = [launcher, "000", driver(stages[si]), "build", "-S", "-w", asmwork, "-I", source, alpha]; runcommanddir(root, strings.concat("mode-asm-", tags[si]), asmdir, asmav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(!os.exists(strings.concat(asmdir, "/alpha"))); // A later link failure rolls back already staged siblings. Existing // destination bytes and modes survive; neither transaction nor action // stages remain. let failout: str = strings.concat(root, "/failout-", tags[si]); let failwork: str = strings.concat(root, "/failwork-", tags[si]); mkdirall(failout); mkdirall(failwork); writefile(strings.concat(failout, "/alpha"), "old-alpha\n"); writefile(strings.concat(failout, "/beta"), "old-beta\n"); let failenv: []str = alloc([], (baseenv.len + 2): u64)!; let ei: i32 = 0; for (ei < baseenv.len) { if (!strings.hasprefix(baseenv[ei], "WW_W6L=") && !strings.hasprefix(baseenv[ei], "WW_MODE_REAL_LINKER=")) { append(failenv, baseenv[ei]); }; ei += 1; }; append(failenv, strings.concat("WW_W6L=", linkerwrapper)); append(failenv, strings.concat("WW_MODE_REAL_LINKER=", driver(linkers[si]))); let failav: []str = [launcher, "000", driver(stages[si]), "build", "-w", failwork, "-I", source, "-o", failout, strings.concat(fan, "/...")]; runcommandenv(root, strings.concat("mode-failure-", tags[si]), failav, failenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, "injected build-mode linker failure\n")); assert(has(out.stderr, "ww: w6l failed\n")); assert(same(readfile(strings.concat(failout, "/alpha")), "old-alpha\n")); assert(same(readfile(strings.concat(failout, "/beta")), "old-beta\n")); assert(permissionmode(strings.concat(failout, "/alpha")) == 384u32); assert(permissionmode(strings.concat(failout, "/beta")) == 384u32); assert(!directoryhasnew(failout) && !directoryhasnew(failwork)); let faildiag: str = normalizedtrace(out.stderr, strings.concat(failwork, "/"), failout); if (si == 0) { faildiagref = strings.dup(faildiag); } else { assert(same(faildiagref, faildiag)); }; // An occupied caller-visible stage is rejected before production and // leaves both the prior output and the occupied inode untouched. let occupied: str = strings.concat(root, "/occupied"); let occupiedstage: str = strings.concat(occupied, ".new"); if (si == 0) { writefile(occupied, "old-output\n"); writefile(occupiedstage, "occupied-stage\n"); }; let occupiedav: []str = [launcher, "000", driver(stages[si]), "build", "-I", source, "-o", occupied, alpha]; runcommand(root, strings.concat("mode-occupied-", tags[si]), occupiedav, time.second, &out); expectexit(&out, 1); assert(same(readfile(occupied), "old-output\n")); assert(same(readfile(occupiedstage), "occupied-stage\n")); assert(permissionmode(occupied) == 384u32); assert(permissionmode(occupiedstage) == 384u32); if (si == 0) { occupieddiagref = strings.dup(out.stderr); } else { assert(same(occupieddiagref, out.stderr)); }; si += 1; }; assert(same(archiverefs[0], archiverefs[1])); let artifacts: []str = [".unit.ww", ".wwi", ".s", ".o", ".a", ".init.unit.ww", ".init.s", ".init.o"]; let ai: i32 = 0; for (ai < artifacts.len) { assert(same(readfile(strings.concat(root, "/work-c/alpha", artifacts[ai])), readfile(strings.concat(root, "/work-ww/alpha", artifacts[ai])))); ai += 1; }; // Two independent drivers execute simultaneously with different umasks. // Their process-local masks cannot leak across stages, works, or outputs. let parallelcwork: str = strings.concat(root, "/parallel-c-work"); let parallelwwwork: str = strings.concat(root, "/parallel-ww-work"); mkdirall(parallelcwork); mkdirall(parallelwwwork); let parallelcbin: str = strings.concat(root, "/parallel-c"); let parallelwwbin: str = strings.concat(root, "/parallel-ww"); let cav: []str = [launcher, "002", driver("ww"), "build", "-w", parallelcwork, "-I", source, "-o", parallelcbin, fanalpha]; let wav: []str = [launcher, "077", driver("ww_ww"), "build", "-w", parallelwwwork, "-I", source, "-o", parallelwwbin, fanbeta]; let cc: exec.command; cc.path = launcher; cc.argv = cav; cc.env = os.getenvs(); cc.dir = repo(); cc.stdoutpath = strings.concat(root, "/parallel-c.stdout"); cc.stderrpath = strings.concat(root, "/parallel-c.stderr"); cc.deadline = time.add(time.now(time.clock.monotonic), (120i64 * (time.second: i64)): time.duration); cc.grace = (100i64 * (time.millisecond: i64)): time.duration; let wc: exec.command; wc.path = launcher; wc.argv = wav; wc.env = os.getenvs(); wc.dir = repo(); wc.stdoutpath = strings.concat(root, "/parallel-ww.stdout"); wc.stderrpath = strings.concat(root, "/parallel-ww.stderr"); wc.deadline = time.add(time.now(time.clock.monotonic), (120i64 * (time.second: i64)): time.duration); wc.grace = (100i64 * (time.millisecond: i64)): time.duration; let cp: exec.process; let wp: exec.process; exec.start(&cp, &cc); exec.start(&wp, &wc); let cdone: bool = false; let wdone: bool = false; for (!cdone || !wdone) { if (!cdone) { cdone = exec.poll(&cp); }; if (!wdone) { wdone = exec.poll(&wp); }; if (!cdone || !wdone) { time.sleep(time.millisecond, time.clock.monotonic); }; }; assert(cp.result.errno == 0 && cp.result.cleanuperrno == 0); assert(wp.result.errno == 0 && wp.result.cleanuperrno == 0); assert(cp.result.termination == exec.termination.EXIT && cp.result.code == 0); assert(wp.result.termination == exec.termination.EXIT && wp.result.code == 0); assert(readfile(cc.stdoutpath).len == 0 && readfile(cc.stderrpath).len == 0); assert(readfile(wc.stdoutpath).len == 0 && readfile(wc.stderrpath).len == 0); assert(permissionmode(parallelcbin) == 509u32); assert(permissionmode(parallelwwbin) == 448u32); assert(!directoryhasnew(parallelcwork)); 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); }; // Go 1.26.5 ignores one UTF-8 BOM at the first codepoint of each physical // source and rejects U+FEFF everywhere else. The lexer owns direct and // imports-only parsing; the directory composer preserves that per-file // boundary when it builds one synthetic unit. @test fn utf8_bom_is_per_source_and_only_first() void = { let root: str = fresh(); let source: str = strings.concat(root, "/source"); let dep: str = strings.concat(source, "/dep"); let app: str = strings.concat(source, "/app"); let tests: str = strings.concat(source, "/bomtests"); let onlytests: str = strings.concat(source, "/onlytests"); let bad: str = strings.concat(source, "/bad"); let badtest: str = strings.concat(source, "/badtest"); let warm: str = strings.concat(source, "/warm"); mkdirall(dep); mkdirall(app); mkdirall(tests); mkdirall(onlytests); mkdirall(bad); mkdirall(badtest); mkdirall(warm); writebomfile(strings.concat(dep, "/dep.ww"), strings.concat( "package dep;\n", "export fn value() i32 = { return 41; };\n")); writebomfile(strings.concat(dep, "/more.ww"), strings.concat( "package dep;\n", "export fn more() i32 = { return 1; };\n")); let appmain: str = strings.concat(app, "/main.ww"); let appvalid: str = strings.concat( "package main;\nimport dep;\n", "fn main() i32 = { return dep.value() + dep.more() + local() - 43; };\n"); writebomfile(appmain, appvalid); writebomfile(strings.concat(app, "/local.ww"), strings.concat( "package main;\n", "fn local() i32 = { return 1; };\n")); // Wrong-target sources are excluded before source decoding. writemidbomfile(strings.concat(app, "/ignored_windows.ww"), "not a package ", " and not valid WW\n"); // Direct frontend ownership: the initial marker is invisible at 1:1 and // both stages emit the same assembly; a later marker is one codepoint error. let direct: str = strings.concat(root, "/direct.ww"); let directbad: str = strings.concat(root, "/direct-bad.ww"); writebomfile(direct, "package main;\nfn main() i32 = { return 0; };\n"); writemidbomfile(directbad, "package main;\nfn main() i32 = { let s: str = \"", "\"; return s.len; };\n"); let directouts: []str = [strings.concat(root, "/direct-c.s"), strings.concat(root, "/direct-ww.s")]; let directbadouts: []str = [strings.concat(root, "/direct-bad-c.s"), strings.concat(root, "/direct-bad-ww.s")]; let compilers: []str = ["w6c", "w6c_ww"]; let tags: []str = ["c", "ww"]; let middiags: []str = ["", ""]; let i: i32 = 0; for (i < compilers.len) { let av: []str = [driver(compilers[i]), "-c", "--command-package", "-o", directouts[i], direct]; let out: commandout; runcommand(root, strings.concat("bom-direct-valid-", tags[i]), av, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); let badav: []str = [driver(compilers[i]), "-c", "--command-package", "-o", directbadouts[i], directbad]; runcommand(root, strings.concat("bom-direct-invalid-", tags[i]), badav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0); assert(has(out.stderr, ":2:33: error: invalid BOM in the middle of the file\n")); assert(occurrences(out.stderr, "invalid BOM in the middle of the file") == 1); middiags[i] = strings.dup(out.stderr); assert(!os.exists(directbadouts[i])); i += 1; }; assert(same(middiags[0], middiags[1])); assert(same(readfile(directouts[0]), readfile(directouts[1]))); // Build consumes multiple BOM-prefixed sources in both the root and its // imported dependency. The marker never enters unit bytes or identity. let stages: []str = ["ww", "ww_ww"]; let works: []str = [strings.concat(root, "/work-c"), strings.concat(root, "/work-ww")]; let bins: []str = [strings.concat(root, "/app-c"), strings.concat(root, "/app-ww")]; i = 0; for (i < stages.len) { mkdirall(works[i]); let av: []str = [driver(stages[i]), "build", "-w", works[i], "-I", source, "-o", bins[i], "app"]; let out: commandout; runcommand(root, strings.concat("bom-build-", tags[i]), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(!directoryhasnew(works[i]) && !directoryhasfragment(works[i], ".wwtxn.")); let runav: []str = [bins[i]]; runcommand(root, strings.concat("bom-run-", tags[i]), runav, time.second, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); i += 1; }; assert(same(readfile(bins[0]), readfile(bins[1]))); let actions: []str = ["dep", "app"]; let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"]; let commandsuffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a", ".init.unit.ww", ".init.s", ".init.o"]; let ai: i32 = 0; for (ai < actions.len) { let unit: str = readfile(strings.concat(works[0], "/", actions[ai], ".unit.ww")); assert(!hasbom(unit) && has(unit, "\n package ")); let si: i32 = 0; for (si < suffixes.len) { assert(same(readfile(strings.concat(works[0], "/", actions[ai], suffixes[si])), readfile(strings.concat(works[1], "/", actions[ai], suffixes[si])))); si += 1; }; ai += 1; }; let ci: i32 = suffixes.len; for (ci < commandsuffixes.len) { assert(same(readfile(strings.concat(works[0], "/app", commandsuffixes[ci])), readfile(strings.concat(works[1], "/app", commandsuffixes[ci])))); ci += 1; }; // Production, same-package, external-package, and test-only roles all get // their own offset-zero allowance and execute through the ordinary harness. writebomfile(strings.concat(tests, "/prod.ww"), strings.concat( "package bomtests;\n", "export fn value() i32 = { return 42; };\n")); writebomfile(strings.concat(tests, "/same_test.ww"), strings.concat( "package bomtests;\n", "@test fn same_bom() void = { assert(value() == 42); };\n")); writebomfile(strings.concat(tests, "/external_test.ww"), strings.concat( "package bomtests_test;\nimport bomtests;\n", "@test fn external_bom() void = { assert(bomtests.value() == 42); };\n")); writebomfile(strings.concat(onlytests, "/only_test.ww"), "package onlytests;\n@test fn only_bom() void = { assert(true); };\n"); let testrefs: []str = ["", ""]; let onlyrefs: []str = ["", ""]; let testworks: []str = [strings.concat(root, "/test-work-c"), strings.concat(root, "/test-work-ww")]; i = 0; for (i < stages.len) { mkdirall(testworks[i]); let av: []str = [driver(stages[i]), "test", "-w", testworks[i], "-I", source, "bomtests"]; let out: commandout; runcommand(root, strings.concat("bom-test-", tags[i]), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0 && has(out.stdout, "2 passed, 0 failed")); testrefs[i] = strings.dup(out.stdout); let onlyav: []str = [driver(stages[i]), "test", "-I", source, "onlytests"]; runcommand(root, strings.concat("bom-test-only-", tags[i]), onlyav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0 && has(out.stdout, "1 passed, 0 failed")); onlyrefs[i] = strings.dup(out.stdout); i += 1; }; assert(same(testrefs[0], testrefs[1]) && same(onlyrefs[0], onlyrefs[1])); let testactions: []str = ["bomtests-internal-test", "bomtests_test-external-test", "bomtests-test-main"]; ai = 0; for (ai < testactions.len) { let testsuffixes: []str = suffixes; if (ai == 2) { testsuffixes = commandsuffixes; }; let tsi: i32 = 0; for (tsi < testsuffixes.len) { assert(same(readfile(strings.concat(testworks[0], "/", testactions[ai], testsuffixes[tsi])), readfile(strings.concat(testworks[1], "/", testactions[ai], testsuffixes[tsi])))); tsi += 1; }; if (ai < 2) { let testunit: str = readfile(strings.concat(testworks[0], "/", testactions[ai], ".unit.ww")); assert(!hasbom(testunit) && has(testunit, "\n package ")); }; ai += 1; }; assert(!directoryhasnew(testworks[0]) && !directoryhasnew(testworks[1])); // A later BOM is rejected during request parsing. Missing-import graph // resolution, compiler/assembler/linker producers, runtime, and publication // are therefore never reached; cold workdirs and products stay empty. writemidbomfile(strings.concat(bad, "/main.ww"), "package main;\n// ", "\nimport missing;\nfn main() i32 = { return 0; };\n"); writefile(strings.concat(badtest, "/prod.ww"), "package badtest;\nfn value() i32 = { return 42; };\n"); writemidbomfile(strings.concat(badtest, "/prod_test.ww"), "package badtest;\n// ", "\n@test fn must_not_run() void = { abort(\"ran\"); };\n"); let baddiags: []str = ["", ""]; let badtestdiags: []str = ["", ""]; i = 0; for (i < stages.len) { let badwork: str = strings.concat(root, "/bad-work-", tags[i]); let badout: str = strings.concat(root, "/bad-output-", tags[i]); mkdirall(badwork); let av: []str = [driver(stages[i]), "build", "-w", badwork, "-I", source, "-o", badout, "bad"]; let out: commandout; runcommand(root, strings.concat("bom-bad-build-", tags[i]), av, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0); assert(has(out.stderr, ":2:4: error: invalid BOM in the middle of the file\n")); assert(!has(out.stderr, "cannot find import") && !has(out.stderr, "w6c failed")); baddiags[i] = strings.dup(out.stderr); assert(!os.exists(badout) && directoryisempty(badwork)); let testav: []str = [driver(stages[i]), "test", "-I", source, "badtest"]; runcommand(root, strings.concat("bom-bad-test-", tags[i]), testav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stdout, "FAIL\n")); assert(has(out.stderr, "invalid BOM in the middle of the file") && !has(out.stderr, "must_not_run ...")); badtestdiags[i] = strings.dup(out.stderr); i += 1; }; assert(same(baddiags[0], baddiags[1]) && same(badtestdiags[0], badtestdiags[1])); // A warm invalid edit cannot mutate committed action bytes or publication. // Restoring the exact leading-BOM source composes the same unit and reuses // the generation without invoking the compiler. let warmpath: str = strings.concat(warm, "/main.ww"); let warmvalid: str = "package main;\nfn main() i32 = { return 0; };\n"; writebomfile(warmpath, warmvalid); let wrapper: str = strings.concat(root, "/compiler-wrapper.sh"); writeexecutable(wrapper, strings.concat( "#!/bin/sh\n", "printf 'compile\\n' >> \"$WW_BOM_TRACE\"\n", "exec \"$WW_BOM_REAL\" \"$@\"\n")); i = 0; for (i < stages.len) { let warmwork: str = strings.concat(root, "/warm-work-", tags[i]); let warmout: str = strings.concat(root, "/warm-output-", tags[i]); let trace: str = strings.concat(root, "/warm-trace-", tags[i]); mkdirall(warmwork); writefile(trace, ""); let env: []str = os.getenvs(); append(env, strings.concat("WW_W6C=", wrapper)); append(env, strings.concat("WW_BOM_TRACE=", trace)); append(env, strings.concat("WW_BOM_REAL=", driver(compilers[i]))); let av: []str = [driver(stages[i]), "build", "-w", warmwork, "-I", source, "-o", warmout, "warm"]; let out: commandout; runcommandenv(root, strings.concat("bom-warm-cold-", tags[i]), av, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && readfile(trace).len != 0); let refs: []str = alloc([], commandsuffixes.len: u64)!; let si: i32 = 0; for (si < commandsuffixes.len) { append(refs, strings.dup(readfile(strings.concat(warmwork, "/warm", commandsuffixes[si])))); si += 1; }; let binref: str = strings.dup(readfile(warmout)); rewritefile(trace, ""); rewritemidbomfile(warmpath, "package main;\n// ", "\nfn main() i32 = { return 0; };\n"); runcommandenv(root, strings.concat("bom-warm-invalid-", tags[i]), av, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && has(out.stderr, "invalid BOM in the middle of the file")); assert(readfile(trace).len == 0 && same(binref, readfile(warmout))); si = 0; for (si < commandsuffixes.len) { assert(same(refs[si], readfile(strings.concat(warmwork, "/warm", commandsuffixes[si])))); si += 1; }; assert(!directoryhasnew(warmwork) && !directoryhasfragment(warmwork, ".wwtxn.")); rewritebomfile(warmpath, warmvalid); runcommandenv(root, strings.concat("bom-warm-restored-", tags[i]), av, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(readfile(trace).len == 0 && same(binref, readfile(warmout)) && !directoryhasnew(warmwork)); // The marker is semantically whitespace but its three source bytes are // observable input: removing and re-adding it invalidates the unit just // as Go's source-content action does, without changing output bytes. rewritefile(trace, ""); rewritefile(warmpath, warmvalid); runcommandenv(root, strings.concat("bom-warm-marker-removed-", tags[i]), av, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(readfile(trace).len != 0 && same(binref, readfile(warmout))); assert(!same(refs[0], readfile(strings.concat(warmwork, "/warm.unit.ww")))); rewritefile(trace, ""); rewritebomfile(warmpath, warmvalid); runcommandenv(root, strings.concat("bom-warm-marker-restored-", tags[i]), av, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(readfile(trace).len != 0 && same(binref, readfile(warmout))); si = 0; for (si < commandsuffixes.len) { assert(same(refs[si], readfile(strings.concat(warmwork, "/warm", commandsuffixes[si])))); si += 1; }; assert(!directoryhasnew(warmwork)); i += 1; }; // Request-local source state: a valid Cstage build and an invalid WWstage // build overlap without sharing diagnostics, products, or transaction data. let pcwork: str = strings.concat(root, "/parallel-c-work"); let pwwork: str = strings.concat(root, "/parallel-ww-work"); let pcout: str = strings.concat(root, "/parallel-c-output"); let pwout: str = strings.concat(root, "/parallel-ww-output"); mkdirall(pcwork); mkdirall(pwwork); let pcav: []str = [driver("ww"), "build", "-w", pcwork, "-I", source, "-o", pcout, "app"]; let pwav: []str = [driver("ww_ww"), "build", "-w", pwwork, "-I", source, "-o", pwout, "bad"]; let pc: exec.command; pc.path = pcav[0]; pc.argv = pcav; pc.env = os.getenvs(); 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), (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 = 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), (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 && pcp.result.termination == exec.termination.EXIT && pcp.result.code == 0); assert(pwp.result.errno == 0 && pwp.result.cleanuperrno == 0 && pwp.result.termination == exec.termination.EXIT && pwp.result.code == 1); assert(readfile(pc.stdoutpath).len == 0 && readfile(pc.stderrpath).len == 0); assert(readfile(pw.stdoutpath).len == 0 && has(readfile(pw.stderrpath), "invalid BOM in the middle of the file")); assert(same(readfile(pcout), readfile(bins[0])) && !os.exists(pwout)); assert(directoryisempty(pwwork) && !directoryhasnew(pcwork) && !directoryhasfragment(pcwork, ".wwtxn.")); let runav: []str = [pcout]; let out: commandout; runcommand(root, "bom-parallel-run", runav, time.second, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(!directoryhasfragment(root, ".wwtxn.") && !directoryhasfragment(root, ".install") && !directoryhasnew(root)); clean(root); }; // Go 1.26.5's physical-source reader rejects U+0000 before token context can // turn it into comment or literal payload. Selected roots, dependencies, and // every test-source role share that rule; target-excluded files do not. @test fn raw_nul_is_rejected_in_every_selected_source() void = { let root: str = fresh(); let source: str = strings.concat(root, "/source"); mkdirall(source); // Direct frontend ownership and exact C/WW recovery parity. let leading: str = strings.concat(root, "/leading.ww"); let comment: str = strings.concat(root, "/comment.ww"); let stringlit: str = strings.concat(root, "/string.ww"); let runelit: str = strings.concat(root, "/rune.ww"); let between: str = strings.concat(root, "/between.ww"); let escapechar: str = strings.concat(root, "/escape-char.ww"); let escapedigit: str = strings.concat(root, "/escape-digit.ww"); let identifier: str = strings.concat(root, "/identifier.ww"); let lineend: str = strings.concat(root, "/line-end.ww"); let blockend: str = strings.concat(root, "/block-end.ww"); let operator: str = strings.concat(root, "/operator.ww"); let number: str = strings.concat(root, "/number.ww"); let suffix: str = strings.concat(root, "/suffix.ww"); let trailing: str = strings.concat(root, "/trailing.ww"); writenulfile(leading, "", "package main;\nfn main() void = {};\n"); writenulfile(comment, "package main;\n// NUL: ", " here\nfn main() void = {};\n"); writenulfile(stringlit, "package main;\nfn main() void = { let value: str = \"NUL: ", "\"; };\n"); writenulfile(runelit, "package main;\nfn main() void = { let value: rune = '", "A'; };\n"); writenulfile(between, "package main;\n", "\nfn main() void = {};\n"); writenulfile(escapechar, "package main;\nfn main() void = { let value: str = \"\\", "n\"; };\n"); writenulfile(escapedigit, "package main;\nfn main() void = { let value: str = \"\\x0", "0\"; };\n"); writenulfile(identifier, "pack", "age main;\nfn main() void = {};\n"); writenulfile(lineend, "package main;\n// comment", "\nfn main() void = {};\n"); writenulfile(blockend, "package main;\n/* end *", "/\nfn main() void = {};\n"); writenulfile(operator, "package main;\nfn main() void = { let value: bool = true =", "= true; };\n"); writenulfile(number, "package main;\nfn main() void = { let value: i32 = 1", "_0; };\n"); writenulfile(suffix, "package main;\nfn main() void = { let value: i32 = 1i", "32; };\n"); writenulfile(trailing, "package main;\nfn main() void = {};\n", ""); let direct: []str = [leading, comment, stringlit, runelit, between, escapechar, escapedigit, identifier, lineend, blockend, operator, number, suffix, trailing]; let positions: []str = [":1:1: error: invalid NUL character\n", ":2:9: error: invalid NUL character\n", ":2:43: error: invalid NUL character\n", ":2:39: error: invalid NUL character\n", ":2:1: error: invalid NUL character\n", ":2:39: error: invalid NUL character\n", ":2:41: error: invalid NUL character\n", ":1:5: error: invalid NUL character\n", ":2:11: error: invalid NUL character\n", ":2:9: error: invalid NUL character\n", ":2:44: error: invalid NUL character\n", ":2:38: error: invalid NUL character\n", ":2:39: error: invalid NUL character\n", ":3:1: error: invalid NUL character\n"]; let compilers: []str = ["w6c", "w6c_ww"]; let stages: []str = ["ww", "ww_ww"]; let tags: []str = ["c", "ww"]; let di: i32 = 0; for (di < direct.len) { let reference: str = ""; let si: i32 = 0; for (si < compilers.len) { let asmout: str = strings.concat(root, "/direct-", tags[si], "-", boundarypkgname(di), ".s"); let av: []str = [driver(compilers[si]), "-c", "--command-package", "-o", asmout, direct[di]]; let out: commandout; runcommand(root, strings.concat("nul-direct-", tags[si], "-", boundarypkgname(di)), av, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && has(out.stderr, positions[di])); assert(occurrences(out.stderr, "invalid NUL character") == 1); assert(!os.exists(asmout)); if (si == 0) { reference = strings.dup(out.stderr); } else { assert(same(reference, out.stderr)); }; si += 1; }; di += 1; }; // Decoder recovery is also exact for repeated bytes and the internal // module-boundary directive used by package-unit composition. let repeated: str = strings.concat(root, "/repeated.ww"); let directive: str = strings.concat(root, "/directive.ww"); writedoublenulfile(repeated, "pack", "age main;\nfn main() void = {};\n"); writenulfile(directive, "//ww:module-reset ma", "in\npackage main;\nfn main() void = {};\n"); let recoverypaths: []str = [repeated, directive]; let recoverycounts: []i32 = [2, 1]; let recoverypositions: []str = [ ":1:5: error: invalid NUL character\n", ":1:21: error: invalid NUL character\n"]; let ri: i32 = 0; for (ri < recoverypaths.len) { let reference: str = ""; let rsi: i32 = 0; for (rsi < compilers.len) { let asmout: str = strings.concat(root, "/recovery-", tags[rsi], "-", boundarypkgname(ri), ".s"); let av: []str = [driver(compilers[rsi]), "-c", "--command-package", "-o", asmout, recoverypaths[ri]]; let out: commandout; runcommand(root, strings.concat("nul-recovery-", tags[rsi], "-", boundarypkgname(ri)), av, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && has(out.stderr, recoverypositions[ri]) && occurrences(out.stderr, "invalid NUL character") == recoverycounts[ri] && !os.exists(asmout)); if (ri == 0) { assert(has(out.stderr, ":1:6: error: invalid NUL character\n")); }; if (rsi == 0) { reference = strings.dup(out.stderr); } else { assert(same(reference, out.stderr)); }; rsi += 1; }; ri += 1; }; // An escape denotes a literal value; it is not a raw source NUL. let escaped: str = strings.concat(root, "/escaped.ww"); writefile(escaped, strings.concat( "package main;\n", "fn main() i32 = { let value: str = \"\\x00\"; return value.len - 1; };\n")); let escapedasm: []str = [strings.concat(root, "/escaped-c.s"), strings.concat(root, "/escaped-ww.s")]; let si: i32 = 0; for (si < compilers.len) { let av: []str = [driver(compilers[si]), "-c", "--command-package", "-o", escapedasm[si], escaped]; let out: commandout; runcommand(root, strings.concat("nul-escaped-", tags[si]), av, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); si += 1; }; assert(same(readfile(escapedasm[0]), readfile(escapedasm[1]))); let dep: str = strings.concat(source, "/dep"); let app: str = strings.concat(source, "/app"); let bad: str = strings.concat(source, "/bad"); let good: str = strings.concat(source, "/good"); mkdirall(dep); mkdirall(app); mkdirall(bad); mkdirall(good); writenulfile(strings.concat(dep, "/dep.ww"), "package dep;\n// NUL: ", " dependency\nexport fn value() i32 = { return 42; };\n"); writefile(strings.concat(app, "/main.ww"), strings.concat( "package main;\nimport dep;\n", "fn main() i32 = { return dep.value() - 42; };\n")); writefile(strings.concat(bad, "/a.ww"), "package main;\nfn helper() i32 = { return 1; };\n"); writenulfile(strings.concat(bad, "/main.ww"), "package main;\n// NUL: ", strings.concat( " root\nimport missing;\n", "fn main() i32 = { return 0; };\n")); writefile(strings.concat(good, "/main.ww"), "package main;\nfn main() i32 = { return 0; };\n"); writenulfile(strings.concat(good, "/ignored_windows.ww"), "not package NUL: ", " and not WW\n"); // Invalid root and dependency sources stop before graph actions and public // output. The wrong-target source remains outside the semantic input set. let goodbins: []str = [strings.concat(root, "/good-c"), strings.concat(root, "/good-ww")]; let invaliddiags: []str = ["", ""]; let rootdiags: []str = ["", ""]; si = 0; for (si < stages.len) { let depwork: str = strings.concat(root, "/dep-work-", tags[si]); let depout: str = strings.concat(root, "/dep-output-", tags[si]); mkdirall(depwork); let depav: []str = [driver(stages[si]), "build", "-w", depwork, "-I", source, "-o", depout, "app"]; let out: commandout; runcommand(root, strings.concat("nul-dependency-", tags[si]), depav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && has(out.stderr, "/dep/dep.ww:2:9: error: invalid NUL character\n")); assert(occurrences(out.stderr, "invalid NUL character") == 1 && !has(out.stderr, "w6c failed")); assert(!os.exists(depout) && directoryisempty(depwork)); invaliddiags[si] = strings.dup(out.stderr); let badwork: str = strings.concat(root, "/bad-work-", tags[si]); let badout: str = strings.concat(root, "/bad-output-", tags[si]); mkdirall(badwork); let badav: []str = [driver(stages[si]), "build", "-w", badwork, "-I", source, "-o", badout, "bad"]; runcommand(root, strings.concat("nul-root-", tags[si]), badav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && has(out.stderr, "/bad/main.ww:2:9: error: invalid NUL character\n")); assert(!has(out.stderr, "cannot find import") && occurrences(out.stderr, "invalid NUL character") == 1); assert(!os.exists(badout) && directoryisempty(badwork)); rootdiags[si] = strings.dup(out.stderr); let goodwork: str = strings.concat(root, "/good-work-", tags[si]); mkdirall(goodwork); let goodav: []str = [driver(stages[si]), "build", "-w", goodwork, "-I", source, "-o", goodbins[si], "good"]; runcommand(root, strings.concat("nul-wrong-target-", tags[si]), goodav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && !directoryhasnew(goodwork) && !directoryhasfragment(goodwork, ".wwtxn.")); let runav: []str = [goodbins[si]]; runcommand(root, strings.concat("nul-good-run-", tags[si]), runav, time.second, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); si += 1; }; assert(same(invaliddiags[0], invaliddiags[1]) && same(rootdiags[0], rootdiags[1]) && same(readfile(goodbins[0]), readfile(goodbins[1]))); // Production, same-package, external-package, and test-only sources all // reject before product construction, execution, accounting, and retention. let prodcase: str = strings.concat(source, "/prodcase"); let samecase: str = strings.concat(source, "/samecase"); let externalcase: str = strings.concat(source, "/externalcase"); let onlycase: str = strings.concat(source, "/onlycase"); let repeatcase: str = strings.concat(source, "/repeatcase"); mkdirall(prodcase); mkdirall(samecase); mkdirall(externalcase); mkdirall(onlycase); mkdirall(repeatcase); writefile(strings.concat(prodcase, "/more.ww"), "package prodcase;\nfn more() i32 = { return 2; };\n"); writenulfile(strings.concat(prodcase, "/prod.ww"), "package prodcase;\n// NUL: ", " production\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(prodcase, "/same_test.ww"), "package prodcase;\n@test fn must_not_run() void = { abort(); };\n"); writefile(strings.concat(samecase, "/prod.ww"), "package samecase;\nfn value() i32 = { return 1; };\n"); writenulfile(strings.concat(samecase, "/same_test.ww"), "package samecase;\n// NUL: ", " same\n@test fn must_not_run() void = { abort(); };\n"); writefile(strings.concat(externalcase, "/prod.ww"), "package externalcase;\nexport fn value() i32 = { return 1; };\n"); writenulfile(strings.concat(externalcase, "/external_test.ww"), "package externalcase_test;\n// NUL: ", strings.concat( " external\nimport externalcase;\n", "@test fn must_not_run() void = { abort(); };\n")); writenulfile(strings.concat(onlycase, "/only_test.ww"), "", "package onlycase;\n@test fn must_not_run() void = { abort(); };\n"); writedoublenulfile(strings.concat(repeatcase, "/prod.ww"), "package repeatcase;\n// pair ", " here\nfn value() i32 = { return 1; };\n"); let testids: []str = ["prodcase", "samecase", "externalcase", "onlycase", "repeatcase"]; let testpositions: []str = ["/prodcase/prod.ww:2:9: error: invalid NUL character\n", "/samecase/same_test.ww:2:9: error: invalid NUL character\n", "/externalcase/external_test.ww:2:9: error: invalid NUL character\n", "/onlycase/only_test.ww:1:1: error: invalid NUL character\n", "/repeatcase/prod.ww:2:9: error: invalid NUL character\n"]; let testcounts: []i32 = [1, 1, 1, 1, 2]; let testdiags: []str = ["", "", "", "", ""]; si = 0; for (si < stages.len) { let ti: i32 = 0; for (ti < testids.len) { let testwork: str = strings.concat(root, "/test-work-", tags[si], "-", boundarypkgname(ti)); let testout: str = strings.concat(root, "/test-output-", tags[si], "-", boundarypkgname(ti)); mkdirall(testwork); let av: []str = [driver(stages[si]), "test", "-w", testwork, "-I", source, "-o", testout, testids[ti]]; let out: commandout; runcommand(root, strings.concat("nul-test-", tags[si], "-", boundarypkgname(ti)), av, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stdout, "FAIL\n") && has(out.stderr, testpositions[ti])); assert(occurrences(out.stderr, "invalid NUL character") == testcounts[ti] && !has(out.stdout, "must_not_run") && !has(out.stdout, "discovered") && !has(out.stdout, "ok ")); if (ti == 4) { assert(has(out.stderr, "/repeatcase/prod.ww:2:10: error: invalid NUL character\n")); }; assert(!os.exists(testout) && directoryisempty(testwork)); if (si == 0) { testdiags[ti] = strings.dup(out.stderr); } else { assert(same(testdiags[ti], out.stderr)); }; ti += 1; }; si += 1; }; // A warm invalid edit commits nothing and preserves prior public and action // bytes. Exact restoration reuses the committed generation without a tool. let warm: str = strings.concat(source, "/warm"); mkdirall(warm); let warmpath: str = strings.concat(warm, "/main.ww"); let warmvalid: str = "package main;\nfn main() i32 = { return 0; };\n"; writefile(warmpath, warmvalid); let wrapper: str = strings.concat(root, "/nul-compiler-wrapper.sh"); writeexecutable(wrapper, strings.concat( "#!/bin/sh\n", "printf 'compile\\n' >> \"$WW_NUL_TRACE\"\n", "exec \"$WW_NUL_REAL\" \"$@\"\n")); let commandsuffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a", ".init.unit.ww", ".init.s", ".init.o"]; si = 0; for (si < stages.len) { let warmwork: str = strings.concat(root, "/warm-work-", tags[si]); let warmout: str = strings.concat(root, "/warm-output-", tags[si]); let trace: str = strings.concat(root, "/warm-trace-", tags[si]); mkdirall(warmwork); writefile(trace, ""); let env: []str = os.getenvs(); append(env, strings.concat("WW_W6C=", wrapper)); append(env, strings.concat("WW_NUL_TRACE=", trace)); append(env, strings.concat("WW_NUL_REAL=", driver(compilers[si]))); let av: []str = [driver(stages[si]), "build", "-w", warmwork, "-I", source, "-o", warmout, "warm"]; let out: commandout; runcommandenv(root, strings.concat("nul-warm-cold-", tags[si]), av, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && readfile(trace).len != 0); let refs: []str = alloc([], commandsuffixes.len: u64)!; let fi: i32 = 0; for (fi < commandsuffixes.len) { append(refs, strings.dup(readfile(strings.concat(warmwork, "/warm", commandsuffixes[fi])))); fi += 1; }; let toolpaths: []str = ["/.wwtool.ww", "/.wwtool.w6c", "/.wwtool.w6a", "/.wwtool.stamp"]; let toolrefs: []str = alloc([], toolpaths.len: u64)!; fi = 0; for (fi < toolpaths.len) { append(toolrefs, strings.dup(readfile(strings.concat(warmwork, toolpaths[fi])))); fi += 1; }; let binref: str = strings.dup(readfile(warmout)); rewritefile(trace, ""); rewritenulfile(warmpath, "package main;\n// NUL: ", " invalid\nfn main() i32 = { return 0; };\n"); runcommandenv(root, strings.concat("nul-warm-invalid-", tags[si]), av, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && has(out.stderr, ":2:9: error: invalid NUL character\n")); assert(readfile(trace).len == 0 && same(binref, readfile(warmout))); fi = 0; for (fi < commandsuffixes.len) { assert(same(refs[fi], readfile(strings.concat(warmwork, "/warm", commandsuffixes[fi])))); fi += 1; }; fi = 0; for (fi < toolpaths.len) { assert(same(toolrefs[fi], readfile(strings.concat(warmwork, toolpaths[fi])))); fi += 1; }; assert(!directoryhasnew(warmwork) && !directoryhasfragment(warmwork, ".wwtxn.")); rewritefile(warmpath, warmvalid); runcommandenv(root, strings.concat("nul-warm-restored-", tags[si]), av, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && readfile(trace).len == 0 && same(binref, readfile(warmout))); fi = 0; for (fi < commandsuffixes.len) { assert(same(refs[fi], readfile(strings.concat(warmwork, "/warm", commandsuffixes[fi])))); fi += 1; }; fi = 0; for (fi < toolpaths.len) { assert(same(toolrefs[fi], readfile(strings.concat(warmwork, toolpaths[fi])))); fi += 1; }; si += 1; }; // Source validation is request-local under overlapping valid and invalid // builds, including independent work, outputs, diagnostics, and cleanup. let pcwork: str = strings.concat(root, "/parallel-c-work"); let pwwork: str = strings.concat(root, "/parallel-ww-work"); let pcout: str = strings.concat(root, "/parallel-c-output"); let pwout: str = strings.concat(root, "/parallel-ww-output"); mkdirall(pcwork); mkdirall(pwwork); let pcav: []str = [driver("ww"), "build", "-w", pcwork, "-I", source, "-o", pcout, "good"]; let pwav: []str = [driver("ww_ww"), "build", "-w", pwwork, "-I", source, "-o", pwout, "bad"]; let pc: exec.command; pc.path = pcav[0]; pc.argv = pcav; pc.env = os.getenvs(); 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), (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 = 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), (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 && pcp.result.termination == exec.termination.EXIT && pcp.result.code == 0); assert(pwp.result.errno == 0 && pwp.result.cleanuperrno == 0 && pwp.result.termination == exec.termination.EXIT && pwp.result.code == 1); assert(readfile(pc.stdoutpath).len == 0 && readfile(pc.stderrpath).len == 0); assert(readfile(pw.stdoutpath).len == 0 && has(readfile(pw.stderrpath), "invalid NUL character")); assert(same(readfile(pcout), readfile(goodbins[0])) && !os.exists(pwout)); assert(directoryisempty(pwwork) && !directoryhasnew(pcwork) && !directoryhasfragment(pcwork, ".wwtxn.")); let runav: []str = [pcout]; let out: commandout; runcommand(root, "nul-parallel-run", runav, time.second, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(!directoryhasfragment(root, ".wwtxn.") && !directoryhasfragment(root, ".install") && !directoryhasnew(root)); clean(root); }; // Go 1.26.5 removes each malformed UTF-8 byte from the logical source after // reporting it. WW applies that decoder rule only after physical-file // eligibility and before package clauses or imports can affect the graph. @test fn malformed_utf8_is_rejected_in_every_selected_source() void = { let root: str = fresh(); let source: str = strings.concat(root, "/source"); mkdirall(source); // Filtering the invalid byte reconstructs the keyword, so the decoder's // positioned error is the only failure and neither frontend emits assembly. let invaliddirect: str = strings.concat(root, "/invalid-direct.ww"); let validdirect: str = strings.concat(root, "/valid-direct.ww"); writeinvalidutf8file(invaliddirect, "pack", "age main;\nfn main() i32 = { return 0; };\n"); writefile(validdirect, strings.concat( "package main;\n", "// 한국어, café, and � are valid UTF-8 source bytes.\n", "fn main() i32 = { return 0; };\n")); let compilers: []str = ["w6c", "w6c_ww"]; let stages: []str = ["ww", "ww_ww"]; let tags: []str = ["c", "ww"]; let invaliddiags: []str = ["", ""]; let validasms: []str = [strings.concat(root, "/valid-c.s"), strings.concat(root, "/valid-ww.s")]; let si: i32 = 0; for (si < compilers.len) { let invalidasm: str = strings.concat(root, "/invalid-", tags[si], ".s"); let invalidav: []str = [driver(compilers[si]), "-c", "--command-package", "-o", invalidasm, invaliddirect]; let out: commandout; runcommand(root, strings.concat("utf8-direct-invalid-", tags[si]), invalidav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && has(out.stderr, ":1:5: error: invalid UTF-8 encoding\n") && occurrences(out.stderr, "invalid UTF-8 encoding") == 1 && !has(out.stderr, "unexpected character") && !has(out.stderr, "invalid or missing package clause") && !os.exists(invalidasm)); invaliddiags[si] = strings.dup(out.stderr); let validav: []str = [driver(compilers[si]), "-c", "--command-package", "-o", validasms[si], validdirect]; runcommand(root, strings.concat("utf8-direct-valid-", tags[si]), validav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); si += 1; }; assert(same(invaliddiags[0], invaliddiags[1])); assert(same(readfile(validasms[0]), readfile(validasms[1]))); let dep: str = strings.concat(source, "/dep"); let app: str = strings.concat(source, "/app"); let bad: str = strings.concat(source, "/bad"); let good: str = strings.concat(source, "/good"); let ordered: str = strings.concat(source, "/ordered"); mkdirall(dep); mkdirall(app); mkdirall(bad); mkdirall(good); mkdirall(ordered); writeinvalidutf8file(strings.concat(dep, "/dep.ww"), "package dep;\n// bad ", " dependency\nexport fn value() i32 = { return 0; };\n"); writefile(strings.concat(app, "/main.ww"), strings.concat( "package main;\nimport dep;\n", "fn main() i32 = { return dep.value(); };\n")); writeinvalidutf8file(strings.concat(bad, "/main.ww"), "package main;\n// bad ", strings.concat( " root\nimport missing;\n", "fn main() i32 = { return 0; };\n")); writefile(strings.concat(good, "/main.ww"), "package main;\nfn main() i32 = { return 0; };\n"); writeinvalidutf8file(strings.concat(good, "/ignored_windows.ww"), "not package ", " and not valid WW\n"); writeinvalidutf8file(strings.concat(good, "/ignored_test.ww"), "not package ", " and not valid WW\n"); writeinvalidutf8file(strings.concat(ordered, "/z.ww"), "package main;\n// z ", "\nfn z() void = {};\n"); writefile(strings.concat(ordered, "/b.ww"), "package main;\nfn main() i32 = { return 0; };\n"); writeinvalidutf8file(strings.concat(ordered, "/a.ww"), "package main;\n// a ", "\nfn a() void = {};\n"); // Root and dependency decoding precede graph resolution and all producers. // Excluded files are not semantic inputs, and selection remains byte-sorted. let goodbins: []str = [strings.concat(root, "/good-c"), strings.concat(root, "/good-ww")]; let depdiags: []str = ["", ""]; let rootdiags: []str = ["", ""]; let orderdiags: []str = ["", ""]; si = 0; for (si < stages.len) { let out: commandout; let depwork: str = strings.concat(root, "/dep-work-", tags[si]); let depout: str = strings.concat(root, "/dep-output-", tags[si]); mkdirall(depwork); let depav: []str = [driver(stages[si]), "build", "-w", depwork, "-I", source, "-o", depout, "app"]; runcommand(root, strings.concat("utf8-dependency-", tags[si]), depav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && has(out.stderr, "/dep/dep.ww:2:8: error: invalid UTF-8 encoding\n") && occurrences(out.stderr, "invalid UTF-8 encoding") == 1 && !has(out.stderr, "w6c failed") && !os.exists(depout) && directoryisempty(depwork)); depdiags[si] = strings.dup(out.stderr); let badwork: str = strings.concat(root, "/bad-work-", tags[si]); let badout: str = strings.concat(root, "/bad-output-", tags[si]); mkdirall(badwork); let badav: []str = [driver(stages[si]), "build", "-w", badwork, "-I", source, "-o", badout, "bad"]; runcommand(root, strings.concat("utf8-root-", tags[si]), badav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && has(out.stderr, "/bad/main.ww:2:8: error: invalid UTF-8 encoding\n") && occurrences(out.stderr, "invalid UTF-8 encoding") == 1 && !has(out.stderr, "cannot find import") && !os.exists(badout) && directoryisempty(badwork)); rootdiags[si] = strings.dup(out.stderr); let orderwork: str = strings.concat(root, "/order-work-", tags[si]); let orderout: str = strings.concat(root, "/order-output-", tags[si]); mkdirall(orderwork); let orderav: []str = [driver(stages[si]), "build", "-w", orderwork, "-I", source, "-o", orderout, "ordered"]; runcommand(root, strings.concat("utf8-order-", tags[si]), orderav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && has(out.stderr, "/ordered/a.ww:2:6: error: invalid UTF-8 encoding\n") && !has(out.stderr, "/ordered/z.ww:") && !os.exists(orderout) && directoryisempty(orderwork)); orderdiags[si] = strings.dup(out.stderr); let goodwork: str = strings.concat(root, "/good-work-", tags[si]); mkdirall(goodwork); let goodav: []str = [driver(stages[si]), "build", "-w", goodwork, "-I", source, "-o", goodbins[si], "good"]; runcommand(root, strings.concat("utf8-excluded-", tags[si]), goodav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && !directoryhasnew(goodwork) && !directoryhasfragment(goodwork, ".wwtxn.")); let runav: []str = [goodbins[si]]; runcommand(root, strings.concat("utf8-good-run-", tags[si]), runav, time.second, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); si += 1; }; assert(same(depdiags[0], depdiags[1]) && same(rootdiags[0], rootdiags[1]) && same(orderdiags[0], orderdiags[1]) && same(readfile(goodbins[0]), readfile(goodbins[1]))); // Every selected test-source role rejects before generated-main creation, // execution, accounting, and retained-binary publication. let prodcase: str = strings.concat(source, "/prodcase"); let samecase: str = strings.concat(source, "/samecase"); let externalcase: str = strings.concat(source, "/externalcase"); let onlycase: str = strings.concat(source, "/onlycase"); mkdirall(prodcase); mkdirall(samecase); mkdirall(externalcase); mkdirall(onlycase); writeinvalidutf8file(strings.concat(prodcase, "/prod.ww"), "package prodcase;\n// bad ", " production\nfn value() i32 = { return 1; };\n"); writefile(strings.concat(prodcase, "/prod_test.ww"), "package prodcase;\n@test fn must_not_run() void = { abort(); };\n"); writefile(strings.concat(samecase, "/prod.ww"), "package samecase;\nfn value() i32 = { return 1; };\n"); writeinvalidutf8file(strings.concat(samecase, "/same_test.ww"), "package samecase;\n// bad ", " same\n@test fn must_not_run() void = { abort(); };\n"); writefile(strings.concat(externalcase, "/prod.ww"), "package externalcase;\nexport fn value() i32 = { return 1; };\n"); writeinvalidutf8file(strings.concat(externalcase, "/external_test.ww"), "package externalcase_test;\n// bad ", strings.concat( " external\nimport externalcase;\n", "@test fn must_not_run() void = { abort(); };\n")); writeinvalidutf8file(strings.concat(onlycase, "/only_test.ww"), "", "package onlycase;\n@test fn must_not_run() void = { abort(); };\n"); let testids: []str = ["prodcase", "samecase", "externalcase", "onlycase"]; let testpositions: []str = [ "/prodcase/prod.ww:2:8: error: invalid UTF-8 encoding\n", "/samecase/same_test.ww:2:8: error: invalid UTF-8 encoding\n", "/externalcase/external_test.ww:2:8: error: invalid UTF-8 encoding\n", "/onlycase/only_test.ww:1:1: error: invalid UTF-8 encoding\n"]; let testdiags: []str = ["", "", "", ""]; si = 0; for (si < stages.len) { let ti: i32 = 0; for (ti < testids.len) { let testwork: str = strings.concat(root, "/test-work-", tags[si], "-", boundarypkgname(ti)); let testout: str = strings.concat(root, "/test-output-", tags[si], "-", boundarypkgname(ti)); mkdirall(testwork); let av: []str = [driver(stages[si]), "test", "-w", testwork, "-I", source, "-o", testout, testids[ti]]; let out: commandout; runcommand(root, strings.concat("utf8-test-", tags[si], "-", boundarypkgname(ti)), av, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stdout, "FAIL\n") && has(out.stderr, testpositions[ti]) && occurrences(out.stderr, "invalid UTF-8 encoding") == 1 && !has(out.stdout, "must_not_run") && !has(out.stdout, "discovered") && !has(out.stdout, "passed") && !has(out.stdout, "failed") && !has(out.stdout, "ok ") && !os.exists(testout) && directoryisempty(testwork)); if (si == 0) { testdiags[ti] = strings.dup(out.stderr); } else { assert(same(testdiags[ti], out.stderr)); }; ti += 1; }; si += 1; }; // Invalid warm input reaches no tool and preserves every committed action, // tool record, and public byte. Exact source restoration is a cache hit. let warm: str = strings.concat(source, "/warm"); mkdirall(warm); let warmpath: str = strings.concat(warm, "/main.ww"); let warmvalid: str = "package main;\nfn main() i32 = { return 0; };\n"; writefile(warmpath, warmvalid); let wrapper: str = strings.concat(root, "/utf8-compiler-wrapper.sh"); writeexecutable(wrapper, strings.concat( "#!/bin/sh\n", "printf 'compile\\n' >> \"$WW_UTF8_TRACE\"\n", "exec \"$WW_UTF8_REAL\" \"$@\"\n")); let commandsuffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a", ".init.unit.ww", ".init.s", ".init.o"]; let toolpaths: []str = ["/.wwtool.ww", "/.wwtool.w6c", "/.wwtool.w6a", "/.wwtool.stamp"]; si = 0; for (si < stages.len) { let warmwork: str = strings.concat(root, "/warm-work-", tags[si]); let warmout: str = strings.concat(root, "/warm-output-", tags[si]); let trace: str = strings.concat(root, "/warm-trace-", tags[si]); mkdirall(warmwork); writefile(trace, ""); let env: []str = os.getenvs(); append(env, strings.concat("WW_W6C=", wrapper)); append(env, strings.concat("WW_UTF8_TRACE=", trace)); append(env, strings.concat("WW_UTF8_REAL=", driver(compilers[si]))); let av: []str = [driver(stages[si]), "build", "-w", warmwork, "-I", source, "-o", warmout, "warm"]; let out: commandout; runcommandenv(root, strings.concat("utf8-warm-cold-", tags[si]), av, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && readfile(trace).len != 0); let refs: []str = alloc([], commandsuffixes.len: u64)!; let fi: i32 = 0; for (fi < commandsuffixes.len) { append(refs, strings.dup(readfile(strings.concat(warmwork, "/warm", commandsuffixes[fi])))); fi += 1; }; let toolrefs: []str = alloc([], toolpaths.len: u64)!; fi = 0; for (fi < toolpaths.len) { append(toolrefs, strings.dup(readfile(strings.concat(warmwork, toolpaths[fi])))); fi += 1; }; let binref: str = strings.dup(readfile(warmout)); rewritefile(trace, ""); rewriteinvalidutf8file(warmpath, "package main;\n// bad ", " warm\nfn main() i32 = { return 0; };\n"); runcommandenv(root, strings.concat("utf8-warm-invalid-", tags[si]), av, env, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && has(out.stderr, ":2:8: error: invalid UTF-8 encoding\n") && readfile(trace).len == 0 && same(binref, readfile(warmout))); fi = 0; for (fi < commandsuffixes.len) { assert(same(refs[fi], readfile(strings.concat(warmwork, "/warm", commandsuffixes[fi])))); fi += 1; }; fi = 0; for (fi < toolpaths.len) { assert(same(toolrefs[fi], readfile(strings.concat(warmwork, toolpaths[fi])))); fi += 1; }; assert(!directoryhasnew(warmwork) && !directoryhasfragment(warmwork, ".wwtxn.")); rewritefile(warmpath, warmvalid); runcommandenv(root, strings.concat("utf8-warm-restored-", tags[si]), av, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && readfile(trace).len == 0 && same(binref, readfile(warmout))); fi = 0; for (fi < commandsuffixes.len) { assert(same(refs[fi], readfile(strings.concat(warmwork, "/warm", commandsuffixes[fi])))); fi += 1; }; fi = 0; for (fi < toolpaths.len) { assert(same(toolrefs[fi], readfile(strings.concat(warmwork, toolpaths[fi])))); fi += 1; }; si += 1; }; // Validation is request-local: an overlapping valid Cstage request is not // cancelled or contaminated by an invalid WWstage request. let pcwork: str = strings.concat(root, "/parallel-c-work"); let pwwork: str = strings.concat(root, "/parallel-ww-work"); let pcout: str = strings.concat(root, "/parallel-c-output"); let pwout: str = strings.concat(root, "/parallel-ww-output"); mkdirall(pcwork); mkdirall(pwwork); let pcav: []str = [driver("ww"), "build", "-w", pcwork, "-I", source, "-o", pcout, "good"]; let pwav: []str = [driver("ww_ww"), "build", "-w", pwwork, "-I", source, "-o", pwout, "bad"]; let pc: exec.command; pc.path = pcav[0]; pc.argv = pcav; pc.env = os.getenvs(); 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), (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 = 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), (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 && pcp.result.termination == exec.termination.EXIT && pcp.result.code == 0); assert(pwp.result.errno == 0 && pwp.result.cleanuperrno == 0 && pwp.result.termination == exec.termination.EXIT && pwp.result.code == 1); assert(readfile(pc.stdoutpath).len == 0 && readfile(pc.stderrpath).len == 0); assert(readfile(pw.stdoutpath).len == 0 && has(readfile(pw.stderrpath), "invalid UTF-8 encoding")); assert(same(readfile(pcout), readfile(goodbins[0])) && !os.exists(pwout)); assert(directoryisempty(pwwork) && !directoryhasnew(pcwork) && !directoryhasfragment(pcwork, ".wwtxn.")); let runav: []str = [pcout]; let out: commandout; runcommand(root, "utf8-parallel-run", runav, time.second, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); assert(!directoryhasfragment(root, ".wwtxn.") && !directoryhasfragment(root, ".install") && !directoryhasnew(root)); clean(root); }; // Go 1.26.5 resolves an import target, computes its effective file-local name, // and rejects `init` before installing any binding or unused/duplicate/collision // recovery state. The error belongs to the explicit alias token, or to the path // token when the dependency's declared name supplies the effective binding. @test fn effective_init_imports_never_enter_binding_recovery() void = { let root: str = fresh(); let source: str = strings.concat(root, "/source"); let normal: str = strings.concat(source, "/dep/normal"); let named: str = strings.concat(source, "/dep/named"); let badbinding: str = strings.concat(source, "/dep/badbinding"); let imported: str = strings.concat(source, "/imported"); let explicit: str = strings.concat(source, "/explicit"); let implicit: str = strings.concat(source, "/implicit"); let collision: str = strings.concat(source, "/collision"); let missing: str = strings.concat(source, "/missing"); let control: str = strings.concat(source, "/control"); let warm: str = strings.concat(source, "/warm"); let prodcase: str = strings.concat(source, "/prodcase"); let samecase: str = strings.concat(source, "/samecase"); let externalcase: str = strings.concat(source, "/externalcase"); let onlycase: str = strings.concat(source, "/onlycase"); let dirs: []str = [normal, named, badbinding, imported, explicit, implicit, collision, missing, control, warm, prodcase, samecase, externalcase, onlycase]; let di: i32 = 0; for (di < dirs.len) { mkdirall(dirs[di]); di += 1; }; writefile(strings.concat(normal, "/normal.ww"), strings.concat( "package normal;\n", "export fn value() i32 = { return 37; };\n")); writefile(strings.concat(named, "/named.ww"), strings.concat( "package init;\n", "export fn value() i32 = { return 41; };\n")); writefile(strings.concat(badbinding, "/badbinding.ww"), strings.concat( "package badbinding;\n", "import init dep.normal;\n", "export fn value() i32 = { return 43; };\n")); writefile(strings.concat(imported, "/main.ww"), strings.concat( "package main;\n", "import dep.badbinding;\n", "fn main() i32 = { return badbinding.value(); };\n")); writefile(strings.concat(explicit, "/main.ww"), strings.concat( "package main;\n", "import init dep.normal;\n", "import init dep.normal;\n", "fn main() i32 = { return init.value(); };\n")); writefile(strings.concat(implicit, "/main.ww"), strings.concat( "package main;\n", "import dep.named;\n", "fn main() i32 = { return init.value(); };\n")); writefile(strings.concat(collision, "/main.ww"), strings.concat( "package main;\n", "import init dep.normal;\n", "let init: i32 = 1;\n", "fn main() i32 = { return 0; };\n")); writefile(strings.concat(missing, "/main.ww"), strings.concat( "package main;\n", "import init dep.absent;\n", "fn main() i32 = { return 0; };\n")); writefile(strings.concat(control, "/main.ww"), strings.concat( "package main;\n", "import _ dep.named;\n", "import stable dep.named;\n", "fn main() i32 = { return stable.value(); };\n")); let warmfile: str = strings.concat(warm, "/main.ww"); let warmvalid: str = strings.concat( "package main;\n", "import stable dep.normal;\n", "fn main() i32 = { return stable.value(); };\n"); writefile(warmfile, warmvalid); writefile(strings.concat(prodcase, "/prod.ww"), strings.concat( "package prodcase;\n", "import init dep.normal;\n", "fn value() i32 = { return 1; };\n")); writefile(strings.concat(prodcase, "/prod_test.ww"), strings.concat( "package prodcase;\n", "@test fn must_not_run() void = { abort(); };\n")); writefile(strings.concat(samecase, "/prod.ww"), "package samecase;\nfn value() i32 = { return 1; };\n"); writefile(strings.concat(samecase, "/same_test.ww"), strings.concat( "package samecase;\n", "import init dep.normal;\n", "@test fn must_not_run() void = { abort(); };\n")); writefile(strings.concat(externalcase, "/prod.ww"), strings.concat( "package externalcase;\n", "export fn value() i32 = { return 1; };\n")); writefile(strings.concat(externalcase, "/external_test.ww"), strings.concat( "package externalcase_test;\n", "import init dep.normal;\n", "@test fn must_not_run() void = { abort(); };\n")); writefile(strings.concat(onlycase, "/only_test.ww"), strings.concat( "package onlycase;\n", "import init dep.normal;\n", "@test fn must_not_run() void = { abort(); };\n")); let core: str = "cannot import package as init - init must be a func"; let stages: []str = ["ww", "ww_ww"]; let tags: []str = ["c", "ww"]; let explicitdiag: str = ""; let implicitdiag: str = ""; let collisiondiag: str = ""; let missingdiag: str = ""; let importeddiag: str = ""; let controldata: []str = alloc([], 8u64)!; let controlbin: str = ""; let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a", ".init.unit.ww", ".init.s", ".init.o"]; let si: i32 = 0; for (si < stages.len) { // The same rejection owns a dependency compiler action reached through // an imported root. Request-wide rollback leaves no cold semantic state. let importedwork: str = strings.concat(root, "/imported-work-", tags[si]); let importedout: str = strings.concat(root, "/imported-output-", tags[si]); mkdirall(importedwork); let importedav: []str = [driver(stages[si]), "build", "-w", importedwork, "-I", source, "-o", importedout, "imported"]; let importedresult: commandout; runcommand(root, strings.concat("init-imported-", tags[si]), importedav, (120i64 * (time.second: i64)): time.duration, &importedresult); expectexit(&importedresult, 1); assert(importedresult.stdout.len == 0 && occurrences(importedresult.stderr, core) == 1 && has(importedresult.stderr, "/dep.badbinding.unit.new:3:8: error: cannot import package as init - init must be a func\n") && !has(importedresult.stderr, "imported as init and not used") && !os.exists(importedout) && !os.exists(strings.concat(importedout, ".new")) && !os.exists(strings.concat(importedout, ".sepwork")) && directoryisempty(importedwork)); let importednormalized: str = normalizedtrace(importedresult.stderr, strings.concat(importedwork, "/"), importedout); if (si == 0) { importeddiag = strings.dup(importednormalized); } else { assert(same(importeddiag, importednormalized)); }; let work: str = strings.concat(root, "/reject-work-", tags[si]); mkdirall(work); let targets: []str = ["explicit", "implicit", "collision", "missing"]; let outputs: []str = [strings.concat(root, "/explicit-", tags[si]), strings.concat(root, "/implicit-", tags[si]), strings.concat(root, "/collision-", tags[si]), strings.concat(root, "/missing-", tags[si])]; let ti: i32 = 0; for (ti < targets.len) { let av: []str = [driver(stages[si]), "build", "-w", work, "-I", source, "-o", outputs[ti], targets[ti]]; let out: commandout; runcommand(root, strings.concat("init-build-", tags[si], "-", targets[ti]), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && !os.exists(outputs[ti]) && !os.exists(strings.concat(outputs[ti], ".new")) && !os.exists(strings.concat(outputs[ti], ".sepwork"))); let normalized: str = normalizedtrace(out.stderr, strings.concat(work, "/"), outputs[ti]); if (ti == 0) { assert(occurrences(out.stderr, core) == 2 && has(out.stderr, "/explicit.unit.new:3:8: error: cannot import package as init - init must be a func\n") && has(out.stderr, "/explicit.unit.new:4:8: error: cannot import package as init - init must be a func\n") && has(out.stderr, "undefined: init") && !has(out.stderr, "imported as init and not used") && !has(out.stderr, "init redeclared in this block") && !has(out.stderr, "already declared through import of package init")); if (si == 0) { explicitdiag = strings.dup(normalized); } else { assert(same(explicitdiag, normalized)); }; } else if (ti == 1) { assert(occurrences(out.stderr, core) == 1 && has(out.stderr, "/implicit.unit.new:3:8: error: cannot import package as init - init must be a func\n") && has(out.stderr, "undefined: init") && !has(out.stderr, "imported as init and not used")); if (si == 0) { implicitdiag = strings.dup(normalized); } else { assert(same(implicitdiag, normalized)); }; } else if (ti == 2) { assert(occurrences(out.stderr, core) == 1 && has(out.stderr, "/collision.unit.new:3:8: error: cannot import package as init - init must be a func\n") && has(out.stderr, "cannot declare init - must be func") && !has(out.stderr, "imported as init and not used") && !has(out.stderr, "already declared through import of package init")); if (si == 0) { collisiondiag = strings.dup(normalized); } else { assert(same(collisiondiag, normalized)); }; } else { assert(occurrences(out.stderr, core) == 0 && has(out.stderr, ":2:1: error: cannot find package dep.absent\n")); if (si == 0) { missingdiag = strings.dup(normalized); } else { assert(same(missingdiag, normalized)); }; }; assert(directoryisempty(work)); ti += 1; }; // `_` never creates a binding, and a non-init explicit alias remains // valid even when the loaded package declares its name as init. let controlwork: str = strings.concat(root, "/control-work-", tags[si]); let controlout: str = strings.concat(root, "/control-output-", tags[si]); mkdirall(controlwork); let controlav: []str = [driver(stages[si]), "build", "-w", controlwork, "-I", source, "-o", controlout, "control"]; let out: commandout; runcommand(root, strings.concat("init-control-", tags[si]), controlav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); let runav: []str = [controlout]; runcommand(root, strings.concat("init-control-run-", tags[si]), runav, time.second, &out); expectexit(&out, 41); let fi: i32 = 0; for (fi < suffixes.len) { let bytes: str = readfile(strings.concat(controlwork, "/control", suffixes[fi])); if (si == 0) { append(controldata, strings.dup(bytes)); } else { assert(same(controldata[fi], bytes)); }; fi += 1; }; if (si == 0) { controlbin = strings.dup(readfile(controlout)); } else { assert(same(controlbin, readfile(controlout))); }; assert(!directoryhasnew(controlwork) && !directoryhasfragment(controlwork, ".wwtxn.")); si += 1; }; // Production, same-package test, external-test, and test-only sources all // use the same recovery rule before test-main construction or execution. let testtargets: []str = ["prodcase", "samecase", "externalcase", "onlycase"]; let testdiags: []str = ["", "", "", ""]; si = 0; for (si < stages.len) { let ti: i32 = 0; for (ti < testtargets.len) { let testwork: str = strings.concat(root, "/test-work-", tags[si], "-", boundarypkgname(ti)); let testout: str = strings.concat(root, "/test-output-", tags[si], "-", boundarypkgname(ti)); mkdirall(testwork); let av: []str = [driver(stages[si]), "test", "-w", testwork, "-I", source, "-o", testout, testtargets[ti]]; let out: commandout; runcommand(root, strings.concat("init-test-", tags[si], "-", boundarypkgname(ti)), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stdout, "FAIL\n") && occurrences(out.stderr, core) == 1 && has(out.stderr, strings.concat(":8: error: ", core, "\n")) && !has(out.stderr, "imported as init and not used") && !has(out.stderr, "init redeclared in this block") && !has(out.stderr, "already declared through import of package init") && !has(out.stdout, "must_not_run") && !has(out.stdout, "discovered") && !has(out.stdout, "passed") && !has(out.stdout, "failed") && !os.exists(testout) && !os.exists(strings.concat(testout, ".new")) && !os.exists(strings.concat(testout, ".sepwork")) && directoryisempty(testwork)); let normalized: str = normalizedtrace(out.stderr, strings.concat(testwork, "/"), testout); if (si == 0) { testdiags[ti] = strings.dup(normalized); } else { assert(same(testdiags[ti], normalized)); }; ti += 1; }; si += 1; }; // A warm semantic rejection preserves every committed action and public // byte; exact source restoration returns to the same persisted generation. let warmpaths: []str = ["/.wwtool.ww", "/.wwtool.w6c", "/.wwtool.w6a", "/.wwtool.stamp", "/dep.normal.unit.ww", "/dep.normal.wwi", "/dep.normal.s", "/dep.normal.o", "/dep.normal.a", "/warm.unit.ww", "/warm.wwi", "/warm.s", "/warm.o", "/warm.a", "/warm.init.unit.ww", "/warm.init.s", "/warm.init.o"]; let warmcross: []str = alloc([], warmpaths.len: u64)!; let warmbincross: str = ""; si = 0; for (si < stages.len) { let work: str = strings.concat(root, "/warm-work-", tags[si]); let output: str = strings.concat(root, "/warm-output-", tags[si]); mkdirall(work); let av: []str = [driver(stages[si]), "build", "-w", work, "-I", source, "-o", output, "warm"]; let out: commandout; runcommand(root, strings.concat("init-warm-cold-", tags[si]), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let snapshots: []str = alloc([], warmpaths.len: u64)!; let pi: i32 = 0; for (pi < warmpaths.len) { let bytes: str = strings.dup(readfile(strings.concat(work, warmpaths[pi]))); append(snapshots, bytes); if (si == 0) { append(warmcross, strings.dup(bytes)); } else if (pi >= 4) { assert(same(warmcross[pi], bytes)); }; pi += 1; }; let binbytes: str = strings.dup(readfile(output)); if (si == 0) { warmbincross = strings.dup(binbytes); } else { assert(same(warmbincross, binbytes)); }; rewritefile(warmfile, strings.concat( "package main;\n", "import init dep.normal;\n", "fn main() i32 = { return 0; };\n")); runcommand(root, strings.concat("init-warm-reject-", tags[si]), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && occurrences(out.stderr, core) == 1 && has(out.stderr, "/warm.unit.new:3:8: error: cannot import package as init - init must be a func\n") && !has(out.stderr, "imported as init and not used") && same(binbytes, readfile(output)) && !os.exists(strings.concat(output, ".new")) && !os.exists(strings.concat(output, ".sepwork"))); pi = 0; for (pi < warmpaths.len) { assert(same(snapshots[pi], readfile(strings.concat(work, warmpaths[pi])))); pi += 1; }; assert(!directoryhasnew(work) && !directoryhasfragment(work, ".wwtxn.") && !directoryhasfragment(work, ".install")); rewritefile(warmfile, warmvalid); runcommand(root, strings.concat("init-warm-restored-", tags[si]), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && same(binbytes, readfile(output))); pi = 0; for (pi < warmpaths.len) { assert(same(snapshots[pi], readfile(strings.concat(work, warmpaths[pi])))); pi += 1; }; si += 1; }; assert(!directoryhasnew(root) && !directoryhasfragment(root, ".wwtxn.") && !directoryhasfragment(root, ".install")); clean(root); }; // A skip is an outcome even when it carries no explanatory bytes. Keep that // boundary identical through every directory-package test descriptor and the // retained-binary route; an empty payload must not become a harness error. @test fn empty_skip_reason_is_a_successful_skip() void = { let root: str = fresh(); let mixed: str = strings.concat(root, "/mixed"); let only: str = strings.concat(root, "/only"); let raw: str = strings.concat(root, "/raw_test.ww"); let oversized: str = strings.concat(root, "/oversized"); let outside: str = strings.concat(root, "/outside"); mkdirall(mixed); mkdirall(only); mkdirall(oversized); mkdirall(outside); writefile(strings.concat(mixed, "/prod.ww"), strings.concat( "package skipmix;\n", "import test;\n", "export fn value() i32 = { return 7; };\n", "export fn skip_from_production() never = { test.skip(\"\"); };\n")); writefile(strings.concat(mixed, "/same_test.ww"), strings.concat( "package skipmix;\n", "import test;\n", "@test fn aa_empty_production() void = { skip_from_production(); };\n", "@test fn aa_empty_same() void = { test.skip(\"\"); };\n", "@test fn ab_same_peer() void = { assert(value() == 7); };\n", "@test fn ac_named_same() void = { test.skip(\"named reason\"); };\n")); writefile(strings.concat(mixed, "/external_test.ww"), strings.concat( "package skipmix_test;\n", "import skipmix;\n", "import test;\n", "@test fn ad_empty_external() void = {\n", " assert(skipmix.value() == 7); test.skip(\"\");\n", "};\n", "@test fn ae_external_peer() void = {\n", " assert(skipmix.value() == 7);\n", "};\n")); writefile(strings.concat(only, "/only_test.ww"), strings.concat( "package skiponly;\n", "import test;\n", "@test fn aa_empty_test_only() void = { test.skip(\"\"); };\n", "@test fn ab_test_only_peer() void = { assert(true); };\n")); writefile(raw, strings.concat( "package rawskip;\n", "import test;\n", "@test fn aa_empty_raw() void = { test.skip(\"\"); };\n", "@test fn ab_raw_peer() void = { assert(true); };\n")); writefile(strings.concat(oversized, "/oversized_test.ww"), strings.concat( "package skipoversized;\n", "import strings;\n", "import test;\n", "@test fn too_large() void = {\n", " let reason: []u8 = alloc([], 4094u64)!; reason.len = 4094;\n", " let i: i32 = 0; for (i < reason.len) { reason[i] = 'x'; i += 1; };\n", " test.skip(strings.frombytes(reason));\n", "};\n")); writefile(strings.concat(outside, "/main.ww"), strings.concat( "package main;\n", "import test;\n", "fn main() void = { test.skip(\"\"); };\n")); let stages: []str = ["ww", "ww_ww"]; let tags: []str = ["c", "ww"]; let works: []str = [strings.concat(root, "/work-c"), strings.concat(root, "/work-ww")]; let oversizedworks: []str = [strings.concat(root, "/oversized-work-c"), strings.concat(root, "/oversized-work-ww")]; let outsideworks: []str = [strings.concat(root, "/outside-work-c"), strings.concat(root, "/outside-work-ww")]; let outsidebins: []str = [strings.concat(root, "/outside-c"), strings.concat(root, "/outside-ww")]; let retained: []str = [strings.concat(root, "/retained-c.test"), strings.concat(root, "/retained-ww.test")]; let runningretained: []str = [strings.concat(root, "/running-c.test"), strings.concat(root, "/running-ww.test")]; let ordinaryout: []str = ["", ""]; let ordinaryerr: []str = ["", ""]; let emptyout: []str = ["", ""]; let emptyerr: []str = ["", ""]; let passout: []str = ["", ""]; let passerr: []str = ["", ""]; let listout: []str = ["", ""]; let listerr: []str = ["", ""]; let rawout: []str = ["", ""]; let rawerr: []str = ["", ""]; let oversizedout: []str = ["", ""]; let oversizederr: []str = ["", ""]; let runningout: []str = ["", ""]; let runningerr: []str = ["", ""]; let compileout: []str = ["", ""]; let compileerr: []str = ["", ""]; let directout: []str = ["", ""]; let directerr: []str = ["", ""]; let outsideout: []str = ["", ""]; let outsideerr: []str = ["", ""]; let out: commandout; let si: i32 = 0; for (si < stages.len) { mkdirall(outsideworks[si]); // An ordinary product may import the shared test library. Its artifact // therefore participates in normal content invalidation, while invoking // skip without an active test retains the preexisting abort boundary. let outsidebuildav: []str = [driver(stages[si]), "build", "-w", outsideworks[si], "-o", outsidebins[si], outside]; runcommand(root, strings.concat("empty-skip-outside-build-", tags[si]), outsidebuildav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(outsidebins[si])); let outsiderunav: []str = [outsidebins[si]]; runcommand(root, strings.concat("empty-skip-outside-run-", tags[si]), outsiderunav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && same(out.stderr, "test.skip called outside a test")); outsideout[si] = strings.dup(out.stdout); outsideerr[si] = strings.dup(out.stderr); assert(!directoryhasnew(outsideworks[si]) && !directoryhasfragment(outsideworks[si], ".wwtxn.") && !directoryhasfragment(outsideworks[si], ".install")); let ordinaryav: []str = [driver(stages[si]), "test", "-w", works[si], "-I", root, mixed, only]; runcommand(root, strings.concat("empty-skip-ordinary-cold-", tags[si]), ordinaryav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0); assert(occurrences(out.stdout, "skipmix.aa_empty_production ... SKIP: \n") == 1); assert(occurrences(out.stdout, "skipmix.aa_empty_same ... SKIP: \n") == 1); assert(occurrences(out.stdout, "skipmix.ad_empty_external ... SKIP: \n") == 1); assert(occurrences(out.stdout, "skiponly.aa_empty_test_only ... SKIP: \n") == 1); assert(occurrences(out.stdout, "skipmix.ac_named_same ... SKIP: named reason\n") == 1); assert(occurrences(out.stdout, "skipmix.ab_same_peer ... ok\n") == 1); assert(occurrences(out.stdout, "skipmix.ae_external_peer ... ok\n") == 1); assert(occurrences(out.stdout, "skiponly.ab_test_only_peer ... ok\n") == 1); assert(pos(out.stdout, "skipmix.aa_empty_same ... SKIP: \n") < pos(out.stdout, "skipmix.ab_same_peer ... ok\n")); assert(occurrences(out.stdout, "2 passed, 0 failed, 4 skipped, 0 harness errors\n") == 1); assert(occurrences(out.stdout, "6 discovered, 6 selected, 6 started, 6 completed\n") == 1); assert(occurrences(out.stdout, "1 passed, 0 failed, 1 skipped, 0 harness errors\n") == 1); assert(occurrences(out.stdout, "2 discovered, 2 selected, 2 started, 2 completed\n") == 1); assert(occurrences(out.stdout, strings.concat("ok ", mixed, " [skipmix]\n")) == 1); assert(occurrences(out.stdout, strings.concat("ok ", only, " [skiponly]\n")) == 1); assert(!has(out.stdout, "HARNESS")); ordinaryout[si] = strings.dup(out.stdout); ordinaryerr[si] = strings.dup(out.stderr); // Exact repetition against persisted work preserves the externally // visible result and leaves no transactional residue. runcommand(root, strings.concat("empty-skip-ordinary-warm-", tags[si]), ordinaryav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(out.stdout, ordinaryout[si])); assert(same(out.stderr, ordinaryerr[si])); assert(!directoryhasnew(works[si]) && !directoryhasfragment(works[si], ".wwtxn.") && !directoryhasfragment(works[si], ".install")); let emptyav: []str = [driver(stages[si]), "test", "-w", works[si], "-I", root, "-run", "aa_empty_same", mixed]; runcommand(root, strings.concat("empty-skip-filter-empty-", tags[si]), emptyav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0 && occurrences(out.stdout, "skipmix.aa_empty_same ... SKIP: \n") == 1 && occurrences(out.stdout, "0 passed, 0 failed, 1 skipped, 0 harness errors\n") == 1 && occurrences(out.stdout, "6 discovered, 1 selected, 1 started, 1 completed\n") == 1 && !has(out.stdout, "ab_same_peer ...") && !has(out.stdout, "HARNESS")); emptyout[si] = strings.dup(out.stdout); emptyerr[si] = strings.dup(out.stderr); let passav: []str = [driver(stages[si]), "test", "-w", works[si], "-I", root, "-run", "ab_same_peer", mixed]; runcommand(root, strings.concat("empty-skip-filter-pass-", tags[si]), passav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0 && occurrences(out.stdout, "skipmix.ab_same_peer ... ok\n") == 1 && occurrences(out.stdout, "1 passed, 0 failed, 0 skipped, 0 harness errors\n") == 1 && occurrences(out.stdout, "6 discovered, 1 selected, 1 started, 1 completed\n") == 1 && !has(out.stdout, "SKIP:") && !has(out.stdout, "HARNESS")); passout[si] = strings.dup(out.stdout); passerr[si] = strings.dup(out.stderr); let listav: []str = [driver(stages[si]), "test", "-w", works[si], "-I", root, "-list", mixed, only]; runcommand(root, strings.concat("empty-skip-list-", tags[si]), listav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0); assert(occurrences(out.stdout, "skipmix.aa_empty_same\n") == 1 && occurrences(out.stdout, "skipmix.ad_empty_external\n") == 1 && occurrences(out.stdout, "skiponly.aa_empty_test_only\n") == 1 && !has(out.stdout, " ... ") && !has(out.stdout, "SKIP:") && !has(out.stdout, " discovered, ") && !has(out.stdout, "HARNESS")); listout[si] = strings.dup(out.stdout); listerr[si] = strings.dup(out.stderr); // The raw single-file compatibility route wraps the same runtime and // therefore accepts the same zero-byte skip payload before continuing. let rawav: []str = [driver(stages[si]), "test", raw]; runcommand(root, strings.concat("empty-skip-raw-", tags[si]), rawav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0 && occurrences(out.stdout, "aa_empty_raw ... SKIP: \n") == 1 && occurrences(out.stdout, "ab_raw_peer ... ok\n") == 1 && pos(out.stdout, "aa_empty_raw ... SKIP: \n") < pos(out.stdout, "ab_raw_peer ... ok\n") && occurrences(out.stdout, "1 passed, 0 failed, 1 skipped, 0 harness errors\n") == 1 && occurrences(out.stdout, "2 discovered, 2 selected, 2 started, 2 completed\n") == 1 && !has(out.stdout, "HARNESS")); rawout[si] = strings.dup(out.stdout); rawerr[si] = strings.dup(out.stderr); // The empty-payload correction does not broaden the framed protocol: // an oversized reason remains an isolated harness error. let oversizedav: []str = [driver(stages[si]), "test", "-w", oversizedworks[si], oversized]; runcommand(root, strings.concat("empty-skip-oversized-", tags[si]), oversizedav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stderr.len == 0 && occurrences(out.stdout, strings.concat( "skipoversized.too_large ... ", "HARNESS (malformed or contradictory result)\n")) == 1 && occurrences(out.stdout, "0 passed, 0 failed, 0 skipped, 1 harness errors\n") == 1 && occurrences(out.stdout, "1 discovered, 1 selected, 1 started, 1 completed\n") == 1 && !has(out.stdout, "SKIP:")); oversizedout[si] = strings.dup(out.stdout); oversizederr[si] = strings.dup(out.stderr); assert(!directoryhasnew(oversizedworks[si]) && !directoryhasfragment(oversizedworks[si], ".wwtxn.") && !directoryhasfragment(oversizedworks[si], ".install")); // Running retention consumes the same result before guarded publication. let runningav: []str = [driver(stages[si]), "test", "-w", works[si], "-o", runningretained[si], "-I", root, mixed]; runcommand(root, strings.concat("empty-skip-running-retained-", tags[si]), runningav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0 && occurrences(out.stdout, "skipmix.aa_empty_production ... SKIP: \n") == 1 && occurrences(out.stdout, "skipmix.aa_empty_same ... SKIP: \n") == 1 && occurrences(out.stdout, "skipmix.ad_empty_external ... SKIP: \n") == 1 && occurrences(out.stdout, "skipmix.ac_named_same ... SKIP: named reason\n") == 1 && occurrences(out.stdout, "2 passed, 0 failed, 4 skipped, 0 harness errors\n") == 1 && occurrences(out.stdout, "6 discovered, 6 selected, 6 started, 6 completed\n") == 1 && !has(out.stdout, "HARNESS") && os.exists(runningretained[si]) && !os.exists(strings.concat(runningretained[si], ".new")) && !os.exists(strings.concat(runningretained[si], ".sepwork"))); runningout[si] = strings.dup(out.stdout); runningerr[si] = strings.dup(out.stderr); let compileav: []str = [driver(stages[si]), "test", "-c", "-w", works[si], "-o", retained[si], "-I", root, mixed]; runcommand(root, strings.concat("empty-skip-compile-", tags[si]), compileav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(retained[si]) && !os.exists(strings.concat(retained[si], ".new")) && !os.exists(strings.concat(retained[si], ".sepwork"))); compileout[si] = strings.dup(out.stdout); compileerr[si] = strings.dup(out.stderr); let directav: []str = [retained[si], "-package=skipmix"]; runcommand(root, strings.concat("empty-skip-direct-", tags[si]), directav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0 && occurrences(out.stdout, "skipmix.aa_empty_production ... SKIP: \n") == 1 && occurrences(out.stdout, "skipmix.aa_empty_same ... SKIP: \n") == 1 && occurrences(out.stdout, "skipmix.ad_empty_external ... SKIP: \n") == 1 && occurrences(out.stdout, "skipmix.ac_named_same ... SKIP: named reason\n") == 1 && occurrences(out.stdout, "2 passed, 0 failed, 4 skipped, 0 harness errors\n") == 1 && occurrences(out.stdout, "6 discovered, 6 selected, 6 started, 6 completed\n") == 1 && !has(out.stdout, "HARNESS")); directout[si] = strings.dup(out.stdout); directerr[si] = strings.dup(out.stderr); assert(!directoryhasnew(works[si]) && !directoryhasfragment(works[si], ".wwtxn.") && !directoryhasfragment(works[si], ".install")); si += 1; }; assert(same(ordinaryout[0], ordinaryout[1]) && same(ordinaryerr[0], ordinaryerr[1])); assert(same(emptyout[0], emptyout[1]) && same(emptyerr[0], emptyerr[1])); assert(same(passout[0], passout[1]) && same(passerr[0], passerr[1])); assert(same(listout[0], listout[1]) && same(listerr[0], listerr[1])); assert(same(rawout[0], rawout[1]) && same(rawerr[0], rawerr[1])); assert(same(oversizedout[0], oversizedout[1]) && same(oversizederr[0], oversizederr[1])); assert(same(runningout[0], runningout[1]) && same(runningerr[0], runningerr[1])); assert(same(readfile(runningretained[0]), readfile(runningretained[1]))); assert(same(compileout[0], compileout[1]) && same(compileerr[0], compileerr[1])); assert(same(readfile(retained[0]), readfile(retained[1]))); assert(same(directout[0], directout[1]) && same(directerr[0], directerr[1])); assert(same(outsideout[0], outsideout[1]) && same(outsideerr[0], outsideerr[1])); assert(same(readfile(outsidebins[0]), readfile(outsidebins[1]))); assert(!directoryhasnew(root) && !directoryhasfragment(root, ".wwtxn.") && !directoryhasfragment(root, ".install")); clean(root); }; // An effective import name is a file-local package qualifier. It may qualify // selectors, but it is neither a value nor a type by itself. Keep that rule at // semantic checking, before a malformed binding can leak into code generation // or linking, and preserve the ordinary undefined boundary in sibling files and // after blank imports. @test fn bare_import_bindings_require_selectors() void = { let root: str = fresh(); let source: str = strings.concat(root, "/source"); let dependency: str = strings.concat(source, "/dep/wire"); let sizedependency: str = strings.concat(source, "/dep/size"); let cases: []str = ["bareonly", "aliasonly", "selectorbare", "bareselector", "multiple", "typeonly", "aliastype", "typeselector", "builtinalias", "builtincallee", "arraydim", "enumvalue", "nestedconst", "nestedarg", "structhead", "structfieldtype", "casttype", "builtincall", "sibling", "siblingtype", "transitive", "blank", "initcase", "missing", "packagecalleeprim", "packagecalleetype", "packagecalleealign", "arraypackagecallee", "enumpackagecalleealign"]; let ci: i32 = 0; mkdirall(dependency); mkdirall(sizedependency); for (ci < cases.len) { mkdirall(strings.concat(source, "/", cases[ci])); ci += 1; }; writefile(strings.concat(dependency, "/wire.ww"), strings.concat( "package wire;\n", "export type record = struct { n: i32 };\n", "export fn value() i32 = { return 41; };\n")); writefile(strings.concat(sizedependency, "/size.ww"), "package size;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(source, "/bareonly/main.ww"), strings.concat( "package main;\nimport dep.wire;\n", "fn main() i32 = { let bad: i32 = wire; return bad; };\n")); writefile(strings.concat(source, "/aliasonly/main.ww"), strings.concat( "package main;\nimport stable dep.wire;\n", "fn main() i32 = { let bad: i32 = stable; return bad; };\n")); writefile(strings.concat(source, "/selectorbare/main.ww"), strings.concat( "package main;\nimport dep.wire;\n", "fn main() i32 = { let ok: i32 = wire.value(); ", "let bad: i32 = wire; return ok + bad; };\n")); writefile(strings.concat(source, "/bareselector/main.ww"), strings.concat( "package main;\nimport dep.wire;\n", "fn main() i32 = { let bad: i32 = wire; ", "return bad + wire.value(); };\n")); writefile(strings.concat(source, "/multiple/main.ww"), strings.concat( "package main;\nimport dep.wire;\n", "fn main() i32 = { let ok: i32 = wire.value(); ", "let a: i32 = wire; let b: i32 = wire; return ok + a + b; };\n")); writefile(strings.concat(source, "/typeonly/main.ww"), strings.concat( "package main;\nimport dep.wire;\n", "fn bad(v: wire) i32 = { return 0; };\n", "fn main() i32 = { return 0; };\n")); writefile(strings.concat(source, "/aliastype/main.ww"), strings.concat( "package main;\nimport stable dep.wire;\n", "fn bad(v: stable) i32 = { return 0; };\n", "fn main() i32 = { return 0; };\n")); writefile(strings.concat(source, "/typeselector/main.ww"), strings.concat( "package main;\nimport dep.wire;\n", "fn bad(v: wire) i32 = { return 0; };\n", "fn main() i32 = { return wire.value(); };\n")); writefile(strings.concat(source, "/builtinalias/main.ww"), strings.concat( "package main;\nimport len dep.wire;\n", "fn main() i32 = { let ok: i32 = len.value(); ", "let bad: i32 = len; return ok + bad; };\n")); writefile(strings.concat(source, "/builtincallee/main.ww"), strings.concat( "package main;\nimport len dep.wire;\n", "fn main() i32 = { return len(\"abc\"); };\n")); writefile(strings.concat(source, "/arraydim/main.ww"), strings.concat( "package main;\nimport dep.wire;\n", "let bad: [wire]i32;\nfn main() i32 = { return 0; };\n")); writefile(strings.concat(source, "/enumvalue/main.ww"), strings.concat( "package main;\nimport dep.wire;\n", "type bad = enum { ZERO = wire };\n", "fn main() i32 = { return 0; };\n")); writefile(strings.concat(source, "/nestedconst/main.ww"), strings.concat( "package main;\nimport dep.wire;\n", "def BAD: i32 = 1 + (wire + wire);\n", "fn main() i32 = { return 0; };\n")); writefile(strings.concat(source, "/nestedarg/main.ww"), strings.concat( "package main;\nimport dep.wire;\n", "fn take(v: i32) i32 = { return v; };\n", "fn main() i32 = { return take(wire.value() + (1 + wire)); };\n")); writefile(strings.concat(source, "/structhead/main.ww"), strings.concat( "package main;\nimport dep.wire;\n", "fn main() i32 = { let bad = wire { n = 1 }; return bad.n; };\n")); writefile(strings.concat(source, "/structfieldtype/main.ww"), strings.concat( "package main;\nimport dep.wire;\n", "type holder = struct { bad: wire };\n", "fn main() i32 = { return 0; };\n")); writefile(strings.concat(source, "/casttype/main.ww"), strings.concat( "package main;\nimport dep.wire;\n", "fn main() i32 = { let bad = 0: wire; return 0; };\n")); writefile(strings.concat(source, "/builtincall/main.ww"), strings.concat( "package main;\nimport dep.wire;\n", "fn main() size = { return size(wire); };\n")); writefile(strings.concat(source, "/sibling/a.ww"), strings.concat( "package main;\nimport dep.wire;\n", "fn anchor() i32 = { return wire.value(); };\n")); writefile(strings.concat(source, "/sibling/b.ww"), strings.concat( "package main;\n", "fn bad() i32 = { let v: i32 = wire; return v; };\n", "fn main() i32 = { return anchor() + bad(); };\n")); writefile(strings.concat(source, "/siblingtype/a.ww"), strings.concat( "package main;\nimport dep.wire;\n", "fn anchor() i32 = { return wire.value(); };\n")); writefile(strings.concat(source, "/siblingtype/b.ww"), strings.concat( "package main;\nfn bad(v: wire) i32 = { return 0; };\n", "fn main() i32 = { return anchor(); };\n")); let middle: str = strings.concat(source, "/dep/middle"); mkdirall(middle); writefile(strings.concat(middle, "/middle.ww"), strings.concat( "package middle;\nimport dep.wire;\n", "export fn value() i32 = { return wire.value(); };\n")); writefile(strings.concat(source, "/transitive/main.ww"), strings.concat( "package main;\nimport dep.middle;\n", "fn main() i32 = { let ok: i32 = middle.value(); ", "let bad: i32 = wire; return ok + bad; };\n")); writefile(strings.concat(source, "/blank/main.ww"), strings.concat( "package main;\nimport _ dep.wire;\n", "fn main() i32 = { let v: i32 = wire; return v; };\n")); writefile(strings.concat(source, "/initcase/main.ww"), strings.concat( "package main;\nimport init dep.wire;\n", "fn main() i32 = { return 0; };\n")); writefile(strings.concat(source, "/missing/main.ww"), strings.concat( "package main;\nimport stable dep.absent;\n", "fn main() i32 = { let v: i32 = stable; return v; };\n")); writefile(strings.concat(source, "/packagecalleeprim/main.ww"), strings.concat( "package main;\nimport dep.size;\n", "fn main() i32 = { return size(i32): i32; };\n")); writefile(strings.concat(source, "/packagecalleetype/main.ww"), strings.concat( "package main;\nimport dep.size;\nimport dep.wire;\n", "fn main() i32 = { return size(wire): i32; };\n")); writefile(strings.concat(source, "/packagecalleealign/main.ww"), strings.concat( "package main;\nimport align dep.size;\nimport dep.wire;\n", "fn main() i32 = { return align(wire): i32; };\n")); writefile(strings.concat(source, "/arraypackagecallee/main.ww"), strings.concat( "package main;\nimport dep.size;\nimport dep.wire;\n", "let bad: [size(wire)]u8;\nfn main() i32 = { return 0; };\n")); writefile(strings.concat(source, "/enumpackagecalleealign/main.ww"), strings.concat("package main;\nimport align dep.size;\nimport dep.wire;\n", "type bad = enum { ZERO = align(wire) };\n", "fn main() i32 = { return 0; };\n")); let valuecore: str = "use of package wire not in selector"; let aliascore: str = "use of package stable not in selector"; let builtinaliascore: str = "use of package len not in selector"; let sizecore: str = "use of package size not in selector"; let aligncore: str = "use of package align not in selector"; let typecore: str = "wire (package name) is not a type"; let aliastypecore: str = "stable (package name) is not a type"; let kinds: []i32 = [1, 6, 1, 1, 7, 2, 9, 2, 8, 8, 1, 1, 7, 1, 2, 2, 2, 2, 3, 10, 3, 3, 4, 5, 11, 12, 13, 12, 13]; let unusedcounts: []i32 = [1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2]; let stages: []str = ["ww", "ww_ww"]; let tags: []str = ["c", "ww"]; let diagrefs: []str = alloc([], cases.len: u64)!; ci = 0; for (ci < cases.len) { append(diagrefs, ""); ci += 1; }; let si: i32 = 0; for (si < stages.len) { ci = 0; for (ci < cases.len) { let work: str = strings.concat(root, "/matrix-work-", tags[si], "-", boundarypkgname(ci)); let output: str = strings.concat(root, "/matrix-output-", tags[si], "-", boundarypkgname(ci)); mkdirall(work); let av: []str = [driver(stages[si]), "build", "-w", work, "-I", source, "-o", output, cases[ci]]; let out: commandout; runcommand(root, strings.concat("bare-import-matrix-", tags[si], "-", boundarypkgname(ci)), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && !os.exists(output) && !os.exists(strings.concat(output, ".new")) && !os.exists(strings.concat(output, ".sepwork")) && directoryisempty(work)); assert(!has(out.stderr, "undefined reference") && !has(out.stderr, "cgident: unresolvable identifier")); if (kinds[ci] == 1) { assert(occurrences(out.stderr, valuecore) == 1); } else if (kinds[ci] == 2) { assert(occurrences(out.stderr, typecore) == 1); } else if (kinds[ci] == 3) { assert(occurrences(out.stderr, "undefined: wire") == 1 && !has(out.stderr, "package name) is not a type") && !has(out.stderr, "not in selector")); } else if (kinds[ci] == 4) { assert(occurrences(out.stderr, "cannot import package as init - init must be a func") == 1 && !has(out.stderr, "not in selector") && !has(out.stderr, "package name) is not a type")); } else if (kinds[ci] == 5) { assert(occurrences(out.stderr, "cannot find package dep.absent") == 1 && !has(out.stderr, "not in selector") && !has(out.stderr, "package name) is not a type")); } else if (kinds[ci] == 6) { assert(occurrences(out.stderr, aliascore) == 1); } else if (kinds[ci] == 7) { assert(occurrences(out.stderr, valuecore) == 2); } else if (kinds[ci] == 8) { assert(occurrences(out.stderr, builtinaliascore) == 1); } else if (kinds[ci] == 10) { assert(occurrences(out.stderr, "unknown type 'wire'") == 1 && !has(out.stderr, "package name) is not a type") && !has(out.stderr, "not in selector")); } else if (kinds[ci] == 11) { assert(occurrences(out.stderr, sizecore) == 1 && !has(out.stderr, "internal: unhandled expr kind") && !has(out.stderr, "package name) is not a type")); } else if (kinds[ci] == 12) { assert(occurrences(out.stderr, sizecore) == 1 && occurrences(out.stderr, typecore) == 1 && !has(out.stderr, "internal: unhandled expr kind")); } else if (kinds[ci] == 13) { assert(occurrences(out.stderr, aligncore) == 1 && occurrences(out.stderr, typecore) == 1 && !has(out.stderr, "internal: unhandled expr kind")); } else { assert(occurrences(out.stderr, aliastypecore) == 1); }; assert(occurrences(out.stderr, "not used") == unusedcounts[ci]); assert(!has(out.stderr, "array length must be an integer literal") && !has(out.stderr, "enum value must be a constant integer expression") && !has(out.stderr, "arithmetic on non-numeric type") && !has(out.stderr, "unknown struct type") && !has(out.stderr, "called object is not a function") && !has(out.stderr, "asserttyped:")); let normalized: str = normalizedtrace(out.stderr, strings.concat(work, "/"), output); if (ci == 0) { assert(same(normalized, strings.concat( "$WORK/bareonly.unit.new:3:1: error: ", "\"dep.wire\" imported and not used\n", "$WORK/bareonly.unit.new:4:34: error: ", valuecore, "\n", "ww: w6c failed for bareonly\n"))); } else if (ci == 4) { assert(same(normalized, strings.concat( "$WORK/multiple.unit.new:4:60: error: ", valuecore, "\n", "$WORK/multiple.unit.new:4:79: error: ", valuecore, "\n", "ww: w6c failed for multiple\n"))); } else if (ci == 5) { assert(same(normalized, strings.concat( "$WORK/typeonly.unit.new:3:1: error: ", "\"dep.wire\" imported and not used\n", "$WORK/typeonly.unit.new:4:11: error: ", typecore, "\n", "ww: w6c failed for typeonly\n"))); } else if (ci == 9) { assert(same(normalized, strings.concat( "$WORK/builtincallee.unit.new:3:1: error: ", "\"dep.wire\" imported as len and not used\n", "$WORK/builtincallee.unit.new:4:26: error: ", builtinaliascore, "\n", "ww: w6c failed for builtincallee\n"))); } else if (ci == 10) { assert(same(normalized, strings.concat( "$WORK/arraydim.unit.new:3:1: error: ", "\"dep.wire\" imported and not used\n", "$WORK/arraydim.unit.new:4:11: error: ", valuecore, "\n", "ww: w6c failed for arraydim\n"))); } else if (ci == 12) { assert(same(normalized, strings.concat( "$WORK/nestedconst.unit.new:3:1: error: ", "\"dep.wire\" imported and not used\n", "$WORK/nestedconst.unit.new:4:21: error: ", valuecore, "\n", "$WORK/nestedconst.unit.new:4:28: error: ", valuecore, "\n", "ww: w6c failed for nestedconst\n"))); } else if (ci == 13) { assert(same(normalized, strings.concat( "$WORK/nestedarg.unit.new:5:51: error: ", valuecore, "\n", "ww: w6c failed for nestedarg\n"))); } else if (ci == 18) { assert(same(normalized, strings.concat( "$WORK/sibling.unit.new:8:31: error: undefined: wire\n", "ww: w6c failed for sibling\n"))); } else if (ci == 24) { assert(same(normalized, strings.concat( "$WORK/packagecalleeprim.unit.new:3:1: error: ", "\"dep.size\" imported and not used\n", "$WORK/packagecalleeprim.unit.new:4:26: error: ", sizecore, "\nww: w6c failed for packagecalleeprim\n"))); } else if (ci == 25) { assert(same(normalized, strings.concat( "$WORK/packagecalleetype.unit.new:3:1: error: ", "\"dep.size\" imported and not used\n", "$WORK/packagecalleetype.unit.new:4:1: error: ", "\"dep.wire\" imported and not used\n", "$WORK/packagecalleetype.unit.new:5:26: error: ", sizecore, "\n$WORK/packagecalleetype.unit.new:5:31: error: ", typecore, "\nww: w6c failed for packagecalleetype\n"))); } else if (ci == 26) { assert(same(normalized, strings.concat( "$WORK/packagecalleealign.unit.new:3:1: error: ", "\"dep.size\" imported as align and not used\n", "$WORK/packagecalleealign.unit.new:4:1: error: ", "\"dep.wire\" imported and not used\n", "$WORK/packagecalleealign.unit.new:5:26: error: ", aligncore, "\n$WORK/packagecalleealign.unit.new:5:32: error: ", typecore, "\nww: w6c failed for packagecalleealign\n"))); } else if (ci == 27) { assert(same(normalized, strings.concat( "$WORK/arraypackagecallee.unit.new:3:1: error: ", "\"dep.size\" imported and not used\n", "$WORK/arraypackagecallee.unit.new:4:1: error: ", "\"dep.wire\" imported and not used\n", "$WORK/arraypackagecallee.unit.new:5:11: error: ", sizecore, "\n$WORK/arraypackagecallee.unit.new:5:16: error: ", typecore, "\nww: w6c failed for arraypackagecallee\n"))); } else if (ci == 28) { assert(same(normalized, strings.concat( "$WORK/enumpackagecalleealign.unit.new:3:1: error: ", "\"dep.size\" imported as align and not used\n", "$WORK/enumpackagecalleealign.unit.new:4:1: error: ", "\"dep.wire\" imported and not used\n", "$WORK/enumpackagecalleealign.unit.new:5:26: error: ", aligncore, "\n$WORK/enumpackagecalleealign.unit.new:5:32: error: ", typecore, "\nww: w6c failed for enumpackagecalleealign\n"))); }; if (si == 0) { diagrefs[ci] = strings.dup(normalized); } else { assert(same(diagrefs[ci], normalized)); }; ci += 1; }; si += 1; }; // A raw source operand uses the same file-local import object and checker. let raw: str = strings.concat(root, "/raw.ww"); writefile(raw, strings.concat( "package main;\nimport dep.wire;\n", "fn main() i32 = { let ok: i32 = wire.value(); ", "let bad: i32 = wire; return ok + bad; };\n")); let rawdiag: str = ""; si = 0; for (si < stages.len) { let work: str = strings.concat(root, "/raw-work-", tags[si]); let output: str = strings.concat(root, "/raw-output-", tags[si]); mkdirall(work); let av: []str = [driver(stages[si]), "build", "-w", work, "-I", source, "-o", output, raw]; let out: commandout; runcommand(root, strings.concat("bare-import-raw-", tags[si]), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && occurrences(out.stderr, valuecore) == 1 && !has(out.stderr, "imported as wire and not used") && !os.exists(output) && directoryisempty(work)); let normalized: str = normalizedtrace(out.stderr, strings.concat(work, "/"), output); if (si == 0) { rawdiag = strings.dup(normalized); } else { assert(same(rawdiag, normalized)); }; si += 1; }; // Valid selector use still owns import usage, artifact construction, and // execution. Every comparable semantic action and public binary is exact. let control: str = strings.concat(source, "/control"); mkdirall(control); writefile(strings.concat(control, "/main.ww"), strings.concat( "package main;\nimport dep.wire;\n", "fn main() i32 = { let r: wire.record = wire.record { n = 0 }; ", "return wire.value() + r.n; };\n")); let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a", ".init.unit.ww", ".init.s", ".init.o"]; let controlrefs: []str = alloc([], suffixes.len: u64)!; let controlbin: str = ""; si = 0; for (si < stages.len) { let work: str = strings.concat(root, "/control-work-", tags[si]); let output: str = strings.concat(root, "/control-output-", tags[si]); mkdirall(work); let av: []str = [driver(stages[si]), "build", "-w", work, "-I", source, "-o", output, "control"]; let out: commandout; runcommand(root, strings.concat("bare-import-control-", tags[si]), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(output) && !directoryhasnew(work) && !directoryhasfragment(work, ".wwtxn.")); let runav: []str = [output]; runcommand(root, strings.concat("bare-import-control-run-", tags[si]), runav, time.second, &out); expectexit(&out, 41); assert(out.stdout.len == 0 && out.stderr.len == 0); let fi: i32 = 0; for (fi < suffixes.len) { let bytes: str = readfile(strings.concat(work, "/control", suffixes[fi])); if (si == 0) { append(controlrefs, strings.dup(bytes)); } else { assert(same(controlrefs[fi], bytes)); }; fi += 1; }; if (si == 0) { controlbin = strings.dup(readfile(output)); } else { assert(same(controlbin, readfile(output))); }; si += 1; }; // Production, same-package, external, honest test-only, a production // function reached by a test, and an imported dependency all reject before // test-main publication or execution. A filter cannot hide source errors. let testtargets: []str = ["prodcase", "samecase", "externalcase", "onlycase", "calledcase", "importedcase"]; ci = 0; for (ci < testtargets.len) { mkdirall(strings.concat(source, "/", testtargets[ci])); ci += 1; }; writefile(strings.concat(source, "/prodcase/prod.ww"), strings.concat( "package prodcase;\nimport dep.wire;\n", "fn bad() i32 = { let ok: i32 = wire.value(); ", "let v: i32 = wire; return ok + v; };\n")); writefile(strings.concat(source, "/prodcase/prod_test.ww"), "package prodcase;\n@test fn must_not_run() void = { abort(); };\n"); writefile(strings.concat(source, "/samecase/prod.ww"), "package samecase;\nfn value() i32 = { return 1; };\n"); writefile(strings.concat(source, "/samecase/same_test.ww"), strings.concat( "package samecase;\nimport dep.wire;\n", "@test fn must_not_run() void = { let ok: i32 = wire.value(); ", "let bad: i32 = wire; abort(); };\n")); writefile(strings.concat(source, "/externalcase/prod.ww"), strings.concat( "package externalcase;\n", "export fn value() i32 = { return 1; };\n")); writefile(strings.concat(source, "/externalcase/external_test.ww"), strings.concat("package externalcase_test;\nimport dep.wire;\n", "@test fn must_not_run() void = { let ok: i32 = wire.value(); ", "let bad: i32 = wire; abort(); };\n")); writefile(strings.concat(source, "/onlycase/only_test.ww"), strings.concat( "package onlycase;\nimport dep.wire;\n", "@test fn must_not_run() void = { let ok: i32 = wire.value(); ", "let bad: i32 = wire; abort(); };\n")); writefile(strings.concat(source, "/calledcase/prod.ww"), strings.concat( "package calledcase;\nimport dep.wire;\n", "fn bad() i32 = { let ok: i32 = wire.value(); ", "let v: i32 = wire; return ok + v; };\n")); writefile(strings.concat(source, "/calledcase/called_test.ww"), "package calledcase;\n@test fn must_not_run() void = { assert(bad() > 0); };\n"); let baddep: str = strings.concat(source, "/dep/baddep"); let buildimport: str = strings.concat(source, "/buildimport"); mkdirall(baddep); mkdirall(buildimport); writefile(strings.concat(baddep, "/baddep.ww"), strings.concat( "package baddep;\nimport dep.wire;\n", "export fn bad() i32 = { let ok: i32 = wire.value(); ", "let v: i32 = wire; return ok + v; };\n")); writefile(strings.concat(source, "/importedcase/prod.ww"), strings.concat( "package importedcase;\nimport dep.baddep;\n", "fn value() i32 = { return baddep.bad(); };\n")); writefile(strings.concat(source, "/importedcase/imported_test.ww"), "package importedcase;\n@test fn must_not_run() void = { assert(value() > 0); };\n"); writefile(strings.concat(buildimport, "/main.ww"), strings.concat( "package main;\nimport dep.baddep;\n", "fn main() i32 = { return baddep.bad(); };\n")); let testdiagrefs: []str = alloc([], testtargets.len: u64)!; ci = 0; for (ci < testtargets.len) { append(testdiagrefs, ""); ci += 1; }; si = 0; for (si < stages.len) { ci = 0; for (ci < testtargets.len) { let work: str = strings.concat(root, "/test-work-", tags[si], "-", boundarypkgname(ci)); let output: str = strings.concat(root, "/test-output-", tags[si], "-", boundarypkgname(ci)); mkdirall(work); let av: []str = [driver(stages[si]), "test", "-w", work, "-I", source, "-run", "must_not_run", "-o", output, testtargets[ci]]; let out: commandout; runcommand(root, strings.concat("bare-import-test-", tags[si], "-", boundarypkgname(ci)), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stdout, "FAIL\n") && occurrences(out.stderr, valuecore) == 1 && !has(out.stderr, "undefined reference") && !has(out.stderr, "cgident: unresolvable identifier") && !has(out.stdout, "must_not_run ...") && !os.exists(output) && !os.exists(strings.concat(output, ".new")) && !os.exists(strings.concat(output, ".sepwork")) && directoryisempty(work)); let normalized: str = normalizedtrace(out.stderr, strings.concat(work, "/"), output); if (si == 0) { testdiagrefs[ci] = strings.dup(normalized); } else { assert(same(testdiagrefs[ci], normalized)); }; ci += 1; }; si += 1; }; // Ordinary build reaches the same invalid dependency compiler action, and // a genuinely empty test selection still compiles selected source before // considering whether any runtime test would match. let buildimportdiag: str = ""; let nomatchdiag: str = ""; si = 0; for (si < stages.len) { let buildwork: str = strings.concat(root, "/buildimport-work-", tags[si]); let buildout: str = strings.concat(root, "/buildimport-output-", tags[si]); mkdirall(buildwork); let buildav: []str = [driver(stages[si]), "build", "-w", buildwork, "-I", source, "-o", buildout, "buildimport"]; let out: commandout; runcommand(root, strings.concat("bare-import-build-dependency-", tags[si]), buildav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && occurrences(out.stderr, valuecore) == 1 && !has(out.stderr, "undefined reference") && !has(out.stderr, "cgident: unresolvable identifier") && !has(out.stderr, "asserttyped:") && !os.exists(buildout) && !os.exists(strings.concat(buildout, ".new")) && !os.exists(strings.concat(buildout, ".sepwork")) && directoryisempty(buildwork)); let buildnormalized: str = normalizedtrace(out.stderr, strings.concat(buildwork, "/"), buildout); if (si == 0) { buildimportdiag = strings.dup(buildnormalized); } else { assert(same(buildimportdiag, buildnormalized)); }; let testwork: str = strings.concat(root, "/nomatch-work-", tags[si]); let testout: str = strings.concat(root, "/nomatch-output-", tags[si]); mkdirall(testwork); let testav: []str = [driver(stages[si]), "test", "-w", testwork, "-I", source, "-run", "does_not_match_any_test", "-o", testout, "samecase"]; runcommand(root, strings.concat("bare-import-test-nomatch-", tags[si]), testav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stdout, "FAIL\n") && occurrences(out.stderr, valuecore) == 1 && !has(out.stdout, "testing: warning: no tests to run") && !has(out.stdout, " discovered, ") && !has(out.stdout, "must_not_run ...") && !has(out.stderr, "undefined reference") && !has(out.stderr, "cgident: unresolvable identifier") && !has(out.stderr, "asserttyped:") && !os.exists(testout) && !os.exists(strings.concat(testout, ".new")) && !os.exists(strings.concat(testout, ".sepwork")) && directoryisempty(testwork)); let testnormalized: str = normalizedtrace(out.stderr, strings.concat(testwork, "/"), testout); if (si == 0) { nomatchdiag = strings.dup(testnormalized); } else { assert(same(nomatchdiag, testnormalized)); }; si += 1; }; // A warm command keeps its committed graph/actions, package artifacts, and // public binary across rejection; restoring exact source reuses the same // semantic generation. The same publication rule protects a filtered, // retained test binary, which remains directly executable after rejection. let warm: str = strings.concat(source, "/warm"); mkdirall(warm); let warmfile: str = strings.concat(warm, "/main.ww"); let warmvalid: str = strings.concat( "package main;\nimport dep.wire;\n", "fn main() i32 = { return wire.value(); };\n"); let warminvalid: str = strings.concat( "package main;\nimport dep.wire;\n", "fn main() i32 = { let ok: i32 = wire.value(); ", "let bad: i32 = wire; return ok + bad; };\n"); writefile(warmfile, warmvalid); let retainedcase: str = strings.concat(source, "/retainedcase"); mkdirall(retainedcase); let retainedfile: str = strings.concat(retainedcase, "/retained_test.ww"); let retainedvalid: str = strings.concat( "package retainedcase;\nimport dep.wire;\n", "@test fn kept() void = { assert(wire.value() == 41); };\n"); let retainedinvalid: str = strings.concat( "package retainedcase;\nimport dep.wire;\n", "@test fn kept() void = { let ok: i32 = wire.value(); ", "let bad: i32 = wire; assert(ok + bad > 0); };\n"); writefile(retainedfile, retainedvalid); let warmpaths: []str = ["/.wwtool.ww", "/.wwtool.w6c", "/.wwtool.w6a", "/.wwtool.stamp", "/dep.wire.unit.ww", "/dep.wire.wwi", "/dep.wire.s", "/dep.wire.o", "/dep.wire.a", "/warm.unit.ww", "/warm.wwi", "/warm.s", "/warm.o", "/warm.a", "/warm.init.unit.ww", "/warm.init.s", "/warm.init.o"]; let warmcross: []str = alloc([], warmpaths.len: u64)!; let warmbincross: str = ""; let retainedcross: str = ""; let directoutrefs: []str = ["", ""]; let directerrrefs: []str = ["", ""]; si = 0; for (si < stages.len) { let work: str = strings.concat(root, "/warm-work-", tags[si]); let output: str = strings.concat(root, "/warm-output-", tags[si]); mkdirall(work); let av: []str = [driver(stages[si]), "build", "-w", work, "-I", source, "-o", output, "warm"]; let out: commandout; runcommand(root, strings.concat("bare-import-warm-cold-", tags[si]), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0); let snapshots: []str = alloc([], warmpaths.len: u64)!; let pi: i32 = 0; for (pi < warmpaths.len) { let bytes: str = strings.dup(readfile(strings.concat(work, warmpaths[pi]))); append(snapshots, bytes); if (si == 0) { append(warmcross, strings.dup(bytes)); } else if (pi >= 4) { assert(same(warmcross[pi], bytes)); }; pi += 1; }; let binbytes: str = strings.dup(readfile(output)); if (si == 0) { warmbincross = strings.dup(binbytes); } else { assert(same(warmbincross, binbytes)); }; rewritefile(warmfile, warminvalid); runcommand(root, strings.concat("bare-import-warm-reject-", tags[si]), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && occurrences(out.stderr, valuecore) == 1 && same(binbytes, readfile(output)) && !os.exists(strings.concat(output, ".new")) && !os.exists(strings.concat(output, ".sepwork"))); pi = 0; for (pi < warmpaths.len) { assert(same(snapshots[pi], readfile(strings.concat(work, warmpaths[pi])))); pi += 1; }; assert(!directoryhasnew(work) && !directoryhasfragment(work, ".wwtxn.") && !directoryhasfragment(work, ".install")); rewritefile(warmfile, warmvalid); runcommand(root, strings.concat("bare-import-warm-restored-", tags[si]), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && same(binbytes, readfile(output))); pi = 0; for (pi < warmpaths.len) { assert(same(snapshots[pi], readfile(strings.concat(work, warmpaths[pi])))); pi += 1; }; let testwork: str = strings.concat(root, "/retained-work-", tags[si]); let retained: str = strings.concat(root, "/retained-", tags[si], ".test"); mkdirall(testwork); let compileav: []str = [driver(stages[si]), "test", "-w", testwork, "-I", source, "-run", "kept", "-o", retained, "retainedcase"]; runcommand(root, strings.concat("bare-import-retained-cold-", tags[si]), compileav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0 && os.exists(retained) && occurrences(out.stdout, "retainedcase.kept ... ok\n") == 1 && occurrences(out.stdout, "1 passed, 0 failed, 0 skipped, 0 harness errors\n") == 1); let retainedstdout: str = strings.dup(out.stdout); let retainedbytes: str = strings.dup(readfile(retained)); if (si == 0) { retainedcross = strings.dup(retainedbytes); } else { assert(same(retainedcross, retainedbytes)); }; rewritefile(retainedfile, retainedinvalid); runcommand(root, strings.concat("bare-import-retained-reject-", tags[si]), compileav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stdout, "FAIL\n") && occurrences(out.stderr, valuecore) == 1 && same(retainedbytes, readfile(retained)) && !os.exists(strings.concat(retained, ".new")) && !os.exists(strings.concat(retained, ".sepwork")) && !directoryhasnew(testwork) && !directoryhasfragment(testwork, ".wwtxn.")); let directav: []str = [retained, "-package=retainedcase", "kept"]; runcommand(root, strings.concat("bare-import-retained-direct-", tags[si]), directav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0 && occurrences(out.stdout, "retainedcase.kept ... ok\n") == 1 && occurrences(out.stdout, "1 passed, 0 failed, 0 skipped, 0 harness errors\n") == 1); directoutrefs[si] = strings.dup(out.stdout); directerrrefs[si] = strings.dup(out.stderr); rewritefile(retainedfile, retainedvalid); runcommand(root, strings.concat("bare-import-retained-restored-", tags[si]), compileav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(out.stdout, retainedstdout) && out.stderr.len == 0 && same(retainedbytes, readfile(retained)) && !directoryhasnew(testwork) && !directoryhasfragment(testwork, ".wwtxn.") && !directoryhasfragment(testwork, ".install")); si += 1; }; assert(same(directoutrefs[0], directoutrefs[1]) && same(directerrrefs[0], directerrrefs[1])); assert(!directoryhasnew(root) && !directoryhasfragment(root, ".wwtxn.") && !directoryhasfragment(root, ".install")); clean(root); }; // A file-local import package-name object participates in ordinary lexical // lookup: a closer local wins from its declaration point through its scope, // and the package qualifier is visible again afterward. Exercise that rule // through raw and package builds, imported packages, every test source role, // filtered retained execution, and transactional rejection/restoration. @test fn lexical_import_bindings_shadow_normally() void = { let root: str = fresh(); let source: str = strings.concat(root, "/source"); let wire: str = strings.concat(source, "/dep/wire"); let aliasdep: str = strings.concat(source, "/dep/aliasdep"); let builtindep: str = strings.concat(source, "/dep/builtindep"); let palette: str = strings.concat(source, "/dep/palette"); let shadowlib: str = strings.concat(source, "/dep/shadowlib"); let matrix: str = strings.concat(source, "/matrix"); let client: str = strings.concat(source, "/client"); let sibling: str = strings.concat(source, "/sibling"); let invalid: str = strings.concat(source, "/invalid"); let invalidscalar: str = strings.concat(source, "/invalidscalar"); let invalidtype: str = strings.concat(source, "/invalidtype"); let warm: str = strings.concat(source, "/warm"); let roleprod: str = strings.concat(source, "/roleprod"); let rolesame: str = strings.concat(source, "/rolesame"); let roleexternal: str = strings.concat(source, "/roleexternal"); let roleonly: str = strings.concat(source, "/roleonly"); let retainedcase: str = strings.concat(source, "/retainedcase"); let dirs: []str = [wire, aliasdep, builtindep, palette, shadowlib, matrix, client, sibling, invalid, invalidscalar, invalidtype, warm, roleprod, rolesame, roleexternal, roleonly, retainedcase]; let di: i32 = 0; for (di < dirs.len) { mkdirall(dirs[di]); di += 1; }; writefile(strings.concat(wire, "/wire.ww"), strings.concat( "package wire;\n", "export type record = struct { n: i32 };\n", "export type count = i32;\n", "export fn value() i32 = { return 11; };\n")); writefile(strings.concat(aliasdep, "/aliasdep.ww"), "package aliasdep;\nexport fn value() i32 = { return 13; };\n"); writefile(strings.concat(builtindep, "/builtindep.ww"), "package builtindep;\nexport fn value() i32 = { return 17; };\n"); writefile(strings.concat(palette, "/palette.ww"), strings.concat( "package palette;\n", "export type shade = enum i32 { RED = 100 };\n", "export fn value() i32 = { return shade.RED: i32; };\n")); // The initializer still sees the import, the declared local wins // afterward, a nested binding is restored on exit, aliases behave like // default names, and local dot/call fields beat package and enum shortcuts. writefile(strings.concat(matrix, "/main.ww"), strings.concat( "package main;\n", "import dep.wire;\n", "import stable dep.aliasdep;\n", "import len dep.builtindep;\n", "import shade dep.palette;\n", "type box = struct { n: i32, RED: i32, call: *fn() i32 };\n", "fn seven() i32 = { return 7; };\n", "fn typedpair() (i32, wire.count) = { return 5, 6; };\n", "fn localafter() bool = { let before: i32 = wire.value(); ", "let wire: i32 = wire.value() + 1; ", "return before == 11 && wire == 12; };\n", "fn nestedrestore() bool = { let before: i32 = wire.value(); ", "if (true) { let wire: i32 = 3; ", "if (wire != 3) { return false; }; }; ", "return before == 11 && wire.value() == 11; };\n", "fn aliases() bool = { let a: i32 = stable.value(); ", "let stable: i32 = stable.value() + 1; ", "let b: i32 = len.value(); let len: i32 = len.value() + 1; ", "return a == 13 && stable == 14 && b == 17 && len == 18; };\n", "fn localfields() bool = { let wire: box = box { n = 5, ", "RED = 0, call = &seven }; let imported: i32 = shade.value(); ", "let shade: box = box { n = 0, RED = 19, call = &seven }; ", "return wire.n == 5 && (*wire.call)() == 7 ", "&& imported == 100 && shade.RED == 19; };\n", "fn tupledecl() bool = { ", "let (wire: i32, second: wire.count) = typedpair(); ", "return wire == 5 && (second: i32) == 6; };\n", "fn ordinaryfor() bool = { let total: i32 = wire.value(); ", "for (let wire: i32 = 1; wire < 3; wire += 1) ", "{ total += wire; }; ", "return total == 14 && wire.value() == 11; };\n", "fn main() i32 = { if (!localafter()) { return 1; }; ", "if (!nestedrestore()) { return 2; }; ", "if (!aliases()) { return 3; }; ", "if (!localfields()) { return 4; }; ", "if (!tupledecl()) { return 5; }; ", "if (!ordinaryfor()) { return 6; }; return 42; };\n")); let stages: []str = ["ww", "ww_ww"]; let tags: []str = ["c", "ww"]; let artifactpaths: []str = [ "/dep.wire.unit.ww", "/dep.wire.wwi", "/dep.wire.s", "/dep.wire.o", "/dep.wire.a", "/dep.aliasdep.unit.ww", "/dep.aliasdep.wwi", "/dep.aliasdep.s", "/dep.aliasdep.o", "/dep.aliasdep.a", "/dep.builtindep.unit.ww", "/dep.builtindep.wwi", "/dep.builtindep.s", "/dep.builtindep.o", "/dep.builtindep.a", "/dep.palette.unit.ww", "/dep.palette.wwi", "/dep.palette.s", "/dep.palette.o", "/dep.palette.a", "/matrix.unit.ww", "/matrix.wwi", "/matrix.s", "/matrix.o", "/matrix.a", "/matrix.init.unit.ww", "/matrix.init.s", "/matrix.init.o"]; let artifactrefs: []str = alloc([], artifactpaths.len: u64)!; let matrixbin: str = ""; let matrixoutrefs: []str = ["", ""]; let matrixerrrefs: []str = ["", ""]; let si: i32 = 0; for (si < stages.len) { let work: str = strings.concat(root, "/matrix-work-", tags[si]); let output: str = strings.concat(root, "/matrix-output-", tags[si]); mkdirall(work); let av: []str = [driver(stages[si]), "build", "-w", work, "-I", source, "-o", output, "matrix"]; let out: commandout; runcommand(root, strings.concat("import-shadow-matrix-", tags[si]), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(output) && !os.exists(strings.concat(output, ".new")) && !os.exists(strings.concat(output, ".sepwork")) && !directoryhasnew(work) && !directoryhasfragment(work, ".wwtxn.") && !directoryhasfragment(work, ".install") && !directoryhasfragment(work, ".sepwork")); matrixoutrefs[si] = strings.dup(out.stdout); matrixerrrefs[si] = strings.dup(out.stderr); let runav: []str = [output]; runcommand(root, strings.concat("import-shadow-matrix-run-", tags[si]), runav, time.second, &out); expectexit(&out, 42); assert(out.stdout.len == 0 && out.stderr.len == 0); let ai: i32 = 0; for (ai < artifactpaths.len) { let bytes: str = readfile(strings.concat(work, artifactpaths[ai])); if (si == 0) { append(artifactrefs, strings.dup(bytes)); } else { assert(same(artifactrefs[ai], bytes)); }; ai += 1; }; if (si == 0) { matrixbin = strings.dup(readfile(output)); } else { assert(same(matrixbin, readfile(output))); }; si += 1; }; assert(same(matrixoutrefs[0], matrixoutrefs[1]) && same(matrixerrrefs[0], matrixerrrefs[1])); // A raw source operand and a package reached through an import graph use // the same checker and declaration-point rule. let raw: str = strings.concat(root, "/raw-shadow.ww"); writefile(raw, strings.concat( "package main;\nimport dep.wire;\n", "fn main() i32 = { let wire: i32 = wire.value() + 31; ", "return wire; };\n")); writefile(strings.concat(shadowlib, "/shadowlib.ww"), strings.concat( "package shadowlib;\nimport dep.wire;\n", "export fn value() i32 = { let before: i32 = wire.value(); ", "let wire: i32 = wire.value() + 31; ", "if (before != 11) { return 1; }; return wire; };\n")); writefile(strings.concat(client, "/main.ww"), strings.concat( "package main;\nimport dep.shadowlib;\n", "fn main() i32 = { return shadowlib.value(); };\n")); writefile(strings.concat(sibling, "/imported.ww"), strings.concat( "package main;\nimport dep.wire;\n", "fn imported() i32 = { return wire.value(); };\n")); writefile(strings.concat(sibling, "/main.ww"), strings.concat( "package main;\ntype box = struct { n: i32 };\n", "fn main() i32 = { let wire: box = box { n = 31 }; ", "return imported() + wire.n; };\n")); let rawbin: str = ""; let clientbin: str = ""; let siblingbin: str = ""; si = 0; for (si < stages.len) { let rawwork: str = strings.concat(root, "/raw-work-", tags[si]); let rawout: str = strings.concat(root, "/raw-output-", tags[si]); mkdirall(rawwork); let rawav: []str = [driver(stages[si]), "build", "-w", rawwork, "-I", source, "-o", rawout, raw]; let out: commandout; runcommand(root, strings.concat("import-shadow-raw-", tags[si]), rawav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(rawout) && !os.exists(strings.concat(rawout, ".sepwork")) && !directoryhasnew(rawwork) && !directoryhasfragment(rawwork, ".wwtxn.") && !directoryhasfragment(rawwork, ".sepwork")); let runav: []str = [rawout]; runcommand(root, strings.concat("import-shadow-raw-run-", tags[si]), runav, time.second, &out); expectexit(&out, 42); assert(out.stdout.len == 0 && out.stderr.len == 0); if (si == 0) { rawbin = strings.dup(readfile(rawout)); } else { assert(same(rawbin, readfile(rawout))); }; let clientwork: str = strings.concat(root, "/client-work-", tags[si]); let clientout: str = strings.concat(root, "/client-output-", tags[si]); mkdirall(clientwork); let clientav: []str = [driver(stages[si]), "build", "-w", clientwork, "-I", source, "-o", clientout, "client"]; runcommand(root, strings.concat("import-shadow-client-", tags[si]), clientav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(clientout) && !os.exists(strings.concat(clientout, ".sepwork")) && !directoryhasnew(clientwork) && !directoryhasfragment(clientwork, ".wwtxn.") && !directoryhasfragment(clientwork, ".sepwork")); let clientrun: []str = [clientout]; runcommand(root, strings.concat("import-shadow-client-run-", tags[si]), clientrun, time.second, &out); expectexit(&out, 42); assert(out.stdout.len == 0 && out.stderr.len == 0); if (si == 0) { clientbin = strings.dup(readfile(clientout)); } else { assert(same(clientbin, readfile(clientout))); }; let siblingwork: str = strings.concat(root, "/sibling-work-", tags[si]); let siblingout: str = strings.concat(root, "/sibling-output-", tags[si]); mkdirall(siblingwork); let siblingav: []str = [driver(stages[si]), "build", "-w", siblingwork, "-I", source, "-o", siblingout, "sibling"]; runcommand(root, strings.concat("import-shadow-sibling-", tags[si]), siblingav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(siblingout) && !os.exists(strings.concat(siblingout, ".sepwork")) && !directoryhasnew(siblingwork) && !directoryhasfragment(siblingwork, ".wwtxn.") && !directoryhasfragment(siblingwork, ".sepwork")); let siblingrun: []str = [siblingout]; runcommand(root, strings.concat("import-shadow-sibling-run-", tags[si]), siblingrun, time.second, &out); expectexit(&out, 42); assert(out.stdout.len == 0 && out.stderr.len == 0); if (si == 0) { siblingbin = strings.dup(readfile(siblingout)); } else { assert(same(siblingbin, readfile(siblingout))); }; si += 1; }; // A selector on a closer local is not package use. The otherwise-unused // import is diagnosed first by source position, ahead of the later invalid // local selector, and the request commits no cold work or publication. writefile(strings.concat(invalid, "/main.ww"), strings.concat( "package main;\nimport dep.wire;\n", "fn main() i32 = { let wire: i32 = 2; ", "return wire.n; };\n")); writefile(strings.concat(invalidscalar, "/main.ww"), strings.concat( "package main;\nimport dep.wire;\n", "fn main() i32 = { let before: i32 = wire.value(); ", "let wire: i32 = 2; if (before == 0) { return 0; }; ", "return wire.n; };\n")); writefile(strings.concat(invalidscalar, "/main_test.ww"), "package main;\n@test fn never() void = { assert(main() == 0); };\n"); writefile(strings.concat(invalidtype, "/main.ww"), strings.concat( "package main;\nimport dep.wire;\n", "fn main() i32 = { let before: i32 = wire.value(); ", "let wire: i32 = 2; let later: wire.record; ", "return before + wire; };\n")); let invaliddiag: str = ""; si = 0; for (si < stages.len) { let work: str = strings.concat(root, "/invalid-work-", tags[si]); let output: str = strings.concat(root, "/invalid-output-", tags[si]); mkdirall(work); let av: []str = [driver(stages[si]), "build", "-w", work, "-I", source, "-o", output, "invalid"]; let out: commandout; runcommand(root, strings.concat("import-shadow-invalid-", tags[si]), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && same(primarydiagnostic(out.stderr), "\"dep.wire\" imported and not used") && occurrences(out.stderr, "imported and not used") == 1 && occurrences(out.stderr, "selector 'n' undefined") == 1 && !has(out.stderr, "shadows imported module") && !has(out.stderr, "use of package wire not in selector") && !has(out.stderr, "asserttyped:") && !has(out.stderr, "undefined reference") && !os.exists(output) && !os.exists(strings.concat(output, ".new")) && !os.exists(strings.concat(output, ".sepwork")) && directoryisempty(work)); let normalized: str = normalizedtrace(out.stderr, strings.concat(work, "/"), output); if (si == 0) { invaliddiag = strings.dup(normalized); } else { assert(same(invaliddiag, normalized)); }; si += 1; }; // Once the closer binding exists, value and type selectors must stay on // that local object. Invalid local value selection is one positioned // checker error, and a dotted type cannot recover the hidden package name. let invalidtargets: []str = ["invalidscalar", "invalidtype"]; let invalidcores: []str = ["selector 'n' undefined", "unknown type 'wire.record'"]; let invalidlocations: []str = [":4:109: error: selector 'n' undefined", ":4:81: error: unknown type 'wire.record'"]; let invalidshaperefs: []str = ["", ""]; let invalidretainedrefs: []str = ["", ""]; si = 0; for (si < stages.len) { let ii: i32 = 0; for (ii < invalidtargets.len) { let work: str = strings.concat(root, "/invalid-shape-work-", tags[si], "-", boundarypkgname(ii)); let output: str = strings.concat(root, "/invalid-shape-output-", tags[si], "-", boundarypkgname(ii)); mkdirall(work); let av: []str = [driver(stages[si]), "build", "-w", work, "-I", source, "-o", output, invalidtargets[ii]]; let out: commandout; runcommand(root, strings.concat("import-shadow-invalid-shape-", tags[si], "-", boundarypkgname(ii)), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && same(primarydiagnostic(out.stderr), invalidcores[ii]) && has(out.stderr, invalidlocations[ii]) && occurrences(out.stderr, invalidcores[ii]) == 1 && !has(out.stderr, "shadows imported module") && !has(out.stderr, "asserttyped:") && !has(out.stderr, "calling non-function") && !has(out.stderr, "undefined reference") && !os.exists(output) && !os.exists(strings.concat(output, ".new")) && !os.exists(strings.concat(output, ".sepwork")) && directoryisempty(work)); let normalized: str = normalizedtrace(out.stderr, strings.concat(work, "/"), output); if (si == 0) { invalidshaperefs[ii] = strings.dup(normalized); } else { assert(same(invalidshaperefs[ii], normalized)); }; ii += 1; }; // A cold retained test request must reject before producing a runnable // or any committed/staged work artifact. let testwork: str = strings.concat(root, "/invalid-retained-work-", tags[si]); let retained: str = strings.concat(root, "/invalid-retained-", tags[si], ".test"); mkdirall(testwork); let testav: []str = [driver(stages[si]), "test", "-w", testwork, "-I", source, "-run", "never", "-o", retained, "invalidscalar"]; let tout: commandout; runcommand(root, strings.concat("import-shadow-invalid-retained-", tags[si]), testav, (120i64 * (time.second: i64)): time.duration, &tout); expectexit(&tout, 1); assert(same(tout.stdout, "FAIL\n") && same(primarydiagnostic(tout.stderr), invalidcores[0]) && has(tout.stderr, invalidlocations[0]) && occurrences(tout.stderr, invalidcores[0]) == 1 && !has(tout.stderr, "asserttyped:") && !os.exists(retained) && !os.exists(strings.concat(retained, ".new")) && !os.exists(strings.concat(retained, ".sepwork")) && directoryisempty(testwork)); let testnormalized: str = normalizedtrace(tout.stderr, strings.concat(testwork, "/"), retained); if (si == 0) { invalidretainedrefs[si] = strings.dup(testnormalized); } else { invalidretainedrefs[si] = strings.dup(testnormalized); assert(same(invalidretainedrefs[0], invalidretainedrefs[1])); }; si += 1; }; // Production called by a test, same-package test source, external-test // source, and an honest test-only package all accept the same lexical rule. writefile(strings.concat(roleprod, "/prod.ww"), strings.concat( "package roleprod;\nimport dep.wire;\n", "fn value() i32 = { let wire: i32 = wire.value() + 31; ", "return wire; };\n")); writefile(strings.concat(roleprod, "/prod_test.ww"), "package roleprod;\n@test fn shadow_ok() void = { assert(value() == 42); };\n"); writefile(strings.concat(rolesame, "/prod.ww"), "package rolesame;\nfn anchor() i32 = { return 1; };\n"); writefile(strings.concat(rolesame, "/same_test.ww"), strings.concat( "package rolesame;\nimport dep.wire;\n", "@test fn shadow_ok() void = { let before: i32 = wire.value(); ", "let wire: i32 = wire.value() + 31; ", "assert(anchor() == 1 && before == 11 && wire == 42); };\n")); writefile(strings.concat(roleexternal, "/prod.ww"), "package roleexternal;\nexport fn anchor() i32 = { return 1; };\n"); writefile(strings.concat(roleexternal, "/external_test.ww"), strings.concat( "package roleexternal_test;\nimport dep.wire;\n", "@test fn shadow_ok() void = { let wire: i32 = wire.value() + 31; ", "assert(wire == 42); };\n")); writefile(strings.concat(roleonly, "/only_test.ww"), strings.concat( "package roleonly;\nimport dep.wire;\n", "@test fn shadow_ok() void = { let wire: i32 = wire.value() + 31; ", "assert(wire == 42); };\n")); let roletargets: []str = ["roleprod", "rolesame", "roleexternal", "roleonly"]; let roleoutrefs: []str = ["", "", "", ""]; let roleerrrefs: []str = ["", "", "", ""]; let rolebinrefs: []str = ["", "", "", ""]; si = 0; for (si < stages.len) { let ri: i32 = 0; for (ri < roletargets.len) { let work: str = strings.concat(root, "/role-work-", tags[si], "-", boundarypkgname(ri)); let output: str = strings.concat(root, "/role-output-", tags[si], "-", boundarypkgname(ri)); mkdirall(work); let av: []str = [driver(stages[si]), "test", "-w", work, "-I", source, "-run", "shadow_ok", "-o", output, roletargets[ri]]; let out: commandout; runcommand(root, strings.concat("import-shadow-role-", tags[si], "-", boundarypkgname(ri)), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0 && os.exists(output) && !os.exists(strings.concat(output, ".sepwork")) && occurrences(out.stdout, ".shadow_ok ... ok\n") == 1 && occurrences(out.stdout, "1 passed, 0 failed, 0 skipped, 0 harness errors\n") == 1 && !directoryhasnew(work) && !directoryhasfragment(work, ".wwtxn.") && !directoryhasfragment(work, ".install") && !directoryhasfragment(work, ".sepwork")); if (si == 0) { roleoutrefs[ri] = strings.dup(out.stdout); roleerrrefs[ri] = strings.dup(out.stderr); rolebinrefs[ri] = strings.dup(readfile(output)); } else { assert(same(roleoutrefs[ri], out.stdout) && same(roleerrrefs[ri], out.stderr) && same(rolebinrefs[ri], readfile(output))); }; ri += 1; }; si += 1; }; // Warm semantic rejection preserves every committed action byte and public // binary. Restoring exact input reuses the same generation byte-for-byte. let warmfile: str = strings.concat(warm, "/main.ww"); let warmvalid: str = strings.concat( "package main;\nimport dep.wire;\n", "fn main() i32 = { let wire: i32 = wire.value() + 31; ", "return wire; };\n"); let warminvalid: str = strings.concat( "package main;\nimport dep.wire;\n", "fn main() i32 = { let wire: i32 = 42; ", "return wire.n; };\n"); writefile(warmfile, warmvalid); let warmpaths: []str = ["/.wwtool.ww", "/.wwtool.w6c", "/.wwtool.w6a", "/.wwtool.stamp", "/dep.wire.unit.ww", "/dep.wire.wwi", "/dep.wire.s", "/dep.wire.o", "/dep.wire.a", "/warm.unit.ww", "/warm.wwi", "/warm.s", "/warm.o", "/warm.a", "/warm.init.unit.ww", "/warm.init.s", "/warm.init.o"]; let warmcross: []str = alloc([], warmpaths.len: u64)!; let warmbincross: str = ""; let warmdiag: str = ""; // Retention uses a production function called by the selected test. The // unselected aborting row proves filtering survives retained/direct routes. let retainedfile: str = strings.concat(retainedcase, "/prod.ww"); let retainedvalid: str = strings.concat( "package retainedcase;\nimport dep.wire;\n", "fn value() i32 = { let wire: i32 = wire.value() + 31; ", "return wire; };\n"); let retainedinvalid: str = strings.concat( "package retainedcase;\nimport dep.wire;\n", "fn value() i32 = { let wire: i32 = 42; ", "return wire.n; };\n"); writefile(retainedfile, retainedvalid); writefile(strings.concat(retainedcase, "/retained_test.ww"), strings.concat( "package retainedcase;\n", "@test fn kept() void = { assert(value() == 42); };\n", "@test fn not_selected() void = { abort(); };\n")); let retainedcross: str = ""; let retainedoutrefs: []str = ["", ""]; let retainederrrefs: []str = ["", ""]; let retainedinvaliddiagrefs: []str = ["", ""]; let directoutrefs: []str = ["", ""]; let directerrrefs: []str = ["", ""]; si = 0; for (si < stages.len) { let work: str = strings.concat(root, "/warm-work-", tags[si]); let output: str = strings.concat(root, "/warm-output-", tags[si]); mkdirall(work); let av: []str = [driver(stages[si]), "build", "-w", work, "-I", source, "-o", output, "warm"]; let out: commandout; runcommand(root, strings.concat("import-shadow-warm-cold-", tags[si]), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(output)); let snapshots: []str = alloc([], warmpaths.len: u64)!; let wi: i32 = 0; for (wi < warmpaths.len) { let bytes: str = strings.dup(readfile(strings.concat(work, warmpaths[wi]))); append(snapshots, bytes); if (si == 0) { append(warmcross, strings.dup(bytes)); } else if (wi >= 4) { assert(same(warmcross[wi], bytes)); }; wi += 1; }; let binbytes: str = strings.dup(readfile(output)); if (si == 0) { warmbincross = strings.dup(binbytes); } else { assert(same(warmbincross, binbytes)); }; rewritefile(warmfile, warminvalid); runcommand(root, strings.concat("import-shadow-warm-invalid-", tags[si]), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(out.stdout.len == 0 && same(primarydiagnostic(out.stderr), "\"dep.wire\" imported and not used") && occurrences(out.stderr, "selector 'n' undefined") == 1 && same(binbytes, readfile(output)) && !os.exists(strings.concat(output, ".new")) && !os.exists(strings.concat(output, ".sepwork"))); let normalized: str = normalizedtrace(out.stderr, strings.concat(work, "/"), output); if (si == 0) { warmdiag = strings.dup(normalized); } else { assert(same(warmdiag, normalized)); }; wi = 0; for (wi < warmpaths.len) { assert(same(snapshots[wi], readfile(strings.concat(work, warmpaths[wi])))); wi += 1; }; assert(!directoryhasnew(work) && !directoryhasfragment(work, ".wwtxn.") && !directoryhasfragment(work, ".install") && !directoryhasfragment(work, ".sepwork")); rewritefile(warmfile, warmvalid); runcommand(root, strings.concat("import-shadow-warm-restored-", tags[si]), av, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stdout.len == 0 && out.stderr.len == 0 && same(binbytes, readfile(output)) && !os.exists(strings.concat(output, ".sepwork")) && !directoryhasnew(work) && !directoryhasfragment(work, ".wwtxn.") && !directoryhasfragment(work, ".install") && !directoryhasfragment(work, ".sepwork")); wi = 0; for (wi < warmpaths.len) { assert(same(snapshots[wi], readfile(strings.concat(work, warmpaths[wi])))); wi += 1; }; let testwork: str = strings.concat(root, "/retained-work-", tags[si]); let retained: str = strings.concat(root, "/retained-", tags[si], ".test"); mkdirall(testwork); let testav: []str = [driver(stages[si]), "test", "-w", testwork, "-I", source, "-run", "kept", "-o", retained, "retainedcase"]; runcommand(root, strings.concat("import-shadow-retained-cold-", tags[si]), testav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0 && os.exists(retained) && !os.exists(strings.concat(retained, ".sepwork")) && occurrences(out.stdout, "retainedcase.kept ... ok\n") == 1 && occurrences(out.stdout, "1 passed, 0 failed, 0 skipped, 0 harness errors\n") == 1 && !has(out.stdout, "not_selected") && !directoryhasnew(testwork) && !directoryhasfragment(testwork, ".wwtxn.") && !directoryhasfragment(testwork, ".sepwork")); retainedoutrefs[si] = strings.dup(out.stdout); retainederrrefs[si] = strings.dup(out.stderr); let retainedbytes: str = strings.dup(readfile(retained)); if (si == 0) { retainedcross = strings.dup(retainedbytes); } else { assert(same(retainedcross, retainedbytes)); }; rewritefile(retainedfile, retainedinvalid); runcommand(root, strings.concat("import-shadow-retained-invalid-", tags[si]), testav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(same(out.stdout, "FAIL\n") && same(primarydiagnostic(out.stderr), "\"dep.wire\" imported and not used") && occurrences(out.stderr, "selector 'n' undefined") == 1 && same(retainedbytes, readfile(retained)) && !os.exists(strings.concat(retained, ".new")) && !os.exists(strings.concat(retained, ".sepwork")) && !directoryhasnew(testwork) && !directoryhasfragment(testwork, ".wwtxn.") && !directoryhasfragment(testwork, ".install") && !directoryhasfragment(testwork, ".sepwork")); let retainedinvalidnormalized: str = normalizedtrace(out.stderr, strings.concat(testwork, "/"), retained); retainedinvaliddiagrefs[si] = strings.dup(retainedinvalidnormalized); if (si == 1) { assert(same(retainedinvaliddiagrefs[0], retainedinvaliddiagrefs[1])); }; let directav: []str = [retained, "-package=retainedcase", "kept"]; runcommand(root, strings.concat("import-shadow-retained-direct-", tags[si]), directav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0 && occurrences(out.stdout, "retainedcase.kept ... ok\n") == 1 && occurrences(out.stdout, "1 passed, 0 failed, 0 skipped, 0 harness errors\n") == 1 && !has(out.stdout, "not_selected")); directoutrefs[si] = strings.dup(out.stdout); directerrrefs[si] = strings.dup(out.stderr); rewritefile(retainedfile, retainedvalid); runcommand(root, strings.concat("import-shadow-retained-restored-", tags[si]), testav, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(out.stdout, retainedoutrefs[si]) && same(out.stderr, retainederrrefs[si]) && same(retainedbytes, readfile(retained)) && !os.exists(strings.concat(retained, ".sepwork")) && !directoryhasnew(testwork) && !directoryhasfragment(testwork, ".wwtxn.") && !directoryhasfragment(testwork, ".install") && !directoryhasfragment(testwork, ".sepwork")); si += 1; }; assert(same(retainedoutrefs[0], retainedoutrefs[1]) && same(retainederrrefs[0], retainederrrefs[1]) && same(directoutrefs[0], directoutrefs[1]) && same(directerrrefs[0], directerrrefs[1])); assert(!directoryhasnew(root) && !directoryhasfragment(root, ".wwtxn.") && !directoryhasfragment(root, ".install") && !directoryhasfragment(root, ".sepwork") && !directoryhasfragment(root, ".capture") && !directoryhasfragment(root, ".result") && !directoryhasfragment(root, ".request")); clean(root); }; // An explicit run target ends driver-option parsing. The child receives every // later string unchanged, including spellings that would have been options at // the driver front; argv[0] remains WW's private PID-bearing executable path. fn runargvexpect(out: *commandout, code: i32, stdout: str, stderr: str, message: str) void = { if (out.termination != exec.termination.EXIT || out.code != code || !same(out.stdout, stdout) || !same(out.stderr, stderr)) { abort(message); }; }; fn runargvrequire(ok: bool, message: str) void = { if (!ok) { abort(message); }; }; fn runargvprivateclean(record: str, message: str) void = { let path: str = readfile(record); runargvrequire(path.len != 0 && !os.exists(path) && !os.exists(strings.concat(path, ".sepwork")), message); }; fn runargvnormalizedstderr(stderr: str) str = { let prefix: str = "/tmp/ww_run_"; let begin: i32 = pos(stderr, prefix); runargvrequire(begin >= 0, "run argv: missing private diagnostic path"); let digits: i32 = begin + prefix.len; let end: i32 = digits; for (end < stderr.len && stderr[end] >= '0' && stderr[end] <= '9') { end += 1; }; runargvrequire(end > digits, "run argv: malformed private diagnostic path"); return strings.concat(strings.sub(stderr, 0, begin), "$RUN", strings.sub(stderr, end, stderr.len)); }; @test fn run_post_target_arguments_are_program_argv() void = { let root: str = fresh(); let source: str = strings.concat(root, "/source"); let command: str = strings.concat(source, "/cmd/argv"); let library: str = strings.concat(source, "/lib/argv"); let tests: str = strings.concat(source, "/proof/tests"); mkdirall(command); mkdirall(library); mkdirall(tests); let named: str = strings.concat(source, "/named.ww"); let program: str = strings.concat( "package main;\nimport os;\nimport strings;\n", "fn main() i32 = {\n", " let av: []str = os.args(); let i: i32 = 1;\n", " if (av.len > 1) {\n", " let fd: i32 = os.open(av[1], os.flag.WRONLY | os.flag.CREATE |\n", " os.flag.TRUNC, 384i32); if (fd < 0) { return 96; };\n", " os.write(fd, av[0].ptr, av[0].len: u64); os.close(fd);\n", " };\n", " for (i < av.len) {\n", " os.write(os.STDOUT_FILENO, \"<\".ptr, 1u64);\n", " os.write(os.STDOUT_FILENO, av[i].ptr, av[i].len: u64);\n", " os.write(os.STDOUT_FILENO, \">\\n\".ptr, 2u64); i += 1;\n", " };\n", " if (av.len == 3 && strings.compare(av[2], \"exit-zero\") == 0) {\n", " return 0;\n", " }; return 47;\n};\n"); writefile(named, program); writefile(strings.concat(command, "/main.ww"), program); writefile(strings.concat(library, "/library.ww"), "package argvlib;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(tests, "/tests.ww"), "package argvtests;\nfn value() i32 = { return 1; };\n"); writefile(strings.concat(tests, "/tests_test.ww"), strings.concat( "package argvtests;\n", "@test fn ordinary() void = { assert(value() == 1); };\n")); let expected: str = strings.concat( "<-I>\n\n<--I=joined-include>\n", "<-o>\n\n<--o=joined-output>\n", "<-L>\n\n<-l>\n\n", "<-p>\n\n<-->\n\n\n<>\n<-I>\n"); let suffix: []str = ["-I", "runtime-include", "--I=joined-include", "-o", "runtime-output", "--o=joined-output", "-L", "runtime-library-dir", "-l", "runtime-library", "-p", "ignored", "--", "later", "later.ww", "", "-I"]; let stages: []str = ["ww", "ww_ww"]; let tags: []str = ["c", "ww"]; let targets: []str = [named, "cmd.argv", command]; let targettags: []str = ["named", "dotted", "directory"]; let out: commandout; let si: i32 = 0; for (si < stages.len) { let ti: i32 = 0; for (ti < targets.len) { let record: str = strings.concat(root, "/private-", tags[si], "-", targettags[ti]); let ignoredoutput: str = strings.concat(root, "/ignored-run-", tags[si], "-", targettags[ti]); let av: []str = [driver(stages[si]), "run", "-I", source, "-o", ignoredoutput, targets[ti]]; append(av, record); let ai: i32 = 0; for (ai < suffix.len) { append(av, suffix[ai]); ai += 1; }; runcommand(root, strings.concat("run-argv-", tags[si], "-", targettags[ti]), av, (120i64 * (time.second: i64)): time.duration, &out); runargvexpect(&out, 47, strings.concat("<", record, ">\n", expected), "", "run argv: target suffix result"); runargvrequire(!os.exists(ignoredoutput) && !os.exists(strings.concat(ignoredoutput, ".sepwork")), "run argv: run published an output"); runargvprivateclean(record, "run argv: runtime-nonzero cleanup"); ti += 1; }; let successrecord: str = strings.concat(root, "/private-success-", tags[si]); let successav: []str = [driver(stages[si]), "run", named, successrecord, "exit-zero"]; runcommand(root, strings.concat("run-argv-success-", tags[si]), successav, (120i64 * (time.second: i64)): time.duration, &out); runargvexpect(&out, 0, strings.concat("<", successrecord, ">\n\n"), "", "run argv: runtime-success result"); runargvprivateclean(successrecord, "run argv: runtime-success cleanup"); // Before an explicit target, the existing option grammar remains in // force. No-operand run retains its implicit current-directory target. let unknownav: []str = [driver(stages[si]), "run", "-p", named]; runcommand(root, strings.concat("run-argv-unknown-", tags[si]), unknownav, time.second, &out); runargvexpect(&out, 2, "", "ww run: unknown flag\n", "run argv: pre-target unknown flag"); let missingflagav: []str = [driver(stages[si]), "run", "-I"]; runcommand(root, strings.concat("run-argv-missing-flag-", tags[si]), missingflagav, time.second, &out); runargvexpect(&out, 2, "", "ww run: -I needs an argument\n", "run argv: pre-target missing option value"); let leadingstopav: []str = [driver(stages[si]), "run", "--", named]; runcommand(root, strings.concat("run-argv-leading-stop-", tags[si]), leadingstopav, time.second, &out); runargvexpect(&out, 2, "", "ww run: unknown flag\n", "run argv: leading terminator control"); let defaultav: []str = [driver(stages[si]), "run"]; runcommanddir(root, strings.concat("run-argv-default-", tags[si]), command, defaultav, (120i64 * (time.second: i64)): time.duration, &out); runargvexpect(&out, 47, "", "", "run argv: default-dot control"); // Target selection and loading diagnose before inert runtime bytes. let missingtarget: []str = [driver(stages[si]), "run", "-I", source, "missing.argv", "-p", "ignored"]; runcommand(root, strings.concat("run-argv-missing-target-", tags[si]), missingtarget, time.second, &out); runargvexpect(&out, 1, "", "ww run: cannot find module missing.argv\n", "run argv: missing target precedence"); let nonmain: []str = [driver(stages[si]), "run", "-I", source, "lib.argv", "--", "later.ww"]; runcommand(root, strings.concat("run-argv-non-main-", tags[si]), nonmain, time.second, &out); runargvexpect(&out, 1, "", "ww: package lib.argv is not a main package\n", "run argv: non-main target precedence"); si += 1; }; // A selected main that reaches the compiler still diagnoses before any // flag-like suffix can be interpreted or any runtime side effect can occur. let broken: str = strings.concat(source, "/broken.ww"); let brokenmarker: str = strings.concat(root, "/broken-ran"); writefile(broken, strings.concat( "package main;\nimport os;\nfn main() i32 = {\n", " let fd: i32 = os.open(\"", brokenmarker, "\", os.flag.WRONLY |\n", " os.flag.CREATE | os.flag.TRUNC, 384i32);\n", " if (fd >= 0) { os.close(fd); }; return missing;\n};\n")); let producerstderr: str = ""; let producerwant: str = strings.concat( "$RUN/main.sepwork/__root.unit.ww:7:41: error: undefined: missing\n", "ww: w6c failed for (root)\n"); si = 0; for (si < stages.len) { let brokenav: []str = [driver(stages[si]), "run", broken, "-p", "ignored", "--", brokenmarker]; runcommand(root, strings.concat("run-argv-producer-", tags[si]), brokenav, (120i64 * (time.second: i64)): time.duration, &out); runargvrequire(out.termination == exec.termination.EXIT && out.code == 1 && out.stdout.len == 0 && occurrences(out.stderr, "undefined: missing") == 1 && occurrences(out.stderr, "ww: w6c failed for (root)") == 1 && pos(out.stderr, "undefined: missing") < pos(out.stderr, "ww: w6c failed for (root)") && !has(out.stderr, "unknown flag") && !os.exists(brokenmarker), "run argv: producer failure precedence"); let normalized: str = runargvnormalizedstderr(out.stderr); runargvrequire(same(normalized, producerwant), "run argv: exact producer diagnostic"); if (si == 0) { producerstderr = strings.dup(normalized); } else { runargvrequire(same(producerstderr, normalized), "run argv: producer diagnostic stage parity"); }; si += 1; }; // Runtime suffixes are request-local even while the two fronts execute // concurrently and share their source closure. let crecord: str = strings.concat(root, "/private-parallel-c"); let wrecord: str = strings.concat(root, "/private-parallel-ww"); let cav: []str = [driver("ww"), "run", named, crecord, "c-only", "", "-I"]; let wav: []str = [driver("ww_ww"), "run", "-I", source, command, wrecord, "ww-only", "--", "tail.ww"]; let cc: exec.command; cc.path = cav[0]; cc.argv = cav; cc.env = os.getenvs(); cc.dir = repo(); cc.stdoutpath = strings.concat(root, "/run-argv-parallel-c.stdout"); cc.stderrpath = strings.concat(root, "/run-argv-parallel-c.stderr"); cc.deadline = time.add(time.now(time.clock.monotonic), (120i64 * (time.second: i64)): time.duration); cc.grace = (100i64 * (time.millisecond: i64)): time.duration; let wc: exec.command; wc.path = wav[0]; wc.argv = wav; wc.env = os.getenvs(); wc.dir = repo(); wc.stdoutpath = strings.concat(root, "/run-argv-parallel-ww.stdout"); wc.stderrpath = strings.concat(root, "/run-argv-parallel-ww.stderr"); wc.deadline = time.add(time.now(time.clock.monotonic), (120i64 * (time.second: i64)): time.duration); wc.grace = (100i64 * (time.millisecond: i64)): time.duration; let cp: exec.process; let wp: exec.process; exec.start(&cp, &cc); exec.start(&wp, &wc); let cdone: bool = false; let wdone: bool = false; for (!cdone || !wdone) { if (!cdone) { cdone = exec.poll(&cp); }; if (!wdone) { wdone = exec.poll(&wp); }; if (!cdone || !wdone) { time.sleep(time.millisecond, time.clock.monotonic); }; }; runargvrequire(cp.result.errno == 0 && cp.result.cleanuperrno == 0 && cp.result.termination == exec.termination.EXIT && cp.result.code == 47 && wp.result.errno == 0 && wp.result.cleanuperrno == 0 && wp.result.termination == exec.termination.EXIT && wp.result.code == 47 && same(readfile(cc.stdoutpath), strings.concat("<", crecord, ">\n\n<>\n<-I>\n")) && readfile(cc.stderrpath).len == 0 && same(readfile(wc.stdoutpath), strings.concat("<", wrecord, ">\n\n<-->\n\n")) && readfile(wc.stderrpath).len == 0, "run argv: concurrent isolation"); runargvprivateclean(crecord, "run argv: parallel C cleanup"); runargvprivateclean(wrecord, "run argv: parallel WW cleanup"); // Run argv never changes build/test products or persistent action bytes. let cbuildwork: str = strings.concat(root, "/build-work-c"); let wbuildwork: str = strings.concat(root, "/build-work-ww"); let cbuildout: str = strings.concat(root, "/build-output-c"); let wbuildout: str = strings.concat(root, "/build-output-ww"); mkdirall(cbuildwork); mkdirall(wbuildwork); let cbuildav: []str = [driver("ww"), "build", "-w", cbuildwork, "-o", cbuildout, named]; let wbuildav: []str = [driver("ww_ww"), "build", "-w", wbuildwork, "-o", wbuildout, named]; runcommand(root, "run-argv-build-c", cbuildav, (120i64 * (time.second: i64)): time.duration, &out); runargvexpect(&out, 0, "", "", "run argv: C build control"); runcommand(root, "run-argv-build-ww", wbuildav, (120i64 * (time.second: i64)): time.duration, &out); runargvexpect(&out, 0, "", "", "run argv: WW build control"); runargvrequire(same(readfile(cbuildout), readfile(wbuildout)) && same(wrongsuffixsemantictreesnapshot(cbuildwork), wrongsuffixsemantictreesnapshot(wbuildwork)), "run argv: build artifact parity"); let ctestwork: str = strings.concat(root, "/test-work-c"); let wtestwork: str = strings.concat(root, "/test-work-ww"); let ctestout: str = strings.concat(root, "/test-output-c"); let wtestout: str = strings.concat(root, "/test-output-ww"); mkdirall(ctestwork); mkdirall(wtestwork); let ctestav: []str = [driver("ww"), "test", "-c", "-w", ctestwork, "-I", source, "-o", ctestout, "proof.tests"]; let wtestav: []str = [driver("ww_ww"), "test", "-c", "-w", wtestwork, "-I", source, "-o", wtestout, "proof.tests"]; runcommand(root, "run-argv-test-c", ctestav, (120i64 * (time.second: i64)): time.duration, &out); runargvexpect(&out, 0, "", "", "run argv: C test control"); runcommand(root, "run-argv-test-ww", wtestav, (120i64 * (time.second: i64)): time.duration, &out); runargvexpect(&out, 0, "", "", "run argv: WW test control"); runargvrequire(same(readfile(ctestout), readfile(wtestout)) && same(wrongsuffixsemantictreesnapshot(ctestwork), wrongsuffixsemantictreesnapshot(wtestwork)), "run argv: test artifact parity"); runargvrequire(!wrongsuffixpathfragment(root, ".new") && !wrongsuffixpathfragment(root, ".old") && !wrongsuffixpathfragment(root, ".wwtxn.") && !wrongsuffixpathfragment(root, ".install") && !wrongsuffixpathfragment(root, ".sepwork") && !wrongsuffixpathfragment(root, ".stage") && !wrongsuffixpathfragment(root, ".capture") && !wrongsuffixpathfragment(root, ".result") && !wrongsuffixpathfragment(root, ".request") && !wrongsuffixpathfragment(root, ".descriptor") && !wrongsuffixpathfragment(root, ".child"), "run argv: normal residue"); clean(root); };