package package_test; // Make builds and invokes this one binary; discovery, subprocess ownership, // output interpretation, and assertions remain WW code. import os; import os.exec; import strings; import temp; import time; type commandout = struct { termination: exec.termination, code: i32, stdout: str, stderr: str, }; 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 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 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 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 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 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 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); }; @test fn deterministic_routing_and_filters() void = { let root: str = fresh(); let target: str = packagepath("routing"); let av: []str = [driver("ww"), "test", "-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_test.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_test.external_package\n") == 1); let rav: []str = [driver("ww"), "test", 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_test.external_package ... ok\n")); assert(has(out.stdout, "2 passed, 0 failed, 0 skipped, 0 harness errors\n")); assert(has(out.stdout, "1 passed, 0 failed, 0 skipped, 0 harness errors\n")); let fav: []str = [driver("ww"), "test", "-run", "routing.private_helper_first", "-filter", "external_package", 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_test.external_package ... ok\n")); let nav: []str = [driver("ww"), "test", "-run", "no-such-*", target]; runcommand(root, "nomatch", nav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(occurrences(out.stdout, "[no matches]\n") == 2); assert(occurrences(out.stdout, "1 discovered, 0 selected, 0 started, 0 completed\n") == 1); assert(occurrences(out.stdout, "2 discovered, 0 selected, 0 started, 0 completed\n") == 1); let lnav: []str = [driver("ww"), "test", "-list", "-run", "no-such-*", target]; runcommand(root, "list-nomatch", lnav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(occurrences(out.stdout, "[no matches]\n") == 2); 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 av: []str = [driver("ww"), "test", packagepath("no_tests")]; let out: commandout; runcommand(root, "notests", av, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, " [no tests]\n")); assert(!has(out.stdout, " ... ok")); 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 tests]")); // 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"); let stages: []str = ["ww", "ww_ww"]; let si: i32 = 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")); // The bootstrap drivers store package identities in the same 256-byte // slot. Reject an overlong coordinator selector before either stage can // truncate it into a different package graph. 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")); 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, 1); expectexit(&outw, 1); assert(same(outc.stdout, outw.stdout)); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stderr, "invalid --ww-package-test variant")); 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 tests]")); 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, " [no tests]\n")); assert(!has(out.stdout, "phantom")); clean(root); }; @test fn symlink_package_is_rejected() void = { let root: str = fresh(); let real: str = strings.concat(root, "/real"); let link: str = strings.concat(root, "/link"); assert(os.mkdir(real, 448i32) == 0); writefile(strings.concat(real, "/real.ww"), "package real;\nexport fn value() int = { return 1; };\n"); let lav: []str = ["/bin/ln", "-s", real, link]; let out: commandout; runcommand(root, "link", lav, time.second, &out); expectexit(&out, 0); let av: []str = [driver("ww"), "test", link]; runcommand(root, "reject", av, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 1); assert(has(out.stderr, "symlink traversal is not allowed")); 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_test.alpha_pass ... ok\n")); assert(has(out.stdout, "pass_test.beta_skip ... SKIP: synthetic unavailable feature\n")); assert(has(out.stdout, "pass_test.gamma_expected_abort ... ok\n")); assert(has(out.stdout, "pass_test.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, "assert_failure_test.assertion_failure ... FAIL (exit 1)\n")); assert(!has(out.stderr, "captures:")); 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_test.deliberate_nonzero_exit ... FAIL (exit 7)\n")); 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_test.clean_exit_without_completion ... HARNESS (incomplete result)\n")); 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_test.signal_is_not_exit ... FAIL (signal 15)\n")); 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")); 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_test.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_test.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(); writefile(strings.concat(root, "/route.ww"), "package route;\nexport fn value() int = { return 7; };\n"); writefile(strings.concat(root, "/a_test.ww"), "package route;\n@test fn white() void = { assert(value() == 7); };\n"); writefile(strings.concat(root, "/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", "-list", root]; let listw: []str = [driver("ww_ww"), "test", "-list", root]; 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", root]; let timeoutw: []str = [driver("ww_ww"), "test", "-timeout-ms=4000000", root]; 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 cc: []str = [driver("ww"), "test", "-c", root]; runcommand(root, "compile-c", cc, (30i64 * (time.second: i64)): time.duration, &outc); expectexit(&outc, 0); assert(has(outc.stdout, strings.concat(" -> ", root, "/route.test\n"))); assert(has(outc.stdout, strings.concat(" -> ", root, "/route_test.test\n"))); let cwhite: str = readfile(strings.concat(root, "/route.test.sepwork/__ww-test-same.s")); let cexternal: str = readfile(strings.concat(root, "/route.test.sepwork/__ww-test-external.s")); // The explicit package `-c` outputs are caller-owned artifacts. Release // the shared C-stage tree before asking the WW driver to acquire the same // stem; the driver never deletes a pre-existing `.sepwork` path. clean(strings.concat(root, "/route.test.sepwork")); let wc: []str = [driver("ww_ww"), "test", "-c", root]; 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(root, "/route.test.sepwork/__ww-test-same.s")))); assert(same(cexternal, readfile(strings.concat(root, "/route.test.sepwork/__ww-test-external.s")))); 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 testonly: str = strings.concat(root, "/__same"); let externalonly: str = strings.concat(root, "/__external"); let pkg: str = strings.concat(root, "/pkg"); assert(os.mkdir(implementation, 448i32) == 0); assert(os.mkdir(api, 448i32) == 0); assert(os.mkdir(testonly, 448i32) == 0); assert(os.mkdir(externalonly, 448i32) == 0); assert(os.mkdir(pkg, 448i32) == 0); 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, "/api_test.ww"), strings.concat( "package api;\n", "TEST_DEPENDENCY_MUST_NOT_COMPILE\n")); writefile(strings.concat(testonly, "/__same.ww"), strings.concat( "package __same;\n", "export fn value() i32 = { return 1; };\n")); writefile(strings.concat(externalonly, "/__external.ww"), strings.concat( "package __external;\n", "export 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(pkg, "/same_test.ww"), strings.concat( "package pkg;\nimport __same;\n", "// SAME_TEST_SOURCE\n", "@test fn same_graph() void = {\n", " assert(value() + __same.value() == 42);\n", " assert(secret() == 6);\n};\n")); writefile(strings.concat(pkg, "/external_test.ww"), strings.concat( "package pkg_test;\nimport __external;\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};\n")); let prodout: str = strings.concat(root, "/production.a"); let prodav: []str = [driver("ww"), "build", "-p", "-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, "//ww:module api\n")); assert(!has(produnit, "//ww:module implementation\n")); assert(!has(produnit, "__same")); assert(!has(produnit, "__external")); assert(!os.exists(strings.concat(prodwork, "__same.unit.ww"))); assert(!os.exists(strings.concat(prodwork, "__external.unit.ww"))); assert(!has(readfile(strings.concat(prodwork, "api.unit.ww")), "TEST_DEPENDENCY_MUST_NOT_COMPILE")); let samebin: str = strings.concat(pkg, "/pkg.test"); let externalbin: str = strings.concat(pkg, "/pkg_test.test"); let sharedworkroot: str = strings.concat(samebin, ".sepwork"); let sharedwork: str = strings.concat(sharedworkroot, "/"); let compilertrace: str = strings.concat(root, "/compiler.trace"); let linkertrace: str = strings.concat(root, "/linker.trace"); let compilerwrapper: str = strings.concat(root, "/trace-w6c.sh"); let linkerwrapper: str = strings.concat(root, "/trace-w6l.sh"); writefile(compilertrace, ""); writefile(linkertrace, ""); writeexecutable(compilerwrapper, strings.concat( "#!/bin/sh\n", "printf '%s\\n' \"$*\" >> \"$WW_PACKAGE_COMPILER_TRACE\"\n", "exec \"$WW_PACKAGE_W6C\" \"$@\"\n")); writeexecutable(linkerwrapper, strings.concat( "#!/bin/sh\n", "printf '%s\\n' \"$*\" >> \"$WW_PACKAGE_LINKER_TRACE\"\n", "exec \"$WW_PACKAGE_W6L\" \"$@\"\n")); let baseenv: []str = os.getenvs(); let traceenv: []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_W6L=") && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_COMPILER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_LINKER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6C=") && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_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_PACKAGE_COMPILER_TRACE=", compilertrace)); append(traceenv, strings.concat("WW_PACKAGE_LINKER_TRACE=", linkertrace)); append(traceenv, strings.concat("WW_PACKAGE_W6C=", driver("w6c"))); append(traceenv, strings.concat("WW_PACKAGE_W6L=", driver("w6l"))); let drivers: []str = ["ww", "ww", "ww_ww"]; let tags: []str = ["c1", "c2", "ww"]; let referenceout: str = ""; let referenceerr: str = ""; let referencesame: str = ""; let referenceexternal: str = ""; let referencesameunit: str = ""; let referenceexternalunit: str = ""; let referenceproductionunit: str = ""; let i: i32 = 0; for (i < drivers.len) { let av: []str = [driver(drivers[i]), "test", "-c", "-I", root, pkg]; if (i == 0) { runcommandenv(root, strings.concat("variant-build-", tags[i]), av, traceenv, (120i64 * (time.second: i64)): time.duration, &out); } else { runcommand(root, strings.concat("variant-build-", tags[i]), av, (120i64 * (time.second: i64)): time.duration, &out); }; expectexit(&out, 0); let buildstdout: str = strings.dup(out.stdout); let buildstderr: str = strings.dup(out.stderr); let sameunit: str = readfile(strings.concat(sharedwork, "__ww-test-same.unit.ww")); let externalunit: str = readfile(strings.concat(sharedwork, "__ww-test-external.unit.ww")); let externalproduction: str = readfile(strings.concat(sharedwork, "pkg.unit.ww")); assert(!os.exists(strings.concat(externalbin, ".sepwork"))); assert(has(sameunit, "//ww:module api\n")); assert(has(sameunit, "//ww:module __same\n")); assert(has(sameunit, "//ww:module test\n")); assert(!has(sameunit, "//ww:module implementation\n")); assert(!has(sameunit, "//ww:module __external\n")); assert(os.exists(strings.concat(sharedwork, "__external.unit.ww"))); assert(has(sameunit, "PACKAGE_PRODUCTION_A")); assert(has(sameunit, "PACKAGE_PRODUCTION_Z")); assert(has(sameunit, "SAME_TEST_SOURCE")); assert(!has(sameunit, "EXTERNAL_TEST_SOURCE")); assert(has(externalunit, "//ww:module pkg\n")); assert(has(externalunit, "//ww:module __external\n")); assert(has(externalunit, "//ww:module test\n")); assert(!has(externalunit, "//ww:module api\n")); assert(!has(externalunit, "//ww:module implementation\n")); assert(!has(externalunit, "//ww:module __same\n")); assert(has(externalunit, "EXTERNAL_TEST_SOURCE")); assert(!has(externalunit, "PACKAGE_PRODUCTION_A")); assert(!has(externalunit, "PACKAGE_PRODUCTION_Z")); assert(has(externalproduction, "PACKAGE_PRODUCTION_A")); assert(has(externalproduction, "PACKAGE_PRODUCTION_Z")); assert(has(externalproduction, "//ww:module api\n")); assert(!has(externalproduction, "//ww:module implementation\n")); assert(!has(externalproduction, "SAME_TEST_SOURCE")); assert(!has(externalproduction, "EXTERNAL_TEST_SOURCE")); assert(!has(readfile(strings.concat(sharedwork, "api.unit.ww")), "TEST_DEPENDENCY_MUST_NOT_COMPILE")); let artifacts: []str = ["implementation", "api", "pkg", "__same", "__external", "test"]; let ai: i32 = 0; for (ai < artifacts.len) { assert(os.exists(strings.concat(sharedwork, artifacts[ai], ".wwi"))); assert(os.exists(strings.concat(sharedwork, artifacts[ai], ".a"))); ai += 1; }; assert(os.exists(strings.concat(sharedwork, "__ww-test-same.o"))); assert(os.exists(strings.concat(sharedwork, "__ww-test-external.o"))); assert(!os.exists(strings.concat(sharedwork, "__ww-test-same.wwi"))); assert(!os.exists(strings.concat(sharedwork, "__ww-test-external.wwi"))); assert(!os.exists(strings.concat(sharedwork, "__ww-test-same.a"))); assert(!os.exists(strings.concat(sharedwork, "__ww-test-external.a"))); assert(os.exists(samebin)); assert(os.exists(externalbin)); if (i == 0) { let ctrace: str = readfile(compilertrace); assert(occurrences(ctrace, "implementation.unit.ww") == 1); assert(occurrences(ctrace, "api.unit.ww") == 1); assert(occurrences(ctrace, "pkg.unit.ww") == 1); assert(occurrences(ctrace, "/__same.unit.ww") == 1); assert(occurrences(ctrace, "/__external.unit.ww") == 1); assert(occurrences(ctrace, "/test.unit.ww") == 1); assert(occurrences(ctrace, "__ww-test-same.unit.ww") == 1); assert(occurrences(ctrace, "__ww-test-external.unit.ww") == 1); assert(occurrences(ctrace, "-T --test-support-module") == 2); let ltrace: str = readfile(linkertrace); assert(occurrences(ltrace, "\n") == 2); let samelink: str = linecontaining(ltrace, strings.concat("-o ", samebin, " ")); let externallink: str = linecontaining(ltrace, strings.concat("-o ", externalbin, " ")); assert(has(samelink, strings.concat(sharedwork, "api.a"))); assert(has(samelink, strings.concat(sharedwork, "implementation.a"))); assert(has(samelink, strings.concat(sharedwork, "__same.a"))); assert(!has(samelink, strings.concat(sharedwork, "pkg.a"))); assert(!has(samelink, strings.concat(sharedwork, "__external.a"))); assert(has(externallink, strings.concat(sharedwork, "pkg.a"))); assert(has(externallink, strings.concat(sharedwork, "api.a"))); assert(has(externallink, strings.concat(sharedwork, "implementation.a"))); assert(has(externallink, strings.concat(sharedwork, "__external.a"))); assert(!has(externallink, strings.concat(sharedwork, "__same.a"))); }; let runav: []str = [samebin]; runcommand(root, strings.concat("variant-run-same-", tags[i]), runav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "same_graph ... ok\n")); let externalrun: []str = [externalbin]; runcommand(root, strings.concat("variant-run-external-", tags[i]), externalrun, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "external_graph ... ok\n")); if (i == 0) { referenceout = buildstdout; referenceerr = buildstderr; referencesame = readfile(samebin); referenceexternal = readfile(externalbin); referencesameunit = strings.dup(sameunit); referenceexternalunit = strings.dup(externalunit); referenceproductionunit = strings.dup(externalproduction); } else { assert(same(referencesame, readfile(samebin))); assert(same(referenceexternal, readfile(externalbin))); assert(same(referencesameunit, sameunit)); assert(same(referenceexternalunit, externalunit)); assert(same(referenceproductionunit, externalproduction)); assert(same(referenceout, buildstdout)); assert(same(referenceerr, buildstderr)); }; if (i + 1 < drivers.len) { clean(sharedworkroot); clean(samebin); clean(externalbin); }; i += 1; }; // The illegal-in-import root keys stay disjoint from the valid packages // literally named __same and __external, including warm reuse. let warmroot: str = strings.concat(root, "/warm"); assert(os.mkdir(warmroot, 448i32) == 0); let warmav: []str = [driver("ww"), "test", "-w", warmroot, "-I", root, pkg]; runcommandenv(root, "variant-warm-cold", warmav, traceenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let warmstdout: str = strings.dup(out.stdout); let warmstderr: str = strings.dup(out.stderr); let warmtrace: str = readfile(compilertrace); let warmwork: str = strings.concat(warmroot, "/d_", workescape(pkg), "_p_pkg/"); assert(has(readfile(strings.concat(warmwork, "__same.unit.ww")), "package __same;")); assert(has(readfile(strings.concat(warmwork, "__ww-test-same.unit.ww")), "SAME_TEST_SOURCE")); assert(has(readfile(strings.concat(warmwork, "__external.unit.ww")), "package __external;")); assert(has(readfile(strings.concat(warmwork, "__ww-test-external.unit.ww")), "EXTERNAL_TEST_SOURCE")); runcommandenv(root, "variant-warm-reuse", warmav, traceenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(warmstdout, out.stdout)); assert(same(warmstderr, out.stderr)); assert(same(warmtrace, readfile(compilertrace))); let aliasunit: str = strings.concat(root, "/alias.unit.ww"); writefile(aliasunit, strings.concat( "//ww:module-reset __wwtest\n", "package test;\nexport fn value() i32 = { return 1; };\n")); let aliasstages: []str = ["w6c", "w6c_ww"]; let aliaserrs: []str = ["", ""]; i = 0; for (i < aliasstages.len) { let asmout: str = strings.concat(root, "/alias-", aliasstages[i], ".s"); let rawav: []str = [driver(aliasstages[i]), "-c", "-o", asmout, aliasunit]; runcommand(root, strings.concat("alias-unowned-", aliasstages[i]), rawav, (30i64 * (time.second: i64)): time.duration, &out); assert(out.termination == exec.termination.EXIT && out.code != 0); assert(has(out.stderr, "package") && has(out.stderr, "does not match import path")); let badav: []str = [driver(aliasstages[i]), "-c", "--test-support-module", "arbitrary", "-o", asmout, aliasunit]; runcommand(root, strings.concat("alias-arbitrary-", aliasstages[i]), badav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 2); assert(has(out.stderr, "invalid --test-support-module")); aliaserrs[i] = strings.dup(out.stderr); i += 1; }; assert(same(aliaserrs[0], aliaserrs[1])); clean(root); }; // One failing root is a product failure, not permission for the shared build // request to suppress an independently compilable sibling root. @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(has(out.stdout, "valid_same_runs ... ok\n")); assert(has(out.stdout, "[badexternal, same-package]\n")); assert(has(out.stderr, "has no exported declaration 'hidden'")); assert(has(out.stderr, strings.concat("FAIL ", badexternal, " [badexternal_test] (build exit 1)\n"))); assert(!has(out.stderr, strings.concat("FAIL ", badexternal, " [badexternal]"))); 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(has(out.stdout, "valid_external_runs ... ok\n")); assert(has(out.stdout, "[badsame_test, external]\n")); assert(has(out.stderr, "undefined: missing")); assert(has(out.stderr, strings.concat("FAIL ", badsame, " [badsame] (build exit 1)\n"))); assert(!has(out.stderr, strings.concat("FAIL ", badsame, " [badsame_test]"))); samediagnostics[i] = strings.dup(primarydiagnostic(out.stderr)); i += 1; }; assert(same(externaldiagnostics[0], externaldiagnostics[1])); assert(same(samediagnostics[0], samediagnostics[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", "-c", "-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, "__ww-test-external.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 test\n")); assert(has(rootunit, "//ww:module __wwtest\n")); 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, "__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 diamond_test_dependency_compiles_once() void = { let root: str = fresh(); let shared: str = strings.concat(root, "/shared"); let left: str = strings.concat(root, "/left"); let right: str = strings.concat(root, "/right"); let target: str = strings.concat(root, "/diamond"); assert(os.mkdir(shared, 448i32) == 0); assert(os.mkdir(left, 448i32) == 0); assert(os.mkdir(right, 448i32) == 0); assert(os.mkdir(target, 448i32) == 0); writefile(strings.concat(shared, "/shared.ww"), "package shared;\nexport fn value() i32 = { return 20; };\n"); writefile(strings.concat(left, "/left.ww"), strings.concat( "package left;\nimport shared;\n", "export fn value() i32 = { return shared.value(); };\n")); writefile(strings.concat(right, "/right.ww"), strings.concat( "package right;\nimport shared;\n", "export fn value() i32 = { return shared.value() + 1; };\n")); writefile(strings.concat(target, "/diamond.ww"), "package diamond;\nfn local() i32 = { return 1; };\n"); writefile(strings.concat(target, "/diamond_test.ww"), strings.concat( "package diamond;\nimport left;\nimport right;\n", "@test fn one_shared_compile() void = {\n", " assert(left.value() + right.value() + local() == 42);\n};\n")); let trace: str = strings.concat(root, "/compiler.trace"); let wrapper: str = strings.concat(root, "/trace-w6c.sh"); writefile(trace, ""); writefile(wrapper, strings.concat( "printf '%s\\n' \"$*\" >> \"$WW_PACKAGE_TRACE\"\n", "exec \"$WW_PACKAGE_W6C\" \"$@\"\n")); let baseenv: []str = os.getenvs(); 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_PACKAGE_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6C=")) { append(env, baseenv[ei]); }; ei += 1; }; append(env, strings.concat("WW_W6C=/bin/sh ", wrapper)); append(env, strings.concat("WW_PACKAGE_TRACE=", trace)); append(env, strings.concat("WW_PACKAGE_W6C=", driver("w6c"))); let bin: str = strings.concat(root, "/diamond.test"); let av: []str = [driver("ww"), "test", "-c", "-o", bin, "-I", root, target]; let out: commandout; runcommandenv(root, "diamond-build", av, env, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(occurrences(readfile(trace), "shared.unit.ww") == 1); let work: str = strings.concat(bin, ".sepwork/"); let unit: str = readfile(strings.concat(work, "__ww-test-same.unit.ww")); assert(has(unit, "//ww:module left\n")); assert(has(unit, "//ww:module right\n")); assert(!has(unit, "//ww:module shared\n")); assert(has(readfile(strings.concat(work, "left.unit.ww")), "//ww:module shared\n")); assert(has(readfile(strings.concat(work, "right.unit.ww")), "//ww:module shared\n")); assert(os.exists(strings.concat(work, "shared.a"))); let runav: []str = [bin]; runcommand(root, "diamond-run", runav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "one_shared_compile ... ok\n")); 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"); 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); 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")); 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'"); 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 ./... parity: a trailing "..." element walks the tree, one package // run per test-bearing directory, dot- and underscore-prefixed directory // names excluded. The excluded sentinels hold failing tests so a wrong // descent turns the whole run red rather than passing silently. @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, "/.hidden"), 448i32) == 0); assert(os.mkdir(strings.concat(tree, "/_skip"), 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"), "package alpha_test;\nimport alpha;\n@test fn treealpha() void = { assert(alpha.value() == 7); };\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, "/.hidden/hid_test.ww"), "package hid;\n@test fn hidden() void = { assert(false); };\n"); writefile(strings.concat(tree, "/_skip/skip_test.ww"), "package skip;\n@test fn skipped() void = { assert(false); };\n"); let spec: str = strings.concat(tree, "/..."); let outc: commandout; let outw: commandout; let treec: []str = [driver("ww"), "test", spec]; let treew: []str = [driver("ww_ww"), "test", 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)); assert(has(outc.stdout, strings.concat("ok ", tree, " [rootpkg, same-package]\n"))); assert(has(outc.stdout, strings.concat("ok ", tree, "/alpha [alpha_test, external]\n"))); assert(has(outc.stdout, strings.concat("ok ", tree, "/beta/inner [inner, same-package]\n"))); assert(has(outc.stdout, strings.concat("? ", tree, "/gamma [no tests]\n"))); let rootreport: i32 = pos(outc.stdout, strings.concat("ok ", tree, " [rootpkg, same-package]\n")); let alphareport: i32 = pos(outc.stdout, strings.concat("ok ", tree, "/alpha [alpha_test, external]\n")); let innerreport: i32 = pos(outc.stdout, strings.concat("ok ", tree, "/beta/inner [inner, same-package]\n")); let gammareport: i32 = pos(outc.stdout, strings.concat("? ", tree, "/gamma [no tests]\n")); assert(rootreport >= 0 && rootreport < alphareport && alphareport < innerreport && innerreport < gammareport); assert(occurrences(outc.stdout, strings.concat(tree, " [")) == 1); assert(!has(outc.stdout, ".hidden")); assert(!has(outc.stdout, "_skip")); // Concurrent scheduling must not reorder the emitted byte stream. let seq: str = strings.dup(outc.stdout); let j4: []str = [driver("ww"), "test", "-j", "4", 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 patc: []str = [driver("ww"), "test", spec, "glob*"]; let patw: []str = [driver("ww_ww"), "test", spec, "glob*"]; runcommand(root, "tree-pattern-c", patc, time.second, &outc); runcommand(root, "tree-pattern-ww", patw, time.second, &outw); expectexit(&outc, 2); expectexit(&outw, 2); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stderr, "ww test: pattern needs a single test file\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, time.second, &outc); runcommand(root, "tree-o-ww", ow, time.second, &outw); expectexit(&outc, 2); expectexit(&outw, 2); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stderr, "ww test: -o needs -c for a package target\n")); // -w on a package target forwards to the coordinator: one shared // persistent driver workdir per directory plan under the root, // 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, spec]; let wdw: []str = [driver("ww_ww"), "test", "-w", wdroot, spec]; runcommand(root, "tree-w-c", wdc, (30i64 * (time.second: i64)): time.duration, &outc); 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)); // -c publishes caller-owned artifacts; a persistent workdir is a // different ownership contract — the coordinator rejects the mix. let wcc: []str = [driver("ww"), "test", "-c", "-w", wdroot, spec]; let wcw: []str = [driver("ww_ww"), "test", "-c", "-w", wdroot, spec]; runcommand(root, "tree-wc-c", wcc, time.second, &outc); runcommand(root, "tree-wc-ww", wcw, time.second, &outw); expectexit(&outc, 2); expectexit(&outw, 2); assert(same(outc.stderr, outw.stderr)); assert(has(outc.stderr, "wwtest package: -w conflicts with -c\n")); let bare: str = strings.concat(root, "/bare"); assert(os.mkdir(bare, 448i32) == 0); assert(os.mkdir(strings.concat(bare, "/sub"), 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, "directory contains no WW package sources\n")); clean(root); }; @test fn persistent_package_workdirs_do_not_collide() void = { let root: str = fresh(); let tree: str = strings.concat(root, "/tree"); let a: str = strings.concat(tree, "/a"); let ab: str = strings.concat(tree, "/a_b"); let work: str = strings.concat(root, "/work"); assert(os.mkdir(tree, 448i32) == 0); assert(os.mkdir(a, 448i32) == 0); assert(os.mkdir(ab, 448i32) == 0); assert(os.mkdir(work, 448i32) == 0); writefile(strings.concat(a, "/a.ww"), "package b_c;\nfn value() i32 = { return 1; };\n"); writefile(strings.concat(a, "/a_test.ww"), "package b_c;\n@test fn first() void = { assert(value() == 1); };\n"); writefile(strings.concat(ab, "/ab.ww"), "package c;\nfn value() i32 = { return 2; };\n"); writefile(strings.concat(ab, "/ab_test.ww"), "package c;\n@test fn second() void = { assert(value() == 2); };\n"); let av: []str = [driver("ww"), "test", "-j", "2", "-w", work, 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, " [b_c, same-package]\n"))); assert(has(cold, strings.concat("ok ", ab, " [c, same-package]\n"))); let akey: str = strings.concat(work, "/d_", workescape(a), "_p_", workescape("b_c")); let abkey: str = strings.concat(work, "/d_", workescape(ab), "_p_", workescape("c")); assert(!same(akey, abkey)); assert(os.exists(akey) && os.exists(abkey)); runcommand(root, "workkey-warm", av, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(cold, out.stdout)); clean(root); }; fn runtimepath(relative: str) str = { return strings.concat(repo(), "/test/package/runtime/", relative); }; // 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); }; // 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); }; // 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 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 pkg;\nexport fn v() i32 = { return 7; };\n"); writefile(strings.concat(pdir, "/pkg_test.ww"), strings.concat( "package pkg_test;\nimport pkg;\n", "@test fn seven() void = { assert(pkg.v() == 7); };\n")); let adir: str = strings.concat(root, "/a"); assert(os.mkdir(adir, 493i32) == 0); // '_'-prefixed: the tree walk skips it, so the artifacts and // their .sepwork trees never pollute the multi-package discovery. let outdir: str = strings.concat(root, "/_out"); assert(os.mkdir(outdir, 493i32) == 0); writefile(strings.concat(adir, "/a.ww"), "package a;\nexport fn v() i32 = { return 1; };\n"); writefile(strings.concat(adir, "/a_test.ww"), strings.concat( "package a_test;\nimport a;\n", "@test fn one() void = { assert(a.v() == 1); };\n")); let out: commandout; let drvs: []str = ["ww", "ww_ww"]; 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", "-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"); }; 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; }; 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 ... ok")); let nocav: []str = [driver(drvs[i]), "test", "-o", named, pdir]; runcommand(root, strings.concat("noc_", drvs[i]), nocav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 2); assert(has(out.stderr, "-o needs -c for a package target")); let mulav: []str = [driver(drvs[i]), "test", "-c", "-o", named, strings.concat(root, "/...")]; runcommand(root, strings.concat("multi_", drvs[i]), mulav, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 2); assert(has(out.stderr, "cannot use -o with multiple packages")); i += 1; }; clean(root); };