5087 lines
209 KiB
Plaintext
5087 lines
209 KiB
Plaintext
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 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 requestworkkey(dir: str) str = {
|
|
let escaped: str = strings.concat("d_", workescape(dir));
|
|
if (escaped.len <= 255) { return escaped; };
|
|
let state: sha256.state = sha256.sha256();
|
|
let h: *hash.hash = (&state): *hash.hash;
|
|
hash.write(h, strings.toutf8("ww-request-workdir-v1:"));
|
|
hash.write(h, strings.toutf8(dir));
|
|
return strings.concat("d_", digesthex(h));
|
|
};
|
|
|
|
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");
|
|
};
|
|
};
|
|
|
|
@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 <package>_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 <package>_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 = strings.concat(work, "/", requestworkkey(real));
|
|
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, "/__same.ww"), strings.concat(
|
|
"package __same;\n",
|
|
"export fn value() i32 = { return 1; };\n"));
|
|
writefile(strings.concat(externalonly, "/__external.ww"), strings.concat(
|
|
"package __external;\n",
|
|
"export fn value() i32 = { return 1; };\n"));
|
|
writefile(strings.concat(pkg, "/a.ww"), strings.concat(
|
|
"package pkg;\nimport api;\n",
|
|
"// PACKAGE_PRODUCTION_A\n",
|
|
"export fn value() i32 = { return api.value(); };\n"));
|
|
writefile(strings.concat(pkg, "/z.ww"), strings.concat(
|
|
"package pkg;\n",
|
|
"// PACKAGE_PRODUCTION_Z\n",
|
|
"fn secret() i32 = { return 6; };\n",
|
|
"export fn second() i32 = { return 9; };\n"));
|
|
writefile(strings.concat(pkg, "/same_test.ww"), strings.concat(
|
|
"package pkg;\nimport __same;\n",
|
|
"// SAME_TEST_SOURCE\n",
|
|
"@test fn same_graph() void = {\n",
|
|
" assert(value() + __same.value() == 42);\n",
|
|
" assert(secret() == 6);\n};\n"));
|
|
writefile(strings.concat(pkg, "/external_test.ww"), strings.concat(
|
|
"package pkg_test;\nimport __external;\nimport pkg;\n",
|
|
"// EXTERNAL_TEST_SOURCE\n",
|
|
"@test fn external_graph() void = {\n",
|
|
" assert(pkg.value() + __external.value() == 42);\n",
|
|
" assert(pkg.second() == 9);\n};\n"));
|
|
|
|
let prodout: str = strings.concat(root, "/production.a");
|
|
let prodav: []str = [driver("ww"), "build", "-p", "-I", root,
|
|
"-o", prodout, pkg];
|
|
let out: commandout;
|
|
runcommand(root, "production-build", prodav,
|
|
(60i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 0);
|
|
let prodwork: str = strings.concat(prodout, ".sepwork/");
|
|
let produnit: str = readfile(strings.concat(prodwork,
|
|
"pkg.unit.ww"));
|
|
assert(has(produnit, "//ww:module-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, "/d_", workescape(pkg), "/");
|
|
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, "/_alpha_same.ww"),
|
|
"package _alpha_same;\nexport fn value() i32 = { return 1; };\n");
|
|
writefile(strings.concat(alphaexternal, "/_alpha_external.ww"),
|
|
"package _alpha_external;\nexport fn value() i32 = { return 1; };\n");
|
|
writefile(strings.concat(betasame, "/_beta_same.ww"),
|
|
"package _beta_same;\nexport fn value() i32 = { return 2; };\n");
|
|
writefile(strings.concat(betaexternal, "/_beta_external.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, "/_alpha_same.ww"),
|
|
"package _alpha_same;\nexport fn value() i32 = { return 99; };\n");
|
|
writefile(strings.concat(betashadow, "/_beta_same.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 = strings.concat("/d_", workescape(suite), "/");
|
|
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", "-p",
|
|
"-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", root, 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><p000><",
|
|
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 = [strings.concat(workroots[0], "/", requestworkkey(suite)),
|
|
strings.concat(workroots[1], "/", requestworkkey(suite))];
|
|
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);
|
|
assert(os.mkdir(works[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 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", "-p", "-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, "/", requestworkkey(target),
|
|
"/");
|
|
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", "-p", "-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", "-p", "-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", "-p", "-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 ./... parity: a trailing "..." element walks the tree, one package
|
|
// run per test-bearing directory, dot- and underscore-prefixed directory
|
|
// names excluded. The excluded sentinels hold failing tests so a wrong
|
|
// descent turns the whole run red rather than passing silently.
|
|
@test fn recursive_tree_discovery() void = {
|
|
let root: str = fresh();
|
|
let tree: str = strings.concat(root, "/tree");
|
|
assert(os.mkdir(tree, 448i32) == 0);
|
|
assert(os.mkdir(strings.concat(tree, "/alpha"), 448i32) == 0);
|
|
assert(os.mkdir(strings.concat(tree, "/beta"), 448i32) == 0);
|
|
assert(os.mkdir(strings.concat(tree, "/beta/inner"), 448i32) == 0);
|
|
assert(os.mkdir(strings.concat(tree, "/gamma"), 448i32) == 0);
|
|
assert(os.mkdir(strings.concat(tree, "/.hidden"), 448i32) == 0);
|
|
assert(os.mkdir(strings.concat(tree, "/_skip"), 448i32) == 0);
|
|
writefile(strings.concat(tree, "/a.ww"),
|
|
"package rootpkg;\nfn first() i32 = { return 1; };\n");
|
|
writefile(strings.concat(tree, "/root_test.ww"), strings.concat(
|
|
"package rootpkg;\n",
|
|
"@test fn treeroot() void = { assert(first() + last() == 3); };\n"));
|
|
writefile(strings.concat(tree, "/z.ww"),
|
|
"package rootpkg;\nfn last() i32 = { return 2; };\n");
|
|
writefile(strings.concat(tree, "/alpha/alpha.ww"),
|
|
"package alpha;\nexport fn value() int = { return 7; };\n");
|
|
writefile(strings.concat(tree, "/alpha/alpha_test.ww"),
|
|
"package alpha_test;\nimport alpha;\n@test fn treealpha() void = { assert(alpha.value() == 7); };\n");
|
|
writefile(strings.concat(tree, "/beta/inner/inner_test.ww"),
|
|
"package inner;\n@test fn treeinner() void = { assert(1 == 1); };\n");
|
|
writefile(strings.concat(tree, "/gamma/gamma.ww"),
|
|
"package gamma;\nexport fn g() int = { return 1; };\n");
|
|
writefile(strings.concat(tree, "/.hidden/hid_test.ww"),
|
|
"package hid;\n@test fn hidden() void = { assert(false); };\n");
|
|
writefile(strings.concat(tree, "/_skip/skip_test.ww"),
|
|
"package skip;\n@test fn skipped() void = { assert(false); };\n");
|
|
|
|
let spec: str = strings.concat(tree, "/...");
|
|
let outc: commandout;
|
|
let outw: commandout;
|
|
let treec: []str = [driver("ww"), "test", spec];
|
|
let treew: []str = [driver("ww_ww"), "test", spec];
|
|
runcommand(root, "tree-c", treec,
|
|
(30i64 * (time.second: i64)): time.duration, &outc);
|
|
runcommand(root, "tree-ww", treew,
|
|
(30i64 * (time.second: i64)): time.duration, &outw);
|
|
expectexit(&outc, 0);
|
|
expectexit(&outw, 0);
|
|
assert(same(outc.stdout, outw.stdout));
|
|
assert(same(outc.stderr, outw.stderr));
|
|
assert(has(outc.stdout, strings.concat("ok ", tree,
|
|
" [rootpkg, same-package]\n")));
|
|
assert(has(outc.stdout, strings.concat("ok ", tree,
|
|
"/alpha [alpha_test, external]\n")));
|
|
assert(has(outc.stdout, strings.concat("ok ", tree,
|
|
"/beta/inner [inner, same-package]\n")));
|
|
assert(has(outc.stdout, strings.concat("? ", tree,
|
|
"/gamma [no tests]\n")));
|
|
let rootreport: i32 = pos(outc.stdout, strings.concat("ok ", tree,
|
|
" [rootpkg, same-package]\n"));
|
|
let alphareport: i32 = pos(outc.stdout, strings.concat("ok ", tree,
|
|
"/alpha [alpha_test, external]\n"));
|
|
let innerreport: i32 = pos(outc.stdout, strings.concat("ok ", tree,
|
|
"/beta/inner [inner, same-package]\n"));
|
|
let gammareport: i32 = pos(outc.stdout, strings.concat("? ", tree,
|
|
"/gamma [no tests]\n"));
|
|
assert(rootreport >= 0 && rootreport < alphareport
|
|
&& alphareport < innerreport && innerreport < gammareport);
|
|
assert(occurrences(outc.stdout, strings.concat(tree, " [")) == 1);
|
|
assert(!has(outc.stdout, ".hidden"));
|
|
assert(!has(outc.stdout, "_skip"));
|
|
|
|
// Concurrent scheduling must not reorder the emitted byte stream.
|
|
let seq: str = strings.dup(outc.stdout);
|
|
let j4: []str = [driver("ww"), "test", "-j", "4", spec];
|
|
runcommand(root, "tree-j4", j4,
|
|
(30i64 * (time.second: i64)): time.duration, &outc);
|
|
expectexit(&outc, 0);
|
|
assert(same(outc.stdout, seq));
|
|
|
|
let j0: []str = [driver("ww"), "test", "-j", "0", spec];
|
|
runcommand(root, "tree-j0", j0, time.second, &outc);
|
|
expectexit(&outc, 2);
|
|
assert(has(outc.stderr, "usage: wwtest package"));
|
|
|
|
let patc: []str = [driver("ww"), "test", spec, "glob*"];
|
|
let patw: []str = [driver("ww_ww"), "test", spec, "glob*"];
|
|
runcommand(root, "tree-pattern-c", patc, time.second, &outc);
|
|
runcommand(root, "tree-pattern-ww", patw, time.second, &outw);
|
|
expectexit(&outc, 2);
|
|
expectexit(&outw, 2);
|
|
assert(same(outc.stderr, outw.stderr));
|
|
assert(has(outc.stderr, "ww test: pattern needs a single test file\n"));
|
|
|
|
let oc: []str = [driver("ww"), "test", "-o",
|
|
strings.concat(root, "/stem"), spec];
|
|
let ow: []str = [driver("ww_ww"), "test", "-o",
|
|
strings.concat(root, "/stem"), spec];
|
|
runcommand(root, "tree-o-c", oc, time.second, &outc);
|
|
runcommand(root, "tree-o-ww", ow, time.second, &outw);
|
|
expectexit(&outc, 2);
|
|
expectexit(&outw, 2);
|
|
assert(same(outc.stderr, outw.stderr));
|
|
assert(has(outc.stderr,
|
|
"ww test: -o needs -c for a package target\n"));
|
|
|
|
// -w on a package target forwards to the coordinator: one shared
|
|
// persistent driver workdir 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, spec];
|
|
let wdw: []str = [driver("ww_ww"), "test", "-w", wdroot, spec];
|
|
runcommand(root, "tree-w-c", wdc,
|
|
(30i64 * (time.second: i64)): time.duration, &outc);
|
|
runcommand(root, "tree-w-ww", wdw,
|
|
(30i64 * (time.second: i64)): time.duration, &outw);
|
|
expectexit(&outc, 0);
|
|
expectexit(&outw, 0);
|
|
assert(same(outc.stdout, outw.stdout));
|
|
assert(same(outc.stdout, seq));
|
|
runcommand(root, "tree-w-warm", wdc,
|
|
(30i64 * (time.second: i64)): time.duration, &outc);
|
|
expectexit(&outc, 0);
|
|
assert(same(outc.stdout, seq));
|
|
// -c publishes caller-owned artifacts; a persistent workdir is a
|
|
// different ownership contract — the coordinator rejects the mix.
|
|
let wcc: []str = [driver("ww"), "test", "-c", "-w", wdroot, spec];
|
|
let wcw: []str = [driver("ww_ww"), "test", "-c", "-w", wdroot, spec];
|
|
runcommand(root, "tree-wc-c", wcc, time.second, &outc);
|
|
runcommand(root, "tree-wc-ww", wcw, time.second, &outw);
|
|
expectexit(&outc, 2);
|
|
expectexit(&outw, 2);
|
|
assert(same(outc.stderr, outw.stderr));
|
|
assert(has(outc.stderr, "wwtest package: -w conflicts with -c\n"));
|
|
|
|
let bare: str = strings.concat(root, "/bare");
|
|
assert(os.mkdir(bare, 448i32) == 0);
|
|
assert(os.mkdir(strings.concat(bare, "/sub"), 448i32) == 0);
|
|
let barec: []str = [driver("ww"), "test",
|
|
strings.concat(bare, "/...")];
|
|
let barew: []str = [driver("ww_ww"), "test",
|
|
strings.concat(bare, "/...")];
|
|
runcommand(root, "tree-bare-c", barec,
|
|
(10i64 * (time.second: i64)): time.duration, &outc);
|
|
runcommand(root, "tree-bare-ww", barew,
|
|
(10i64 * (time.second: i64)): time.duration, &outw);
|
|
expectexit(&outc, 1);
|
|
expectexit(&outw, 1);
|
|
assert(same(outc.stderr, outw.stderr));
|
|
assert(has(outc.stderr, "directory contains no WW package sources\n"));
|
|
clean(root);
|
|
};
|
|
|
|
@test fn persistent_package_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,
|
|
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 = strings.concat(workc, "/", requestworkkey(tree));
|
|
assert(os.exists(ckey));
|
|
assert(requestworkkey(tree).len == 66);
|
|
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,
|
|
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 = strings.concat(workww, "/", requestworkkey(tree));
|
|
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 canonical coordinator key and warm action set are unchanged by an
|
|
// equivalent recursive-root spelling.
|
|
let equivalentav: []str = [driver("ww_ww"), "test", "-j", "2", "-w",
|
|
workww, 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 ", tree,
|
|
"/./a/foo [foo, same-package]\n")));
|
|
assert(has(out.stdout, strings.concat("ok ", tree,
|
|
"/./b/foo [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 <file>` 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", "-p", "-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);
|
|
};
|
|
|
|
// The package-level -o contract: -c -o names the single package's
|
|
// artifact in place of the fixed <package>.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);
|
|
};
|