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 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 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 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); }; 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 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, leaf: str) str = { let out: []u8 = alloc([], (dir.len * 4 + leaf.len + 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; }; append(out, '.'); i = 0; for (i < leaf.len) { append(out, leaf[i]); 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 packagestoragekey(path: str, canon: str, variant: i32, role: i32) str = { let state: sha256.state = sha256.sha256(); let h: *hash.hash = (&state): *hash.hash; hash.write(h, strings.toutf8("ww-package-storage-v2:")); 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)); 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 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 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); }; @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_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", "-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_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", "-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_test.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(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-*", "-I", sourceroot, 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")); // 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 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_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_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(); 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 cc: []str = [driver("ww"), "test", "-c", "-I", root, route]; runcommand(root, "compile-c", cc, (30i64 * (time.second: i64)): time.duration, &outc); expectexit(&outc, 0); assert(has(outc.stdout, strings.concat(" -> ", route, "/route.test\n"))); assert(has(outc.stdout, strings.concat(" -> ", route, "/route_test.test\n"))); let cwhite: str = readfile(strings.concat(route, "/route.test.sepwork/route-internal-test.s")); let cexternal: str = readfile(strings.concat(route, "/route.test.sepwork/route_test-external-test.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(route, "/route.test.sepwork")); let wc: []str = [driver("ww_ww"), "test", "-c", "-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(route, "/route.test.sepwork/route-internal-test.s")))); assert(same(cexternal, readfile(strings.concat(route, "/route.test.sepwork/route_test-external-test.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, "/source.ww"), strings.concat( "package __same;\n", "export fn value() i32 = { return 1; };\n")); writefile(strings.concat(externalonly, "/source.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", "-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-reset pkg\n")); assert(!has(produnit, "//ww:module ")); assert(!has(produnit, "IMPLEMENTATION_SOURCE")); 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 wwtraceenv: []str = alloc([], (baseenv.len + 6): u64)!; ei = 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(wwtraceenv, baseenv[ei]); }; ei += 1; }; append(wwtraceenv, strings.concat("WW_W6C=", compilerwrapper)); append(wwtraceenv, strings.concat("WW_W6L=", linkerwrapper)); append(wwtraceenv, strings.concat("WW_PACKAGE_COMPILER_TRACE=", compilertrace)); append(wwtraceenv, strings.concat("WW_PACKAGE_LINKER_TRACE=", linkertrace)); append(wwtraceenv, strings.concat("WW_PACKAGE_W6C=", driver("w6c_ww"))); append(wwtraceenv, strings.concat("WW_PACKAGE_W6L=", driver("w6l_ww"))); 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 packageactions: []str = ["pkg-internal-test", "pkg-internal-test-main", "pkg_test-external-test", "pkg_test-external-test-main"]; let referenceactionexports: []str = ["", "", "", ""]; let referenceactionarchives: []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, "pkg-internal-test.unit.ww")); let externalunit: str = readfile(strings.concat(sharedwork, "pkg_test-external-test.unit.ww")); let externalproduction: str = readfile(strings.concat(sharedwork, "pkg.unit.ww")); let samemainunit: str = readfile(strings.concat(sharedwork, "pkg-internal-test-main.unit.ww")); let externalmainunit: str = readfile(strings.concat(sharedwork, "pkg_test-external-test-main.unit.ww")); assert(!os.exists(strings.concat(sharedwork, "__ww-test-001-external-production.unit.ww"))); assert(!os.exists(strings.concat(externalbin, ".sepwork"))); assert(!has(sameunit, "//ww:module ")); 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 ")); 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 ")); assert(!has(externalproduction, "SAME_TEST_SOURCE")); assert(!has(externalproduction, "EXTERNAL_TEST_SOURCE")); assert(same(samemainunit, strings.concat( "//ww:module-reset __wwtestmain.pkg.internal.main\n", "package main;\nimport pkg;\nimport test;\n"))); assert(same(externalmainunit, strings.concat( "//ww:module-reset __wwtestmain.pkg_test.external.main\n", "package main;\nimport pkg_test;\nimport test;\n"))); assert(!has(samemainunit, "PACKAGE_PRODUCTION")); assert(!has(samemainunit, "SAME_TEST_SOURCE")); assert(!has(externalmainunit, "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", "pkg-internal-test", "pkg-internal-test-main", "pkg_test-external-test", "pkg_test-external-test-main"]; 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; }; ai = 0; for (ai < packageactions.len) { let ex: str = readfile(strings.concat(sharedwork, packageactions[ai], ".wwi")); let ar: str = readfile(strings.concat(sharedwork, packageactions[ai], ".a")); if (i == 0) { referenceactionexports[ai] = strings.dup(ex); referenceactionarchives[ai] = strings.dup(ar); } else { assert(same(referenceactionexports[ai], ex)); assert(same(referenceactionarchives[ai], ar)); }; ai += 1; }; 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, "pkg-internal-test.unit.ww") == 1); assert(occurrences(ctrace, "pkg_test-external-test.unit.ww") == 1); assert(occurrences(ctrace, "pkg-internal-test-main.unit.ww") == 1); assert(occurrences(ctrace, "pkg_test-external-test-main.unit.ww") == 1); assert(occurrences(ctrace, "-T --entry --test-support-module") == 2); assert(occurrences(ctrace, "--test-package") == 2); let samecompile: str = linecontaining(ctrace, "pkg-internal-test.unit.ww"); let externalcompile: str = linecontaining(ctrace, "pkg_test-external-test.unit.ww"); let productioncompile: str = linecontaining(ctrace, "/pkg.unit.ww"); assert(same(samecompile, strings.concat( "--test-package -c --import __same ", sharedwork, "__same.wwi --import api ", sharedwork, "api.wwi -I ", sharedwork, "pkg-internal-test.wwi -o ", sharedwork, "pkg-internal-test.s ", sharedwork, "pkg-internal-test.unit.ww"))); assert(same(externalcompile, strings.concat( "--test-package -c --import __external ", sharedwork, "__external.wwi --import pkg ", sharedwork, "pkg.wwi -I ", sharedwork, "pkg_test-external-test.wwi -o ", sharedwork, "pkg_test-external-test.s ", sharedwork, "pkg_test-external-test.unit.ww"))); let samemaincompile: str = linecontaining(ctrace, "pkg-internal-test-main.unit.ww"); let externalmaincompile: str = linecontaining(ctrace, "pkg_test-external-test-main.unit.ww"); assert(same(samemaincompile, strings.concat( "-T --entry --test-support-module test -c --import pkg ", sharedwork, "pkg-internal-test.wwi --import test ", sharedwork, "test.wwi -I ", sharedwork, "pkg-internal-test-main.wwi -o ", sharedwork, "pkg-internal-test-main.s ", sharedwork, "pkg-internal-test-main.unit.ww"))); assert(same(externalmaincompile, strings.concat( "-T --entry --test-support-module test -c --import pkg_test ", sharedwork, "pkg_test-external-test.wwi --import test ", sharedwork, "test.wwi -I ", sharedwork, "pkg_test-external-test-main.wwi -o ", sharedwork, "pkg_test-external-test-main.s ", sharedwork, "pkg_test-external-test-main.unit.ww"))); assert(same(productioncompile, strings.concat( "-c --import api ", sharedwork, "api.wwi -I ", sharedwork, "pkg.wwi -o ", sharedwork, "pkg.s ", sharedwork, "pkg.unit.ww"))); 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-internal-test-main.a"))); assert(has(samelink, strings.concat(sharedwork, "pkg-internal-test.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, "pkg_test-external-test-main.a"))); assert(has(externallink, strings.concat(sharedwork, "pkg_test-external-test.a"))); assert(!has(samelink, ".wwi")); assert(!has(externallink, ".wwi")); 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; }; // Canonical variant keys stay disjoint from 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, "/"); assert(has(readfile(strings.concat(warmwork, "__same.unit.ww")), "package __same;")); assert(has(readfile(strings.concat(warmwork, "pkg-internal-test.unit.ww")), "SAME_TEST_SOURCE")); assert(has(readfile(strings.concat(warmwork, "__external.unit.ww")), "package __external;")); assert(has(readfile(strings.concat(warmwork, "pkg_test-external-test.unit.ww")), "EXTERNAL_TEST_SOURCE")); assert(has(readfile(strings.concat(warmwork, "pkg.unit.ww")), "PACKAGE_PRODUCTION_A")); assert(!os.exists(strings.concat(warmwork, "__ww-test-001-external-production.unit.ww"))); // Equivalent hidden product order is presentation-only. The driver sorts // and interns before compilation, so reversing the descriptors reuses the // same persistent actions and leaves every artifact byte unchanged. let orderwork: str = strings.concat(root, "/order-work"); assert(os.mkdir(orderwork, 448i32) == 0); let orderav: []str = [driver("ww"), "test", "-c", "-w", orderwork, "-I", root, "--ww-package-test", "external", "pkg_test", pkg, externalbin, strings.concat(root, "/order-external.status"), "--ww-package-test", "same", "pkg", pkg, samebin, strings.concat(root, "/order-same.status"), pkg]; let cordertracebefore: str = strings.dup(readfile(compilertrace)); let corderlinkbefore: str = strings.dup(readfile(linkertrace)); runcommandenv(root, "variant-order-reversed", orderav, traceenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let cordertraceafter: str = strings.dup(readfile(compilertrace)); let corderlinkafter: str = strings.dup(readfile(linkertrace)); let creversedtrace: str = strings.dup(strings.sub(cordertraceafter, cordertracebefore.len, cordertraceafter.len)); let creversedlink: str = strings.dup(strings.sub(corderlinkafter, corderlinkbefore.len, corderlinkafter.len)); let orderworkdir: str = strings.concat(orderwork, "/"); let orderactions: []str = ["pkg", "pkg-internal-test", "pkg_test-external-test", "pkg-internal-test-main", "pkg_test-external-test-main"]; let oi: i32 = 0; for (oi < orderactions.len) { let suffixes: []str = [".unit.ww", ".wwi", ".a"]; let xi: i32 = 0; for (xi < suffixes.len) { assert(same(readfile(strings.concat(warmwork, orderactions[oi], suffixes[xi])), readfile(strings.concat(orderworkdir, orderactions[oi], suffixes[xi])))); xi += 1; }; oi += 1; }; clean(orderwork); assert(os.mkdir(orderwork, 448i32) == 0); let orderforwardav: []str = [driver("ww"), "test", "-c", "-w", orderwork, "-I", root, "--ww-package-test", "same", "pkg", pkg, samebin, strings.concat(root, "/order-same.status"), "--ww-package-test", "external", "pkg_test", pkg, externalbin, strings.concat(root, "/order-external.status"), pkg]; let cforwardtracebefore: str = strings.dup(readfile(compilertrace)); let cforwardlinkbefore: str = strings.dup(readfile(linkertrace)); runcommandenv(root, "variant-order-forward", orderforwardav, traceenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let cforwardtraceafter: str = strings.dup(readfile(compilertrace)); let cforwardlinkafter: str = strings.dup(readfile(linkertrace)); assert(same(creversedtrace, strings.sub(cforwardtraceafter, cforwardtracebefore.len, cforwardtraceafter.len))); assert(same(creversedlink, strings.sub(cforwardlinkafter, cforwardlinkbefore.len, cforwardlinkafter.len))); let ordertrace: str = readfile(compilertrace); let coldcounts: []i32 = [occurrences(ordertrace, "/pkg.unit.new"), occurrences(ordertrace, "/pkg-internal-test.unit.new"), occurrences(ordertrace, "/pkg_test-external-test.unit.new")]; assert(coldcounts[0] == 3 && coldcounts[1] == 3 && coldcounts[2] == 3); let orderwwwork: str = strings.concat(root, "/order-ww-work"); let orderwwsame: str = strings.concat(root, "/order-ww-same.test"); let orderwwexternal: str = strings.concat(root, "/order-ww-external.test"); assert(os.mkdir(orderwwwork, 448i32) == 0); let orderwwav: []str = [driver("ww_ww"), "test", "-c", "-w", orderwwwork, "-I", root, "--ww-package-test", "external", "pkg_test", pkg, orderwwexternal, strings.concat(root, "/order-ww-external.status"), "--ww-package-test", "same", "pkg", pkg, orderwwsame, strings.concat(root, "/order-ww-same.status"), pkg]; let wordertracebefore: str = strings.dup(readfile(compilertrace)); let worderlinkbefore: str = strings.dup(readfile(linkertrace)); runcommandenv(root, "variant-order-reversed-ww", orderwwav, wwtraceenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let wordertraceafter: str = strings.dup(readfile(compilertrace)); let worderlinkafter: str = strings.dup(readfile(linkertrace)); let wreversedtrace: str = strings.dup(strings.sub(wordertraceafter, wordertracebefore.len, wordertraceafter.len)); let wreversedlink: str = strings.dup(strings.sub(worderlinkafter, worderlinkbefore.len, worderlinkafter.len)); clean(orderwwwork); assert(os.mkdir(orderwwwork, 448i32) == 0); let orderwwforward: []str = [driver("ww_ww"), "test", "-c", "-w", orderwwwork, "-I", root, "--ww-package-test", "same", "pkg", pkg, orderwwsame, strings.concat(root, "/order-ww-same.status"), "--ww-package-test", "external", "pkg_test", pkg, orderwwexternal, strings.concat(root, "/order-ww-external.status"), pkg]; let wforwardtracebefore: str = strings.dup(readfile(compilertrace)); let wforwardlinkbefore: str = strings.dup(readfile(linkertrace)); runcommandenv(root, "variant-order-forward-ww", orderwwforward, wwtraceenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let wforwardtraceafter: str = strings.dup(readfile(compilertrace)); let wforwardlinkafter: str = strings.dup(readfile(linkertrace)); assert(same(wreversedtrace, strings.sub(wforwardtraceafter, wforwardtracebefore.len, wforwardtraceafter.len))); assert(same(wreversedlink, strings.sub(wforwardlinkafter, wforwardlinkbefore.len, wforwardlinkafter.len))); oi = 0; for (oi < orderactions.len) { let suffixes: []str = [".unit.ww", ".wwi", ".a"]; let xi: i32 = 0; for (xi < suffixes.len) { assert(same(readfile(strings.concat(warmwork, orderactions[oi], suffixes[xi])), readfile(strings.concat(orderwwwork, "/", orderactions[oi], suffixes[xi])))); xi += 1; }; oi += 1; }; let tracedorders: str = readfile(compilertrace); coldcounts[0] = occurrences(tracedorders, "/pkg.unit.new"); coldcounts[1] = occurrences(tracedorders, "/pkg-internal-test.unit.new"); coldcounts[2] = occurrences(tracedorders, "/pkg_test-external-test.unit.new"); 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)); let reusedtrace: str = readfile(compilertrace); assert(occurrences(reusedtrace, "/pkg.unit.new") == coldcounts[0]); assert(occurrences(reusedtrace, "/pkg-internal-test.unit.new") == coldcounts[1]); assert(occurrences(reusedtrace, "/pkg_test-external-test.unit.new") == coldcounts[2]); 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); }; @test fn multi_directory_shared_package_plan() void = { let root: str = fresh(); let leaf: str = strings.concat(root, "/leaf"); let common: str = strings.concat(root, "/common"); let suite: str = strings.concat(root, "/suite"); 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(alpha, "/_alpha_same"); let alphaexternal: str = strings.concat(alpha, "/_alpha_external"); let betasame: str = strings.concat(beta, "/_beta_same"); let betaexternal: str = strings.concat(beta, "/_beta_external"); let alphashadow: str = strings.concat(root, "/_alpha_same"); let betashadow: str = strings.concat(root, "/_beta_same"); let dirs: []str = [leaf, common, suite, alpha, beta, gamma, alphasame, alphaexternal, betasame, betaexternal, alphashadow, betashadow]; 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(); };\n")); writefile(strings.concat(common, "/common_test.ww"), strings.concat( "package common;\n", "DEPENDENCY_TEST_FILE_MUST_NOT_COMPILE\n")); writefile(strings.concat(alphasame, "/source.ww"), "package _alpha_same;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(alphaexternal, "/source.ww"), "package _alpha_external;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(betasame, "/source.ww"), "package _beta_same;\nexport fn value() i32 = { return 2; };\n"); writefile(strings.concat(betaexternal, "/source.ww"), "package _beta_external;\nexport fn value() i32 = { return 2; };\n"); // The explicit -I root contains deliberately wrong shadows. Each selected // directory must retain its own first lookup root inside the union plan. writefile(strings.concat(alphashadow, "/source.ww"), "package _alpha_same;\nexport fn value() i32 = { return 99; };\n"); writefile(strings.concat(betashadow, "/source.ww"), "package _beta_same;\nexport fn value() i32 = { return 99; };\n"); writefile(strings.concat(alpha, "/a.ww"), strings.concat( "package alpha;\nimport common;\n", "// MULTIDIR_ALPHA_A\n", "export fn value() i32 = { return common.value(); };\n")); writefile(strings.concat(alpha, "/z.ww"), strings.concat( "package alpha;\n", "// MULTIDIR_ALPHA_Z\n", "fn secret() i32 = { return 1; };\n", "export fn exported() i32 = { return 1; };\n")); writefile(strings.concat(alpha, "/same_test.ww"), strings.concat( "package alpha;\nimport _alpha_same;\n", "// MULTIDIR_ALPHA_SAME\n", "@test fn alpha_same_runs() void = {\n", " assert(value() + secret() + _alpha_same.value() == 42);\n};\n")); writefile(strings.concat(alpha, "/external_test.ww"), strings.concat( "package alpha_test;\nimport alpha;\nimport _alpha_external;\n", "// MULTIDIR_ALPHA_EXTERNAL\n", "@test fn alpha_external_runs() void = {\n", " assert(alpha.value() + alpha.exported()", " + _alpha_external.value() == 42);\n};\n")); // A no-test directory is an ordinary production product. Beta also imports // it, proving that root/dependency discovery shares the same action. writefile(strings.concat(gamma, "/gamma.ww"), strings.concat( "package gamma;\nimport common;\n", "// MULTIDIR_GAMMA\n", "export fn value() i32 = { return common.value(); };\n")); writefile(strings.concat(beta, "/beta.ww"), strings.concat( "package beta;\nimport gamma;\nimport alpha;\n", "// MULTIDIR_BETA\n", "fn secret() i32 = { return 2; };\n", "export fn value() i32 = { return gamma.value(); };\n", "export fn exported() i32 = { return alpha.exported() + 1; };\n")); writefile(strings.concat(beta, "/same_test.ww"), strings.concat( "package beta;\nimport _beta_same;\n", "// MULTIDIR_BETA_SAME\n", "@test fn beta_same_runs() void = {\n", " assert(value() + secret() + _beta_same.value() == 44);\n};\n")); writefile(strings.concat(beta, "/external_test.ww"), strings.concat( "package beta_test;\nimport beta;\nimport _beta_external;\n", "// MULTIDIR_BETA_EXTERNAL\n", "@test fn beta_external_runs() void = {\n", " assert(beta.value() + beta.exported()", " + _beta_external.value() == 44);\n};\n")); 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"))); // Exercise default sibling discovery independently of the exact-path // override contract covered below: the copied driver sits beside narrow // tracing wrappers for its real compiler and linker actions. let wwtracebin: str = strings.concat(repo(), "/out/package-trace-", workescape(root)); assert(os.mkdir(wwtracebin, 448i32) == 0); let wwtracedriver: str = strings.concat(wwtracebin, "/ww_ww"); let wwcompilertrace: str = strings.concat(root, "/ww-compiler.trace"); let wwlinkertrace: str = strings.concat(root, "/ww-linker.trace"); writefile(wwcompilertrace, ""); writefile(wwlinkertrace, ""); writeexecutable(wwtracedriver, 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"), strings.concat( "#!/bin/sh\nexec \"$WW_PACKAGE_W6A_WW\" \"$@\"\n")); writeexecutable(strings.concat(wwtracebin, "/w6l_ww"), strings.concat( "#!/bin/sh\n", "printf '%s\\n' \"$*\" >> \"$WW_PACKAGE_WW_LINKER_TRACE\"\n", "exec \"$WW_PACKAGE_W6L_WW\" \"$@\"\n")); let wwtraceenv: []str = alloc([], (baseenv.len + 5): u64)!; ei = 0; for (ei < baseenv.len) { if (!strings.hasprefix(baseenv[ei], "WW_PACKAGE_WW_COMPILER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_WW_LINKER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6C_WW=") && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6A_WW=") && !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6L_WW=")) { append(wwtraceenv, baseenv[ei]); }; ei += 1; }; append(wwtraceenv, strings.concat("WW_PACKAGE_WW_COMPILER_TRACE=", wwcompilertrace)); append(wwtraceenv, strings.concat("WW_PACKAGE_WW_LINKER_TRACE=", wwlinkertrace)); append(wwtraceenv, strings.concat("WW_PACKAGE_W6C_WW=", driver("w6c_ww"))); append(wwtraceenv, strings.concat("WW_PACKAGE_W6A_WW=", driver("w6a_ww"))); append(wwtraceenv, strings.concat("WW_PACKAGE_W6L_WW=", driver("w6l_ww"))); let bins: []str = [strings.concat(alpha, "/alpha.test"), strings.concat(alpha, "/alpha_test.test"), strings.concat(beta, "/beta.test"), strings.concat(beta, "/beta_test.test"), strings.concat(gamma, "/gamma.test")]; let rootkeys: []str = ["alpha-internal-test", "alpha_test-external-test", "beta-internal-test", "beta_test-external-test", "gamma"]; let mainkeys: []str = ["alpha-internal-test-main", "alpha_test-external-test-main", "beta-internal-test-main", "beta_test-external-test-main", "gamma-main"]; let alphaartifact: str = "alpha"; let betaartifact: str = "beta"; let expectedtests: []str = ["alpha_same_runs ... ok\n", "alpha_external_runs ... ok\n", "beta_same_runs ... ok\n", "beta_external_runs ... ok\n"]; let sharedworkroot: str = strings.concat(bins[0], ".sepwork"); let sharedwork: str = strings.concat(sharedworkroot, "/"); let referencebins: []str = ["", "", "", "", ""]; let referenceroots: []str = ["", "", "", "", ""]; let referenceout: str = ""; let referenceerr: str = ""; let referencecommonwwi: str = ""; let referencecommona: str = ""; let referencealphaa: str = ""; let stages: []str = ["ww", "ww", "ww_ww"]; let tags: []str = ["c1", "c2", "ww"]; let out: commandout; let si: i32 = 0; for (si < stages.len) { let stagepath: str = driver(stages[si]); if (si == 2) { stagepath = wwtracedriver; }; let av: []str = [stagepath, "test", "-c", "-j", "1", "-I", suite, "-I", root, strings.concat(suite, "/...")]; if (si == 0) { runcommandenv(root, "multidir-c1", av, traceenv, (120i64 * (time.second: i64)): time.duration, &out); } else { if (si == 2) { runcommandenv(root, strings.concat("multidir-", tags[si]), av, wwtraceenv, (120i64 * (time.second: i64)): time.duration, &out); } else { runcommand(root, strings.concat("multidir-", tags[si]), 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 commonunit: str = readfile(strings.concat(sharedwork, "common.unit.ww")); let alphaunit: str = readfile(strings.concat(sharedwork, alphaartifact, ".unit.ww")); let betaunit: str = readfile(strings.concat(sharedwork, betaartifact, ".unit.ww")); let gammaunit: str = readfile(strings.concat(sharedwork, "gamma.unit.ww")); assert(has(commonunit, "//ww:module-reset common\n")); assert(!has(commonunit, "//ww:module ")); assert(!has(alphaunit, "//ww:module ")); assert(has(alphaunit, "MULTIDIR_ALPHA_A")); assert(has(alphaunit, "MULTIDIR_ALPHA_Z")); assert(!has(betaunit, "//ww:module ")); assert(has(betaunit, "MULTIDIR_BETA")); assert(!has(gammaunit, "//ww:module ")); assert(has(gammaunit, "MULTIDIR_GAMMA")); assert(!has(gammaunit, "MULTIDIR_COMMON")); assert(!has(commonunit, "DEPENDENCY_TEST_FILE_MUST_NOT_COMPILE")); let roots: []str = [readfile(strings.concat(sharedwork, rootkeys[0], ".unit.ww")), readfile(strings.concat(sharedwork, rootkeys[1], ".unit.ww")), readfile(strings.concat(sharedwork, rootkeys[2], ".unit.ww")), readfile(strings.concat(sharedwork, rootkeys[3], ".unit.ww")), readfile(strings.concat(sharedwork, rootkeys[4], ".unit.ww"))]; assert(!has(roots[0], "//ww:module ")); assert(has(roots[0], "MULTIDIR_ALPHA_A")); assert(has(roots[0], "MULTIDIR_ALPHA_Z")); assert(has(roots[0], "MULTIDIR_ALPHA_SAME")); assert(!has(roots[0], "MULTIDIR_ALPHA_EXTERNAL")); assert(!has(roots[1], "//ww:module ")); assert(has(roots[1], "MULTIDIR_ALPHA_EXTERNAL")); assert(!has(roots[1], "MULTIDIR_ALPHA_A")); assert(!has(roots[2], "//ww:module ")); assert(has(roots[2], "MULTIDIR_BETA")); assert(has(roots[2], "MULTIDIR_BETA_SAME")); assert(!has(roots[3], "//ww:module ")); assert(has(roots[3], "MULTIDIR_BETA_EXTERNAL")); assert(!has(roots[3], "MULTIDIR_BETA_SAME")); assert(has(roots[4], "MULTIDIR_GAMMA")); assert(!has(roots[4], "MULTIDIR_COMMON")); let products: []str = ["leaf", "common", alphaartifact, betaartifact, "gamma", "_alpha_same", "_alpha_external", "_beta_same", "_beta_external", "test", "alpha-internal-test", "alpha_test-external-test", "beta-internal-test", "beta_test-external-test", "alpha-internal-test-main", "alpha_test-external-test-main", "beta-internal-test-main", "beta_test-external-test-main", "gamma-main"]; let pi: i32 = 0; for (pi < products.len) { assert(os.exists(strings.concat(sharedwork, products[pi], ".unit.ww"))); assert(os.exists(strings.concat(sharedwork, products[pi], ".wwi"))); assert(os.exists(strings.concat(sharedwork, products[pi], ".a"))); pi += 1; }; let ri: i32 = 0; for (ri < rootkeys.len) { assert(os.exists(strings.concat(sharedwork, rootkeys[ri], ".o"))); assert(os.exists(strings.concat(sharedwork, rootkeys[ri], ".wwi"))); assert(os.exists(strings.concat(sharedwork, rootkeys[ri], ".a"))); assert(os.exists(strings.concat(sharedwork, mainkeys[ri], ".unit.ww"))); assert(os.exists(strings.concat(sharedwork, mainkeys[ri], ".wwi"))); assert(os.exists(strings.concat(sharedwork, mainkeys[ri], ".a"))); assert(os.exists(bins[ri])); if (ri > 0) { assert(!os.exists(strings.concat(bins[ri], ".sepwork"))); }; let runav: []str = [bins[ri]]; runcommand(root, strings.concat("multidir-run-", tags[si], "-", rootkeys[ri]), runav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); if (ri < expectedtests.len) { assert(has(out.stdout, expectedtests[ri])); } else { assert(out.stdout.len == 0 && out.stderr.len == 0); }; ri += 1; }; if (si == 0) { let ctrace: str = readfile(compilertrace); let once: []str = ["leaf", "common", alphaartifact, betaartifact, "gamma", "_alpha_same", "_alpha_external", "_beta_same", "_beta_external", "test"]; pi = 0; for (pi < once.len) { assert(occurrences(ctrace, strings.concat("/", once[pi], ".unit.ww")) == 1); pi += 1; }; // alpha is both external-test production and a direct dependency of // beta. The canonical production action above is compiled once and no // role-qualified production action exists. assert(occurrences(ctrace, "/alpha.unit.ww") == 1); assert(occurrences(ctrace, "/gamma.unit.ww") == 1); assert(occurrences(ctrace, "external-production") == 0); ri = 0; for (ri < rootkeys.len) { assert(occurrences(ctrace, strings.concat(rootkeys[ri], ".unit.ww")) == 1); assert(occurrences(ctrace, strings.concat(mainkeys[ri], ".unit.ww")) == 1); ri += 1; }; assert(pos(ctrace, "/leaf.unit.ww") < pos(ctrace, "/common.unit.ww")); assert(pos(ctrace, "/common.unit.ww") < pos(ctrace, strings.concat("/", alphaartifact, ".unit.ww"))); assert(pos(ctrace, "/common.unit.ww") < pos(ctrace, strings.concat("/", betaartifact, ".unit.ww"))); let ltrace: str = readfile(linkertrace); assert(occurrences(ltrace, "\n") == 5); ri = 0; for (ri < bins.len) { let link: str = linecontaining(ltrace, strings.concat("-o ", bins[ri], " ")); assert(has(link, strings.concat(sharedwork, mainkeys[ri], ".a"))); assert(has(link, strings.concat(sharedwork, rootkeys[ri], ".a"))); assert(!has(link, ".wwi")); assert(has(link, strings.concat(sharedwork, "common.a"))); assert(has(link, strings.concat(sharedwork, "leaf.a"))); assert(has(link, strings.concat(sharedwork, "test.a"))); if (ri >= 2) { assert(has(link, strings.concat(sharedwork, "gamma.a"))); } else { assert(!has(link, strings.concat(sharedwork, "gamma.a"))); }; if (ri == 4) { assert(!has(link, strings.concat(sharedwork, alphaartifact, ".a"))); assert(!has(link, strings.concat(sharedwork, betaartifact, ".a"))); assert(!has(link, strings.concat(sharedwork, "_alpha_same.a"))); assert(!has(link, strings.concat(sharedwork, "_alpha_external.a"))); assert(!has(link, strings.concat(sharedwork, "_beta_same.a"))); assert(!has(link, strings.concat(sharedwork, "_beta_external.a"))); } else { if (ri < 2) { assert(!has(link, strings.concat(sharedwork, betaartifact, ".a"))); assert(!has(link, strings.concat(sharedwork, "_beta_same.a"))); assert(!has(link, strings.concat(sharedwork, "_beta_external.a"))); } else { assert(has(link, strings.concat(sharedwork, alphaartifact, ".a"))); assert(!has(link, strings.concat(sharedwork, "_alpha_same.a"))); assert(!has(link, strings.concat(sharedwork, "_alpha_external.a"))); }; if (ri == 0) { assert(has(link, strings.concat(sharedwork, "_alpha_same.a"))); assert(!has(link, strings.concat(sharedwork, alphaartifact, ".a"))); assert(!has(link, strings.concat(sharedwork, "_alpha_external.a"))); } else { if (ri == 1) { assert(has(link, strings.concat(sharedwork, alphaartifact, ".a"))); assert(has(link, strings.concat(sharedwork, "_alpha_external.a"))); assert(!has(link, strings.concat(sharedwork, "_alpha_same.a"))); } else { if (ri == 2) { assert(has(link, strings.concat(sharedwork, "_beta_same.a"))); assert(!has(link, strings.concat(sharedwork, betaartifact, ".a"))); assert(!has(link, strings.concat(sharedwork, "_beta_external.a"))); } else { assert(has(link, strings.concat(sharedwork, betaartifact, ".a"))); assert(has(link, strings.concat(sharedwork, "_beta_external.a"))); assert(!has(link, strings.concat(sharedwork, "_beta_same.a"))); }; }; }; }; ri += 1; }; referenceout = buildstdout; referenceerr = buildstderr; referencecommonwwi = strings.dup(readfile(strings.concat(sharedwork, "common.wwi"))); referencecommona = strings.dup(readfile(strings.concat(sharedwork, "common.a"))); referencealphaa = strings.dup(readfile(strings.concat(sharedwork, alphaartifact, ".a"))); ri = 0; for (ri < bins.len) { referencebins[ri] = strings.dup(readfile(bins[ri])); referenceroots[ri] = strings.dup(roots[ri]); ri += 1; }; } else { assert(same(referenceout, buildstdout)); assert(same(referenceerr, buildstderr)); assert(same(referencecommonwwi, readfile(strings.concat(sharedwork, "common.wwi")))); assert(same(referencecommona, readfile(strings.concat(sharedwork, "common.a")))); assert(same(referencealphaa, readfile(strings.concat(sharedwork, alphaartifact, ".a")))); ri = 0; for (ri < bins.len) { assert(same(referencebins[ri], readfile(bins[ri]))); assert(same(referenceroots[ri], roots[ri])); ri += 1; }; }; if (si == 2) { let wwtrace: str = readfile(wwcompilertrace); let once: []str = ["leaf", "common", alphaartifact, betaartifact, "gamma", "_alpha_same", "_alpha_external", "_beta_same", "_beta_external", "test"]; pi = 0; for (pi < once.len) { assert(occurrences(wwtrace, strings.concat("/", once[pi], ".unit.ww")) == 1); pi += 1; }; assert(occurrences(wwtrace, "/alpha.unit.ww") == 1); assert(occurrences(wwtrace, "/gamma.unit.ww") == 1); assert(occurrences(wwtrace, "external-production") == 0); ri = 0; for (ri < rootkeys.len) { assert(occurrences(wwtrace, strings.concat(rootkeys[ri], ".unit.ww")) == 1); assert(occurrences(wwtrace, strings.concat(mainkeys[ri], ".unit.ww")) == 1); ri += 1; }; assert(occurrences(readfile(wwlinkertrace), "\n") == 5); }; if (si + 1 < stages.len) { clean(sharedworkroot); ri = 0; for (ri < bins.len) { clean(bins[ri]); ri += 1; }; }; si += 1; }; clean(sharedworkroot); let bi: i32 = 0; for (bi < bins.len) { clean(bins[bi]); bi += 1; }; let warmroot: str = strings.concat(root, "/warm-c"); let warmwwroot: str = strings.concat(root, "/warm-ww"); assert(os.mkdir(warmroot, 448i32) == 0); assert(os.mkdir(warmwwroot, 448i32) == 0); let warmc: []str = [driver("ww"), "test", "-j", "1", "-w", warmroot, "-I", suite, "-I", root, strings.concat(suite, "/...")]; runcommandenv(root, "multidir-warm-cold", warmc, 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 warmparallel: []str = [driver("ww"), "test", "-j", "4", "-w", warmroot, "-I", suite, "-I", root, strings.concat(suite, "/...")]; let warmequivalentc: []str = [driver("ww"), "test", "-j", "4", "-w", warmroot, "-I", suite, "-I", root, strings.concat(suite, "/./...")]; runcommandenv(root, "multidir-warm-reuse", warmequivalentc, traceenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(warmstderr, out.stderr)); assert(same(warmtrace, readfile(compilertrace))); let warmkey: str = "/"; let warmwork: str = strings.concat(warmroot, warmkey); assert(os.exists(strings.concat(warmwork, "alpha-internal-test.unit.ww"))); assert(os.exists(strings.concat(warmwork, "beta_test-external-test.unit.ww"))); let warmww: []str = [wwtracedriver, "test", "-j", "4", "-w", warmwwroot, "-I", suite, "-I", root, strings.concat(suite, "/...")]; runcommandenv(root, "multidir-warm-ww", warmww, wwtraceenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(warmstdout, out.stdout)); assert(same(warmstderr, out.stderr)); let warmwwtrace: str = strings.dup(readfile(wwcompilertrace)); let warmwwequivalent: []str = [wwtracedriver, "test", "-j", "4", "-w", warmwwroot, "-I", suite, "-I", root, strings.concat(suite, "/./...")]; runcommandenv(root, "multidir-warm-ww-reuse", warmwwequivalent, wwtraceenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(warmstderr, out.stderr)); assert(same(warmwwtrace, readfile(wwcompilertrace))); let warmwwwork: str = strings.concat(warmwwroot, warmkey); assert(same(readfile(strings.concat(warmwork, "common.wwi")), readfile(strings.concat(warmwwwork, "common.wwi")))); assert(same(readfile(strings.concat(warmwork, "common.a")), readfile(strings.concat(warmwwwork, "common.a")))); assert(same(readfile(strings.concat(warmwork, "alpha-internal-test.unit.ww")), readfile(strings.concat(warmwwwork, "alpha-internal-test.unit.ww")))); // Change one shared dependency's public export in place. Each stage must // rebuild that canonical action and its one direct importer exactly once; // common's unchanged regenerated export stops propagation to gamma/alpha. let oldleafwwi: str = readfile(strings.concat(warmwork, "leaf.wwi")); let oldcommonwwi: str = readfile(strings.concat(warmwork, "common.wwi")); let oldcommona: str = readfile(strings.concat(warmwork, "common.a")); let oldgammaa: str = readfile(strings.concat(warmwork, "gamma.a")); assert(os.remove(strings.concat(leaf, "/leaf.ww")) == 0); writefile(strings.concat(leaf, "/leaf.ww"), strings.concat( "package leaf;\n// MULTIDIR_LEAF\n", "export fn value() i32 = { return 40; };\n", "export fn newly_visible() i32 = { return 2; };\n")); let cbeforechange: str = strings.dup(readfile(compilertrace)); let wbeforechange: str = strings.dup(readfile(wwcompilertrace)); runcommandenv(root, "multidir-warm-c-export-change", warmparallel, traceenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(warmstdout, out.stdout)); assert(same(warmstderr, out.stderr)); let cafterchange: str = strings.dup(readfile(compilertrace)); assert(occurrences(cafterchange, "/leaf.unit.new") == occurrences(cbeforechange, "/leaf.unit.new") + 1); assert(occurrences(cafterchange, "/common.unit.new") == occurrences(cbeforechange, "/common.unit.new") + 1); assert(occurrences(cafterchange, "/gamma.unit.new") == occurrences(cbeforechange, "/gamma.unit.new")); assert(occurrences(cafterchange, "/alpha-internal-test.unit.new") == occurrences(cbeforechange, "/alpha-internal-test.unit.new")); assert(!same(oldleafwwi, readfile(strings.concat(warmwork, "leaf.wwi")))); assert(same(oldcommonwwi, readfile(strings.concat(warmwork, "common.wwi")))); assert(same(oldcommona, readfile(strings.concat(warmwork, "common.a")))); assert(same(oldgammaa, readfile(strings.concat(warmwork, "gamma.a")))); let changedleafunit: str = readfile(strings.concat(warmwork, "leaf.unit.ww")); let changedleafwwi: str = readfile(strings.concat(warmwork, "leaf.wwi")); let changedleafa: str = readfile(strings.concat(warmwork, "leaf.a")); let changedcommonunit: str = readfile(strings.concat(warmwork, "common.unit.ww")); runcommandenv(root, "multidir-warm-ww-export-change", warmww, wwtraceenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(warmstdout, out.stdout)); assert(same(warmstderr, out.stderr)); let wafterchange: str = strings.dup(readfile(wwcompilertrace)); assert(occurrences(wafterchange, "/leaf.unit.new") == occurrences(wbeforechange, "/leaf.unit.new") + 1); assert(occurrences(wafterchange, "/common.unit.new") == occurrences(wbeforechange, "/common.unit.new") + 1); assert(occurrences(wafterchange, "/gamma.unit.new") == occurrences(wbeforechange, "/gamma.unit.new")); assert(occurrences(wafterchange, "/alpha-internal-test.unit.new") == occurrences(wbeforechange, "/alpha-internal-test.unit.new")); assert(same(changedleafunit, readfile(strings.concat(warmwwwork, "leaf.unit.ww")))); assert(same(changedleafwwi, readfile(strings.concat(warmwwwork, "leaf.wwi")))); assert(same(changedleafa, readfile(strings.concat(warmwwwork, "leaf.a")))); assert(same(changedcommonunit, readfile(strings.concat(warmwwwork, "common.unit.ww")))); assert(same(readfile(strings.concat(warmwork, "common.wwi")), readfile(strings.concat(warmwwwork, "common.wwi")))); assert(same(readfile(strings.concat(warmwork, "common.a")), readfile(strings.concat(warmwwwork, "common.a")))); // A second unchanged request in either stage performs no compilation. runcommandenv(root, "multidir-warm-c-export-reuse", warmparallel, traceenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(cafterchange, readfile(compilertrace))); runcommandenv(root, "multidir-warm-ww-export-reuse", warmww, wwtraceenv, (120i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(same(wafterchange, readfile(wwcompilertrace))); // Exercise the inverse discovery order explicitly. aaroot is selected and // loaded before zimporter, which later imports it; gamma above is first // discovered through beta and only later visited as a selected root. Both // orders must compile one production action, and a root-only build must emit // byte-identical owner unit, export, and archive. let aaroot: str = strings.concat(root, "/aaroot"); let zimporter: str = strings.concat(root, "/zimporter"); assert(os.mkdir(aaroot, 448i32) == 0); assert(os.mkdir(zimporter, 448i32) == 0); writefile(strings.concat(aaroot, "/aaroot.ww"), strings.concat( "package aaroot;\n// ROOT_FIRST_OWNER\n", "export fn value() i32 = { return 42; };\n")); writefile(strings.concat(zimporter, "/zimporter.ww"), strings.concat( "package zimporter;\nimport aaroot;\n", "export fn value() i32 = { return aaroot.value(); };\n")); let rootrefs: []str = ["", "", ""]; let rootstages: []str = ["ww", "ww_ww"]; let rsi: i32 = 0; for (rsi < rootstages.len) { let uniondriver: str = driver(rootstages[rsi]); if (rsi == 1) { uniondriver = wwtracedriver; }; let rootout: str = strings.concat(root, "/root-only-", rootstages[rsi], ".a"); let rootav: []str = [driver(rootstages[rsi]), "build", "-I", root, "-o", rootout, aaroot]; runcommand(root, strings.concat("root-only-", rootstages[rsi]), rootav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); let rootwork: str = strings.concat(rootout, ".sepwork/"); let depbin: str = strings.concat(root, "/root-first-", rootstages[rsi], "-dep.test"); let userbin: str = strings.concat(root, "/root-first-", rootstages[rsi], "-user.test"); let unionav: []str = [uniondriver, "test", "-c", "-I", root, "--ww-package-test", "production", "aaroot", aaroot, depbin, strings.concat(root, "/root-first-", rootstages[rsi], "-dep.status"), "--ww-package-test", "production", "zimporter", zimporter, userbin, strings.concat(root, "/root-first-", rootstages[rsi], "-user.status"), aaroot]; let beforetrace: str = ""; if (rsi == 0) { beforetrace = strings.dup(readfile(compilertrace)); } else { beforetrace = strings.dup(readfile(wwcompilertrace)); }; if (rsi == 0) { runcommandenv(root, "root-first-c", unionav, traceenv, (120i64 * (time.second: i64)): time.duration, &out); } else { runcommandenv(root, "root-first-ww", unionav, wwtraceenv, (120i64 * (time.second: i64)): time.duration, &out); }; expectexit(&out, 0); let aftertrace: str = ""; if (rsi == 0) { aftertrace = strings.dup(readfile(compilertrace)); } else { aftertrace = strings.dup(readfile(wwcompilertrace)); }; assert(occurrences(aftertrace, "/aaroot.unit.ww") == occurrences(beforetrace, "/aaroot.unit.ww") + 1); let unionwork: str = strings.concat(depbin, ".sepwork/"); let suffixes: []str = [".unit.ww", ".wwi", ".a"]; let xi: i32 = 0; for (xi < suffixes.len) { let rootbytes: str = readfile(strings.concat(rootwork, "aaroot", suffixes[xi])); assert(same(rootbytes, readfile(strings.concat(unionwork, "aaroot", suffixes[xi])))); if (rsi == 0) { rootrefs[xi] = strings.dup(rootbytes); } else { assert(same(rootrefs[xi], rootbytes)); }; xi += 1; }; rsi += 1; }; clean(wwtracebin); 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(named, "/test_test.test"), strings.concat(consumer, "/zconsumer.test"), strings.concat(consumer, "/zconsumer_test.test")]; let keys: []str = ["test-internal-test", "test_test-external-test", "zconsumer-internal-test", "zconsumer_test-external-test"]; let expected: []str = ["named_same_runs ... ok\n", "named_external_runs ... ok\n", "consumer_same_runs ... ok\n", "consumer_external_runs ... ok\n"]; 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) { let av: []str = [driver(stages[si]), "test", "-c", "-j", "1", "-I", suite, strings.concat(suite, "/...")]; 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); let namedprod: str = readfile(strings.concat(work, "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 = ["test", "__wwtest"]; 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 ")); let runav: []str = [bins[ri]]; runcommand(root, strings.concat("mixed-run-", stages[si], "-", keys[ri]), runav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, expected[ri])); if (si == 0) { referenceunits[ri] = strings.dup(unit); } else { assert(same(referenceunits[ri], unit)); }; ri += 1; }; if (si == 0) { let links: str = readfile(linktrace); assert(occurrences(links, "\n") == 4); let namedsame: str = linecontaining(links, strings.concat("-o ", bins[0], " ")); let namedexternal: str = linecontaining(links, strings.concat("-o ", bins[1], " ")); let consumersame: str = linecontaining(links, strings.concat("-o ", bins[2], " ")); let consumerexternal: str = linecontaining(links, strings.concat("-o ", bins[3], " ")); assert(has(namedsame, strings.concat(work, "__wwtest.a"))); assert(!has(namedsame, strings.concat(work, "test.a"))); assert(has(namedexternal, strings.concat(work, "test.a"))); assert(has(namedexternal, strings.concat(work, "__wwtest.a"))); assert(has(consumersame, strings.concat(work, "__wwtest.a"))); assert(!has(consumersame, strings.concat(work, "test.a"))); assert(has(consumerexternal, strings.concat(work, "__wwtest.a"))); assert(!has(consumerexternal, 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) { roota = strings.concat(roota, "import ", boundarypkgname(i), ";\n"); i += 1; }; let rootz: str = strings.concat("package main;\nimport p150;\n", "// ROOT_Z\nfn main() i32 = { return p000.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 + 1); assert(occurrences(ltrace, "\n") == 1); let rootline: str = linecontaining(ctrace, strings.concat("/", rootaction, ".unit.new")); assert(occurrences(rootline, "<--import>") == packagecount); let expectedroot: str = "BEGIN<--entry><-c>"; i = 0; for (i < packagecount) { let name: str = boundarypkgname(i); expectedroot = strings.concat(expectedroot, "<--import><", name, "><", works[si], "/", name, ".wwi>"); assert(occurrences(rootline, strings.concat("<", name, "><", works[si], "/", name, ".wwi>")) == 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 = "BEGIN<-c>"; if (i + 1 < packagecount) { let next: str = boundarypkgname(i + 1); expectedcompile = strings.concat(expectedcompile, "<--import><", next, "><", works[si], "/", next, ".wwi>"); }; 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>")) == 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"); assert(same(readfile(strings.concat(works[si], "/", name, ".unit.ww")), strings.concat("//ww:module-reset ", name, "\n", a, "\n//ww:module-reset ", name, "\n", z, "\n"))); 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>")) == 1); assert(!has(ltrace, ".wwi>")); let expectedlink: str = strings.concat("BEGIN<-o><", bins[si], "><", works[si], "/", rootaction, ".a>"); i = 0; for (i < packagecount) { expectedlink = strings.concat(expectedlink, "<", works[si], "/", boundarypkgname(i), ".a>"); i += 1; }; expectedlink = strings.concat(expectedlink, "<", repo(), "/out/bin/../lib/libwwrt.a>\n"); assert(same(ltrace, expectedlink)); assert(same(readfile(strings.concat(works[si], "/", rootaction, ".unit.ww")), strings.concat("//ww:module-reset target\n", roota, "\n//ww:module-reset target\n", rootz, "\n"))); 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)); // 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") == 1); assert(occurrences(ldelta, "\n") == 1); assert(same(cdelta, strings.concat("BEGIN<--entry><-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"))); let expectedchainlink: str = strings.concat("BEGIN<-o><", chainbins[si], "><", works[si], "/", chainaction, ".a>"); 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>")); assert(same(readfile(strings.concat(works[si], "/", chainaction, ".unit.ww")), strings.concat("//ww:module-reset chain\n", chainsource, "\n"))); 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 directorycount: i32 = 52; let aliascount: i32 = 205; let totaldirectorycount: i32 = directorycount + aliascount; let productcount: i32 = directorycount * 2 + aliascount; // Each selected directory contributes production, internal, external, and // two generated-main actions. The shared test support action and its nine // ordinary dependencies are command-global actions too. let directactioncount: i32 = directorycount * 5 + 10; let coordinatoractioncount: i32 = directactioncount + aliascount * 2; let i: i32 = 0; for (i < directorycount) { let name: str = boundarypkgname(i); let dir: str = strings.concat(suite, "/", name); assert(os.mkdir(dir, 448i32) == 0); writefile(strings.concat(dir, "/a.ww"), strings.concat( "package ", name, ";\n// DYNAMIC_PRODUCTION_", name, "\nexport fn value() i32 = { return 42; };\n")); 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; }; // Production-only directories make the recursive coordinator request own // 257 resolution contexts and 309 products. Each contributes a production // action plus its generated production test-main action. for (i < totaldirectorycount) { let name: str = boundarypkgname(i); let dir: str = strings.concat(suite, "/", name); assert(os.mkdir(dir, 448i32) == 0); writefile(strings.concat(dir, "/a.ww"), strings.concat( "package ", name, ";\n// DYNAMIC_PRODUCTION_ONLY_", name, "\nexport fn value() i32 = { return 42; };\n")); i += 1; }; i = 0; for (i < aliascount) { let alias: str = strings.concat(suite, "/alias-", boundarypkgname(i)); assert(os.symlink(strings.concat(suite, "/p000"), alias) == 0); i += 1; }; let compilerwrapper: str = strings.concat(root, "/universe-w6c.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(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 workroots: []str = [strings.concat(root, "/universe-c-workroot"), strings.concat(root, "/universe-ww-workroot")]; let works: []str = [workroots[0], workroots[1]]; let compilertraces: []str = [strings.concat(root, "/universe-c-compiler"), strings.concat(root, "/universe-ww-compiler")]; let linkertraces: []str = [strings.concat(root, "/universe-c-linker"), strings.concat(root, "/universe-ww-linker")]; let buildertraces: []str = [strings.concat(root, "/universe-c-builder"), strings.concat(root, "/universe-ww-builder")]; let baseenv: []str = os.getenvs(); let coordinatoroutputs: []str = ["", ""]; let out: commandout; let si: i32 = 0; for (si < stages.len) { assert(os.mkdir(workroots[si], 448i32) == 0); writefile(compilertraces[si], ""); writefile(linkertraces[si], ""); writefile(buildertraces[si], ""); let env: []str = alloc([], (baseenv.len + 14): 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_LINKER_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_UNIVERSE_W6C=") && !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=", driver(assemblers[si]))); append(env, strings.concat("WW_W6L=", linkerwrapper)); append(env, strings.concat("WW_UNIVERSE_COMPILER_TRACE=", compilertraces[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_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 * 6): 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 < directorycount) { let name: str = boundarypkgname(i); let dir: str = strings.concat(suite, "/", name); append(av, "--ww-package-test"); append(av, "same"); append(av, name); append(av, dir); append(av, strings.concat(root, "/c-same-", name)); append(av, strings.concat(root, "/c-same-", name, ".status")); append(av, "--ww-package-test"); append(av, "external"); append(av, strings.concat(name, "_test")); append(av, dir); append(av, strings.concat(root, "/c-external-", name)); append(av, strings.concat(root, "/c-external-", name, ".status")); i += 1; }; i = 0; for (i < aliascount) { let aname: str = boundarypkgname(i); let alias: str = strings.concat(suite, "/alias-", aname); append(av, "--ww-package-test"); append(av, "same"); append(av, "p000"); append(av, alias); append(av, strings.concat(root, "/c-alias-", aname)); append(av, strings.concat(root, "/c-alias-", aname, ".status")); i += 1; }; } else { // Reverse both the product groups and variant order. The driver must // derive the same canonical action universe independent of request order. i = aliascount - 1; for (i >= 0) { let aname: str = boundarypkgname(i); let alias: str = strings.concat(suite, "/alias-", aname); append(av, "--ww-package-test"); append(av, "same"); append(av, "p000"); append(av, alias); append(av, strings.concat(root, "/ww-alias-", aname)); append(av, strings.concat(root, "/ww-alias-", aname, ".status")); i -= 1; }; i = directorycount - 1; for (i >= 0) { let name: str = boundarypkgname(i); let dir: str = strings.concat(suite, "/", name); append(av, "--ww-package-test"); append(av, "external"); append(av, strings.concat(name, "_test")); append(av, dir); append(av, strings.concat(root, "/ww-external-", name)); append(av, strings.concat(root, "/ww-external-", name, ".status")); append(av, "--ww-package-test"); append(av, "same"); append(av, name); append(av, dir); append(av, strings.concat(root, "/ww-same-", name)); append(av, strings.concat(root, "/ww-same-", name, ".status")); i -= 1; }; }; append(av, strings.concat(suite, "/p000")); assert(av.len == 8 + productcount * 6); runcommandenv(root, strings.concat("dynamic-universe-", stages[si]), av, env, (600i64 * (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 ltrace: str = readfile(linkertraces[si]); assert(occurrences(ctrace, "\n") == directactioncount); assert(occurrences(ltrace, "\n") == productcount); assert(!has(ltrace, ".wwi")); assert(occurrences(ctrace, "/test.unit.new") == 1); i = 0; for (i < directorycount) { let name: str = boundarypkgname(i); let internal: str = strings.concat(name, "-internal-test"); let external: str = strings.concat(name, "_test-external-test"); let internalmain: str = strings.concat(internal, "-main"); let externalmain: str = strings.concat(external, "-main"); assert(occurrences(ctrace, strings.concat("/", name, ".unit.new")) == 1); assert(occurrences(ctrace, strings.concat("/", internal, ".unit.new")) == 1); assert(occurrences(ctrace, strings.concat("/", external, ".unit.new")) == 1); assert(occurrences(ctrace, strings.concat("/", internalmain, ".unit.new")) == 1); assert(occurrences(ctrace, strings.concat("/", externalmain, ".unit.new")) == 1); let actions: []str = [name, internal, external, internalmain, externalmain]; let ai: i32 = 0; for (ai < actions.len) { assert(os.exists(strings.concat(works[si], "/", actions[ai], ".unit.ww"))); assert(os.exists(strings.concat(works[si], "/", actions[ai], ".wwi"))); assert(os.exists(strings.concat(works[si], "/", actions[ai], ".s"))); assert(os.exists(strings.concat(works[si], "/", actions[ai], ".o"))); assert(os.exists(strings.concat(works[si], "/", actions[ai], ".a"))); ai += 1; }; let prefix: str = "c"; if (si == 1) { prefix = "ww"; }; assert(os.exists(strings.concat(root, "/", prefix, "-same-", name))); assert(os.exists(strings.concat(root, "/", prefix, "-external-", name))); i += 1; }; i = 0; for (i < aliascount) { let name: str = boundarypkgname(i); let prefix: str = "c"; if (si == 1) { prefix = "ww"; }; assert(os.exists(strings.concat(root, "/", prefix, "-alias-", name))); i += 1; }; let prefix: str = "c"; if (si == 1) { prefix = "ww"; }; let samples: []str = [strings.concat(root, "/", prefix, "-same-p051"), strings.concat(root, "/", prefix, "-external-p051"), strings.concat(root, "/", prefix, "-alias-p204")]; i = 0; for (i < samples.len) { let runav: []str = [samples[i]]; runcommand(root, strings.concat("dynamic-run-", stages[si], "-", boundarypkgname(i)), runav, (60i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(has(out.stdout, "_value ... ok\n")); i += 1; }; // Exercise the public coordinator path with the same persistent package // universe. It must construct and pass all 309 descriptors in one argv, // retain the 270 existing actions, and add exactly the 410 actions owned by // the 205 production-only directories. let cbefore: i32 = readfile(compilertraces[si]).len; let lbefore: i32 = readfile(linkertraces[si]).len; let recursive: str = strings.concat(suite, "/..."); let coordinatorav: []str = [driver(stages[si]), "test", "-j", "1", "-w", workroots[si], "-I", suite, recursive]; 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); let cafter: str = readfile(compilertraces[si]); let lafter: str = readfile(linkertraces[si]); assert(occurrences(cafter, "\n") == coordinatoractioncount); assert(occurrences(strings.sub(cafter, cbefore, cafter.len), "\n") == aliascount * 2); assert(occurrences(strings.sub(lafter, lbefore, lafter.len), "\n") == productcount); assert(!has(strings.sub(lafter, lbefore, lafter.len), ".wwi")); assert(same(readfile(buildertraces[si]), "309\n")); assert(occurrences(out.stdout, "ok ") == directorycount * 2); assert(occurrences(out.stdout, "? ") == aliascount); i = 0; for (i < directorycount) { let name: str = boundarypkgname(i); assert(has(out.stdout, strings.concat("ok ", suite, "/", name, " [", name, ", same-package]\n"))); assert(has(out.stdout, strings.concat("ok ", suite, "/", name, " [", name, "_test, external]\n"))); i += 1; }; i = directorycount; for (i < totaldirectorycount) { let name: str = boundarypkgname(i); assert(has(out.stdout, strings.concat("? ", suite, "/", name, " [no tests]\n"))); i += 1; }; // Repeating the public request may relink products, but every canonical // action in the >256 universe must be warm. let warmcompilerlen: i32 = cafter.len; runcommandenv(root, strings.concat("dynamic-coordinator-warm-", stages[si]), coordinatorav, env, (1200i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 0); assert(out.stderr.len == 0); assert(same(out.stdout, coordinatoroutputs[si])); assert(readfile(compilertraces[si]).len == warmcompilerlen); assert(same(readfile(buildertraces[si]), "309\n309\n")); si += 1; }; assert(same(coordinatoroutputs[0], coordinatoroutputs[1])); let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"]; i = 0; for (i < directorycount) { let name: str = boundarypkgname(i); let actions: []str = [name, strings.concat(name, "-internal-test"), strings.concat(name, "_test-external-test"), strings.concat(name, "-internal-test-main"), strings.concat(name, "_test-external-test-main")]; let ai: i32 = 0; for (ai < actions.len) { let xi: i32 = 0; for (xi < suffixes.len) { assert(same(readfile(strings.concat(works[0], "/", actions[ai], suffixes[xi])), readfile(strings.concat(works[1], "/", actions[ai], suffixes[xi])))); xi += 1; }; ai += 1; }; i += 1; }; i = directorycount; for (i < totaldirectorycount) { let name: str = boundarypkgname(i); let actions: []str = [name, strings.concat(name, "-main")]; let ai: i32 = 0; for (ai < actions.len) { let xi: i32 = 0; for (xi < suffixes.len) { assert(same(readfile(strings.concat(works[0], "/", actions[ai], suffixes[xi])), readfile(strings.concat(works[1], "/", actions[ai], suffixes[xi])))); xi += 1; }; ai += 1; }; i += 1; }; let supportactions: []str = ["ascii", "bytes", "encoding.utf8", "fnmatch", "os", "rt", "strings", "test", "time", "types"]; let ai: i32 = 0; for (ai < supportactions.len) { i = 0; for (i < suffixes.len) { assert(same(readfile(strings.concat(works[0], "/", supportactions[ai], suffixes[i])), readfile(strings.concat(works[1], "/", supportactions[ai], suffixes[i])))); i += 1; }; ai += 1; }; assert(same(readfile(strings.concat(root, "/c-same-p051")), readfile(strings.concat(root, "/ww-same-p051")))); assert(same(readfile(strings.concat(root, "/c-external-p051")), readfile(strings.concat(root, "/ww-external-p051")))); assert(same(readfile(strings.concat(root, "/c-alias-p204")), readfile(strings.concat(root, "/ww-alias-p204")))); let internalunit: str = readfile(strings.concat(works[0], "/p051-internal-test.unit.ww")); let externalunit: str = readfile(strings.concat(works[0], "/p051_test-external-test.unit.ww")); let productionunit: str = readfile(strings.concat(works[0], "/p051.unit.ww")); assert(has(internalunit, "DYNAMIC_PRODUCTION_p051")); assert(has(internalunit, "DYNAMIC_INTERNAL_p051")); assert(!has(internalunit, "DYNAMIC_EXTERNAL_p051")); assert(has(externalunit, "DYNAMIC_EXTERNAL_p051")); assert(!has(externalunit, "DYNAMIC_PRODUCTION_p051")); assert(has(productionunit, "DYNAMIC_PRODUCTION_p051")); assert(!has(productionunit, "DYNAMIC_INTERNAL_p051")); assert(!has(productionunit, "DYNAMIC_EXTERNAL_p051")); 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(has(out.stdout, "alpha_same_survives ... ok\n")); assert(has(out.stdout, "beta_same_survives ... ok\n")); assert(has(out.stdout, "beta_external_survives ... ok\n")); assert(has(out.stdout, strings.concat("ok ", alpha, " [alpha, same-package]\n"))); assert(has(out.stdout, strings.concat("ok ", beta, " [beta, same-package]\n"))); assert(has(out.stdout, strings.concat("ok ", beta, " [beta_test, external]\n"))); assert(!has(out.stdout, "alpha_external_fails ...")); assert(has(out.stderr, "has no exported declaration 'hidden'")); assert(has(out.stderr, strings.concat("FAIL ", alpha, " [alpha_test] (build exit 1)\n"))); assert(!has(out.stderr, strings.concat("FAIL ", alpha, " [alpha]"))); assert(!has(out.stderr, strings.concat("FAIL ", beta, " ["))); let alphapos: i32 = pos(out.stdout, strings.concat("ok ", alpha, " [alpha, same-package]\n")); let betapos: i32 = pos(out.stdout, strings.concat("ok ", beta, " [beta, same-package]\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); }; // 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])); // A first persistent union pass may contain a root-local compiler failure. // Its successfully committed actions still become reusable: only the // uncommitted failing root is compiled again on the next request in both // driver stages. 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") == 1); assert(occurrences(secondtrace, "/badexternal_test-external-test.unit.new") == 2); 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") == 1); assert(occurrences(wwsecond, "/badexternal_test-external-test.unit.new") == 2); clean(wwtracebin); 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", "-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", "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([], 12u64)!; ri = 0; for (ri < 12) { 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"); assert(same(readfile(strings.concat(works[si], "/domain.internal.secret.unit.ww")), wantsecret)); 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") == 3); assert(occurrences(ltrace, "\n") == 1); 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-internal-test-main", "domain.testclient_test-external-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")); let samemainline: str = linecontaining(testcompiletrace, "domain.testclient-internal-test-main.unit"); let externalmainline: str = linecontaining(testcompiletrace, "domain.testclient_test-external-test-main.unit"); assert(!has(samemainline, "domain.internal.secret.wwi")); assert(!has(externalmainline, "domain.internal.secret.wwi")); assert(occurrences(testcompiletrace, "--test-package") == 2); assert(occurrences(testcompiletrace, "<-T><--entry><--test-support-module>") == 2); 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", "production", "main", client, strings.concat(root, "/order-client-", stages[si]), strings.concat(root, "/order-client-", stages[si], ".status"), "--ww-package-test", "production", "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", "production", "main", outsider, strings.concat(root, "/order-outsider-", stages[si]), strings.concat(root, "/order-outsider-", stages[si], ".status"), "--ww-package-test", "production", "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", "import ", baseidentity, ";\n", "export fn value() 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, "/left.ww"), leftbody); 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"), 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(same(unit, wantunits[ui])); 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, strings.concat("\npackage paritycommand", ";\n"))); assert(!has(exportf, "\npackage main;\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<-c><-I><", work, artifacts[0], ".wwi><-o><", work, artifacts[0], ".s><", work, artifacts[0], ".unit.ww>"); let leftline: str = strings.concat("BEGIN<-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<-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><-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"))); // The primary command declaration is accepted only when the compiler // receives command classification from --entry. Imported interfaces and // ordinary package compiles retain strict identity-leaf validation. 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", "-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, 1); assert(has(out.stderr, "does not match import path")); assert(has(out.stderr, rootidentity)); 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") == 4); 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; }; assert(occurrences(ltrace, "\n") == 1); assert(has(ltrace, strings.concat("BEGIN<-o><", bin, "><", 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-test", "production", "paritybase", base, combinedbase, combinedbasestatus, "--ww-package-test", "production", "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(cmdtool.value() == 41); };\n")); writefile(strings.concat(importer, "/importer.ww"), strings.concat( "package importer;\n", "import ", commandid, ";\n", "export fn value() i32 = { return cmdtool.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 internalmainid: str = strings.concat("__wwtestmain.", commandid, ".internal.main"); let externalmainid: str = strings.concat("__wwtestmain.", externalid, ".external.main"); let actionids: []str = [commandid, internalid, externalid, internalmainid, externalmainid]; let artifacts: []str = [packagestoragekey(commandid, target, 0, 0), packagestoragekey(internalid, target, 1, 0), packagestoragekey(externalid, target, 2, 0), packagestoragekey(internalmainid, strings.concat(target, "#internal-test-main"), 3, 2), packagestoragekey(externalmainid, strings.concat(target, "#external-test-main"), 3, 2)]; let suffixes: []str = [".unit.ww", ".wwi", ".a"]; let reference: []str = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", ""]; let samebin: str = strings.concat(target, "/main.test"); let externalbin: str = strings.concat(target, "/main_test.test"); 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_test.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 < 3) { 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[2], ".unit.new")); assert(has(externalline, strings.concat("<--import><", commandid, "><", work, artifacts[0], ".wwi>"))); let internalmainline: str = linecontaining(ctrace, strings.concat(artifacts[3], ".unit.new")); let externalmainline: str = linecontaining(ctrace, strings.concat(artifacts[4], ".unit.new")); assert(has(internalmainline, strings.concat("<--import><", internalid, "><", work, artifacts[1], ".wwi>"))); assert(has(externalmainline, strings.concat("<--import><", externalid, "><", work, artifacts[2], ".wwi>"))); assert(occurrences(ctrace, strings.concat("<", work, artifacts[0], ".unit.new>")) == 1); assert(has(readfile(strings.concat(work, artifacts[0], ".s")), strings.concat(commandid, ".value"))); assert(has(readfile(strings.concat(work, artifacts[1], ".s")), strings.concat(commandid, ".command_internal"))); assert(has(readfile(strings.concat(work, artifacts[2], ".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_test.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 before leaf validation 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); clean(samebin); clean(externalbin); 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"); 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); 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")); 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 linkerwrapper: str = strings.concat(root, "/identity-w6l.sh"); writefile(tooltrace, ""); writeexecutable(compilerwrapper, strings.concat( "#!/bin/sh\nprintf 'compile\\n' >> \"$WW_IDENTITY_TRACE\"\n", "exec \"$WW_IDENTITY_W6C\" \"$@\"\n")); writeexecutable(linkerwrapper, strings.concat( "#!/bin/sh\nprintf 'link\\n' >> \"$WW_IDENTITY_TRACE\"\n", "exec \"$WW_IDENTITY_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_IDENTITY_TRACE=") && !strings.hasprefix(baseenv[ei], "WW_IDENTITY_W6C=") && !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_W6L=", linkerwrapper)); append(traceenv, strings.concat("WW_IDENTITY_TRACE=", tooltrace)); append(traceenv, strings.concat("WW_IDENTITY_W6C=", driver("w6c"))); append(traceenv, strings.concat("WW_IDENTITY_W6L=", driver("w6l"))); 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 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); }; // 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") == 3); assert(occurrences(commandltrace, "\n") == 2); let commandalphacompile: str = linecontaining(commandctrace, strings.concat(commandalphaid, ".unit.new")); assert(occurrences(commandalphacompile, strings.concat( "<--import><", commandwork, "library.wwi>")) == 1); assert(!has(commandalphacompile, "cmd-zeta.wwi")); let commandalphalink: str = linecontaining(commandltrace, strings.concat("<-o><", commandbins[commandstage], "/cmd-alpha>")); let commandzetalink: str = linecontaining(commandltrace, strings.concat("<-o><", commandbins[commandstage], "/cmd-zeta>")); assert(occurrences(commandalphalink, strings.concat("<", commandwork, commandalphaid, ".a>")) == 1); assert(occurrences(commandalphalink, strings.concat("<", commandwork, "library.a>")) == 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>"))); 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, same-package]\n"))); assert(has(seq, strings.concat("ok ", tree, "/alpha [alpha_test, external]\n"))); assert(has(seq, strings.concat("ok ", tree, "/app [app, same-package]\n"))); assert(has(seq, strings.concat("ok ", tree, "/beta/inner [inner, same-package]\n"))); assert(has(seq, strings.concat("ok ", tree, "/domain/app [app, same-package]\n"))); assert(has(seq, strings.concat("? ", tree, "/gamma [no tests]\n"))); assert(has(seq, strings.concat("? ", tree, "/vendor [no tests]\n"))); assert(has(seq, strings.concat("? ", tree, "/vendorx [no tests]\n"))); let rootreport: i32 = pos(seq, strings.concat("ok ", tree, " [rootpkg, same-package]\n")); let alphareport: i32 = pos(seq, strings.concat("ok ", tree, "/alpha [alpha_test, external]\n")); let appreport: i32 = pos(seq, strings.concat("ok ", tree, "/app [app, same-package]\n")); let innerreport: i32 = pos(seq, strings.concat("ok ", tree, "/beta/inner [inner, same-package]\n")); let domainreport: i32 = pos(seq, strings.concat("ok ", tree, "/domain/app [app, same-package]\n")); let gammareport: i32 = pos(seq, strings.concat("? ", tree, "/gamma [no tests]\n")); let vendorreport: i32 = pos(seq, strings.concat("? ", tree, "/vendor [no tests]\n")); let vendorxreport: i32 = pos(seq, strings.concat("? ", tree, "/vendorx [no tests]\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_test, external]\n")) == 1); assert(occurrences(seq, strings.concat("ok ", tree, "/app [app, same-package]\n")) == 1); assert(occurrences(seq, strings.concat("ok ", tree, "/beta/inner [inner, same-package]\n")) == 1); assert(occurrences(seq, strings.concat("ok ", tree, "/domain/app [app, same-package]\n")) == 1); assert(occurrences(seq, strings.concat("? ", tree, "/gamma [no tests]\n")) == 1); assert(occurrences(seq, strings.concat("? ", tree, "/vendor [no tests]\n")) == 1); assert(occurrences(seq, strings.concat("? ", tree, "/vendorx [no tests]\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_test, external]\n"))); assert(!has(outc.stdout, strings.concat("ok ", tree, " [rootpkg, same-package]\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, same-package]\n"))); assert(has(outc.stdout, strings.concat("ok ", tree, "/_skip [_skip, same-package]\n"))); assert(has(outc.stdout, strings.concat("ok ", tree, "/testdata/bad [bad, same-package]\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 tests]\n"))); assert(has(outc.stdout, strings.concat("ok ", tree, "/vendor/bad [bad, same-package]\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 tests]\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 tests]\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, same-package]\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 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()")); 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, "-I", tree, spec]; let wcw: []str = [driver("ww_ww"), "test", "-c", "-w", wdroot, "-I", tree, 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"); 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, same-package]\n"))); assert(has(cold, strings.concat("ok ", b, " [foo, same-package]\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, same-package]\n"))); assert(has(out.stdout, strings.concat("ok ", b, " [foo, same-package]\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-test", "production", "foo", localleft, leftout, strings.concat(root, "/local-", stages[si], "-left.status"), "--ww-package-test", "production", "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); }; // 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 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, 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"); 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 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 av: []str = [driver(stages[si]), "build", "-w", works[si], "-I", source, "-o", bins[si], client]; let out: commandout; 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"); assert(same(readfile(strings.concat(works[si], "/", expanded, ".unit.ww")), wantnear)); 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") == 3); assert(occurrences(ltrace, "\n") == 1); 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>")) == 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>")) == 1); assert(occurrences(ltrace, strings.concat("<", works[si], "/", expanded, ".a>")) == 1); assert(occurrences(ltrace, strings.concat("<", works[si], "/lib.leaf.a>")) == 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 = [0, 0, 0, 0, 0, 0, 0]; 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([], (8 + combinedtargets.len * 6): u64)!; append(combinedav, driver(stages[si])); append(combinedav, "test"); append(combinedav, "-c"); append(combinedav, "-w"); append(combinedav, combinedwork); append(combinedav, "-I"); append(combinedav, source); let ui: i32 = 0; for (ui < combinedtargets.len) { append(combinedav, "--ww-package-test"); append(combinedav, "production"); append(combinedav, combinedpackages[ui]); append(combinedav, combinedtargets[ui]); append(combinedav, combinedoutputs[ui]); 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 < combinedoutputs.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; }; // 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-test", "production", "main", client, orderallowed, orderallowedstatus, "--ww-package-test", "production", "main", outsider, orderforbidden, orderforbiddenstatus, client]; let orderreverse: []str = [driver(stages[si]), "test", "-c", "-w", orderwork, "-I", source, "--ww-package-test", "production", "main", outsider, orderforbidden, orderforbiddenstatus, "--ww-package-test", "production", "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>")) == 1); assert(occurrences(externalline, strings.concat("<--import><", expanded, "><", testwork, expanded, ".wwi>")) == 1); assert(occurrences(internalline, strings.concat( "<--import-map><", expanded, ">")) == 1); assert(occurrences(externalline, strings.concat( "<--import-map><", expanded, ">")) == 1); assert(!has(internalline, "lib.leaf.wwi")); assert(!has(externalline, "lib.leaf.wwi")); let internalmainline: str = linecontaining(testtrace, "domain.app.testclient-internal-test-main.unit.new"); let externalmainline: str = linecontaining(testtrace, "domain.app.testclient_test-external-test-main.unit.new"); assert(!has(internalmainline, expanded)); assert(!has(externalmainline, expanded)); assert(!has(readfile(linkertraces[si]), ".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); // 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-internal-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 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", "-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"); }; 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", "-I", root, "-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); };