14474 lines
630 KiB
Plaintext
14474 lines
630 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 permissionmode(path: str) u32 = {
|
||
let fi: os.filestat;
|
||
match (os.stat(&fi, path)) {
|
||
case void => return (fi.mode: u32) & 511u32;
|
||
case let e: os.oserror => abort("stat failed");
|
||
};
|
||
};
|
||
|
||
fn writefile(path: str, content: str) void = {
|
||
let fd: i32 = os.open(path,
|
||
os.flag.WRONLY | os.flag.CREATE | os.flag.EXCL, 384i32);
|
||
assert(fd >= 0);
|
||
match (os.writeall(fd, content.ptr, content.len: u64)) {
|
||
case let n: i64 => assert(n == content.len: i64);
|
||
case let e: os.oserror => abort("write failed");
|
||
};
|
||
assert(os.close(fd) == 0);
|
||
};
|
||
|
||
fn rewritefile(path: str, content: str) void = {
|
||
let fd: i32 = os.open(path, os.flag.WRONLY | os.flag.TRUNC, 384i32);
|
||
assert(fd >= 0);
|
||
match (os.writeall(fd, content.ptr, content.len: u64)) {
|
||
case let n: i64 => assert(n == content.len: i64);
|
||
case let e: os.oserror => abort("write failed");
|
||
};
|
||
assert(os.close(fd) == 0);
|
||
};
|
||
|
||
fn writeexecutable(path: str, content: str) void = {
|
||
let fd: i32 = os.open(path,
|
||
os.flag.WRONLY | os.flag.CREATE | os.flag.EXCL, 448i32);
|
||
assert(fd >= 0);
|
||
match (os.writeall(fd, content.ptr, content.len: u64)) {
|
||
case let n: i64 => assert(n == content.len: i64);
|
||
case let e: os.oserror => abort("write failed");
|
||
};
|
||
assert(os.close(fd) == 0);
|
||
};
|
||
|
||
fn runcommand(root: str, name: str, argv: []str,
|
||
lifetime: time.duration, out: *commandout) void = {
|
||
let c: exec.command;
|
||
c.path = argv[0];
|
||
c.argv = argv;
|
||
c.env = os.getenvs();
|
||
c.dir = repo();
|
||
c.stdoutpath = strings.concat(root, "/", name, ".stdout");
|
||
c.stderrpath = strings.concat(root, "/", name, ".stderr");
|
||
c.deadline = time.add(time.now(time.clock.monotonic), lifetime);
|
||
c.grace = (100i64 * (time.millisecond: i64)): time.duration;
|
||
let r: exec.result;
|
||
exec.run(&c, &r);
|
||
assert(r.errno == 0 && r.cleanuperrno == 0);
|
||
out.termination = r.termination;
|
||
out.code = r.code;
|
||
out.stdout = readfile(c.stdoutpath);
|
||
out.stderr = readfile(c.stderrpath);
|
||
};
|
||
|
||
fn runcommanddir(root: str, name: str, dir: str, argv: []str,
|
||
lifetime: time.duration, out: *commandout) void = {
|
||
let c: exec.command;
|
||
c.path = argv[0];
|
||
c.argv = argv;
|
||
c.env = os.getenvs();
|
||
c.dir = dir;
|
||
c.stdoutpath = strings.concat(root, "/", name, ".stdout");
|
||
c.stderrpath = strings.concat(root, "/", name, ".stderr");
|
||
c.deadline = time.add(time.now(time.clock.monotonic), lifetime);
|
||
c.grace = (100i64 * (time.millisecond: i64)): time.duration;
|
||
let r: exec.result;
|
||
exec.run(&c, &r);
|
||
assert(r.errno == 0 && r.cleanuperrno == 0);
|
||
out.termination = r.termination;
|
||
out.code = r.code;
|
||
out.stdout = readfile(c.stdoutpath);
|
||
out.stderr = readfile(c.stderrpath);
|
||
};
|
||
|
||
fn runcommandenv(root: str, name: str, argv: []str, env: []str,
|
||
lifetime: time.duration, out: *commandout) void = {
|
||
let c: exec.command;
|
||
c.path = argv[0];
|
||
c.argv = argv;
|
||
c.env = env;
|
||
c.dir = repo();
|
||
c.stdoutpath = strings.concat(root, "/", name, ".stdout");
|
||
c.stderrpath = strings.concat(root, "/", name, ".stderr");
|
||
c.deadline = time.add(time.now(time.clock.monotonic), lifetime);
|
||
c.grace = (100i64 * (time.millisecond: i64)): time.duration;
|
||
let r: exec.result;
|
||
exec.run(&c, &r);
|
||
assert(r.errno == 0 && r.cleanuperrno == 0);
|
||
out.termination = r.termination;
|
||
out.code = r.code;
|
||
out.stdout = readfile(c.stdoutpath);
|
||
out.stderr = readfile(c.stderrpath);
|
||
};
|
||
|
||
fn runcommandenvdir(root: str, name: str, argv: []str, env: []str,
|
||
dir: str, lifetime: time.duration, out: *commandout) void = {
|
||
let c: exec.command;
|
||
c.path = argv[0];
|
||
c.argv = argv;
|
||
c.env = env;
|
||
c.dir = dir;
|
||
c.stdoutpath = strings.concat(root, "/", name, ".stdout");
|
||
c.stderrpath = strings.concat(root, "/", name, ".stderr");
|
||
c.deadline = time.add(time.now(time.clock.monotonic), lifetime);
|
||
c.grace = (100i64 * (time.millisecond: i64)): time.duration;
|
||
let r: exec.result;
|
||
exec.run(&c, &r);
|
||
assert(r.errno == 0 && r.cleanuperrno == 0);
|
||
out.termination = r.termination;
|
||
out.code = r.code;
|
||
out.stdout = readfile(c.stdoutpath);
|
||
out.stderr = readfile(c.stderrpath);
|
||
};
|
||
|
||
// A direct descriptor mapping keeps the caller's duplicate environment and cwd
|
||
// intact while giving the nested driver known nonempty input.
|
||
fn runcommandinputenvdir(root: str, name: str, argv: []str, env: []str,
|
||
dir: str, input: str, lifetime: time.duration, out: *commandout) void = {
|
||
let c: exec.command;
|
||
c.path = argv[0];
|
||
c.argv = argv;
|
||
c.env = env;
|
||
c.dir = dir;
|
||
c.stdinpath = input;
|
||
c.stdoutpath = strings.concat(root, "/", name, ".stdout");
|
||
c.stderrpath = strings.concat(root, "/", name, ".stderr");
|
||
c.deadline = time.add(time.now(time.clock.monotonic), lifetime);
|
||
c.grace = (100i64 * (time.millisecond: i64)): time.duration;
|
||
let r: exec.result;
|
||
exec.run(&c, &r);
|
||
assert(r.errno == 0 && r.cleanuperrno == 0);
|
||
out.termination = r.termination;
|
||
out.code = r.code;
|
||
out.stdout = readfile(c.stdoutpath);
|
||
out.stderr = readfile(c.stderrpath);
|
||
};
|
||
|
||
fn clean(root: str) void = {
|
||
let av: []str = ["/bin/rm", "-rf", "--", root];
|
||
let c: exec.command;
|
||
c.path = av[0];
|
||
c.argv = av;
|
||
c.env = os.getenvs();
|
||
c.dir = "/";
|
||
c.stdoutpath = strings.concat(root, ".cleanup.stdout");
|
||
c.stderrpath = strings.concat(root, ".cleanup.stderr");
|
||
c.deadline = time.add(time.now(time.clock.monotonic), time.second);
|
||
c.grace = (50i64 * (time.millisecond: i64)): time.duration;
|
||
let r: exec.result;
|
||
exec.run(&c, &r);
|
||
assert(r.termination == exec.termination.EXIT && r.code == 0);
|
||
assert(os.remove(c.stdoutpath) == 0);
|
||
assert(os.remove(c.stderrpath) == 0);
|
||
};
|
||
|
||
fn fresh() str = { return strings.dup(temp.dir()); };
|
||
|
||
fn pos(haystack: str, needle: str) i32 = {
|
||
match (strings.index(haystack, needle)) {
|
||
case let n: i32 => return n;
|
||
case void => return -1;
|
||
};
|
||
};
|
||
|
||
fn has(haystack: str, needle: str) bool = {
|
||
return pos(haystack, needle) >= 0;
|
||
};
|
||
|
||
fn same(a: str, b: str) bool = {
|
||
if (a.len != b.len) { return false; };
|
||
let i: i32 = 0;
|
||
for (i < a.len) {
|
||
if (a[i] != b[i]) { return false; };
|
||
i += 1;
|
||
};
|
||
return true;
|
||
};
|
||
|
||
fn occurrences(haystack: str, needle: str) i32 = {
|
||
if (needle.len == 0 || haystack.len < needle.len) { return 0; };
|
||
let count: i32 = 0;
|
||
let i: i32 = 0;
|
||
for (i + needle.len <= haystack.len) {
|
||
let matches: bool = true;
|
||
let j: i32 = 0;
|
||
for (j < needle.len) {
|
||
if (haystack[i + j] != needle[j]) { matches = false; break; };
|
||
j += 1;
|
||
};
|
||
if (matches) { count += 1; i += needle.len; }
|
||
else { i += 1; };
|
||
};
|
||
return count;
|
||
};
|
||
|
||
fn boundarypkgname(n: i32) str = {
|
||
assert(n >= 0 && n < 1000);
|
||
let out: []u8 = alloc([], 4u64)!;
|
||
append(out, 'p');
|
||
append(out, ('0': i32 + (n / 100)): u8);
|
||
append(out, ('0': i32 + ((n / 10) % 10)): u8);
|
||
append(out, ('0': i32 + (n % 10)): u8);
|
||
return strings.frombytes(out);
|
||
};
|
||
|
||
fn linecontaining(text: str, needle: str) str = {
|
||
let start: i32 = 0;
|
||
let i: i32 = 0;
|
||
for (i <= text.len) {
|
||
if (i == text.len || text[i] == '\n': u8) {
|
||
let line: str;
|
||
line.ptr = text.ptr + (start: u64);
|
||
line.len = i - start;
|
||
if (has(line, needle)) { return line; };
|
||
start = i + 1;
|
||
};
|
||
i += 1;
|
||
};
|
||
abort("missing trace line");
|
||
};
|
||
|
||
fn expectexit(out: *commandout, code: i32) void = {
|
||
assert(out.termination == exec.termination.EXIT);
|
||
assert(out.code == code);
|
||
};
|
||
|
||
fn primarydiagnostic(s: str) str = {
|
||
let begin: i32 = pos(s, ": error: ");
|
||
if (begin >= 0) { begin += 9; }
|
||
else { begin = pos(s, "ww: dependency cycle: "); };
|
||
if (begin < 0) { return s; };
|
||
let end: i32 = begin;
|
||
for (end < s.len && s[end] != '\n') { end += 1; };
|
||
return strings.sub(s, begin, end);
|
||
};
|
||
|
||
fn rejectpackagestable(root: str, label: str, target: str,
|
||
needle: str) void = {
|
||
let drivers: []str = ["ww", "ww_ww", "ww", "ww_ww"];
|
||
let tags: []str = ["c1", "w1", "c2", "w2"];
|
||
let diagnostics: []str = ["", "", "", ""];
|
||
let outstem: str = strings.concat(root, "/reject-", label);
|
||
let i: i32 = 0;
|
||
for (i < drivers.len) {
|
||
let av: []str = [driver(drivers[i]), "test", "-c", "-o",
|
||
outstem, "-I", root, target];
|
||
let out: commandout;
|
||
runcommand(root, strings.concat("reject-", label, "-", tags[i]),
|
||
av, (60i64 * (time.second: i64)): time.duration, &out);
|
||
assert(out.termination == exec.termination.EXIT && out.code != 0);
|
||
assert(has(out.stderr, needle));
|
||
diagnostics[i] = strings.dup(primarydiagnostic(out.stderr));
|
||
let scratch: str = strings.concat(outstem, ".sepwork");
|
||
if (os.exists(scratch)) { clean(scratch); };
|
||
if (os.exists(outstem)) { clean(outstem); };
|
||
i += 1;
|
||
};
|
||
assert(same(diagnostics[0], diagnostics[2]));
|
||
assert(same(diagnostics[1], diagnostics[3]));
|
||
assert(same(diagnostics[0], diagnostics[1]));
|
||
};
|
||
|
||
fn packagepath(relative: str) str = {
|
||
return strings.concat(repo(), "/test/package/", relative);
|
||
};
|
||
|
||
fn workescape(s: str) str = {
|
||
let out: []u8 = alloc([], (s.len * 2 + 1): u64)!;
|
||
let i: i32 = 0;
|
||
for (i < s.len) {
|
||
if (s[i] == '_') {
|
||
append(out, '_'); append(out, 'u');
|
||
} else { if (s[i] == '/') {
|
||
append(out, '_'); append(out, 's');
|
||
} else {
|
||
append(out, s[i]);
|
||
}; };
|
||
i += 1;
|
||
};
|
||
return strings.frombytes(out);
|
||
};
|
||
|
||
fn localidentity(dir: str, declaredname: str) str = {
|
||
let out: []u8 = alloc([], (dir.len * 4 + 16): u64)!;
|
||
let prefix: str = "__wwlocal.p";
|
||
let i: i32 = 0;
|
||
for (i < prefix.len) { append(out, prefix[i]); i += 1; };
|
||
let hex: str = "0123456789abcdef";
|
||
i = 0;
|
||
for (i < dir.len) {
|
||
let c: u8 = dir[i];
|
||
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|
||
|| (c >= '0' && c <= '9')) {
|
||
append(out, c);
|
||
} else if (c == '_') {
|
||
append(out, '_'); append(out, 'u');
|
||
} else if (c == '/') {
|
||
append(out, '_'); append(out, 's');
|
||
} else {
|
||
let high: i32 = (c / 16u8): i32;
|
||
let low: i32 = (c % 16u8): i32;
|
||
append(out, '_'); append(out, 'x');
|
||
append(out, hex[high]); append(out, hex[low]);
|
||
};
|
||
i += 1;
|
||
};
|
||
return strings.frombytes(out);
|
||
};
|
||
|
||
fn digesthex(h: *hash.hash) str = {
|
||
let digest: [32]u8;
|
||
hash.sum(h, digest[0:32]);
|
||
let out: []u8 = alloc([], 65u64)!;
|
||
let digits: str = "0123456789abcdef";
|
||
let i: i32 = 0;
|
||
for (i < 32) {
|
||
let high: i32 = (digest[i] / 16u8): i32;
|
||
let low: i32 = (digest[i] % 16u8): i32;
|
||
append(out, digits[high]);
|
||
append(out, digits[low]);
|
||
i += 1;
|
||
};
|
||
return strings.frombytes(out);
|
||
};
|
||
|
||
fn packagestoragekeyfor(path: str, canon: str, variant: i32, role: i32,
|
||
for_test: str) str = {
|
||
let state: sha256.state = sha256.sha256();
|
||
let h: *hash.hash = (&state): *hash.hash;
|
||
hash.write(h, strings.toutf8("ww-package-storage-v3:"));
|
||
let tag: [4]u8;
|
||
tag[0] = ('0': i32 + variant): u8;
|
||
tag[1] = ':': u8;
|
||
tag[2] = ('0': i32 + role): u8;
|
||
tag[3] = ':': u8;
|
||
hash.write(h, tag[0:4]);
|
||
hash.write(h, strings.toutf8(path));
|
||
let zero: [1]u8;
|
||
zero[0] = 0u8;
|
||
hash.write(h, zero[0:1]);
|
||
hash.write(h, strings.toutf8(canon));
|
||
hash.write(h, zero[0:1]);
|
||
if (for_test.len != 0) {
|
||
hash.write(h, strings.toutf8(for_test));
|
||
};
|
||
let out: []u8 = alloc([], 81u64)!;
|
||
let prefix: str = "__wwpkg.v";
|
||
let i: i32 = 0;
|
||
for (i < prefix.len) { append(out, prefix[i]); i += 1; };
|
||
append(out, tag[0]); append(out, '.'); append(out, 'r');
|
||
append(out, tag[2]); append(out, '.'); append(out, 'h');
|
||
let hex: str = digesthex(h);
|
||
i = 0;
|
||
for (i < hex.len) { append(out, hex[i]); i += 1; };
|
||
return strings.frombytes(out);
|
||
};
|
||
|
||
fn packagestoragekey(path: str, canon: str, variant: i32, role: i32) str = {
|
||
return packagestoragekeyfor(path, canon, variant, role, "");
|
||
};
|
||
|
||
fn importrelative(path: str) str = {
|
||
let out: []u8 = alloc([], (path.len + 1): u64)!;
|
||
let i: i32 = 0;
|
||
for (i < path.len) {
|
||
if (path[i] == '.') { append(out, '/'); }
|
||
else { append(out, path[i]); };
|
||
i += 1;
|
||
};
|
||
return strings.frombytes(out);
|
||
};
|
||
|
||
fn longidentity(leaf: str) str = {
|
||
let out: []u8 = alloc([], 384u64)!;
|
||
let component: str = "segmentabcdefghijklmnop";
|
||
let n: i32 = 0;
|
||
for (n < 13) {
|
||
let i: i32 = 0;
|
||
for (i < component.len) { append(out, component[i]); i += 1; };
|
||
append(out, '.');
|
||
n += 1;
|
||
};
|
||
let i: i32 = 0;
|
||
for (i < leaf.len) { append(out, leaf[i]); i += 1; };
|
||
return strings.frombytes(out);
|
||
};
|
||
|
||
fn deeppunctuationpath(root: str, branch: str) str = {
|
||
let out: str = strings.concat(root, "/outside +% @ _/", branch);
|
||
let component: str = "/punctuation + percent% at@ under_score";
|
||
let n: i32 = 0;
|
||
for (n < 9) { out = strings.concat(out, component); n += 1; };
|
||
return out;
|
||
};
|
||
|
||
fn mkdirall(path: str) void = {
|
||
match (os.mkdirs(path, 448i32)) {
|
||
case void => void;
|
||
case let e: os.oserror => abort("mkdirs failed");
|
||
};
|
||
};
|
||
|
||
fn normalizedtrace(trace: str, work: str, output: str) str = {
|
||
let first: str;
|
||
match (strings.replace(trace, work, "$WORK/")) {
|
||
case let value: str => first = value;
|
||
case nomem => abort("trace normalization failed");
|
||
};
|
||
match (strings.replace(first, output, "$OUTPUT")) {
|
||
case let value: str => return value;
|
||
case nomem => abort("trace normalization failed");
|
||
};
|
||
};
|
||
|
||
fn directoryhasnew(path: str) bool = {
|
||
let fd: i32 = os.open(path, os.flag.RDONLY, 0i32);
|
||
assert(fd >= 0);
|
||
let buf: []u8 = alloc([], 16384u64)!;
|
||
buf.len = 16384;
|
||
let n: i64 = os.getdents64(fd, buf.ptr, buf.len: u64);
|
||
for (n > 0i64) {
|
||
let off: i32 = 0;
|
||
for (off < n: i32) {
|
||
let reclen: i32 = (buf[off + 16]: i32)
|
||
+ ((buf[off + 17]: i32) * 256);
|
||
assert(reclen >= 20 && off + reclen <= n: i32);
|
||
let len: i32 = 0;
|
||
for (buf[off + 19 + len] != 0u8) { len += 1; };
|
||
if (len >= 4 && buf[off + 19 + len - 4] == '.'
|
||
&& buf[off + 19 + len - 3] == 'n'
|
||
&& buf[off + 19 + len - 2] == 'e'
|
||
&& buf[off + 19 + len - 1] == 'w') {
|
||
assert(os.close(fd) == 0);
|
||
return true;
|
||
};
|
||
off += reclen;
|
||
};
|
||
n = os.getdents64(fd, buf.ptr, buf.len: u64);
|
||
};
|
||
assert(n == 0i64);
|
||
assert(os.close(fd) == 0);
|
||
return false;
|
||
};
|
||
|
||
fn directoryhasfragment(path: str, needle: str) bool = {
|
||
let fd: i32 = os.open(path, os.flag.RDONLY, 0i32);
|
||
assert(fd >= 0);
|
||
let buf: []u8 = alloc([], 16384u64)!;
|
||
buf.len = 16384;
|
||
let n: i64 = os.getdents64(fd, buf.ptr, buf.len: u64);
|
||
for (n > 0i64) {
|
||
let off: i32 = 0;
|
||
for (off < n: i32) {
|
||
let reclen: i32 = (buf[off + 16]: i32)
|
||
+ ((buf[off + 17]: i32) * 256);
|
||
assert(reclen >= 20 && off + reclen <= n: i32);
|
||
let len: i32 = 0;
|
||
for (buf[off + 19 + len] != 0u8) { len += 1; };
|
||
let name: str;
|
||
name.ptr = buf.ptr + (off + 19): u64;
|
||
name.len = len;
|
||
if (has(name, needle)) {
|
||
assert(os.close(fd) == 0);
|
||
return true;
|
||
};
|
||
off += reclen;
|
||
};
|
||
n = os.getdents64(fd, buf.ptr, buf.len: u64);
|
||
};
|
||
assert(n == 0i64);
|
||
assert(os.close(fd) == 0);
|
||
return false;
|
||
};
|
||
|
||
fn directoryisempty(path: str) bool = {
|
||
let fd: i32 = os.open(path, os.flag.RDONLY, 0i32);
|
||
assert(fd >= 0);
|
||
let buf: []u8 = alloc([], 16384u64)!;
|
||
buf.len = 16384;
|
||
let n: i64 = os.getdents64(fd, buf.ptr, buf.len: u64);
|
||
for (n > 0i64) {
|
||
let off: i32 = 0;
|
||
for (off < n: i32) {
|
||
let reclen: i32 = (buf[off + 16]: i32)
|
||
+ ((buf[off + 17]: i32) * 256);
|
||
assert(reclen >= 20 && off + reclen <= n: i32);
|
||
let len: i32 = 0;
|
||
for (buf[off + 19 + len] != 0u8) { len += 1; };
|
||
let dot: bool = len == 1 && buf[off + 19] == '.';
|
||
let dotdot: bool = len == 2 && buf[off + 19] == '.'
|
||
&& buf[off + 20] == '.';
|
||
if (!dot && !dotdot) {
|
||
assert(os.close(fd) == 0);
|
||
return false;
|
||
};
|
||
off += reclen;
|
||
};
|
||
n = os.getdents64(fd, buf.ptr, buf.len: u64);
|
||
};
|
||
assert(n == 0i64);
|
||
assert(os.close(fd) == 0);
|
||
return true;
|
||
};
|
||
|
||
fn discardedlinkcleaned(marker: str) void = {
|
||
let output: str = readfile(marker);
|
||
assert(output.len != 0);
|
||
assert(!has(output, "/dev/null"));
|
||
let slash: i32 = output.len - 1;
|
||
for (slash >= 0 && output[slash] != '/') { slash -= 1; };
|
||
assert(slash > 0);
|
||
assert(!os.exists(strings.sub(output, 0, slash)));
|
||
};
|
||
|
||
fn byteshasat(s: str, off: i32, want: str) bool = {
|
||
if (off < 0 || off + want.len > s.len) { return false; };
|
||
let i: i32 = 0;
|
||
for (i < want.len) {
|
||
if (s[off + i] != want[i]) { return false; };
|
||
i += 1;
|
||
};
|
||
return true;
|
||
};
|
||
|
||
fn archivememberend(ar: str, off: i32, name: str) i32 = {
|
||
assert(byteshasat(ar, off, name));
|
||
assert(byteshasat(ar, off + 58, "`\n"));
|
||
let size: i32 = 0;
|
||
let seen: bool = false;
|
||
let i: i32 = 0;
|
||
for (i < 10) {
|
||
let c: u8 = ar[off + 48 + i];
|
||
if (c >= '0' && c <= '9') {
|
||
seen = true;
|
||
size = size * 10 + ((c - '0'): i32);
|
||
} else { assert(c == ' '); };
|
||
i += 1;
|
||
};
|
||
assert(seen);
|
||
return off + 60 + size + (size & 1);
|
||
};
|
||
|
||
fn checkactionarchive(ar: str, hasinit: bool) void = {
|
||
assert(byteshasat(ar, 0, "!<arch>\n"));
|
||
let off: i32 = archivememberend(ar, 8, "pkg.o/");
|
||
if (hasinit) { off = archivememberend(ar, off, "init.o/"); };
|
||
assert(off == ar.len);
|
||
};
|
||
|
||
fn hexbytes(value: str) str = {
|
||
let out: []u8 = alloc([], (value.len * 2 + 1): u64)!;
|
||
let digits: str = "0123456789abcdef";
|
||
let i: i32 = 0;
|
||
for (i < value.len) {
|
||
let high: i32 = (value[i] / 16u8): i32;
|
||
let low: i32 = (value[i] % 16u8): i32;
|
||
append(out, digits[high]);
|
||
append(out, digits[low]);
|
||
i += 1;
|
||
};
|
||
return strings.frombytes(out);
|
||
};
|
||
|
||
fn cwdtestenv(stage: str) []str = {
|
||
let inherited: []str = os.getenvs();
|
||
let env: []str = alloc([], (inherited.len + 8): u64)!;
|
||
let i: i32 = 0;
|
||
for (i < inherited.len) {
|
||
if (!strings.hasprefix(inherited[i], "PWD=")
|
||
&& !strings.hasprefix(inherited[i], "WW_W6C=")
|
||
&& !strings.hasprefix(inherited[i], "WW_W6A=")
|
||
&& !strings.hasprefix(inherited[i], "WW_W6L=")
|
||
&& !strings.hasprefix(inherited[i], "WW_SRCLIB=")
|
||
&& !strings.hasprefix(inherited[i], "WW_CWD_")) {
|
||
append(env, inherited[i]);
|
||
};
|
||
i += 1;
|
||
};
|
||
append(env, "PWD=/ww-cwd-inherited-first");
|
||
if (same(stage, "ww")) {
|
||
append(env, strings.concat("WW_W6C=", driver("w6c")));
|
||
append(env, strings.concat("WW_W6A=", driver("w6a")));
|
||
append(env, strings.concat("WW_W6L=", driver("w6l")));
|
||
} else {
|
||
assert(same(stage, "ww_ww"));
|
||
append(env, strings.concat("WW_W6C=", driver("w6c_ww")));
|
||
append(env, strings.concat("WW_W6A=", driver("w6a_ww")));
|
||
append(env, strings.concat("WW_W6L=", driver("w6l_ww")));
|
||
};
|
||
append(env, strings.concat("WW_SRCLIB=", repo(), "/lib"));
|
||
append(env, "PWD=/ww-cwd-inherited-last");
|
||
return env;
|
||
};
|
||
|
||
fn cwdassertrecord(text: str, label: str, cwd: str, pwd: str,
|
||
pwdcount: str, pwdlast: str, data: str, testdata: str,
|
||
input: str) void = {
|
||
assert(has(text, strings.concat(label, " cwd=", cwd, "\n")));
|
||
assert(has(text, strings.concat(label, " PWD=", pwd, "\n")));
|
||
assert(has(text, strings.concat(label, " PWD-count=", pwdcount, "\n")));
|
||
assert(has(text, strings.concat(label, " PWD-last=", pwdlast, "\n")));
|
||
assert(has(text, strings.concat(label, " data=", data, "\n")));
|
||
assert(has(text, strings.concat(label, " testdata=", testdata, "\n")));
|
||
assert(has(text, strings.concat(label, " stdin=", input, "\n")));
|
||
assert(has(text, strings.concat(label, " create=ok\n")));
|
||
};
|
||
|
||
fn cwdassertmergedstreams(text: str, label: str, dir: str) void = {
|
||
let out1: str = strings.concat("STREAM ", label, " out-1 cwd=", dir, "\n");
|
||
let err1: str = strings.concat("STREAM ", label, " err-1 cwd=", dir, "\n");
|
||
let out2: str = strings.concat("STREAM ", label, " out-2 cwd=", dir, "\n");
|
||
let err2: str = strings.concat("STREAM ", label, " err-2 cwd=", dir, "\n");
|
||
let p0: i32 = pos(text, out1);
|
||
let p1: i32 = pos(text, err1);
|
||
let p2: i32 = pos(text, out2);
|
||
let p3: i32 = pos(text, err2);
|
||
assert(p0 >= 0 && p0 < p1 && p1 < p2 && p2 < p3);
|
||
assert(occurrences(text, out1) == 1 && occurrences(text, err1) == 1);
|
||
assert(occurrences(text, out2) == 1 && occurrences(text, err2) == 1);
|
||
};
|
||
|
||
fn cwdassertsplitstreams(stdout: str, stderr: str, label: str, dir: str) void = {
|
||
let out1: str = strings.concat("STREAM ", label, " out-1 cwd=", dir, "\n");
|
||
let err1: str = strings.concat("STREAM ", label, " err-1 cwd=", dir, "\n");
|
||
let out2: str = strings.concat("STREAM ", label, " out-2 cwd=", dir, "\n");
|
||
let err2: str = strings.concat("STREAM ", label, " err-2 cwd=", dir, "\n");
|
||
let p0: i32 = pos(stdout, out1);
|
||
let p2: i32 = pos(stdout, out2);
|
||
let p1: i32 = pos(stderr, err1);
|
||
let p3: i32 = pos(stderr, err2);
|
||
assert(p0 >= 0 && p0 < p2 && p1 >= 0 && p1 < p3);
|
||
assert(!has(stdout, err1) && !has(stdout, err2));
|
||
assert(!has(stderr, out1) && !has(stderr, out2));
|
||
};
|
||
|
||
fn cwdassertpackage(text: str, label: str, dir: str, data: str,
|
||
testdata: str) void = {
|
||
cwdassertrecord(text, label, dir, dir, "1", "yes", data, testdata,
|
||
"eof");
|
||
cwdassertmergedstreams(text, label, dir);
|
||
};
|
||
|
||
fn cwdwritedata(dir: str, label: str) void = {
|
||
let testdata: str = strings.concat(dir, "/testdata");
|
||
assert(os.mkdir(testdata, 448i32) == 0);
|
||
writefile(strings.concat(dir, "/data.txt"),
|
||
strings.concat(label, "-data\n"));
|
||
writefile(strings.concat(testdata, "/input.txt"),
|
||
strings.concat(label, "-testdata\n"));
|
||
};
|
||
|
||
@test fn deterministic_routing_and_filters() void = {
|
||
let root: str = fresh();
|
||
let target: str = packagepath("routing");
|
||
let sourceroot: str = strings.concat(repo(), "/test/package");
|
||
let av: []str = [driver("ww"), "test", "-I", sourceroot,
|
||
"-list", target];
|
||
let out: commandout;
|
||
runcommand(root, "list", av, (30i64 * (time.second: i64)): time.duration,
|
||
&out);
|
||
expectexit(&out, 0);
|
||
let first: i32 = pos(out.stdout, "routing.private_helper_first\n");
|
||
let second: i32 = pos(out.stdout, "routing.same_package\n");
|
||
let third: i32 = pos(out.stdout, "routing.external_package\n");
|
||
assert(first >= 0 && first < second && second < third);
|
||
assert(occurrences(out.stdout, "routing.private_helper_first\n") == 1);
|
||
assert(occurrences(out.stdout, "routing.same_package\n") == 1);
|
||
assert(occurrences(out.stdout, "routing.external_package\n") == 1);
|
||
|
||
let rav: []str = [driver("ww"), "test", "-I", sourceroot, target];
|
||
runcommand(root, "run", rav,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout, "routing.private_helper_first ... ok\n"));
|
||
assert(has(out.stdout, "routing.same_package ... ok\n"));
|
||
assert(has(out.stdout, "routing.external_package ... ok\n"));
|
||
assert(has(out.stdout,
|
||
"3 passed, 0 failed, 0 skipped, 0 harness errors\n"));
|
||
assert(occurrences(out.stdout,
|
||
"3 passed, 0 failed, 0 skipped, 0 harness errors\n") == 1);
|
||
|
||
let fav: []str = [driver("ww"), "test", "-run",
|
||
"routing.private_helper_first", "-filter", "external_package",
|
||
"-I", sourceroot, target];
|
||
runcommand(root, "filters", fav,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout, "routing.private_helper_first ... ok\n"));
|
||
assert(!has(out.stdout, "routing.same_package ..."));
|
||
assert(has(out.stdout, "routing.external_package ... ok\n"));
|
||
|
||
let nav: []str = [driver("ww"), "test", "-I", sourceroot,
|
||
"-run", "no-such-*", target];
|
||
runcommand(root, "nomatch", nav,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(occurrences(out.stdout, "[no matches]\n") == 1);
|
||
assert(occurrences(out.stdout,
|
||
"3 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") == 1);
|
||
clean(root);
|
||
};
|
||
|
||
// Directory-package execution is one product-level runtime property. This
|
||
// fixture keeps every source tree disposable while covering the variant,
|
||
// dependency, routing, concurrency, persistence, and failure boundaries that
|
||
// could otherwise accidentally derive a cwd from an action or request name.
|
||
@test fn directory_test_execution_working_directory() void = {
|
||
let root: str = fresh();
|
||
let caller: str = strings.concat(root, "/caller");
|
||
let tree: str = strings.concat(root, "/tree");
|
||
let treelink: str = strings.concat(root, "/tree-link");
|
||
let failtree: str = strings.concat(root, "/failtree");
|
||
mkdirall(caller);
|
||
mkdirall(tree);
|
||
mkdirall(failtree);
|
||
cwdwritedata(caller, "caller");
|
||
writefile(strings.concat(caller, "/sentinel"), "parent-sentinel\n");
|
||
let stdinpath: str = strings.concat(root, "/stdin.txt");
|
||
let stdinbytes: []u8 = alloc([], 4096u64)!;
|
||
let inputi: i32 = 0;
|
||
for (inputi < 2048) {
|
||
append(stdinbytes, 'X');
|
||
append(stdinbytes, '\n');
|
||
inputi += 1;
|
||
};
|
||
writefile(stdinpath, strings.frombytes(stdinbytes));
|
||
let missingstdinout: str = strings.concat(root, "/missing-stdin.stdout");
|
||
let missingstdinerr: str = strings.concat(root, "/missing-stdin.stderr");
|
||
let missingstdinav: []str = ["/bin/true"];
|
||
let missingstdincmd: exec.command;
|
||
missingstdincmd.path = missingstdinav[0];
|
||
missingstdincmd.argv = missingstdinav;
|
||
missingstdincmd.env = os.getenvs();
|
||
missingstdincmd.dir = caller;
|
||
missingstdincmd.stdinpath = strings.concat(root, "/missing-stdin");
|
||
missingstdincmd.stdoutpath = missingstdinout;
|
||
missingstdincmd.stderrpath = missingstdinerr;
|
||
missingstdincmd.deadline.sec = 0i64;
|
||
missingstdincmd.deadline.nsec = 0i64;
|
||
missingstdincmd.grace = 0i64: time.duration;
|
||
let missingstdinresult: exec.result;
|
||
exec.run(&missingstdincmd, &missingstdinresult);
|
||
assert(missingstdinresult.termination == exec.termination.ERROR);
|
||
assert(missingstdinresult.errno == 2
|
||
&& missingstdinresult.cleanuperrno == 0);
|
||
assert(!os.exists(missingstdinout) && !os.exists(missingstdinerr));
|
||
|
||
// Create in reverse lexical order; discovery and product emission may not
|
||
// inherit filesystem or request order.
|
||
let names: []str = ["probe", "plain", "p6", "p5", "p4", "p3",
|
||
"p2", "p1", "testdep", "dep", "cmdno"];
|
||
let i: i32 = 0;
|
||
for (i < names.len) {
|
||
let dir: str = strings.concat(tree, "/", names[i]);
|
||
assert(os.mkdir(dir, 448i32) == 0);
|
||
cwdwritedata(dir, names[i]);
|
||
i += 1;
|
||
};
|
||
let probe: str = strings.concat(tree, "/probe");
|
||
let dep: str = strings.concat(tree, "/dep");
|
||
let testdep: str = strings.concat(tree, "/testdep");
|
||
let p1: str = strings.concat(tree, "/p1");
|
||
let p2: str = strings.concat(tree, "/p2");
|
||
let p3: str = strings.concat(tree, "/p3");
|
||
let p4: str = strings.concat(tree, "/p4");
|
||
let p5: str = strings.concat(tree, "/p5");
|
||
let p6: str = strings.concat(tree, "/p6");
|
||
let plain: str = strings.concat(tree, "/plain");
|
||
let cmdno: str = strings.concat(tree, "/cmdno");
|
||
|
||
let streamsource: str = strings.concat(
|
||
"fn putfd(fd: i32, value: str) void = {\n",
|
||
" match (os.writeall(fd, value.ptr, value.len: u64)) {\n",
|
||
" case let n: i64 => assert(n == value.len: i64);\n",
|
||
" case let e: os.oserror => abort(\"write failed\");\n",
|
||
" };\n",
|
||
"};\n",
|
||
"fn put(value: str) void = { putfd(os.STDOUT_FILENO, value); };\n",
|
||
"fn puterr(value: str) void = { putfd(os.STDERR_FILENO, value); };\n",
|
||
"export fn streams(label: str, context: str) void = {\n",
|
||
" put(strings.concat(\"STREAM \", label, \" out-1 cwd=\", context, \"\\n\"));\n",
|
||
" puterr(strings.concat(\"STREAM \", label, \" err-1 cwd=\", context, \"\\n\"));\n",
|
||
" put(strings.concat(\"STREAM \", label, \" out-2 cwd=\", context, \"\\n\"));\n",
|
||
" puterr(strings.concat(\"STREAM \", label, \" err-2 cwd=\", context, \"\\n\"));\n",
|
||
"};\n");
|
||
writefile(strings.concat(probe, "/probe.ww"), strings.concat(
|
||
"package probe;\n",
|
||
"import os;\n",
|
||
"import strings;\n",
|
||
streamsource,
|
||
"fn emitfile(label: str, key: str, path: str) void = {\n",
|
||
" put(label); put(\" \" ); put(key); put(\"=\");\n",
|
||
" let fd: i32 = os.open(path, os.flag.RDONLY, 0i32);\n",
|
||
" if (fd < 0) { put(\"<missing>\\n\"); return; };\n",
|
||
" let size: i64 = -1i64;\n",
|
||
" match (os.filesize(fd)) {\n",
|
||
" case let value: i64 => size = value;\n",
|
||
" case let e: os.oserror => void;\n",
|
||
" };\n",
|
||
" if (size < 0i64) { os.close(fd); put(\"<size-error>\\n\"); return; };\n",
|
||
" let bytes: []u8 = alloc([], size: u64)!; bytes.len = size: i32;\n",
|
||
" match (os.readall(fd, bytes.ptr, size: u64)) {\n",
|
||
" case let n: i64 => assert(n == size);\n",
|
||
" case let e: os.oserror => abort(\"read failed\");\n",
|
||
" }; os.close(fd);\n",
|
||
" let body: str; body.ptr = bytes.ptr; body.len = bytes.len; put(body);\n",
|
||
" if (body.len == 0 || body[body.len - 1] != '\\n') { put(\"\\n\"); };\n",
|
||
"};\n",
|
||
"export fn run(label: str) void = {\n",
|
||
" let buf: [4096]u8;\n",
|
||
" let n: i64 = os.getcwd(&buf[0], size([4096]u8)); assert(n > 1i64);\n",
|
||
" let cwd: str; cwd.ptr = &buf[0]; cwd.len = (n - 1i64): i32;\n",
|
||
" streams(label, cwd);\n",
|
||
" put(label); put(\" cwd=\"); put(cwd); put(\"\\n\");\n",
|
||
" put(label); put(\" PWD=\");\n",
|
||
" match (os.getenv(\"PWD\")) {\n",
|
||
" case let value: str => put(value); case void => put(\"<unset>\");\n",
|
||
" }; put(\"\\n\");\n",
|
||
" let env: []str = os.getenvs(); let count: i32 = 0; let last: i32 = -1;\n",
|
||
" let i: i32 = 0; for (i < env.len) {\n",
|
||
" if (strings.hasprefix(env[i], \"PWD=\")) { count += 1; last = i; };\n",
|
||
" i += 1;\n",
|
||
" };\n",
|
||
" put(label); put(\" PWD-count=\");\n",
|
||
" if (count == 0) { put(\"0\\n\"); } else { if (count == 1) {\n",
|
||
" put(\"1\\n\"); } else { if (count == 2) { put(\"2\\n\");\n",
|
||
" } else { put(\"many\\n\"); }; }; };\n",
|
||
" put(label); put(\" PWD-last=\");\n",
|
||
" if (last == env.len - 1) { put(\"yes\\n\"); } else { put(\"no\\n\"); };\n",
|
||
" emitfile(label, \"data\", \"data.txt\");\n",
|
||
" emitfile(label, \"testdata\", \"testdata/input.txt\");\n",
|
||
" let input: [1]u8;\n let inputn: i64 = os.read(os.STDIN_FILENO, &input[0], 1u64);\n put(label); put(\" stdin=\");\n if (inputn == 0i64) { put(\"eof\\n\"); } else {\n if (inputn == 1i64) { put(\"data\\n\"); } else { put(\"<error>\\n\"); };\n };\n",
|
||
" let fd: i32 = os.open(\"created.txt\", os.flag.WRONLY |\n",
|
||
" os.flag.CREATE | os.flag.TRUNC, 384i32);\n",
|
||
" put(label); put(\" create=\");\n",
|
||
" if (fd < 0) { put(\"<error>\\n\"); } else {\n",
|
||
" match (os.writeall(fd, label.ptr, label.len: u64)) {\n",
|
||
" case let wrote: i64 => assert(wrote == label.len: i64);\n",
|
||
" case let e: os.oserror => abort(\"create write failed\");\n",
|
||
" }; assert(os.close(fd) == 0); put(\"ok\\n\");\n",
|
||
" };\n",
|
||
"};\n"));
|
||
writefile(strings.concat(dep, "/dep.ww"), strings.concat(
|
||
"package dep;\nimport probe;\n",
|
||
"export fn value() i32 = { return 7; };\n",
|
||
"fn init() void = { probe.run(\"dep-init\"); };\n"));
|
||
writefile(strings.concat(dep, "/dep_test.ww"),
|
||
"package dep;\nimport probe;\n@test fn cwd() void = { probe.run(\"dep-test\"); };\n");
|
||
writefile(strings.concat(testdep, "/testdep.ww"), strings.concat(
|
||
"package testdep;\nimport probe;\n",
|
||
"export fn value() i32 = { return 8; };\n",
|
||
"fn init() void = { probe.run(\"testdep-init\"); };\n"));
|
||
writefile(strings.concat(testdep, "/testdep_test.ww"), strings.concat(
|
||
"package testdep;\nimport probe;\n",
|
||
"@test fn cwd() void = { probe.run(\"testdep-test\"); };\n"));
|
||
writefile(strings.concat(p1, "/p1.ww"),
|
||
"package p1;\nimport _ dep;\nexport fn value() i32 = { return 1; };\n");
|
||
writefile(strings.concat(p1, "/p1_test.ww"),
|
||
"package p1;\nimport probe;\n@test fn cwd() void = { probe.run(\"p1-internal\"); };\n");
|
||
writefile(strings.concat(p2, "/p2.ww"),
|
||
"package p2;\nimport _ dep;\nexport fn value() i32 = { return 2; };\n");
|
||
writefile(strings.concat(p2, "/p2_test.ww"), strings.concat(
|
||
"package p2_test;\nimport p2;\nimport probe;\n",
|
||
"@test fn cwd() void = { assert(p2.value() == 2); ",
|
||
"probe.run(\"p2-external\"); };\n"));
|
||
writefile(strings.concat(p3, "/p3.ww"),
|
||
"package p3;\nimport _ dep;\nexport fn value() i32 = { return 3; };\n");
|
||
writefile(strings.concat(p3, "/internal_test.ww"), strings.concat(
|
||
"package p3;\nimport _ testdep;\nimport probe;\n",
|
||
"export fn helper() i32 = { return 33; };\n",
|
||
"@test fn cwd_internal() void = { probe.run(\"p3-internal\"); };\n"));
|
||
writefile(strings.concat(p3, "/external_test.ww"), strings.concat(
|
||
"package p3_test;\nimport p3;\nimport probe;\n",
|
||
"@test fn cwd_external() void = { assert(p3.helper() == 33); ",
|
||
"probe.run(\"p3-external\"); };\n"));
|
||
writefile(strings.concat(p4, "/internal_test.ww"),
|
||
"package p4;\nimport probe;\n@test fn cwd() void = { probe.run(\"p4-internal-only\"); };\n");
|
||
writefile(strings.concat(p5, "/external_test.ww"),
|
||
"package p5_test;\nimport probe;\n@test fn cwd() void = { probe.run(\"p5-external-only\"); };\n");
|
||
writefile(strings.concat(p6, "/internal_test.ww"), strings.concat(
|
||
"package p6;\nimport probe;\nexport fn helper() i32 = { return 66; };\n",
|
||
"@test fn cwd_internal() void = { probe.run(\"p6-internal-only\"); };\n"));
|
||
writefile(strings.concat(p6, "/external_test.ww"), strings.concat(
|
||
"package p6_test;\nimport p6;\nimport probe;\n",
|
||
"@test fn cwd_external() void = { assert(p6.helper() == 66); ",
|
||
"probe.run(\"p6-external-only\"); };\n"));
|
||
writefile(strings.concat(plain, "/plain.ww"),
|
||
"package plain;\nimport probe;\nfn init() void = { probe.run(\"plain-init\"); };\n");
|
||
writefile(strings.concat(cmdno, "/main.ww"), strings.concat(
|
||
"package main;\nimport probe;\n",
|
||
"fn init() void = { probe.run(\"cmdno-init\"); };\n",
|
||
"fn main() void = { probe.run(\"cmdno-main\"); };\n"));
|
||
|
||
let failnames: []str = ["reject", "p9", "p8", "p7"];
|
||
i = 0;
|
||
for (i < failnames.len) {
|
||
let dir: str = strings.concat(failtree, "/", failnames[i]);
|
||
assert(os.mkdir(dir, 448i32) == 0);
|
||
cwdwritedata(dir, failnames[i]);
|
||
i += 1;
|
||
};
|
||
let p7: str = strings.concat(failtree, "/p7");
|
||
let p8: str = strings.concat(failtree, "/p8");
|
||
let p9: str = strings.concat(failtree, "/p9");
|
||
let reject: str = strings.concat(failtree, "/reject");
|
||
writefile(strings.concat(p7, "/p7_test.ww"), strings.concat(
|
||
"package p7;\nimport probe;\n@test fn fails() void = {\n",
|
||
" probe.run(\"p7-fail\"); assert(false);\n};\n"));
|
||
writefile(strings.concat(p8, "/p8_test.ww"), strings.concat(
|
||
"package p8;\nimport probe;\nimport time;\n@test fn timesout() void = {\n",
|
||
" probe.run(\"p8-timeout\");\n",
|
||
" time.sleep((2i64 * (time.second: i64)): time.duration, ",
|
||
"time.clock.monotonic);\n};\n"));
|
||
writefile(strings.concat(p9, "/p9_test.ww"), strings.concat(
|
||
"package p9;\nimport os;\nimport probe;\n@test fn signals() void = {\n",
|
||
" probe.run(\"p9-signal\"); os.kill(os.getpid(), os.SIGTERM);\n};\n"));
|
||
writefile(strings.concat(reject, "/reject.ww"),
|
||
"package reject;\nexport fn value() i32 = { return 1; };\n");
|
||
writefile(strings.concat(reject, "/bad_test.ww"),
|
||
"package unrelated;\n@test fn unreachable() void = { assert(false); };\n");
|
||
|
||
let out: commandout;
|
||
let lnav: []str = ["/bin/ln", "-s", tree, treelink];
|
||
runcommand(root, "cwd-link", lnav, time.second, &out);
|
||
expectexit(&out, 0);
|
||
|
||
// The first environment has hundreds of unrelated entries after two PWD
|
||
// entries. The product must dynamically clone it, remove both PWD entries,
|
||
// and append exactly one authoritative PWD last.
|
||
let cbase: []str = cwdtestenv("ww");
|
||
let cenv: []str = alloc([], (cbase.len + 300): u64)!;
|
||
i = 0;
|
||
for (i < cbase.len) { append(cenv, cbase[i]); i += 1; };
|
||
i = 0;
|
||
for (i < 300) {
|
||
append(cenv, strings.concat("WW_CWD_PADDING_", boundarypkgname(i),
|
||
"=abcdefghijklmnopqrstuvwxyz0123456789"));
|
||
i += 1;
|
||
};
|
||
let wenv: []str = cwdtestenv("ww_ww");
|
||
let forward: []str = [driver("ww"), "test", "-j", "1", "-I", tree,
|
||
dep, testdep, p1, p2, p3, p4, p5, p6];
|
||
let reverse: []str = [driver("ww_ww"), "test", "-j", "4", "-I", tree,
|
||
p6, p5, p4, p3, p2, p1, testdep, dep];
|
||
let outc: commandout;
|
||
let outw: commandout;
|
||
runcommandinputenvdir(root, "cwd-multi-c", forward, cenv, caller,
|
||
stdinpath,
|
||
(120i64 * (time.second: i64)): time.duration, &outc);
|
||
runcommandinputenvdir(root, "cwd-multi-ww", reverse, wenv, caller,
|
||
stdinpath,
|
||
(120i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 0);
|
||
expectexit(&outw, 0);
|
||
assert(same(outc.stdout, outw.stdout));
|
||
assert(same(outc.stderr, outw.stderr) && outc.stderr.len == 0);
|
||
cwdassertpackage(outc.stdout, "dep-init", dep, "dep-data", "dep-testdata");
|
||
cwdassertpackage(outc.stdout, "dep-test", dep, "dep-data", "dep-testdata");
|
||
cwdassertpackage(outc.stdout, "testdep-init", testdep,
|
||
"testdep-data", "testdep-testdata");
|
||
cwdassertpackage(outc.stdout, "testdep-test", testdep,
|
||
"testdep-data", "testdep-testdata");
|
||
cwdassertpackage(outc.stdout, "dep-init", p1, "p1-data", "p1-testdata");
|
||
cwdassertpackage(outc.stdout, "p1-internal", p1,
|
||
"p1-data", "p1-testdata");
|
||
let multiout: str = strings.dup(outc.stdout);
|
||
let ran: []str = [dep, testdep, p1, p2, p3, p4, p5, p6];
|
||
i = 0;
|
||
for (i < ran.len) {
|
||
assert(os.exists(strings.concat(ran[i], "/created.txt")));
|
||
i += 1;
|
||
};
|
||
assert(!os.exists(strings.concat(caller, "/created.txt")));
|
||
assert(same(readfile(strings.concat(caller, "/sentinel")),
|
||
"parent-sentinel\n"));
|
||
|
||
// Filtering, listing, and a no-match filter all execute the same product
|
||
// binary. Package initialization therefore has the product cwd even when no
|
||
// selected test body runs.
|
||
let filterav: []str = [driver("ww"), "test", "-run", "cwd_external",
|
||
"-I", tree, p3];
|
||
runcommandinputenvdir(root, "cwd-filter", filterav, cenv, caller,
|
||
stdinpath,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
cwdassertpackage(out.stdout, "dep-init", p3, "p3-data", "p3-testdata");
|
||
cwdassertpackage(out.stdout, "testdep-init", p3,
|
||
"p3-data", "p3-testdata");
|
||
cwdassertpackage(out.stdout, "p3-external", p3,
|
||
"p3-data", "p3-testdata");
|
||
assert(!has(out.stdout, "p3-internal cwd="));
|
||
|
||
let listav: []str = [driver("ww_ww"), "test", "-list", "-I", tree, p3];
|
||
runcommandinputenvdir(root, "cwd-list", listav, wenv, caller,
|
||
stdinpath,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
cwdassertpackage(out.stdout, "dep-init", p3, "p3-data", "p3-testdata");
|
||
cwdassertpackage(out.stdout, "testdep-init", p3,
|
||
"p3-data", "p3-testdata");
|
||
assert(has(out.stdout, "p3.cwd_internal\n"));
|
||
assert(has(out.stdout, "p3.cwd_external\n"));
|
||
assert(!has(out.stdout, "p3-internal cwd="));
|
||
assert(!has(out.stdout, "p3-external cwd="));
|
||
|
||
let nomatchav: []str = [driver("ww"), "test", "-run", "no-such-test",
|
||
"-I", tree, p3];
|
||
runcommandinputenvdir(root, "cwd-no-match", nomatchav, cenv, caller,
|
||
stdinpath,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
cwdassertpackage(out.stdout, "dep-init", p3, "p3-data", "p3-testdata");
|
||
cwdassertpackage(out.stdout, "testdep-init", p3,
|
||
"p3-data", "p3-testdata");
|
||
assert(has(out.stdout, "[no matches]\n"));
|
||
assert(!has(out.stdout, "p3-internal cwd="));
|
||
assert(!has(out.stdout, "p3-external cwd="));
|
||
|
||
let failc: []str = [driver("ww"), "test", "-I", tree, "-I", failtree,
|
||
p7];
|
||
let failw: []str = [driver("ww_ww"), "test", "-I", tree, "-I",
|
||
failtree, p7];
|
||
runcommandinputenvdir(root, "cwd-fail-c", failc, cenv, caller,
|
||
stdinpath,
|
||
(60i64 * (time.second: i64)): time.duration, &outc);
|
||
runcommandinputenvdir(root, "cwd-fail-ww", failw, wenv, caller,
|
||
stdinpath,
|
||
(60i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 1);
|
||
expectexit(&outw, 1);
|
||
assert(same(outc.stdout, outw.stdout));
|
||
assert(same(outc.stderr, outw.stderr));
|
||
cwdassertpackage(outc.stdout, "p7-fail", p7, "p7-data", "p7-testdata");
|
||
assert(outc.stderr.len == 0);
|
||
assert(has(outc.stdout, strings.concat("FAIL ", p7,
|
||
" [p7] (test exit 1)\n")));
|
||
|
||
let timeoutc: []str = [driver("ww"), "test", "-timeout-ms=50", "-I",
|
||
tree, "-I", failtree, p8];
|
||
let timeoutw: []str = [driver("ww_ww"), "test", "-timeout-ms=50", "-I",
|
||
tree, "-I", failtree, p8];
|
||
runcommandinputenvdir(root, "cwd-timeout-c", timeoutc, cenv, caller,
|
||
stdinpath,
|
||
(60i64 * (time.second: i64)): time.duration, &outc);
|
||
runcommandinputenvdir(root, "cwd-timeout-ww", timeoutw, wenv, caller,
|
||
stdinpath,
|
||
(60i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 1);
|
||
expectexit(&outw, 1);
|
||
assert(same(outc.stdout, outw.stdout));
|
||
assert(same(outc.stderr, outw.stderr));
|
||
cwdassertpackage(outc.stdout, "p8-timeout", p8,
|
||
"p8-data", "p8-testdata");
|
||
assert(has(outc.stdout, "FAIL(timeout)\n"));
|
||
assert(outc.stderr.len == 0);
|
||
assert(has(outc.stdout, strings.concat("FAIL ", p8,
|
||
" [p8] (test exit 1)\n")));
|
||
|
||
let signalc: []str = [driver("ww"), "test", "-I", tree, "-I",
|
||
failtree, p9];
|
||
let signalw: []str = [driver("ww_ww"), "test", "-I", tree, "-I",
|
||
failtree, p9];
|
||
runcommandinputenvdir(root, "cwd-signal-c", signalc, cenv, caller,
|
||
stdinpath, (60i64 * (time.second: i64)): time.duration, &outc);
|
||
runcommandinputenvdir(root, "cwd-signal-ww", signalw, wenv, caller,
|
||
stdinpath, (60i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 1);
|
||
expectexit(&outw, 1);
|
||
assert(same(outc.stdout, outw.stdout));
|
||
assert(same(outc.stderr, outw.stderr));
|
||
cwdassertpackage(outc.stdout, "p9-signal", p9,
|
||
"p9-data", "p9-testdata");
|
||
assert(has(outc.stdout, "FAIL (signal 15)\n"));
|
||
assert(outc.stderr.len == 0);
|
||
assert(has(outc.stdout, strings.concat("FAIL ", p9,
|
||
" [p9] (test exit 1)\n")));
|
||
|
||
// Source-class rejection precedes every captured build or test child, so a
|
||
// nonempty caller input cannot create a partial persistent generation.
|
||
let rejectcwork: str = strings.concat(root, "/stdin-reject-c");
|
||
let rejectwwork: str = strings.concat(root, "/stdin-reject-ww");
|
||
assert(os.mkdir(rejectcwork, 448i32) == 0);
|
||
assert(os.mkdir(rejectwwork, 448i32) == 0);
|
||
let rejectcav: []str = [driver("ww"), "test", "-w", rejectcwork,
|
||
"-I", failtree, reject];
|
||
let rejectwav: []str = [driver("ww_ww"), "test", "-w", rejectwwork,
|
||
"-I", failtree, reject];
|
||
runcommandinputenvdir(root, "stdin-reject-c", rejectcav, cenv, caller,
|
||
stdinpath, (60i64 * (time.second: i64)): time.duration, &outc);
|
||
runcommandinputenvdir(root, "stdin-reject-ww", rejectwav, wenv, caller,
|
||
stdinpath, (60i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 1);
|
||
expectexit(&outw, 1);
|
||
assert(outc.stdout.len == 0 && outw.stdout.len == 0);
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(has(outc.stderr,
|
||
"test package must match production package or <package>_test\n"));
|
||
assert(directoryisempty(rejectcwork));
|
||
assert(directoryisempty(rejectwwork));
|
||
|
||
// Packages without selected test sources and `ww build` never start a test
|
||
// process, even if production initialization would make that visible.
|
||
let notestav: []str = [driver("ww_ww"), "test", "-I", tree,
|
||
plain, cmdno];
|
||
runcommandinputenvdir(root, "cwd-no-tests", notestav, wenv, caller,
|
||
stdinpath,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(!has(out.stdout, "plain-init"));
|
||
assert(!has(out.stdout, "cmdno-init"));
|
||
assert(!has(out.stdout, "cmdno-main"));
|
||
assert(!os.exists(strings.concat(plain, "/created.txt")));
|
||
assert(!os.exists(strings.concat(cmdno, "/created.txt")));
|
||
let commandbin: str = strings.concat(root, "/cmdno.bin");
|
||
let buildav: []str = [driver("ww"), "build", "-I", tree, "-o",
|
||
commandbin, cmdno];
|
||
runcommandinputenvdir(root, "cwd-build", buildav, cenv, caller,
|
||
stdinpath,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(os.exists(commandbin));
|
||
assert(!has(out.stdout, "cmdno-init") && !has(out.stdout, "cmdno-main"));
|
||
assert(!os.exists(strings.concat(cmdno, "/created.txt")));
|
||
|
||
// Compile-only paths do not run. A published binary has no embedded cwd or
|
||
// wrapper: direct invocation keeps the caller's cwd and duplicate PWDs.
|
||
assert(os.remove(strings.concat(p2, "/created.txt")) == 0);
|
||
let defaultcompile: []str = [driver("ww"), "test", "-c", "-I", tree, p2];
|
||
runcommandinputenvdir(root, "cwd-compile-default", defaultcompile, cenv,
|
||
caller, stdinpath,
|
||
(90i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let defaultbin: str = strings.concat(caller, "/p2.test");
|
||
assert(os.exists(defaultbin));
|
||
assert(!os.exists(strings.concat(p2, "/p2.test")));
|
||
assert(!os.exists(strings.concat(p2, "/created.txt")));
|
||
assert(!has(out.stdout, "dep-init") && !has(out.stdout, "p2-external"));
|
||
assert(os.remove(defaultbin) == 0);
|
||
|
||
assert(os.remove(strings.concat(p1, "/created.txt")) == 0);
|
||
let p1cbin: str = strings.concat(root, "/p1-c.test");
|
||
let p1wbin: str = strings.concat(root, "/p1-ww.test");
|
||
let compilec: []str = [driver("ww"), "test", "-c", "-o", p1cbin,
|
||
"-I", tree, p1];
|
||
let compilew: []str = [driver("ww_ww"), "test", "-c", "-o", p1wbin,
|
||
"-I", tree, p1];
|
||
runcommandinputenvdir(root, "cwd-compile-c", compilec, cenv, caller,
|
||
stdinpath,
|
||
(90i64 * (time.second: i64)): time.duration, &outc);
|
||
runcommandinputenvdir(root, "cwd-compile-ww", compilew, wenv, caller,
|
||
stdinpath,
|
||
(90i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 0);
|
||
expectexit(&outw, 0);
|
||
assert(!os.exists(strings.concat(p1, "/created.txt")));
|
||
assert(same(readfile(p1cbin), readfile(p1wbin)));
|
||
assert(!has(outc.stdout, "dep-init") && !has(outw.stdout, "dep-init"));
|
||
|
||
let directbin: []str = [p1cbin, "-package=p1"];
|
||
runcommandinputenvdir(root, "cwd-published-direct", directbin, cenv,
|
||
caller, stdinpath,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
cwdassertrecord(out.stdout, "dep-init", caller,
|
||
"/ww-cwd-inherited-first", "2", "no", "caller-data",
|
||
"caller-testdata", "data");
|
||
cwdassertrecord(out.stdout, "p1-internal", caller,
|
||
"/ww-cwd-inherited-first", "2", "no", "caller-data",
|
||
"caller-testdata", "data");
|
||
cwdassertsplitstreams(out.stdout, out.stderr, "dep-init", caller);
|
||
cwdassertsplitstreams(out.stdout, out.stderr, "p1-internal", caller);
|
||
assert(os.exists(strings.concat(caller, "/created.txt")));
|
||
assert(os.remove(strings.concat(caller, "/created.txt")) == 0);
|
||
|
||
// Raw single-file compatibility is deliberately outside the directory
|
||
// coordinator rule and retains its caller execution context.
|
||
let rawc: []str = [driver("ww"), "test", "-I", tree,
|
||
strings.concat(p4, "/internal_test.ww")];
|
||
let raww: []str = [driver("ww_ww"), "test", "-I", tree,
|
||
strings.concat(p4, "/internal_test.ww")];
|
||
runcommandinputenvdir(root, "cwd-raw-c", rawc, cbase, caller,
|
||
stdinpath,
|
||
(60i64 * (time.second: i64)): time.duration, &outc);
|
||
assert(os.remove(strings.concat(caller, "/created.txt")) == 0);
|
||
runcommandinputenvdir(root, "cwd-raw-ww", raww, wenv, caller,
|
||
stdinpath,
|
||
(60i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 0);
|
||
expectexit(&outw, 0);
|
||
assert(same(outc.stdout, outw.stdout));
|
||
assert(same(outc.stderr, outw.stderr));
|
||
cwdassertrecord(outc.stdout, "p4-internal-only", caller,
|
||
"/ww-cwd-inherited-first", "2", "yes", "caller-data",
|
||
"caller-testdata", "data");
|
||
cwdassertsplitstreams(outc.stdout, outc.stderr,
|
||
"p4-internal-only", caller);
|
||
assert(os.remove(strings.concat(caller, "/created.txt")) == 0);
|
||
cwdassertpackage(multiout, "dep-init", p2, "p2-data", "p2-testdata");
|
||
cwdassertpackage(multiout, "p2-external", p2,
|
||
"p2-data", "p2-testdata");
|
||
cwdassertpackage(multiout, "dep-init", p3, "p3-data", "p3-testdata");
|
||
cwdassertpackage(multiout, "testdep-init", p3,
|
||
"p3-data", "p3-testdata");
|
||
cwdassertpackage(multiout, "p3-internal", p3,
|
||
"p3-data", "p3-testdata");
|
||
cwdassertpackage(multiout, "p3-external", p3,
|
||
"p3-data", "p3-testdata");
|
||
cwdassertpackage(multiout, "p4-internal-only", p4,
|
||
"p4-data", "p4-testdata");
|
||
cwdassertpackage(multiout, "p5-external-only", p5,
|
||
"p5-data", "p5-testdata");
|
||
cwdassertpackage(multiout, "p6-internal-only", p6,
|
||
"p6-data", "p6-testdata");
|
||
cwdassertpackage(multiout, "p6-external-only", p6,
|
||
"p6-data", "p6-testdata");
|
||
let realrec: []str = [driver("ww"), "test", "-j", "1", "-I", tree,
|
||
strings.concat(tree, "/...")];
|
||
let linkrec: []str = [driver("ww_ww"), "test", "-j", "4", "-I",
|
||
"../tree-link", "../tree-link/..."];
|
||
runcommandinputenvdir(root, "cwd-recursive-real", realrec, cenv, caller,
|
||
stdinpath,
|
||
(120i64 * (time.second: i64)): time.duration, &outc);
|
||
runcommandinputenvdir(root, "cwd-recursive-link", linkrec, wenv, caller,
|
||
stdinpath,
|
||
(120i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 0);
|
||
expectexit(&outw, 0);
|
||
assert(same(outc.stdout, outw.stdout));
|
||
assert(same(outc.stderr, outw.stderr) && outc.stderr.len == 0);
|
||
assert(has(outc.stdout, strings.concat("? ", plain, " [no tests]\n")));
|
||
assert(has(outc.stdout, strings.concat("? ", cmdno, " [no tests]\n")));
|
||
assert(!has(outc.stdout, "plain-init"));
|
||
assert(!has(outc.stdout, "cmdno-init"));
|
||
assert(!has(outc.stdout, "cmdno-main"));
|
||
cwdassertpackage(outc.stdout, "p6-external-only", p6,
|
||
"p6-data", "p6-testdata");
|
||
|
||
let direct: []str = [driver("ww"), "test", "-I", tree, p1];
|
||
let equivalent: []str = [driver("ww_ww"), "test", "-j", "3", "-I",
|
||
"../tree-link", "../tree/p1", p1, "../tree/./p1/../p1",
|
||
"../tree-link/p1"];
|
||
runcommandinputenvdir(root, "cwd-direct", direct, cenv, caller,
|
||
stdinpath,
|
||
(60i64 * (time.second: i64)): time.duration, &outc);
|
||
runcommandinputenvdir(root, "cwd-equivalent", equivalent, wenv, caller,
|
||
stdinpath,
|
||
(60i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 0);
|
||
expectexit(&outw, 0);
|
||
assert(same(outc.stdout, outw.stdout));
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(occurrences(outc.stdout,
|
||
strings.concat("ok ", p1, " [p1]\n")) == 1);
|
||
cwdassertpackage(outc.stdout, "dep-init", p1, "p1-data", "p1-testdata");
|
||
cwdassertpackage(outc.stdout, "p1-internal", p1,
|
||
"p1-data", "p1-testdata");
|
||
|
||
// Tool wrappers observe the established build-tool context. Only the
|
||
// generated product binary gets the package cwd; compiler, assembler, and
|
||
// linker cwd/argv/locale/TMPDIR remain owned by the build plan.
|
||
let tooltrace: str = strings.concat(root, "/cwd-tools.trace");
|
||
let cw: str = strings.concat(root, "/cwd-w6c.sh");
|
||
let aw: str = strings.concat(root, "/cwd-w6a.sh");
|
||
let lw: str = strings.concat(root, "/cwd-w6l.sh");
|
||
writefile(tooltrace, "");
|
||
writeexecutable(cw, strings.concat(
|
||
"#!/bin/sh\n",
|
||
"printf 'compiler cwd=' >> \"$WW_CWD_TOOL_TRACE\"\n",
|
||
"/bin/pwd >> \"$WW_CWD_TOOL_TRACE\"\n",
|
||
"printf 'compiler env=LC_ALL:%s TMPDIR:%s\\n' \"$LC_ALL\" ",
|
||
"\"$TMPDIR\" >> \"$WW_CWD_TOOL_TRACE\"\n",
|
||
"if IFS= read -r WW_CWD_STDIN; then\n",
|
||
" printf 'compiler stdin=data\\n' >> \"$WW_CWD_TOOL_TRACE\"\n",
|
||
"else printf 'compiler stdin=eof\\n' >> \"$WW_CWD_TOOL_TRACE\"; fi\n",
|
||
"printf 'compiler argv' >> \"$WW_CWD_TOOL_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf ' <%s>' \"$arg\" >> ",
|
||
"\"$WW_CWD_TOOL_TRACE\"; done\n",
|
||
"printf '\\n' >> \"$WW_CWD_TOOL_TRACE\"\n",
|
||
"exec \"$WW_CWD_REAL_W6C\" \"$@\"\n"));
|
||
writeexecutable(aw, strings.concat(
|
||
"#!/bin/sh\n",
|
||
"printf 'assembler cwd=' >> \"$WW_CWD_TOOL_TRACE\"\n",
|
||
"/bin/pwd >> \"$WW_CWD_TOOL_TRACE\"\n",
|
||
"printf 'assembler env=LC_ALL:%s TMPDIR:%s\\n' \"$LC_ALL\" ",
|
||
"\"$TMPDIR\" >> \"$WW_CWD_TOOL_TRACE\"\n",
|
||
"if IFS= read -r WW_CWD_STDIN; then\n",
|
||
" printf 'assembler stdin=data\\n' >> \"$WW_CWD_TOOL_TRACE\"\n",
|
||
"else printf 'assembler stdin=eof\\n' >> \"$WW_CWD_TOOL_TRACE\"; fi\n",
|
||
"printf 'assembler argv' >> \"$WW_CWD_TOOL_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf ' <%s>' \"$arg\" >> ",
|
||
"\"$WW_CWD_TOOL_TRACE\"; done\n",
|
||
"printf '\\n' >> \"$WW_CWD_TOOL_TRACE\"\n",
|
||
"exec \"$WW_CWD_REAL_W6A\" \"$@\"\n"));
|
||
writeexecutable(lw, strings.concat(
|
||
"#!/bin/sh\n",
|
||
"printf 'linker cwd=' >> \"$WW_CWD_TOOL_TRACE\"\n",
|
||
"/bin/pwd >> \"$WW_CWD_TOOL_TRACE\"\n",
|
||
"printf 'linker env=LC_ALL:%s TMPDIR:%s\\n' \"$LC_ALL\" ",
|
||
"\"$TMPDIR\" >> \"$WW_CWD_TOOL_TRACE\"\n",
|
||
"if IFS= read -r WW_CWD_STDIN; then\n",
|
||
" printf 'linker stdin=data\\n' >> \"$WW_CWD_TOOL_TRACE\"\n",
|
||
"else printf 'linker stdin=eof\\n' >> \"$WW_CWD_TOOL_TRACE\"; fi\n",
|
||
"printf 'linker argv' >> \"$WW_CWD_TOOL_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf ' <%s>' \"$arg\" >> ",
|
||
"\"$WW_CWD_TOOL_TRACE\"; done\n",
|
||
"printf '\\n' >> \"$WW_CWD_TOOL_TRACE\"\n",
|
||
"exec \"$WW_CWD_REAL_W6L\" \"$@\"\n"));
|
||
let tracebase: []str = cwdtestenv("ww");
|
||
let traceenv: []str = alloc([], (tracebase.len + 7): u64)!;
|
||
i = 0;
|
||
for (i < tracebase.len) {
|
||
if (!strings.hasprefix(tracebase[i], "WW_W6C=")
|
||
&& !strings.hasprefix(tracebase[i], "WW_W6A=")
|
||
&& !strings.hasprefix(tracebase[i], "WW_W6L=")) {
|
||
append(traceenv, tracebase[i]);
|
||
};
|
||
i += 1;
|
||
};
|
||
append(traceenv, strings.concat("WW_W6C=", cw));
|
||
append(traceenv, strings.concat("WW_W6A=", aw));
|
||
append(traceenv, strings.concat("WW_W6L=", lw));
|
||
append(traceenv, strings.concat("WW_CWD_REAL_W6C=", driver("w6c")));
|
||
append(traceenv, strings.concat("WW_CWD_REAL_W6A=", driver("w6a")));
|
||
append(traceenv, strings.concat("WW_CWD_REAL_W6L=", driver("w6l")));
|
||
append(traceenv, strings.concat("WW_CWD_TOOL_TRACE=", tooltrace));
|
||
|
||
let work: str = strings.concat(root, "/cwd-work");
|
||
assert(os.mkdir(work, 448i32) == 0);
|
||
let persistav: []str = [driver("ww"), "test", "-w", work, "-I", tree,
|
||
p1];
|
||
runcommandinputenvdir(root, "cwd-persist-cold", persistav, traceenv,
|
||
caller, stdinpath,
|
||
(120i64 * (time.second: i64)): time.duration, &outc);
|
||
expectexit(&outc, 0);
|
||
cwdassertpackage(outc.stdout, "dep-init", p1, "p1-data", "p1-testdata");
|
||
cwdassertpackage(outc.stdout, "p1-internal", p1,
|
||
"p1-data", "p1-testdata");
|
||
let coldtrace: str = readfile(tooltrace);
|
||
assert(has(coldtrace, strings.concat("compiler cwd=", caller, "\n")));
|
||
assert(has(coldtrace, strings.concat("assembler cwd=", caller, "\n")));
|
||
assert(has(coldtrace, strings.concat("linker cwd=", caller, "\n")));
|
||
assert(!has(coldtrace, strings.concat("cwd=", p1, "\n")));
|
||
assert(has(coldtrace, "compiler env=LC_ALL:C TMPDIR:/tmp/"));
|
||
assert(has(coldtrace, "assembler env=LC_ALL:C TMPDIR:/tmp/"));
|
||
assert(has(coldtrace, "linker env=LC_ALL:C TMPDIR:/tmp/"));
|
||
assert(has(coldtrace, "compiler stdin=eof\n"));
|
||
assert(has(coldtrace, "assembler stdin=eof\n"));
|
||
assert(has(coldtrace, "linker stdin=eof\n"));
|
||
assert(!has(coldtrace, "stdin=data\n"));
|
||
assert(has(coldtrace, "compiler argv <"));
|
||
assert(has(coldtrace, ".unit.new>"));
|
||
assert(has(coldtrace, "assembler argv <"));
|
||
assert(has(coldtrace, "linker argv <"));
|
||
assert(has(coldtrace, ".a.new>"));
|
||
|
||
let artifactpaths: []str = [
|
||
strings.concat(work, "/p1-internal-test.unit.ww"),
|
||
strings.concat(work, "/p1-internal-test.wwi"),
|
||
strings.concat(work, "/p1-internal-test.s"),
|
||
strings.concat(work, "/p1-internal-test.o"),
|
||
strings.concat(work, "/p1-internal-test.a"),
|
||
strings.concat(work, "/p1-test-main.unit.ww"),
|
||
strings.concat(work, "/p1-test-main.init.unit.ww"),
|
||
strings.concat(work, "/p1-test-main.wwi"),
|
||
strings.concat(work, "/p1-test-main.s"),
|
||
strings.concat(work, "/p1-test-main.init.s"),
|
||
strings.concat(work, "/p1-test-main.o"),
|
||
strings.concat(work, "/p1-test-main.init.o"),
|
||
strings.concat(work, "/p1-test-main.a"),
|
||
strings.concat(work, "/dep.wwi"),
|
||
strings.concat(work, "/dep.a"),
|
||
strings.concat(work, "/probe.wwi"),
|
||
strings.concat(work, "/probe.a")
|
||
];
|
||
let artifacts: []str = alloc([], artifactpaths.len: u64)!;
|
||
i = 0;
|
||
for (i < artifactpaths.len) {
|
||
append(artifacts, strings.dup(readfile(artifactpaths[i])));
|
||
i += 1;
|
||
};
|
||
|
||
rewritefile(tooltrace, "");
|
||
runcommandinputenvdir(root, "cwd-persist-warm", persistav, traceenv,
|
||
caller, stdinpath,
|
||
(120i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outw, 0);
|
||
assert(same(outc.stdout, outw.stdout));
|
||
let warmtrace: str = readfile(tooltrace);
|
||
assert(!has(warmtrace, "compiler cwd="));
|
||
assert(!has(warmtrace, "assembler cwd="));
|
||
assert(occurrences(warmtrace, "linker cwd=") == 1);
|
||
assert(has(warmtrace, strings.concat("linker cwd=", caller, "\n")));
|
||
assert(has(warmtrace, "linker stdin=eof\n"));
|
||
assert(!has(warmtrace, "stdin=data\n"));
|
||
|
||
// Non-source data does not enter graph/action/storage bytes. The established
|
||
// warm final relink remains one invocation, while the next uncached runtime
|
||
// observes the new relative data immediately.
|
||
rewritefile(strings.concat(p1, "/data.txt"), "p1-data-v2\n");
|
||
rewritefile(strings.concat(p1, "/testdata/input.txt"),
|
||
"p1-testdata-v2\n");
|
||
let changedstdin: []u8 = alloc([], 4096u64)!;
|
||
inputi = 0;
|
||
for (inputi < 2048) {
|
||
append(changedstdin, 'Y');
|
||
append(changedstdin, '\n');
|
||
inputi += 1;
|
||
};
|
||
rewritefile(stdinpath, strings.frombytes(changedstdin));
|
||
rewritefile(tooltrace, "");
|
||
runcommandinputenvdir(root, "cwd-persist-data", persistav, traceenv,
|
||
caller, stdinpath,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
cwdassertpackage(out.stdout, "dep-init", p1,
|
||
"p1-data-v2", "p1-testdata-v2");
|
||
cwdassertpackage(out.stdout, "p1-internal", p1,
|
||
"p1-data-v2", "p1-testdata-v2");
|
||
let datatrace: str = readfile(tooltrace);
|
||
assert(!has(datatrace, "compiler cwd="));
|
||
assert(!has(datatrace, "assembler cwd="));
|
||
assert(occurrences(datatrace, "linker cwd=") == 1);
|
||
assert(has(datatrace, strings.concat("linker cwd=", caller, "\n")));
|
||
assert(has(datatrace, "linker stdin=eof\n"));
|
||
assert(!has(datatrace, "stdin=data\n"));
|
||
i = 0;
|
||
for (i < artifactpaths.len) {
|
||
assert(same(artifacts[i], readfile(artifactpaths[i])));
|
||
i += 1;
|
||
};
|
||
assert(!directoryhasnew(work));
|
||
assert(!os.exists(strings.concat(work, "/p1.result")));
|
||
assert(!os.exists(strings.concat(work, "/p1.output")));
|
||
assert(!os.exists(strings.concat(work, "/p1.status")));
|
||
assert(!os.exists(strings.concat(work, "/p1.voucher")));
|
||
let p1after: str = strings.concat(root, "/p1-after-data.test");
|
||
let afterav: []str = [driver("ww_ww"), "test", "-c", "-o", p1after,
|
||
"-I", tree, p1];
|
||
runcommandinputenvdir(root, "cwd-after-data-binary", afterav, wenv,
|
||
caller, stdinpath,
|
||
(90i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(same(readfile(p1cbin), readfile(p1after)));
|
||
|
||
// The builder wrapper creates a deterministic post-build/pre-run boundary.
|
||
// Removing one owned source directory there must be a child setup error;
|
||
// the sibling still runs from its own directory and both stages agree.
|
||
let hidewrapper: str = strings.concat(root, "/build-then-hide.sh");
|
||
writeexecutable(hidewrapper, strings.concat(
|
||
"#!/bin/sh\n",
|
||
"\"$WW_CWD_REAL_DRIVER\" \"$@\"\n",
|
||
"status=$?\n",
|
||
"if [ \"$status\" -eq 0 ]; then\n",
|
||
" mv -- \"$WW_CWD_HIDE_DIR\" \"$WW_CWD_HIDE_DIR.gone\" || exit 125\n",
|
||
" printf 'wrapper-hidden=%s\\n' \"$WW_CWD_HIDE_DIR\" >&2\n",
|
||
"fi\n",
|
||
"exit \"$status\"\n"));
|
||
let hidestages: []str = ["ww", "ww_ww"];
|
||
let hidestdout: []str = ["", ""];
|
||
let hidestderr: []str = ["", ""];
|
||
i = 0;
|
||
for (i < hidestages.len) {
|
||
let base: []str = cwdtestenv(hidestages[i]);
|
||
let hideenv: []str = alloc([], (base.len + 2): u64)!;
|
||
let j: i32 = 0;
|
||
for (j < base.len) { append(hideenv, base[j]); j += 1; };
|
||
append(hideenv, strings.concat("WW_CWD_REAL_DRIVER=",
|
||
driver(hidestages[i])));
|
||
append(hideenv, strings.concat("WW_CWD_HIDE_DIR=", p1));
|
||
let hideav: []str = [driver("wwtest"), "package", "--ww-driver",
|
||
hidewrapper, "-j", "2", "-I", tree, p1, p2];
|
||
runcommandinputenvdir(root, strings.concat("cwd-hide-", hidestages[i]),
|
||
hideav, hideenv, caller, stdinpath,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, strings.concat("wrapper-hidden=", p1, "\n")));
|
||
assert(has(out.stdout, strings.concat("FAIL ", p1,
|
||
" [p1] (test harness error 2)\n")));
|
||
assert(!has(out.stdout, "p1-internal cwd="));
|
||
cwdassertpackage(out.stdout, "dep-init", p2,
|
||
"p2-data", "p2-testdata");
|
||
cwdassertpackage(out.stdout, "p2-external", p2,
|
||
"p2-data", "p2-testdata");
|
||
assert(os.rename(strings.concat(p1, ".gone"), p1) == 0);
|
||
assert(!os.exists(strings.concat(p1, ".gone")));
|
||
hidestdout[i] = strings.dup(out.stdout);
|
||
hidestderr[i] = strings.dup(out.stderr);
|
||
i += 1;
|
||
};
|
||
assert(same(hidestdout[0], hidestdout[1]));
|
||
assert(same(hidestderr[0], hidestderr[1]));
|
||
assert(!directoryhasnew(work));
|
||
assert(same(readfile(strings.concat(caller, "/sentinel")),
|
||
"parent-sentinel\n"));
|
||
assert(!os.exists(strings.concat(caller, "/created.txt")));
|
||
clean(root);
|
||
};
|
||
|
||
@test fn 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, "ok "));
|
||
assert(!has(out.stdout, " [no tests]\n"));
|
||
assert(!has(out.stdout, "phantom"));
|
||
|
||
// Go's complete empty-package matrix: production-only, internal-only,
|
||
// external-only, combined, test-only same, test-only external, and honest
|
||
// mixed test-only directories are each one directory-owned result.
|
||
let prodonly: str = strings.concat(root, "/shapeprod");
|
||
let internalonly: str = strings.concat(root, "/shapeinternal");
|
||
let externalonly: str = strings.concat(root, "/shapeexternal");
|
||
let both: str = strings.concat(root, "/shapeboth");
|
||
let testonlysame: str = strings.concat(root, "/tosame");
|
||
let testonlyexternal: str = strings.concat(root,
|
||
"/toexternal");
|
||
let testonlymixed: str = strings.concat(root, "/tomixed");
|
||
let shapes: []str = [prodonly, internalonly, externalonly, both,
|
||
testonlysame, testonlyexternal, testonlymixed];
|
||
let shi: i32 = 0;
|
||
for (shi < shapes.len) {
|
||
assert(os.mkdir(shapes[shi], 448i32) == 0);
|
||
shi += 1;
|
||
};
|
||
writefile(strings.concat(prodonly, "/prod.ww"),
|
||
"package shapeprod;\nexport fn value() i32 = { return 1; };\n");
|
||
writefile(strings.concat(internalonly, "/prod.ww"),
|
||
"package shapeinternal;\nfn value() i32 = { return 1; };\n");
|
||
writefile(strings.concat(internalonly, "/same_test.ww"), strings.concat(
|
||
"package shapeinternal;\n",
|
||
"@test fn internal_runs() void = { assert(value() == 1); };\n"));
|
||
writefile(strings.concat(externalonly, "/prod.ww"),
|
||
"package shapeexternal;\nexport fn value() i32 = { return 1; };\n");
|
||
writefile(strings.concat(externalonly, "/external_test.ww"), strings.concat(
|
||
"package shapeexternal_test;\nimport shapeexternal;\n",
|
||
"@test fn external_runs() void = { assert(shapeexternal.value() == 1); };\n"));
|
||
writefile(strings.concat(both, "/prod.ww"),
|
||
"package shapeboth;\nexport fn value() i32 = { return 1; };\n");
|
||
writefile(strings.concat(both, "/same_test.ww"), strings.concat(
|
||
"package shapeboth;\n",
|
||
"export fn helper() i32 = { return 2; };\n",
|
||
"@test fn same_runs() void = { assert(value() == 1); };\n"));
|
||
writefile(strings.concat(both, "/external_test.ww"), strings.concat(
|
||
"package shapeboth_test;\nimport shapeboth;\n",
|
||
"@test fn external_runs() void = { assert(shapeboth.helper() == 2); };\n"));
|
||
writefile(strings.concat(testonlysame, "/same_test.ww"),
|
||
"package tosame;\n@test fn same_only() void = { assert(true); };\n");
|
||
writefile(strings.concat(testonlyexternal, "/external_test.ww"),
|
||
"package toexternal_test;\n@test fn external_only() void = { assert(true); };\n");
|
||
writefile(strings.concat(testonlymixed, "/same_test.ww"), strings.concat(
|
||
"package tomixed;\nexport fn helper() i32 = { return 3; };\n",
|
||
"@test fn same_test_only() void = { assert(helper() == 3); };\n"));
|
||
writefile(strings.concat(testonlymixed, "/external_test.ww"), strings.concat(
|
||
"package tomixed_test;\nimport tomixed;\n",
|
||
"@test fn external_test_only() void = { assert(tomixed.helper() == 3); };\n"));
|
||
let shaperefs: []str = ["", "", "", "", "", "", ""];
|
||
shi = 0;
|
||
for (shi < shapes.len) {
|
||
let stagei: i32 = 0;
|
||
for (stagei < stages.len) {
|
||
let shapeav: []str = [driver(stages[stagei]), "test", "-I", root,
|
||
shapes[shi]];
|
||
runcommand(root, strings.concat("shape-", stages[stagei], "-",
|
||
boundarypkgname(shi)), shapeav,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
if (stagei == 0) { shaperefs[shi] = strings.dup(out.stdout); }
|
||
else { assert(same(shaperefs[shi], out.stdout)); };
|
||
assert(out.stderr.len == 0);
|
||
stagei += 1;
|
||
};
|
||
shi += 1;
|
||
};
|
||
assert(has(shaperefs[0], " [no tests]\n"));
|
||
assert(occurrences(shaperefs[0], strings.concat(
|
||
"? ", prodonly, " [no tests]\n")) == 1);
|
||
assert(has(shaperefs[1], "shapeinternal.internal_runs ... ok\n"));
|
||
assert(has(shaperefs[2], "shapeexternal.external_runs ... ok\n"));
|
||
assert(occurrences(shaperefs[3], " ... ok\n") == 2);
|
||
assert(has(shaperefs[4], "tosame.same_only ... ok\n"));
|
||
assert(has(shaperefs[5], "toexternal.external_only ... ok\n"));
|
||
assert(has(shaperefs[6], "tomixed.same_test_only ... ok\n"));
|
||
assert(has(shaperefs[6], "tomixed.external_test_only ... ok\n"));
|
||
let shapefamilies: []str = ["shapeprod", "shapeinternal",
|
||
"shapeexternal", "shapeboth", "tosame", "toexternal", "tomixed"];
|
||
shi = 1;
|
||
for (shi < shapes.len) {
|
||
assert(occurrences(shaperefs[shi], strings.concat(
|
||
"ok ", shapes[shi], " [", shapefamilies[shi], "]\n")) == 1);
|
||
shi += 1;
|
||
};
|
||
|
||
// The five non-combined real-test shapes have one structural product in
|
||
// both stages. The combined case is proved in full by the graph observer.
|
||
let shapecompilerwrapper: str = strings.concat(root,
|
||
"/shape-w6c-wrapper.sh");
|
||
let shapelinkerwrapper: str = strings.concat(root,
|
||
"/shape-w6l-wrapper.sh");
|
||
writeexecutable(shapecompilerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_SHAPE_COMPILER_TRACE\"\n",
|
||
"exec \"$WW_SHAPE_REAL_COMPILER\" \"$@\"\n"));
|
||
writeexecutable(shapelinkerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_SHAPE_LINKER_TRACE\"\n",
|
||
"exec \"$WW_SHAPE_REAL_LINKER\" \"$@\"\n"));
|
||
let shapeindices: []i32 = [1, 2, 4, 5, 6];
|
||
let shapemains: []str = ["shapeinternal-test-main",
|
||
"shapeexternal-test-main", "tosame-test-main",
|
||
"toexternal-test-main", "tomixed-test-main"];
|
||
let shapeordinary: []str = ["shapeinternal", "shapeexternal", "tosame",
|
||
"toexternal", "tomixed"];
|
||
let shapeordinarypresent: []bool = [false, true, false, false, false];
|
||
let shapeinternal: []str = ["shapeinternal-internal-test",
|
||
"shapeexternal-internal-test", "tosame-internal-test",
|
||
"toexternal-internal-test", "tomixed-internal-test"];
|
||
let shapeinternalpresent: []bool = [true, false, true, false, true];
|
||
let shapeexternal: []str = ["shapeinternal_test-external-test",
|
||
"shapeexternal_test-external-test", "tosame_test-external-test",
|
||
"toexternal_test-external-test", "tomixed_test-external-test"];
|
||
let shapeexternalpresent: []bool = [false, true, false, true, true];
|
||
let shapetargetfirst: []str = ["shapeinternal", "shapeexternal", "tosame",
|
||
"toexternal_test", "tomixed"];
|
||
let shapetargetsecond: []str = ["", "shapeexternal_test", "", "",
|
||
"tomixed_test"];
|
||
let shapeproductionselector: []str = ["shapeinternal", "shapeexternal",
|
||
"-", "-", "-"];
|
||
let shapeinternalselector: []str = ["shapeinternal", "-", "tosame",
|
||
"-", "tomixed"];
|
||
let shapeexternalselector: []str = ["-", "shapeexternal_test", "-",
|
||
"toexternal_test", "tomixed_test"];
|
||
let shapecompilerrefs: []str = ["", "", "", "", ""];
|
||
let shapelinkerrefs: []str = ["", "", "", "", ""];
|
||
let shapemainrefs: []str = ["", "", "", "", ""];
|
||
let shapebinrefs: []str = ["", "", "", "", ""];
|
||
let shapecompilers: []str = ["w6c", "w6c_ww"];
|
||
let shapelinkers: []str = ["w6l", "w6l_ww"];
|
||
let shapebaseenv: []str = os.getenvs();
|
||
let sj: i32 = 0;
|
||
for (sj < shapeindices.len) {
|
||
let index: i32 = shapeindices[sj];
|
||
let shapebin: str = strings.concat(root, "/shape-product-",
|
||
boundarypkgname(sj), ".test");
|
||
let shapework: str = strings.concat(shapebin, ".sepwork");
|
||
let shapestatus: str = strings.concat(shapebin, ".status");
|
||
let ctracepath: str = strings.concat(root, "/shape-compiler-",
|
||
boundarypkgname(sj), ".trace");
|
||
let ltracepath: str = strings.concat(root, "/shape-linker-",
|
||
boundarypkgname(sj), ".trace");
|
||
writefile(ctracepath, "");
|
||
writefile(ltracepath, "");
|
||
let stagei: i32 = 0;
|
||
for (stagei < stages.len) {
|
||
assert(os.mkdir(shapework, 448i32) == 0);
|
||
if (stagei != 0) {
|
||
rewritefile(ctracepath, "");
|
||
rewritefile(ltracepath, "");
|
||
};
|
||
let env: []str = alloc([], (shapebaseenv.len + 6): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < shapebaseenv.len) {
|
||
if (!strings.hasprefix(shapebaseenv[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(shapebaseenv[ei], "WW_W6L=")
|
||
&& !strings.hasprefix(shapebaseenv[ei],
|
||
"WW_SHAPE_COMPILER_TRACE=")
|
||
&& !strings.hasprefix(shapebaseenv[ei],
|
||
"WW_SHAPE_LINKER_TRACE=")
|
||
&& !strings.hasprefix(shapebaseenv[ei],
|
||
"WW_SHAPE_REAL_COMPILER=")
|
||
&& !strings.hasprefix(shapebaseenv[ei],
|
||
"WW_SHAPE_REAL_LINKER=")) {
|
||
append(env, shapebaseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(env, strings.concat("WW_W6C=", shapecompilerwrapper));
|
||
append(env, strings.concat("WW_W6L=", shapelinkerwrapper));
|
||
append(env, strings.concat("WW_SHAPE_COMPILER_TRACE=", ctracepath));
|
||
append(env, strings.concat("WW_SHAPE_LINKER_TRACE=", ltracepath));
|
||
append(env, strings.concat("WW_SHAPE_REAL_COMPILER=",
|
||
driver(shapecompilers[stagei])));
|
||
append(env, strings.concat("WW_SHAPE_REAL_LINKER=",
|
||
driver(shapelinkers[stagei])));
|
||
let shapeav: []str = [driver(stages[stagei]), "test", "-c", "-w",
|
||
shapework, "-I", root,
|
||
"--ww-package-test", "test", shapefamilies[index],
|
||
shapeproductionselector[sj], shapeinternalselector[sj],
|
||
shapeexternalselector[sj], shapes[index], shapebin, "-",
|
||
shapestatus, shapes[index]];
|
||
runcommandenv(root, strings.concat("shape-structure-",
|
||
stages[stagei], "-", boundarypkgname(sj)), shapeav, env,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stderr.len == 0);
|
||
assert(os.exists(shapebin));
|
||
assert(!os.exists(strings.concat(shapes[index], "/",
|
||
shapefamilies[index], ".test")));
|
||
let ctrace: str = readfile(ctracepath);
|
||
let ltrace: str = readfile(ltracepath);
|
||
assert(occurrences(ctrace, "-test-main.unit.new") == 1);
|
||
let mainline: str = linecontaining(ctrace,
|
||
strings.concat("/", shapemains[sj], ".unit.new"));
|
||
let targetcount: i32 = 1;
|
||
if (shapetargetsecond[sj].len != 0) { targetcount = 2; };
|
||
assert(occurrences(mainline, "--test-target-package")
|
||
== targetcount);
|
||
assert(has(mainline, strings.concat("--test-target-package ",
|
||
shapetargetfirst[sj], " ")));
|
||
if (shapetargetsecond[sj].len != 0) {
|
||
assert(pos(mainline, strings.concat("--test-target-package ",
|
||
shapetargetfirst[sj], " ")) < pos(mainline, strings.concat(
|
||
"--test-target-package ", shapetargetsecond[sj], " ")));
|
||
};
|
||
assert(occurrences(ltrace, "\n") == 1);
|
||
let linkline: str = linecontaining(ltrace,
|
||
strings.concat("-o ", shapebin, ".new "));
|
||
assert(occurrences(linkline, "-test-main.a") == 1);
|
||
assert(has(linkline, strings.concat("/", shapemains[sj],
|
||
".a")));
|
||
assert(!has(linkline, ".wwi"));
|
||
assert(os.exists(strings.concat(shapework, "/", shapemains[sj],
|
||
".unit.ww")));
|
||
let actionnames: []str = [shapeordinary[sj], shapeinternal[sj],
|
||
shapeexternal[sj]];
|
||
let actionpresent: []bool = [shapeordinarypresent[sj],
|
||
shapeinternalpresent[sj], shapeexternalpresent[sj]];
|
||
let actioni: i32 = 0;
|
||
for (actioni < actionnames.len) {
|
||
assert(os.exists(strings.concat(shapework, "/",
|
||
actionnames[actioni], ".unit.ww"))
|
||
== actionpresent[actioni]);
|
||
assert(has(linkline, strings.concat("/", actionnames[actioni],
|
||
".a")) == actionpresent[actioni]);
|
||
actioni += 1;
|
||
};
|
||
let nc: str = normalizedtrace(ctrace,
|
||
strings.concat(shapework, "/"), shapebin);
|
||
let nl: str = normalizedtrace(ltrace,
|
||
strings.concat(shapework, "/"), shapebin);
|
||
let mainbytes: str = readfile(strings.concat(shapework, "/",
|
||
shapemains[sj], ".unit.ww"));
|
||
let binbytes: str = readfile(shapebin);
|
||
if (stagei == 0) {
|
||
shapecompilerrefs[sj] = nc;
|
||
shapelinkerrefs[sj] = nl;
|
||
shapemainrefs[sj] = mainbytes;
|
||
shapebinrefs[sj] = binbytes;
|
||
} else {
|
||
assert(same(shapecompilerrefs[sj], nc));
|
||
assert(same(shapelinkerrefs[sj], nl));
|
||
assert(same(shapemainrefs[sj], mainbytes));
|
||
assert(same(shapebinrefs[sj], binbytes));
|
||
};
|
||
clean(shapework);
|
||
clean(shapebin);
|
||
clean(shapestatus);
|
||
stagei += 1;
|
||
};
|
||
sj += 1;
|
||
};
|
||
clean(root);
|
||
};
|
||
|
||
@test fn symlink_package_root_reuses_canonical_directory() void = {
|
||
let root: str = fresh();
|
||
let real: str = deeppunctuationpath(root, "symlink target");
|
||
let link: str = strings.concat(root, "/link");
|
||
let wrapper: str = strings.concat(root, "/compiler wrapper.sh");
|
||
mkdirall(real);
|
||
writefile(strings.concat(real, "/real.ww"),
|
||
"package real;\nexport fn value() int = { return 1; };\n");
|
||
writefile(strings.concat(real, "/real_test.ww"),
|
||
"package real;\n@test fn value_is_one() void = { assert(value() == 1); };\n");
|
||
let lav: []str = ["/bin/ln", "-s", real, link];
|
||
let out: commandout;
|
||
runcommand(root, "link", lav, time.second, &out);
|
||
expectexit(&out, 0);
|
||
writeexecutable(wrapper, strings.concat(
|
||
"#!/bin/sh\n",
|
||
"printf 'BEGIN' >> \"$WW_SYMLINK_COMPILER_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||
"\"$WW_SYMLINK_COMPILER_TRACE\"; done\n",
|
||
"printf '\\n' >> \"$WW_SYMLINK_COMPILER_TRACE\"\n",
|
||
"exec \"$WW_SYMLINK_REAL_COMPILER\" \"$@\"\n"));
|
||
let actionid: str = localidentity(real, "real");
|
||
assert(actionid.len > 255);
|
||
let action: str = packagestoragekey(actionid, real, 1, 0);
|
||
assert(action.len + 9 <= 255);
|
||
let stages: []str = ["ww", "ww_ww"];
|
||
let compilers: []str = ["w6c", "w6c_ww"];
|
||
let references: []str = ["", "", ""];
|
||
let baseenv: []str = os.getenvs();
|
||
let si: i32 = 0;
|
||
for (si < stages.len) {
|
||
let work: str = strings.concat(root, "/work-", stages[si]);
|
||
let trace: str = strings.concat(root, "/trace-", stages[si]);
|
||
assert(os.mkdir(work, 448i32) == 0);
|
||
writefile(trace, "");
|
||
let env: []str = alloc([], (baseenv.len + 3): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < baseenv.len) {
|
||
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_SYMLINK_COMPILER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_SYMLINK_REAL_COMPILER=")) {
|
||
append(env, baseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(env, strings.concat("WW_W6C=", wrapper));
|
||
append(env, strings.concat("WW_SYMLINK_COMPILER_TRACE=", trace));
|
||
append(env, strings.concat("WW_SYMLINK_REAL_COMPILER=",
|
||
driver(compilers[si])));
|
||
let av: []str = [driver(stages[si]), "test", "-w", work, real];
|
||
runcommandenv(root, strings.concat("real-cold-", stages[si]), av, env,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout, "real.value_is_one ... ok\n"));
|
||
let coldtrace: str = readfile(trace);
|
||
assert(coldtrace.len > 0);
|
||
assert(has(coldtrace, strings.concat("<--import><", actionid, "><")));
|
||
let key: str = work;
|
||
assert(os.exists(key));
|
||
let suffixes: []str = [".unit.ww", ".wwi", ".a"];
|
||
let xi: i32 = 0;
|
||
for (xi < suffixes.len) {
|
||
let artifact: str = readfile(strings.concat(key, "/", action,
|
||
suffixes[xi]));
|
||
if (xi == 0) {
|
||
assert(has(artifact, strings.concat("//ww:module-reset ",
|
||
actionid, "\n")));
|
||
} else { if (xi == 1) {
|
||
assert(strings.hasprefix(artifact, strings.concat("//ww:module ",
|
||
actionid, "\n")));
|
||
}; };
|
||
if (si == 0) { references[xi] = strings.dup(artifact); }
|
||
else { assert(same(references[xi], artifact)); };
|
||
xi += 1;
|
||
};
|
||
let linkav: []str = [driver(stages[si]), "test", "-w", work, link];
|
||
runcommandenv(root, strings.concat("symlink-warm-", stages[si]),
|
||
linkav, env, (60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout, "real.value_is_one ... ok\n"));
|
||
assert(same(coldtrace, readfile(trace)));
|
||
xi = 0;
|
||
for (xi < suffixes.len) {
|
||
assert(same(references[xi], readfile(strings.concat(key, "/",
|
||
action, suffixes[xi]))));
|
||
xi += 1;
|
||
};
|
||
let relative: str = strings.concat(".",
|
||
strings.sub(real, root.len, real.len));
|
||
let relativeav: []str = [driver(stages[si]), "test", "-w", work,
|
||
relative];
|
||
runcommandenvdir(root, strings.concat("relative-warm-", stages[si]),
|
||
relativeav, env, root,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout, "real.value_is_one ... ok\n"));
|
||
assert(same(coldtrace, readfile(trace)));
|
||
xi = 0;
|
||
for (xi < suffixes.len) {
|
||
assert(same(references[xi], readfile(strings.concat(key, "/",
|
||
action, suffixes[xi]))));
|
||
xi += 1;
|
||
};
|
||
si += 1;
|
||
};
|
||
clean(root);
|
||
};
|
||
|
||
@test fn pass_skip_and_expected_abort() void = {
|
||
let root: str = fresh();
|
||
let av: []str = [driver("ww"), "test", packagepath("cases/pass")];
|
||
let out: commandout;
|
||
runcommand(root, "pass", av,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout, "pass.alpha_pass ... ok\n"));
|
||
assert(has(out.stdout,
|
||
"pass.beta_skip ... SKIP: synthetic unavailable feature\n"));
|
||
assert(has(out.stdout, "pass.gamma_expected_abort ... ok\n"));
|
||
assert(has(out.stdout, "pass.delta_pass ... ok\n"));
|
||
assert(has(out.stdout,
|
||
"3 passed, 0 failed, 1 skipped, 0 harness errors\n"));
|
||
assert(has(out.stdout,
|
||
"4 discovered, 4 selected, 4 started, 4 completed\n"));
|
||
clean(root);
|
||
};
|
||
|
||
@test fn failures_are_distinct_and_clamped() void = {
|
||
let root: str = fresh();
|
||
let out: commandout;
|
||
let av: []str = [driver("ww"), "test",
|
||
packagepath("cases/assert_failure")];
|
||
runcommand(root, "assert", av,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stdout, strings.concat(
|
||
"assert_failure.assertion_failure ... synthetic assertion failure",
|
||
"FAIL (exit 1)\n")));
|
||
assert(out.stderr.len == 0);
|
||
|
||
let nonzeroav: []str = [driver("ww"), "test",
|
||
packagepath("cases/nonzero")];
|
||
runcommand(root, "nonzero", nonzeroav,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stdout,
|
||
"nonzero.deliberate_nonzero_exit ... FAIL (exit 7)\n"));
|
||
assert(out.stderr.len == 0);
|
||
|
||
let prematureav: []str = [driver("ww"), "test",
|
||
packagepath("cases/premature")];
|
||
runcommand(root, "premature", prematureav,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stdout,
|
||
"premature.clean_exit_without_completion ... HARNESS (incomplete result)\n"));
|
||
assert(out.stderr.len == 0);
|
||
|
||
let signalav: []str = [driver("ww"), "test",
|
||
packagepath("cases/signal")];
|
||
runcommand(root, "signal", signalav,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stdout,
|
||
"signal.signal_is_not_exit ... FAIL (signal 15)\n"));
|
||
assert(out.stderr.len == 0);
|
||
|
||
let multiav: []str = [driver("ww"), "test",
|
||
packagepath("cases/multi_fail")];
|
||
runcommand(root, "multifail", multiav,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stdout,
|
||
"1 passed, 2 failed, 0 skipped, 0 harness errors\n"));
|
||
assert(has(out.stdout,
|
||
"3 discovered, 3 selected, 3 started, 3 completed\n"));
|
||
assert(out.stderr.len == 0);
|
||
clean(root);
|
||
};
|
||
|
||
@test fn timeout_and_descendant_cleanup() void = {
|
||
let root: str = fresh();
|
||
let av: []str = [driver("ww"), "test", "-timeout-ms=100",
|
||
packagepath("cases/timeout")];
|
||
let out: commandout;
|
||
runcommand(root, "timeout", av,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stdout,
|
||
"timeout.timeout_clears_term_resistant_descendant ... FAIL(timeout)\n"));
|
||
assert(has(out.stdout,
|
||
"1 discovered, 1 selected, 1 started, 1 completed\n"));
|
||
|
||
let descendantav: []str = [driver("ww"), "test",
|
||
packagepath("cases/descendant")];
|
||
runcommand(root, "descendant", descendantav,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout,
|
||
"descendant.normal_return_clears_descendant ... ok\n"));
|
||
assert(!has(out.stdout, "HARNESS"));
|
||
clean(root);
|
||
};
|
||
|
||
@test fn cstage_wwstage_behavior_and_assembly_match() void = {
|
||
let root: str = fresh();
|
||
let route: str = strings.concat(root, "/route");
|
||
assert(os.mkdir(route, 448i32) == 0);
|
||
writefile(strings.concat(route, "/route.ww"),
|
||
"package route;\nexport fn value() int = { return 7; };\n");
|
||
writefile(strings.concat(route, "/a_test.ww"),
|
||
"package route;\n@test fn white() void = { assert(value() == 7); };\n");
|
||
writefile(strings.concat(route, "/z_test.ww"),
|
||
"package route_test;\nimport route;\n@test fn external() void = { assert(route.value() == 7); };\n");
|
||
|
||
let outc: commandout;
|
||
let outw: commandout;
|
||
let listc: []str = [driver("ww"), "test", "-I", root, "-list", route];
|
||
let listw: []str = [driver("ww_ww"), "test", "-I", root, "-list", route];
|
||
runcommand(root, "list-c", listc,
|
||
(30i64 * (time.second: i64)): time.duration, &outc);
|
||
runcommand(root, "list-ww", listw,
|
||
(30i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 0);
|
||
expectexit(&outw, 0);
|
||
assert(same(outc.stdout, outw.stdout));
|
||
assert(same(outc.stderr, outw.stderr));
|
||
|
||
let misusec: []str = [driver("ww"), "test", "-run"];
|
||
let misusew: []str = [driver("ww_ww"), "test", "-run"];
|
||
runcommand(root, "misuse-c", misusec, time.second, &outc);
|
||
runcommand(root, "misuse-ww", misusew, time.second, &outw);
|
||
expectexit(&outc, 2);
|
||
expectexit(&outw, 2);
|
||
assert(same(outc.stdout, outw.stdout));
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(has(outc.stderr, "ww test: -run needs an argument\n"));
|
||
|
||
let timeoutc: []str = [driver("ww"), "test",
|
||
"-timeout-ms=4000000", "-I", root, route];
|
||
let timeoutw: []str = [driver("ww_ww"), "test",
|
||
"-timeout-ms=4000000", "-I", root, route];
|
||
runcommand(root, "timeout-misuse-c", timeoutc, time.second, &outc);
|
||
runcommand(root, "timeout-misuse-ww", timeoutw, time.second, &outw);
|
||
expectexit(&outc, 2);
|
||
expectexit(&outw, 2);
|
||
assert(same(outc.stdout, outw.stdout));
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(has(outc.stderr, "usage: wwtest package"));
|
||
|
||
let cwork: str = strings.concat(root, "/compile-c-work");
|
||
let wwork: str = strings.concat(root, "/compile-ww-work");
|
||
let cbinarypath: str = strings.concat(root, "/route-c.test");
|
||
let wbinarypath: str = strings.concat(root, "/route-ww.test");
|
||
let cc: []str = [driver("ww"), "test", "-w", cwork, "-o",
|
||
cbinarypath, "-I", root, route];
|
||
runcommand(root, "compile-c", cc,
|
||
(30i64 * (time.second: i64)): time.duration, &outc);
|
||
expectexit(&outc, 0);
|
||
assert(has(outc.stdout, "route.white ... ok\n"));
|
||
assert(has(outc.stdout, "route.external ... ok\n"));
|
||
let cwhite: str = readfile(strings.concat(cwork,
|
||
"/route-internal-test.s"));
|
||
let cexternal: str = readfile(strings.concat(cwork,
|
||
"/route_test-external-test.s"));
|
||
let cmain: str = readfile(strings.concat(cwork,
|
||
"/route-test-main.s"));
|
||
let cbinary: str = readfile(cbinarypath);
|
||
|
||
let wc: []str = [driver("ww_ww"), "test", "-w", wwork, "-o",
|
||
wbinarypath, "-I", root, route];
|
||
runcommand(root, "compile-ww", wc,
|
||
(30i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outw, 0);
|
||
assert(same(outc.stdout, outw.stdout));
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(same(cwhite, readfile(strings.concat(wwork,
|
||
"/route-internal-test.s"))));
|
||
assert(same(cexternal, readfile(strings.concat(wwork,
|
||
"/route_test-external-test.s"))));
|
||
assert(same(cmain, readfile(strings.concat(wwork,
|
||
"/route-test-main.s"))));
|
||
assert(same(cbinary, readfile(wbinarypath)));
|
||
clean(root);
|
||
};
|
||
|
||
@test fn directory_package_graph_variants() void = {
|
||
let root: str = fresh();
|
||
let implementation: str = strings.concat(root, "/implementation");
|
||
let api: str = strings.concat(root, "/api");
|
||
let samedep: str = strings.concat(root, "/__same");
|
||
let external: str = strings.concat(root, "/__external");
|
||
let bridge: str = strings.concat(root, "/bridge");
|
||
let pkg: str = strings.concat(root, "/pkg");
|
||
let dirs: []str = [implementation, api, samedep, external, bridge, pkg];
|
||
let di: i32 = 0;
|
||
for (di < dirs.len) {
|
||
assert(os.mkdir(dirs[di], 448i32) == 0);
|
||
di += 1;
|
||
};
|
||
writefile(strings.concat(implementation, "/implementation.ww"),
|
||
strings.concat("package implementation;\n",
|
||
"// IMPLEMENTATION_SOURCE\n",
|
||
"export fn value() i32 = { return 41; };\n"));
|
||
writefile(strings.concat(api, "/api.ww"), strings.concat(
|
||
"package api;\nimport implementation;\n",
|
||
"// API_SOURCE\n",
|
||
"export fn value() i32 = { return implementation.value(); };\n"));
|
||
writefile(strings.concat(api, "/ignored_test.ww"), strings.concat(
|
||
"package api;\n",
|
||
"TEST_DEPENDENCY_MUST_NOT_COMPILE\n"));
|
||
writefile(strings.concat(samedep, "/same.ww"),
|
||
"package __same;\nexport fn value() i32 = { return 1; };\n");
|
||
writefile(strings.concat(external, "/external.ww"),
|
||
"package __external;\nexport fn value() i32 = { return 1; };\n");
|
||
writefile(strings.concat(pkg, "/a.ww"), strings.concat(
|
||
"package pkg;\nimport api;\n",
|
||
"// PACKAGE_PRODUCTION_A\n",
|
||
"export fn value() i32 = { return api.value(); };\n"));
|
||
writefile(strings.concat(pkg, "/z.ww"), strings.concat(
|
||
"package pkg;\n",
|
||
"// PACKAGE_PRODUCTION_Z\n",
|
||
"fn secret() i32 = { return 6; };\n",
|
||
"export fn second() i32 = { return 9; };\n"));
|
||
writefile(strings.concat(bridge, "/bridge.ww"), strings.concat(
|
||
"package bridge;\nimport pkg;\n",
|
||
"// TRANSITIVE_TEST_IMPORTER\n",
|
||
"export fn helper() i32 = { return pkg.internal_helper(); };\n"));
|
||
let samebody: str = strings.concat(
|
||
"package pkg;\nimport __same;\n",
|
||
"// SAME_TEST_SOURCE\n",
|
||
"export fn internal_helper() i32 = { return 6; };\n",
|
||
"@test fn same_graph() void = {\n",
|
||
" assert(value() + __same.value() == 42);\n",
|
||
" assert(secret() == 6);\n",
|
||
"};\n");
|
||
let samebodychanged: str = strings.concat(
|
||
"package pkg;\nimport __same;\n",
|
||
"// SAME_TEST_SOURCE_BODY_CHANGED\n",
|
||
"export fn internal_helper() i32 = { return 7; };\n",
|
||
"@test fn same_graph() void = {\n",
|
||
" assert(value() + __same.value() == 42);\n",
|
||
" assert(secret() == 6);\n",
|
||
"};\n");
|
||
let samebodyexported: str = strings.concat(
|
||
"package pkg;\nimport __same;\n",
|
||
"// SAME_TEST_SOURCE_EXPORT_CHANGED\n",
|
||
"export fn internal_helper() i32 = { return 7; };\n",
|
||
"export fn second_helper() i32 = { return 8; };\n",
|
||
"@test fn same_graph() void = {\n",
|
||
" assert(value() + __same.value() == 42);\n",
|
||
" assert(secret() == 6);\n",
|
||
"};\n");
|
||
let externalbody: str = strings.concat(
|
||
"package pkg_test;\nimport __external;\nimport bridge;\nimport pkg;\n",
|
||
"// EXTERNAL_TEST_SOURCE\n",
|
||
"@test fn external_graph() void = {\n",
|
||
" assert(pkg.value() + __external.value() == 42);\n",
|
||
" assert(pkg.second() == 9);\n",
|
||
" assert(pkg.internal_helper() >= 6);\n",
|
||
" assert(bridge.helper() >= 6);\n",
|
||
"};\n");
|
||
let externalbodychanged: str = strings.concat(
|
||
"package pkg_test;\nimport __external;\nimport bridge;\nimport pkg;\n",
|
||
"// EXTERNAL_TEST_SOURCE_BODY_CHANGED\n",
|
||
"@test fn external_graph() void = {\n",
|
||
" assert(pkg.value() + __external.value() == 42);\n",
|
||
" assert(pkg.second() == 9);\n",
|
||
" assert(pkg.internal_helper() >= 7);\n",
|
||
" assert(bridge.helper() >= 7);\n",
|
||
"};\n");
|
||
let externalbodyreadded: str = strings.concat(
|
||
"package pkg_test;\nimport __external;\nimport bridge;\nimport pkg;\n",
|
||
"// EXTERNAL_TEST_SOURCE_READDED\n",
|
||
"@test fn external_graph() void = {\n",
|
||
" assert(pkg.value() + __external.value() == 42);\n",
|
||
" assert(pkg.second() == 9);\n",
|
||
" assert(pkg.internal_helper() >= 7);\n",
|
||
" assert(bridge.helper() >= 7);\n",
|
||
" assert(__external.value() == 1);\n",
|
||
"};\n");
|
||
let samefile: str = strings.concat(pkg, "/same_test.ww");
|
||
let externalfile: str = strings.concat(pkg, "/external_test.ww");
|
||
writefile(samefile, samebody);
|
||
writefile(externalfile, externalbody);
|
||
|
||
// Ordinary builds neither parse nor load any test-only source or dependency.
|
||
let prodout: str = strings.concat(root, "/production.a");
|
||
let prodav: []str = [driver("ww"), "build", "-I", root,
|
||
"-o", prodout, pkg];
|
||
let out: commandout;
|
||
runcommand(root, "production-build", prodav,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let prodwork: str = strings.concat(prodout, ".sepwork/");
|
||
let produnit: str = readfile(strings.concat(prodwork, "pkg.unit.ww"));
|
||
assert(has(produnit, "PACKAGE_PRODUCTION_A"));
|
||
assert(has(produnit, "PACKAGE_PRODUCTION_Z"));
|
||
assert(!has(produnit, "SAME_TEST_SOURCE"));
|
||
assert(!has(produnit, "internal_helper"));
|
||
assert(!os.exists(strings.concat(prodwork, "__same.unit.ww")));
|
||
assert(!os.exists(strings.concat(prodwork, "__external.unit.ww")));
|
||
assert(!os.exists(strings.concat(prodwork, "bridge.unit.ww")));
|
||
assert(!has(readfile(strings.concat(prodwork, "api.unit.ww")),
|
||
"TEST_DEPENDENCY_MUST_NOT_COMPILE"));
|
||
|
||
let compilerwrapper: str = strings.concat(root, "/trace-w6c.sh");
|
||
let assemblerwrapper: str = strings.concat(root, "/trace-w6a.sh");
|
||
let linkerwrapper: str = strings.concat(root, "/trace-w6l.sh");
|
||
writeexecutable(compilerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_GRAPH_COMPILER_TRACE\"\n",
|
||
"exec \"$WW_GRAPH_REAL_COMPILER\" \"$@\"\n"));
|
||
writeexecutable(assemblerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_GRAPH_ASSEMBLER_TRACE\"\n",
|
||
"exec \"$WW_GRAPH_REAL_ASSEMBLER\" \"$@\"\n"));
|
||
writeexecutable(linkerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_GRAPH_LINKER_TRACE\"\n",
|
||
"exec \"$WW_GRAPH_REAL_LINKER\" \"$@\"\n"));
|
||
|
||
let stages: []str = ["ww", "ww_ww"];
|
||
let compilers: []str = ["w6c", "w6c_ww"];
|
||
let assemblers: []str = ["w6a", "w6a_ww"];
|
||
let linkers: []str = ["w6l", "w6l_ww"];
|
||
let works: []str = [strings.concat(root, "/c-work"),
|
||
strings.concat(root, "/ww-work")];
|
||
let bins: []str = [strings.concat(root, "/c-pkg.test"),
|
||
strings.concat(root, "/ww-pkg.test")];
|
||
let statuses: []str = [strings.concat(root, "/c-pkg.status"),
|
||
strings.concat(root, "/ww-pkg.status")];
|
||
let compilertraces: []str = [strings.concat(root, "/c-w6c.trace"),
|
||
strings.concat(root, "/ww-w6c.trace")];
|
||
let assemblertraces: []str = [strings.concat(root, "/c-w6a.trace"),
|
||
strings.concat(root, "/ww-w6a.trace")];
|
||
let linkertraces: []str = [strings.concat(root, "/c-w6l.trace"),
|
||
strings.concat(root, "/ww-w6l.trace")];
|
||
let bridgeowner: str = strings.concat("pkg#", pkg);
|
||
let bridgeaction: str = packagestoragekeyfor(
|
||
"bridge", bridge, 4, 0, bridgeowner);
|
||
assert(strings.hasprefix(bridgeaction, "__wwpkg.v4.r0.h"));
|
||
let actions: []str = ["implementation", "api", "__same", "__external",
|
||
bridgeaction, "test", "pkg-internal-test", "pkg_test-external-test",
|
||
"pkg-test-main"];
|
||
let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"];
|
||
let references: []str = alloc([],
|
||
(actions.len * suffixes.len): u64)!;
|
||
let ai: i32 = 0;
|
||
for (ai < actions.len * suffixes.len) {
|
||
append(references, "");
|
||
ai += 1;
|
||
};
|
||
let referencebin: str = "";
|
||
let referenceout: str = "";
|
||
let referenceerr: str = "";
|
||
let referencecompiler: str = "";
|
||
let referenceassembler: str = "";
|
||
let referencelinker: str = "";
|
||
let bodycompiler: str = "";
|
||
let bodyassembler: str = "";
|
||
let bodylinker: str = "";
|
||
let externalcompiler: str = "";
|
||
let externalassembler: str = "";
|
||
let externallinker: str = "";
|
||
let exportcompiler: str = "";
|
||
let exportassembler: str = "";
|
||
let exportlinker: str = "";
|
||
let exportbridge: []str = ["", "", "", "", ""];
|
||
let removecompiler: str = "";
|
||
let removeassembler: str = "";
|
||
let removelinker: str = "";
|
||
let removebin: str = "";
|
||
let removeout: str = "";
|
||
let removeerr: str = "";
|
||
let readdcompiler: str = "";
|
||
let readdassembler: str = "";
|
||
let readdlinker: str = "";
|
||
let readdbin: str = "";
|
||
let readdout: str = "";
|
||
let readderr: str = "";
|
||
let baseenv: []str = os.getenvs();
|
||
let si: i32 = 0;
|
||
for (si < stages.len) {
|
||
assert(os.mkdir(works[si], 448i32) == 0);
|
||
writefile(compilertraces[si], "");
|
||
writefile(assemblertraces[si], "");
|
||
writefile(linkertraces[si], "");
|
||
let env: []str = alloc([], (baseenv.len + 9): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < baseenv.len) {
|
||
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6A=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6L=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_GRAPH_COMPILER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_GRAPH_ASSEMBLER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_GRAPH_LINKER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_GRAPH_REAL_COMPILER=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_GRAPH_REAL_ASSEMBLER=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_GRAPH_REAL_LINKER=")) {
|
||
append(env, baseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(env, strings.concat("WW_W6C=", compilerwrapper));
|
||
append(env, strings.concat("WW_W6A=", assemblerwrapper));
|
||
append(env, strings.concat("WW_W6L=", linkerwrapper));
|
||
append(env, strings.concat("WW_GRAPH_COMPILER_TRACE=",
|
||
compilertraces[si]));
|
||
append(env, strings.concat("WW_GRAPH_ASSEMBLER_TRACE=",
|
||
assemblertraces[si]));
|
||
append(env, strings.concat("WW_GRAPH_LINKER_TRACE=",
|
||
linkertraces[si]));
|
||
append(env, strings.concat("WW_GRAPH_REAL_COMPILER=",
|
||
driver(compilers[si])));
|
||
append(env, strings.concat("WW_GRAPH_REAL_ASSEMBLER=",
|
||
driver(assemblers[si])));
|
||
append(env, strings.concat("WW_GRAPH_REAL_LINKER=",
|
||
driver(linkers[si])));
|
||
|
||
let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si],
|
||
"-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg",
|
||
"pkg_test", pkg, bins[si], "-", statuses[si], pkg];
|
||
runcommandenv(root, strings.concat("graph-cold-", stages[si]), av,
|
||
env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
assert(os.exists(bins[si]));
|
||
assert(same(readfile(statuses[si]), "ok\n"));
|
||
assert(!os.exists(strings.concat(pkg, "/pkg_test.test")));
|
||
assert(!os.exists(strings.concat(works[si], "/pkg.unit.ww")));
|
||
assert(!os.exists(strings.concat(works[si], "/bridge.unit.ww")));
|
||
|
||
let ptestunit: str = readfile(strings.concat(works[si],
|
||
"/pkg-internal-test.unit.ww"));
|
||
let pxtestunit: str = readfile(strings.concat(works[si],
|
||
"/pkg_test-external-test.unit.ww"));
|
||
let mainunit: str = readfile(strings.concat(works[si],
|
||
"/pkg-test-main.unit.ww"));
|
||
assert(has(ptestunit, "PACKAGE_PRODUCTION_A"));
|
||
assert(has(ptestunit, "PACKAGE_PRODUCTION_Z"));
|
||
assert(has(ptestunit, "SAME_TEST_SOURCE"));
|
||
assert(has(ptestunit, "export fn internal_helper"));
|
||
assert(!has(ptestunit, "EXTERNAL_TEST_SOURCE"));
|
||
assert(has(pxtestunit, "EXTERNAL_TEST_SOURCE"));
|
||
assert(!has(pxtestunit, "PACKAGE_PRODUCTION_A"));
|
||
assert(!has(pxtestunit, "SAME_TEST_SOURCE"));
|
||
assert(strings.hasprefix(mainunit, strings.concat(
|
||
"//ww:module-reset __wwtestmain.pkg.main\n",
|
||
"package main;\nimport pkg;\nimport pkg_test;\nimport test;\n")));
|
||
|
||
let ctrace: str = readfile(compilertraces[si]);
|
||
let atrace: str = readfile(assemblertraces[si]);
|
||
let ltrace: str = readfile(linkertraces[si]);
|
||
assert(occurrences(ctrace, "pkg-internal-test.unit.new") == 1);
|
||
assert(occurrences(ctrace,
|
||
"pkg_test-external-test.unit.new") == 1);
|
||
assert(occurrences(ctrace, "pkg-test-main.unit.new") == 1);
|
||
assert(occurrences(ctrace,
|
||
"--test-target-package pkg --test-target-package pkg_test") == 1);
|
||
assert(!has(ctrace,
|
||
"--package-init-symbol __ww..pkg.p.pkg.v0.r0.init"));
|
||
let ptestcompile: str = linecontaining(ctrace,
|
||
"pkg-internal-test.unit.new");
|
||
assert(has(ptestcompile,
|
||
"--package-init-symbol __ww..pkg.p.pkg.v1.r0.init"));
|
||
assert(pos(ptestcompile, "--import __same ")
|
||
< pos(ptestcompile, "--import api "));
|
||
let bridgecompile: str = linecontaining(ctrace,
|
||
"--package-init-symbol __ww..pkg.p.bridge.v4.r0.init");
|
||
assert(has(bridgecompile, "--import pkg "));
|
||
assert(has(bridgecompile, "/pkg-internal-test.wwi.new"));
|
||
assert(!has(ctrace,
|
||
"--package-init-symbol __ww..pkg.p.bridge.v0.r0.init"));
|
||
let externalcompile: str = linecontaining(ctrace,
|
||
"pkg_test-external-test.unit.new");
|
||
assert(pos(externalcompile, "--import __external ")
|
||
< pos(externalcompile, "--import bridge "));
|
||
assert(pos(externalcompile, "--import bridge ")
|
||
< pos(externalcompile, "--import pkg "));
|
||
assert(has(externalcompile, "/pkg-internal-test.wwi.new"));
|
||
assert(!has(externalcompile, "/pkg.wwi"));
|
||
let maincompile: str = linecontaining(ctrace,
|
||
"pkg-test-main.unit.new");
|
||
assert(has(maincompile, strings.concat(
|
||
"-T --entry --test-support-module test ",
|
||
"--test-target-package pkg --test-target-package pkg_test ",
|
||
"--package-init-symbol ",
|
||
"__ww..pkg.p.__wwtestmain.pkg.main.v3.r2.init ")));
|
||
assert(pos(maincompile, "--import pkg ")
|
||
< pos(maincompile, "--import pkg_test "));
|
||
assert(pos(maincompile, "--import pkg_test ")
|
||
< pos(maincompile, "--import test "));
|
||
let workprefix: str = strings.concat(works[si], "/");
|
||
assert(same(ptestcompile, strings.concat(
|
||
"--test-package --package-init-symbol ",
|
||
"__ww..pkg.p.pkg.v1.r0.init -c --import __same ", workprefix,
|
||
"__same.wwi.new --import api ", workprefix,
|
||
"api.wwi.new -I ", workprefix,
|
||
"pkg-internal-test.wwi.new -o ", workprefix,
|
||
"pkg-internal-test.s.new ", workprefix,
|
||
"pkg-internal-test.unit.new")));
|
||
assert(same(bridgecompile, strings.concat(
|
||
"--package-init-symbol __ww..pkg.p.bridge.v4.r0.init -c ",
|
||
"--import pkg ", workprefix,
|
||
"pkg-internal-test.wwi.new -I ", workprefix, bridgeaction,
|
||
".wwi.new -o ", workprefix, bridgeaction, ".s.new ", workprefix,
|
||
bridgeaction, ".unit.new")));
|
||
assert(same(externalcompile, strings.concat(
|
||
"--test-package --package-init-symbol ",
|
||
"__ww..pkg.p.pkg_test.v2.r0.init -c --import __external ",
|
||
workprefix, "__external.wwi.new --import bridge ", workprefix,
|
||
bridgeaction, ".wwi.new --import pkg ", workprefix,
|
||
"pkg-internal-test.wwi.new -I ", workprefix,
|
||
"pkg_test-external-test.wwi.new -o ", workprefix,
|
||
"pkg_test-external-test.s.new ", workprefix,
|
||
"pkg_test-external-test.unit.new")));
|
||
assert(same(maincompile, strings.concat(
|
||
"-T --entry --test-support-module test --test-target-package pkg ",
|
||
"--test-target-package pkg_test --package-init-symbol ",
|
||
"__ww..pkg.p.__wwtestmain.pkg.main.v3.r2.init ",
|
||
"--init-dispatch-symbol __ww..dispatch -c --import pkg ",
|
||
workprefix, "pkg-internal-test.wwi.new --import pkg_test ",
|
||
workprefix, "pkg_test-external-test.wwi.new --import test ",
|
||
workprefix, "test.wwi.new -I ", workprefix,
|
||
"pkg-test-main.wwi.new -o ", workprefix,
|
||
"pkg-test-main.s.new ", workprefix, "pkg-test-main.unit.new")));
|
||
let supportcompile: str = linecontaining(ctrace,
|
||
"--test-support-module test --package-init-symbol");
|
||
assert(same(supportcompile, strings.concat(
|
||
"--test-support-module test --package-init-symbol ",
|
||
"__ww..pkg.p.test.v0.r0.init -c --import fnmatch ", workprefix,
|
||
"fnmatch.wwi.new --import os ", workprefix,
|
||
"os.wwi.new --import time ", workprefix,
|
||
"time.wwi.new -I ", workprefix, "test.wwi.new -o ", workprefix,
|
||
"test.s.new ", workprefix, "test.unit.new")));
|
||
assert(same(linecontaining(atrace, "/pkg-internal-test.o.new"),
|
||
strings.concat("-o ", works[si],
|
||
"/pkg-internal-test.o.new ", works[si],
|
||
"/pkg-internal-test.s.new")));
|
||
assert(same(linecontaining(atrace, "/pkg-test-main.init.o.new"),
|
||
strings.concat("-o ", works[si],
|
||
"/pkg-test-main.init.o.new ", works[si],
|
||
"/pkg-test-main.init.s.new")));
|
||
let exactassembled: []str = [bridgeaction,
|
||
"pkg_test-external-test", "pkg-test-main", "test"];
|
||
let exacti: i32 = 0;
|
||
for (exacti < exactassembled.len) {
|
||
assert(same(linecontaining(atrace, strings.concat("/",
|
||
exactassembled[exacti], ".o.new")), strings.concat("-o ",
|
||
workprefix, exactassembled[exacti], ".o.new ", workprefix,
|
||
exactassembled[exacti], ".s.new")));
|
||
exacti += 1;
|
||
};
|
||
assert(occurrences(ltrace, "\n") == 1);
|
||
let link: str = linecontaining(ltrace,
|
||
strings.concat("-o ", bins[si], ".new "));
|
||
assert(has(link, "/pkg-test-main.a.new"));
|
||
assert(has(link, "/pkg-internal-test.a.new"));
|
||
assert(has(link, "/pkg_test-external-test.a.new"));
|
||
assert(has(link, "/__same.a.new"));
|
||
assert(has(link, "/__external.a.new"));
|
||
assert(has(link, "/api.a.new"));
|
||
assert(has(link, "/implementation.a.new"));
|
||
assert(has(link, "/test.a.new"));
|
||
assert(has(link, strings.concat("/", bridgeaction, ".a.new")));
|
||
assert(!has(link, strings.concat(works[si], "/pkg.a")));
|
||
assert(!has(link, ".wwi"));
|
||
assert(same(link, strings.concat("-o ", bins[si], ".new ",
|
||
workprefix, "pkg-test-main.a.new ", workprefix, "test.a.new ",
|
||
workprefix, "fnmatch.a.new ", workprefix, "ascii.a.new ",
|
||
workprefix, "strings.a.new ", workprefix, "os.a.new ",
|
||
workprefix, "time.a.new ", workprefix, "encoding.utf8.a.new ",
|
||
workprefix, "bytes.a.new ", workprefix, "types.a.new ",
|
||
workprefix, "pkg_test-external-test.a.new ", workprefix,
|
||
bridgeaction, ".a.new ", workprefix, "__external.a.new ",
|
||
workprefix, "pkg-internal-test.a.new ", workprefix, "api.a.new ",
|
||
workprefix, "implementation.a.new ", workprefix, "__same.a.new ",
|
||
repo(), "/out/bin/../lib/libwwrt.a")));
|
||
|
||
ai = 0;
|
||
for (ai < actions.len) {
|
||
let archive: str = readfile(strings.concat(works[si], "/",
|
||
actions[ai], ".a"));
|
||
checkactionarchive(archive,
|
||
strings.compare(actions[ai], "pkg-test-main") == 0);
|
||
let xi: i32 = 0;
|
||
for (xi < suffixes.len) {
|
||
let bytes: str = readfile(strings.concat(works[si], "/",
|
||
actions[ai], suffixes[xi]));
|
||
let index: i32 = ai * suffixes.len + xi;
|
||
if (si == 0) { references[index] = strings.dup(bytes); }
|
||
else { assert(same(references[index], bytes)); };
|
||
xi += 1;
|
||
};
|
||
ai += 1;
|
||
};
|
||
let normalizedcompiler: str = normalizedtrace(ctrace,
|
||
strings.concat(works[si], "/"), bins[si]);
|
||
let normalizedassembler: str = normalizedtrace(atrace,
|
||
strings.concat(works[si], "/"), bins[si]);
|
||
let normalizedlinker: str = normalizedtrace(ltrace,
|
||
strings.concat(works[si], "/"), bins[si]);
|
||
if (si == 0) {
|
||
referencebin = readfile(bins[si]);
|
||
referenceout = strings.dup(out.stdout);
|
||
referenceerr = strings.dup(out.stderr);
|
||
referencecompiler = normalizedcompiler;
|
||
referenceassembler = normalizedassembler;
|
||
referencelinker = normalizedlinker;
|
||
} else {
|
||
assert(same(referencebin, readfile(bins[si])));
|
||
assert(same(referenceout, out.stdout));
|
||
assert(same(referenceerr, out.stderr));
|
||
assert(same(referencecompiler, normalizedcompiler));
|
||
assert(same(referenceassembler, normalizedassembler));
|
||
assert(same(referencelinker, normalizedlinker));
|
||
};
|
||
let runav: []str = [bins[si]];
|
||
runcommand(root, strings.concat("graph-run-", stages[si]), runav,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(occurrences(out.stdout, "same_graph ... ok\n") == 1);
|
||
assert(occurrences(out.stdout, "external_graph ... ok\n") == 1);
|
||
|
||
// Warm action reuse is exact; linking republishes the requested output.
|
||
rewritefile(compilertraces[si], "");
|
||
rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
runcommandenv(root, strings.concat("graph-warm-", stages[si]), av,
|
||
env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(readfile(compilertraces[si]).len == 0);
|
||
assert(readfile(assemblertraces[si]).len == 0);
|
||
assert(occurrences(readfile(linkertraces[si]), "\n") == 1);
|
||
si += 1;
|
||
};
|
||
|
||
// An internal helper body edit recompiles only ptest; its stable export
|
||
// stops reverse compilation of pxtest, the transitive clone, and pmain.
|
||
rewritefile(samefile, samebodychanged);
|
||
si = 0;
|
||
for (si < stages.len) {
|
||
let oldptestwwi: str = readfile(strings.concat(works[si],
|
||
"/pkg-internal-test.wwi"));
|
||
let oldptesta: str = readfile(strings.concat(works[si],
|
||
"/pkg-internal-test.a"));
|
||
let oldpxtestwwi: str = readfile(strings.concat(works[si],
|
||
"/pkg_test-external-test.wwi"));
|
||
let oldpxtesta: str = readfile(strings.concat(works[si],
|
||
"/pkg_test-external-test.a"));
|
||
let oldmainwwi: str = readfile(strings.concat(works[si],
|
||
"/pkg-test-main.wwi"));
|
||
let oldmaina: str = readfile(strings.concat(works[si],
|
||
"/pkg-test-main.a"));
|
||
rewritefile(compilertraces[si], "");
|
||
rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
let env: []str = os.getenvs();
|
||
let tracedenv: []str = alloc([], (env.len + 9): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < env.len) {
|
||
if (!strings.hasprefix(env[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(env[ei], "WW_W6A=")
|
||
&& !strings.hasprefix(env[ei], "WW_W6L=")) {
|
||
append(tracedenv, env[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(tracedenv, strings.concat("WW_W6C=", compilerwrapper));
|
||
append(tracedenv, strings.concat("WW_W6A=", assemblerwrapper));
|
||
append(tracedenv, strings.concat("WW_W6L=", linkerwrapper));
|
||
append(tracedenv, strings.concat("WW_GRAPH_COMPILER_TRACE=",
|
||
compilertraces[si]));
|
||
append(tracedenv, strings.concat("WW_GRAPH_ASSEMBLER_TRACE=",
|
||
assemblertraces[si]));
|
||
append(tracedenv, strings.concat("WW_GRAPH_LINKER_TRACE=",
|
||
linkertraces[si]));
|
||
append(tracedenv, strings.concat("WW_GRAPH_REAL_COMPILER=",
|
||
driver(compilers[si])));
|
||
append(tracedenv, strings.concat("WW_GRAPH_REAL_ASSEMBLER=",
|
||
driver(assemblers[si])));
|
||
append(tracedenv, strings.concat("WW_GRAPH_REAL_LINKER=",
|
||
driver(linkers[si])));
|
||
let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si],
|
||
"-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg",
|
||
"pkg_test", pkg, bins[si], "-", statuses[si], pkg];
|
||
runcommandenv(root, strings.concat("graph-body-", stages[si]), av,
|
||
tracedenv, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let ct: str = readfile(compilertraces[si]);
|
||
let at: str = readfile(assemblertraces[si]);
|
||
let lt: str = readfile(linkertraces[si]);
|
||
assert(occurrences(ct, "\n") == 1);
|
||
assert(has(ct, "pkg-internal-test.unit.new"));
|
||
assert(!has(ct, "pkg_test-external-test.unit.new"));
|
||
assert(!has(ct, "pkg-test-main.unit.new"));
|
||
assert(!has(ct, ".bridge.v4.r0.init"));
|
||
assert(occurrences(at, "\n") == 1);
|
||
assert(has(at, "pkg-internal-test.o.new"));
|
||
assert(occurrences(lt, "\n") == 1);
|
||
assert(same(oldptestwwi, readfile(strings.concat(works[si],
|
||
"/pkg-internal-test.wwi"))));
|
||
assert(!same(oldptesta, readfile(strings.concat(works[si],
|
||
"/pkg-internal-test.a"))));
|
||
assert(same(oldpxtestwwi, readfile(strings.concat(works[si],
|
||
"/pkg_test-external-test.wwi"))));
|
||
assert(same(oldpxtesta, readfile(strings.concat(works[si],
|
||
"/pkg_test-external-test.a"))));
|
||
assert(same(oldmainwwi, readfile(strings.concat(works[si],
|
||
"/pkg-test-main.wwi"))));
|
||
assert(same(oldmaina, readfile(strings.concat(works[si],
|
||
"/pkg-test-main.a"))));
|
||
let nc: str = normalizedtrace(ct, strings.concat(works[si], "/"),
|
||
bins[si]);
|
||
let na: str = normalizedtrace(at, strings.concat(works[si], "/"),
|
||
bins[si]);
|
||
let nl: str = normalizedtrace(lt, strings.concat(works[si], "/"),
|
||
bins[si]);
|
||
if (si == 0) {
|
||
bodycompiler = nc; bodyassembler = na; bodylinker = nl;
|
||
} else {
|
||
assert(same(bodycompiler, nc));
|
||
assert(same(bodyassembler, na));
|
||
assert(same(bodylinker, nl));
|
||
assert(same(readfile(bins[0]), readfile(bins[1])));
|
||
};
|
||
si += 1;
|
||
};
|
||
|
||
// An external test body edit is confined to pxtest when its export is stable.
|
||
rewritefile(externalfile, externalbodychanged);
|
||
si = 0;
|
||
for (si < stages.len) {
|
||
rewritefile(compilertraces[si], "");
|
||
rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
let env: []str = os.getenvs();
|
||
let tracedenv: []str = alloc([], (env.len + 9): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < env.len) {
|
||
if (!strings.hasprefix(env[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(env[ei], "WW_W6A=")
|
||
&& !strings.hasprefix(env[ei], "WW_W6L=")) {
|
||
append(tracedenv, env[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(tracedenv, strings.concat("WW_W6C=", compilerwrapper));
|
||
append(tracedenv, strings.concat("WW_W6A=", assemblerwrapper));
|
||
append(tracedenv, strings.concat("WW_W6L=", linkerwrapper));
|
||
append(tracedenv, strings.concat("WW_GRAPH_COMPILER_TRACE=",
|
||
compilertraces[si]));
|
||
append(tracedenv, strings.concat("WW_GRAPH_ASSEMBLER_TRACE=",
|
||
assemblertraces[si]));
|
||
append(tracedenv, strings.concat("WW_GRAPH_LINKER_TRACE=",
|
||
linkertraces[si]));
|
||
append(tracedenv, strings.concat("WW_GRAPH_REAL_COMPILER=",
|
||
driver(compilers[si])));
|
||
append(tracedenv, strings.concat("WW_GRAPH_REAL_ASSEMBLER=",
|
||
driver(assemblers[si])));
|
||
append(tracedenv, strings.concat("WW_GRAPH_REAL_LINKER=",
|
||
driver(linkers[si])));
|
||
let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si],
|
||
"-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg",
|
||
"pkg_test", pkg, bins[si], "-", statuses[si], pkg];
|
||
runcommandenv(root, strings.concat("graph-external-", stages[si]), av,
|
||
tracedenv, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let ct: str = readfile(compilertraces[si]);
|
||
let at: str = readfile(assemblertraces[si]);
|
||
let lt: str = readfile(linkertraces[si]);
|
||
assert(occurrences(ct, "\n") == 1);
|
||
assert(has(ct, "pkg_test-external-test.unit.new"));
|
||
assert(!has(ct, "pkg-internal-test.unit.new"));
|
||
assert(!has(ct, "pkg-test-main.unit.new"));
|
||
assert(!has(ct, ".bridge.v4.r0.init"));
|
||
assert(occurrences(at, "\n") == 1);
|
||
assert(has(at, "pkg_test-external-test.o.new"));
|
||
assert(occurrences(lt, "\n") == 1);
|
||
let nc: str = normalizedtrace(ct, strings.concat(works[si], "/"),
|
||
bins[si]);
|
||
let na: str = normalizedtrace(at, strings.concat(works[si], "/"),
|
||
bins[si]);
|
||
let nl: str = normalizedtrace(lt, strings.concat(works[si], "/"),
|
||
bins[si]);
|
||
if (si == 0) {
|
||
externalcompiler = nc; externalassembler = na;
|
||
externallinker = nl;
|
||
} else {
|
||
assert(same(externalcompiler, nc));
|
||
assert(same(externalassembler, na));
|
||
assert(same(externallinker, nl));
|
||
assert(same(readfile(bins[0]), readfile(bins[1])));
|
||
};
|
||
si += 1;
|
||
};
|
||
|
||
// An internal exported-helper interface edit reaches the variant-4
|
||
// importer, pxtest, and pmain, but still never introduces ordinary p.
|
||
rewritefile(samefile, samebodyexported);
|
||
si = 0;
|
||
for (si < stages.len) {
|
||
let oldwwi: str = readfile(strings.concat(works[si],
|
||
"/pkg-internal-test.wwi"));
|
||
rewritefile(compilertraces[si], "");
|
||
rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
let env: []str = os.getenvs();
|
||
let tracedenv: []str = alloc([], (env.len + 9): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < env.len) {
|
||
if (!strings.hasprefix(env[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(env[ei], "WW_W6A=")
|
||
&& !strings.hasprefix(env[ei], "WW_W6L=")) {
|
||
append(tracedenv, env[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(tracedenv, strings.concat("WW_W6C=", compilerwrapper));
|
||
append(tracedenv, strings.concat("WW_W6A=", assemblerwrapper));
|
||
append(tracedenv, strings.concat("WW_W6L=", linkerwrapper));
|
||
append(tracedenv, strings.concat("WW_GRAPH_COMPILER_TRACE=",
|
||
compilertraces[si]));
|
||
append(tracedenv, strings.concat("WW_GRAPH_ASSEMBLER_TRACE=",
|
||
assemblertraces[si]));
|
||
append(tracedenv, strings.concat("WW_GRAPH_LINKER_TRACE=",
|
||
linkertraces[si]));
|
||
append(tracedenv, strings.concat("WW_GRAPH_REAL_COMPILER=",
|
||
driver(compilers[si])));
|
||
append(tracedenv, strings.concat("WW_GRAPH_REAL_ASSEMBLER=",
|
||
driver(assemblers[si])));
|
||
append(tracedenv, strings.concat("WW_GRAPH_REAL_LINKER=",
|
||
driver(linkers[si])));
|
||
let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si],
|
||
"-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg",
|
||
"pkg_test", pkg, bins[si], "-", statuses[si], pkg];
|
||
runcommandenv(root, strings.concat("graph-export-", stages[si]), av,
|
||
tracedenv, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let ct: str = readfile(compilertraces[si]);
|
||
let at: str = readfile(assemblertraces[si]);
|
||
let lt: str = readfile(linkertraces[si]);
|
||
assert(!same(oldwwi, readfile(strings.concat(works[si],
|
||
"/pkg-internal-test.wwi"))));
|
||
assert(occurrences(ct, "pkg-internal-test.unit.new") == 1);
|
||
assert(occurrences(ct,
|
||
"--package-init-symbol __ww..pkg.p.bridge.v4.r0.init") == 1);
|
||
assert(occurrences(ct,
|
||
"pkg_test-external-test.unit.new") == 1);
|
||
assert(occurrences(ct, "pkg-test-main.unit.new") == 1);
|
||
assert(!has(ct,
|
||
"--package-init-symbol __ww..pkg.p.pkg.v0.r0.init"));
|
||
assert(occurrences(lt, "\n") == 1);
|
||
let nc: str = normalizedtrace(ct, strings.concat(works[si], "/"),
|
||
bins[si]);
|
||
let na: str = normalizedtrace(at, strings.concat(works[si], "/"),
|
||
bins[si]);
|
||
let nl: str = normalizedtrace(lt, strings.concat(works[si], "/"),
|
||
bins[si]);
|
||
if (si == 0) {
|
||
exportcompiler = nc; exportassembler = na; exportlinker = nl;
|
||
} else {
|
||
assert(same(exportcompiler, nc));
|
||
assert(same(exportassembler, na));
|
||
assert(same(exportlinker, nl));
|
||
assert(same(readfile(bins[0]), readfile(bins[1])));
|
||
};
|
||
let xi: i32 = 0;
|
||
for (xi < suffixes.len) {
|
||
let bytes: str = readfile(strings.concat(works[si], "/",
|
||
bridgeaction, suffixes[xi]));
|
||
if (si == 0) { exportbridge[xi] = strings.dup(bytes); }
|
||
else { assert(same(exportbridge[xi], bytes)); };
|
||
xi += 1;
|
||
};
|
||
let runav: []str = [bins[si]];
|
||
runcommand(root, strings.concat("graph-export-run-", stages[si]),
|
||
runav, (60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(occurrences(out.stdout, "same_graph ... ok\n") == 1);
|
||
assert(occurrences(out.stdout, "external_graph ... ok\n") == 1);
|
||
si += 1;
|
||
};
|
||
|
||
// Removing the external variant changes only the directory main's target
|
||
// set. The now-unreachable pxtest and transitive clone remain intact in the
|
||
// persistent store, while the linked process runs the internal target once.
|
||
assert(os.remove(externalfile) == 0);
|
||
si = 0;
|
||
for (si < stages.len) {
|
||
let oldptest: str = readfile(strings.concat(works[si],
|
||
"/pkg-internal-test.a"));
|
||
let oldpxtest: str = readfile(strings.concat(works[si],
|
||
"/pkg_test-external-test.a"));
|
||
let oldbridge: str = readfile(strings.concat(works[si], "/",
|
||
bridgeaction, ".a"));
|
||
rewritefile(compilertraces[si], "");
|
||
rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
let env: []str = alloc([], (baseenv.len + 9): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < baseenv.len) {
|
||
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6A=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6L=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_GRAPH_COMPILER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_GRAPH_ASSEMBLER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_GRAPH_LINKER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_GRAPH_REAL_COMPILER=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_GRAPH_REAL_ASSEMBLER=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_GRAPH_REAL_LINKER=")) {
|
||
append(env, baseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(env, strings.concat("WW_W6C=", compilerwrapper));
|
||
append(env, strings.concat("WW_W6A=", assemblerwrapper));
|
||
append(env, strings.concat("WW_W6L=", linkerwrapper));
|
||
append(env, strings.concat("WW_GRAPH_COMPILER_TRACE=",
|
||
compilertraces[si]));
|
||
append(env, strings.concat("WW_GRAPH_ASSEMBLER_TRACE=",
|
||
assemblertraces[si]));
|
||
append(env, strings.concat("WW_GRAPH_LINKER_TRACE=",
|
||
linkertraces[si]));
|
||
append(env, strings.concat("WW_GRAPH_REAL_COMPILER=",
|
||
driver(compilers[si])));
|
||
append(env, strings.concat("WW_GRAPH_REAL_ASSEMBLER=",
|
||
driver(assemblers[si])));
|
||
append(env, strings.concat("WW_GRAPH_REAL_LINKER=",
|
||
driver(linkers[si])));
|
||
let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si],
|
||
"-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg",
|
||
"-", pkg, bins[si], "-", statuses[si], pkg];
|
||
runcommandenv(root, strings.concat("graph-remove-external-", stages[si]),
|
||
av, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
assert(same(readfile(statuses[si]), "ok\n"));
|
||
let ct: str = readfile(compilertraces[si]);
|
||
let at: str = readfile(assemblertraces[si]);
|
||
let lt: str = readfile(linkertraces[si]);
|
||
assert(occurrences(ct, "\n") == 1);
|
||
assert(has(ct, "pkg-test-main.unit.new"));
|
||
assert(!has(ct, "pkg-internal-test.unit.new"));
|
||
assert(!has(ct, "pkg_test-external-test.unit.new"));
|
||
assert(!has(ct, strings.concat("/", bridgeaction, ".unit.new")));
|
||
let maincompile: str = linecontaining(ct,
|
||
"pkg-test-main.unit.new");
|
||
assert(occurrences(maincompile, "--test-target-package") == 1);
|
||
assert(has(maincompile, "--test-target-package pkg "));
|
||
assert(!has(maincompile, "--test-target-package pkg_test "));
|
||
assert(occurrences(at, "\n") == 2);
|
||
assert(has(at, "/pkg-test-main.o.new"));
|
||
assert(has(at, "/pkg-test-main.init.o.new"));
|
||
assert(occurrences(lt, "\n") == 1);
|
||
let link: str = linecontaining(lt,
|
||
strings.concat("-o ", bins[si], ".new "));
|
||
assert(has(link, "/pkg-test-main.a.new"));
|
||
assert(has(link, "/pkg-internal-test.a"));
|
||
assert(!has(link, "/pkg_test-external-test.a"));
|
||
assert(!has(link, strings.concat("/", bridgeaction, ".a")));
|
||
assert(!has(link, "/__external.a"));
|
||
assert(same(oldptest, readfile(strings.concat(works[si],
|
||
"/pkg-internal-test.a"))));
|
||
assert(same(oldpxtest, readfile(strings.concat(works[si],
|
||
"/pkg_test-external-test.a"))));
|
||
assert(same(oldbridge, readfile(strings.concat(works[si], "/",
|
||
bridgeaction, ".a"))));
|
||
let runav: []str = [bins[si]];
|
||
runcommand(root, strings.concat("graph-remove-run-", stages[si]),
|
||
runav, (60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(occurrences(out.stdout, "same_graph ... ok\n") == 1);
|
||
assert(!has(out.stdout, "external_graph ... ok\n"));
|
||
let nc: str = normalizedtrace(ct, strings.concat(works[si], "/"),
|
||
bins[si]);
|
||
let na: str = normalizedtrace(at, strings.concat(works[si], "/"),
|
||
bins[si]);
|
||
let nl: str = normalizedtrace(lt, strings.concat(works[si], "/"),
|
||
bins[si]);
|
||
if (si == 0) {
|
||
removecompiler = nc; removeassembler = na; removelinker = nl;
|
||
removebin = readfile(bins[si]);
|
||
removeout = strings.dup(out.stdout);
|
||
removeerr = strings.dup(out.stderr);
|
||
} else {
|
||
assert(same(removecompiler, nc));
|
||
assert(same(removeassembler, na));
|
||
assert(same(removelinker, nl));
|
||
assert(same(removebin, readfile(bins[si])));
|
||
assert(same(removeout, out.stdout));
|
||
assert(same(removeerr, out.stderr));
|
||
};
|
||
si += 1;
|
||
};
|
||
|
||
// Re-adding the external variant with a body-only edit rebuilds pxtest and
|
||
// the target-set main. Its stable export stops at the unchanged ptest and
|
||
// transitive clone, whose complete persisted bytes remain identical.
|
||
writefile(externalfile, externalbodyreadded);
|
||
si = 0;
|
||
for (si < stages.len) {
|
||
let oldptest: str = readfile(strings.concat(works[si],
|
||
"/pkg-internal-test.a"));
|
||
let oldpxtestwwi: str = readfile(strings.concat(works[si],
|
||
"/pkg_test-external-test.wwi"));
|
||
let oldpxtesta: str = readfile(strings.concat(works[si],
|
||
"/pkg_test-external-test.a"));
|
||
let oldbridge: []str = ["", "", "", "", ""];
|
||
let xi: i32 = 0;
|
||
for (xi < suffixes.len) {
|
||
oldbridge[xi] = readfile(strings.concat(works[si], "/",
|
||
bridgeaction, suffixes[xi]));
|
||
xi += 1;
|
||
};
|
||
rewritefile(compilertraces[si], "");
|
||
rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
let env: []str = alloc([], (baseenv.len + 9): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < baseenv.len) {
|
||
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6A=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6L=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_GRAPH_COMPILER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_GRAPH_ASSEMBLER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_GRAPH_LINKER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_GRAPH_REAL_COMPILER=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_GRAPH_REAL_ASSEMBLER=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_GRAPH_REAL_LINKER=")) {
|
||
append(env, baseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(env, strings.concat("WW_W6C=", compilerwrapper));
|
||
append(env, strings.concat("WW_W6A=", assemblerwrapper));
|
||
append(env, strings.concat("WW_W6L=", linkerwrapper));
|
||
append(env, strings.concat("WW_GRAPH_COMPILER_TRACE=",
|
||
compilertraces[si]));
|
||
append(env, strings.concat("WW_GRAPH_ASSEMBLER_TRACE=",
|
||
assemblertraces[si]));
|
||
append(env, strings.concat("WW_GRAPH_LINKER_TRACE=",
|
||
linkertraces[si]));
|
||
append(env, strings.concat("WW_GRAPH_REAL_COMPILER=",
|
||
driver(compilers[si])));
|
||
append(env, strings.concat("WW_GRAPH_REAL_ASSEMBLER=",
|
||
driver(assemblers[si])));
|
||
append(env, strings.concat("WW_GRAPH_REAL_LINKER=",
|
||
driver(linkers[si])));
|
||
let av: []str = [driver(stages[si]), "test", "-c", "-w", works[si],
|
||
"-I", root, "--ww-package-test", "test", "pkg", "pkg", "pkg",
|
||
"pkg_test", pkg, bins[si], "-", statuses[si], pkg];
|
||
runcommandenv(root, strings.concat("graph-readd-external-", stages[si]),
|
||
av, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
let ct: str = readfile(compilertraces[si]);
|
||
let at: str = readfile(assemblertraces[si]);
|
||
let lt: str = readfile(linkertraces[si]);
|
||
assert(occurrences(ct, "\n") == 2);
|
||
assert(occurrences(ct,
|
||
"pkg_test-external-test.unit.new") == 1);
|
||
assert(occurrences(ct, "pkg-test-main.unit.new") == 1);
|
||
assert(!has(ct, "pkg-internal-test.unit.new"));
|
||
assert(!has(ct, strings.concat("/", bridgeaction, ".unit.new")));
|
||
let maincompile: str = linecontaining(ct,
|
||
"pkg-test-main.unit.new");
|
||
assert(occurrences(maincompile, "--test-target-package") == 2);
|
||
assert(pos(maincompile, "--test-target-package pkg ")
|
||
< pos(maincompile, "--test-target-package pkg_test "));
|
||
let externalcompile: str = linecontaining(ct,
|
||
"pkg_test-external-test.unit.new");
|
||
assert(has(externalcompile, "/pkg-internal-test.wwi"));
|
||
assert(has(externalcompile, strings.concat("/", bridgeaction,
|
||
".wwi")));
|
||
assert(occurrences(at, "\n") == 3);
|
||
assert(occurrences(lt, "\n") == 1);
|
||
let link: str = linecontaining(lt,
|
||
strings.concat("-o ", bins[si], ".new "));
|
||
assert(has(link, "/pkg-test-main.a.new"));
|
||
assert(has(link, "/pkg_test-external-test.a.new"));
|
||
assert(has(link, "/pkg-internal-test.a"));
|
||
assert(has(link, strings.concat("/", bridgeaction, ".a")));
|
||
assert(same(oldptest, readfile(strings.concat(works[si],
|
||
"/pkg-internal-test.a"))));
|
||
assert(same(oldpxtestwwi, readfile(strings.concat(works[si],
|
||
"/pkg_test-external-test.wwi"))));
|
||
assert(!same(oldpxtesta, readfile(strings.concat(works[si],
|
||
"/pkg_test-external-test.a"))));
|
||
xi = 0;
|
||
for (xi < suffixes.len) {
|
||
assert(same(oldbridge[xi], readfile(strings.concat(works[si], "/",
|
||
bridgeaction, suffixes[xi]))));
|
||
xi += 1;
|
||
};
|
||
let runav: []str = [bins[si]];
|
||
runcommand(root, strings.concat("graph-readd-run-", stages[si]),
|
||
runav, (60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(occurrences(out.stdout, "same_graph ... ok\n") == 1);
|
||
assert(occurrences(out.stdout, "external_graph ... ok\n") == 1);
|
||
let nc: str = normalizedtrace(ct, strings.concat(works[si], "/"),
|
||
bins[si]);
|
||
let na: str = normalizedtrace(at, strings.concat(works[si], "/"),
|
||
bins[si]);
|
||
let nl: str = normalizedtrace(lt, strings.concat(works[si], "/"),
|
||
bins[si]);
|
||
if (si == 0) {
|
||
readdcompiler = nc; readdassembler = na; readdlinker = nl;
|
||
readdbin = readfile(bins[si]);
|
||
readdout = strings.dup(out.stdout);
|
||
readderr = strings.dup(out.stderr);
|
||
} else {
|
||
assert(same(readdcompiler, nc));
|
||
assert(same(readdassembler, na));
|
||
assert(same(readdlinker, nl));
|
||
assert(same(readdbin, readfile(bins[si])));
|
||
assert(same(readdout, out.stdout));
|
||
assert(same(readderr, out.stderr));
|
||
};
|
||
si += 1;
|
||
};
|
||
|
||
// The public coordinator accepts one explicit output for the combined
|
||
// directory in both stages because variants are actions, not products.
|
||
let directbin: str = strings.concat(root, "/direct.test");
|
||
let directbytes: str = "";
|
||
let directout: str = "";
|
||
let directerr: str = "";
|
||
si = 0;
|
||
for (si < stages.len) {
|
||
let directav: []str = [driver(stages[si]), "test", "-c", "-o",
|
||
directbin, "-I", root, pkg];
|
||
runcommand(root, strings.concat("graph-direct-product-", stages[si]),
|
||
directav, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0);
|
||
assert(os.exists(directbin));
|
||
if (si == 0) {
|
||
directbytes = readfile(directbin);
|
||
directout = strings.dup(out.stdout);
|
||
directerr = strings.dup(out.stderr);
|
||
} else {
|
||
assert(same(directbytes, readfile(directbin)));
|
||
assert(same(directout, out.stdout));
|
||
assert(same(directerr, out.stderr));
|
||
};
|
||
clean(strings.concat(directbin, ".sepwork"));
|
||
clean(directbin);
|
||
si += 1;
|
||
};
|
||
clean(root);
|
||
};
|
||
|
||
@test fn multi_directory_shared_package_plan() void = {
|
||
let root: str = fresh();
|
||
let suite: str = strings.concat(root, "/suite");
|
||
let leaf: str = strings.concat(root, "/leaf");
|
||
let common: str = strings.concat(root, "/common");
|
||
let alpha: str = strings.concat(suite, "/alpha");
|
||
let beta: str = strings.concat(suite, "/beta");
|
||
let gamma: str = strings.concat(suite, "/gamma");
|
||
let alphasame: str = strings.concat(suite, "/_alpha_same");
|
||
let alphaexternal: str = strings.concat(suite, "/_alpha_external");
|
||
let betasame: str = strings.concat(suite, "/_beta_same");
|
||
let betaexternal: str = strings.concat(suite, "/_beta_external");
|
||
let dirs: []str = [suite, leaf, common, alpha, beta, gamma,
|
||
alphasame, alphaexternal, betasame, betaexternal];
|
||
let di: i32 = 0;
|
||
for (di < dirs.len) {
|
||
assert(os.mkdir(dirs[di], 448i32) == 0);
|
||
di += 1;
|
||
};
|
||
writefile(strings.concat(leaf, "/leaf.ww"), strings.concat(
|
||
"package leaf;\n// MULTIDIR_LEAF\n",
|
||
"export fn value() i32 = { return 40; };\n"));
|
||
writefile(strings.concat(common, "/common.ww"), strings.concat(
|
||
"package common;\nimport leaf;\n// MULTIDIR_COMMON\n",
|
||
"export fn value() i32 = { return leaf.value() + 1; };\n"));
|
||
writefile(strings.concat(alphasame, "/dep.ww"),
|
||
"package _alpha_same;\nexport fn value() i32 = { return 0; };\n");
|
||
writefile(strings.concat(alphaexternal, "/dep.ww"),
|
||
"package _alpha_external;\nexport fn value() i32 = { return 0; };\n");
|
||
writefile(strings.concat(betasame, "/dep.ww"),
|
||
"package _beta_same;\nexport fn value() i32 = { return 0; };\n");
|
||
writefile(strings.concat(betaexternal, "/dep.ww"),
|
||
"package _beta_external;\nexport fn value() i32 = { return 0; };\n");
|
||
writefile(strings.concat(alpha, "/a.ww"), strings.concat(
|
||
"package alpha;\nimport common;\n// MULTIDIR_ALPHA\n",
|
||
"export fn value() i32 = { return common.value() + 1; };\n"));
|
||
writefile(strings.concat(alpha, "/same_test.ww"), strings.concat(
|
||
"package alpha;\nimport _alpha_same;\n// MULTIDIR_ALPHA_SAME\n",
|
||
"export fn test_helper() i32 = { return 1; };\n",
|
||
"@test fn alpha_same_runs() void = {\n",
|
||
" assert(value() + _alpha_same.value() == 42);\n",
|
||
"};\n"));
|
||
writefile(strings.concat(alpha, "/external_test.ww"), strings.concat(
|
||
"package alpha_test;\nimport _alpha_external;\nimport alpha;\n",
|
||
"// MULTIDIR_ALPHA_EXTERNAL\n",
|
||
"@test fn alpha_external_runs() void = {\n",
|
||
" assert(alpha.value() + _alpha_external.value() == 42);\n",
|
||
" assert(alpha.test_helper() == 1);\n",
|
||
"};\n"));
|
||
writefile(strings.concat(gamma, "/gamma.ww"), strings.concat(
|
||
"package gamma;\n// MULTIDIR_GAMMA\n",
|
||
"export fn value() i32 = { return 0; };\n"));
|
||
writefile(strings.concat(beta, "/beta.ww"), strings.concat(
|
||
"package beta;\nimport alpha;\nimport gamma;\n// MULTIDIR_BETA\n",
|
||
"export fn value() i32 = { return alpha.value() + gamma.value(); };\n"));
|
||
writefile(strings.concat(beta, "/same_test.ww"), strings.concat(
|
||
"package beta;\nimport _beta_same;\n// MULTIDIR_BETA_SAME\n",
|
||
"export fn test_helper() i32 = { return 2; };\n",
|
||
"@test fn beta_same_runs() void = {\n",
|
||
" assert(value() + _beta_same.value() == 42);\n",
|
||
"};\n"));
|
||
writefile(strings.concat(beta, "/external_test.ww"), strings.concat(
|
||
"package beta_test;\nimport _beta_external;\nimport beta;\n",
|
||
"// MULTIDIR_BETA_EXTERNAL\n",
|
||
"@test fn beta_external_runs() void = {\n",
|
||
" assert(beta.value() + _beta_external.value() == 42);\n",
|
||
" assert(beta.test_helper() == 2);\n",
|
||
"};\n"));
|
||
|
||
let compilerwrapper: str = strings.concat(root, "/trace-w6c.sh");
|
||
let assemblerwrapper: str = strings.concat(root, "/trace-w6a.sh");
|
||
let linkerwrapper: str = strings.concat(root, "/trace-w6l.sh");
|
||
writeexecutable(compilerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_MULTI_COMPILER_TRACE\"\n",
|
||
"exec \"$WW_MULTI_REAL_COMPILER\" \"$@\"\n"));
|
||
writeexecutable(assemblerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_MULTI_ASSEMBLER_TRACE\"\n",
|
||
"exec \"$WW_MULTI_REAL_ASSEMBLER\" \"$@\"\n"));
|
||
writeexecutable(linkerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_MULTI_LINKER_TRACE\"\n",
|
||
"exec \"$WW_MULTI_REAL_LINKER\" \"$@\"\n"));
|
||
|
||
let stages: []str = ["ww", "ww_ww"];
|
||
let compilers: []str = ["w6c", "w6c_ww"];
|
||
let assemblers: []str = ["w6a", "w6a_ww"];
|
||
let linkers: []str = ["w6l", "w6l_ww"];
|
||
let works: []str = [strings.concat(root, "/c-work"),
|
||
strings.concat(root, "/ww-work")];
|
||
let compilertraces: []str = [strings.concat(root, "/c-w6c.trace"),
|
||
strings.concat(root, "/ww-w6c.trace")];
|
||
let assemblertraces: []str = [strings.concat(root, "/c-w6a.trace"),
|
||
strings.concat(root, "/ww-w6a.trace")];
|
||
let linkertraces: []str = [strings.concat(root, "/c-w6l.trace"),
|
||
strings.concat(root, "/ww-w6l.trace")];
|
||
let bins: []str = [strings.concat(root, "/alpha-product.test"),
|
||
strings.concat(root, "/beta-product.test")];
|
||
let statuses: []str = [strings.concat(root, "/alpha.status"),
|
||
strings.concat(root, "/beta.status"),
|
||
strings.concat(root, "/gamma.status")];
|
||
let gammabin: str = strings.concat(root, "/gamma-product.test");
|
||
let actions: []str = ["leaf", "common", "alpha", "gamma",
|
||
"_alpha_same", "_alpha_external", "_beta_same", "_beta_external",
|
||
"test", "alpha-internal-test", "alpha_test-external-test",
|
||
"alpha-test-main", "beta-internal-test",
|
||
"beta_test-external-test", "beta-test-main"];
|
||
let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"];
|
||
let references: []str = alloc([],
|
||
(actions.len * suffixes.len): u64)!;
|
||
let ri: i32 = 0;
|
||
for (ri < actions.len * suffixes.len) {
|
||
append(references, "");
|
||
ri += 1;
|
||
};
|
||
let referencealpha: str = "";
|
||
let referencebeta: str = "";
|
||
let referencecompiler: str = "";
|
||
let referenceassembler: str = "";
|
||
let referencelinker: str = "";
|
||
let baseenv: []str = os.getenvs();
|
||
let out: commandout;
|
||
let si: i32 = 0;
|
||
for (si < stages.len) {
|
||
assert(os.mkdir(works[si], 448i32) == 0);
|
||
writefile(compilertraces[si], "");
|
||
writefile(assemblertraces[si], "");
|
||
writefile(linkertraces[si], "");
|
||
let env: []str = alloc([], (baseenv.len + 9): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < baseenv.len) {
|
||
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6A=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6L=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_MULTI_COMPILER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_MULTI_ASSEMBLER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_MULTI_LINKER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_MULTI_REAL_COMPILER=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_MULTI_REAL_ASSEMBLER=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_MULTI_REAL_LINKER=")) {
|
||
append(env, baseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(env, strings.concat("WW_W6C=", compilerwrapper));
|
||
append(env, strings.concat("WW_W6A=", assemblerwrapper));
|
||
append(env, strings.concat("WW_W6L=", linkerwrapper));
|
||
append(env, strings.concat("WW_MULTI_COMPILER_TRACE=",
|
||
compilertraces[si]));
|
||
append(env, strings.concat("WW_MULTI_ASSEMBLER_TRACE=",
|
||
assemblertraces[si]));
|
||
append(env, strings.concat("WW_MULTI_LINKER_TRACE=",
|
||
linkertraces[si]));
|
||
append(env, strings.concat("WW_MULTI_REAL_COMPILER=",
|
||
driver(compilers[si])));
|
||
append(env, strings.concat("WW_MULTI_REAL_ASSEMBLER=",
|
||
driver(assemblers[si])));
|
||
append(env, strings.concat("WW_MULTI_REAL_LINKER=",
|
||
driver(linkers[si])));
|
||
|
||
let forward: []str = [driver(stages[si]), "test", "-c", "-w",
|
||
works[si], "-I", suite, "-I", root,
|
||
"--ww-package-test", "test", "alpha", "alpha", "alpha",
|
||
"alpha_test", alpha, bins[0], "-", statuses[0],
|
||
"--ww-package-test", "test", "beta", "beta", "beta",
|
||
"beta_test", beta, bins[1], "-", statuses[1],
|
||
"--ww-package-test", "test", "gamma", "gamma", "-", "-",
|
||
gamma, gammabin, "-", statuses[2], alpha];
|
||
let reverse: []str = [driver(stages[si]), "test", "-c", "-w",
|
||
works[si], "-I", suite, "-I", root,
|
||
"--ww-package-test", "test", "gamma", "gamma", "-", "-",
|
||
gamma, gammabin, "-", statuses[2],
|
||
"--ww-package-test", "test", "beta", "beta", "beta",
|
||
"beta_test", beta, bins[1], "-", statuses[1],
|
||
"--ww-package-test", "test", "alpha", "alpha", "alpha",
|
||
"alpha_test", alpha, bins[0], "-", statuses[0], alpha];
|
||
if (si == 0) {
|
||
runcommandenv(root, "multi-forward-c", forward, env,
|
||
(180i64 * (time.second: i64)): time.duration, &out);
|
||
} else {
|
||
runcommandenv(root, "multi-reverse-ww", reverse, env,
|
||
(180i64 * (time.second: i64)): time.duration, &out);
|
||
};
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
assert(os.exists(bins[0]) && os.exists(bins[1]));
|
||
assert(!os.exists(gammabin));
|
||
assert(same(readfile(statuses[0]), "ok\n"));
|
||
assert(same(readfile(statuses[1]), "ok\n"));
|
||
assert(same(readfile(statuses[2]), "ok\n"));
|
||
assert(!os.exists(strings.concat(works[si], "/beta.unit.ww")));
|
||
assert(!os.exists(strings.concat(works[si], "/gamma-main.unit.ww")));
|
||
assert(!os.exists(strings.concat(works[si],
|
||
"/alpha-internal-test-main.unit.ww")));
|
||
assert(!os.exists(strings.concat(works[si],
|
||
"/alpha_test-external-test-main.unit.ww")));
|
||
|
||
let ctrace: str = readfile(compilertraces[si]);
|
||
let atrace: str = readfile(assemblertraces[si]);
|
||
let ltrace: str = readfile(linkertraces[si]);
|
||
assert(occurrences(ctrace, "/alpha.unit.new") == 1);
|
||
assert(occurrences(ctrace, "/gamma.unit.new") == 1);
|
||
assert(occurrences(ctrace, "/beta.unit.new") == 0);
|
||
assert(occurrences(ctrace,
|
||
"/alpha-internal-test.unit.new") == 1);
|
||
assert(occurrences(ctrace,
|
||
"/alpha_test-external-test.unit.new") == 1);
|
||
assert(occurrences(ctrace, "/alpha-test-main.unit.new") == 1);
|
||
assert(occurrences(ctrace,
|
||
"/beta-internal-test.unit.new") == 1);
|
||
assert(occurrences(ctrace,
|
||
"/beta_test-external-test.unit.new") == 1);
|
||
assert(occurrences(ctrace, "/beta-test-main.unit.new") == 1);
|
||
assert(occurrences(ctrace, "--test-target-package") == 4);
|
||
let alphamain: str = linecontaining(ctrace,
|
||
"/alpha-test-main.unit.new");
|
||
assert(occurrences(alphamain, "--test-target-package") == 2);
|
||
assert(pos(alphamain, "--test-target-package alpha ")
|
||
< pos(alphamain, "--test-target-package alpha_test "));
|
||
let betamain: str = linecontaining(ctrace,
|
||
"/beta-test-main.unit.new");
|
||
assert(occurrences(betamain, "--test-target-package") == 2);
|
||
assert(pos(betamain, "--test-target-package beta ")
|
||
< pos(betamain, "--test-target-package beta_test "));
|
||
|
||
assert(occurrences(ltrace, "\n") == 2);
|
||
let alink: str = linecontaining(ltrace,
|
||
strings.concat("-o ", bins[0], ".new "));
|
||
assert(has(alink, "/alpha-test-main.a.new"));
|
||
assert(has(alink, "/alpha-internal-test.a.new"));
|
||
assert(has(alink, "/alpha_test-external-test.a.new"));
|
||
assert(!has(alink, strings.concat(works[si], "/alpha.a")));
|
||
assert(!has(alink, "/beta-internal-test.a.new"));
|
||
assert(!has(alink, "/gamma.a.new"));
|
||
assert(!has(alink, ".wwi"));
|
||
let blink: str = linecontaining(ltrace,
|
||
strings.concat("-o ", bins[1], ".new "));
|
||
assert(has(blink, "/beta-test-main.a.new"));
|
||
assert(has(blink, "/beta-internal-test.a.new"));
|
||
assert(has(blink, "/beta_test-external-test.a.new"));
|
||
assert(!has(blink, strings.concat(works[si], "/beta.a")));
|
||
assert(has(blink, strings.concat(works[si], "/alpha.a.new")));
|
||
assert(has(blink, strings.concat(works[si], "/gamma.a.new")));
|
||
assert(!has(blink, "/alpha-internal-test.a.new"));
|
||
assert(!has(blink, "/alpha_test-external-test.a.new"));
|
||
assert(!has(blink, ".wwi"));
|
||
|
||
let runav: []str = [bins[0]];
|
||
runcommand(root, strings.concat("multi-alpha-run-", stages[si]),
|
||
runav, (60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(occurrences(out.stdout, "alpha_same_runs ... ok\n") == 1);
|
||
assert(occurrences(out.stdout, "alpha_external_runs ... ok\n") == 1);
|
||
let betarunav: []str = [bins[1]];
|
||
runcommand(root, strings.concat("multi-beta-run-", stages[si]),
|
||
betarunav, (60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(occurrences(out.stdout, "beta_same_runs ... ok\n") == 1);
|
||
assert(occurrences(out.stdout, "beta_external_runs ... ok\n") == 1);
|
||
|
||
ri = 0;
|
||
for (ri < actions.len) {
|
||
let xi: i32 = 0;
|
||
for (xi < suffixes.len) {
|
||
let bytes: str = readfile(strings.concat(works[si], "/",
|
||
actions[ri], suffixes[xi]));
|
||
let index: i32 = ri * suffixes.len + xi;
|
||
if (si == 0) { references[index] = strings.dup(bytes); }
|
||
else { assert(same(references[index], bytes)); };
|
||
xi += 1;
|
||
};
|
||
ri += 1;
|
||
};
|
||
let nc: str = normalizedtrace(ctrace,
|
||
strings.concat(works[si], "/"), bins[0]);
|
||
let na: str = normalizedtrace(atrace,
|
||
strings.concat(works[si], "/"), bins[0]);
|
||
let nl: str = normalizedtrace(ltrace,
|
||
strings.concat(works[si], "/"), bins[0]);
|
||
if (si == 0) {
|
||
referencealpha = readfile(bins[0]);
|
||
referencebeta = readfile(bins[1]);
|
||
referencecompiler = nc;
|
||
referenceassembler = na;
|
||
referencelinker = nl;
|
||
} else {
|
||
assert(same(referencealpha, readfile(bins[0])));
|
||
assert(same(referencebeta, readfile(bins[1])));
|
||
assert(same(referencecompiler, nc));
|
||
assert(same(referenceassembler, na));
|
||
assert(same(referencelinker, nl));
|
||
};
|
||
|
||
// Every unchanged action is warm-reused. Only the two requested
|
||
// directory binaries are linked again; gamma remains compile-only.
|
||
// The opposite descriptor order is warm in each stage, so product
|
||
// order cannot enter action identity or invalidation.
|
||
let warmalpha: str = readfile(bins[0]);
|
||
let warmbeta: str = readfile(bins[1]);
|
||
rewritefile(compilertraces[si], "");
|
||
rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
if (si == 0) {
|
||
runcommandenv(root, "multi-warm-reverse-c", reverse, env,
|
||
(180i64 * (time.second: i64)): time.duration, &out);
|
||
} else {
|
||
runcommandenv(root, "multi-warm-forward-ww", forward, env,
|
||
(180i64 * (time.second: i64)): time.duration, &out);
|
||
};
|
||
expectexit(&out, 0);
|
||
assert(readfile(compilertraces[si]).len == 0);
|
||
assert(readfile(assemblertraces[si]).len == 0);
|
||
assert(occurrences(readfile(linkertraces[si]), "\n") == 2);
|
||
assert(same(warmalpha, readfile(bins[0])));
|
||
assert(same(warmbeta, readfile(bins[1])));
|
||
si += 1;
|
||
};
|
||
|
||
// Public root order is presentation-only. Default -c destinations are in
|
||
// the invocation cwd, and the selected no-test package publishes nothing.
|
||
let publicalpha: str = strings.concat(root, "/alpha.test");
|
||
let publicbeta: str = strings.concat(root, "/beta.test");
|
||
let publicgamma: str = strings.concat(root, "/gamma.test");
|
||
let publicout: str = "";
|
||
let publicerr: str = "";
|
||
let publicalphabytes: str = "";
|
||
let publicbetabytes: str = "";
|
||
let publiclabels: []str = ["c-forward", "c-reverse",
|
||
"ww-forward", "ww-reverse"];
|
||
let pi: i32 = 0;
|
||
for (pi < 4) {
|
||
let publicdriver: str = driver("ww");
|
||
if (pi >= 2) { publicdriver = driver("ww_ww"); };
|
||
let publicav: []str = alloc([], 16u64)!;
|
||
append(publicav, publicdriver);
|
||
append(publicav, "test"); append(publicav, "-c");
|
||
append(publicav, "-j"); append(publicav, "1");
|
||
append(publicav, "-I"); append(publicav, suite);
|
||
append(publicav, "-I"); append(publicav, root);
|
||
if (pi % 2 == 0) {
|
||
append(publicav, alpha); append(publicav, beta);
|
||
append(publicav, gamma);
|
||
} else {
|
||
append(publicav, gamma); append(publicav, beta);
|
||
append(publicav, alpha);
|
||
};
|
||
runcommanddir(root, strings.concat("multi-public-order-",
|
||
publiclabels[pi]), root, publicav,
|
||
(180i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(occurrences(out.stdout, "built ") == 0);
|
||
assert(has(out.stdout, strings.concat(
|
||
"? ", gamma, " [no tests]\n")));
|
||
assert(os.exists(publicalpha) && os.exists(publicbeta));
|
||
assert(!os.exists(publicgamma));
|
||
assert(!os.exists(strings.concat(alpha, "/alpha_test.test")));
|
||
assert(!os.exists(strings.concat(beta, "/beta_test.test")));
|
||
if (pi == 0) {
|
||
publicout = strings.dup(out.stdout);
|
||
publicerr = strings.dup(out.stderr);
|
||
publicalphabytes = readfile(publicalpha);
|
||
publicbetabytes = readfile(publicbeta);
|
||
} else {
|
||
assert(same(publicout, out.stdout));
|
||
assert(same(publicerr, out.stderr));
|
||
assert(same(publicalphabytes, readfile(publicalpha)));
|
||
assert(same(publicbetabytes, readfile(publicbeta)));
|
||
};
|
||
clean(publicalpha); clean(publicbeta); clean(publicgamma);
|
||
pi += 1;
|
||
};
|
||
clean(root);
|
||
};
|
||
|
||
@test fn shared_package_resolution_context_conflict_is_stable() void = {
|
||
let root: str = fresh();
|
||
let suite: str = strings.concat(root, "/suite");
|
||
let a: str = strings.concat(suite, "/a");
|
||
let b: str = strings.concat(suite, "/b");
|
||
let good: str = strings.concat(suite, "/good");
|
||
let aleaf: str = strings.concat(a, "/_leaf");
|
||
let bleaf: str = strings.concat(b, "/_leaf");
|
||
let common: str = strings.concat(root, "/common");
|
||
let dirs: []str = [suite, a, b, good, aleaf, bleaf, common];
|
||
let di: i32 = 0;
|
||
for (di < dirs.len) { assert(os.mkdir(dirs[di], 448i32) == 0); di += 1; };
|
||
writefile(strings.concat(aleaf, "/_leaf.ww"),
|
||
"package _leaf;\nexport fn value() i32 = { return 20; };\n");
|
||
writefile(strings.concat(bleaf, "/_leaf.ww"),
|
||
"package _leaf;\nexport fn value() i32 = { return 21; };\n");
|
||
writefile(strings.concat(common, "/common.ww"), strings.concat(
|
||
"package common;\nimport _leaf;\n",
|
||
"export fn value() i32 = { return _leaf.value(); };\n"));
|
||
writefile(strings.concat(a, "/a.ww"),
|
||
"package a;\nimport common;\nfn value() i32 = { return common.value(); };\n");
|
||
writefile(strings.concat(a, "/a_test.ww"),
|
||
"package a;\n@test fn a_context() void = { assert(value() == 20); };\n");
|
||
writefile(strings.concat(b, "/b.ww"),
|
||
"package b;\nimport common;\nfn value() i32 = { return common.value(); };\n");
|
||
writefile(strings.concat(b, "/b_test.ww"),
|
||
"package b;\n@test fn b_context() void = { assert(value() == 21); };\n");
|
||
writefile(strings.concat(good, "/good.ww"),
|
||
"package good;\nfn value() i32 = { return 42; };\n");
|
||
writefile(strings.concat(good, "/good_test.ww"),
|
||
"package good;\n@test fn unrelated_runs() void = { assert(value() == 42); };\n");
|
||
let tooltrace: str = strings.concat(root, "/context-tool.trace");
|
||
let compilerwrapper: str = strings.concat(root, "/context-w6c.sh");
|
||
let linkerwrapper: str = strings.concat(root, "/context-w6l.sh");
|
||
writefile(tooltrace, "");
|
||
writeexecutable(compilerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf 'compile\\n' >> \"$WW_CONTEXT_TRACE\"\n",
|
||
"exec \"$WW_CONTEXT_W6C\" \"$@\"\n"));
|
||
writeexecutable(linkerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf 'link\\n' >> \"$WW_CONTEXT_TRACE\"\n",
|
||
"exec \"$WW_CONTEXT_W6L\" \"$@\"\n"));
|
||
let baseenv: []str = os.getenvs();
|
||
let traceenv: []str = alloc([], (baseenv.len + 5): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < baseenv.len) {
|
||
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6L=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_CONTEXT_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_CONTEXT_W6C=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_CONTEXT_W6L=")) {
|
||
append(traceenv, baseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(traceenv, strings.concat("WW_W6C=", compilerwrapper));
|
||
append(traceenv, strings.concat("WW_W6L=", linkerwrapper));
|
||
append(traceenv, strings.concat("WW_CONTEXT_TRACE=", tooltrace));
|
||
append(traceenv, strings.concat("WW_CONTEXT_W6C=", driver("w6c")));
|
||
append(traceenv, strings.concat("WW_CONTEXT_W6L=", driver("w6l")));
|
||
let stages: []str = ["ww", "ww_ww"];
|
||
let jobs: []str = ["1", "4"];
|
||
let diagnostics: []str = ["", ""];
|
||
let referenceout: str = "";
|
||
let out: commandout;
|
||
let i: i32 = 0;
|
||
for (i < stages.len) {
|
||
let av: []str = [driver(stages[i]), "test", "-j", jobs[i],
|
||
"-I", root, strings.concat(suite, "/...")];
|
||
let stageenv: []str = traceenv;
|
||
if (i == 1) {
|
||
let wwenv: []str = alloc([], (baseenv.len + 5): u64)!;
|
||
stageenv = wwenv;
|
||
ei = 0;
|
||
for (ei < baseenv.len) {
|
||
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6L=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_CONTEXT_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_CONTEXT_W6C=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_CONTEXT_W6L=")) {
|
||
append(stageenv, baseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(stageenv, strings.concat("WW_W6C=", compilerwrapper));
|
||
append(stageenv, strings.concat("WW_W6L=", linkerwrapper));
|
||
append(stageenv, strings.concat("WW_CONTEXT_TRACE=", tooltrace));
|
||
append(stageenv, strings.concat("WW_CONTEXT_W6C=", driver("w6c_ww")));
|
||
append(stageenv, strings.concat("WW_CONTEXT_W6L=", driver("w6l_ww")));
|
||
};
|
||
runcommandenv(root, strings.concat("context-conflict-", stages[i]), av,
|
||
stageenv, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, strings.concat(
|
||
"ww: package _leaf resolves to directories ", aleaf,
|
||
" and ", bleaf, "\n")));
|
||
assert(has(out.stderr, strings.concat("FAIL ", a,
|
||
" [a] (build exit 1)\n")));
|
||
assert(has(out.stderr, strings.concat("FAIL ", b,
|
||
" [b] (build exit 1)\n")));
|
||
assert(has(out.stderr, strings.concat("FAIL ", good,
|
||
" [good] (build exit 1)\n")));
|
||
assert(!has(out.stdout, "unrelated_runs"));
|
||
diagnostics[i] = strings.dup(primarydiagnostic(out.stderr));
|
||
if (i == 0) { referenceout = strings.dup(out.stdout); }
|
||
else { assert(same(referenceout, out.stdout)); };
|
||
i += 1;
|
||
};
|
||
assert(same(diagnostics[0], diagnostics[1]));
|
||
assert(readfile(tooltrace).len == 0);
|
||
clean(root);
|
||
};
|
||
|
||
@test fn mixed_named_test_and_toolchain_test_actions_do_not_collide() void = {
|
||
let root: str = fresh();
|
||
let suite: str = strings.concat(root, "/suite");
|
||
let named: str = strings.concat(suite, "/test");
|
||
let consumer: str = strings.concat(suite, "/zconsumer");
|
||
assert(os.mkdir(suite, 448i32) == 0);
|
||
assert(os.mkdir(named, 448i32) == 0);
|
||
assert(os.mkdir(consumer, 448i32) == 0);
|
||
writefile(strings.concat(named, "/named.ww"), strings.concat(
|
||
"package test;\n// MIXED_USER_TEST_PRODUCTION\n",
|
||
"fn hidden() i32 = { return 19; };\n",
|
||
"export fn value() i32 = { return 42; };\n"));
|
||
writefile(strings.concat(named, "/same_test.ww"), strings.concat(
|
||
"package test;\n@test fn named_same_runs() void = {",
|
||
" assert(hidden() == 19); };\n"));
|
||
writefile(strings.concat(named, "/external_test.ww"), strings.concat(
|
||
"package test_test;\nimport test;\n",
|
||
"@test fn named_external_runs() void = {",
|
||
" assert(test.value() == 42); };\n"));
|
||
writefile(strings.concat(consumer, "/consumer.ww"), strings.concat(
|
||
"package zconsumer;\n",
|
||
"// MIXED_GENERATED_TOOLCHAIN_TEST_SUPPORT\n",
|
||
"export fn value() i32 = { return 42; };\n"));
|
||
writefile(strings.concat(consumer, "/same_test.ww"), strings.concat(
|
||
"package zconsumer;\n@test fn consumer_same_runs() void = {",
|
||
" assert(value() == 42); };\n"));
|
||
writefile(strings.concat(consumer, "/external_test.ww"), strings.concat(
|
||
"package zconsumer_test;\nimport zconsumer;\n",
|
||
"@test fn consumer_external_runs() void = {",
|
||
" assert(zconsumer.value() == 42); };\n"));
|
||
let linktrace: str = strings.concat(root, "/mixed-link.trace");
|
||
let linkwrapper: str = strings.concat(root, "/mixed-w6l.sh");
|
||
writefile(linktrace, "");
|
||
writeexecutable(linkwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_MIXED_LINK_TRACE\"\n",
|
||
"exec \"$WW_MIXED_W6L\" \"$@\"\n"));
|
||
let baseenv: []str = os.getenvs();
|
||
let linkenv: []str = alloc([], (baseenv.len + 3): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < baseenv.len) {
|
||
if (!strings.hasprefix(baseenv[ei], "WW_W6L=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_MIXED_LINK_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_MIXED_W6L=")) {
|
||
append(linkenv, baseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(linkenv, strings.concat("WW_W6L=", linkwrapper));
|
||
append(linkenv, strings.concat("WW_MIXED_LINK_TRACE=", linktrace));
|
||
append(linkenv, strings.concat("WW_MIXED_W6L=", driver("w6l")));
|
||
let bins: []str = [strings.concat(named, "/test.test"),
|
||
strings.concat(consumer, "/zconsumer.test")];
|
||
let statuses: []str = [strings.concat(root, "/mixed-test.status"),
|
||
strings.concat(root, "/mixed-zconsumer.status")];
|
||
let keys: []str = ["test-internal-test", "test_test-external-test",
|
||
"test-test-main", "zconsumer-internal-test",
|
||
"zconsumer_test-external-test", "zconsumer-test-main"];
|
||
let workroot: str = strings.concat(bins[0], ".sepwork");
|
||
let work: str = strings.concat(workroot, "/");
|
||
let referenceunits: []str = ["", "", "", "", "", ""];
|
||
let stages: []str = ["ww", "ww_ww"];
|
||
let out: commandout;
|
||
let si: i32 = 0;
|
||
for (si < stages.len) {
|
||
assert(os.mkdir(workroot, 448i32) == 0);
|
||
let av: []str = [driver(stages[si]), "test", "-c", "-w", workroot,
|
||
"-I", suite,
|
||
"--ww-package-test", "test", "test", "test", "test",
|
||
"test_test", named, bins[0], "-", statuses[0],
|
||
"--ww-package-test", "test", "zconsumer", "zconsumer",
|
||
"zconsumer", "zconsumer_test", consumer, bins[1], "-",
|
||
statuses[1], named];
|
||
if (si == 0) {
|
||
runcommandenv(root, "mixed-actions-c", av, linkenv,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
} else {
|
||
runcommand(root, "mixed-actions-ww", av,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
};
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
let namedprod: str = readfile(strings.concat(work,
|
||
"test-internal-test.unit.ww"));
|
||
let support: str = readfile(strings.concat(work, "__wwtest.unit.ww"));
|
||
assert(has(namedprod, "MIXED_USER_TEST_PRODUCTION"));
|
||
assert(has(namedprod, "//ww:module-reset test\n"));
|
||
assert(has(support, "//ww:module-reset __wwtest\n"));
|
||
let suffixes: []str = [".unit.ww", ".wwi", ".a"];
|
||
let actions: []str = ["__wwtest", "test-internal-test",
|
||
"test_test-external-test", "test-test-main",
|
||
"zconsumer-internal-test", "zconsumer_test-external-test",
|
||
"zconsumer-test-main"];
|
||
let ai: i32 = 0;
|
||
for (ai < actions.len) {
|
||
let xi: i32 = 0;
|
||
for (xi < suffixes.len) {
|
||
assert(os.exists(strings.concat(work, actions[ai], suffixes[xi])));
|
||
xi += 1;
|
||
};
|
||
ai += 1;
|
||
};
|
||
let ri: i32 = 0;
|
||
for (ri < keys.len) {
|
||
let unit: str = readfile(strings.concat(work, keys[ri], ".unit.ww"));
|
||
assert(!has(unit, "//ww:module "));
|
||
if (si == 0) { referenceunits[ri] = strings.dup(unit); }
|
||
else { assert(same(referenceunits[ri], unit)); };
|
||
ri += 1;
|
||
};
|
||
let namedrun: []str = [bins[0]];
|
||
runcommand(root, strings.concat("mixed-run-", stages[si], "-named"),
|
||
namedrun, (60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout, "named_same_runs ... ok\n"));
|
||
assert(has(out.stdout, "named_external_runs ... ok\n"));
|
||
let consumerrun: []str = [bins[1]];
|
||
runcommand(root, strings.concat("mixed-run-", stages[si], "-consumer"),
|
||
consumerrun, (60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout, "consumer_same_runs ... ok\n"));
|
||
assert(has(out.stdout, "consumer_external_runs ... ok\n"));
|
||
if (si == 0) {
|
||
let links: str = readfile(linktrace);
|
||
assert(occurrences(links, "\n") == 2);
|
||
let namedlink: str = linecontaining(links,
|
||
strings.concat("-o ", bins[0], ".new "));
|
||
let consumerlink: str = linecontaining(links,
|
||
strings.concat("-o ", bins[1], ".new "));
|
||
assert(has(namedlink, strings.concat(work, "__wwtest.a")));
|
||
assert(has(namedlink, strings.concat(work,
|
||
"test-test-main.a")));
|
||
assert(has(namedlink, strings.concat(work,
|
||
"test-internal-test.a")));
|
||
assert(has(namedlink, strings.concat(work,
|
||
"test_test-external-test.a")));
|
||
assert(!has(namedlink, strings.concat(work, "test.a")));
|
||
assert(has(consumerlink, strings.concat(work, "__wwtest.a")));
|
||
assert(has(consumerlink, strings.concat(work,
|
||
"zconsumer-test-main.a")));
|
||
assert(has(consumerlink, strings.concat(work,
|
||
"zconsumer-internal-test.a")));
|
||
assert(has(consumerlink, strings.concat(work,
|
||
"zconsumer_test-external-test.a")));
|
||
assert(!has(consumerlink, strings.concat(work, "test.a")));
|
||
};
|
||
if (si == 0) {
|
||
clean(workroot);
|
||
let bi: i32 = 0;
|
||
for (bi < bins.len) { clean(bins[bi]); bi += 1; };
|
||
};
|
||
si += 1;
|
||
};
|
||
clean(root);
|
||
};
|
||
|
||
@test fn long_shared_link_closure_is_complete() void = {
|
||
let root: str = fresh();
|
||
let d1: str = strings.concat(root,
|
||
"/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
|
||
let d2: str = strings.concat(d1,
|
||
"/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
|
||
let d3: str = strings.concat(d2,
|
||
"/cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc");
|
||
let target: str = strings.concat(d3, "/target");
|
||
let chainroot: str = strings.concat(d3, "/chain");
|
||
assert(os.mkdir(d1, 448i32) == 0);
|
||
assert(os.mkdir(d2, 448i32) == 0);
|
||
assert(os.mkdir(d3, 448i32) == 0);
|
||
assert(os.mkdir(target, 448i32) == 0);
|
||
assert(os.mkdir(chainroot, 448i32) == 0);
|
||
let packagecount: i32 = 300;
|
||
let i: i32 = 0;
|
||
for (i < packagecount) {
|
||
let name: str = boundarypkgname(i);
|
||
let dir: str = strings.concat(root, "/", name);
|
||
assert(os.mkdir(dir, 448i32) == 0);
|
||
let a: str = "";
|
||
if (i + 1 < packagecount) {
|
||
let next: str = boundarypkgname(i + 1);
|
||
a = strings.concat("package ", name, ";\nimport ", next,
|
||
";\n// OWNER_A_", name,
|
||
"\nexport fn value() i32 = { return ", next,
|
||
".value(); };\n");
|
||
} else {
|
||
a = strings.concat("package ", name, ";\n// OWNER_A_", name,
|
||
"\nexport fn value() i32 = { return 42; };\n");
|
||
};
|
||
let z: str = strings.concat("package ", name, ";\n// OWNER_Z_",
|
||
name, "\nfn private_value() i32 = { return value(); };\n");
|
||
writefile(strings.concat(dir, "/a.ww"), a);
|
||
writefile(strings.concat(dir, "/z.ww"), z);
|
||
i += 1;
|
||
};
|
||
let roota: str = "package main;\n// ROOT_A\n";
|
||
i = 0;
|
||
for (i < packagecount) {
|
||
if (i != 150) {
|
||
roota = strings.concat(roota, "import ", boundarypkgname(i), ";\n");
|
||
};
|
||
i += 1;
|
||
};
|
||
i = 0;
|
||
for (i < packagecount) {
|
||
if (i != 150) {
|
||
let name: str = boundarypkgname(i);
|
||
roota = strings.concat(roota, "fn use_", name,
|
||
"() i32 = { return ", name, ".value(); };\n");
|
||
};
|
||
i += 1;
|
||
};
|
||
let rootz: str = strings.concat("package main;\nimport p150;\n",
|
||
"// ROOT_Z\nfn main() i32 = { return p150.value() - 42; };\n");
|
||
writefile(strings.concat(target, "/a.ww"), roota);
|
||
writefile(strings.concat(target, "/z.ww"), rootz);
|
||
let chainsource: str = strings.concat("package main;\nimport p000;\n",
|
||
"// CHAIN_ROOT\nfn main() i32 = { return p000.value() - 42; };\n");
|
||
writefile(strings.concat(chainroot, "/main.ww"), chainsource);
|
||
let rootaction: str = "target";
|
||
let chainaction: str = "chain";
|
||
|
||
let compilerwrapper: str = strings.concat(root, "/boundary-w6c.sh");
|
||
let assemblerwrapper: str = strings.concat(root, "/boundary-w6a.sh");
|
||
let linkerwrapper: str = strings.concat(root, "/boundary-w6l.sh");
|
||
writeexecutable(compilerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf 'BEGIN' >> \"$WW_BOUNDARY_COMPILER_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||
"\"$WW_BOUNDARY_COMPILER_TRACE\"; done\n",
|
||
"printf '\\n' >> \"$WW_BOUNDARY_COMPILER_TRACE\"\n",
|
||
"exec \"$WW_BOUNDARY_W6C\" \"$@\"\n"));
|
||
writeexecutable(assemblerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf 'BEGIN' >> \"$WW_BOUNDARY_ASSEMBLER_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||
"\"$WW_BOUNDARY_ASSEMBLER_TRACE\"; done\n",
|
||
"printf '\\n' >> \"$WW_BOUNDARY_ASSEMBLER_TRACE\"\n",
|
||
"exec \"$WW_BOUNDARY_W6A\" \"$@\"\n"));
|
||
writeexecutable(linkerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf 'BEGIN' >> \"$WW_BOUNDARY_LINKER_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||
"\"$WW_BOUNDARY_LINKER_TRACE\"; done\n",
|
||
"printf '\\n' >> \"$WW_BOUNDARY_LINKER_TRACE\"\n",
|
||
"exec \"$WW_BOUNDARY_W6L\" \"$@\"\n"));
|
||
let stages: []str = ["ww", "ww_ww"];
|
||
let compilers: []str = ["w6c", "w6c_ww"];
|
||
let assemblers: []str = ["w6a", "w6a_ww"];
|
||
let linkers: []str = ["w6l", "w6l_ww"];
|
||
let works: []str = [strings.concat(root, "/boundary-c-work"),
|
||
strings.concat(root, "/boundary-ww-work")];
|
||
let bins: []str = [strings.concat(root, "/boundary-c-bin"),
|
||
strings.concat(root, "/boundary-ww-bin")];
|
||
let chainbins: []str = [strings.concat(root, "/boundary-c-chain-bin"),
|
||
strings.concat(root, "/boundary-ww-chain-bin")];
|
||
let compilertraces: []str = [strings.concat(root, "/boundary-c-compiler"),
|
||
strings.concat(root, "/boundary-ww-compiler")];
|
||
let assemblertraces: []str = [strings.concat(root, "/boundary-c-assembler"),
|
||
strings.concat(root, "/boundary-ww-assembler")];
|
||
let linkertraces: []str = [strings.concat(root, "/boundary-c-linker"),
|
||
strings.concat(root, "/boundary-ww-linker")];
|
||
let baseenv: []str = os.getenvs();
|
||
let environments: [][]str = alloc([], stages.len: u64)!;
|
||
let out: commandout;
|
||
let si: i32 = 0;
|
||
for (si < stages.len) {
|
||
assert(os.mkdir(works[si], 448i32) == 0);
|
||
writefile(compilertraces[si], "");
|
||
writefile(assemblertraces[si], "");
|
||
writefile(linkertraces[si], "");
|
||
let env: []str = alloc([], (baseenv.len + 11): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < baseenv.len) {
|
||
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6A=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6L=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_BOUNDARY_COMPILER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_BOUNDARY_ASSEMBLER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_BOUNDARY_LINKER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_BOUNDARY_W6C=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_BOUNDARY_W6A=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_BOUNDARY_W6L=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_LIB=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_SRCLIB=")) {
|
||
append(env, baseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(env, strings.concat("WW_W6C=", compilerwrapper));
|
||
append(env, strings.concat("WW_W6A=", assemblerwrapper));
|
||
append(env, strings.concat("WW_W6L=", linkerwrapper));
|
||
append(env, strings.concat("WW_BOUNDARY_COMPILER_TRACE=",
|
||
compilertraces[si]));
|
||
append(env, strings.concat("WW_BOUNDARY_ASSEMBLER_TRACE=",
|
||
assemblertraces[si]));
|
||
append(env, strings.concat("WW_BOUNDARY_LINKER_TRACE=",
|
||
linkertraces[si]));
|
||
append(env, strings.concat("WW_BOUNDARY_W6C=",
|
||
driver(compilers[si])));
|
||
append(env, strings.concat("WW_BOUNDARY_W6A=",
|
||
driver(assemblers[si])));
|
||
append(env, strings.concat("WW_BOUNDARY_W6L=",
|
||
driver(linkers[si])));
|
||
append(env, strings.concat("WW_LIB=", repo(), "/out/bin/../lib"));
|
||
append(env, strings.concat("WW_SRCLIB=", repo(), "/lib"));
|
||
append(environments, env);
|
||
let av: []str = [driver(stages[si]), "build", "-w", works[si],
|
||
"-I", d3, "-I", root, "-o", bins[si], target];
|
||
runcommandenv(root, strings.concat("boundary-cold-", stages[si]), av,
|
||
env, (600i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
let runav: []str = [bins[si]];
|
||
runcommand(root, strings.concat("boundary-run-", stages[si]), runav,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
|
||
let ctrace: str = readfile(compilertraces[si]);
|
||
let atrace: str = readfile(assemblertraces[si]);
|
||
let ltrace: str = readfile(linkertraces[si]);
|
||
assert(occurrences(ctrace, "\n") == packagecount + 1);
|
||
assert(occurrences(atrace, "\n") == packagecount + 2);
|
||
assert(occurrences(ltrace, "\n") == 1);
|
||
let rootline: str = linecontaining(ctrace,
|
||
strings.concat("/", rootaction, ".unit.new"));
|
||
assert(occurrences(rootline, "<--import>") == packagecount);
|
||
let expectedroot: str = strings.concat(
|
||
"BEGIN<--entry><--package-init-symbol><__ww..pkg.p.",
|
||
rootaction, ".v0.r0.init><--init-dispatch-symbol>",
|
||
"<__ww..dispatch><-c>");
|
||
i = 0;
|
||
for (i < packagecount) {
|
||
let name: str = boundarypkgname(i);
|
||
expectedroot = strings.concat(expectedroot, "<--import><", name,
|
||
"><", works[si], "/", name, ".wwi.new>");
|
||
assert(occurrences(rootline, strings.concat("<", name, "><",
|
||
works[si], "/", name, ".wwi.new>")) == 1);
|
||
i += 1;
|
||
};
|
||
expectedroot = strings.concat(expectedroot, "<-I><", works[si], "/",
|
||
rootaction, ".wwi.new><-o><", works[si], "/", rootaction,
|
||
".s.new><", works[si], "/", rootaction, ".unit.new>");
|
||
assert(same(rootline, expectedroot));
|
||
i = 0;
|
||
for (i < packagecount) {
|
||
let name: str = boundarypkgname(i);
|
||
let expectedcompile: str = strings.concat(
|
||
"BEGIN<--package-init-symbol><__ww..pkg.p.", name,
|
||
".v0.r0.init><-c>");
|
||
if (i + 1 < packagecount) {
|
||
let next: str = boundarypkgname(i + 1);
|
||
expectedcompile = strings.concat(expectedcompile,
|
||
"<--import><", next, "><", works[si], "/", next,
|
||
".wwi.new>");
|
||
};
|
||
expectedcompile = strings.concat(expectedcompile, "<-I><",
|
||
works[si], "/", name, ".wwi.new><-o><", works[si], "/",
|
||
name, ".s.new><", works[si], "/", name, ".unit.new>");
|
||
assert(same(linecontaining(ctrace,
|
||
strings.concat("/", name, ".unit.new")), expectedcompile));
|
||
let expectedassemble: str = strings.concat("BEGIN<-o><", works[si],
|
||
"/", name, ".o.new><", works[si], "/", name, ".s.new>");
|
||
assert(same(linecontaining(atrace,
|
||
strings.concat("/", name, ".o.new")), expectedassemble));
|
||
assert(occurrences(ltrace, strings.concat("<", works[si], "/",
|
||
name, ".a.new>")) == 1);
|
||
assert(os.exists(strings.concat(works[si], "/", name, ".wwi")));
|
||
assert(os.exists(strings.concat(works[si], "/", name, ".s")));
|
||
assert(os.exists(strings.concat(works[si], "/", name, ".o")));
|
||
assert(os.exists(strings.concat(works[si], "/", name, ".a")));
|
||
let a: str = "";
|
||
if (i + 1 < packagecount) {
|
||
let next: str = boundarypkgname(i + 1);
|
||
a = strings.concat("package ", name, ";\nimport ", next,
|
||
";\n// OWNER_A_", name,
|
||
"\nexport fn value() i32 = { return ", next,
|
||
".value(); };\n");
|
||
} else {
|
||
a = strings.concat("package ", name, ";\n// OWNER_A_", name,
|
||
"\nexport fn value() i32 = { return 42; };\n");
|
||
};
|
||
let z: str = strings.concat("package ", name, ";\n// OWNER_Z_",
|
||
name, "\nfn private_value() i32 = { return value(); };\n");
|
||
let unit: str = readfile(strings.concat(works[si], "/", name,
|
||
".unit.ww"));
|
||
assert(strings.hasprefix(unit, strings.concat("//ww:module-reset ",
|
||
name, "\n", a, "\n//ww:module-reset ", name, "\n", z, "\n")));
|
||
if (i + 1 < packagecount) {
|
||
assert(has(unit, strings.concat("//ww:direct-export ",
|
||
boundarypkgname(i + 1), " ")));
|
||
};
|
||
assert(strings.hasprefix(readfile(strings.concat(works[si], "/",
|
||
name, ".wwi")), strings.concat("//ww:module ", name, "\n")));
|
||
i += 1;
|
||
};
|
||
assert(occurrences(ltrace, strings.concat("<", works[si], "/",
|
||
rootaction, ".a.new>")) == 1);
|
||
assert(!has(ltrace, ".wwi>"));
|
||
let expectedlink: str = strings.concat("BEGIN<-o><", bins[si], ".new><",
|
||
works[si], "/", rootaction, ".a.new>");
|
||
i = 0;
|
||
for (i < packagecount) {
|
||
expectedlink = strings.concat(expectedlink, "<", works[si], "/",
|
||
boundarypkgname(i), ".a.new>");
|
||
i += 1;
|
||
};
|
||
expectedlink = strings.concat(expectedlink, "<", repo(),
|
||
"/out/bin/../lib/libwwrt.a>\n");
|
||
assert(same(ltrace, expectedlink));
|
||
let rootunit: str = readfile(strings.concat(works[si], "/", rootaction,
|
||
".unit.ww"));
|
||
assert(strings.hasprefix(rootunit,
|
||
strings.concat("//ww:module-reset target\n", roota,
|
||
"\n//ww:module-reset target\n", rootz, "\n")));
|
||
assert(occurrences(rootunit, "//ww:direct-export p") == packagecount);
|
||
let rootassemble: str = strings.concat("BEGIN<-o><", works[si], "/",
|
||
rootaction, ".o.new><", works[si], "/", rootaction, ".s.new>");
|
||
assert(same(linecontaining(atrace,
|
||
strings.concat("/", rootaction, ".o.new")), rootassemble));
|
||
let dispatchassemble: str = strings.concat("BEGIN<-o><", works[si],
|
||
"/", rootaction, ".init.o.new><", works[si], "/", rootaction,
|
||
".init.s.new>");
|
||
assert(same(linecontaining(atrace,
|
||
strings.concat("/", rootaction, ".init.o.new")), dispatchassemble));
|
||
|
||
// Repeating the same semantic request in the persistent work root may
|
||
// relink the requested binary, but must not compile or assemble any action.
|
||
let cbefore: i32 = ctrace.len;
|
||
let abefore: i32 = atrace.len;
|
||
runcommandenv(root, strings.concat("boundary-warm-", stages[si]), av,
|
||
env, (600i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
assert(readfile(compilertraces[si]).len == cbefore);
|
||
assert(readfile(assemblertraces[si]).len == abefore);
|
||
|
||
// A second root importing only p000 proves that link closure expansion,
|
||
// not the wide root's direct import list, reaches every archive beyond 256.
|
||
let chaincbefore: i32 = readfile(compilertraces[si]).len;
|
||
let chainabefore: i32 = readfile(assemblertraces[si]).len;
|
||
let chainlbefore: i32 = readfile(linkertraces[si]).len;
|
||
let chainav: []str = [driver(stages[si]), "build", "-w", works[si],
|
||
"-I", d3, "-I", root, "-o", chainbins[si], chainroot];
|
||
runcommandenv(root, strings.concat("boundary-chain-", stages[si]), chainav,
|
||
env, (600i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
let chainctrace: str = readfile(compilertraces[si]);
|
||
let chainatrace: str = readfile(assemblertraces[si]);
|
||
let chainltrace: str = readfile(linkertraces[si]);
|
||
let cdelta: str = strings.sub(chainctrace, chaincbefore, chainctrace.len);
|
||
let adelta: str = strings.sub(chainatrace, chainabefore, chainatrace.len);
|
||
let ldelta: str = strings.sub(chainltrace, chainlbefore, chainltrace.len);
|
||
assert(occurrences(cdelta, "\n") == 1);
|
||
assert(occurrences(adelta, "\n") == 2);
|
||
assert(occurrences(ldelta, "\n") == 1);
|
||
assert(same(cdelta, strings.concat(
|
||
"BEGIN<--entry><--package-init-symbol><__ww..pkg.p.",
|
||
chainaction, ".v0.r0.init><--init-dispatch-symbol>",
|
||
"<__ww..dispatch><-c><--import><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",
|
||
"BEGIN<-o><", works[si], "/", chainaction, ".init.o.new><",
|
||
works[si], "/", chainaction, ".init.s.new>\n")));
|
||
let expectedchainlink: str = strings.concat("BEGIN<-o><", chainbins[si],
|
||
".new><", works[si], "/", chainaction, ".a.new>");
|
||
i = 0;
|
||
for (i < packagecount) {
|
||
expectedchainlink = strings.concat(expectedchainlink, "<", works[si],
|
||
"/", boundarypkgname(i), ".a>");
|
||
i += 1;
|
||
};
|
||
expectedchainlink = strings.concat(expectedchainlink, "<", repo(),
|
||
"/out/bin/../lib/libwwrt.a>\n");
|
||
assert(same(ldelta, expectedchainlink));
|
||
assert(!has(ldelta, ".wwi>"));
|
||
let chainunit: str = readfile(strings.concat(works[si], "/", chainaction,
|
||
".unit.ww"));
|
||
assert(strings.hasprefix(chainunit, strings.concat(
|
||
"//ww:module-reset chain\n", chainsource, "\n")));
|
||
assert(has(chainunit, "//ww:direct-export p000 "));
|
||
let chainrun: []str = [chainbins[si]];
|
||
runcommand(root, strings.concat("boundary-chain-run-", stages[si]),
|
||
chainrun, (60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
si += 1;
|
||
};
|
||
|
||
let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"];
|
||
i = 0;
|
||
for (i < packagecount) {
|
||
let name: str = boundarypkgname(i);
|
||
let xi: i32 = 0;
|
||
for (xi < suffixes.len) {
|
||
assert(same(readfile(strings.concat(works[0], "/", name,
|
||
suffixes[xi])), readfile(strings.concat(works[1], "/", name,
|
||
suffixes[xi]))));
|
||
xi += 1;
|
||
};
|
||
i += 1;
|
||
};
|
||
let xi: i32 = 0;
|
||
for (xi < suffixes.len) {
|
||
assert(same(readfile(strings.concat(works[0], "/", rootaction,
|
||
suffixes[xi])), readfile(strings.concat(works[1], "/", rootaction,
|
||
suffixes[xi]))));
|
||
xi += 1;
|
||
};
|
||
assert(same(readfile(bins[0]), readfile(bins[1])));
|
||
xi = 0;
|
||
for (xi < suffixes.len) {
|
||
assert(same(readfile(strings.concat(works[0], "/", chainaction,
|
||
suffixes[xi])), readfile(strings.concat(works[1], "/", chainaction,
|
||
suffixes[xi]))));
|
||
xi += 1;
|
||
};
|
||
assert(same(readfile(chainbins[0]), readfile(chainbins[1])));
|
||
|
||
// p257 is inserted beyond the former 256-action boundary. A changed
|
||
// export recompiles that package, its direct importer p256, and the root
|
||
// (which directly imports every package), then stops at p255 because the
|
||
// regenerated p256 export is byte-identical.
|
||
let changed: str = strings.concat(root, "/p257/a.ww");
|
||
rewritefile(changed, strings.concat(
|
||
"package p257;\nimport p258;\n// OWNER_A_p257_CHANGED\n",
|
||
"export fn value() i32 = { return p258.value(); };\n",
|
||
"export fn changed() i32 = { return 257; };\n"));
|
||
si = 0;
|
||
for (si < stages.len) {
|
||
let before: str = readfile(compilertraces[si]);
|
||
let av: []str = [driver(stages[si]), "build", "-w", works[si],
|
||
"-I", d3, "-I", root, "-o", bins[si], target];
|
||
runcommandenv(root, strings.concat("boundary-change-", stages[si]), av,
|
||
environments[si],
|
||
(600i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
let after: str = readfile(compilertraces[si]);
|
||
let delta: str = strings.sub(after, before.len, after.len);
|
||
assert(occurrences(delta, "\n") == 3);
|
||
assert(occurrences(delta, "/p257.unit.new") == 1);
|
||
assert(occurrences(delta, "/p256.unit.new") == 1);
|
||
assert(occurrences(delta, strings.concat("/", rootaction,
|
||
".unit.new")) == 1);
|
||
assert(!has(delta, "/p255.unit.new"));
|
||
assert(!has(delta, "/p258.unit.new"));
|
||
let runav: []str = [bins[si]];
|
||
runcommand(root, strings.concat("boundary-change-run-", stages[si]),
|
||
runav, (60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
si += 1;
|
||
};
|
||
assert(same(readfile(strings.concat(works[0], "/p257.wwi")),
|
||
readfile(strings.concat(works[1], "/p257.wwi"))));
|
||
assert(same(readfile(bins[0]), readfile(bins[1])));
|
||
|
||
// Close the 300-node chain into a cycle. Both graph implementations must
|
||
// emit the complete deterministic path before compiler, assembler, or linker.
|
||
rewritefile(strings.concat(root, "/p299/a.ww"), strings.concat(
|
||
"package p299;\nimport p000;\n// OWNER_A_p299_CYCLE\n",
|
||
"export fn value() i32 = { return p000.value(); };\n"));
|
||
let wantcycle: str = "ww: dependency cycle: ";
|
||
i = 0;
|
||
for (i < packagecount) {
|
||
if (i != 0) { wantcycle = strings.concat(wantcycle, " -> "); };
|
||
wantcycle = strings.concat(wantcycle, boundarypkgname(i));
|
||
i += 1;
|
||
};
|
||
wantcycle = strings.concat(wantcycle, " -> p000\n");
|
||
let referencecycle: str = "";
|
||
si = 0;
|
||
for (si < stages.len) {
|
||
rewritefile(compilertraces[si], "");
|
||
rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
let cyclework: str = strings.concat(root, "/cycle-work-", stages[si]);
|
||
assert(os.mkdir(cyclework, 448i32) == 0);
|
||
let cyclebin: str = strings.concat(root, "/cycle-bin-", stages[si]);
|
||
let av: []str = [driver(stages[si]), "build", "-w", cyclework,
|
||
"-I", d3, "-I", root, "-o", cyclebin, target];
|
||
runcommandenv(root, strings.concat("boundary-cycle-", stages[si]), av,
|
||
environments[si],
|
||
(600i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(out.stdout.len == 0);
|
||
assert(same(out.stderr, wantcycle));
|
||
assert(readfile(compilertraces[si]).len == 0);
|
||
assert(readfile(assemblertraces[si]).len == 0);
|
||
assert(readfile(linkertraces[si]).len == 0);
|
||
if (si == 0) { referencecycle = strings.dup(out.stderr); }
|
||
else { assert(same(referencecycle, out.stderr)); };
|
||
si += 1;
|
||
};
|
||
clean(root);
|
||
};
|
||
|
||
@test fn dynamic_package_universe_crosses_former_boundary() void = {
|
||
let root: str = fresh();
|
||
let suite: str = strings.concat(root, "/suite");
|
||
assert(os.mkdir(suite, 448i32) == 0);
|
||
let testcount: i32 = 52;
|
||
let notestcount: i32 = 205;
|
||
let productcount: i32 = testcount + notestcount;
|
||
let actioncount: i32 = testcount * 3 + notestcount + 9;
|
||
let assemblycount: i32 = actioncount + testcount;
|
||
let i: i32 = 0;
|
||
for (i < productcount) {
|
||
let name: str = boundarypkgname(i);
|
||
let dir: str = strings.concat(suite, "/", name);
|
||
assert(os.mkdir(dir, 448i32) == 0);
|
||
writefile(strings.concat(dir, "/prod.ww"), strings.concat(
|
||
"package ", name, ";\n// DYNAMIC_PRODUCTION_", name,
|
||
"\nexport fn value() i32 = { return 42; };\n"));
|
||
if (i < testcount) {
|
||
writefile(strings.concat(dir, "/same_test.ww"), strings.concat(
|
||
"package ", name, ";\n// DYNAMIC_INTERNAL_", name,
|
||
"\n@test fn same_value() void = { assert(value() == 42); };\n"));
|
||
writefile(strings.concat(dir, "/external_test.ww"), strings.concat(
|
||
"package ", name, "_test;\nimport ", name,
|
||
";\n// DYNAMIC_EXTERNAL_", name,
|
||
"\n@test fn external_value() void = { assert(", name,
|
||
".value() == 42); };\n"));
|
||
};
|
||
i += 1;
|
||
};
|
||
|
||
let compilerwrapper: str = strings.concat(root, "/universe-w6c.sh");
|
||
let assemblerwrapper: str = strings.concat(root, "/universe-w6a.sh");
|
||
let linkerwrapper: str = strings.concat(root, "/universe-w6l.sh");
|
||
writeexecutable(compilerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf '%s\\n' \"$*\" >> ",
|
||
"\"$WW_UNIVERSE_COMPILER_TRACE\"\n",
|
||
"exec \"$WW_UNIVERSE_W6C\" \"$@\"\n"));
|
||
writeexecutable(assemblerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf '%s\\n' \"$*\" >> ",
|
||
"\"$WW_UNIVERSE_ASSEMBLER_TRACE\"\n",
|
||
"exec \"$WW_UNIVERSE_W6A\" \"$@\"\n"));
|
||
writeexecutable(linkerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf '%s\\n' \"$*\" >> ",
|
||
"\"$WW_UNIVERSE_LINKER_TRACE\"\n",
|
||
"exec \"$WW_UNIVERSE_W6L\" \"$@\"\n"));
|
||
let coordinatorproxy: str = strings.concat(root,
|
||
"/universe-coordinator.sh");
|
||
let driverwrapper: str = strings.concat(root, "/universe-driver.sh");
|
||
writeexecutable(coordinatorproxy, strings.concat(
|
||
"#!/bin/sh\n",
|
||
"if [ \"$1\" != package ] || [ \"$2\" != --ww-driver ] || ",
|
||
"[ \"$#\" -lt 3 ]; then exit 97; fi\n",
|
||
"shift 3\nexec \"$WW_UNIVERSE_WWTEST\" package --ww-driver ",
|
||
"\"$WW_UNIVERSE_DRIVER_WRAPPER\" \"$@\"\n"));
|
||
writeexecutable(driverwrapper, strings.concat(
|
||
"#!/bin/sh\ncount=0\n",
|
||
"for arg in \"$@\"; do\n",
|
||
" if [ \"$arg\" = --ww-package-test ]; then count=$((count + 1)); fi\n",
|
||
"done\nprintf '%s\\n' \"$count\" >> ",
|
||
"\"$WW_UNIVERSE_BUILDER_TRACE\"\n",
|
||
"exec \"$WW_UNIVERSE_DRIVER\" \"$@\"\n"));
|
||
|
||
let stages: []str = ["ww", "ww_ww"];
|
||
let compilers: []str = ["w6c", "w6c_ww"];
|
||
let assemblers: []str = ["w6a", "w6a_ww"];
|
||
let linkers: []str = ["w6l", "w6l_ww"];
|
||
let works: []str = [strings.concat(root, "/universe-c-work"),
|
||
strings.concat(root, "/universe-ww-work")];
|
||
let compilertraces: []str = [strings.concat(root, "/universe-c-w6c"),
|
||
strings.concat(root, "/universe-ww-w6c")];
|
||
let assemblertraces: []str = [strings.concat(root, "/universe-c-w6a"),
|
||
strings.concat(root, "/universe-ww-w6a")];
|
||
let linkertraces: []str = [strings.concat(root, "/universe-c-w6l"),
|
||
strings.concat(root, "/universe-ww-w6l")];
|
||
let buildertraces: []str = [strings.concat(root, "/universe-c-builder"),
|
||
strings.concat(root, "/universe-ww-builder")];
|
||
let referencecompiler: str = "";
|
||
let referenceassembler: str = "";
|
||
let referencelinker: str = "";
|
||
let referencebinary: str = "";
|
||
let referenceartifacts: []str = ["", "", "", "", "", "", "", "", "",
|
||
"", "", "", "", "", "", "", "", "", "", ""];
|
||
let coordinatoroutputs: []str = ["", ""];
|
||
let baseenv: []str = os.getenvs();
|
||
let out: commandout;
|
||
let si: i32 = 0;
|
||
for (si < stages.len) {
|
||
assert(os.mkdir(works[si], 448i32) == 0);
|
||
writefile(compilertraces[si], "");
|
||
writefile(assemblertraces[si], "");
|
||
writefile(linkertraces[si], "");
|
||
writefile(buildertraces[si], "");
|
||
let env: []str = alloc([], (baseenv.len + 17): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < baseenv.len) {
|
||
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6A=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6L=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_UNIVERSE_COMPILER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_UNIVERSE_ASSEMBLER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_UNIVERSE_LINKER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_UNIVERSE_W6C=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_UNIVERSE_W6A=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_UNIVERSE_W6L=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_UNIVERSE_WWTEST=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_UNIVERSE_DRIVER_WRAPPER=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_UNIVERSE_DRIVER=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_UNIVERSE_BUILDER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_WWTEST=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_LIB=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_SRCLIB=")) {
|
||
append(env, baseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(env, strings.concat("WW_W6C=", compilerwrapper));
|
||
append(env, strings.concat("WW_W6A=", assemblerwrapper));
|
||
append(env, strings.concat("WW_W6L=", linkerwrapper));
|
||
append(env, strings.concat("WW_UNIVERSE_COMPILER_TRACE=",
|
||
compilertraces[si]));
|
||
append(env, strings.concat("WW_UNIVERSE_ASSEMBLER_TRACE=",
|
||
assemblertraces[si]));
|
||
append(env, strings.concat("WW_UNIVERSE_LINKER_TRACE=",
|
||
linkertraces[si]));
|
||
append(env, strings.concat("WW_UNIVERSE_W6C=",
|
||
driver(compilers[si])));
|
||
append(env, strings.concat("WW_UNIVERSE_W6A=",
|
||
driver(assemblers[si])));
|
||
append(env, strings.concat("WW_UNIVERSE_W6L=",
|
||
driver(linkers[si])));
|
||
append(env, strings.concat("WW_LIB=", repo(), "/out/bin/../lib"));
|
||
append(env, strings.concat("WW_SRCLIB=", repo(), "/lib"));
|
||
append(env, strings.concat("WW_WWTEST=", coordinatorproxy));
|
||
append(env, strings.concat("WW_UNIVERSE_WWTEST=", repo(),
|
||
"/out/bin/wwtest"));
|
||
append(env, strings.concat("WW_UNIVERSE_DRIVER_WRAPPER=",
|
||
driverwrapper));
|
||
append(env, strings.concat("WW_UNIVERSE_DRIVER=", driver(stages[si])));
|
||
append(env, strings.concat("WW_UNIVERSE_BUILDER_TRACE=",
|
||
buildertraces[si]));
|
||
|
||
let av: []str = alloc([], (8 + productcount * 10): u64)!;
|
||
append(av, driver(stages[si]));
|
||
append(av, "test"); append(av, "-c");
|
||
append(av, "-w"); append(av, works[si]);
|
||
append(av, "-I"); append(av, suite);
|
||
if (si == 0) {
|
||
i = 0;
|
||
for (i < productcount) {
|
||
let name: str = boundarypkgname(i);
|
||
let dir: str = strings.concat(suite, "/", name);
|
||
append(av, "--ww-package-test"); append(av, "test");
|
||
append(av, name); append(av, name);
|
||
if (i < testcount) {
|
||
append(av, name);
|
||
append(av, strings.concat(name, "_test"));
|
||
} else {
|
||
append(av, "-"); append(av, "-");
|
||
};
|
||
append(av, dir);
|
||
append(av, strings.concat(root, "/product-", name, ".test"));
|
||
append(av, "-");
|
||
append(av, strings.concat(root, "/product-", name, ".status"));
|
||
i += 1;
|
||
};
|
||
} else {
|
||
i = productcount - 1;
|
||
for (i >= 0) {
|
||
let name: str = boundarypkgname(i);
|
||
let dir: str = strings.concat(suite, "/", name);
|
||
append(av, "--ww-package-test"); append(av, "test");
|
||
append(av, name); append(av, name);
|
||
if (i < testcount) {
|
||
append(av, name);
|
||
append(av, strings.concat(name, "_test"));
|
||
} else {
|
||
append(av, "-"); append(av, "-");
|
||
};
|
||
append(av, dir);
|
||
append(av, strings.concat(root, "/product-", name, ".test"));
|
||
append(av, "-");
|
||
append(av, strings.concat(root, "/product-", name, ".status"));
|
||
i -= 1;
|
||
};
|
||
};
|
||
append(av, strings.concat(suite, "/p000"));
|
||
assert(av.len == 8 + productcount * 10);
|
||
runcommandenv(root, strings.concat("dynamic-universe-", stages[si]), av,
|
||
env, (1200i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
let ctrace: str = readfile(compilertraces[si]);
|
||
let atrace: str = readfile(assemblertraces[si]);
|
||
let ltrace: str = readfile(linkertraces[si]);
|
||
assert(occurrences(ctrace, "\n") == actioncount);
|
||
assert(occurrences(atrace, "\n") == assemblycount);
|
||
assert(occurrences(ltrace, "\n") == testcount);
|
||
assert(!has(ltrace, ".wwi"));
|
||
assert(occurrences(ctrace, "/test.unit.new") == 1);
|
||
i = 0;
|
||
for (i < productcount) {
|
||
let name: str = boundarypkgname(i);
|
||
if (i < testcount) {
|
||
assert(occurrences(ctrace, strings.concat("/", name,
|
||
"-internal-test.unit.new")) == 1);
|
||
assert(occurrences(ctrace, strings.concat("/", name,
|
||
"_test-external-test.unit.new")) == 1);
|
||
assert(occurrences(ctrace, strings.concat("/", name,
|
||
"-test-main.unit.new")) == 1);
|
||
assert(occurrences(ctrace, strings.concat("/", name,
|
||
".unit.new")) == 0);
|
||
assert(os.exists(strings.concat(root, "/product-", name,
|
||
".test")));
|
||
} else {
|
||
assert(occurrences(ctrace, strings.concat("/", name,
|
||
".unit.new")) == 1);
|
||
assert(occurrences(ctrace, strings.concat("/", name,
|
||
"-test-main.unit.new")) == 0);
|
||
assert(!os.exists(strings.concat(root, "/product-", name,
|
||
".test")));
|
||
};
|
||
assert(same(readfile(strings.concat(root, "/product-", name,
|
||
".status")), "ok\n"));
|
||
i += 1;
|
||
};
|
||
let samplemain: str = linecontaining(ctrace,
|
||
"/p051-test-main.unit.new");
|
||
assert(has(samplemain,
|
||
"--test-target-package p051 --test-target-package p051_test"));
|
||
let sampleexternal: str = linecontaining(ctrace,
|
||
"/p051_test-external-test.unit.new");
|
||
assert(has(sampleexternal,
|
||
strings.concat("--import p051 ", works[si],
|
||
"/p051-internal-test.wwi.new")));
|
||
let samplelink: str = linecontaining(ltrace,
|
||
strings.concat("-o ", root, "/product-p051.test.new "));
|
||
assert(has(samplelink, "/p051-test-main.a.new"));
|
||
assert(has(samplelink, "/p051-internal-test.a.new"));
|
||
assert(has(samplelink, "/p051_test-external-test.a.new"));
|
||
assert(!has(samplelink, strings.concat(works[si], "/p051.a")));
|
||
assert(!has(samplelink, ".wwi"));
|
||
let runav: []str = [strings.concat(root, "/product-p051.test")];
|
||
runcommand(root, strings.concat("dynamic-run-", stages[si]), runav,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout, "same_value ... ok\n"));
|
||
assert(has(out.stdout, "external_value ... ok\n"));
|
||
|
||
let samples: []str = ["p051-internal-test",
|
||
"p051_test-external-test", "p051-test-main", "p256"];
|
||
let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"];
|
||
let sri: i32 = 0;
|
||
for (sri < samples.len) {
|
||
let xi: i32 = 0;
|
||
for (xi < suffixes.len) {
|
||
let bytes: str = readfile(strings.concat(works[si], "/",
|
||
samples[sri], suffixes[xi]));
|
||
let index: i32 = sri * suffixes.len + xi;
|
||
if (si == 0) {
|
||
referenceartifacts[index] = strings.dup(bytes);
|
||
} else {
|
||
assert(same(referenceartifacts[index], bytes));
|
||
};
|
||
xi += 1;
|
||
};
|
||
sri += 1;
|
||
};
|
||
let impossible: str = strings.concat(root, "/not-an-output");
|
||
let nc: str = normalizedtrace(ctrace,
|
||
strings.concat(works[si], "/"), impossible);
|
||
let na: str = normalizedtrace(atrace,
|
||
strings.concat(works[si], "/"), impossible);
|
||
let nl: str = normalizedtrace(ltrace,
|
||
strings.concat(works[si], "/"), impossible);
|
||
if (si == 0) {
|
||
referencecompiler = nc;
|
||
referenceassembler = na;
|
||
referencelinker = nl;
|
||
referencebinary = readfile(strings.concat(root,
|
||
"/product-p051.test"));
|
||
} else {
|
||
assert(same(referencecompiler, nc));
|
||
assert(same(referenceassembler, na));
|
||
assert(same(referencelinker, nl));
|
||
assert(same(referencebinary, readfile(strings.concat(root,
|
||
"/product-p051.test"))));
|
||
};
|
||
|
||
// The complete >256 action universe is reused on an unchanged request.
|
||
rewritefile(compilertraces[si], "");
|
||
rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
runcommandenv(root, strings.concat("dynamic-warm-", stages[si]), av,
|
||
env, (1200i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(readfile(compilertraces[si]).len == 0);
|
||
assert(readfile(assemblertraces[si]).len == 0);
|
||
assert(occurrences(readfile(linkertraces[si]), "\n") == testcount);
|
||
|
||
// The public recursive coordinator emits the same 257 canonical
|
||
// directory products in one delegated request, runs 52 binaries, and
|
||
// takes the no-real-run path for all remaining directories.
|
||
rewritefile(compilertraces[si], "");
|
||
rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
let coordinatorav: []str = [driver(stages[si]), "test", "-j", "1",
|
||
"-w", works[si], "-I", suite, strings.concat(suite, "/...")];
|
||
runcommandenv(root, strings.concat("dynamic-coordinator-", stages[si]),
|
||
coordinatorav, env,
|
||
(1200i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stderr.len == 0);
|
||
coordinatoroutputs[si] = strings.dup(out.stdout);
|
||
assert(readfile(compilertraces[si]).len == 0);
|
||
assert(readfile(assemblertraces[si]).len == 0);
|
||
assert(occurrences(readfile(linkertraces[si]), "\n") == testcount);
|
||
assert(same(readfile(buildertraces[si]), "257\n"));
|
||
assert(occurrences(out.stdout, "ok ") == testcount);
|
||
assert(occurrences(out.stdout, "? ") == notestcount);
|
||
assert(has(out.stdout, strings.concat("ok ", suite,
|
||
"/p051 [p051]\n")));
|
||
assert(has(out.stdout, strings.concat("? ", suite,
|
||
"/p256 [no tests]\n")));
|
||
|
||
let beforecompiler: i32 = readfile(compilertraces[si]).len;
|
||
runcommandenv(root, strings.concat("dynamic-coordinator-warm-",
|
||
stages[si]), coordinatorav, env,
|
||
(1200i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(same(coordinatoroutputs[si], out.stdout));
|
||
assert(readfile(compilertraces[si]).len == beforecompiler);
|
||
assert(same(readfile(buildertraces[si]), "257\n257\n"));
|
||
si += 1;
|
||
};
|
||
assert(same(coordinatoroutputs[0], coordinatoroutputs[1]));
|
||
clean(root);
|
||
};
|
||
|
||
@test fn multi_directory_test_failures_are_isolated() void = {
|
||
let root: str = fresh();
|
||
let suite: str = strings.concat(root, "/suite");
|
||
let alpha: str = strings.concat(suite, "/alpha");
|
||
let beta: str = strings.concat(suite, "/beta");
|
||
assert(os.mkdir(suite, 448i32) == 0);
|
||
assert(os.mkdir(alpha, 448i32) == 0);
|
||
assert(os.mkdir(beta, 448i32) == 0);
|
||
writefile(strings.concat(alpha, "/alpha.ww"), strings.concat(
|
||
"package alpha;\n",
|
||
"fn hidden() i32 = { return 41; };\n",
|
||
"export fn shown() i32 = { return 41; };\n"));
|
||
writefile(strings.concat(alpha, "/same_test.ww"), strings.concat(
|
||
"package alpha;\n",
|
||
"@test fn alpha_same_survives() void = {",
|
||
" assert(hidden() == 41); };\n"));
|
||
writefile(strings.concat(alpha, "/external_test.ww"), strings.concat(
|
||
"package alpha_test;\nimport alpha;\n",
|
||
"@test fn alpha_external_fails() void = {",
|
||
" assert(alpha.hidden() == 41); };\n"));
|
||
writefile(strings.concat(beta, "/beta.ww"), strings.concat(
|
||
"package beta;\n",
|
||
"fn hidden() i32 = { return 42; };\n",
|
||
"export fn shown() i32 = { return 42; };\n"));
|
||
writefile(strings.concat(beta, "/same_test.ww"), strings.concat(
|
||
"package beta;\n",
|
||
"@test fn beta_same_survives() void = {",
|
||
" assert(hidden() == 42); };\n"));
|
||
writefile(strings.concat(beta, "/external_test.ww"), strings.concat(
|
||
"package beta_test;\nimport beta;\n",
|
||
"@test fn beta_external_survives() void = {",
|
||
" assert(beta.shown() == 42); };\n"));
|
||
let stages: []str = ["ww", "ww_ww"];
|
||
let jobs: []str = ["1", "4"];
|
||
let diagnostics: []str = ["", ""];
|
||
let referenceout: str = "";
|
||
let out: commandout;
|
||
let i: i32 = 0;
|
||
for (i < stages.len) {
|
||
let av: []str = [driver(stages[i]), "test", "-j", jobs[i],
|
||
"-I", root, strings.concat(suite, "/...")];
|
||
runcommand(root, strings.concat("multidir-failure-", stages[i]), av,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(out.stdout.len == 0);
|
||
assert(has(out.stderr, "has no exported declaration 'hidden'"));
|
||
assert(has(out.stderr, strings.concat("FAIL ", alpha,
|
||
" [alpha] (build exit 1)\n")));
|
||
assert(has(out.stderr, strings.concat("FAIL ", beta,
|
||
" [beta] (build exit 1)\n")));
|
||
assert(occurrences(out.stderr, strings.concat("FAIL ", alpha)) == 1);
|
||
assert(occurrences(out.stderr, strings.concat("FAIL ", beta)) == 1);
|
||
assert(!has(out.stderr, "[alpha_test]"));
|
||
assert(!has(out.stderr, "[beta_test]"));
|
||
let alphapos: i32 = pos(out.stderr, strings.concat("FAIL ", alpha,
|
||
" [alpha] (build exit 1)\n"));
|
||
let betapos: i32 = pos(out.stderr, strings.concat("FAIL ", beta,
|
||
" [beta] (build exit 1)\n"));
|
||
assert(alphapos >= 0 && alphapos < betapos);
|
||
diagnostics[i] = strings.dup(primarydiagnostic(out.stderr));
|
||
if (i == 0) { referenceout = strings.dup(out.stdout); }
|
||
else { assert(same(referenceout, out.stdout)); };
|
||
i += 1;
|
||
};
|
||
assert(same(diagnostics[0], diagnostics[1]));
|
||
clean(root);
|
||
};
|
||
|
||
// Roots remain independently diagnosed and may be staged in parallel, but one
|
||
// failing product rejects publication and execution of the entire request.
|
||
@test fn sibling_test_variant_failures_are_isolated() void = {
|
||
let root: str = fresh();
|
||
let badexternal: str = strings.concat(root, "/badexternal");
|
||
let badsame: str = strings.concat(root, "/badsame");
|
||
assert(os.mkdir(badexternal, 448i32) == 0);
|
||
assert(os.mkdir(badsame, 448i32) == 0);
|
||
writefile(strings.concat(badexternal, "/badexternal.ww"), strings.concat(
|
||
"package badexternal;\n",
|
||
"fn hidden() i32 = { return 41; };\n",
|
||
"export fn shown() i32 = { return 41; };\n"));
|
||
writefile(strings.concat(badexternal, "/same_test.ww"), strings.concat(
|
||
"package badexternal;\n",
|
||
"@test fn valid_same_runs() void = { assert(hidden() == 41); };\n"));
|
||
writefile(strings.concat(badexternal, "/external_test.ww"), strings.concat(
|
||
"package badexternal_test;\nimport badexternal;\n",
|
||
"@test fn invalid_external() void = {\n",
|
||
" assert(badexternal.hidden() == 41);\n};\n"));
|
||
writefile(strings.concat(badsame, "/badsame.ww"), strings.concat(
|
||
"package badsame;\n",
|
||
"export fn shown() i32 = { return 42; };\n"));
|
||
writefile(strings.concat(badsame, "/same_test.ww"), strings.concat(
|
||
"package badsame;\n",
|
||
"@test fn invalid_same() void = { assert(missing() == 0); };\n"));
|
||
writefile(strings.concat(badsame, "/external_test.ww"), strings.concat(
|
||
"package badsame_test;\nimport badsame;\n",
|
||
"@test fn valid_external_runs() void = {\n",
|
||
" assert(badsame.shown() == 42);\n};\n"));
|
||
|
||
let stages: []str = ["ww", "ww_ww"];
|
||
let tags: []str = ["c", "ww"];
|
||
let externaldiagnostics: []str = ["", ""];
|
||
let samediagnostics: []str = ["", ""];
|
||
let out: commandout;
|
||
let i: i32 = 0;
|
||
for (i < stages.len) {
|
||
let externalav: []str = [driver(stages[i]), "test", "-I", root,
|
||
badexternal];
|
||
runcommand(root, strings.concat("isolated-external-", tags[i]),
|
||
externalav, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(out.stdout.len == 0);
|
||
assert(has(out.stderr,
|
||
"has no exported declaration 'hidden'"));
|
||
assert(has(out.stderr,
|
||
strings.concat("FAIL ", badexternal,
|
||
" [badexternal] (build exit 1)\n")));
|
||
assert(occurrences(out.stderr,
|
||
strings.concat("FAIL ", badexternal)) == 1);
|
||
assert(!has(out.stderr, "[badexternal_test]"));
|
||
externaldiagnostics[i] = strings.dup(primarydiagnostic(out.stderr));
|
||
|
||
let sameav: []str = [driver(stages[i]), "test", "-I", root,
|
||
badsame];
|
||
runcommand(root, strings.concat("isolated-same-", tags[i]), sameav,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(out.stdout.len == 0);
|
||
assert(has(out.stderr, "undefined: missing"));
|
||
assert(has(out.stderr, strings.concat("FAIL ", badsame,
|
||
" [badsame] (build exit 1)\n")));
|
||
assert(occurrences(out.stderr, strings.concat("FAIL ", badsame)) == 1);
|
||
assert(!has(out.stderr, "[badsame_test]"));
|
||
samediagnostics[i] = strings.dup(primarydiagnostic(out.stderr));
|
||
i += 1;
|
||
};
|
||
assert(same(externaldiagnostics[0], externaldiagnostics[1]));
|
||
assert(same(samediagnostics[0], samediagnostics[1]));
|
||
|
||
// A persistent request is one publication transaction. A rejection leaves
|
||
// no reusable generation, so every staged action is reconsidered next time.
|
||
let warmroot: str = strings.concat(root, "/warm-partial");
|
||
assert(os.mkdir(warmroot, 448i32) == 0);
|
||
let trace: str = strings.concat(root, "/warm-partial.trace");
|
||
let wrapper: str = strings.concat(root, "/warm-partial-w6c.sh");
|
||
writefile(trace, "");
|
||
writeexecutable(wrapper, strings.concat(
|
||
"#!/bin/sh\n",
|
||
"printf '%s\\n' \"$*\" >> \"$WW_PACKAGE_COMPILER_TRACE\"\n",
|
||
"exec \"$WW_PACKAGE_W6C\" \"$@\"\n"));
|
||
let baseenv: []str = os.getenvs();
|
||
let traceenv: []str = alloc([], (baseenv.len + 3): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < baseenv.len) {
|
||
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_PACKAGE_COMPILER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6C=")) {
|
||
append(traceenv, baseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(traceenv, strings.concat("WW_W6C=", wrapper));
|
||
append(traceenv, strings.concat("WW_PACKAGE_COMPILER_TRACE=", trace));
|
||
append(traceenv, strings.concat("WW_PACKAGE_W6C=", driver("w6c")));
|
||
let warmav: []str = [driver("ww"), "test", "-j", "4", "-w",
|
||
warmroot, "-I", root, badexternal];
|
||
runcommandenv(root, "isolated-warm-first", warmav, traceenv,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
let firsttrace: str = readfile(trace);
|
||
assert(occurrences(firsttrace,
|
||
"/badexternal-internal-test.unit.new") == 1);
|
||
assert(occurrences(firsttrace,
|
||
"/badexternal_test-external-test.unit.new") == 1);
|
||
runcommandenv(root, "isolated-warm-reuse", warmav, traceenv,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
let secondtrace: str = readfile(trace);
|
||
assert(occurrences(secondtrace,
|
||
"/badexternal-internal-test.unit.new") == 2);
|
||
assert(occurrences(secondtrace,
|
||
"/badexternal_test-external-test.unit.new") == 2);
|
||
assert(!os.exists(strings.concat(warmroot,
|
||
"/badexternal-internal-test.unit.ww")));
|
||
assert(!os.exists(strings.concat(warmroot,
|
||
"/badexternal_test-external-test.unit.ww")));
|
||
assert(!os.exists(strings.concat(warmroot, "/.wwtool.stamp")));
|
||
|
||
let wwtracebin: str = strings.concat(repo(), "/out/partial-trace-",
|
||
workescape(root));
|
||
assert(os.mkdir(wwtracebin, 448i32) == 0);
|
||
let wwdriver: str = strings.concat(wwtracebin, "/ww_ww");
|
||
let wwtrace: str = strings.concat(root, "/warm-partial-ww.trace");
|
||
let wwwarmroot: str = strings.concat(root, "/warm-partial-ww");
|
||
assert(os.mkdir(wwwarmroot, 448i32) == 0);
|
||
writefile(wwtrace, "");
|
||
writeexecutable(wwdriver, readfile(driver("ww_ww")));
|
||
writeexecutable(strings.concat(wwtracebin, "/wwtest"),
|
||
readfile(driver("wwtest")));
|
||
writeexecutable(strings.concat(wwtracebin, "/w6c_ww"), strings.concat(
|
||
"#!/bin/sh\n",
|
||
"printf '%s\\n' \"$*\" >> \"$WW_PACKAGE_WW_COMPILER_TRACE\"\n",
|
||
"exec \"$WW_PACKAGE_W6C_WW\" \"$@\"\n"));
|
||
writeexecutable(strings.concat(wwtracebin, "/w6a_ww"),
|
||
readfile(driver("w6a_ww")));
|
||
writeexecutable(strings.concat(wwtracebin, "/w6l_ww"),
|
||
readfile(driver("w6l_ww")));
|
||
let wwenv: []str = alloc([], (baseenv.len + 2): u64)!;
|
||
ei = 0;
|
||
for (ei < baseenv.len) {
|
||
if (!strings.hasprefix(baseenv[ei],
|
||
"WW_PACKAGE_WW_COMPILER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6C_WW=")) {
|
||
append(wwenv, baseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(wwenv, strings.concat("WW_PACKAGE_WW_COMPILER_TRACE=", wwtrace));
|
||
append(wwenv, strings.concat("WW_PACKAGE_W6C_WW=", driver("w6c_ww")));
|
||
let wwwarmav: []str = [wwdriver, "test", "-j", "4", "-w",
|
||
wwwarmroot, "-I", root, badexternal];
|
||
runcommandenv(root, "isolated-warm-ww-first", wwwarmav, wwenv,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
let wwfirst: str = readfile(wwtrace);
|
||
assert(occurrences(wwfirst,
|
||
"/badexternal-internal-test.unit.new") == 1);
|
||
assert(occurrences(wwfirst,
|
||
"/badexternal_test-external-test.unit.new") == 1);
|
||
runcommandenv(root, "isolated-warm-ww-reuse", wwwarmav, wwenv,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
let wwsecond: str = readfile(wwtrace);
|
||
assert(occurrences(wwsecond,
|
||
"/badexternal-internal-test.unit.new") == 2);
|
||
assert(occurrences(wwsecond,
|
||
"/badexternal_test-external-test.unit.new") == 2);
|
||
assert(!os.exists(strings.concat(wwwarmroot,
|
||
"/badexternal-internal-test.unit.ww")));
|
||
assert(!os.exists(strings.concat(wwwarmroot,
|
||
"/badexternal_test-external-test.unit.ww")));
|
||
assert(!os.exists(strings.concat(wwwarmroot, "/.wwtool.stamp")));
|
||
clean(wwtracebin);
|
||
|
||
// Late failures are request-wide transactions. Start from a committed
|
||
// combined product, stage a changed dependency first, and then fail each
|
||
// family action or the linker twice. Every retry must reconsider the same
|
||
// work and the committed generation must remain byte-identical.
|
||
let latedep: str = strings.concat(root, "/late_dep");
|
||
let latebridge: str = strings.concat(root, "/late_bridge");
|
||
let late: str = strings.concat(root, "/late");
|
||
assert(os.mkdir(latedep, 448i32) == 0);
|
||
assert(os.mkdir(latebridge, 448i32) == 0);
|
||
assert(os.mkdir(late, 448i32) == 0);
|
||
let depfile: str = strings.concat(latedep, "/dep.ww");
|
||
let latefile: str = strings.concat(late, "/late.ww");
|
||
let latebridgefile: str = strings.concat(latebridge, "/bridge.ww");
|
||
let latesamefile: str = strings.concat(late, "/same_test.ww");
|
||
let lateexternalfile: str = strings.concat(late, "/external_test.ww");
|
||
let depbase: str = strings.concat("package late_dep;\n",
|
||
"export fn value() i32 = { return 40; };\n");
|
||
let depchanged: str = strings.concat("package late_dep;\n",
|
||
"export fn value() i32 = { return 39 + 1; };\n");
|
||
let samebase: str = strings.concat("package late;\n",
|
||
"export fn internal_helper() i32 = { return 41; };\n",
|
||
"@test fn internal_base() void = { assert(value() == 41); };\n");
|
||
let samechanged: str = strings.concat("package late;\n",
|
||
"export fn internal_helper() i32 = { return 40 + 1; };\n",
|
||
"@test fn internal_base() void = { assert(value() == 41); };\n");
|
||
let externalbase: str = strings.concat(
|
||
"package late_test;\nimport late;\nimport late_bridge;\n",
|
||
"@test fn external_base() void = {\n",
|
||
" assert(late.internal_helper() == 41);\n",
|
||
" assert(late_bridge.helper() == 41);\n};\n");
|
||
let externalchanged: str = strings.concat(
|
||
"package late_test;\nimport late;\nimport late_bridge;\n",
|
||
"@test fn external_base() void = {\n",
|
||
" assert(late.internal_helper() >= 41);\n",
|
||
" assert(late_bridge.helper() >= 41);\n};\n");
|
||
let externalmainchanged: str = strings.concat(
|
||
"package late_test;\nimport late;\nimport late_bridge;\n",
|
||
"@test fn external_renamed() void = {\n",
|
||
" assert(late.internal_helper() == 41);\n",
|
||
" assert(late_bridge.helper() == 41);\n};\n");
|
||
writefile(depfile, depbase);
|
||
writefile(latefile, strings.concat("package late;\nimport late_dep;\n",
|
||
"export fn value() i32 = { return late_dep.value() + 1; };\n"));
|
||
writefile(latebridgefile, strings.concat(
|
||
"package late_bridge;\nimport late;\n",
|
||
"export fn helper() i32 = { return late.internal_helper(); };\n"));
|
||
writefile(latesamefile, samebase);
|
||
writefile(lateexternalfile, externalbase);
|
||
let latecompilerwrapper: str = strings.concat(root, "/late-w6c.sh");
|
||
let lateassemblerwrapper: str = strings.concat(root, "/late-w6a.sh");
|
||
let latelinkerwrapper: str = strings.concat(root, "/late-w6l.sh");
|
||
writeexecutable(latecompilerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_LATE_COMPILER_TRACE\"\n",
|
||
"if test -n \"$WW_LATE_FAIL_FRAGMENT\"; then\n",
|
||
" for arg do case \"$arg\" in *\"$WW_LATE_FAIL_FRAGMENT\"*)\n",
|
||
" printf 'injected w6c failure: %s\\n' ",
|
||
"\"$WW_LATE_FAIL_FRAGMENT\" >&2\n exit 97;; esac; done\nfi\n",
|
||
"exec \"$WW_LATE_REAL_COMPILER\" \"$@\"\n"));
|
||
writeexecutable(lateassemblerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_LATE_ASSEMBLER_TRACE\"\n",
|
||
"exec \"$WW_LATE_REAL_ASSEMBLER\" \"$@\"\n"));
|
||
writeexecutable(latelinkerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_LATE_LINKER_TRACE\"\n",
|
||
"if test \"$WW_LATE_FAIL_LINK\" = 1; then\n",
|
||
" printf 'injected w6l failure\\n' >&2\n exit 97\nfi\n",
|
||
"exec \"$WW_LATE_REAL_LINKER\" \"$@\"\n"));
|
||
let lateworks: []str = [strings.concat(root, "/late-c-work"),
|
||
strings.concat(root, "/late-ww-work")];
|
||
let latebins: []str = [strings.concat(root, "/late-c.test"),
|
||
strings.concat(root, "/late-ww.test")];
|
||
let latestatuses: []str = [strings.concat(root, "/late-c.status"),
|
||
strings.concat(root, "/late-ww.status")];
|
||
let latecompilers: []str = ["w6c", "w6c_ww"];
|
||
let lateassemblers: []str = ["w6a", "w6a_ww"];
|
||
let latelinkers: []str = ["w6l", "w6l_ww"];
|
||
let latebridgeowner: str = strings.concat("late#", late);
|
||
let latebridgeaction: str = packagestoragekeyfor(
|
||
"late_bridge", latebridge, 4, 0, latebridgeowner);
|
||
let laterelative: []str = [".wwtool.ww", ".wwtool.w6c", ".wwtool.w6a",
|
||
".wwtool.stamp", "late_dep.unit.ww", "late_dep.wwi", "late_dep.s",
|
||
"late_dep.o", "late_dep.a",
|
||
strings.concat(latebridgeaction, ".unit.ww"),
|
||
strings.concat(latebridgeaction, ".wwi"),
|
||
strings.concat(latebridgeaction, ".s"),
|
||
strings.concat(latebridgeaction, ".o"),
|
||
strings.concat(latebridgeaction, ".a"),
|
||
"late-internal-test.unit.ww", "late-internal-test.wwi",
|
||
"late-internal-test.s", "late-internal-test.o", "late-internal-test.a",
|
||
"late_test-external-test.unit.ww", "late_test-external-test.wwi",
|
||
"late_test-external-test.s", "late_test-external-test.o",
|
||
"late_test-external-test.a", "late-test-main.unit.ww",
|
||
"late-test-main.wwi", "late-test-main.s", "late-test-main.o",
|
||
"late-test-main.a", "late-test-main.init.unit.ww",
|
||
"late-test-main.init.s", "late-test-main.init.o", "test.unit.ww",
|
||
"test.wwi", "test.s", "test.o", "test.a"];
|
||
let latesnapshots: []str = alloc([],
|
||
(stages.len * laterelative.len): u64)!;
|
||
let snapshoti: i32 = 0;
|
||
for (snapshoti < stages.len * laterelative.len) {
|
||
append(latesnapshots, ""); snapshoti += 1;
|
||
};
|
||
let latebinrefs: []str = ["", ""];
|
||
let latestatusrefs: []str = ["", ""];
|
||
let lateseedbaseenv: []str = os.getenvs();
|
||
i = 0;
|
||
for (i < stages.len) {
|
||
assert(os.mkdir(lateworks[i], 448i32) == 0);
|
||
let seedctrace: str = strings.concat(root, "/late-seed-", tags[i],
|
||
"-w6c.trace");
|
||
let seedatrace: str = strings.concat(root, "/late-seed-", tags[i],
|
||
"-w6a.trace");
|
||
let seedltrace: str = strings.concat(root, "/late-seed-", tags[i],
|
||
"-w6l.trace");
|
||
writefile(seedctrace, ""); writefile(seedatrace, "");
|
||
writefile(seedltrace, "");
|
||
let seedenv: []str = alloc([], (lateseedbaseenv.len + 11): u64)!;
|
||
ei = 0;
|
||
for (ei < lateseedbaseenv.len) {
|
||
if (!strings.hasprefix(lateseedbaseenv[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(lateseedbaseenv[ei], "WW_W6A=")
|
||
&& !strings.hasprefix(lateseedbaseenv[ei], "WW_W6L=")
|
||
&& !strings.hasprefix(lateseedbaseenv[ei],
|
||
"WW_LATE_COMPILER_TRACE=")
|
||
&& !strings.hasprefix(lateseedbaseenv[ei],
|
||
"WW_LATE_ASSEMBLER_TRACE=")
|
||
&& !strings.hasprefix(lateseedbaseenv[ei],
|
||
"WW_LATE_LINKER_TRACE=")
|
||
&& !strings.hasprefix(lateseedbaseenv[ei],
|
||
"WW_LATE_REAL_COMPILER=")
|
||
&& !strings.hasprefix(lateseedbaseenv[ei],
|
||
"WW_LATE_REAL_ASSEMBLER=")
|
||
&& !strings.hasprefix(lateseedbaseenv[ei],
|
||
"WW_LATE_REAL_LINKER=")
|
||
&& !strings.hasprefix(lateseedbaseenv[ei],
|
||
"WW_LATE_FAIL_FRAGMENT=")
|
||
&& !strings.hasprefix(lateseedbaseenv[ei],
|
||
"WW_LATE_FAIL_LINK=")) {
|
||
append(seedenv, lateseedbaseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(seedenv, strings.concat("WW_W6C=", latecompilerwrapper));
|
||
append(seedenv, strings.concat("WW_W6A=", lateassemblerwrapper));
|
||
append(seedenv, strings.concat("WW_W6L=", latelinkerwrapper));
|
||
append(seedenv, strings.concat("WW_LATE_COMPILER_TRACE=", seedctrace));
|
||
append(seedenv, strings.concat("WW_LATE_ASSEMBLER_TRACE=", seedatrace));
|
||
append(seedenv, strings.concat("WW_LATE_LINKER_TRACE=", seedltrace));
|
||
append(seedenv, strings.concat("WW_LATE_REAL_COMPILER=",
|
||
driver(latecompilers[i])));
|
||
append(seedenv, strings.concat("WW_LATE_REAL_ASSEMBLER=",
|
||
driver(lateassemblers[i])));
|
||
append(seedenv, strings.concat("WW_LATE_REAL_LINKER=",
|
||
driver(latelinkers[i])));
|
||
append(seedenv, "WW_LATE_FAIL_FRAGMENT=");
|
||
append(seedenv, "WW_LATE_FAIL_LINK=0");
|
||
let seedav: []str = [driver(stages[i]), "test", "-c",
|
||
"-w", lateworks[i], "-I", root, "--ww-package-test", "test",
|
||
"late", "late", "late", "late_test", late, latebins[i],
|
||
"-", latestatuses[i], late];
|
||
runcommandenv(root, strings.concat("late-seed-", tags[i]), seedav,
|
||
seedenv, (180i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
assert(same(readfile(latestatuses[i]), "ok\n"));
|
||
let runav: []str = [latebins[i]];
|
||
runcommand(root, strings.concat("late-seed-run-", tags[i]), runav,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(occurrences(out.stdout, "internal_base ... ok\n") == 1);
|
||
assert(occurrences(out.stdout, "external_base ... ok\n") == 1);
|
||
latebinrefs[i] = readfile(latebins[i]);
|
||
latestatusrefs[i] = readfile(latestatuses[i]);
|
||
snapshoti = 0;
|
||
for (snapshoti < laterelative.len) {
|
||
latesnapshots[i * laterelative.len + snapshoti] = readfile(
|
||
strings.concat(lateworks[i], "/", laterelative[snapshoti]));
|
||
snapshoti += 1;
|
||
};
|
||
i += 1;
|
||
};
|
||
assert(same(latebinrefs[0], latebinrefs[1]));
|
||
let phaselabels: []str = ["internal", "external", "main", "link"];
|
||
let phasefragments: []str = ["/late-internal-test.unit.new",
|
||
"/late_test-external-test.unit.new", "/late-test-main.unit.new", ""];
|
||
let phaseowners: []str = ["late", "late_test",
|
||
"__wwtestmain.late.main", ""];
|
||
let phasecompiles: []i32 = [2, 2, 3, 2];
|
||
let phaseassembles: []i32 = [1, 1, 2, 2];
|
||
let phaselinks: []i32 = [0, 0, 0, 1];
|
||
let phasectrefs: []str = ["", "", "", ""];
|
||
let phaseatrefs: []str = ["", "", "", ""];
|
||
let phaseltrefs: []str = ["", "", "", ""];
|
||
let phasediagnosticrefs: []str = ["", "", "", ""];
|
||
let lateactions: []str = ["late_dep", latebridgeaction,
|
||
"late-internal-test", "late_test-external-test", "late-test-main",
|
||
"test"];
|
||
let latestagesuffixes: []str = [".unit.new", ".wwi.new", ".s.new",
|
||
".o.new", ".a.new", ".init.unit.new", ".init.s.new",
|
||
".init.o.new"];
|
||
let latebaseenv: []str = os.getenvs();
|
||
let phasei: i32 = 0;
|
||
for (phasei < phaselabels.len) {
|
||
rewritefile(depfile, depchanged);
|
||
if (phasei == 0 || phasei == 3) {
|
||
rewritefile(latesamefile, samechanged);
|
||
} else {
|
||
rewritefile(latesamefile, samebase);
|
||
};
|
||
if (phasei == 1) {
|
||
rewritefile(lateexternalfile, externalchanged);
|
||
} else { if (phasei == 2) {
|
||
rewritefile(lateexternalfile, externalmainchanged);
|
||
} else {
|
||
rewritefile(lateexternalfile, externalbase);
|
||
}; };
|
||
i = 0;
|
||
for (i < stages.len) {
|
||
let retry: i32 = 0;
|
||
for (retry < 2) {
|
||
let retrylabel: str = "first";
|
||
if (retry != 0) { retrylabel = "second"; };
|
||
let ctracepath: str = strings.concat(root, "/late-",
|
||
phaselabels[phasei], "-", tags[i], "-", retrylabel,
|
||
"-w6c.trace");
|
||
let atracepath: str = strings.concat(root, "/late-",
|
||
phaselabels[phasei], "-", tags[i], "-", retrylabel,
|
||
"-w6a.trace");
|
||
let ltracepath: str = strings.concat(root, "/late-",
|
||
phaselabels[phasei], "-", tags[i], "-", retrylabel,
|
||
"-w6l.trace");
|
||
writefile(ctracepath, ""); writefile(atracepath, "");
|
||
writefile(ltracepath, "");
|
||
let attemptbin: str = strings.concat(root, "/late-attempt-",
|
||
phaselabels[phasei], "-", tags[i], "-", retrylabel,
|
||
".test");
|
||
let attemptstatus: str = strings.concat(root, "/late-attempt-",
|
||
phaselabels[phasei], "-", tags[i], "-", retrylabel,
|
||
".status");
|
||
let env: []str = alloc([], (latebaseenv.len + 11): u64)!;
|
||
ei = 0;
|
||
for (ei < latebaseenv.len) {
|
||
if (!strings.hasprefix(latebaseenv[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(latebaseenv[ei], "WW_W6A=")
|
||
&& !strings.hasprefix(latebaseenv[ei], "WW_W6L=")
|
||
&& !strings.hasprefix(latebaseenv[ei],
|
||
"WW_LATE_COMPILER_TRACE=")
|
||
&& !strings.hasprefix(latebaseenv[ei],
|
||
"WW_LATE_ASSEMBLER_TRACE=")
|
||
&& !strings.hasprefix(latebaseenv[ei],
|
||
"WW_LATE_LINKER_TRACE=")
|
||
&& !strings.hasprefix(latebaseenv[ei],
|
||
"WW_LATE_REAL_COMPILER=")
|
||
&& !strings.hasprefix(latebaseenv[ei],
|
||
"WW_LATE_REAL_ASSEMBLER=")
|
||
&& !strings.hasprefix(latebaseenv[ei],
|
||
"WW_LATE_REAL_LINKER=")
|
||
&& !strings.hasprefix(latebaseenv[ei],
|
||
"WW_LATE_FAIL_FRAGMENT=")
|
||
&& !strings.hasprefix(latebaseenv[ei],
|
||
"WW_LATE_FAIL_LINK=")) {
|
||
append(env, latebaseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(env, strings.concat("WW_W6C=", latecompilerwrapper));
|
||
append(env, strings.concat("WW_W6A=", lateassemblerwrapper));
|
||
append(env, strings.concat("WW_W6L=", latelinkerwrapper));
|
||
append(env, strings.concat("WW_LATE_COMPILER_TRACE=", ctracepath));
|
||
append(env, strings.concat("WW_LATE_ASSEMBLER_TRACE=", atracepath));
|
||
append(env, strings.concat("WW_LATE_LINKER_TRACE=", ltracepath));
|
||
append(env, strings.concat("WW_LATE_REAL_COMPILER=",
|
||
driver(latecompilers[i])));
|
||
append(env, strings.concat("WW_LATE_REAL_ASSEMBLER=",
|
||
driver(lateassemblers[i])));
|
||
append(env, strings.concat("WW_LATE_REAL_LINKER=",
|
||
driver(latelinkers[i])));
|
||
append(env, strings.concat("WW_LATE_FAIL_FRAGMENT=",
|
||
phasefragments[phasei]));
|
||
let faillink: str = "0";
|
||
if (phasei == 3) { faillink = "1"; };
|
||
append(env, strings.concat("WW_LATE_FAIL_LINK=", faillink));
|
||
let failav: []str = [driver(stages[i]), "test", "-c",
|
||
"-w", lateworks[i], "-I", root, "--ww-package-test", "test",
|
||
"late", "late", "late", "late_test", late, attemptbin,
|
||
"-", attemptstatus, late];
|
||
runcommandenv(root, strings.concat("late-failure-",
|
||
phaselabels[phasei], "-", tags[i], "-", retrylabel), failav,
|
||
env, (180i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(out.stdout.len == 0);
|
||
if (phasei == 3) {
|
||
assert(has(out.stderr, "injected w6l failure\n"));
|
||
assert(has(out.stderr, "ww: w6l failed\n"));
|
||
} else {
|
||
assert(has(out.stderr, strings.concat(
|
||
"injected w6c failure: ", phasefragments[phasei], "\n")));
|
||
assert(has(out.stderr, strings.concat(
|
||
"ww: w6c failed for ", phaseowners[phasei], "\n")));
|
||
};
|
||
assert(!os.exists(attemptbin));
|
||
assert(!os.exists(strings.concat(attemptbin, ".new")));
|
||
assert(!os.exists(strings.concat(attemptbin, ".result")));
|
||
assert(!os.exists(strings.concat(attemptbin, ".result.new")));
|
||
assert(!os.exists(attemptstatus));
|
||
assert(!os.exists(strings.concat(attemptstatus, ".new")));
|
||
let ct: str = readfile(ctracepath);
|
||
let at: str = readfile(atracepath);
|
||
let lt: str = readfile(ltracepath);
|
||
assert(occurrences(ct, "\n") == phasecompiles[phasei]);
|
||
assert(occurrences(at, "\n") == phaseassembles[phasei]);
|
||
assert(occurrences(lt, "\n") == phaselinks[phasei]);
|
||
assert(pos(ct, "/late_dep.unit.new") >= 0);
|
||
if (phasei != 3) {
|
||
assert(pos(ct, "/late_dep.unit.new")
|
||
< pos(ct, phasefragments[phasei]));
|
||
};
|
||
assert(!directoryhasnew(lateworks[i]));
|
||
let actioni: i32 = 0;
|
||
for (actioni < lateactions.len) {
|
||
let suffixi: i32 = 0;
|
||
for (suffixi < latestagesuffixes.len) {
|
||
assert(!os.exists(strings.concat(lateworks[i], "/",
|
||
lateactions[actioni], latestagesuffixes[suffixi])));
|
||
suffixi += 1;
|
||
};
|
||
actioni += 1;
|
||
};
|
||
snapshoti = 0;
|
||
for (snapshoti < laterelative.len) {
|
||
assert(same(latesnapshots[i * laterelative.len + snapshoti],
|
||
readfile(strings.concat(lateworks[i], "/",
|
||
laterelative[snapshoti]))));
|
||
snapshoti += 1;
|
||
};
|
||
assert(same(latebinrefs[i], readfile(latebins[i])));
|
||
assert(same(latestatusrefs[i], readfile(latestatuses[i])));
|
||
let nc: str = normalizedtrace(ct,
|
||
strings.concat(lateworks[i], "/"), attemptbin);
|
||
let na: str = normalizedtrace(at,
|
||
strings.concat(lateworks[i], "/"), attemptbin);
|
||
let nl: str = normalizedtrace(lt,
|
||
strings.concat(lateworks[i], "/"), attemptbin);
|
||
let nd: str = normalizedtrace(out.stderr,
|
||
strings.concat(lateworks[i], "/"), attemptbin);
|
||
if (i == 0 && retry == 0) {
|
||
phasectrefs[phasei] = nc; phaseatrefs[phasei] = na;
|
||
phaseltrefs[phasei] = nl;
|
||
phasediagnosticrefs[phasei] = nd;
|
||
} else {
|
||
assert(same(phasectrefs[phasei], nc));
|
||
assert(same(phaseatrefs[phasei], na));
|
||
assert(same(phaseltrefs[phasei], nl));
|
||
assert(same(phasediagnosticrefs[phasei], nd));
|
||
};
|
||
retry += 1;
|
||
};
|
||
i += 1;
|
||
};
|
||
phasei += 1;
|
||
};
|
||
clean(root);
|
||
};
|
||
|
||
// The compiler-generated runner uses a reserved graph qualifier rather than
|
||
// claiming the user-visible import path `test`. A real multi-file production
|
||
// package named test can therefore be the normal direct dependency of its
|
||
// external test variant in both bootstrap stages.
|
||
@test fn production_package_named_test_remains_available() void = {
|
||
let root: str = fresh();
|
||
let pkg: str = strings.concat(root, "/test");
|
||
assert(os.mkdir(pkg, 448i32) == 0);
|
||
writefile(strings.concat(pkg, "/a.ww"), strings.concat(
|
||
"package test;\n",
|
||
"// USER_TEST_PACKAGE_A\n",
|
||
"export fn first() i32 = { return 19; };\n"));
|
||
writefile(strings.concat(pkg, "/z.ww"), strings.concat(
|
||
"package test;\n",
|
||
"// USER_TEST_PACKAGE_Z\n",
|
||
"export fn second() i32 = { return 23; };\n"));
|
||
writefile(strings.concat(pkg, "/external_test.ww"), strings.concat(
|
||
"package test_test;\nimport test;\n",
|
||
"// USER_TEST_EXTERNAL\n",
|
||
"@test fn package_name_is_not_reserved() void = {\n",
|
||
" assert(test.first() + test.second() == 42);\n};\n"));
|
||
|
||
let bin: str = strings.concat(root, "/named-test.bin");
|
||
let workroot: str = strings.concat(bin, ".sepwork");
|
||
let work: str = strings.concat(workroot, "/");
|
||
let stages: []str = ["ww", "ww_ww"];
|
||
let referencebin: str = "";
|
||
let referenceroot: str = "";
|
||
let referenceprod: str = "";
|
||
let referencesupport: str = "";
|
||
let out: commandout;
|
||
let i: i32 = 0;
|
||
for (i < stages.len) {
|
||
let av: []str = [driver(stages[i]), "test", "-w", workroot,
|
||
"-I", root, "-o", bin, pkg];
|
||
runcommand(root, strings.concat("named-test-build-", stages[i]), av,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let rootunit: str = readfile(strings.concat(work,
|
||
"test_test-external-test.unit.ww"));
|
||
let produnit: str = readfile(strings.concat(work,
|
||
"test.unit.ww"));
|
||
let supportunit: str = readfile(strings.concat(work,
|
||
"__wwtest.unit.ww"));
|
||
assert(!has(rootunit, "//ww:module "));
|
||
assert(has(rootunit, "USER_TEST_EXTERNAL"));
|
||
assert(!has(rootunit, "USER_TEST_PACKAGE_A"));
|
||
assert(!has(rootunit, "USER_TEST_PACKAGE_Z"));
|
||
assert(has(produnit, "USER_TEST_PACKAGE_A"));
|
||
assert(has(produnit, "USER_TEST_PACKAGE_Z"));
|
||
assert(has(supportunit, "//ww:module-reset __wwtest\n"));
|
||
assert(has(supportunit, "package test;\n"));
|
||
assert(os.exists(strings.concat(work,
|
||
"test.wwi")));
|
||
assert(os.exists(strings.concat(work,
|
||
"test.a")));
|
||
assert(!os.exists(strings.concat(work,
|
||
"__ww-test-000-external-production.unit.ww")));
|
||
assert(os.exists(strings.concat(work, "__wwtest.wwi")));
|
||
assert(os.exists(strings.concat(work, "__wwtest.a")));
|
||
let runav: []str = [bin];
|
||
runcommand(root, strings.concat("named-test-run-", stages[i]), runav,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout, "package_name_is_not_reserved ... ok\n"));
|
||
if (i == 0) {
|
||
referencebin = readfile(bin);
|
||
referenceroot = strings.dup(rootunit);
|
||
referenceprod = strings.dup(produnit);
|
||
referencesupport = strings.dup(supportunit);
|
||
} else {
|
||
assert(same(referencebin, readfile(bin)));
|
||
assert(same(referenceroot, rootunit));
|
||
assert(same(referenceprod, produnit));
|
||
assert(same(referencesupport, supportunit));
|
||
};
|
||
if (i + 1 < stages.len) {
|
||
clean(workroot);
|
||
clean(bin);
|
||
};
|
||
i += 1;
|
||
};
|
||
clean(root);
|
||
};
|
||
|
||
@test fn internal_package_import_visibility() void = {
|
||
let root: str = fresh();
|
||
let source: str = strings.concat(root, "/host/internal/source");
|
||
let runtime: str = strings.concat(root, "/runtime");
|
||
let tools: str = strings.concat(root, "/tools");
|
||
let domain: str = strings.concat(source, "/domain");
|
||
let leaf: str = strings.concat(domain, "/leaf");
|
||
let secret: str = strings.concat(domain, "/internal/secret");
|
||
let client: str = strings.concat(domain, "/client");
|
||
let outsider: str = strings.concat(source, "/outsider");
|
||
let prefixclient: str = strings.concat(source, "/domainx/client");
|
||
let deep: str = strings.concat(domain,
|
||
"/internal/outer/internal/deep");
|
||
let deepclient: str = strings.concat(domain, "/internal/outer/client");
|
||
let nestedoutsider: str = strings.concat(domain, "/nestedclient");
|
||
let internalx: str = strings.concat(domain, "/internalx/visible");
|
||
let internalxclient: str = strings.concat(source, "/internalxclient");
|
||
let testclient: str = strings.concat(domain, "/testclient");
|
||
let outsame: str = strings.concat(source, "/outsame");
|
||
let outexternal: str = strings.concat(source, "/outexternal");
|
||
let physical: str = strings.concat(root, "/physical/domain");
|
||
let linkedsecret: str = strings.concat(physical, "/internal/secret");
|
||
let linkedclient: str = strings.concat(physical, "/client");
|
||
let rawallowed: str = strings.concat(source, "/alias/rawclient.ww");
|
||
let rawalias: str = strings.concat(domain, "/rawalias.ww");
|
||
let rawescape: str = strings.concat(domain, "/rawescape.ww");
|
||
let aliasinternal: str = strings.concat(source, "/alias/internal");
|
||
let aliasclient: str = strings.concat(source, "/alias/client");
|
||
let aliasownedclient: str = strings.concat(source,
|
||
"/alias/owned-client");
|
||
let aliasoutsider: str = strings.concat(source, "/alias/outsider");
|
||
let shapedomain: str = strings.concat(source, "/shape/domain");
|
||
let shapeinternal: str = strings.concat(shapedomain, "/internal");
|
||
let shapeclient: str = strings.concat(shapedomain, "/client");
|
||
let shapeoutsider: str = strings.concat(source, "/shape/outsider");
|
||
let shapevault: str = strings.concat(root, "/vault");
|
||
mkdirall(leaf); mkdirall(secret); mkdirall(client); mkdirall(outsider);
|
||
mkdirall(prefixclient); mkdirall(deep); mkdirall(deepclient);
|
||
mkdirall(nestedoutsider); mkdirall(internalx); mkdirall(internalxclient);
|
||
mkdirall(testclient); mkdirall(outsame); mkdirall(outexternal);
|
||
mkdirall(linkedsecret); mkdirall(linkedclient); mkdirall(aliasinternal);
|
||
mkdirall(aliasownedclient); mkdirall(shapeinternal); mkdirall(shapeclient);
|
||
mkdirall(shapeoutsider); mkdirall(shapevault); mkdirall(runtime);
|
||
mkdirall(tools);
|
||
writefile(strings.concat(runtime, "/libwwrt.a"),
|
||
readfile(strings.concat(repo(), "/out/lib/libwwrt.a")));
|
||
|
||
let leafbody: str = strings.concat(
|
||
"package leaf;\n",
|
||
"export fn value() i32 = { return 20; };\n");
|
||
let secreta: str = strings.concat(
|
||
"package secret;\n",
|
||
"import domain.leaf;\n",
|
||
"export fn value() i32 = { return leaf.value() + 22; };\n");
|
||
let secretz: str = strings.concat(
|
||
"package secret;\n",
|
||
"fn private_value() i32 = { return 99; };\n");
|
||
let clientbody: str = strings.concat(
|
||
"package main;\n",
|
||
"import domain.internal.secret;\n",
|
||
"fn main() i32 = { return secret.value(); };\n");
|
||
writefile(strings.concat(leaf, "/leaf.ww"), leafbody);
|
||
writefile(strings.concat(secret, "/a.ww"), secreta);
|
||
writefile(strings.concat(secret, "/z.ww"), secretz);
|
||
writefile(strings.concat(client, "/main.ww"), clientbody);
|
||
writefile(strings.concat(outsider, "/main.ww"), strings.concat(
|
||
"package main;\nimport domain.internal.secret;\n",
|
||
"fn main() i32 = { return secret.value(); };\n"));
|
||
writefile(strings.concat(prefixclient, "/main.ww"), strings.concat(
|
||
"package main;\nimport domain.internal.secret;\n",
|
||
"fn main() i32 = { return secret.value(); };\n"));
|
||
writefile(strings.concat(deep, "/deep.ww"), strings.concat(
|
||
"package deep;\n",
|
||
"export fn value() i32 = { return 7; };\n"));
|
||
writefile(strings.concat(deepclient, "/main.ww"), strings.concat(
|
||
"package main;\n",
|
||
"import domain.internal.outer.internal.deep;\n",
|
||
"fn main() i32 = { return deep.value(); };\n"));
|
||
writefile(strings.concat(nestedoutsider, "/main.ww"), strings.concat(
|
||
"package main;\n",
|
||
"import domain.internal.outer.internal.deep;\n",
|
||
"fn main() i32 = { return deep.value(); };\n"));
|
||
writefile(strings.concat(internalx, "/visible.ww"), strings.concat(
|
||
"package visible;\n",
|
||
"export fn value() i32 = { return 9; };\n"));
|
||
writefile(strings.concat(internalxclient, "/main.ww"), strings.concat(
|
||
"package main;\nimport domain.internalx.visible;\n",
|
||
"fn main() i32 = { return visible.value(); };\n"));
|
||
|
||
writefile(strings.concat(linkedsecret, "/secret.ww"), strings.concat(
|
||
"package secret;\n",
|
||
"export fn value() i32 = { return 31; };\n"));
|
||
writefile(strings.concat(linkedclient, "/main.ww"), strings.concat(
|
||
"package main;\nimport alias.internal.secret;\n",
|
||
"fn main() i32 = { return secret.value(); };\n"));
|
||
writefile(rawallowed, strings.concat(
|
||
"package main;\nimport alias.internal.secret;\n",
|
||
"fn main() i32 = { return secret.value(); };\n"));
|
||
assert(os.symlink(rawallowed, rawalias) == 0);
|
||
assert(os.symlink(strings.concat(outsider, "/main.ww"), rawescape) == 0);
|
||
assert(os.symlink(linkedsecret,
|
||
strings.concat(aliasinternal, "/secret")) == 0);
|
||
writefile(strings.concat(aliasownedclient, "/main.ww"), strings.concat(
|
||
"package main;\nimport alias.internal.secret;\n",
|
||
"fn main() i32 = { return secret.value(); };\n"));
|
||
assert(os.symlink(aliasownedclient, aliasclient) == 0);
|
||
assert(os.symlink(linkedclient, aliasoutsider) == 0);
|
||
writefile(strings.concat(shapevault, "/secret.ww"), strings.concat(
|
||
"package secret;\n",
|
||
"export fn value() i32 = { return 17; };\n"));
|
||
assert(os.symlink(shapevault,
|
||
strings.concat(shapeinternal, "/secret")) == 0);
|
||
writefile(strings.concat(shapeclient, "/main.ww"), strings.concat(
|
||
"package main;\nimport shape.domain.internal.secret;\n",
|
||
"fn main() i32 = { return secret.value(); };\n"));
|
||
writefile(strings.concat(shapeoutsider, "/main.ww"), strings.concat(
|
||
"package main;\nimport shape.domain.internal.secret;\n",
|
||
"fn main() i32 = { return secret.value(); };\n"));
|
||
|
||
writefile(strings.concat(testclient, "/testclient.ww"), strings.concat(
|
||
"package testclient;\n",
|
||
"export fn value() i32 = { return 1; };\n"));
|
||
writefile(strings.concat(testclient, "/same_test.ww"), strings.concat(
|
||
"package testclient;\nimport domain.internal.secret;\n",
|
||
"@test fn allowed_same() void = {\n",
|
||
" assert(secret.value() == 42);\n};\n"));
|
||
writefile(strings.concat(testclient, "/external_test.ww"), strings.concat(
|
||
"package testclient_test;\nimport domain.internal.secret;\n",
|
||
"import domain.testclient;\n",
|
||
"@test fn allowed_external() void = {\n",
|
||
" assert(secret.value() + testclient.value() == 43);\n};\n"));
|
||
writefile(strings.concat(outsame, "/outsame.ww"),
|
||
"package outsame;\nexport fn value() i32 = { return 1; };\n");
|
||
writefile(strings.concat(outsame, "/same_test.ww"), strings.concat(
|
||
"package outsame;\nimport domain.internal.secret;\n",
|
||
"@test fn forbidden_same() void = { assert(false); };\n"));
|
||
writefile(strings.concat(outexternal, "/outexternal.ww"),
|
||
"package outexternal;\nexport fn value() i32 = { return 1; };\n");
|
||
writefile(strings.concat(outexternal, "/external_test.ww"), strings.concat(
|
||
"package outexternal_test;\nimport domain.internal.secret;\n",
|
||
"@test fn forbidden_external() void = { assert(false); };\n"));
|
||
|
||
let compilerwrapper: str = strings.concat(tools, "/w6c.sh");
|
||
let assemblerwrapper: str = strings.concat(tools, "/w6a.sh");
|
||
let linkerwrapper: str = strings.concat(tools, "/w6l.sh");
|
||
writeexecutable(compilerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf 'BEGIN' >> \"$WW_INTERNAL_COMPILER_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||
"\"$WW_INTERNAL_COMPILER_TRACE\"; done\n",
|
||
"printf '\\n' >> \"$WW_INTERNAL_COMPILER_TRACE\"\n",
|
||
"exec \"$WW_INTERNAL_REAL_COMPILER\" \"$@\"\n"));
|
||
writeexecutable(assemblerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf 'BEGIN' >> \"$WW_INTERNAL_ASSEMBLER_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||
"\"$WW_INTERNAL_ASSEMBLER_TRACE\"; done\n",
|
||
"printf '\\n' >> \"$WW_INTERNAL_ASSEMBLER_TRACE\"\n",
|
||
"exec \"$WW_INTERNAL_REAL_ASSEMBLER\" \"$@\"\n"));
|
||
writeexecutable(linkerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf 'BEGIN' >> \"$WW_INTERNAL_LINKER_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||
"\"$WW_INTERNAL_LINKER_TRACE\"; done\n",
|
||
"printf '\\n' >> \"$WW_INTERNAL_LINKER_TRACE\"\n",
|
||
"exec \"$WW_INTERNAL_REAL_LINKER\" \"$@\"\n"));
|
||
|
||
let stages: []str = ["ww", "ww_ww"];
|
||
let compilers: []str = ["w6c", "w6c_ww"];
|
||
let assemblers: []str = ["w6a", "w6a_ww"];
|
||
let linkers: []str = ["w6l", "w6l_ww"];
|
||
let labels: []str = ["0", "1", "2", "3", "4"];
|
||
let works: []str = [strings.concat(root, "/c-work"),
|
||
strings.concat(root, "/ww-work")];
|
||
let bins: []str = [strings.concat(root, "/c-bin"),
|
||
strings.concat(root, "/ww-bin")];
|
||
let compilertraces: []str = [strings.concat(root, "/c-compiler"),
|
||
strings.concat(root, "/ww-compiler")];
|
||
let assemblertraces: []str = [strings.concat(root, "/c-assembler"),
|
||
strings.concat(root, "/ww-assembler")];
|
||
let linkertraces: []str = [strings.concat(root, "/c-linker"),
|
||
strings.concat(root, "/ww-linker")];
|
||
let artifacts: []str = ["domain.leaf", "domain.internal.secret",
|
||
"domain.client"];
|
||
let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"];
|
||
let references: []str = alloc([], (artifacts.len * suffixes.len): u64)!;
|
||
let ri: i32 = 0;
|
||
for (ri < artifacts.len * suffixes.len) {
|
||
append(references, ""); ri += 1;
|
||
};
|
||
let referencebin: str = "";
|
||
let referencecompiler: str = "";
|
||
let referenceassembler: str = "";
|
||
let referencelinker: str = "";
|
||
let referencediagnostic: str = "";
|
||
let referencetestout: str = "";
|
||
let referencetesterr: str = "";
|
||
let referencetestartifacts: []str = alloc([], 9u64)!;
|
||
ri = 0;
|
||
for (ri < 9) { append(referencetestartifacts, ""); ri += 1; };
|
||
let allowedreferences: []str = ["", "", "", ""];
|
||
let testrejectreferences: []str = ["", ""];
|
||
let orderreference: str = "";
|
||
let baseenv: []str = os.getenvs();
|
||
let si: i32 = 0;
|
||
for (si < stages.len) {
|
||
assert(os.mkdir(works[si], 448i32) == 0);
|
||
writefile(compilertraces[si], ""); writefile(assemblertraces[si], "");
|
||
writefile(linkertraces[si], "");
|
||
let env: []str = alloc([], (baseenv.len + 11): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < baseenv.len) {
|
||
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6A=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6L=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_INTERNAL_COMPILER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_INTERNAL_ASSEMBLER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_INTERNAL_LINKER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_INTERNAL_REAL_COMPILER=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_INTERNAL_REAL_ASSEMBLER=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_INTERNAL_REAL_LINKER=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_LIB=")) {
|
||
append(env, baseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(env, strings.concat("WW_W6C=", compilerwrapper));
|
||
append(env, strings.concat("WW_W6A=", assemblerwrapper));
|
||
append(env, strings.concat("WW_W6L=", linkerwrapper));
|
||
append(env, strings.concat("WW_INTERNAL_COMPILER_TRACE=",
|
||
compilertraces[si]));
|
||
append(env, strings.concat("WW_INTERNAL_ASSEMBLER_TRACE=",
|
||
assemblertraces[si]));
|
||
append(env, strings.concat("WW_INTERNAL_LINKER_TRACE=",
|
||
linkertraces[si]));
|
||
append(env, strings.concat("WW_INTERNAL_REAL_COMPILER=",
|
||
driver(compilers[si])));
|
||
append(env, strings.concat("WW_INTERNAL_REAL_ASSEMBLER=",
|
||
driver(assemblers[si])));
|
||
append(env, strings.concat("WW_INTERNAL_REAL_LINKER=",
|
||
driver(linkers[si])));
|
||
append(env, strings.concat("WW_LIB=", runtime));
|
||
|
||
let av: []str = [driver(stages[si]), "build", "-w", works[si],
|
||
"-I", source, "-o", bins[si], client];
|
||
let out: commandout;
|
||
runcommandenv(root, strings.concat("internal-cold-", stages[si]), av,
|
||
env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
let wantsecret: str = strings.concat(
|
||
"//ww:module-reset domain.internal.secret\n", secreta,
|
||
"\n//ww:module-reset domain.internal.secret\n", secretz, "\n");
|
||
let secretunit: str = readfile(strings.concat(works[si],
|
||
"/domain.internal.secret.unit.ww"));
|
||
assert(strings.hasprefix(secretunit, wantsecret));
|
||
assert(has(secretunit, "//ww:direct-export domain.leaf "));
|
||
let ai: i32 = 0;
|
||
for (ai < artifacts.len) {
|
||
let xi: i32 = 0;
|
||
for (xi < suffixes.len) {
|
||
let bytes: str = readfile(strings.concat(works[si], "/",
|
||
artifacts[ai], suffixes[xi]));
|
||
let index: i32 = ai * suffixes.len + xi;
|
||
if (si == 0) { references[index] = strings.dup(bytes); }
|
||
else { assert(same(references[index], bytes)); };
|
||
xi += 1;
|
||
};
|
||
ai += 1;
|
||
};
|
||
let ctrace: str = readfile(compilertraces[si]);
|
||
let atrace: str = readfile(assemblertraces[si]);
|
||
let ltrace: str = readfile(linkertraces[si]);
|
||
assert(occurrences(ctrace, "\n") == 3);
|
||
assert(occurrences(atrace, "\n") == 4);
|
||
assert(occurrences(ltrace, "\n") == 1);
|
||
let dispatchline: str = strings.concat("BEGIN<-o><", works[si],
|
||
"/domain.client.init.o.new><", works[si],
|
||
"/domain.client.init.s.new>");
|
||
assert(same(linecontaining(atrace, "/domain.client.init.o.new"),
|
||
dispatchline));
|
||
let secretline: str = linecontaining(ctrace,
|
||
"domain.internal.secret.unit.new");
|
||
assert(occurrences(secretline, "domain.leaf.wwi") == 1);
|
||
assert(!has(secretline, "domain.client.wwi"));
|
||
let clientline: str = linecontaining(ctrace,
|
||
"domain.client.unit.new");
|
||
assert(occurrences(clientline,
|
||
"domain.internal.secret.wwi") == 1);
|
||
assert(!has(clientline, "domain.leaf.wwi"));
|
||
assert(occurrences(ltrace, strings.concat(works[si],
|
||
"/domain.client.a")) == 1);
|
||
assert(occurrences(ltrace, strings.concat(works[si],
|
||
"/domain.internal.secret.a")) == 1);
|
||
assert(occurrences(ltrace, strings.concat(works[si],
|
||
"/domain.leaf.a")) == 1);
|
||
assert(!has(ltrace, ".wwi"));
|
||
let normalizedcompiler: str = normalizedtrace(ctrace,
|
||
strings.concat(works[si], "/"), bins[si]);
|
||
let normalizedassembler: str = normalizedtrace(atrace,
|
||
strings.concat(works[si], "/"), bins[si]);
|
||
let normalizedlinker: str = normalizedtrace(ltrace,
|
||
strings.concat(works[si], "/"), bins[si]);
|
||
if (si == 0) {
|
||
referencecompiler = strings.dup(normalizedcompiler);
|
||
referenceassembler = strings.dup(normalizedassembler);
|
||
referencelinker = strings.dup(normalizedlinker);
|
||
referencebin = strings.dup(readfile(bins[si]));
|
||
} else {
|
||
assert(same(referencecompiler, normalizedcompiler));
|
||
assert(same(referenceassembler, normalizedassembler));
|
||
assert(same(referencelinker, normalizedlinker));
|
||
assert(same(referencebin, readfile(bins[si])));
|
||
};
|
||
let runav: []str = [bins[si]];
|
||
runcommand(root, strings.concat("internal-run-", stages[si]), runav,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 42);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
|
||
let voucher: str = readfile(strings.concat(works[si],
|
||
"/domain.internal.secret.unit.ww"));
|
||
let toolww: str = readfile(strings.concat(works[si], "/.wwtool.ww"));
|
||
let toolc: str = readfile(strings.concat(works[si], "/.wwtool.w6c"));
|
||
let toola: str = readfile(strings.concat(works[si], "/.wwtool.w6a"));
|
||
let toolstamp: str = readfile(strings.concat(works[si],
|
||
"/.wwtool.stamp"));
|
||
rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
runcommandenv(root, strings.concat("internal-warm-", stages[si]), av,
|
||
env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(readfile(compilertraces[si]).len == 0);
|
||
assert(readfile(assemblertraces[si]).len == 0);
|
||
assert(occurrences(readfile(linkertraces[si]), "\n") == 1);
|
||
assert(same(voucher, readfile(strings.concat(works[si],
|
||
"/domain.internal.secret.unit.ww"))));
|
||
|
||
rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
let badout: str = strings.concat(root, "/warm-forbidden-", stages[si]);
|
||
let badav: []str = [driver(stages[si]), "build", "-w", works[si],
|
||
"-I", source, "-o", badout, outsider];
|
||
runcommandenv(root, strings.concat("internal-warm-forbidden-",
|
||
stages[si]), badav, env,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
let wantdiag: str = strings.concat(outsider,
|
||
"/main.ww:2:1: error: use of internal package ",
|
||
"domain.internal.secret not allowed\n");
|
||
assert(same(out.stderr, wantdiag));
|
||
if (si == 0) { referencediagnostic = strings.dup(out.stderr); }
|
||
else { assert(same(referencediagnostic, out.stderr)); };
|
||
assert(readfile(compilertraces[si]).len == 0);
|
||
assert(readfile(assemblertraces[si]).len == 0);
|
||
assert(readfile(linkertraces[si]).len == 0);
|
||
assert(!os.exists(badout));
|
||
assert(!os.exists(strings.concat(works[si], "/outsider.unit.ww")));
|
||
assert(same(voucher, readfile(strings.concat(works[si],
|
||
"/domain.internal.secret.unit.ww"))));
|
||
assert(same(toolww, readfile(strings.concat(works[si], "/.wwtool.ww"))));
|
||
assert(same(toolc, readfile(strings.concat(works[si], "/.wwtool.w6c"))));
|
||
assert(same(toola, readfile(strings.concat(works[si], "/.wwtool.w6a"))));
|
||
assert(same(toolstamp, readfile(strings.concat(works[si],
|
||
"/.wwtool.stamp"))));
|
||
|
||
let coldreject: str = strings.concat(root, "/cold-reject-", stages[si]);
|
||
assert(os.mkdir(coldreject, 448i32) == 0);
|
||
rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
let coldav: []str = [driver(stages[si]), "build", "-w", coldreject,
|
||
"-I", source, "-o", strings.concat(coldreject, ".bin"), outsider];
|
||
runcommandenv(root, strings.concat("internal-cold-forbidden-",
|
||
stages[si]), coldav, env,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(same(out.stderr, wantdiag));
|
||
assert(readfile(compilertraces[si]).len == 0);
|
||
assert(readfile(assemblertraces[si]).len == 0);
|
||
assert(readfile(linkertraces[si]).len == 0);
|
||
assert(!os.exists(strings.concat(coldreject, "/.wwtool.ww")));
|
||
assert(!os.exists(strings.concat(coldreject, "/.wwtool.w6c")));
|
||
assert(!os.exists(strings.concat(coldreject, "/.wwtool.w6a")));
|
||
assert(!os.exists(strings.concat(coldreject, "/.wwtool.stamp")));
|
||
assert(!os.exists(strings.concat(coldreject, ".bin")));
|
||
assert(!os.exists(strings.concat(coldreject,
|
||
"/domain.internal.secret.unit.ww")));
|
||
assert(!os.exists(strings.concat(coldreject,
|
||
"/domain.internal.secret.a")));
|
||
assert(!os.exists(strings.concat(coldreject, "/outsider.unit.ww")));
|
||
|
||
let allowedtargets: []str = [deepclient, internalxclient, aliasclient,
|
||
shapeclient];
|
||
let allowedcodes: []i32 = [7, 9, 31, 17];
|
||
let allowedi: i32 = 0;
|
||
for (allowedi < allowedtargets.len) {
|
||
let allowedout: str = strings.concat(root, "/allowed-", stages[si],
|
||
"-", labels[allowedi]);
|
||
let allowedav: []str = [driver(stages[si]), "build", "-I", source,
|
||
"-o", allowedout, allowedtargets[allowedi]];
|
||
runcommandenv(root, strings.concat("internal-allowed-", stages[si],
|
||
"-", labels[allowedi]), allowedav, env,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let allowedrun: []str = [allowedout];
|
||
runcommand(root, strings.concat("internal-allowed-run-", stages[si],
|
||
"-", labels[allowedi]), allowedrun,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, allowedcodes[allowedi]);
|
||
if (si == 0) {
|
||
allowedreferences[allowedi] = strings.dup(readfile(allowedout));
|
||
} else {
|
||
assert(same(allowedreferences[allowedi], readfile(allowedout)));
|
||
};
|
||
allowedi += 1;
|
||
};
|
||
let rawout: str = strings.concat(root, "/raw-allowed-", stages[si]);
|
||
let rawav: []str = [driver(stages[si]), "build", "-I", source,
|
||
"-o", rawout, rawalias];
|
||
runcommandenv(root, strings.concat("internal-raw-allowed-", stages[si]),
|
||
rawav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let rawrun: []str = [rawout];
|
||
runcommand(root, strings.concat("internal-raw-run-", stages[si]), rawrun,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 31);
|
||
|
||
let directout: str = strings.concat(root, "/direct-", stages[si], ".a");
|
||
let directav: []str = [driver(stages[si]), "build", "-I", source,
|
||
"-o", directout, secret];
|
||
runcommandenv(root, strings.concat("internal-direct-", stages[si]),
|
||
directav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(same(references[1 * suffixes.len + 4], readfile(directout)));
|
||
assert(same(references[1 * suffixes.len + 1],
|
||
readfile(strings.concat(directout, ".wwi"))));
|
||
|
||
let rejecttargets: []str = [prefixclient, nestedoutsider, aliasoutsider,
|
||
rawescape, shapeoutsider];
|
||
let rejectimports: []str = ["domain.internal.secret",
|
||
"domain.internal.outer.internal.deep", "alias.internal.secret",
|
||
"domain.internal.secret", "shape.domain.internal.secret"];
|
||
let rejecti: i32 = 0;
|
||
for (rejecti < rejecttargets.len) {
|
||
let rejectwork: str = strings.concat(root, "/reject-", stages[si],
|
||
"-", labels[rejecti]);
|
||
assert(os.mkdir(rejectwork, 448i32) == 0);
|
||
rewritefile(compilertraces[si], "");
|
||
rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
let rejectav: []str = [driver(stages[si]), "build", "-w",
|
||
rejectwork, "-I", source, "-o", strings.concat(rejectwork,
|
||
".bin"), rejecttargets[rejecti]];
|
||
runcommandenv(root, strings.concat("internal-reject-", stages[si],
|
||
"-", labels[rejecti]), rejectav, env,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
let rejectsource: str = strings.concat(rejecttargets[rejecti],
|
||
"/main.ww");
|
||
if (rejecti == 3) { rejectsource = rawescape; };
|
||
let rejectdiag: str = strings.concat(rejectsource,
|
||
":2:1: error: use of internal package ",
|
||
rejectimports[rejecti], " not allowed\n");
|
||
assert(same(out.stderr, rejectdiag));
|
||
assert(readfile(compilertraces[si]).len == 0);
|
||
assert(readfile(assemblertraces[si]).len == 0);
|
||
assert(readfile(linkertraces[si]).len == 0);
|
||
assert(!os.exists(strings.concat(rejectwork, "/.wwtool.ww")));
|
||
assert(!os.exists(strings.concat(rejectwork, "/.wwtool.w6c")));
|
||
assert(!os.exists(strings.concat(rejectwork, "/.wwtool.w6a")));
|
||
assert(!os.exists(strings.concat(rejectwork, "/.wwtool.stamp")));
|
||
assert(!os.exists(strings.concat(rejectwork, ".bin")));
|
||
rejecti += 1;
|
||
};
|
||
|
||
let testworkroot: str = strings.concat(root, "/test-work-", stages[si]);
|
||
assert(os.mkdir(testworkroot, 448i32) == 0);
|
||
rewritefile(compilertraces[si], ""); rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
let testav: []str = [driver(stages[si]), "test", "-w", testworkroot,
|
||
"-I", source, testclient];
|
||
runcommandenv(root, strings.concat("internal-test-allowed-", stages[si]),
|
||
testav, env, (180i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout, "allowed_same ... ok\n"));
|
||
assert(has(out.stdout, "allowed_external ... ok\n"));
|
||
if (si == 0) {
|
||
referencetestout = strings.dup(out.stdout);
|
||
referencetesterr = strings.dup(out.stderr);
|
||
} else {
|
||
assert(same(referencetestout, out.stdout));
|
||
assert(same(referencetesterr, out.stderr));
|
||
};
|
||
let testwork: str = strings.concat(testworkroot, "/");
|
||
let testactions: []str = ["domain.testclient-internal-test",
|
||
"domain.testclient_test-external-test",
|
||
"domain.testclient-test-main"];
|
||
ai = 0;
|
||
for (ai < testactions.len) {
|
||
let testsuffixes: []str = [".unit.ww", ".wwi", ".a"];
|
||
let xi: i32 = 0;
|
||
for (xi < testsuffixes.len) {
|
||
let bytes: str = readfile(strings.concat(testwork,
|
||
testactions[ai], testsuffixes[xi]));
|
||
let index: i32 = ai * testsuffixes.len + xi;
|
||
if (si == 0) {
|
||
referencetestartifacts[index] = strings.dup(bytes);
|
||
} else {
|
||
assert(same(referencetestartifacts[index], bytes));
|
||
};
|
||
xi += 1;
|
||
};
|
||
ai += 1;
|
||
};
|
||
let testcompiletrace: str = readfile(compilertraces[si]);
|
||
let sameline: str = linecontaining(testcompiletrace,
|
||
"domain.testclient-internal-test.unit");
|
||
let externalline: str = linecontaining(testcompiletrace,
|
||
"domain.testclient_test-external-test.unit");
|
||
assert(occurrences(sameline, "domain.internal.secret.wwi") == 1);
|
||
assert(occurrences(externalline,
|
||
"domain.internal.secret.wwi") == 1);
|
||
assert(!has(sameline, "domain.leaf.wwi"));
|
||
assert(!has(externalline, "domain.leaf.wwi"));
|
||
assert(has(externalline, strings.concat("<--import>",
|
||
"<domain.testclient><", testwork,
|
||
"domain.testclient-internal-test.wwi.new>")));
|
||
let mainline: str = linecontaining(testcompiletrace,
|
||
"domain.testclient-test-main.unit");
|
||
assert(!has(mainline, "domain.internal.secret.wwi"));
|
||
assert(occurrences(mainline, "<--test-target-package>") == 2);
|
||
assert(pos(mainline,
|
||
"<--test-target-package><domain.testclient>")
|
||
< pos(mainline,
|
||
"<--test-target-package><domain.testclient_test>"));
|
||
assert(has(mainline, strings.concat("<--import><domain.testclient><",
|
||
testwork, "domain.testclient-internal-test.wwi.new>")));
|
||
assert(has(mainline, strings.concat(
|
||
"<--import><domain.testclient_test><", testwork,
|
||
"domain.testclient_test-external-test.wwi.new>")));
|
||
assert(occurrences(testcompiletrace, "--test-package") == 2);
|
||
assert(occurrences(testcompiletrace,
|
||
"<-T><--entry><--test-support-module>") == 1);
|
||
assert(occurrences(readfile(linkertraces[si]), "\n") == 1);
|
||
assert(!has(readfile(linkertraces[si]), ".wwi"));
|
||
|
||
let testrejects: []str = [outsame, outexternal];
|
||
let testfiles: []str = ["/same_test.ww", "/external_test.ww"];
|
||
let tri: i32 = 0;
|
||
for (tri < testrejects.len) {
|
||
let tw: str = strings.concat(root, "/test-reject-", stages[si],
|
||
"-", labels[tri]);
|
||
assert(os.mkdir(tw, 448i32) == 0);
|
||
rewritefile(compilertraces[si], "");
|
||
rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
let tav: []str = [driver(stages[si]), "test", "-w", tw,
|
||
"-I", source, testrejects[tri]];
|
||
runcommandenv(root, strings.concat("internal-test-reject-",
|
||
stages[si], "-", labels[tri]), tav, env,
|
||
(180i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
let testdiag: str = strings.concat(testrejects[tri], testfiles[tri],
|
||
":2:1: error: use of internal package ",
|
||
"domain.internal.secret not allowed\n");
|
||
assert(has(out.stderr, testdiag));
|
||
if (si == 0) {
|
||
testrejectreferences[tri] = strings.dup(out.stderr);
|
||
} else {
|
||
assert(same(testrejectreferences[tri], out.stderr));
|
||
};
|
||
assert(readfile(compilertraces[si]).len == 0);
|
||
assert(readfile(assemblertraces[si]).len == 0);
|
||
assert(readfile(linkertraces[si]).len == 0);
|
||
tri += 1;
|
||
};
|
||
|
||
let orderwork: str = strings.concat(root, "/order-work-", stages[si]);
|
||
assert(os.mkdir(orderwork, 448i32) == 0);
|
||
let orderforward: []str = [driver(stages[si]), "test", "-c", "-w",
|
||
orderwork, "-I", source,
|
||
"--ww-package-test", "test", "main", "main", "-", "-", client,
|
||
strings.concat(root, "/order-client-", stages[si]),
|
||
"-",
|
||
strings.concat(root, "/order-client-", stages[si], ".status"),
|
||
"--ww-package-test", "test", "main", "main", "-", "-", outsider,
|
||
strings.concat(root, "/order-outsider-", stages[si]),
|
||
"-",
|
||
strings.concat(root, "/order-outsider-", stages[si], ".status"),
|
||
client];
|
||
let orderreverse: []str = [driver(stages[si]), "test", "-c", "-w",
|
||
orderwork, "-I", source,
|
||
"--ww-package-test", "test", "main", "main", "-", "-", outsider,
|
||
strings.concat(root, "/order-outsider-", stages[si]),
|
||
"-",
|
||
strings.concat(root, "/order-outsider-", stages[si], ".status"),
|
||
"--ww-package-test", "test", "main", "main", "-", "-", client,
|
||
strings.concat(root, "/order-client-", stages[si]),
|
||
"-",
|
||
strings.concat(root, "/order-client-", stages[si], ".status"),
|
||
client];
|
||
let orderrequests: [][]str = [orderforward, orderreverse];
|
||
let orderdiags: []str = ["", ""];
|
||
let oi: i32 = 0;
|
||
for (oi < 2) {
|
||
rewritefile(compilertraces[si], "");
|
||
rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
runcommandenv(root, strings.concat("internal-order-", stages[si],
|
||
"-", labels[oi]), orderrequests[oi], env,
|
||
(180i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
orderdiags[oi] = strings.dup(out.stderr);
|
||
assert(same(out.stderr, wantdiag));
|
||
assert(readfile(compilertraces[si]).len == 0);
|
||
assert(readfile(assemblertraces[si]).len == 0);
|
||
assert(readfile(linkertraces[si]).len == 0);
|
||
oi += 1;
|
||
};
|
||
assert(same(orderdiags[0], orderdiags[1]));
|
||
if (si == 0) { orderreference = strings.dup(orderdiags[0]); }
|
||
else { assert(same(orderreference, orderdiags[0])); };
|
||
si += 1;
|
||
};
|
||
clean(root);
|
||
};
|
||
|
||
@test fn exact_package_tool_argv_stage_parity() void = {
|
||
let root: str = fresh();
|
||
let source: str = strings.concat(root, "/source tree");
|
||
let runtime: str = strings.concat(root, "/runtime library");
|
||
let baseidentity: str = longidentity("paritybase");
|
||
let leftidentity: str = longidentity("parityleft");
|
||
let rightidentity: str = longidentity("parityright");
|
||
let rootidentity: str = longidentity("paritycommand");
|
||
assert(baseidentity.len > 255 && rootidentity.len > 255);
|
||
let base: str = strings.concat(source, "/", importrelative(baseidentity));
|
||
let left: str = strings.concat(source, "/", importrelative(leftidentity));
|
||
let right: str = strings.concat(source, "/", importrelative(rightidentity));
|
||
let target: str = strings.concat(source, "/", importrelative(rootidentity));
|
||
let tools: str = strings.concat(root, "/tool wrappers");
|
||
assert(os.mkdir(source, 448i32) == 0);
|
||
assert(os.mkdir(runtime, 448i32) == 0);
|
||
writefile(strings.concat(runtime, "/libwwrt.a"),
|
||
readfile(strings.concat(repo(), "/out/lib/libwwrt.a")));
|
||
mkdirall(base);
|
||
mkdirall(left);
|
||
mkdirall(right);
|
||
mkdirall(target);
|
||
assert(os.mkdir(tools, 448i32) == 0);
|
||
let basea: str = strings.concat(
|
||
"package paritybase;\n",
|
||
"export fn value() i32 = { return 20; };\n");
|
||
let basez: str = strings.concat(
|
||
"package paritybase;\n",
|
||
"fn private_value() i32 = { return 99; };\n");
|
||
let leftbody: str = strings.concat(
|
||
"package parityleft;\n",
|
||
"import ", baseidentity, ";\n",
|
||
"export fn value() i32 = { return ",
|
||
"paritybase.value(); };\n");
|
||
let leftz: str = strings.concat(
|
||
"package parityleft;\n",
|
||
"import ", baseidentity, ";\n",
|
||
"export fn repeated_binding() i32 = { return ",
|
||
"paritybase.value(); };\n");
|
||
let rightbody: str = strings.concat(
|
||
"package parityright;\n",
|
||
"import ", baseidentity, ";\n",
|
||
"export fn value() i32 = { return ",
|
||
"paritybase.value() + 1; };\n");
|
||
let rootbody: str = strings.concat(
|
||
"package main;\n",
|
||
"import ", rightidentity, ";\n",
|
||
"import ", leftidentity, ";\n",
|
||
"fn main() i32 = { return parityleft.value() + ",
|
||
"parityright.value() + 1; };\n");
|
||
writefile(strings.concat(base, "/a.ww"), basea);
|
||
writefile(strings.concat(base, "/z.ww"), basez);
|
||
writefile(strings.concat(left, "/a.ww"), leftbody);
|
||
writefile(strings.concat(left, "/z.ww"), leftz);
|
||
writefile(strings.concat(right, "/right.ww"), rightbody);
|
||
writefile(strings.concat(target, "/main.ww"), rootbody);
|
||
|
||
let compilerwrapper: str = strings.concat(tools, "/w6c wrapper.sh");
|
||
let assemblerwrapper: str = strings.concat(tools, "/w6a wrapper.sh");
|
||
let linkerwrapper: str = strings.concat(tools, "/w6l wrapper.sh");
|
||
let failurewrapper: str = strings.concat(tools, "/failing w6c wrapper.sh");
|
||
writeexecutable(compilerwrapper, strings.concat(
|
||
"#!/bin/sh\n",
|
||
"printf 'BEGIN' >> \"$WW_ARGV_COMPILER_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||
"\"$WW_ARGV_COMPILER_TRACE\"; done\n",
|
||
"printf '\\n' >> \"$WW_ARGV_COMPILER_TRACE\"\n",
|
||
"exec \"$WW_ARGV_REAL_COMPILER\" \"$@\"\n"));
|
||
writeexecutable(assemblerwrapper, strings.concat(
|
||
"#!/bin/sh\n",
|
||
"printf 'BEGIN' >> \"$WW_ARGV_ASSEMBLER_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||
"\"$WW_ARGV_ASSEMBLER_TRACE\"; done\n",
|
||
"printf '\\n' >> \"$WW_ARGV_ASSEMBLER_TRACE\"\n",
|
||
"exec \"$WW_ARGV_REAL_ASSEMBLER\" \"$@\"\n"));
|
||
writeexecutable(linkerwrapper, strings.concat(
|
||
"#!/bin/sh\n",
|
||
"printf 'BEGIN' >> \"$WW_ARGV_LINKER_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||
"\"$WW_ARGV_LINKER_TRACE\"; done\n",
|
||
"printf '\\n' >> \"$WW_ARGV_LINKER_TRACE\"\n",
|
||
"exec \"$WW_ARGV_REAL_LINKER\" \"$@\"\n"));
|
||
writeexecutable(failurewrapper, strings.concat(
|
||
"#!/bin/sh\n",
|
||
"printf 'BEGIN' >> \"$WW_ARGV_FAILURE_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||
"\"$WW_ARGV_FAILURE_TRACE\"; done\n",
|
||
"printf '\\n' >> \"$WW_ARGV_FAILURE_TRACE\"\n",
|
||
"owner=\n",
|
||
"for arg in \"$@\"; do if [ \"$arg\" = ",
|
||
"\"$WW_ARGV_MISSING_OWNER\" ]; then owner=1; fi; done\n",
|
||
"if [ -n \"$owner\" ]; then\n",
|
||
" mv -- \"$WW_ARGV_MISSING_EXPORT\" ",
|
||
"\"$WW_ARGV_MISSING_EXPORT.saved\" || exit 24\n",
|
||
" \"$WW_ARGV_REAL_COMPILER\" \"$@\"\n",
|
||
" rc=$?\n",
|
||
" mv -- \"$WW_ARGV_MISSING_EXPORT.saved\" ",
|
||
"\"$WW_ARGV_MISSING_EXPORT\" || exit 25\n",
|
||
" exit $rc\n",
|
||
"fi\n",
|
||
"exec \"$WW_ARGV_REAL_COMPILER\" \"$@\"\n"));
|
||
|
||
let stages: []str = ["ww", "ww", "ww_ww", "ww_ww"];
|
||
let compilers: []str = ["w6c", "w6c", "w6c_ww", "w6c_ww"];
|
||
let assemblers: []str = ["w6a", "w6a", "w6a_ww", "w6a_ww"];
|
||
let linkers: []str = ["w6l", "w6l", "w6l_ww", "w6l_ww"];
|
||
let tags: []str = ["c1", "c2", "ww1", "ww2"];
|
||
let bin: str = strings.concat(root, "/published binary");
|
||
let workroot: str = strings.concat(bin, ".sepwork");
|
||
let work: str = strings.concat(workroot, "/");
|
||
let identities: []str = [baseidentity, leftidentity, rightidentity,
|
||
rootidentity];
|
||
let artifacts: []str = [packagestoragekey(baseidentity, base, 0, 0),
|
||
packagestoragekey(leftidentity, left, 0, 0),
|
||
packagestoragekey(rightidentity, right, 0, 0),
|
||
packagestoragekey(rootidentity, target, 0, 0)];
|
||
let unitartifacts: []str = artifacts;
|
||
let aki: i32 = 0;
|
||
for (aki < artifacts.len) {
|
||
assert(strings.hasprefix(artifacts[aki], "__wwpkg.v0.r0.h"));
|
||
assert(artifacts[aki].len <= 246);
|
||
aki += 1;
|
||
};
|
||
let wantunits: []str = [strings.concat(
|
||
"//ww:module-reset ", baseidentity, "\n", basea,
|
||
"\n//ww:module-reset ", baseidentity, "\n", basez, "\n"),
|
||
strings.concat("//ww:module-reset ", leftidentity, "\n",
|
||
leftbody, "\n//ww:module-reset ", leftidentity, "\n",
|
||
leftz, "\n"),
|
||
strings.concat("//ww:module-reset ", rightidentity, "\n",
|
||
rightbody, "\n"),
|
||
strings.concat("//ww:module-reset ", rootidentity, "\n", rootbody,
|
||
"\n")];
|
||
let referenceunits: []str = ["", "", "", ""];
|
||
let referenceexports: []str = ["", "", "", ""];
|
||
let referencearchives: []str = ["", "", "", ""];
|
||
let referencebin: str = "";
|
||
let referencecompiler: str = "";
|
||
let referenceassembler: str = "";
|
||
let referencelinker: str = "";
|
||
let referenceout: str = "";
|
||
let referenceerr: str = "";
|
||
let referencefailure: str = "";
|
||
let referencecollision: str = "";
|
||
let baseenv: []str = os.getenvs();
|
||
let si: i32 = 0;
|
||
for (si < stages.len) {
|
||
let compilertrace: str = strings.concat(root, "/", tags[si],
|
||
" compiler trace");
|
||
let assemblertrace: str = strings.concat(root, "/", tags[si],
|
||
" assembler trace");
|
||
let linkertrace: str = strings.concat(root, "/", tags[si],
|
||
" linker trace");
|
||
let failuretrace: str = strings.concat(root, "/", tags[si],
|
||
" failure trace");
|
||
writefile(compilertrace, "");
|
||
writefile(assemblertrace, "");
|
||
writefile(linkertrace, "");
|
||
writefile(failuretrace, "");
|
||
let env: []str = alloc([], (baseenv.len + 11): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < baseenv.len) {
|
||
if (!strings.hasprefix(baseenv[ei], "WW_SRCLIB=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_LIB=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6A=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6L=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_ARGV_COMPILER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_ARGV_ASSEMBLER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_ARGV_LINKER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_ARGV_REAL_COMPILER=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_ARGV_REAL_ASSEMBLER=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_ARGV_REAL_LINKER=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_ARGV_FAILURE_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_ARGV_MISSING_EXPORT=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_ARGV_MISSING_OWNER=")) {
|
||
append(env, baseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(env, strings.concat("WW_SRCLIB=", source));
|
||
append(env, strings.concat("WW_LIB=", runtime));
|
||
append(env, strings.concat("WW_W6C=", compilerwrapper));
|
||
append(env, strings.concat("WW_W6A=", assemblerwrapper));
|
||
append(env, strings.concat("WW_W6L=", linkerwrapper));
|
||
append(env, strings.concat("WW_ARGV_COMPILER_TRACE=", compilertrace));
|
||
append(env, strings.concat("WW_ARGV_ASSEMBLER_TRACE=", assemblertrace));
|
||
append(env, strings.concat("WW_ARGV_LINKER_TRACE=", linkertrace));
|
||
append(env, strings.concat("WW_ARGV_REAL_COMPILER=",
|
||
driver(compilers[si])));
|
||
append(env, strings.concat("WW_ARGV_REAL_ASSEMBLER=",
|
||
driver(assemblers[si])));
|
||
append(env, strings.concat("WW_ARGV_REAL_LINKER=",
|
||
driver(linkers[si])));
|
||
|
||
let request: str = target;
|
||
if (si == 1 || si == 3) { request = rootidentity; };
|
||
let av: []str = [driver(stages[si]), "build", "-o", bin,
|
||
request];
|
||
let out: commandout;
|
||
runcommandenv(root, strings.concat("exact-argv-", tags[si]), av, env,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stderr.len == 0);
|
||
let ui: i32 = 0;
|
||
for (ui < unitartifacts.len) {
|
||
let unit: str = readfile(strings.concat(work, unitartifacts[ui],
|
||
".unit.ww"));
|
||
assert(strings.hasprefix(unit, wantunits[ui]));
|
||
if (ui == 1 || ui == 2) {
|
||
assert(has(unit, strings.concat("//ww:direct-export ",
|
||
baseidentity, " ")));
|
||
} else { if (ui == 3) {
|
||
assert(has(unit, strings.concat("//ww:direct-export ",
|
||
leftidentity, " ")));
|
||
assert(has(unit, strings.concat("//ww:direct-export ",
|
||
rightidentity, " ")));
|
||
}; };
|
||
assert(!has(unit, "//ww:module "));
|
||
if (si == 0) { referenceunits[ui] = strings.dup(unit); }
|
||
else { assert(same(referenceunits[ui], unit)); };
|
||
ui += 1;
|
||
};
|
||
let ai: i32 = 0;
|
||
for (ai < artifacts.len) {
|
||
let exportf: str = readfile(strings.concat(work, artifacts[ai],
|
||
".wwi"));
|
||
let assembly: str = readfile(strings.concat(work, artifacts[ai],
|
||
".s"));
|
||
let archive: str = readfile(strings.concat(work, artifacts[ai],
|
||
".a"));
|
||
assert(strings.hasprefix(exportf, strings.concat("//ww:module ",
|
||
identities[ai], "\n")));
|
||
if (ai < 3) {
|
||
assert(has(assembly, strings.concat(identities[ai], ".value")));
|
||
};
|
||
if (ai == 3) {
|
||
assert(has(exportf, "\npackage main;\n"));
|
||
assert(!has(exportf, "\npackage paritycommand;\n"));
|
||
};
|
||
if (si == 0) {
|
||
referenceexports[ai] = strings.dup(exportf);
|
||
referencearchives[ai] = strings.dup(archive);
|
||
} else {
|
||
assert(same(referenceexports[ai], exportf));
|
||
assert(same(referencearchives[ai], archive));
|
||
};
|
||
ai += 1;
|
||
};
|
||
assert(os.exists(bin));
|
||
|
||
let ctrace: str = readfile(compilertrace);
|
||
let atrace: str = readfile(assemblertrace);
|
||
let ltrace: str = readfile(linkertrace);
|
||
assert(occurrences(ctrace, "\n") == 4);
|
||
let baseline: str = strings.concat(
|
||
"BEGIN<--package-init-symbol><__ww..pkg.p.", baseidentity,
|
||
".v0.r0.init><-c><-I><", work,
|
||
artifacts[0], ".wwi><-o><", work,
|
||
artifacts[0], ".s><", work,
|
||
artifacts[0], ".unit.ww>");
|
||
let leftline: str = strings.concat(
|
||
"BEGIN<--package-init-symbol><__ww..pkg.p.", leftidentity,
|
||
".v0.r0.init><-c><--import>",
|
||
"<", baseidentity, "><", work,
|
||
artifacts[0], ".wwi><-I><", work,
|
||
artifacts[1], ".wwi><-o><", work,
|
||
artifacts[1], ".s><", work,
|
||
artifacts[1], ".unit.ww>");
|
||
let rightline: str = strings.concat(
|
||
"BEGIN<--package-init-symbol><__ww..pkg.p.", rightidentity,
|
||
".v0.r0.init><-c><--import>",
|
||
"<", baseidentity, "><", work,
|
||
artifacts[0], ".wwi><-I><", work,
|
||
artifacts[2], ".wwi><-o><", work,
|
||
artifacts[2], ".s><", work,
|
||
artifacts[2], ".unit.ww>");
|
||
let rootline: str = strings.concat(
|
||
"BEGIN<--entry><--package-init-symbol><__ww..pkg.p.",
|
||
rootidentity, ".v0.r0.init><--init-dispatch-symbol>",
|
||
"<__ww..dispatch><-c><--import>",
|
||
"<", leftidentity, "><", work,
|
||
artifacts[1], ".wwi><--import>",
|
||
"<", rightidentity, "><", work,
|
||
artifacts[2], ".wwi><-I><", work,
|
||
artifacts[3], ".wwi><-o><", work,
|
||
artifacts[3], ".s><", work, artifacts[3], ".unit.ww>");
|
||
assert(same(linecontaining(ctrace,
|
||
strings.concat(artifacts[0], ".unit.ww")), baseline));
|
||
assert(same(linecontaining(ctrace,
|
||
strings.concat(artifacts[1], ".unit.ww")), leftline));
|
||
assert(same(linecontaining(ctrace,
|
||
strings.concat(artifacts[2], ".unit.ww")), rightline));
|
||
assert(same(linecontaining(ctrace, strings.concat(artifacts[3],
|
||
".unit.ww")), rootline));
|
||
assert(!has(rootline, strings.concat(artifacts[0], ".wwi")));
|
||
// Canonical owner and declared name stay independent even in a raw
|
||
// package compile. --entry selects link-root code generation; it is not
|
||
// permission to replace canonical owner with the declared name.
|
||
let rejectwwi: str = strings.concat(root, "/reject-", tags[si], ".wwi");
|
||
let rejectasm: str = strings.concat(root, "/reject-", tags[si], ".s");
|
||
let rejectav: []str = [driver(compilers[si]), "-c", "--import",
|
||
leftidentity,
|
||
strings.concat(work, artifacts[1], ".wwi"),
|
||
"--import", rightidentity,
|
||
strings.concat(work, artifacts[2], ".wwi"),
|
||
"-I", rejectwwi, "-o", rejectasm,
|
||
strings.concat(work, artifacts[3], ".unit.ww")];
|
||
runcommand(root, strings.concat("command-without-entry-", tags[si]),
|
||
rejectav, (60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
assert(os.exists(rejectwwi) && os.exists(rejectasm));
|
||
let ownerrejectwwi: str = strings.concat(root, "/owner-reject-",
|
||
tags[si], ".wwi");
|
||
let ownerrejectasm: str = strings.concat(root, "/owner-reject-",
|
||
tags[si], ".s");
|
||
let ownerrejectav: []str = [driver(compilers[si]), "-c", "--import",
|
||
leftidentity, strings.concat(work, artifacts[0], ".wwi"), "-I",
|
||
ownerrejectwwi, "-o", ownerrejectasm,
|
||
strings.concat(work, artifacts[1], ".unit.ww")];
|
||
runcommand(root, strings.concat("export-owner-reject-", tags[si]),
|
||
ownerrejectav, (60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(same(out.stderr, strings.concat("w6c: import ", leftidentity,
|
||
": export owner mismatch in ", work, artifacts[0], ".wwi\n")));
|
||
assert(occurrences(atrace, "\n") == 5);
|
||
ai = 0;
|
||
for (ai < artifacts.len) {
|
||
let assemblerline: str = strings.concat("BEGIN<-o><", work,
|
||
artifacts[ai], ".o><", work, artifacts[ai], ".s>");
|
||
assert(same(linecontaining(atrace, strings.concat(work,
|
||
artifacts[ai], ".o")), assemblerline));
|
||
ai += 1;
|
||
};
|
||
let dispatcherline: str = strings.concat("BEGIN<-o><", work,
|
||
artifacts[3], ".init.o><", work, artifacts[3], ".init.s>");
|
||
assert(same(linecontaining(atrace, strings.concat(work,
|
||
artifacts[3], ".init.o")), dispatcherline));
|
||
assert(occurrences(ltrace, "\n") == 1);
|
||
assert(has(ltrace, strings.concat("BEGIN<-o><", bin, ".new><", work,
|
||
artifacts[3], ".a>")));
|
||
ai = 0;
|
||
for (ai < artifacts.len) {
|
||
assert(occurrences(ltrace, strings.concat("<", work, artifacts[ai],
|
||
".a>")) == 1);
|
||
ai += 1;
|
||
};
|
||
assert(has(ltrace, strings.concat("<", runtime, "/libwwrt.a>")));
|
||
assert(!has(ltrace, strings.concat("<", repo(),
|
||
"/out/lib/libwwrt.a>")));
|
||
assert(!has(ltrace, ".wwi>"));
|
||
|
||
let runav: []str = [bin];
|
||
runcommand(root, strings.concat("exact-argv-run-", tags[si]), runav,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 42);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
if (si == 0) {
|
||
referencebin = strings.dup(readfile(bin));
|
||
referencecompiler = strings.dup(ctrace);
|
||
referenceassembler = strings.dup(atrace);
|
||
referencelinker = strings.dup(ltrace);
|
||
referenceout = strings.dup(out.stdout);
|
||
referenceerr = strings.dup(out.stderr);
|
||
} else {
|
||
assert(same(referencebin, readfile(bin)));
|
||
assert(same(referencecompiler, ctrace));
|
||
assert(same(referenceassembler, atrace));
|
||
assert(same(referencelinker, ltrace));
|
||
assert(same(referenceout, out.stdout));
|
||
assert(same(referenceerr, out.stderr));
|
||
};
|
||
|
||
// Select the same long ordinary directory as both an explicit product
|
||
// root and the dependency of another product in one command. Then build
|
||
// it alone through logical and literal spellings. All routes must intern
|
||
// one production action and emit the dependency-built bytes above.
|
||
let combinedbase: str = strings.concat(root, "/combined-base-", tags[si]);
|
||
let combinedleft: str = strings.concat(root, "/combined-left-", tags[si]);
|
||
let combinedbasestatus: str = strings.concat(combinedbase, ".status");
|
||
let combinedleftstatus: str = strings.concat(combinedleft, ".status");
|
||
clean(compilertrace); writefile(compilertrace, "");
|
||
clean(assemblertrace); writefile(assemblertrace, "");
|
||
clean(linkertrace); writefile(linkertrace, "");
|
||
let combinedav: []str = [driver(stages[si]), "test", "-c", "-I",
|
||
source, "--ww-package-build",
|
||
"--ww-package-test", "build", "paritybase", "paritybase",
|
||
"-", "-", base,
|
||
combinedbase, "-", combinedbasestatus,
|
||
"--ww-package-test", "build", "parityleft", "parityleft",
|
||
"-", "-", left,
|
||
combinedleft, "-", combinedleftstatus, left];
|
||
runcommandenv(root, strings.concat("long-combined-", tags[si]),
|
||
combinedav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let combinedtrace: str = readfile(compilertrace);
|
||
assert(occurrences(combinedtrace, strings.concat(artifacts[0],
|
||
".unit.ww>")) == 1);
|
||
let combinedwork: str = strings.concat(combinedbase, ".sepwork/");
|
||
let combinedunit: str = readfile(strings.concat(combinedwork,
|
||
artifacts[0], ".unit.ww"));
|
||
let combinedwwi: str = readfile(strings.concat(combinedwork,
|
||
artifacts[0], ".wwi"));
|
||
let combinedarchive: str = readfile(strings.concat(combinedwork,
|
||
artifacts[0], ".a"));
|
||
assert(same(referenceunits[0], combinedunit));
|
||
assert(same(referenceexports[0], combinedwwi));
|
||
assert(same(referencearchives[0], combinedarchive));
|
||
|
||
let rootspellings: []str = [baseidentity, base];
|
||
let rootlabels: []str = ["logical", "literal"];
|
||
let ri: i32 = 0;
|
||
for (ri < rootspellings.len) {
|
||
let rootout: str = strings.concat(root, "/long-root-",
|
||
rootlabels[ri], "-", tags[si], ".a");
|
||
clean(compilertrace); writefile(compilertrace, "");
|
||
let rootav: []str = [driver(stages[si]), "build", "-I",
|
||
source, "-o", rootout, rootspellings[ri]];
|
||
runcommandenv(root, strings.concat("long-root-", rootlabels[ri],
|
||
"-", tags[si]), rootav, env,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(occurrences(readfile(compilertrace), strings.concat(
|
||
artifacts[0], ".unit.ww>")) == 1);
|
||
let rootwork: str = strings.concat(rootout, ".sepwork/");
|
||
assert(same(combinedunit, readfile(strings.concat(rootwork,
|
||
artifacts[0], ".unit.ww"))));
|
||
assert(same(combinedwwi, readfile(strings.concat(rootwork,
|
||
artifacts[0], ".wwi"))));
|
||
assert(same(combinedarchive, readfile(strings.concat(rootwork,
|
||
artifacts[0], ".a"))));
|
||
clean(strings.concat(rootout, ".sepwork"));
|
||
clean(rootout); clean(strings.concat(rootout, ".wwi"));
|
||
ri += 1;
|
||
};
|
||
clean(strings.concat(combinedbase, ".sepwork"));
|
||
clean(combinedbase); clean(combinedleft);
|
||
clean(combinedbasestatus); clean(combinedleftstatus);
|
||
let seedav: []str = [driver(stages[si]), "build", "-w", workroot,
|
||
"-o", bin, request];
|
||
runcommandenv(root, strings.concat("persistent-seed-", tags[si]),
|
||
seedav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(os.remove(compilertrace) == 0);
|
||
assert(os.remove(assemblertrace) == 0);
|
||
writefile(compilertrace, "");
|
||
writefile(assemblertrace, "");
|
||
let equivalent: str = rootidentity;
|
||
if (same(request, rootidentity)) { equivalent = target; };
|
||
let warmav: []str = [driver(stages[si]), "build", "-w", workroot,
|
||
"-o", bin, equivalent];
|
||
runcommandenv(root, strings.concat("persistent-equivalent-", tags[si]),
|
||
warmav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(readfile(compilertrace).len == 0);
|
||
assert(readfile(assemblertrace).len == 0);
|
||
let baseafile: str = strings.concat(base, "/a.ww");
|
||
assert(os.remove(baseafile) == 0);
|
||
writefile(baseafile, strings.concat(basea,
|
||
"export fn marker() i32 = { return 7; };\n"));
|
||
runcommandenv(root, strings.concat("persistent-export-", tags[si]),
|
||
warmav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let changedtrace: str = readfile(compilertrace);
|
||
assert(occurrences(changedtrace, "\n") == 3);
|
||
assert(has(changedtrace, strings.concat(artifacts[0], ".unit.new")));
|
||
assert(has(changedtrace, strings.concat(artifacts[1], ".unit.new")));
|
||
assert(has(changedtrace, strings.concat(artifacts[2], ".unit.new")));
|
||
assert(!has(changedtrace, strings.concat(artifacts[3], ".unit.new")));
|
||
let badunit: str = strings.concat(work, artifacts[0], ".unit.ww");
|
||
assert(os.remove(badunit) == 0);
|
||
writefile(badunit, "//ww:module-reset wrong.owner\n");
|
||
assert(os.remove(compilertrace) == 0);
|
||
assert(os.remove(assemblertrace) == 0);
|
||
assert(os.remove(linkertrace) == 0);
|
||
writefile(compilertrace, "");
|
||
writefile(assemblertrace, "");
|
||
writefile(linkertrace, "");
|
||
let collisionav: []str = [driver(stages[si]), "build", "-w",
|
||
workroot, "-o", bin, request];
|
||
runcommandenv(root, strings.concat("storage-owner-", tags[si]),
|
||
collisionav, env,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
let collisiondiag: str = strings.concat(
|
||
"ww: package storage owner mismatch at ", artifacts[0],
|
||
" for ", baseidentity, "\n");
|
||
assert(same(out.stderr, collisiondiag));
|
||
assert(readfile(compilertrace).len == 0);
|
||
assert(readfile(assemblertrace).len == 0);
|
||
assert(readfile(linkertrace).len == 0);
|
||
if (si == 0) { referencecollision = strings.dup(out.stderr); }
|
||
else { assert(same(referencecollision, out.stderr)); };
|
||
assert(os.remove(baseafile) == 0);
|
||
writefile(baseafile, basea);
|
||
clean(workroot);
|
||
clean(bin);
|
||
|
||
let failenv: []str = alloc([], (env.len + 4): u64)!;
|
||
ei = 0;
|
||
for (ei < env.len) {
|
||
if (!strings.hasprefix(env[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(env[ei],
|
||
"WW_ARGV_FAILURE_TRACE=")) {
|
||
append(failenv, env[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(failenv, strings.concat("WW_W6C=", failurewrapper));
|
||
append(failenv, strings.concat("WW_ARGV_FAILURE_TRACE=", failuretrace));
|
||
append(failenv, strings.concat("WW_ARGV_MISSING_EXPORT=", work,
|
||
artifacts[0], ".wwi"));
|
||
append(failenv, strings.concat("WW_ARGV_MISSING_OWNER=", work,
|
||
artifacts[1], ".unit.ww"));
|
||
runcommandenv(root, strings.concat("exact-argv-fail-", tags[si]), av,
|
||
failenv, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
let wantfailure: str = strings.concat(
|
||
"w6c: import ", baseidentity, ": cannot read ", work,
|
||
artifacts[0], ".wwi\n",
|
||
"ww: w6c failed for ", leftidentity, "\n");
|
||
assert(same(out.stderr, wantfailure));
|
||
assert(has(readfile(failuretrace), strings.concat(
|
||
"<--import><", baseidentity, "><", work,
|
||
artifacts[0], ".wwi>")));
|
||
if (si == 0) { referencefailure = strings.dup(out.stderr); }
|
||
else { assert(same(referencefailure, out.stderr)); };
|
||
clean(workroot);
|
||
if (os.exists(bin)) { clean(bin); };
|
||
si += 1;
|
||
};
|
||
clean(root);
|
||
};
|
||
|
||
@test fn command_package_test_variants_keep_canonical_identity() void = {
|
||
let root: str = fresh();
|
||
let source: str = strings.concat(root, "/source");
|
||
let commandid: str = longidentity("cmdtool");
|
||
assert(commandid.len > 255);
|
||
let target: str = strings.concat(source, "/", importrelative(commandid));
|
||
let importer: str = strings.concat(source, "/importer");
|
||
let wrapper: str = strings.concat(root, "/command-w6c.sh");
|
||
assert(os.mkdir(source, 448i32) == 0);
|
||
mkdirall(target);
|
||
assert(os.mkdir(importer, 448i32) == 0);
|
||
writefile(strings.concat(target, "/main.ww"), strings.concat(
|
||
"package main;\n",
|
||
"export fn value() i32 = { return 41; };\n",
|
||
"fn main() i32 = { return 0; };\n"));
|
||
writefile(strings.concat(target, "/internal_test.ww"), strings.concat(
|
||
"package main;\n",
|
||
"@test fn command_internal() void = { assert(value() == 41); };\n"));
|
||
writefile(strings.concat(target, "/external_test.ww"), strings.concat(
|
||
"package main_test;\n",
|
||
"import ", commandid, ";\n",
|
||
"@test fn command_external() void = { ",
|
||
"assert(main.value() == 41); };\n"));
|
||
writefile(strings.concat(importer, "/importer.ww"), strings.concat(
|
||
"package importer;\n",
|
||
"import ", commandid, ";\n",
|
||
"export fn value() i32 = { return main.value(); };\n"));
|
||
writeexecutable(wrapper, strings.concat(
|
||
"#!/bin/sh\n",
|
||
"printf 'BEGIN' >> \"$WW_COMMAND_COMPILER_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||
"\"$WW_COMMAND_COMPILER_TRACE\"; done\n",
|
||
"printf '\\n' >> \"$WW_COMMAND_COMPILER_TRACE\"\n",
|
||
"exec \"$WW_COMMAND_REAL_COMPILER\" \"$@\"\n"));
|
||
|
||
let stages: []str = ["ww", "ww_ww"];
|
||
let compilers: []str = ["w6c", "w6c_ww"];
|
||
let internalid: str = commandid;
|
||
let externalid: str = strings.concat(commandid, "_test");
|
||
let mainid: str = strings.concat("__wwtestmain.", commandid, ".main");
|
||
let actionids: []str = [internalid, externalid, mainid];
|
||
let ordinaryartifact: str = packagestoragekey(commandid, target, 0, 0);
|
||
let artifacts: []str = [packagestoragekey(internalid, target, 1, 0),
|
||
packagestoragekey(externalid, target, 2, 0),
|
||
packagestoragekey(mainid,
|
||
strings.concat(target, "#directory-test-main"), 3, 2)];
|
||
let suffixes: []str = [".unit.ww", ".wwi", ".a"];
|
||
let reference: []str = ["", "", "", "", "", "", "", "", ""];
|
||
let baseenv: []str = os.getenvs();
|
||
let si: i32 = 0;
|
||
for (si < stages.len) {
|
||
let workroot: str = strings.concat(root, "/command-work-", stages[si]);
|
||
assert(os.mkdir(workroot, 448i32) == 0);
|
||
let work: str = strings.concat(workroot, "/");
|
||
let trace: str = strings.concat(root, "/", stages[si], ".trace");
|
||
writefile(trace, "");
|
||
let env: []str = alloc([], (baseenv.len + 3): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < baseenv.len) {
|
||
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_COMMAND_COMPILER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_COMMAND_REAL_COMPILER=")) {
|
||
append(env, baseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(env, strings.concat("WW_W6C=", wrapper));
|
||
append(env, strings.concat("WW_COMMAND_COMPILER_TRACE=", trace));
|
||
append(env, strings.concat("WW_COMMAND_REAL_COMPILER=",
|
||
driver(compilers[si])));
|
||
let av: []str = [driver(stages[si]), "test", "-w", workroot,
|
||
"-I", source, target];
|
||
let out: commandout;
|
||
runcommandenv(root, strings.concat("command-variants-", stages[si]), av,
|
||
env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stderr.len == 0);
|
||
assert(has(out.stdout, "main.command_internal ... ok\n"));
|
||
assert(has(out.stdout, "main.command_external ... ok\n"));
|
||
|
||
let ctrace: str = readfile(trace);
|
||
let ai: i32 = 0;
|
||
for (ai < artifacts.len) {
|
||
let line: str = linecontaining(ctrace,
|
||
strings.concat(artifacts[ai], ".unit.new"));
|
||
if (ai < 2) { assert(has(line, "<--command-package>")); }
|
||
else { assert(has(line, "<-T><--entry>")); };
|
||
let sj: i32 = 0;
|
||
for (sj < suffixes.len) {
|
||
let body: str = readfile(strings.concat(work, artifacts[ai],
|
||
suffixes[sj]));
|
||
if (sj == 0) {
|
||
assert(has(body, strings.concat("//ww:module-reset ",
|
||
actionids[ai], "\n")));
|
||
} else { if (sj == 1) {
|
||
assert(strings.hasprefix(body, strings.concat("//ww:module ",
|
||
actionids[ai], "\n")));
|
||
}; };
|
||
let ri: i32 = ai * suffixes.len + sj;
|
||
if (si == 0) { reference[ri] = strings.dup(body); }
|
||
else { assert(same(reference[ri], body)); };
|
||
sj += 1;
|
||
};
|
||
ai += 1;
|
||
};
|
||
let externalline: str = linecontaining(ctrace,
|
||
strings.concat(artifacts[1], ".unit.new"));
|
||
assert(has(externalline, strings.concat("<--import><", commandid,
|
||
"><", work, artifacts[0], ".wwi.new>")));
|
||
let mainline: str = linecontaining(ctrace,
|
||
strings.concat(artifacts[2], ".unit.new"));
|
||
assert(has(mainline, strings.concat("<--import><", internalid,
|
||
"><", work, artifacts[0], ".wwi.new>")));
|
||
assert(has(mainline, strings.concat("<--import><", externalid,
|
||
"><", work, artifacts[1], ".wwi.new>")));
|
||
assert(has(mainline, strings.concat(
|
||
"<--test-target-package><", internalid, ">")));
|
||
assert(has(mainline, strings.concat(
|
||
"<--test-target-package><", externalid, ">")));
|
||
assert(pos(mainline, strings.concat("<--test-target-package><",
|
||
internalid, ">")) < pos(mainline, strings.concat(
|
||
"<--test-target-package><", externalid, ">")));
|
||
assert(!os.exists(strings.concat(work, ordinaryartifact, ".unit.ww")));
|
||
assert(has(readfile(strings.concat(work, artifacts[0], ".s")),
|
||
strings.concat(commandid, ".value")));
|
||
assert(has(readfile(strings.concat(work, artifacts[0], ".s")),
|
||
strings.concat(commandid, ".command_internal")));
|
||
assert(has(readfile(strings.concat(work, artifacts[1], ".s")),
|
||
strings.concat(externalid, ".command_external")));
|
||
let warmrefs: []str = alloc([], reference.len: u64)!;
|
||
warmrefs.len = reference.len;
|
||
ai = 0;
|
||
for (ai < artifacts.len) {
|
||
let sj: i32 = 0;
|
||
for (sj < suffixes.len) {
|
||
warmrefs[ai * suffixes.len + sj] = strings.dup(readfile(
|
||
strings.concat(work, artifacts[ai], suffixes[sj])));
|
||
sj += 1;
|
||
};
|
||
ai += 1;
|
||
};
|
||
runcommandenv(root, strings.concat("command-warm-", stages[si]), av,
|
||
env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout, "main.command_internal ... ok\n"));
|
||
assert(has(out.stdout, "main.command_external ... ok\n"));
|
||
assert(same(ctrace, readfile(trace)));
|
||
ai = 0;
|
||
for (ai < artifacts.len) {
|
||
let sj: i32 = 0;
|
||
for (sj < suffixes.len) {
|
||
assert(same(warmrefs[ai * suffixes.len + sj], readfile(
|
||
strings.concat(work, artifacts[ai], suffixes[sj]))));
|
||
sj += 1;
|
||
};
|
||
ai += 1;
|
||
};
|
||
|
||
// The same command action is not generally source-importable. It is
|
||
// classified from its declaration so this Go-like diagnostic is stable,
|
||
// and graph failure occurs before any compiler invocation.
|
||
clean(trace);
|
||
writefile(trace, "");
|
||
let importout: str = strings.concat(root, "/importer-", stages[si], ".a");
|
||
let importav: []str = [driver(stages[si]), "build", "-I", source,
|
||
"-o", importout, importer];
|
||
runcommandenv(root, strings.concat("command-import-", stages[si]),
|
||
importav, env, (60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, strings.concat("package ", commandid,
|
||
" is a program, not an importable package\n")));
|
||
assert(readfile(trace).len == 0);
|
||
|
||
clean(workroot);
|
||
si += 1;
|
||
};
|
||
clean(root);
|
||
};
|
||
|
||
@test fn package_graph_diagnostics_are_stable() void = {
|
||
let root: str = fresh();
|
||
let missing: str = strings.concat(root, "/missing");
|
||
let cycle: str = strings.concat(root, "/cycle");
|
||
let left: str = strings.concat(root, "/left");
|
||
let right: str = strings.concat(root, "/right");
|
||
let conflict: str = strings.concat(root, "/conflict");
|
||
let privatepkg: str = strings.concat(root, "/privatepkg");
|
||
let identityroot: str = strings.concat(root, "/identityroot");
|
||
let identityactual: str = strings.concat(root, "/identityactual");
|
||
let identityalias: str = strings.concat(root, "/identityalias");
|
||
let reserved: str = strings.concat(root, "/reservedprobe");
|
||
let foldsource: str = strings.concat(root, "/foldsource");
|
||
let foldupper: str = strings.concat(foldsource, "/domain/Foo");
|
||
let foldlower: str = strings.concat(foldsource, "/domain/foo");
|
||
let folddirect: str = strings.concat(foldsource, "/cmd/direct");
|
||
let foldbackward: str = strings.concat(foldsource, "/cmd/backward");
|
||
let foldindirect: str = strings.concat(foldsource, "/cmd/indirect");
|
||
let foldleft: str = strings.concat(foldsource, "/parent/left");
|
||
let foldright: str = strings.concat(foldsource, "/parent/right");
|
||
let foldrepeat: str = strings.concat(foldsource, "/cmd/repeat");
|
||
let foldaliasroot: str = strings.concat(foldsource, "/aliasroot");
|
||
let foldaliasactual: str = strings.concat(foldsource, "/alias/Foo");
|
||
let foldalias: str = strings.concat(foldsource, "/alias/foo");
|
||
let foldrootleft: str = strings.concat(foldsource, "/roots/left");
|
||
let foldrootright: str = strings.concat(foldsource, "/roots/right");
|
||
let foldrecursive: str = strings.concat(root, "/foldrecursive");
|
||
let foldrecursiveupper: str = strings.concat(foldrecursive, "/domain/Foo");
|
||
let foldrecursivelower: str = strings.concat(foldrecursive, "/domain/foo");
|
||
assert(os.mkdir(missing, 448i32) == 0);
|
||
assert(os.mkdir(cycle, 448i32) == 0);
|
||
assert(os.mkdir(left, 448i32) == 0);
|
||
assert(os.mkdir(right, 448i32) == 0);
|
||
assert(os.mkdir(conflict, 448i32) == 0);
|
||
assert(os.mkdir(privatepkg, 448i32) == 0);
|
||
assert(os.mkdir(identityroot, 448i32) == 0);
|
||
assert(os.mkdir(identityactual, 448i32) == 0);
|
||
assert(os.mkdir(reserved, 448i32) == 0);
|
||
mkdirall(foldupper); mkdirall(foldlower); mkdirall(folddirect);
|
||
mkdirall(foldbackward); mkdirall(foldindirect); mkdirall(foldleft);
|
||
mkdirall(foldright); mkdirall(foldrepeat); mkdirall(foldaliasroot);
|
||
mkdirall(foldaliasactual); mkdirall(foldrootleft); mkdirall(foldrootright);
|
||
mkdirall(foldrecursiveupper); mkdirall(foldrecursivelower);
|
||
writefile(strings.concat(missing, "/missing.ww"),
|
||
"package missing;\nfn local() void = { };\n");
|
||
writefile(strings.concat(missing, "/missing_test.ww"), strings.concat(
|
||
"package missing;\nimport nowhere;\n",
|
||
"@test fn missing_import() void = { };\n"));
|
||
writefile(strings.concat(cycle, "/cycle.ww"),
|
||
"package cycle;\nfn local() void = { };\n");
|
||
writefile(strings.concat(cycle, "/cycle_test.ww"), strings.concat(
|
||
"package cycle;\nimport left;\n",
|
||
"@test fn cycle_import() void = { };\n"));
|
||
writefile(strings.concat(left, "/left.ww"),
|
||
"package left;\nimport right;\nexport fn v() i32 = { return right.v(); };\n");
|
||
writefile(strings.concat(right, "/right.ww"),
|
||
"package right;\nimport left;\nexport fn v() i32 = { return left.v(); };\n");
|
||
writefile(strings.concat(conflict, "/a.ww"),
|
||
"package conflict;\nfn a() void = { };\n");
|
||
writefile(strings.concat(conflict, "/z.ww"),
|
||
"package other;\nfn z() void = { };\n");
|
||
writefile(strings.concat(conflict, "/conflict_test.ww"),
|
||
"package conflict;\n@test fn conflict_name() void = { };\n");
|
||
writefile(strings.concat(privatepkg, "/a.ww"), strings.concat(
|
||
"package privatepkg;\n",
|
||
"export fn shown() i32 = { return 1; };\n"));
|
||
writefile(strings.concat(privatepkg, "/z.ww"),
|
||
"package privatepkg;\nfn hidden() i32 = { return 2; };\n");
|
||
writefile(strings.concat(privatepkg, "/external_test.ww"), strings.concat(
|
||
"package privatepkg_test;\nimport privatepkg;\n",
|
||
"@test fn private_use() void = { assert(privatepkg.hidden() == 2); };\n"));
|
||
writefile(strings.concat(identityactual, "/identityactual.ww"),
|
||
"package identityactual;\nexport fn value() i32 = { return 1; };\n");
|
||
let linkout: commandout;
|
||
let linkav: []str = ["/bin/ln", "-s", identityactual, identityalias];
|
||
runcommand(root, "identity-alias", linkav, time.second, &linkout);
|
||
expectexit(&linkout, 0);
|
||
writefile(strings.concat(identityroot, "/identityroot.ww"), strings.concat(
|
||
"package identityroot;\nimport identityactual;\n",
|
||
"import identityalias;\nexport fn value() i32 = { return 1; };\n"));
|
||
writefile(strings.concat(reserved, "/reservedprobe.ww"), strings.concat(
|
||
"package reservedprobe;\nimport __wwlocal.fake;\n",
|
||
"export fn value() i32 = { return 1; };\n"));
|
||
writefile(strings.concat(foldupper, "/pkg.ww"),
|
||
"package upperdecl;\nexport fn value() i32 = { return 1; };\n");
|
||
writefile(strings.concat(foldlower, "/pkg.ww"),
|
||
"package lowerdecl;\nexport fn value() i32 = { return 2; };\n");
|
||
// File creation and import order deliberately disagree with byte order.
|
||
writefile(strings.concat(folddirect, "/z.ww"), strings.concat(
|
||
"package main;\nimport Lower domain.foo;\n",
|
||
"fn main() i32 = { return 0; };\n"));
|
||
writefile(strings.concat(folddirect, "/a.ww"),
|
||
"package main;\nimport Upper domain.Foo;\n");
|
||
writefile(strings.concat(foldbackward, "/a.ww"),
|
||
"package main;\nimport Lower domain.foo;\n");
|
||
writefile(strings.concat(foldbackward, "/z.ww"), strings.concat(
|
||
"package main;\nimport Upper domain.Foo;\n",
|
||
"fn main() i32 = { return 0; };\n"));
|
||
writefile(strings.concat(foldleft, "/pkg.ww"), strings.concat(
|
||
"package left;\nimport _ domain.Foo;\n",
|
||
"export fn value() i32 = { return 1; };\n"));
|
||
writefile(strings.concat(foldright, "/pkg.ww"), strings.concat(
|
||
"package right;\nimport _ domain.foo;\n",
|
||
"export fn value() i32 = { return 2; };\n"));
|
||
writefile(strings.concat(foldindirect, "/main.ww"), strings.concat(
|
||
"package main;\nimport _ parent.left;\nimport _ parent.right;\n",
|
||
"fn main() i32 = { return 0; };\n"));
|
||
writefile(strings.concat(foldrepeat, "/a.ww"),
|
||
"package main;\nimport _ domain.Foo;\n");
|
||
writefile(strings.concat(foldrepeat, "/z.ww"), strings.concat(
|
||
"package main;\nimport _ domain.Foo;\n",
|
||
"fn main() i32 = { return 0; };\n"));
|
||
writefile(strings.concat(foldaliasactual, "/pkg.ww"),
|
||
"package aliasdecl;\nexport fn value() i32 = { return 1; };\n");
|
||
assert(os.symlink(foldaliasactual, foldalias) == 0);
|
||
writefile(strings.concat(foldaliasroot, "/main.ww"), strings.concat(
|
||
"package main;\nimport _ alias.Foo;\nimport _ alias.foo;\n",
|
||
"fn main() i32 = { return 0; };\n"));
|
||
writefile(strings.concat(foldrootleft, "/pkg.ww"),
|
||
"package leftroot;\nimport _ domain.Foo;\n");
|
||
writefile(strings.concat(foldrootright, "/pkg.ww"),
|
||
"package rightroot;\nimport _ domain.foo;\n");
|
||
writefile(strings.concat(foldrecursiveupper, "/pkg.ww"),
|
||
"package upper;\nexport fn value() i32 = { return 1; };\n");
|
||
writefile(strings.concat(foldrecursivelower, "/pkg.ww"),
|
||
"package lower;\nexport fn value() i32 = { return 2; };\n");
|
||
rejectpackagestable(root, "missing", missing,
|
||
"cannot find package nowhere");
|
||
rejectpackagestable(root, "cycle", cycle, "ww: dependency cycle:");
|
||
rejectpackagestable(root, "conflict", conflict,
|
||
"conflicting package names conflict and other");
|
||
rejectpackagestable(root, "private", privatepkg,
|
||
"package 'privatepkg' has no exported declaration 'hidden'");
|
||
let stages: []str = ["ww", "ww_ww"];
|
||
let tooltrace: str = strings.concat(root, "/identity-tool.trace");
|
||
let compilerwrapper: str = strings.concat(root, "/identity-w6c.sh");
|
||
let assemblerwrapper: str = strings.concat(root, "/identity-w6a.sh");
|
||
let linkerwrapper: str = strings.concat(root, "/identity-w6l.sh");
|
||
writefile(tooltrace, "");
|
||
writeexecutable(compilerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf 'compile' >> \"$WW_IDENTITY_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||
"\"$WW_IDENTITY_TRACE\"; done\nprintf '\\n' >> ",
|
||
"\"$WW_IDENTITY_TRACE\"\n",
|
||
"exec \"$WW_IDENTITY_W6C\" \"$@\"\n"));
|
||
writeexecutable(assemblerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf 'assemble' >> \"$WW_IDENTITY_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||
"\"$WW_IDENTITY_TRACE\"; done\nprintf '\\n' >> ",
|
||
"\"$WW_IDENTITY_TRACE\"\n",
|
||
"exec \"$WW_IDENTITY_W6A\" \"$@\"\n"));
|
||
writeexecutable(linkerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf 'link' >> \"$WW_IDENTITY_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||
"\"$WW_IDENTITY_TRACE\"; done\nprintf '\\n' >> ",
|
||
"\"$WW_IDENTITY_TRACE\"\n",
|
||
"exec \"$WW_IDENTITY_W6L\" \"$@\"\n"));
|
||
let baseenv: []str = os.getenvs();
|
||
let traceenv: []str = alloc([], (baseenv.len + 4): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < baseenv.len) {
|
||
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6A=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6L=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_IDENTITY_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_IDENTITY_W6C=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_IDENTITY_W6A=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_IDENTITY_W6L=")) {
|
||
append(traceenv, baseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(traceenv, strings.concat("WW_W6C=", compilerwrapper));
|
||
append(traceenv, strings.concat("WW_W6A=", assemblerwrapper));
|
||
append(traceenv, strings.concat("WW_W6L=", linkerwrapper));
|
||
append(traceenv, strings.concat("WW_IDENTITY_TRACE=", tooltrace));
|
||
let out: commandout;
|
||
let i: i32 = 0;
|
||
let identitydiagnostics: []str = ["", ""];
|
||
i = 0;
|
||
for (i < stages.len) {
|
||
let av: []str = [driver(stages[i]), "build", "-I", root,
|
||
"-o", strings.concat(root, "/bad-identity-", stages[i], ".a"),
|
||
identityroot];
|
||
runcommandenv(root, strings.concat("duplicate-directory-identity-",
|
||
stages[i]), av, traceenv,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
assert(out.termination == exec.termination.EXIT && out.code != 0);
|
||
assert(has(out.stderr, strings.concat("ww: package directory ",
|
||
identityactual, " has import identities identityactual and ",
|
||
"identityalias\n")));
|
||
identitydiagnostics[i] = strings.dup(out.stderr);
|
||
i += 1;
|
||
};
|
||
assert(same(identitydiagnostics[0], identitydiagnostics[1]));
|
||
// A canonical directory acquiring two import identities rejects before
|
||
// compiler or linker invocation. The converse is covered by the shared
|
||
// multi-context resolution test above.
|
||
assert(readfile(tooltrace).len == 0);
|
||
|
||
let foldexpected: str =
|
||
"ww: case-insensitive import collision: \"domain.Foo\" and \"domain.foo\"\n";
|
||
let aliasexpected: str =
|
||
"ww: case-insensitive import collision: \"alias.Foo\" and \"alias.foo\"\n";
|
||
let foldtargets: []str = [folddirect, foldbackward, foldindirect,
|
||
foldaliasroot];
|
||
let foldlabels: []str = ["direct", "backward", "indirect", "one-dir"];
|
||
let foldwant: []str = [foldexpected, foldexpected, foldexpected,
|
||
aliasexpected];
|
||
let compilers: []str = ["w6c", "w6c_ww"];
|
||
let assemblers: []str = ["w6a", "w6a_ww"];
|
||
let linkers: []str = ["w6l", "w6l_ww"];
|
||
let foldrefs: []str = ["", "", "", "", "", "", ""];
|
||
let repeatref: str = "";
|
||
i = 0;
|
||
for (i < stages.len) {
|
||
let foldenv: []str = alloc([], (traceenv.len + 3): u64)!;
|
||
let tei: i32 = 0;
|
||
for (tei < traceenv.len) {
|
||
append(foldenv, traceenv[tei]); tei += 1;
|
||
};
|
||
append(foldenv, strings.concat("WW_IDENTITY_W6C=",
|
||
driver(compilers[i])));
|
||
append(foldenv, strings.concat("WW_IDENTITY_W6A=",
|
||
driver(assemblers[i])));
|
||
append(foldenv, strings.concat("WW_IDENTITY_W6L=",
|
||
driver(linkers[i])));
|
||
let fi: i32 = 0;
|
||
for (fi < foldtargets.len) {
|
||
rewritefile(tooltrace, "");
|
||
let work: str = strings.concat(root, "/fold-", foldlabels[fi],
|
||
"-", stages[i], "-work");
|
||
assert(os.mkdir(work, 448i32) == 0);
|
||
let output: str = strings.concat(root, "/fold-", foldlabels[fi],
|
||
"-", stages[i]);
|
||
let av: []str = [driver(stages[i]), "build", "-w", work,
|
||
"-I", foldsource, "-o", output, foldtargets[fi]];
|
||
runcommandenv(root, strings.concat("case-fold-", foldlabels[fi],
|
||
"-", stages[i]), av, foldenv,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(out.stdout.len == 0 && same(out.stderr, foldwant[fi]));
|
||
assert(readfile(tooltrace).len == 0);
|
||
assert(!os.exists(output));
|
||
assert(!os.exists(strings.concat(output, ".new")));
|
||
assert(!os.exists(strings.concat(output, ".sepwork")));
|
||
assert(!os.exists(strings.concat(work, "/.wwtool.stamp")));
|
||
assert(directoryisempty(work));
|
||
if (i == 0) { foldrefs[fi] = strings.dup(out.stderr); }
|
||
else { assert(same(foldrefs[fi], out.stderr)); };
|
||
fi += 1;
|
||
};
|
||
|
||
// The coordinator canonicalizes product order, so reversing roots cannot
|
||
// choose a different representative or diagnostic.
|
||
let rootfirst: []str = [foldrootleft, foldrootright];
|
||
let rootsecond: []str = [foldrootright, foldrootleft];
|
||
let oi: i32 = 0;
|
||
for (oi < rootfirst.len) {
|
||
rewritefile(tooltrace, "");
|
||
let work: str = strings.concat(root, "/fold-roots-", stages[i],
|
||
"-", foldlabels[oi], "-work");
|
||
assert(os.mkdir(work, 448i32) == 0);
|
||
let av: []str = [driver(stages[i]), "build", "-w", work,
|
||
"-I", foldsource, rootfirst[oi], rootsecond[oi]];
|
||
runcommandenv(root, strings.concat("case-fold-roots-", stages[i],
|
||
"-", foldlabels[oi]), av, foldenv,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(out.stdout.len == 0 && has(out.stderr, foldexpected));
|
||
assert(readfile(tooltrace).len == 0);
|
||
assert(!os.exists(strings.concat(work, "/.wwtool.stamp")));
|
||
assert(directoryisempty(work));
|
||
let refindex: i32 = foldtargets.len + oi;
|
||
if (i == 0) { foldrefs[refindex] = strings.dup(out.stderr); }
|
||
else { assert(same(foldrefs[refindex], out.stderr)); };
|
||
oi += 1;
|
||
};
|
||
assert(same(foldrefs[4], foldrefs[5]));
|
||
|
||
rewritefile(tooltrace, "");
|
||
let recursivework: str = strings.concat(root, "/fold-recursive-",
|
||
stages[i], "-work");
|
||
assert(os.mkdir(recursivework, 448i32) == 0);
|
||
let recursiveav: []str = [driver(stages[i]), "build", "-w",
|
||
recursivework, "-I", foldrecursive,
|
||
strings.concat(foldrecursive, "/...")];
|
||
runcommandenv(root, strings.concat("case-fold-recursive-", stages[i]),
|
||
recursiveav, foldenv,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(out.stdout.len == 0 && has(out.stderr, foldexpected));
|
||
assert(readfile(tooltrace).len == 0);
|
||
assert(!os.exists(strings.concat(recursivework, "/.wwtool.stamp")));
|
||
assert(directoryisempty(recursivework));
|
||
if (i == 0) { foldrefs[6] = strings.dup(out.stderr); }
|
||
else { assert(same(foldrefs[6], out.stderr)); };
|
||
|
||
// Exact repeated occurrences retain one canonical action and accepted
|
||
// compiler input, rather than being merged by the folded key.
|
||
rewritefile(tooltrace, "");
|
||
let repeatwork: str = strings.concat(root, "/fold-repeat-", stages[i],
|
||
"-work");
|
||
let repeatout: str = strings.concat(root, "/fold-repeat-", stages[i]);
|
||
assert(os.mkdir(repeatwork, 448i32) == 0);
|
||
let repeatav: []str = [driver(stages[i]), "build", "-w", repeatwork,
|
||
"-I", foldsource, "-o", repeatout, foldrepeat];
|
||
runcommandenv(root, strings.concat("case-fold-repeat-", stages[i]),
|
||
repeatav, foldenv,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
let repeattrace: str = readfile(tooltrace);
|
||
assert(occurrences(repeattrace,
|
||
"/domain.Foo.unit.new>") == 1);
|
||
let repeatline: str = linecontaining(repeattrace,
|
||
"/cmd.repeat.unit.new>");
|
||
assert(occurrences(repeatline, "<--import><domain.Foo>") == 1);
|
||
assert(occurrences(repeatline, "/domain.Foo.wwi.new>") == 1);
|
||
assert(os.exists(strings.concat(repeatwork, "/.wwtool.stamp")));
|
||
let repeatpaths: []str = ["/.wwtool.ww", "/.wwtool.w6c",
|
||
"/.wwtool.w6a", "/.wwtool.stamp", "/cmd.repeat.unit.ww",
|
||
"/cmd.repeat.wwi", "/cmd.repeat.s", "/cmd.repeat.o",
|
||
"/cmd.repeat.a", "/cmd.repeat.init.unit.ww",
|
||
"/cmd.repeat.init.s", "/cmd.repeat.init.o",
|
||
"/domain.Foo.unit.ww", "/domain.Foo.wwi", "/domain.Foo.s",
|
||
"/domain.Foo.o", "/domain.Foo.a"];
|
||
let repeatbytes: []str = alloc([], repeatpaths.len: u64)!;
|
||
let rsi: i32 = 0;
|
||
for (rsi < repeatpaths.len) {
|
||
append(repeatbytes, strings.dup(readfile(strings.concat(repeatwork,
|
||
repeatpaths[rsi]))));
|
||
rsi += 1;
|
||
};
|
||
let repeatoutput: str = strings.dup(readfile(repeatout));
|
||
if (i == 0) { repeatref = strings.dup(repeatoutput); }
|
||
else { assert(same(repeatref, repeatoutput)); };
|
||
let repeatcollision: str = strings.concat(foldrepeat, "/m.ww");
|
||
writefile(repeatcollision,
|
||
"package main;\nimport _ domain.foo;\n");
|
||
rewritefile(tooltrace, "");
|
||
runcommandenv(root, strings.concat("case-fold-repeat-added-", stages[i]),
|
||
repeatav, foldenv,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(out.stdout.len == 0 && same(out.stderr, foldexpected));
|
||
assert(readfile(tooltrace).len == 0);
|
||
rsi = 0;
|
||
for (rsi < repeatpaths.len) {
|
||
assert(same(repeatbytes[rsi], readfile(strings.concat(repeatwork,
|
||
repeatpaths[rsi]))));
|
||
rsi += 1;
|
||
};
|
||
assert(!directoryhasnew(repeatwork));
|
||
assert(same(repeatoutput, readfile(repeatout)));
|
||
assert(!os.exists(strings.concat(repeatout, ".new")));
|
||
assert(!os.exists(strings.concat(repeatout, ".sepwork")));
|
||
assert(!os.exists(strings.concat(repeatwork, "/domain.foo.unit.ww")));
|
||
assert(!os.exists(strings.concat(repeatwork, "/domain.foo.wwi")));
|
||
assert(!os.exists(strings.concat(repeatwork, "/domain.foo.a")));
|
||
assert(os.remove(repeatcollision) == 0);
|
||
rewritefile(tooltrace, "");
|
||
runcommandenv(root, strings.concat("case-fold-repeat-removed-", stages[i]),
|
||
repeatav, foldenv,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
assert(occurrences(readfile(tooltrace), "compile") == 0);
|
||
assert(occurrences(readfile(tooltrace), "assemble") == 0);
|
||
assert(occurrences(readfile(tooltrace), "link") == 1);
|
||
rsi = 0;
|
||
for (rsi < repeatpaths.len) {
|
||
assert(same(repeatbytes[rsi], readfile(strings.concat(repeatwork,
|
||
repeatpaths[rsi]))));
|
||
rsi += 1;
|
||
};
|
||
assert(!directoryhasnew(repeatwork));
|
||
assert(same(repeatoutput, readfile(repeatout)));
|
||
assert(!os.exists(strings.concat(repeatout, ".new")));
|
||
assert(!os.exists(strings.concat(repeatout, ".sepwork")));
|
||
i += 1;
|
||
};
|
||
rewritefile(tooltrace, "");
|
||
let reserveddiagnostics: []str = ["", ""];
|
||
i = 0;
|
||
for (i < stages.len) {
|
||
let av: []str = [driver(stages[i]), "build", "-I", root,
|
||
"-o", strings.concat(root, "/bad-reserved-", stages[i], ".a"),
|
||
reserved];
|
||
runcommandenv(root, strings.concat("reserved-local-import-", stages[i]),
|
||
av, traceenv, (60i64 * (time.second: i64)): time.duration, &out);
|
||
assert(out.termination == exec.termination.EXIT && out.code != 0);
|
||
assert(has(out.stderr, "package path __wwlocal.fake is reserved"));
|
||
reserveddiagnostics[i] = strings.dup(primarydiagnostic(out.stderr));
|
||
i += 1;
|
||
};
|
||
assert(same(reserveddiagnostics[0], reserveddiagnostics[1]));
|
||
assert(readfile(tooltrace).len == 0);
|
||
clean(root);
|
||
};
|
||
|
||
@test fn platform_filename_source_selection() void = {
|
||
let root: str = fresh();
|
||
let source: str = strings.concat(root, "/source");
|
||
let app: str = strings.concat(source, "/app");
|
||
let good: str = strings.concat(source, "/dep/good");
|
||
let pkg: str = strings.concat(source, "/pkg");
|
||
let testgood: str = strings.concat(source, "/test/good");
|
||
let wrongonly: str = strings.concat(source, "/wrongonly");
|
||
let notests: str = strings.concat(source, "/notests");
|
||
let internal: str = strings.concat(source, "/foreign/internal/secret");
|
||
let command: str = strings.concat(source, "/command");
|
||
let vendored: str = strings.concat(app, "/vendor/vendored");
|
||
let order: str = strings.concat(root, "/order");
|
||
let tools: str = strings.concat(root, "/tools");
|
||
let foldfiles: str = strings.concat(root, "/foldfiles");
|
||
let foldunicode: str = strings.concat(foldfiles, "/unicode");
|
||
let foldinvalid: str = strings.concat(foldfiles, "/invalid");
|
||
let foldnonormal: str = strings.concat(foldfiles, "/nonormal");
|
||
let foldinternal: str = strings.concat(foldfiles, "/internal");
|
||
let foldexternal: str = strings.concat(foldfiles, "/external");
|
||
let foldsameonly: str = strings.concat(foldfiles, "/sameonly");
|
||
let foldexternalonly: str = strings.concat(foldfiles, "/externalonly");
|
||
let foldmixed: str = strings.concat(foldfiles, "/mixed");
|
||
let foldcross: str = strings.concat(foldfiles, "/cross");
|
||
let foldcreationroot: str = strings.concat(root, "/fold-creation-order");
|
||
let foldcreation: str = strings.concat(foldcreationroot, "/prod");
|
||
mkdirall(app); mkdirall(good); mkdirall(pkg); mkdirall(testgood);
|
||
mkdirall(wrongonly); mkdirall(notests); mkdirall(internal);
|
||
mkdirall(command); mkdirall(vendored); mkdirall(order); mkdirall(tools);
|
||
mkdirall(foldunicode); mkdirall(foldinvalid); mkdirall(foldnonormal);
|
||
mkdirall(foldinternal); mkdirall(foldexternal); mkdirall(foldsameonly);
|
||
mkdirall(foldexternalonly); mkdirall(foldmixed); mkdirall(foldcross);
|
||
mkdirall(foldcreation);
|
||
|
||
let good5: str = strings.concat("package good;\n",
|
||
"export fn value() i32 = { return 5; };\n");
|
||
let good6: str = strings.concat("package good;\n",
|
||
"export fn value() i32 = { return 6; };\n");
|
||
let good7: str = strings.concat("package good;\n",
|
||
"export fn value() i32 = { return 7; };\n");
|
||
let appmain: str = strings.concat(
|
||
"package main;\nimport dep.good;\n",
|
||
"fn main() i32 = { return good.value() + wholelinux() + ",
|
||
"afterdot() + arch() + reversed() + future() + osname() + pair(); };\n");
|
||
let wholelinux: str =
|
||
"package main;\nfn wholelinux() i32 = { return 1; };\n";
|
||
let afterdot: str =
|
||
"package main;\nfn afterdot() i32 = { return 1; };\n";
|
||
let arch: str =
|
||
"package main;\nfn arch() i32 = { return 1; };\n";
|
||
let reversed: str =
|
||
"package main;\nfn reversed() i32 = { return 1; };\n";
|
||
let future: str =
|
||
"package main;\nfn future() i32 = { return 1; };\n";
|
||
let osname: str = strings.concat(
|
||
"package main;\nimport _ dep.good;\n",
|
||
"fn osname() i32 = { return 1; };\n");
|
||
let pair: str =
|
||
"package main;\nfn pair() i32 = { return 1; };\n";
|
||
let ignoredparse: str =
|
||
"this wrong-target source is deliberately malformed\n";
|
||
let ignoredparseedit: str =
|
||
"a different malformed wrong-target edit {\n";
|
||
let ignoreddep: str = strings.concat(
|
||
"package main;\nimport dep.wrong;\n",
|
||
"fn wrong_dep() i32 = { return wrong.value(); };\n");
|
||
let ignoredinternal: str = strings.concat(
|
||
"package main;\nimport foreign.internal.secret;\n",
|
||
"fn wrong_internal() i32 = { return secret.value(); };\n");
|
||
let ignoredmain: str = strings.concat(
|
||
"package main;\nimport command;\n",
|
||
"fn wrong_main() i32 = { return 0; };\n");
|
||
let ignoredvendor: str = strings.concat(
|
||
"package main;\nimport vendored;\n",
|
||
"fn wrong_vendor() i32 = { return vendored.value(); };\n");
|
||
writefile(strings.concat(good, "/good.ww"), good5);
|
||
writefile(strings.concat(app, "/main.ww"), appmain);
|
||
writefile(strings.concat(app, "/linux.ww"), wholelinux);
|
||
writefile(strings.concat(app, "/platform.extra_windows.ww"), afterdot);
|
||
writefile(strings.concat(app, "/platform_amd64.ww"), arch);
|
||
writefile(strings.concat(app, "/platform_amd64_linux.ww"), reversed);
|
||
writefile(strings.concat(app, "/platform_future.ww"), future);
|
||
writefile(strings.concat(app, "/platform_linux.ww"), osname);
|
||
writefile(strings.concat(app, "/platform_linux_amd64.ww"), pair);
|
||
writefile(strings.concat(app, "/badparse_windows.ww"), ignoredparse);
|
||
writefile(strings.concat(app, "/baddep_arm64.ww"), ignoreddep);
|
||
writefile(strings.concat(app, "/badinternal_windows_amd64.ww"),
|
||
ignoredinternal);
|
||
writefile(strings.concat(app, "/badmain_linux_arm64.ww"), ignoredmain);
|
||
writefile(strings.concat(app, "/badvendor_windows.ww"), ignoredvendor);
|
||
writefile(strings.concat(app, "/.Fold.ww"), ignoredparse);
|
||
writefile(strings.concat(app, "/.fold.ww"), ignoredparseedit);
|
||
writefile(strings.concat(app, "/_Fold.ww"), ignoredparse);
|
||
writefile(strings.concat(app, "/_fold.ww"), ignoredparseedit);
|
||
writefile(strings.concat(app, "/Fold_windows.ww"), ignoredparse);
|
||
writefile(strings.concat(app, "/fold_windows.ww"), ignoredparseedit);
|
||
writefile(strings.concat(internal, "/secret.ww"), strings.concat(
|
||
"package secret;\n",
|
||
"export fn value() i32 = { return 19; };\n"));
|
||
writefile(strings.concat(command, "/main.ww"),
|
||
"package main;\nfn main() i32 = { return 0; };\n");
|
||
writefile(strings.concat(vendored, "/vendored.ww"), strings.concat(
|
||
"package vendored;\n",
|
||
"export fn value() i32 = { return 23; };\n"));
|
||
|
||
let pkgbase: str = strings.concat("package pkg;\n",
|
||
"export fn value() i32 = { return 7; };\n");
|
||
let samepair: str = strings.concat(
|
||
"package pkg;\nimport test.good;\n",
|
||
"@test fn selected_internal_pair() void = { ",
|
||
"assert(value() + good.value() == 18); };\n");
|
||
let samewhole: str = strings.concat(
|
||
"package pkg;\n",
|
||
"@test fn selected_whole_plan9_name() void = { assert(value() == 7); };\n");
|
||
let samereversed: str = strings.concat(
|
||
"package pkg;\n",
|
||
"@test fn selected_reversed_suffix() void = { assert(value() == 7); };\n");
|
||
let externalpair: str = strings.concat(
|
||
"package pkg_test;\nimport pkg;\nimport test.good;\n",
|
||
"@test fn selected_external_pair() void = { ",
|
||
"assert(pkg.value() + good.value() == 18); };\n");
|
||
let externaldot: str = strings.concat(
|
||
"package pkg_test;\nimport pkg;\n",
|
||
"@test fn selected_external_after_dot() void = { ",
|
||
"assert(pkg.value() == 7); };\n");
|
||
let wrongtest: str = strings.concat(
|
||
"package pkg;\nimport test.wrong;\n",
|
||
"@test fn wrong_target_test() void = { abort(\"wrong test ran\"); };\n");
|
||
let wrongtestedit: str =
|
||
"this edited wrong-target test remains deliberately malformed\n";
|
||
writefile(strings.concat(pkg, "/base.ww"), pkgbase);
|
||
writefile(strings.concat(pkg, "/internal_linux_amd64_test.ww"), samepair);
|
||
writefile(strings.concat(pkg, "/plan9_test.ww"), samewhole);
|
||
writefile(strings.concat(pkg, "/reverse_amd64_linux_test.ww"),
|
||
samereversed);
|
||
writefile(strings.concat(pkg, "/external_linux_amd64_test.ww"),
|
||
externalpair);
|
||
writefile(strings.concat(pkg, "/external.extra_windows_test.ww"),
|
||
externaldot);
|
||
writefile(strings.concat(pkg, "/internal_windows_amd64_test.ww"),
|
||
wrongtest);
|
||
writefile(strings.concat(pkg, "/external_linux_arm64_test.ww"),
|
||
wrongtest);
|
||
writefile(strings.concat(pkg, "/x_plan9_test.ww"), wrongtest);
|
||
writefile(strings.concat(testgood, "/good.ww"), strings.concat(
|
||
"package good;\n",
|
||
"export fn value() i32 = { return 11; };\n"));
|
||
writefile(strings.concat(wrongonly, "/only_windows.ww"), ignoredparse);
|
||
writefile(strings.concat(notests, "/base.ww"),
|
||
"package notests;\nexport fn value() i32 = { return 1; };\n");
|
||
writefile(strings.concat(notests, "/only_windows_test.ww"), ignoredparse);
|
||
writefile(strings.concat(pkg, "/.Fold_test.ww"), ignoredparse);
|
||
writefile(strings.concat(pkg, "/.fold_test.ww"), ignoredparseedit);
|
||
writefile(strings.concat(pkg, "/_Fold_test.ww"), ignoredparse);
|
||
writefile(strings.concat(pkg, "/_fold_test.ww"), ignoredparseedit);
|
||
writefile(strings.concat(pkg, "/Fold_windows_test.ww"), ignoredparse);
|
||
writefile(strings.concat(pkg, "/fold_windows_test.ww"), ignoredparseedit);
|
||
|
||
writefile(strings.concat(foldunicode, "/K.ww"),
|
||
"package main;\nfn latin() i32 = { return 1; };\n");
|
||
writefile(strings.concat(foldunicode, "/K.ww"),
|
||
"package main;\nfn kelvin() i32 = { return 2; };\n");
|
||
writefile(strings.concat(foldunicode, "/main.ww"),
|
||
"package main;\nfn main() i32 = { return 0; };\n");
|
||
let invalidupperbytes: []u8 = alloc([], 6u64)!;
|
||
append(invalidupperbytes, 'A'); append(invalidupperbytes, '-');
|
||
append(invalidupperbytes, 255u8); append(invalidupperbytes, '.');
|
||
append(invalidupperbytes, 'w'); append(invalidupperbytes, 'w');
|
||
let invalidlowerbytes: []u8 = alloc([], 6u64)!;
|
||
append(invalidlowerbytes, 'a'); append(invalidlowerbytes, '-');
|
||
append(invalidlowerbytes, 254u8); append(invalidlowerbytes, '.');
|
||
append(invalidlowerbytes, 'w'); append(invalidlowerbytes, 'w');
|
||
writefile(strings.concat(foldinvalid, "/",
|
||
strings.frombytes(invalidupperbytes)),
|
||
"package main;\nfn invalidupper() i32 = { return 1; };\n");
|
||
writefile(strings.concat(foldinvalid, "/",
|
||
strings.frombytes(invalidlowerbytes)),
|
||
"package main;\nfn invalidlower() i32 = { return 2; };\n");
|
||
writefile(strings.concat(foldinvalid, "/main.ww"),
|
||
"package main;\nfn main() i32 = { return 0; };\n");
|
||
writefile(strings.concat(foldnonormal, "/É.ww"),
|
||
"package main;\nfn composed() i32 = { return 1; };\n");
|
||
writefile(strings.concat(foldnonormal, "/É.ww"),
|
||
"package main;\nfn decomposed() i32 = { return 2; };\n");
|
||
writefile(strings.concat(foldnonormal, "/main.ww"),
|
||
"package main;\nfn main() i32 = { return 0; };\n");
|
||
writefile(strings.concat(foldinternal, "/base.ww"),
|
||
"package sample;\nexport fn value() i32 = { return 1; };\n");
|
||
writefile(strings.concat(foldinternal, "/File_test.ww"),
|
||
"package sample;\n@test fn upper() void = { assert(true); };\n");
|
||
writefile(strings.concat(foldinternal, "/file_test.ww"),
|
||
"package sample;\n@test fn lower() void = { assert(true); };\n");
|
||
writefile(strings.concat(foldexternal, "/base.ww"),
|
||
"package sample;\nexport fn value() i32 = { return 1; };\n");
|
||
writefile(strings.concat(foldexternal, "/File_test.ww"),
|
||
"package sample_test;\n@test fn upper() void = { assert(true); };\n");
|
||
writefile(strings.concat(foldexternal, "/file_test.ww"),
|
||
"package sample_test;\n@test fn lower() void = { assert(true); };\n");
|
||
writefile(strings.concat(foldsameonly, "/File_test.ww"),
|
||
"package sample;\n@test fn upper() void = { assert(true); };\n");
|
||
writefile(strings.concat(foldsameonly, "/file_test.ww"),
|
||
"package sample;\n@test fn lower() void = { assert(true); };\n");
|
||
writefile(strings.concat(foldexternalonly, "/File_test.ww"),
|
||
"package sample_test;\n@test fn upper() void = { assert(true); };\n");
|
||
writefile(strings.concat(foldexternalonly, "/file_test.ww"),
|
||
"package sample_test;\n@test fn lower() void = { assert(true); };\n");
|
||
writefile(strings.concat(foldmixed, "/File_test.ww"),
|
||
"package sample;\n@test fn upper() void = { assert(true); };\n");
|
||
writefile(strings.concat(foldmixed, "/file_test.ww"),
|
||
"package sample_test;\n@test fn lower() void = { assert(true); };\n");
|
||
writefile(strings.concat(foldcross, "/X_TEST.ww"),
|
||
"package cross;\nexport fn value() i32 = { return 1; };\n");
|
||
writefile(strings.concat(foldcross, "/x_test.ww"),
|
||
"package cross;\n@test fn lower() void = { assert(value() == 1); };\n");
|
||
// The lower spelling is created first to prove getdents creation order is
|
||
// irrelevant to both direct and recursive collision diagnostics.
|
||
writefile(strings.concat(foldcreation, "/file.ww"),
|
||
"package main;\nfn lower() i32 = { return 1; };\n");
|
||
writefile(strings.concat(foldcreation, "/File.ww"), strings.concat(
|
||
"package main;\nfn upper() i32 = { return 2; };\n",
|
||
"fn main() i32 = { return 0; };\n"));
|
||
// Creation order is deliberately the inverse of byte order.
|
||
writefile(strings.concat(order, "/z_linux.ww"),
|
||
"package order;\n@test fn z() void = { };\n");
|
||
writefile(strings.concat(order, "/a_linux.ww"),
|
||
"package order;\n@test fn a() void = { };\n");
|
||
|
||
let compilerwrapper: str = strings.concat(tools, "/w6c.sh");
|
||
let assemblerwrapper: str = strings.concat(tools, "/w6a.sh");
|
||
let linkerwrapper: str = strings.concat(tools, "/w6l.sh");
|
||
writeexecutable(compilerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf 'BEGIN' >> \"$WW_PLATFORM_COMPILER_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||
"\"$WW_PLATFORM_COMPILER_TRACE\"; done\n",
|
||
"printf '\\n' >> \"$WW_PLATFORM_COMPILER_TRACE\"\n",
|
||
"exec \"$WW_PLATFORM_REAL_COMPILER\" \"$@\"\n"));
|
||
writeexecutable(assemblerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf 'BEGIN' >> \"$WW_PLATFORM_ASSEMBLER_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||
"\"$WW_PLATFORM_ASSEMBLER_TRACE\"; done\n",
|
||
"printf '\\n' >> \"$WW_PLATFORM_ASSEMBLER_TRACE\"\n",
|
||
"exec \"$WW_PLATFORM_REAL_ASSEMBLER\" \"$@\"\n"));
|
||
writeexecutable(linkerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf 'BEGIN' >> \"$WW_PLATFORM_LINKER_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||
"\"$WW_PLATFORM_LINKER_TRACE\"; done\n",
|
||
"printf '\\n' >> \"$WW_PLATFORM_LINKER_TRACE\"\n",
|
||
"exec \"$WW_PLATFORM_REAL_LINKER\" \"$@\"\n"));
|
||
|
||
let stages: []str = ["ww", "ww_ww"];
|
||
let compilers: []str = ["w6c", "w6c_ww"];
|
||
let assemblers: []str = ["w6a", "w6a_ww"];
|
||
let linkers: []str = ["w6l", "w6l_ww"];
|
||
let labels: []str = ["c", "ww"];
|
||
let artifactnames: []str = ["app.unit.ww", "app.wwi", "app.s",
|
||
"app.o", "app.a", "app.init.unit.ww", "app.init.s", "app.init.o",
|
||
"dep.good.unit.ww", "dep.good.wwi", "dep.good.s", "dep.good.o",
|
||
"dep.good.a"];
|
||
let artifactrefs: []str = ["", "", "", "", "", "", "", "", "",
|
||
"", "", "", ""];
|
||
let testartifactnames: []str = ["pkg-internal-test.unit.ww",
|
||
"pkg-internal-test.wwi",
|
||
"pkg-internal-test.a", "pkg_test-external-test.unit.ww",
|
||
"pkg_test-external-test.wwi", "pkg_test-external-test.a",
|
||
"pkg-test-main.unit.ww", "pkg-test-main.wwi", "pkg-test-main.a"];
|
||
let testartifactrefs: []str = ["", "", "", "", "", "", "", "",
|
||
""];
|
||
let binref: str = "";
|
||
let compilerref: str = "";
|
||
let assemblerref: str = "";
|
||
let linkerref: str = "";
|
||
let prodarchiveref: str = "";
|
||
let prodwwiref: str = "";
|
||
let testoutref: str = "";
|
||
let testerrref: str = "";
|
||
let faildiagref: str = "";
|
||
let recursiveoutref: str = "";
|
||
let recursiveerrref: str = "";
|
||
let foldfilerefs: []str = ["", "", "", "", "", "", "", ""];
|
||
let crossref: str = "";
|
||
let nonormalref: str = "";
|
||
let nonormalunitref: str = "";
|
||
let foldcreationref: str = "";
|
||
let foldcreationrecursiveref: str = "";
|
||
let baseenv: []str = os.getenvs();
|
||
let si: i32 = 0;
|
||
for (si < stages.len) {
|
||
rewritefile(strings.concat(good, "/good.ww"), good5);
|
||
rewritefile(strings.concat(app, "/platform_future.ww"), future);
|
||
rewritefile(strings.concat(app, "/badparse_windows.ww"), ignoredparse);
|
||
rewritefile(strings.concat(pkg, "/internal_windows_amd64_test.ww"),
|
||
wrongtest);
|
||
let work: str = strings.concat(root, "/", labels[si], "-work");
|
||
let reversework: str = strings.concat(root, "/", labels[si],
|
||
"-reverse-work");
|
||
let outdir: str = strings.concat(root, "/", labels[si], "-out");
|
||
let reverseout: str = strings.concat(root, "/", labels[si],
|
||
"-reverse-out");
|
||
let compilertrace: str = strings.concat(root, "/", labels[si],
|
||
"-compiler");
|
||
let assemblertrace: str = strings.concat(root, "/", labels[si],
|
||
"-assembler");
|
||
let linkertrace: str = strings.concat(root, "/", labels[si],
|
||
"-linker");
|
||
assert(os.mkdir(work, 448i32) == 0);
|
||
assert(os.mkdir(reversework, 448i32) == 0);
|
||
assert(os.mkdir(outdir, 448i32) == 0);
|
||
assert(os.mkdir(reverseout, 448i32) == 0);
|
||
writefile(compilertrace, ""); writefile(assemblertrace, "");
|
||
writefile(linkertrace, "");
|
||
let env: []str = alloc([], (baseenv.len + 9): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < baseenv.len) {
|
||
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6A=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6L=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_PLATFORM_COMPILER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_PLATFORM_ASSEMBLER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_PLATFORM_LINKER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_PLATFORM_REAL_COMPILER=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_PLATFORM_REAL_ASSEMBLER=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_PLATFORM_REAL_LINKER=")) {
|
||
append(env, baseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(env, strings.concat("WW_W6C=", compilerwrapper));
|
||
append(env, strings.concat("WW_W6A=", assemblerwrapper));
|
||
append(env, strings.concat("WW_W6L=", linkerwrapper));
|
||
append(env, strings.concat("WW_PLATFORM_COMPILER_TRACE=", compilertrace));
|
||
append(env, strings.concat("WW_PLATFORM_ASSEMBLER_TRACE=", assemblertrace));
|
||
append(env, strings.concat("WW_PLATFORM_LINKER_TRACE=", linkertrace));
|
||
append(env, strings.concat("WW_PLATFORM_REAL_COMPILER=",
|
||
driver(compilers[si])));
|
||
append(env, strings.concat("WW_PLATFORM_REAL_ASSEMBLER=",
|
||
driver(assemblers[si])));
|
||
append(env, strings.concat("WW_PLATFORM_REAL_LINKER=",
|
||
driver(linkers[si])));
|
||
|
||
let forward: []str = [driver(stages[si]), "build", "-w", work,
|
||
"-I", source, "-o", strings.concat(outdir, "/"), app, good];
|
||
let reverse: []str = [driver(stages[si]), "build", "-w", reversework,
|
||
"-I", source, "-o", strings.concat(reverseout, "/"), good, app];
|
||
let out: commandout;
|
||
runcommandenv(root, strings.concat("platform-cold-", labels[si]),
|
||
forward, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
let ctrace: str = readfile(compilertrace);
|
||
let atrace: str = readfile(assemblertrace);
|
||
let ltrace: str = readfile(linkertrace);
|
||
assert(occurrences(ctrace, "\n") == 2);
|
||
assert(occurrences(atrace, "\n") == 3);
|
||
assert(occurrences(ltrace, "\n") == 1);
|
||
let appline: str = linecontaining(ctrace, "/app.unit.new");
|
||
assert(occurrences(appline, "<--import><dep.good>") == 1);
|
||
assert(occurrences(appline, "/dep.good.wwi.new>") == 1);
|
||
assert(!has(ctrace, "dep.wrong"));
|
||
assert(!has(ctrace, "foreign.internal.secret"));
|
||
assert(!has(ctrace, "vendored"));
|
||
assert(!has(ctrace, "command.wwi"));
|
||
assert(occurrences(ltrace, strings.concat(work, "/app.a.new")) == 1);
|
||
assert(occurrences(ltrace, strings.concat(work,
|
||
"/dep.good.a.new")) == 1);
|
||
assert(!has(ltrace, ".wwi"));
|
||
assert(!has(ltrace, "dep.wrong"));
|
||
let unitprefix: str = strings.concat(
|
||
"//ww:module-reset app\n", wholelinux, "\n",
|
||
"//ww:module-reset app\n", appmain, "\n",
|
||
"//ww:module-reset app\n", afterdot, "\n",
|
||
"//ww:module-reset app\n", arch, "\n",
|
||
"//ww:module-reset app\n", reversed, "\n",
|
||
"//ww:module-reset app\n", future, "\n",
|
||
"//ww:module-reset app\n", osname, "\n",
|
||
"//ww:module-reset app\n", pair, "\n");
|
||
let appunit: str = readfile(strings.concat(work, "/app.unit.ww"));
|
||
assert(strings.hasprefix(appunit, unitprefix));
|
||
assert(has(appunit, "//ww:direct-export dep.good "));
|
||
assert(!has(appunit, "wrong_dep"));
|
||
assert(!has(appunit, "wrong_internal"));
|
||
assert(!has(appunit, "wrong_main"));
|
||
assert(!has(appunit, "wrong_vendor"));
|
||
assert(!os.exists(strings.concat(work, "/dep.wrong.unit.ww")));
|
||
let bin: str = strings.concat(outdir, "/app");
|
||
let runav: []str = [bin];
|
||
runcommand(root, strings.concat("platform-run-", labels[si]), runav,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 12);
|
||
let ai: i32 = 0;
|
||
for (ai < artifactnames.len) {
|
||
let bytes: str = readfile(strings.concat(work, "/",
|
||
artifactnames[ai]));
|
||
if (si == 0) { artifactrefs[ai] = strings.dup(bytes); }
|
||
else { assert(same(artifactrefs[ai], bytes)); };
|
||
ai += 1;
|
||
};
|
||
let normalizedcompiler: str = normalizedtrace(ctrace,
|
||
strings.concat(work, "/"), outdir);
|
||
let normalizedassembler: str = normalizedtrace(atrace,
|
||
strings.concat(work, "/"), outdir);
|
||
let normalizedlinker: str = normalizedtrace(ltrace,
|
||
strings.concat(work, "/"), outdir);
|
||
if (si == 0) {
|
||
compilerref = strings.dup(normalizedcompiler);
|
||
assemblerref = strings.dup(normalizedassembler);
|
||
linkerref = strings.dup(normalizedlinker);
|
||
binref = strings.dup(readfile(bin));
|
||
} else {
|
||
assert(same(compilerref, normalizedcompiler));
|
||
assert(same(assemblerref, normalizedassembler));
|
||
assert(same(linkerref, normalizedlinker));
|
||
assert(same(binref, readfile(bin)));
|
||
};
|
||
|
||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||
rewritefile(linkertrace, "");
|
||
runcommandenv(root, strings.concat("platform-reverse-", labels[si]),
|
||
reverse, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(same(readfile(bin), readfile(strings.concat(reverseout, "/app"))));
|
||
assert(same(normalizedcompiler, normalizedtrace(readfile(compilertrace),
|
||
strings.concat(reversework, "/"), reverseout)));
|
||
assert(same(normalizedassembler, normalizedtrace(readfile(assemblertrace),
|
||
strings.concat(reversework, "/"), reverseout)));
|
||
assert(same(normalizedlinker, normalizedtrace(readfile(linkertrace),
|
||
strings.concat(reversework, "/"), reverseout)));
|
||
ai = 0;
|
||
for (ai < artifactnames.len) {
|
||
assert(same(readfile(strings.concat(work, "/", artifactnames[ai])),
|
||
readfile(strings.concat(reversework, "/", artifactnames[ai]))));
|
||
ai += 1;
|
||
};
|
||
|
||
let stableunit: str = strings.dup(readfile(strings.concat(work,
|
||
"/app.unit.ww")));
|
||
let stablewwi: str = strings.dup(readfile(strings.concat(work,
|
||
"/app.wwi")));
|
||
let stablearchive: str = strings.dup(readfile(strings.concat(work,
|
||
"/app.a")));
|
||
let stabledepwwi: str = strings.dup(readfile(strings.concat(work,
|
||
"/dep.good.wwi")));
|
||
let stablebin: str = strings.dup(readfile(bin));
|
||
let stablestamp: str = strings.dup(readfile(strings.concat(work,
|
||
"/.wwtool.stamp")));
|
||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||
rewritefile(linkertrace, "");
|
||
runcommandenv(root, strings.concat("platform-warm-", labels[si]),
|
||
forward, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(readfile(compilertrace).len == 0);
|
||
assert(readfile(assemblertrace).len == 0);
|
||
assert(occurrences(readfile(linkertrace), "\n") == 1);
|
||
|
||
rewritefile(strings.concat(app, "/badparse_windows.ww"),
|
||
ignoredparseedit);
|
||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||
rewritefile(linkertrace, "");
|
||
runcommandenv(root, strings.concat("platform-ignored-edit-", labels[si]),
|
||
forward, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(readfile(compilertrace).len == 0);
|
||
assert(readfile(assemblertrace).len == 0);
|
||
assert(occurrences(readfile(linkertrace), "\n") == 1);
|
||
assert(same(stableunit, readfile(strings.concat(work, "/app.unit.ww"))));
|
||
assert(same(stablewwi, readfile(strings.concat(work, "/app.wwi"))));
|
||
assert(same(stablearchive, readfile(strings.concat(work, "/app.a"))));
|
||
assert(same(stabledepwwi, readfile(strings.concat(work,
|
||
"/dep.good.wwi"))));
|
||
assert(same(stablebin, readfile(bin)));
|
||
assert(same(stablestamp, readfile(strings.concat(work,
|
||
"/.wwtool.stamp"))));
|
||
|
||
rewritefile(strings.concat(good, "/good.ww"), good6);
|
||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||
rewritefile(linkertrace, "");
|
||
runcommandenv(root, strings.concat("platform-private-edit-", labels[si]),
|
||
forward, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(occurrences(readfile(compilertrace), "\n") == 1);
|
||
assert(has(readfile(compilertrace), "/dep.good.unit.new"));
|
||
assert(!has(readfile(compilertrace), "/app.unit.new"));
|
||
assert(occurrences(readfile(assemblertrace), "\n") == 1);
|
||
assert(occurrences(readfile(linkertrace), "\n") == 1);
|
||
assert(same(stabledepwwi, readfile(strings.concat(work,
|
||
"/dep.good.wwi"))));
|
||
assert(same(stableunit, readfile(strings.concat(work, "/app.unit.ww"))));
|
||
assert(same(stablewwi, readfile(strings.concat(work, "/app.wwi"))));
|
||
assert(same(stablearchive, readfile(strings.concat(work, "/app.a"))));
|
||
assert(!same(stablebin, readfile(bin)));
|
||
let runchanged: []str = [bin];
|
||
runcommand(root, strings.concat("platform-run-edited-", labels[si]),
|
||
runchanged, (60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 13);
|
||
let committeddepunit: str = strings.dup(readfile(strings.concat(work,
|
||
"/dep.good.unit.ww")));
|
||
let committeddepa: str = strings.dup(readfile(strings.concat(work,
|
||
"/dep.good.a")));
|
||
let committedbin: str = strings.dup(readfile(bin));
|
||
rewritefile(strings.concat(good, "/good.ww"), good7);
|
||
rewritefile(strings.concat(app, "/platform_future.ww"),
|
||
"package main;\nfn future() i32 = { return missing_name; };\n");
|
||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||
rewritefile(linkertrace, "");
|
||
runcommandenv(root, strings.concat("platform-late-failure-", labels[si]),
|
||
forward, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, "undefined: missing_name"));
|
||
let normalizedfailure: str = normalizedtrace(out.stderr,
|
||
strings.concat(work, "/"), bin);
|
||
if (si == 0) { faildiagref = strings.dup(normalizedfailure); }
|
||
else { assert(same(faildiagref, normalizedfailure)); };
|
||
assert(occurrences(readfile(compilertrace), "\n") == 2);
|
||
assert(occurrences(readfile(assemblertrace), "\n") == 1);
|
||
assert(readfile(linkertrace).len == 0);
|
||
assert(same(committeddepunit, readfile(strings.concat(work,
|
||
"/dep.good.unit.ww"))));
|
||
assert(same(committeddepa, readfile(strings.concat(work,
|
||
"/dep.good.a"))));
|
||
assert(same(committedbin, readfile(bin)));
|
||
assert(same(stablestamp, readfile(strings.concat(work,
|
||
"/.wwtool.stamp"))));
|
||
assert(!os.exists(strings.concat(work, "/dep.good.unit.new")));
|
||
assert(!os.exists(strings.concat(work, "/dep.good.wwi.new")));
|
||
assert(!os.exists(strings.concat(work, "/dep.good.a.new")));
|
||
assert(!os.exists(strings.concat(work, "/app.unit.new")));
|
||
assert(!os.exists(strings.concat(work, "/app.wwi.new")));
|
||
assert(!os.exists(strings.concat(work, "/app.a.new")));
|
||
rewritefile(strings.concat(good, "/good.ww"), good6);
|
||
rewritefile(strings.concat(app, "/platform_future.ww"), future);
|
||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||
rewritefile(linkertrace, "");
|
||
runcommandenv(root, strings.concat("platform-restored-", labels[si]),
|
||
forward, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(readfile(compilertrace).len == 0);
|
||
assert(readfile(assemblertrace).len == 0);
|
||
assert(occurrences(readfile(linkertrace), "\n") == 1);
|
||
assert(same(committedbin, readfile(bin)));
|
||
|
||
rewritefile(strings.concat(good, "/good.ww"), good5);
|
||
rewritefile(strings.concat(app, "/badparse_windows.ww"), ignoredparse);
|
||
let prodwork: str = strings.concat(root, "/", labels[si], "-prod-work");
|
||
let prodout: str = strings.concat(root, "/", labels[si], "-pkg.a");
|
||
assert(os.mkdir(prodwork, 448i32) == 0);
|
||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||
rewritefile(linkertrace, "");
|
||
let prodav: []str = [driver(stages[si]), "build", "-w", prodwork,
|
||
"-I", source, "-o", prodout, pkg];
|
||
runcommandenv(root, strings.concat("platform-production-", labels[si]),
|
||
prodav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(occurrences(readfile(compilertrace), "\n") == 1);
|
||
assert(!has(readfile(compilertrace), "test.good"));
|
||
assert(!has(readfile(strings.concat(prodwork, "/pkg.unit.ww")),
|
||
"selected_internal"));
|
||
if (si == 0) {
|
||
prodarchiveref = strings.dup(readfile(prodout));
|
||
prodwwiref = strings.dup(readfile(strings.concat(prodout, ".wwi")));
|
||
} else {
|
||
assert(same(prodarchiveref, readfile(prodout)));
|
||
assert(same(prodwwiref, readfile(strings.concat(prodout, ".wwi"))));
|
||
};
|
||
|
||
let testwork: str = strings.concat(root, "/", labels[si], "-test-work");
|
||
assert(os.mkdir(testwork, 448i32) == 0);
|
||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||
rewritefile(linkertrace, "");
|
||
let testav: []str = [driver(stages[si]), "test", "-w", testwork,
|
||
"-I", source, pkg];
|
||
runcommandenv(root, strings.concat("platform-test-", labels[si]), testav,
|
||
env, (180i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout, "selected_internal_pair ... ok\n"));
|
||
assert(has(out.stdout, "selected_whole_plan9_name ... ok\n"));
|
||
assert(has(out.stdout, "selected_reversed_suffix ... ok\n"));
|
||
assert(has(out.stdout, "selected_external_pair ... ok\n"));
|
||
assert(has(out.stdout, "selected_external_after_dot ... ok\n"));
|
||
assert(!has(out.stdout, "wrong_target_test"));
|
||
assert(!has(out.stderr, "wrong-target"));
|
||
let testctrace: str = readfile(compilertrace);
|
||
let testltrace: str = readfile(linkertrace);
|
||
assert(!has(testctrace, "test.wrong"));
|
||
let sameline: str = linecontaining(testctrace,
|
||
"/pkg-internal-test.unit.new");
|
||
let externalline: str = linecontaining(testctrace,
|
||
"/pkg_test-external-test.unit.new");
|
||
assert(occurrences(sameline, "<--import><test.good>") == 1);
|
||
assert(occurrences(externalline, "<--import><pkg>") == 1);
|
||
assert(occurrences(externalline, "<--import><test.good>") == 1);
|
||
assert(has(externalline, strings.concat("<--import><pkg><", testwork,
|
||
"/pkg-internal-test.wwi.new>")));
|
||
let mainline: str = linecontaining(testctrace,
|
||
"/pkg-test-main.unit.new");
|
||
assert(occurrences(mainline, "<--test-target-package>") == 2);
|
||
assert(pos(mainline, "<--test-target-package><pkg>")
|
||
< pos(mainline, "<--test-target-package><pkg_test>"));
|
||
assert(occurrences(testltrace, "\n") == 1);
|
||
assert(has(testltrace, "/pkg-test-main.a.new>"));
|
||
assert(has(testltrace, "/pkg-internal-test.a.new>"));
|
||
assert(has(testltrace, "/pkg_test-external-test.a.new>"));
|
||
assert(!has(testltrace, ".wwi"));
|
||
assert(!os.exists(strings.concat(testwork, "/pkg.unit.ww")));
|
||
let sameunit: str = readfile(strings.concat(testwork,
|
||
"/pkg-internal-test.unit.ww"));
|
||
let externalunit: str = readfile(strings.concat(testwork,
|
||
"/pkg_test-external-test.unit.ww"));
|
||
assert(has(sameunit, "selected_internal_pair"));
|
||
assert(has(sameunit, "selected_whole_plan9_name"));
|
||
assert(has(sameunit, "selected_reversed_suffix"));
|
||
assert(!has(sameunit, "wrong_target_test"));
|
||
assert(has(externalunit, "selected_external_pair"));
|
||
assert(has(externalunit, "selected_external_after_dot"));
|
||
assert(!has(externalunit, "wrong_target_test"));
|
||
ai = 0;
|
||
for (ai < testartifactnames.len) {
|
||
let bytes: str = readfile(strings.concat(testwork, "/",
|
||
testartifactnames[ai]));
|
||
if (si == 0) { testartifactrefs[ai] = strings.dup(bytes); }
|
||
else { assert(same(testartifactrefs[ai], bytes)); };
|
||
ai += 1;
|
||
};
|
||
if (si == 0) {
|
||
testoutref = strings.dup(out.stdout);
|
||
testerrref = strings.dup(out.stderr);
|
||
} else {
|
||
assert(same(testoutref, out.stdout));
|
||
assert(same(testerrref, out.stderr));
|
||
};
|
||
let stabletestunit: str = strings.dup(sameunit);
|
||
rewritefile(strings.concat(pkg, "/internal_windows_amd64_test.ww"),
|
||
wrongtestedit);
|
||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||
rewritefile(linkertrace, "");
|
||
runcommandenv(root, strings.concat("platform-test-warm-", labels[si]),
|
||
testav, env, (180i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(same(testoutref, out.stdout));
|
||
assert(readfile(compilertrace).len == 0);
|
||
assert(readfile(assemblertrace).len == 0);
|
||
assert(occurrences(readfile(linkertrace), "\n") == 1);
|
||
assert(same(stabletestunit, readfile(strings.concat(testwork,
|
||
"/pkg-internal-test.unit.ww"))));
|
||
rewritefile(strings.concat(pkg, "/internal_windows_amd64_test.ww"),
|
||
wrongtest);
|
||
|
||
let onlywork: str = strings.concat(root, "/", labels[si], "-only-work");
|
||
let onlyout: str = strings.concat(root, "/", labels[si], "-only-bin");
|
||
assert(os.mkdir(onlywork, 448i32) == 0);
|
||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||
rewritefile(linkertrace, "");
|
||
let onlyav: []str = [driver(stages[si]), "build", "-w", onlywork,
|
||
"-o", onlyout, wrongonly];
|
||
runcommandenv(root, strings.concat("platform-only-", labels[si]), onlyav,
|
||
env, (60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(same(out.stderr, strings.concat("ww: ", wrongonly,
|
||
": directory contains no WW package sources\n")));
|
||
assert(readfile(compilertrace).len == 0);
|
||
assert(readfile(assemblertrace).len == 0);
|
||
assert(readfile(linkertrace).len == 0);
|
||
assert(!os.exists(onlyout));
|
||
assert(!os.exists(strings.concat(onlywork, "/.wwtool.stamp")));
|
||
|
||
let orderwork: str = strings.concat(root, "/", labels[si],
|
||
"-order-work");
|
||
assert(os.mkdir(orderwork, 448i32) == 0);
|
||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||
rewritefile(linkertrace, "");
|
||
let orderav: []str = [driver(stages[si]), "build", "-w", orderwork,
|
||
"-o", strings.concat(root, "/", labels[si], "-order.a"), order];
|
||
runcommandenv(root, strings.concat("platform-order-", labels[si]),
|
||
orderav, env, (60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(same(out.stderr, strings.concat("ww: ", order,
|
||
"/a_linux.ww: @test declaration outside *_test.ww\n")));
|
||
assert(readfile(compilertrace).len == 0);
|
||
assert(readfile(assemblertrace).len == 0);
|
||
assert(readfile(linkertrace).len == 0);
|
||
assert(!os.exists(strings.concat(orderwork, "/.wwtool.stamp")));
|
||
|
||
let recursivework: str = strings.concat(root, "/", labels[si],
|
||
"-recursive-work");
|
||
let recursiveout: str = strings.concat(root, "/", labels[si],
|
||
"-recursive-out");
|
||
assert(os.mkdir(recursivework, 448i32) == 0);
|
||
assert(os.mkdir(recursiveout, 448i32) == 0);
|
||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||
rewritefile(linkertrace, "");
|
||
let recursiveav: []str = [driver(stages[si]), "build", "-w",
|
||
recursivework, "-I", source, "-o", strings.concat(recursiveout,
|
||
"/"), strings.concat(source, "/...")];
|
||
runcommandenv(root, strings.concat("platform-recursive-build-",
|
||
labels[si]), recursiveav, env,
|
||
(180i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
assert(!has(readfile(compilertrace), "wrongonly"));
|
||
assert(!has(readfile(compilertrace), "dep.wrong"));
|
||
let recbin: str = strings.concat(recursiveout, "/app");
|
||
let recrun: []str = [recbin];
|
||
runcommand(root, strings.concat("platform-recursive-run-", labels[si]),
|
||
recrun, (60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 12);
|
||
assert(same(binref, readfile(recbin)));
|
||
|
||
let recursivetestwork: str = strings.concat(root, "/", labels[si],
|
||
"-recursive-test-work");
|
||
assert(os.mkdir(recursivetestwork, 448i32) == 0);
|
||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||
rewritefile(linkertrace, "");
|
||
let recursivetestav: []str = [driver(stages[si]), "test", "-w",
|
||
recursivetestwork, "-I", source, strings.concat(source, "/...")];
|
||
runcommandenv(root, strings.concat("platform-recursive-test-",
|
||
labels[si]), recursivetestav, env,
|
||
(240i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout, "selected_internal_pair ... ok\n"));
|
||
assert(has(out.stdout, "selected_external_pair ... ok\n"));
|
||
assert(has(out.stdout, strings.concat("? ", notests,
|
||
" [no tests]\n")));
|
||
assert(!has(out.stdout, "wrong_target_test"));
|
||
assert(!has(out.stderr, "only_windows"));
|
||
assert(!has(readfile(compilertrace), "dep.wrong"));
|
||
if (si == 0) {
|
||
recursiveoutref = strings.dup(out.stdout);
|
||
recursiveerrref = strings.dup(out.stderr);
|
||
} else {
|
||
assert(same(recursiveoutref, out.stdout));
|
||
assert(same(recursiveerrref, out.stderr));
|
||
};
|
||
|
||
// An upper-case TEST suffix is production source, while its lower-case
|
||
// twin is test source. Ordinary build stays isolated; test selects both
|
||
// categories into one directory-owned filename-collision scope.
|
||
let crosswork: str = strings.concat(root, "/", labels[si],
|
||
"-fold-cross-build-work");
|
||
let crossout: str = strings.concat(root, "/", labels[si],
|
||
"-fold-cross.a");
|
||
assert(os.mkdir(crosswork, 448i32) == 0);
|
||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||
rewritefile(linkertrace, "");
|
||
let crossbuildav: []str = [driver(stages[si]), "build", "-w",
|
||
crosswork, "-I", foldfiles, "-o", crossout, foldcross];
|
||
runcommandenv(root, strings.concat("fold-cross-build-", labels[si]),
|
||
crossbuildav, env,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
assert(!has(readfile(strings.concat(crosswork, "/cross.unit.ww")),
|
||
"@test fn lower"));
|
||
if (si == 0) { crossref = strings.dup(readfile(crossout)); }
|
||
else { assert(same(crossref, readfile(crossout))); };
|
||
|
||
let foldtargets: []str = [foldunicode, foldinvalid, foldinternal,
|
||
foldexternal, foldsameonly, foldexternalonly, foldmixed, foldcross];
|
||
let foldmodes: []str = ["build", "build", "test", "test", "test",
|
||
"test", "test", "test"];
|
||
let foldpackages: []str = ["", "", "sample", "sample", "sample",
|
||
"sample", "sample", "cross"];
|
||
let foldlabels: []str = ["unicode", "invalid", "internal", "external",
|
||
"same-only", "external-only", "mixed", "cross"];
|
||
let foldwants: []str = [
|
||
"ww: case-insensitive file name collision: \"K.ww\" and \"K.ww\"\n",
|
||
"ww: case-insensitive file name collision: \"A-\\xff.ww\" and \"a-\\xfe.ww\"\n",
|
||
"ww: case-insensitive file name collision: \"File_test.ww\" and \"file_test.ww\"\n",
|
||
"ww: case-insensitive file name collision: \"File_test.ww\" and \"file_test.ww\"\n",
|
||
"ww: case-insensitive file name collision: \"File_test.ww\" and \"file_test.ww\"\n",
|
||
"ww: case-insensitive file name collision: \"File_test.ww\" and \"file_test.ww\"\n",
|
||
"ww: case-insensitive file name collision: \"File_test.ww\" and \"file_test.ww\"\n",
|
||
"ww: case-insensitive file name collision: \"X_TEST.ww\" and \"x_test.ww\"\n",
|
||
];
|
||
let fi: i32 = 0;
|
||
for (fi < foldtargets.len) {
|
||
let foldwork: str = strings.concat(root, "/", labels[si],
|
||
"-fold-", foldlabels[fi], "-work");
|
||
let foldoutput: str = strings.concat(root, "/", labels[si],
|
||
"-fold-", foldlabels[fi]);
|
||
assert(os.mkdir(foldwork, 448i32) == 0);
|
||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||
rewritefile(linkertrace, "");
|
||
let foldbuildav: []str = [driver(stages[si]), "build", "-w",
|
||
foldwork, "-I", foldfiles, "-o", foldoutput, foldtargets[fi]];
|
||
let foldtestav: []str = [driver(stages[si]), "test", "-w",
|
||
foldwork, "-I", foldfiles, foldtargets[fi]];
|
||
let foldav: []str = foldtestav;
|
||
if (same(foldmodes[fi], "build")) { foldav = foldbuildav; };
|
||
runcommandenv(root, strings.concat("filename-fold-", labels[si],
|
||
"-", foldlabels[fi]), foldav, env,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
let foldfullwant: str = foldwants[fi];
|
||
if (same(foldmodes[fi], "test")) {
|
||
foldfullwant = strings.concat(foldwants[fi], "FAIL ",
|
||
foldtargets[fi], " [", foldpackages[fi],
|
||
"] (build exit 1)\n");
|
||
};
|
||
assert(out.stdout.len == 0 && same(out.stderr, foldfullwant));
|
||
assert(readfile(compilertrace).len == 0);
|
||
assert(readfile(assemblertrace).len == 0);
|
||
assert(readfile(linkertrace).len == 0);
|
||
assert(!os.exists(foldoutput));
|
||
assert(!os.exists(strings.concat(foldoutput, ".new")));
|
||
assert(!os.exists(strings.concat(foldoutput, ".sepwork")));
|
||
assert(!os.exists(strings.concat(foldwork, "/.wwtool.stamp")));
|
||
assert(directoryisempty(foldwork));
|
||
if (si == 0) { foldfilerefs[fi] = strings.dup(out.stderr); }
|
||
else { assert(same(foldfilerefs[fi], out.stderr)); };
|
||
fi += 1;
|
||
};
|
||
|
||
let creationmodes: []str = ["direct", "recursive"];
|
||
let creationfull: []str = ["", ""];
|
||
let creationwant: str =
|
||
"ww: case-insensitive file name collision: \"File.ww\" and \"file.ww\"\n";
|
||
let ci: i32 = 0;
|
||
for (ci < creationmodes.len) {
|
||
let creationwork: str = strings.concat(root, "/", labels[si],
|
||
"-fold-creation-", creationmodes[ci], "-work");
|
||
let creationout: str = strings.concat(root, "/", labels[si],
|
||
"-fold-creation-", creationmodes[ci]);
|
||
assert(os.mkdir(creationwork, 448i32) == 0);
|
||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||
rewritefile(linkertrace, "");
|
||
let directav: []str = [driver(stages[si]), "build", "-w",
|
||
creationwork, "-I", foldcreationroot, "-o", creationout,
|
||
foldcreation];
|
||
if (ci != 0) { assert(os.mkdir(creationout, 448i32) == 0); };
|
||
let recursiveav: []str = [driver(stages[si]), "build", "-w",
|
||
creationwork, "-I", foldcreationroot, "-o",
|
||
strings.concat(creationout, "/"),
|
||
strings.concat(foldcreationroot, "/...")];
|
||
let creationav: []str = directav;
|
||
if (ci != 0) { creationav = recursiveav; };
|
||
runcommandenv(root, strings.concat("filename-fold-creation-",
|
||
labels[si], "-", creationmodes[ci]), creationav, env,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(out.stdout.len == 0);
|
||
// Build-output failures are emitted by the shared driver after its
|
||
// load/check boundary; the coordinator adds no test-style FAIL line.
|
||
assert(same(out.stderr, creationwant));
|
||
assert(readfile(compilertrace).len == 0);
|
||
assert(readfile(assemblertrace).len == 0);
|
||
assert(readfile(linkertrace).len == 0);
|
||
if (ci == 0) { assert(!os.exists(creationout)); }
|
||
else { assert(directoryisempty(creationout)); };
|
||
assert(!os.exists(strings.concat(creationout, ".new")));
|
||
assert(!os.exists(strings.concat(creationout, ".sepwork")));
|
||
assert(directoryisempty(creationwork));
|
||
creationfull[ci] = strings.dup(out.stderr);
|
||
ci += 1;
|
||
};
|
||
if (si == 0) {
|
||
foldcreationref = strings.dup(creationfull[0]);
|
||
foldcreationrecursiveref = strings.dup(creationfull[1]);
|
||
} else {
|
||
assert(same(foldcreationref, creationfull[0]));
|
||
assert(same(foldcreationrecursiveref, creationfull[1]));
|
||
};
|
||
|
||
// Canonically equivalent Unicode normalization forms remain distinct.
|
||
let nonormalwork: str = strings.concat(root, "/", labels[si],
|
||
"-fold-nonormal-work");
|
||
let nonormalout: str = strings.concat(root, "/", labels[si],
|
||
"-fold-nonormal");
|
||
assert(os.mkdir(nonormalwork, 448i32) == 0);
|
||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||
rewritefile(linkertrace, "");
|
||
let nonormalav: []str = [driver(stages[si]), "build", "-w",
|
||
nonormalwork, "-I", foldfiles, "-o", nonormalout, foldnonormal];
|
||
runcommandenv(root, strings.concat("filename-no-normalization-",
|
||
labels[si]), nonormalav, env,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
let nonormalunit: str = readfile(strings.concat(nonormalwork,
|
||
"/nonormal.unit.ww"));
|
||
assert(has(nonormalunit, "fn composed() i32"));
|
||
assert(has(nonormalunit, "fn decomposed() i32"));
|
||
if (si == 0) { nonormalref = strings.dup(readfile(nonormalout)); }
|
||
else { assert(same(nonormalref, readfile(nonormalout))); };
|
||
if (si == 0) { nonormalunitref = strings.dup(nonormalunit); }
|
||
else { assert(same(nonormalunitref, nonormalunit)); };
|
||
|
||
// A collision introduced after a committed generation is pre-tool and
|
||
// cannot mutate any successful action, stamp, or published output. Its
|
||
// removal restores exact warm reuse (only the normal relink remains).
|
||
let warmname: str = strings.concat("warm", labels[si]);
|
||
let warmdir: str = strings.concat(foldfiles, "/", warmname);
|
||
mkdirall(warmdir);
|
||
writefile(strings.concat(warmdir, "/File.ww"), strings.concat(
|
||
"package main;\nfn upper() i32 = { return 1; };\n"));
|
||
writefile(strings.concat(warmdir, "/main.ww"), strings.concat(
|
||
"package main;\nfn main() i32 = { return upper() - 1; };\n"));
|
||
let warmwork: str = strings.concat(root, "/", labels[si],
|
||
"-fold-warm-work");
|
||
let warmout: str = strings.concat(root, "/", labels[si],
|
||
"-fold-warm");
|
||
assert(os.mkdir(warmwork, 448i32) == 0);
|
||
let warmav: []str = [driver(stages[si]), "build", "-w", warmwork,
|
||
"-I", foldfiles, "-o", warmout, warmdir];
|
||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||
rewritefile(linkertrace, "");
|
||
runcommandenv(root, strings.concat("filename-fold-warm-cold-",
|
||
labels[si]), warmav, env,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let warmsuffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a",
|
||
".init.unit.ww", ".init.s", ".init.o"];
|
||
let warmrefs: []str = ["", "", "", "", "", "", "", ""];
|
||
let wi: i32 = 0;
|
||
for (wi < warmsuffixes.len) {
|
||
warmrefs[wi] = strings.dup(readfile(strings.concat(warmwork, "/",
|
||
warmname, warmsuffixes[wi])));
|
||
wi += 1;
|
||
};
|
||
let warmbinref: str = strings.dup(readfile(warmout));
|
||
let warmstampref: str = strings.dup(readfile(strings.concat(warmwork,
|
||
"/.wwtool.stamp")));
|
||
let warmtoolpaths: []str = ["/.wwtool.ww", "/.wwtool.w6c",
|
||
"/.wwtool.w6a"];
|
||
let warmtoolrefs: []str = alloc([], warmtoolpaths.len: u64)!;
|
||
wi = 0;
|
||
for (wi < warmtoolpaths.len) {
|
||
append(warmtoolrefs, strings.dup(readfile(strings.concat(warmwork,
|
||
warmtoolpaths[wi]))));
|
||
wi += 1;
|
||
};
|
||
let collidingfile: str = strings.concat(warmdir, "/file.ww");
|
||
writefile(collidingfile,
|
||
"package main;\nfn lower() i32 = { return 2; };\n");
|
||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||
rewritefile(linkertrace, "");
|
||
runcommandenv(root, strings.concat("filename-fold-warm-added-",
|
||
labels[si]), warmav, env,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(out.stdout.len == 0 && same(out.stderr,
|
||
"ww: case-insensitive file name collision: \"File.ww\" and \"file.ww\"\n"));
|
||
assert(readfile(compilertrace).len == 0);
|
||
assert(readfile(assemblertrace).len == 0);
|
||
assert(readfile(linkertrace).len == 0);
|
||
wi = 0;
|
||
for (wi < warmsuffixes.len) {
|
||
assert(same(warmrefs[wi], readfile(strings.concat(warmwork, "/",
|
||
warmname, warmsuffixes[wi]))));
|
||
wi += 1;
|
||
};
|
||
wi = 0;
|
||
for (wi < warmtoolpaths.len) {
|
||
assert(same(warmtoolrefs[wi], readfile(strings.concat(warmwork,
|
||
warmtoolpaths[wi]))));
|
||
wi += 1;
|
||
};
|
||
assert(!directoryhasnew(warmwork));
|
||
assert(same(warmbinref, readfile(warmout)));
|
||
assert(!os.exists(strings.concat(warmout, ".new")));
|
||
assert(!os.exists(strings.concat(warmout, ".sepwork")));
|
||
assert(same(warmstampref, readfile(strings.concat(warmwork,
|
||
"/.wwtool.stamp"))));
|
||
assert(os.remove(collidingfile) == 0);
|
||
rewritefile(compilertrace, ""); rewritefile(assemblertrace, "");
|
||
rewritefile(linkertrace, "");
|
||
runcommandenv(root, strings.concat("filename-fold-warm-removed-",
|
||
labels[si]), warmav, env,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
assert(readfile(compilertrace).len == 0);
|
||
assert(readfile(assemblertrace).len == 0);
|
||
assert(occurrences(readfile(linkertrace), "\n") == 1);
|
||
wi = 0;
|
||
for (wi < warmsuffixes.len) {
|
||
assert(same(warmrefs[wi], readfile(strings.concat(warmwork, "/",
|
||
warmname, warmsuffixes[wi]))));
|
||
wi += 1;
|
||
};
|
||
wi = 0;
|
||
for (wi < warmtoolpaths.len) {
|
||
assert(same(warmtoolrefs[wi], readfile(strings.concat(warmwork,
|
||
warmtoolpaths[wi]))));
|
||
wi += 1;
|
||
};
|
||
assert(!directoryhasnew(warmwork));
|
||
assert(same(warmbinref, readfile(warmout)));
|
||
assert(!os.exists(strings.concat(warmout, ".new")));
|
||
assert(!os.exists(strings.concat(warmout, ".sepwork")));
|
||
assert(same(warmstampref, readfile(strings.concat(warmwork,
|
||
"/.wwtool.stamp"))));
|
||
si += 1;
|
||
};
|
||
clean(root);
|
||
};
|
||
|
||
// Invalid @test attribute shapes reject at build with stable text on
|
||
// BOTH frontends (the -T synth checker owns them; the fixture corpus
|
||
// cannot reach -T, so these rows live here). Fragments only — the
|
||
// stages' error-line prefixes legitimately differ (file:line:col vs
|
||
// file:).
|
||
@test fn invalid_test_shapes_reject() void = {
|
||
let root: str = fresh();
|
||
let shapes: []str = [
|
||
"@test export fn t() void = { };",
|
||
"@test fn t() void;",
|
||
"@test fn t(x: i32) void = { };",
|
||
"@test fn t() i32 = { return 0; };",
|
||
"@test @test fn t() void = { };",
|
||
];
|
||
let frags: []str = [
|
||
"@test fn 't' cannot be exported",
|
||
"@test fn 't' needs a body",
|
||
"@test fn 't' must be fn() void",
|
||
"@test fn 't' must be fn() void",
|
||
"duplicate @test on fn 't'",
|
||
];
|
||
let digits: []str = ["0", "1", "2", "3", "4"];
|
||
let outc: commandout;
|
||
let outw: commandout;
|
||
let i: i32 = 0;
|
||
for (i < shapes.len) {
|
||
let dir: str = strings.concat(root, "/shape", digits[i]);
|
||
assert(os.mkdir(dir, 448i32) == 0);
|
||
let src: str = strings.concat(dir, "/bad_test.ww");
|
||
writefile(src, strings.concat("package main;\n",
|
||
shapes[i], "\n"));
|
||
let avc: []str = [driver("ww"), "test", src];
|
||
let avw: []str = [driver("ww_ww"), "test", src];
|
||
runcommand(root, strings.concat("shape-c", digits[i]), avc,
|
||
(30i64 * (time.second: i64)): time.duration, &outc);
|
||
runcommand(root, strings.concat("shape-ww", digits[i]), avw,
|
||
(30i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 1);
|
||
expectexit(&outw, 1);
|
||
assert(has(outc.stderr, frags[i]));
|
||
assert(has(outw.stderr, frags[i]));
|
||
i += 1;
|
||
};
|
||
clean(root);
|
||
};
|
||
|
||
// Go's local-pattern parity: "..." walks below the prefix preceding its first
|
||
// occurrence. Dot-, underscore-, and testdata-prefixed trees are excluded;
|
||
// vendor is traversed but a wildcard cannot consume a non-terminal vendor.
|
||
// Excluded sentinels fail so a wrong descent turns the whole run red.
|
||
@test fn recursive_tree_discovery() void = {
|
||
let root: str = fresh();
|
||
let tree: str = strings.concat(root, "/tree");
|
||
assert(os.mkdir(tree, 448i32) == 0);
|
||
assert(os.mkdir(strings.concat(tree, "/alpha"), 448i32) == 0);
|
||
assert(os.mkdir(strings.concat(tree, "/beta"), 448i32) == 0);
|
||
assert(os.mkdir(strings.concat(tree, "/beta/inner"), 448i32) == 0);
|
||
assert(os.mkdir(strings.concat(tree, "/gamma"), 448i32) == 0);
|
||
assert(os.mkdir(strings.concat(tree, "/app"), 448i32) == 0);
|
||
assert(os.mkdir(strings.concat(tree, "/app/vendor"), 448i32) == 0);
|
||
assert(os.mkdir(strings.concat(tree, "/app/vendor/lib"), 448i32) == 0);
|
||
assert(os.mkdir(strings.concat(tree, "/app/vendor/lib/math"), 448i32) == 0);
|
||
assert(os.mkdir(strings.concat(tree, "/domain"), 448i32) == 0);
|
||
assert(os.mkdir(strings.concat(tree, "/domain/app"), 448i32) == 0);
|
||
assert(os.mkdir(strings.concat(tree, "/domain/vendor"), 448i32) == 0);
|
||
assert(os.mkdir(strings.concat(tree, "/domain/vendor/dep"), 448i32) == 0);
|
||
assert(os.mkdir(strings.concat(tree, "/.hidden"), 448i32) == 0);
|
||
assert(os.mkdir(strings.concat(tree, "/.hidden/deep"), 448i32) == 0);
|
||
assert(os.mkdir(strings.concat(tree, "/_skip"), 448i32) == 0);
|
||
assert(os.mkdir(strings.concat(tree, "/testdata"), 448i32) == 0);
|
||
assert(os.mkdir(strings.concat(tree, "/testdata/bad"), 448i32) == 0);
|
||
assert(os.mkdir(strings.concat(tree, "/vendor"), 448i32) == 0);
|
||
assert(os.mkdir(strings.concat(tree, "/vendor/bad"), 448i32) == 0);
|
||
assert(os.mkdir(strings.concat(tree, "/vendor/bad/vendor"), 448i32) == 0);
|
||
assert(os.mkdir(strings.concat(tree, "/vendor/bad/vendor/deep"), 448i32) == 0);
|
||
assert(os.mkdir(strings.concat(tree, "/vendorx"), 448i32) == 0);
|
||
writefile(strings.concat(tree, "/a.ww"),
|
||
"package rootpkg;\nfn first() i32 = { return 1; };\n");
|
||
writefile(strings.concat(tree, "/root_test.ww"), strings.concat(
|
||
"package rootpkg;\n",
|
||
"@test fn treeroot() void = { assert(first() + last() == 3); };\n"));
|
||
writefile(strings.concat(tree, "/z.ww"),
|
||
"package rootpkg;\nfn last() i32 = { return 2; };\n");
|
||
writefile(strings.concat(tree, "/alpha/alpha.ww"),
|
||
"package alpha;\nexport fn value() int = { return 7; };\n");
|
||
writefile(strings.concat(tree, "/alpha/alpha_test.ww"),
|
||
strings.concat("package alpha_test;\nimport alpha;\n",
|
||
"@test fn treealpha() void = { assert(alpha.value() == 7);",
|
||
" assert(alpha.linked() == 11); };\n"));
|
||
writefile(strings.concat(tree, "/beta/inner/inner_test.ww"),
|
||
"package inner;\n@test fn treeinner() void = { assert(1 == 1); };\n");
|
||
writefile(strings.concat(tree, "/gamma/gamma.ww"),
|
||
"package gamma;\nexport fn g() int = { return 1; };\n");
|
||
writefile(strings.concat(tree, "/app/app.ww"), strings.concat(
|
||
"package app;\nimport lib.math;\n",
|
||
"export fn value() int = { return math.value(); };\n"));
|
||
writefile(strings.concat(tree, "/app/app_test.ww"), strings.concat(
|
||
"package app;\n@test fn vendored_dependency() void = {",
|
||
" assert(value() == 17); };\n"));
|
||
writefile(strings.concat(tree, "/app/vendor/lib/math/math.ww"),
|
||
"package math;\nexport fn value() int = { return 17; };\n");
|
||
writefile(strings.concat(tree, "/domain/app/app.ww"), strings.concat(
|
||
"package app;\nimport dep;\n",
|
||
"fn value() int = { return dep.value(); };\n"));
|
||
writefile(strings.concat(tree, "/domain/app/app_test.ww"), strings.concat(
|
||
"package app;\n@test fn ancestor_vendor() void = {",
|
||
" assert(value() == 29); };\n"));
|
||
writefile(strings.concat(tree, "/domain/vendor/dep/dep.ww"),
|
||
"package dep;\nexport fn value() int = { return 29; };\n");
|
||
writefile(strings.concat(tree, "/gamma/.ignored.ww"),
|
||
"this hidden source must not make package loading fail\n");
|
||
writefile(strings.concat(tree, "/gamma/_ignored.ww"),
|
||
"this underscore source must not make package loading fail\n");
|
||
writefile(strings.concat(tree, "/.hidden/hid_test.ww"),
|
||
"package hid;\n@test fn hidden_direct() void = { assert(true); };\n");
|
||
writefile(strings.concat(tree, "/.hidden/deep/bad.ww"),
|
||
"this excluded malformed directory must not be selected\n");
|
||
writefile(strings.concat(tree, "/_skip/skip_test.ww"),
|
||
"package _skip;\n@test fn underscore_direct() void = { assert(true); };\n");
|
||
writefile(strings.concat(tree, "/testdata/bad/bad_test.ww"),
|
||
"package bad;\n@test fn testdata_direct() void = { assert(true); };\n");
|
||
writefile(strings.concat(tree, "/vendor/vendor.ww"),
|
||
"package vendor;\nexport fn value() int = { return 1; };\n");
|
||
writefile(strings.concat(tree, "/vendor/bad/bad.ww"),
|
||
"package bad;\nexport fn value() int = { return 2; };\n");
|
||
writefile(strings.concat(tree, "/vendor/bad/bad_test.ww"),
|
||
"package bad;\n@test fn explicit_vendor() void = { assert(value() == 2); };\n");
|
||
writefile(strings.concat(tree, "/vendor/bad/vendor/deep/deep_test.ww"),
|
||
"package deep;\n@test fn nested_vendor_must_not_run() void = { assert(false); };\n");
|
||
writefile(strings.concat(tree, "/vendorx/vendorx.ww"),
|
||
"package vendorx;\nexport fn value() int = { return 3; };\n");
|
||
let outside: str = strings.concat(root, "/outside");
|
||
let alias: str = strings.concat(root, "/tree-alias");
|
||
assert(os.mkdir(outside, 448i32) == 0);
|
||
writefile(strings.concat(outside, "/outside.ww"),
|
||
"package outside;\nexport fn value() int = { return 23; };\n");
|
||
writefile(strings.concat(outside, "/outside_test.ww"), strings.concat(
|
||
"package outside;\n@test fn explicit_root_symlink() void = {",
|
||
" assert(value() == 23); };\n"));
|
||
let linkout: commandout;
|
||
let linkedsource: str = strings.concat(root, "/alpha-linked-source");
|
||
writefile(linkedsource,
|
||
"package alpha;\nexport fn linked() int = { return 11; };\n");
|
||
let linkedav: []str = ["/bin/ln", "-s", linkedsource,
|
||
strings.concat(tree, "/alpha/linked.ww")];
|
||
runcommand(root, "recursive-source-link", linkedav, time.second, &linkout);
|
||
expectexit(&linkout, 0);
|
||
let linkeddirectory: str = strings.concat(root, "/source-directory");
|
||
assert(os.mkdir(linkeddirectory, 448i32) == 0);
|
||
let linkeddirav: []str = ["/bin/ln", "-s", linkeddirectory,
|
||
strings.concat(tree, "/alpha/directory.ww")];
|
||
runcommand(root, "recursive-source-directory-link", linkeddirav,
|
||
time.second, &linkout);
|
||
expectexit(&linkout, 0);
|
||
let aliasav: []str = ["/bin/ln", "-s", tree, alias];
|
||
runcommand(root, "recursive-root-alias", aliasav, time.second, &linkout);
|
||
expectexit(&linkout, 0);
|
||
let escapeav: []str = ["/bin/ln", "-s", outside,
|
||
strings.concat(tree, "/escape")];
|
||
runcommand(root, "recursive-child-escape", escapeav, time.second, &linkout);
|
||
expectexit(&linkout, 0);
|
||
let cycleav: []str = ["/bin/ln", "-s", tree,
|
||
strings.concat(tree, "/cycle")];
|
||
runcommand(root, "recursive-child-cycle", cycleav, time.second, &linkout);
|
||
expectexit(&linkout, 0);
|
||
|
||
let spec: str = strings.concat(tree, "/...");
|
||
let outc: commandout;
|
||
let outw: commandout;
|
||
let treec: []str = [driver("ww"), "test", "-I", tree, spec];
|
||
let treew: []str = [driver("ww_ww"), "test", "-I", tree, spec];
|
||
runcommand(root, "tree-c", treec,
|
||
(30i64 * (time.second: i64)): time.duration, &outc);
|
||
runcommand(root, "tree-ww", treew,
|
||
(30i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 0);
|
||
expectexit(&outw, 0);
|
||
assert(same(outc.stdout, outw.stdout));
|
||
assert(same(outc.stderr, outw.stderr));
|
||
let seq: str = strings.dup(outc.stdout);
|
||
|
||
// A directory -o publishes every selected command by directory basename.
|
||
// Non-command production roots remain graph roots but have no named output.
|
||
let commands: str = strings.concat(root, "/commands");
|
||
let commandalpha: str = strings.concat(commands, "/cmd-alpha");
|
||
let commandzeta: str = strings.concat(commands, "/cmd-zeta");
|
||
let commandlib: str = strings.concat(commands, "/library");
|
||
assert(os.mkdir(commands, 448i32) == 0);
|
||
assert(os.mkdir(commandalpha, 448i32) == 0);
|
||
assert(os.mkdir(commandzeta, 448i32) == 0);
|
||
assert(os.mkdir(commandlib, 448i32) == 0);
|
||
writefile(strings.concat(commandalpha, "/main.ww"), strings.concat(
|
||
"package main;\nimport library;\n",
|
||
"fn main() i32 = { return library.value(); };\n"));
|
||
writefile(strings.concat(commandzeta, "/main.ww"),
|
||
"package main;\nfn main() i32 = { return 0; };\n");
|
||
writefile(strings.concat(commandlib, "/library.ww"),
|
||
"package library;\nexport fn value() i32 = { return 0; };\n");
|
||
let commandalias: str = strings.concat(root, "/commands-alias");
|
||
let commandaliasav: []str = ["/bin/ln", "-s", commands, commandalias];
|
||
runcommand(root, "recursive-command-alias", commandaliasav, time.second,
|
||
&linkout);
|
||
expectexit(&linkout, 0);
|
||
let dashselector: str = "-dash-command";
|
||
let dashselectorpath: str = strings.concat(root, "/", dashselector);
|
||
let dashselectorav: []str = ["/bin/ln", "-s", commandalpha,
|
||
dashselectorpath];
|
||
runcommand(root, "recursive-dash-selector", dashselectorav, time.second,
|
||
&linkout);
|
||
expectexit(&linkout, 0);
|
||
let commandalphaid: str = localidentity(commandalpha, "main");
|
||
let commandzetaid: str = localidentity(commandzeta, "main");
|
||
let binc: str = strings.concat(root, "/bin-c");
|
||
let binw: str = strings.concat(root, "/bin-ww");
|
||
let reversebinc: str = strings.concat(root, "/reverse-bin-c");
|
||
let reversebinw: str = strings.concat(root, "/reverse-bin-ww");
|
||
assert(os.mkdir(binc, 448i32) == 0);
|
||
assert(os.mkdir(binw, 448i32) == 0);
|
||
let commandspec: str = strings.concat(commands, "/...");
|
||
let commandaliasspec: str = strings.concat(commandalias, "/...");
|
||
let commandworks: []str = [strings.concat(root, "/command-work-c"),
|
||
strings.concat(root, "/command-work-ww")];
|
||
let commandreverseworks: []str = [strings.concat(root,
|
||
"/command-reverse-work-c"), strings.concat(root,
|
||
"/command-reverse-work-ww")];
|
||
let commandbins: []str = [binc, binw];
|
||
let commandreversebins: []str = [reversebinc, reversebinw];
|
||
let commandreverseoutputs: []str = [strings.concat(reversebinc, "/"),
|
||
strings.concat(reversebinw, "/")];
|
||
let commandcompilertraces: []str = [strings.concat(root,
|
||
"/command-compiler-c"), strings.concat(root, "/command-compiler-ww")];
|
||
let commandassemblertraces: []str = [strings.concat(root,
|
||
"/command-assembler-c"), strings.concat(root, "/command-assembler-ww")];
|
||
let commandlinkertraces: []str = [strings.concat(root,
|
||
"/command-linker-c"), strings.concat(root, "/command-linker-ww")];
|
||
let commandasmworks: []str = [strings.concat(root, "/asm-work-c"),
|
||
strings.concat(root, "/asm-work-ww")];
|
||
let commandtools: str = strings.concat(root, "/command-tools");
|
||
assert(os.mkdir(commandtools, 448i32) == 0);
|
||
let commandcompilerwrapper: str = strings.concat(commandtools, "/w6c.sh");
|
||
let commandassemblerwrapper: str = strings.concat(commandtools, "/w6a.sh");
|
||
let commandlinkerwrapper: str = strings.concat(commandtools, "/w6l.sh");
|
||
writeexecutable(commandcompilerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf 'BEGIN' >> \"$WW_PATTERN_COMPILER_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||
"\"$WW_PATTERN_COMPILER_TRACE\"; done\n",
|
||
"printf '\\n' >> \"$WW_PATTERN_COMPILER_TRACE\"\n",
|
||
"exec \"$WW_PATTERN_REAL_COMPILER\" \"$@\"\n"));
|
||
writeexecutable(commandassemblerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf 'BEGIN' >> \"$WW_PATTERN_ASSEMBLER_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||
"\"$WW_PATTERN_ASSEMBLER_TRACE\"; done\n",
|
||
"printf '\\n' >> \"$WW_PATTERN_ASSEMBLER_TRACE\"\n",
|
||
"exec \"$WW_PATTERN_REAL_ASSEMBLER\" \"$@\"\n"));
|
||
writeexecutable(commandlinkerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf 'BEGIN' >> \"$WW_PATTERN_LINKER_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||
"\"$WW_PATTERN_LINKER_TRACE\"; done\n",
|
||
"printf '\\n' >> \"$WW_PATTERN_LINKER_TRACE\"\n",
|
||
"exec \"$WW_PATTERN_REAL_LINKER\" \"$@\"\n"));
|
||
|
||
let collisions: str = strings.concat(root, "/collisions");
|
||
let collisionleft: str = strings.concat(collisions, "/left");
|
||
let collisionright: str = strings.concat(collisions, "/right");
|
||
let collisionleftcmd: str = strings.concat(collisionleft, "/same");
|
||
let collisionrightcmd: str = strings.concat(collisionright, "/same");
|
||
assert(os.mkdir(collisions, 448i32) == 0);
|
||
assert(os.mkdir(collisionleft, 448i32) == 0);
|
||
assert(os.mkdir(collisionright, 448i32) == 0);
|
||
assert(os.mkdir(collisionleftcmd, 448i32) == 0);
|
||
assert(os.mkdir(collisionrightcmd, 448i32) == 0);
|
||
writefile(strings.concat(collisionleftcmd, "/main.ww"),
|
||
"package main;\nfn main() i32 = { return 0; };\n");
|
||
writefile(strings.concat(collisionrightcmd, "/main.ww"),
|
||
"package main;\nfn main() i32 = { return 0; };\n");
|
||
let collisionspec: str = strings.concat(collisions, "/...");
|
||
let collisionout: str = strings.concat(root, "/collision-bin");
|
||
assert(os.mkdir(collisionout, 448i32) == 0);
|
||
let collisionworks: []str = [strings.concat(root, "/collision-work-c"),
|
||
strings.concat(root, "/collision-work-ww")];
|
||
|
||
let commandstages: []str = ["ww", "ww_ww"];
|
||
let commandcompilers: []str = ["w6c", "w6c_ww"];
|
||
let commandassemblers: []str = ["w6a", "w6a_ww"];
|
||
let commandlinkers: []str = ["w6l", "w6l_ww"];
|
||
let commandbaseenv: []str = os.getenvs();
|
||
let referencecommandcompiler: str = "";
|
||
let referencecommandassembler: str = "";
|
||
let referencecommandlinker: str = "";
|
||
let referenceasmcompiler: str = "";
|
||
let referenceasmrejectdiag: str = "";
|
||
let referencecollisiondiag: str = "";
|
||
let referencefanoutpathdiag: str = "";
|
||
let referencescratchpathdiag: str = "";
|
||
let referenceworkpathdiag: str = "";
|
||
// Raw -o/-w spellings still fit the public PATH_MAX contract. The
|
||
// directory fan-out, cold scratch, and persistent tool-record spellings
|
||
// derived from them do not, and must be rejected before any producer.
|
||
let longoutputbytes: []u8 = alloc([], os.PATH_MAX: u64)!;
|
||
let longpathi: i32 = 0;
|
||
for (longpathi < root.len) {
|
||
append(longoutputbytes, root[longpathi]); longpathi += 1;
|
||
};
|
||
append(longoutputbytes, '/': u8);
|
||
for (longoutputbytes.len < os.PATH_MAX - 2) {
|
||
append(longoutputbytes, 'o': u8);
|
||
};
|
||
append(longoutputbytes, '/': u8);
|
||
assert(longoutputbytes.len == os.PATH_MAX - 1);
|
||
let longoutputdir: str = strings.frombytes(longoutputbytes);
|
||
let longscratchbytes: []u8 = alloc([], os.PATH_MAX: u64)!;
|
||
longpathi = 0;
|
||
for (longpathi < root.len) {
|
||
append(longscratchbytes, root[longpathi]); longpathi += 1;
|
||
};
|
||
append(longscratchbytes, '/': u8);
|
||
for (longscratchbytes.len < os.PATH_MAX - 1) {
|
||
append(longscratchbytes, 's': u8);
|
||
};
|
||
assert(longscratchbytes.len == os.PATH_MAX - 1);
|
||
let longscratchout: str = strings.frombytes(longscratchbytes);
|
||
let longworkbytes: []u8 = alloc([], os.PATH_MAX: u64)!;
|
||
longpathi = 0;
|
||
for (longpathi < root.len) {
|
||
append(longworkbytes, root[longpathi]); longpathi += 1;
|
||
};
|
||
for (longworkbytes.len < os.PATH_MAX - 8) {
|
||
append(longworkbytes, '/': u8);
|
||
let segmentbytes: i32 = 0;
|
||
for (segmentbytes < 120 && longworkbytes.len < os.PATH_MAX - 8) {
|
||
append(longworkbytes, 'w': u8); segmentbytes += 1;
|
||
};
|
||
};
|
||
let longworkdir: str = strings.frombytes(longworkbytes);
|
||
mkdirall(longworkdir);
|
||
let commandworkpaths: []str = ["", ""];
|
||
let commandstage: i32 = 0;
|
||
for (commandstage < commandstages.len) {
|
||
writefile(commandcompilertraces[commandstage], "");
|
||
writefile(commandassemblertraces[commandstage], "");
|
||
writefile(commandlinkertraces[commandstage], "");
|
||
let commandenv: []str = alloc([], (commandbaseenv.len + 9): u64)!;
|
||
let commandei: i32 = 0;
|
||
for (commandei < commandbaseenv.len) {
|
||
if (!strings.hasprefix(commandbaseenv[commandei], "WW_W6C=")
|
||
&& !strings.hasprefix(commandbaseenv[commandei], "WW_W6A=")
|
||
&& !strings.hasprefix(commandbaseenv[commandei], "WW_W6L=")
|
||
&& !strings.hasprefix(commandbaseenv[commandei],
|
||
"WW_PATTERN_COMPILER_TRACE=")
|
||
&& !strings.hasprefix(commandbaseenv[commandei],
|
||
"WW_PATTERN_ASSEMBLER_TRACE=")
|
||
&& !strings.hasprefix(commandbaseenv[commandei],
|
||
"WW_PATTERN_LINKER_TRACE=")
|
||
&& !strings.hasprefix(commandbaseenv[commandei],
|
||
"WW_PATTERN_REAL_COMPILER=")
|
||
&& !strings.hasprefix(commandbaseenv[commandei],
|
||
"WW_PATTERN_REAL_ASSEMBLER=")
|
||
&& !strings.hasprefix(commandbaseenv[commandei],
|
||
"WW_PATTERN_REAL_LINKER=")) {
|
||
append(commandenv, commandbaseenv[commandei]);
|
||
};
|
||
commandei += 1;
|
||
};
|
||
append(commandenv, strings.concat("WW_W6C=", commandcompilerwrapper));
|
||
append(commandenv, strings.concat("WW_W6A=", commandassemblerwrapper));
|
||
append(commandenv, strings.concat("WW_W6L=", commandlinkerwrapper));
|
||
append(commandenv, strings.concat("WW_PATTERN_COMPILER_TRACE=",
|
||
commandcompilertraces[commandstage]));
|
||
append(commandenv, strings.concat("WW_PATTERN_ASSEMBLER_TRACE=",
|
||
commandassemblertraces[commandstage]));
|
||
append(commandenv, strings.concat("WW_PATTERN_LINKER_TRACE=",
|
||
commandlinkertraces[commandstage]));
|
||
append(commandenv, strings.concat("WW_PATTERN_REAL_COMPILER=",
|
||
driver(commandcompilers[commandstage])));
|
||
append(commandenv, strings.concat("WW_PATTERN_REAL_ASSEMBLER=",
|
||
driver(commandassemblers[commandstage])));
|
||
append(commandenv, strings.concat("WW_PATTERN_REAL_LINKER=",
|
||
driver(commandlinkers[commandstage])));
|
||
|
||
let commandforward: []str = [driver(commandstages[commandstage]), "build",
|
||
"-w", commandworks[commandstage], "-I", commands,
|
||
"-L", strings.concat(repo(), "/out/lib"), "-lwcc", "-o",
|
||
commandbins[commandstage], commandspec, commandaliasspec,
|
||
commandspec, commandalpha];
|
||
let commandreverse: []str = [driver(commandstages[commandstage]), "build",
|
||
"-w", commandreverseworks[commandstage], "-I", commands,
|
||
"-L", strings.concat(repo(), "/out/lib"), "-lwcc", "-o",
|
||
commandreverseoutputs[commandstage], commandalpha, commandspec,
|
||
commandaliasspec, commandspec];
|
||
if (commandstage == 0) {
|
||
runcommandenv(root, "tree-command-output-c", commandforward, commandenv,
|
||
(60i64 * (time.second: i64)): time.duration, &outc);
|
||
expectexit(&outc, 0);
|
||
} else {
|
||
runcommandenv(root, "tree-command-output-ww", commandforward, commandenv,
|
||
(60i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outw, 0);
|
||
};
|
||
let commandstdout: str;
|
||
let commandstderr: str;
|
||
if (commandstage == 0) {
|
||
commandstdout = strings.dup(outc.stdout);
|
||
commandstderr = strings.dup(outc.stderr);
|
||
} else {
|
||
commandstdout = strings.dup(outw.stdout);
|
||
commandstderr = strings.dup(outw.stderr);
|
||
};
|
||
let commandalphabin: str = strings.dup(readfile(strings.concat(
|
||
commandbins[commandstage], "/cmd-alpha")));
|
||
let commandzetabin: str = strings.dup(readfile(strings.concat(
|
||
commandbins[commandstage], "/cmd-zeta")));
|
||
let commandwork: str = strings.concat(commandworks[commandstage], "/");
|
||
commandworkpaths[commandstage] = strings.dup(commandwork);
|
||
let commandctrace: str = readfile(commandcompilertraces[commandstage]);
|
||
let commandatrace: str = readfile(commandassemblertraces[commandstage]);
|
||
let commandltrace: str = readfile(commandlinkertraces[commandstage]);
|
||
assert(occurrences(commandctrace, "\n") == 3);
|
||
assert(occurrences(commandatrace, "\n") == 5);
|
||
assert(occurrences(commandltrace, "\n") == 2);
|
||
let commandalphacompile: str = linecontaining(commandctrace,
|
||
strings.concat(commandalphaid, ".unit.new"));
|
||
assert(occurrences(commandalphacompile, strings.concat(
|
||
"<--import><library><", commandwork, "library.wwi.new>")) == 1);
|
||
assert(!has(commandalphacompile, "cmd-zeta.wwi"));
|
||
let alphaassembly: str = strings.concat("BEGIN<-o><", commandwork,
|
||
commandalphaid, ".init.o.new><", commandwork, commandalphaid,
|
||
".init.s.new>");
|
||
let zetaassembly: str = strings.concat("BEGIN<-o><", commandwork,
|
||
commandzetaid, ".init.o.new><", commandwork, commandzetaid,
|
||
".init.s.new>");
|
||
assert(same(linecontaining(commandatrace, strings.concat(commandalphaid,
|
||
".init.o.new")), alphaassembly));
|
||
assert(same(linecontaining(commandatrace, strings.concat(commandzetaid,
|
||
".init.o.new")), zetaassembly));
|
||
let commandalphalink: str = linecontaining(commandltrace,
|
||
strings.concat("<-o><", commandbins[commandstage], "/cmd-alpha.new>"));
|
||
let commandzetalink: str = linecontaining(commandltrace,
|
||
strings.concat("<-o><", commandbins[commandstage], "/cmd-zeta.new>"));
|
||
assert(occurrences(commandalphalink, strings.concat("<", commandwork,
|
||
commandalphaid, ".a.new>")) == 1);
|
||
assert(occurrences(commandalphalink, strings.concat("<", commandwork,
|
||
"library.a.new>")) == 1);
|
||
assert(!has(commandalphalink, strings.concat(commandzetaid, ".a")));
|
||
assert(!has(commandalphalink, ".wwi>"));
|
||
assert(has(commandalphalink, strings.concat("<-L><", repo(),
|
||
"/out/lib><-l><wcc>")));
|
||
assert(has(commandzetalink, strings.concat("<", commandwork,
|
||
commandzetaid, ".a.new>")));
|
||
assert(!has(commandzetalink, "library.a"));
|
||
assert(!has(commandzetalink, ".wwi>"));
|
||
let normalizedcommandcompiler: str = normalizedtrace(commandctrace,
|
||
commandwork, commandbins[commandstage]);
|
||
let normalizedcommandassembler: str = normalizedtrace(commandatrace,
|
||
commandwork, commandbins[commandstage]);
|
||
let normalizedcommandlinker: str = normalizedtrace(commandltrace,
|
||
commandwork, commandbins[commandstage]);
|
||
if (commandstage == 0) {
|
||
referencecommandcompiler = strings.dup(normalizedcommandcompiler);
|
||
referencecommandassembler = strings.dup(normalizedcommandassembler);
|
||
referencecommandlinker = strings.dup(normalizedcommandlinker);
|
||
} else {
|
||
assert(same(referencecommandcompiler, normalizedcommandcompiler));
|
||
assert(same(referencecommandassembler, normalizedcommandassembler));
|
||
assert(same(referencecommandlinker, normalizedcommandlinker));
|
||
};
|
||
// Reverse the identical request multiset into an independent cold
|
||
// work/output root inside this stage. Canonical product ordering leaves
|
||
// diagnostics, normalized tool argv, artifacts, and binaries fixed.
|
||
rewritefile(commandcompilertraces[commandstage], "");
|
||
rewritefile(commandassemblertraces[commandstage], "");
|
||
rewritefile(commandlinkertraces[commandstage], "");
|
||
if (commandstage == 0) {
|
||
runcommandenv(root, "tree-command-reverse-c", commandreverse, commandenv,
|
||
(60i64 * (time.second: i64)): time.duration, &outc);
|
||
expectexit(&outc, 0);
|
||
assert(same(commandstdout, outc.stdout));
|
||
assert(same(commandstderr, outc.stderr));
|
||
} else {
|
||
runcommandenv(root, "tree-command-reverse-ww", commandreverse, commandenv,
|
||
(60i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outw, 0);
|
||
assert(same(commandstdout, outw.stdout));
|
||
assert(same(commandstderr, outw.stderr));
|
||
};
|
||
let reversework: str = strings.concat(
|
||
commandreverseworks[commandstage], "/");
|
||
let reversectrace: str = readfile(commandcompilertraces[commandstage]);
|
||
let reverseatrace: str = readfile(commandassemblertraces[commandstage]);
|
||
let reverseltrace: str = readfile(commandlinkertraces[commandstage]);
|
||
assert(same(normalizedcommandcompiler, normalizedtrace(reversectrace,
|
||
reversework, commandreversebins[commandstage])));
|
||
assert(same(normalizedcommandassembler, normalizedtrace(reverseatrace,
|
||
reversework, commandreversebins[commandstage])));
|
||
assert(same(normalizedcommandlinker, normalizedtrace(reverseltrace,
|
||
reversework, commandreversebins[commandstage])));
|
||
let reverseactions: []str = [commandalphaid, commandzetaid, "library"];
|
||
let reversesuffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"];
|
||
let rai: i32 = 0;
|
||
for (rai < reverseactions.len) {
|
||
let rsi: i32 = 0;
|
||
for (rsi < reversesuffixes.len) {
|
||
assert(same(readfile(strings.concat(commandwork,
|
||
reverseactions[rai], reversesuffixes[rsi])),
|
||
readfile(strings.concat(reversework, reverseactions[rai],
|
||
reversesuffixes[rsi]))));
|
||
rsi += 1;
|
||
};
|
||
rai += 1;
|
||
};
|
||
assert(same(commandalphabin, readfile(strings.concat(
|
||
commandreversebins[commandstage], "/cmd-alpha"))));
|
||
assert(same(commandzetabin, readfile(strings.concat(
|
||
commandreversebins[commandstage], "/cmd-zeta"))));
|
||
|
||
// Return to the forward store: a warm equivalent request invokes no
|
||
// compiler or assembler before the direct-form reuse check below.
|
||
rewritefile(commandcompilertraces[commandstage], "");
|
||
rewritefile(commandassemblertraces[commandstage], "");
|
||
rewritefile(commandlinkertraces[commandstage], "");
|
||
if (commandstage == 0) {
|
||
runcommandenv(root, "tree-command-warm-c", commandforward, commandenv,
|
||
(60i64 * (time.second: i64)): time.duration, &outc);
|
||
expectexit(&outc, 0);
|
||
} else {
|
||
runcommandenv(root, "tree-command-warm-ww", commandforward, commandenv,
|
||
(60i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outw, 0);
|
||
};
|
||
assert(readfile(commandcompilertraces[commandstage]).len == 0);
|
||
assert(readfile(commandassemblertraces[commandstage]).len == 0);
|
||
let directbin: str = strings.concat(root, "/direct-",
|
||
commandstages[commandstage]);
|
||
let directav: []str = [driver(commandstages[commandstage]), "build",
|
||
"-w", commandworks[commandstage], "-I", commands,
|
||
"-L", strings.concat(repo(), "/out/lib"), "-lwcc",
|
||
"-o", directbin, commandalpha];
|
||
if (commandstage == 0) {
|
||
runcommandenv(root, "tree-command-direct-c", directav, commandenv,
|
||
(60i64 * (time.second: i64)): time.duration, &outc);
|
||
expectexit(&outc, 0);
|
||
} else {
|
||
runcommandenv(root, "tree-command-direct-ww", directav, commandenv,
|
||
(60i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outw, 0);
|
||
};
|
||
assert(readfile(commandcompilertraces[commandstage]).len == 0);
|
||
assert(readfile(commandassemblertraces[commandstage]).len == 0);
|
||
assert(same(commandalphabin, readfile(directbin)));
|
||
|
||
rewritefile(commandcompilertraces[commandstage], "");
|
||
rewritefile(commandassemblertraces[commandstage], "");
|
||
rewritefile(commandlinkertraces[commandstage], "");
|
||
let commandasm: []str = [driver(commandstages[commandstage]), "build",
|
||
"-S", "-w", commandasmworks[commandstage], "-I", commands,
|
||
commandspec, commandaliasspec, commandalpha];
|
||
if (commandstage == 0) {
|
||
runcommandenv(root, "tree-command-asm-c", commandasm, commandenv,
|
||
(60i64 * (time.second: i64)): time.duration, &outc);
|
||
expectexit(&outc, 0);
|
||
} else {
|
||
runcommandenv(root, "tree-command-asm-ww", commandasm, commandenv,
|
||
(60i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outw, 0);
|
||
};
|
||
let commandasmwork: str = strings.concat(commandasmworks[commandstage],
|
||
"/");
|
||
let commandasmtrace: str = readfile(commandcompilertraces[commandstage]);
|
||
assert(occurrences(commandasmtrace, "\n") == 3);
|
||
assert(readfile(commandassemblertraces[commandstage]).len == 0);
|
||
assert(readfile(commandlinkertraces[commandstage]).len == 0);
|
||
assert(os.exists(strings.concat(commandasmwork, commandalphaid, ".s")));
|
||
assert(os.exists(strings.concat(commandasmwork, commandzetaid, ".s")));
|
||
assert(os.exists(strings.concat(commandasmwork, "library.s")));
|
||
let normalizedasmcompiler: str = normalizedtrace(commandasmtrace,
|
||
commandasmwork, commandbins[commandstage]);
|
||
if (commandstage == 0) {
|
||
referenceasmcompiler = strings.dup(normalizedasmcompiler);
|
||
} else { assert(same(referenceasmcompiler, normalizedasmcompiler)); };
|
||
if (commandstage == 0) {
|
||
runcommandenv(root, "tree-command-asm-warm-c", commandasm, commandenv,
|
||
(60i64 * (time.second: i64)): time.duration, &outc);
|
||
expectexit(&outc, 0);
|
||
} else {
|
||
runcommandenv(root, "tree-command-asm-warm-ww", commandasm, commandenv,
|
||
(60i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outw, 0);
|
||
};
|
||
assert(same(commandasmtrace,
|
||
readfile(commandcompilertraces[commandstage])));
|
||
rewritefile(commandcompilertraces[commandstage], "");
|
||
rewritefile(commandassemblertraces[commandstage], "");
|
||
rewritefile(commandlinkertraces[commandstage], "");
|
||
let commandasmnowork: []str = [driver(commandstages[commandstage]),
|
||
"build", "-S", "-I", commands, commandspec];
|
||
if (commandstage == 0) {
|
||
runcommandenv(root, "tree-command-asm-no-work-c", commandasmnowork,
|
||
commandenv, time.second, &outc);
|
||
expectexit(&outc, 2);
|
||
referenceasmrejectdiag = strings.dup(outc.stderr);
|
||
} else {
|
||
runcommandenv(root, "tree-command-asm-no-work-ww", commandasmnowork,
|
||
commandenv, time.second, &outw);
|
||
expectexit(&outw, 2);
|
||
assert(same(referenceasmrejectdiag, outw.stderr));
|
||
};
|
||
assert(has(referenceasmrejectdiag,
|
||
"wwtest package: recursive -S needs -w\n"));
|
||
assert(readfile(commandcompilertraces[commandstage]).len == 0);
|
||
assert(readfile(commandassemblertraces[commandstage]).len == 0);
|
||
assert(readfile(commandlinkertraces[commandstage]).len == 0);
|
||
|
||
rewritefile(commandcompilertraces[commandstage], "");
|
||
rewritefile(commandassemblertraces[commandstage], "");
|
||
rewritefile(commandlinkertraces[commandstage], "");
|
||
let collisionav: []str = [driver(commandstages[commandstage]), "build",
|
||
"-w", collisionworks[commandstage], "-o", collisionout,
|
||
collisionspec];
|
||
if (commandstage == 0) {
|
||
runcommandenv(root, "tree-collision-c", collisionav, commandenv,
|
||
time.second, &outc);
|
||
expectexit(&outc, 1);
|
||
referencecollisiondiag = strings.dup(outc.stderr);
|
||
} else {
|
||
runcommandenv(root, "tree-collision-ww", collisionav, commandenv,
|
||
time.second, &outw);
|
||
expectexit(&outw, 1);
|
||
assert(same(referencecollisiondiag, outw.stderr));
|
||
};
|
||
assert(readfile(commandcompilertraces[commandstage]).len == 0);
|
||
assert(readfile(commandassemblertraces[commandstage]).len == 0);
|
||
assert(readfile(commandlinkertraces[commandstage]).len == 0);
|
||
assert(!os.exists(collisionworks[commandstage]));
|
||
|
||
rewritefile(commandcompilertraces[commandstage], "");
|
||
rewritefile(commandassemblertraces[commandstage], "");
|
||
rewritefile(commandlinkertraces[commandstage], "");
|
||
let fanoutpathav: []str = [driver(commandstages[commandstage]),
|
||
"build", "-o", longoutputdir, "-I", commands, commandspec];
|
||
if (commandstage == 0) {
|
||
runcommandenv(root, "tree-fanout-path-c", fanoutpathav, commandenv,
|
||
time.second, &outc);
|
||
expectexit(&outc, 1);
|
||
referencefanoutpathdiag = strings.dup(outc.stderr);
|
||
} else {
|
||
runcommandenv(root, "tree-fanout-path-ww", fanoutpathav, commandenv,
|
||
time.second, &outw);
|
||
expectexit(&outw, 1);
|
||
assert(same(referencefanoutpathdiag, outw.stderr));
|
||
};
|
||
assert(has(referencefanoutpathdiag,
|
||
"ww: command output path is too long\n"));
|
||
assert(readfile(commandcompilertraces[commandstage]).len == 0);
|
||
assert(readfile(commandassemblertraces[commandstage]).len == 0);
|
||
assert(readfile(commandlinkertraces[commandstage]).len == 0);
|
||
|
||
let scratchpathav: []str = [driver(commandstages[commandstage]),
|
||
"build", "-o", longscratchout, "-I", commands, commandalpha];
|
||
if (commandstage == 0) {
|
||
runcommandenv(root, "tree-scratch-path-c", scratchpathav, commandenv,
|
||
time.second, &outc);
|
||
expectexit(&outc, 1);
|
||
referencescratchpathdiag = strings.dup(outc.stderr);
|
||
} else {
|
||
runcommandenv(root, "tree-scratch-path-ww", scratchpathav, commandenv,
|
||
time.second, &outw);
|
||
expectexit(&outw, 1);
|
||
assert(same(referencescratchpathdiag, outw.stderr));
|
||
};
|
||
assert(has(referencescratchpathdiag,
|
||
"ww: scratch path is too long\n"));
|
||
assert(readfile(commandcompilertraces[commandstage]).len == 0);
|
||
assert(readfile(commandassemblertraces[commandstage]).len == 0);
|
||
assert(readfile(commandlinkertraces[commandstage]).len == 0);
|
||
|
||
let workpathav: []str = [driver(commandstages[commandstage]),
|
||
"build", "-w", longworkdir, "-I", commands, commandalpha];
|
||
if (commandstage == 0) {
|
||
runcommandenv(root, "tree-work-path-c", workpathav, commandenv,
|
||
time.second, &outc);
|
||
expectexit(&outc, 1);
|
||
referenceworkpathdiag = strings.dup(outc.stderr);
|
||
} else {
|
||
runcommandenv(root, "tree-work-path-ww", workpathav, commandenv,
|
||
time.second, &outw);
|
||
expectexit(&outw, 1);
|
||
assert(same(referenceworkpathdiag, outw.stderr));
|
||
};
|
||
assert(has(referenceworkpathdiag,
|
||
"ww: workdir path is too long\n"));
|
||
assert(readfile(commandcompilertraces[commandstage]).len == 0);
|
||
assert(readfile(commandassemblertraces[commandstage]).len == 0);
|
||
assert(readfile(commandlinkertraces[commandstage]).len == 0);
|
||
commandstage += 1;
|
||
};
|
||
assert(has(referencecollisiondiag,
|
||
"ww: multiple commands produce output basename \"same\""));
|
||
assert(!os.exists(strings.concat(collisionout, "/same")));
|
||
let commandactions: []str = [commandalphaid, commandzetaid, "library"];
|
||
let commandsuffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"];
|
||
let commandaction: i32 = 0;
|
||
for (commandaction < commandactions.len) {
|
||
let commandsuffix: i32 = 0;
|
||
for (commandsuffix < commandsuffixes.len) {
|
||
assert(same(readfile(strings.concat(commandworkpaths[0],
|
||
commandactions[commandaction], commandsuffixes[commandsuffix])),
|
||
readfile(strings.concat(commandworkpaths[1],
|
||
commandactions[commandaction], commandsuffixes[commandsuffix]))));
|
||
commandsuffix += 1;
|
||
};
|
||
commandaction += 1;
|
||
};
|
||
assert(os.exists(strings.concat(binc, "/cmd-alpha")));
|
||
assert(os.exists(strings.concat(binc, "/cmd-zeta")));
|
||
assert(os.exists(strings.concat(binw, "/cmd-alpha")));
|
||
assert(os.exists(strings.concat(binw, "/cmd-zeta")));
|
||
assert(!os.exists(strings.concat(binc, "/library")));
|
||
assert(!os.exists(strings.concat(binw, "/library")));
|
||
assert(same(readfile(strings.concat(binc, "/cmd-alpha")),
|
||
readfile(strings.concat(binw, "/cmd-alpha"))));
|
||
assert(same(readfile(strings.concat(binc, "/cmd-zeta")),
|
||
readfile(strings.concat(binw, "/cmd-zeta"))));
|
||
// `--` ends build option parsing and every following argument remains a
|
||
// package selector. The coordinator receives and builds that direct root.
|
||
let dashworks: []str = [strings.concat(root, "/dash-work-c"),
|
||
strings.concat(root, "/dash-work-ww")];
|
||
let dashbins: []str = [strings.concat(root, "/dash-bin-c"),
|
||
strings.concat(root, "/dash-bin-ww")];
|
||
let dashtrailworks: []str = [strings.concat(root, "/dash-trail-work-c"),
|
||
strings.concat(root, "/dash-trail-work-ww")];
|
||
let dashtrailbins: []str = [strings.concat(root, "/dash-trail-bin-c"),
|
||
strings.concat(root, "/dash-trail-bin-ww")];
|
||
let referencedashstdout: str = "";
|
||
let referencedashstderr: str = "";
|
||
let dashstage: i32 = 0;
|
||
for (dashstage < commandstages.len) {
|
||
let dashav: []str = [driver(commandstages[dashstage]), "build",
|
||
"-w", dashworks[dashstage], "-I", commands, "-o",
|
||
dashbins[dashstage], "--", dashselector];
|
||
if (dashstage == 0) {
|
||
runcommanddir(root, "tree-dash-c", root, dashav,
|
||
(30i64 * (time.second: i64)): time.duration, &outc);
|
||
expectexit(&outc, 0);
|
||
referencedashstdout = strings.dup(outc.stdout);
|
||
referencedashstderr = strings.dup(outc.stderr);
|
||
} else {
|
||
runcommanddir(root, "tree-dash-ww", root, dashav,
|
||
(30i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outw, 0);
|
||
assert(same(referencedashstdout, outw.stdout));
|
||
assert(same(referencedashstderr, outw.stderr));
|
||
};
|
||
assert(os.exists(strings.concat(dashworks[dashstage], "/",
|
||
commandalphaid, ".unit.ww")));
|
||
// Once the first positional has stopped flag parsing, a later `--` is
|
||
// another package argument, not a second terminator.
|
||
let dashtrailav: []str = [driver(commandstages[dashstage]), "build",
|
||
"-w", dashtrailworks[dashstage], "-I", commands, "-o",
|
||
dashtrailbins[dashstage], commandalpha, "--"];
|
||
if (dashstage == 0) {
|
||
runcommand(root, "tree-dash-trailing-c", dashtrailav,
|
||
time.second, &outc);
|
||
expectexit(&outc, 1);
|
||
assert(has(outc.stderr,
|
||
"--: cannot canonicalize package directory\n"));
|
||
} else {
|
||
runcommand(root, "tree-dash-trailing-ww", dashtrailav,
|
||
time.second, &outw);
|
||
expectexit(&outw, 1);
|
||
assert(same(outc.stdout, outw.stdout));
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(has(outw.stderr,
|
||
"--: cannot canonicalize package directory\n"));
|
||
};
|
||
assert(!os.exists(dashtrailworks[dashstage]));
|
||
assert(!os.exists(dashtrailbins[dashstage]));
|
||
dashstage += 1;
|
||
};
|
||
assert(same(readfile(dashbins[0]), readfile(dashbins[1])));
|
||
let commandrun: []str = [strings.concat(binc, "/cmd-alpha")];
|
||
runcommand(root, "tree-command-run", commandrun, time.second, &outc);
|
||
expectexit(&outc, 0);
|
||
let nomainpattern: str = strings.concat(commandlib, "/...");
|
||
let nomainc: []str = [driver("ww"), "build", "-o", binc, nomainpattern];
|
||
let nomainw: []str = [driver("ww_ww"), "build", "-o", binw, nomainpattern];
|
||
runcommand(root, "tree-no-main-c", nomainc, time.second, &outc);
|
||
runcommand(root, "tree-no-main-ww", nomainw, time.second, &outw);
|
||
expectexit(&outc, 1);
|
||
expectexit(&outw, 1);
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(has(outc.stderr, "ww: no main packages to build\n"));
|
||
assert(has(seq, strings.concat("ok ", tree,
|
||
" [rootpkg]\n")));
|
||
assert(has(seq, strings.concat("ok ", tree,
|
||
"/alpha [alpha]\n")));
|
||
assert(has(seq, strings.concat("ok ", tree,
|
||
"/app [app]\n")));
|
||
assert(has(seq, strings.concat("ok ", tree,
|
||
"/beta/inner [inner]\n")));
|
||
assert(has(seq, strings.concat("ok ", tree,
|
||
"/domain/app [app]\n")));
|
||
assert(has(seq, strings.concat("? ", tree,
|
||
"/gamma [no tests]\n")));
|
||
assert(has(seq, strings.concat("? ", tree,
|
||
"/vendor [no tests]\n")));
|
||
assert(has(seq, strings.concat("? ", tree,
|
||
"/vendorx [no tests]\n")));
|
||
let rootreport: i32 = pos(seq, strings.concat("ok ", tree,
|
||
" [rootpkg]\n"));
|
||
let alphareport: i32 = pos(seq, strings.concat("ok ", tree,
|
||
"/alpha [alpha]\n"));
|
||
let appreport: i32 = pos(seq, strings.concat("ok ", tree,
|
||
"/app [app]\n"));
|
||
let innerreport: i32 = pos(seq, strings.concat("ok ", tree,
|
||
"/beta/inner [inner]\n"));
|
||
let domainreport: i32 = pos(seq, strings.concat("ok ", tree,
|
||
"/domain/app [app]\n"));
|
||
let gammareport: i32 = pos(seq, strings.concat("? ", tree,
|
||
"/gamma [no tests]\n"));
|
||
let vendorreport: i32 = pos(seq, strings.concat("? ", tree,
|
||
"/vendor [no tests]\n"));
|
||
let vendorxreport: i32 = pos(seq, strings.concat("? ", tree,
|
||
"/vendorx [no tests]\n"));
|
||
assert(rootreport >= 0 && rootreport < alphareport
|
||
&& alphareport < appreport && appreport < innerreport
|
||
&& innerreport < domainreport && domainreport < gammareport
|
||
&& gammareport < vendorreport && vendorreport < vendorxreport);
|
||
assert(occurrences(seq, strings.concat(tree, " [")) == 1);
|
||
assert(occurrences(seq, strings.concat("ok ", tree,
|
||
"/alpha [alpha]\n")) == 1);
|
||
assert(occurrences(seq, strings.concat("ok ", tree,
|
||
"/app [app]\n")) == 1);
|
||
assert(occurrences(seq, strings.concat("ok ", tree,
|
||
"/beta/inner [inner]\n")) == 1);
|
||
assert(occurrences(seq, strings.concat("ok ", tree,
|
||
"/domain/app [app]\n")) == 1);
|
||
assert(occurrences(seq, strings.concat("? ", tree,
|
||
"/gamma [no tests]\n")) == 1);
|
||
assert(occurrences(seq, strings.concat("? ", tree,
|
||
"/vendor [no tests]\n")) == 1);
|
||
assert(occurrences(seq, strings.concat("? ", tree,
|
||
"/vendorx [no tests]\n")) == 1);
|
||
assert(!has(seq, ".hidden"));
|
||
assert(!has(seq, "_skip"));
|
||
assert(!has(seq, "/testdata"));
|
||
assert(!has(seq, "/vendor/bad"));
|
||
assert(!has(seq, "/app/vendor/lib/math"));
|
||
assert(!has(seq, outside));
|
||
|
||
let buildworkc: str = strings.concat(root, "/build-work-c");
|
||
let buildworkw: str = strings.concat(root, "/build-work-ww");
|
||
let buildc: []str = [driver("ww"), "build", "-w", buildworkc,
|
||
"-I", tree, spec];
|
||
let buildw: []str = [driver("ww_ww"), "build", "-w", buildworkw,
|
||
"-I", tree, spec];
|
||
runcommand(root, "tree-build-c", buildc,
|
||
(30i64 * (time.second: i64)): time.duration, &outc);
|
||
runcommand(root, "tree-build-ww", buildw,
|
||
(30i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 0);
|
||
expectexit(&outw, 0);
|
||
assert(same(outc.stdout, outw.stdout));
|
||
assert(same(outc.stderr, outw.stderr));
|
||
let buildactions: []str = [localidentity(tree, "rootpkg"), "alpha",
|
||
"app", "domain.app", "gamma", "vendor", "vendorx"];
|
||
let bai: i32 = 0;
|
||
for (bai < buildactions.len) {
|
||
let cunit: str = readfile(strings.concat(buildworkc, "/",
|
||
buildactions[bai], ".unit.ww"));
|
||
assert(same(cunit, readfile(strings.concat(buildworkw, "/",
|
||
buildactions[bai], ".unit.ww"))));
|
||
bai += 1;
|
||
};
|
||
assert(!os.exists(strings.concat(buildworkc,
|
||
"/beta.inner.unit.ww")));
|
||
assert(!os.exists(strings.concat(buildworkw,
|
||
"/beta.inner.unit.ww")));
|
||
assert(!os.exists(strings.concat(buildworkc,
|
||
"/alpha-internal-test.unit.ww")));
|
||
assert(!os.exists(strings.concat(buildworkw,
|
||
"/alpha-internal-test.unit.ww")));
|
||
|
||
// Concurrent scheduling must not reorder the emitted byte stream.
|
||
let j4: []str = [driver("ww"), "test", "-j", "4", "-I", tree, spec];
|
||
runcommand(root, "tree-j4", j4,
|
||
(30i64 * (time.second: i64)): time.duration, &outc);
|
||
expectexit(&outc, 0);
|
||
assert(same(outc.stdout, seq));
|
||
|
||
let j0: []str = [driver("ww"), "test", "-j", "0", spec];
|
||
runcommand(root, "tree-j0", j0, time.second, &outc);
|
||
expectexit(&outc, 2);
|
||
assert(has(outc.stderr, "usage: wwtest package"));
|
||
|
||
let overlapc: []str = [driver("ww"), "test", "-I", tree,
|
||
strings.concat(tree, "/alpha"), spec, spec,
|
||
strings.concat(alias, "/...")];
|
||
let overlapcreverse: []str = [driver("ww"), "test", "-I", tree,
|
||
strings.concat(alias, "/..."), spec, spec,
|
||
strings.concat(tree, "/alpha")];
|
||
let overlapw: []str = [driver("ww_ww"), "test", "-I", tree,
|
||
strings.concat(alias, "/..."), spec, spec,
|
||
strings.concat(tree, "/alpha")];
|
||
let overlapwreverse: []str = [driver("ww_ww"), "test", "-I", tree,
|
||
strings.concat(tree, "/alpha"), spec, spec,
|
||
strings.concat(alias, "/...")];
|
||
runcommand(root, "tree-overlap-c", overlapc,
|
||
(30i64 * (time.second: i64)): time.duration, &outc);
|
||
runcommand(root, "tree-overlap-ww", overlapw,
|
||
(30i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 0);
|
||
expectexit(&outw, 0);
|
||
assert(same(outc.stdout, seq));
|
||
assert(same(outw.stdout, seq));
|
||
assert(same(outc.stderr, outw.stderr));
|
||
let overlapstderr: str = strings.dup(outc.stderr);
|
||
runcommand(root, "tree-overlap-reverse-c", overlapcreverse,
|
||
(30i64 * (time.second: i64)): time.duration, &outc);
|
||
runcommand(root, "tree-overlap-reverse-ww", overlapwreverse,
|
||
(30i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 0);
|
||
expectexit(&outw, 0);
|
||
assert(same(outc.stdout, seq));
|
||
assert(same(outw.stdout, seq));
|
||
assert(same(outc.stderr, overlapstderr));
|
||
assert(same(outw.stderr, overlapstderr));
|
||
|
||
// The first ellipsis need not be a path element. With cwd as the implicit
|
||
// traversal root, matching uses cleaned relative candidates rather than an
|
||
// accidental "./" prefix.
|
||
let middlec: []str = [driver("ww"), "test", "-I", tree, "tr.../alpha"];
|
||
let middlew: []str = [driver("ww_ww"), "test", "-I", tree,
|
||
"tr.../alpha"];
|
||
runcommanddir(root, "tree-middle-pattern-c", root, middlec,
|
||
(30i64 * (time.second: i64)): time.duration, &outc);
|
||
runcommanddir(root, "tree-middle-pattern-ww", root, middlew,
|
||
(30i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 0);
|
||
expectexit(&outw, 0);
|
||
assert(same(outc.stdout, outw.stdout));
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(has(outc.stdout, strings.concat("ok ", tree,
|
||
"/alpha [alpha]\n")));
|
||
assert(!has(outc.stdout, strings.concat("ok ", tree,
|
||
" [rootpkg]\n")));
|
||
|
||
// The recursive exclusions are traversal rules, not bans on direct roots.
|
||
let directc: []str = [driver("ww"), "test", "-I", tree,
|
||
strings.concat(tree, "/.hidden"), strings.concat(tree, "/_skip"),
|
||
strings.concat(tree, "/testdata/bad")];
|
||
let directw: []str = [driver("ww_ww"), "test", "-I", tree,
|
||
strings.concat(tree, "/testdata/bad"), strings.concat(tree, "/_skip"),
|
||
strings.concat(tree, "/.hidden")];
|
||
runcommand(root, "tree-direct-exclusions-c", directc,
|
||
(30i64 * (time.second: i64)): time.duration, &outc);
|
||
runcommand(root, "tree-direct-exclusions-ww", directw,
|
||
(30i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 0);
|
||
expectexit(&outw, 0);
|
||
assert(same(outc.stdout, outw.stdout));
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(has(outc.stdout, strings.concat("ok ", tree,
|
||
"/.hidden [hid]\n")));
|
||
assert(has(outc.stdout, strings.concat("ok ", tree,
|
||
"/_skip [_skip]\n")));
|
||
assert(has(outc.stdout, strings.concat("ok ", tree,
|
||
"/testdata/bad [bad]\n")));
|
||
|
||
// A generic wildcard selects a code-bearing terminal vendor directory but
|
||
// cannot consume it. Rooting the pattern at vendor selects its descendants;
|
||
// a second non-terminal vendor remains a barrier.
|
||
let vendorspec: str = strings.concat(tree, "/vendor/...");
|
||
let vendorc: []str = [driver("ww"), "test", "-I", tree, vendorspec];
|
||
let vendorw: []str = [driver("ww_ww"), "test", "-I", tree, vendorspec];
|
||
runcommand(root, "tree-explicit-vendor-c", vendorc,
|
||
(30i64 * (time.second: i64)): time.duration, &outc);
|
||
runcommand(root, "tree-explicit-vendor-ww", vendorw,
|
||
(30i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 0);
|
||
expectexit(&outw, 0);
|
||
assert(same(outc.stdout, outw.stdout));
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(has(outc.stdout, strings.concat("? ", tree,
|
||
"/vendor [no tests]\n")));
|
||
assert(has(outc.stdout, strings.concat("ok ", tree,
|
||
"/vendor/bad [bad]\n")));
|
||
assert(!has(outc.stdout, "/vendor/bad/vendor/deep"));
|
||
|
||
let vendordirectc: []str = [driver("ww"), "test", "-I", tree,
|
||
strings.concat(tree, "/app/vendor/lib/math")];
|
||
let vendordirectw: []str = [driver("ww_ww"), "test", "-I", tree,
|
||
strings.concat(tree, "/app/vendor/lib/math")];
|
||
runcommand(root, "tree-direct-below-vendor-c", vendordirectc,
|
||
(30i64 * (time.second: i64)): time.duration, &outc);
|
||
runcommand(root, "tree-direct-below-vendor-ww", vendordirectw,
|
||
(30i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 0);
|
||
expectexit(&outw, 0);
|
||
assert(same(outc.stdout, outw.stdout));
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(has(outc.stdout, strings.concat("? ", tree,
|
||
"/app/vendor/lib/math [no tests]\n")));
|
||
let vendorroots: []str = [driver("ww"), "test", "-I", tree,
|
||
strings.concat(tree, "/app/vendor/...")];
|
||
runcommand(root, "tree-pattern-below-vendor", vendorroots,
|
||
(30i64 * (time.second: i64)): time.duration, &outc);
|
||
expectexit(&outc, 0);
|
||
assert(has(outc.stdout, strings.concat("? ", tree,
|
||
"/app/vendor/lib/math [no tests]\n")));
|
||
|
||
// A child symlink is never traversed. The same symlink is followed when it
|
||
// is itself the explicit traversal root, then canonical output names it once.
|
||
let escapec: []str = [driver("ww"), "test", "-I", root,
|
||
strings.concat(tree, "/escape/...")];
|
||
let escapew: []str = [driver("ww_ww"), "test", "-I", root,
|
||
strings.concat(tree, "/escape/...")];
|
||
runcommand(root, "tree-explicit-symlink-root-c", escapec,
|
||
(30i64 * (time.second: i64)): time.duration, &outc);
|
||
runcommand(root, "tree-explicit-symlink-root-ww", escapew,
|
||
(30i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 0);
|
||
expectexit(&outw, 0);
|
||
assert(same(outc.stdout, outw.stdout));
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(has(outc.stdout, strings.concat("ok ", outside,
|
||
" [outside]\n")));
|
||
|
||
let oc: []str = [driver("ww"), "test", "-o",
|
||
strings.concat(root, "/stem"), spec];
|
||
let ow: []str = [driver("ww_ww"), "test", "-o",
|
||
strings.concat(root, "/stem"), spec];
|
||
runcommand(root, "tree-o-c", oc,
|
||
(30i64 * (time.second: i64)): time.duration, &outc);
|
||
runcommand(root, "tree-o-ww", ow,
|
||
(30i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 1);
|
||
expectexit(&outw, 1);
|
||
assert(outc.stdout.len == 0 && outw.stdout.len == 0);
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(has(outc.stderr,
|
||
"with multiple packages, -o must refer to a directory or /dev/null"));
|
||
assert(!os.exists(strings.concat(root, "/stem")));
|
||
|
||
// -w on a package target forwards to the coordinator: one shared
|
||
// persistent driver workdir for the complete selected request,
|
||
// reuse owned by the driver's content-identity contract. The
|
||
// byte stream must match the plain run, cold and warm.
|
||
let wdroot: str = strings.concat(root, "/wd");
|
||
assert(os.mkdir(wdroot, 448i32) == 0);
|
||
let wdc: []str = [driver("ww"), "test", "-w", wdroot,
|
||
"-I", tree, spec];
|
||
let wdw: []str = [driver("ww_ww"), "test", "-w", wdroot,
|
||
"-I", tree, spec];
|
||
runcommand(root, "tree-w-c", wdc,
|
||
(30i64 * (time.second: i64)): time.duration, &outc);
|
||
let wdkey: str = wdroot;
|
||
let expandedmath: str = "app.vendor.lib.math";
|
||
let expandedancestor: str = "domain.vendor.dep";
|
||
assert(os.exists(strings.concat(wdkey, "/", expandedmath, ".unit.ww")));
|
||
assert(os.exists(strings.concat(wdkey, "/", expandedancestor, ".unit.ww")));
|
||
assert(!os.exists(strings.concat(wdkey, "/math.unit.ww")));
|
||
let appunit: str = readfile(strings.concat(wdkey,
|
||
"/app-internal-test.unit.ww"));
|
||
assert(has(appunit, strings.concat("//ww:import-map lib.math ",
|
||
expandedmath, " ")));
|
||
assert(has(readfile(strings.concat(wdkey, "/", expandedmath,
|
||
".unit.ww")), strings.concat("//ww:module-reset ",
|
||
expandedmath, "\n")));
|
||
let ancestorunit: str = readfile(strings.concat(wdkey,
|
||
"/domain.app-internal-test.unit.ww"));
|
||
assert(has(ancestorunit, strings.concat("//ww:import-map dep ",
|
||
expandedancestor, " ")));
|
||
assert(has(readfile(strings.concat(wdkey, "/", expandedancestor,
|
||
".unit.ww")), strings.concat("//ww:module-reset ",
|
||
expandedancestor, "\n")));
|
||
let alphaunit: str = readfile(strings.concat(wdkey, "/alpha.unit.ww"));
|
||
assert(pos(alphaunit, "export fn value()")
|
||
< pos(alphaunit, "export fn linked()"));
|
||
assert(same(readfile(strings.concat(wdkey, "/.wwtool.stamp")),
|
||
"ww workdir fmt 19 mode test asm 0\n"));
|
||
let directorymains: []str = [strings.concat(localidentity(tree,
|
||
"rootpkg"), "-test-main"),
|
||
"alpha-test-main", "app-test-main", "beta.inner-test-main",
|
||
"domain.app-test-main"];
|
||
let dmi: i32 = 0;
|
||
for (dmi < directorymains.len) {
|
||
assert(os.exists(strings.concat(wdkey, "/", directorymains[dmi],
|
||
".unit.ww")));
|
||
dmi += 1;
|
||
};
|
||
let notestactions: []str = ["gamma", "vendor", "vendorx"];
|
||
let nti: i32 = 0;
|
||
for (nti < notestactions.len) {
|
||
assert(os.exists(strings.concat(wdkey, "/", notestactions[nti],
|
||
".unit.ww")));
|
||
assert(!os.exists(strings.concat(wdkey, "/", notestactions[nti],
|
||
"-test-main.unit.ww")));
|
||
nti += 1;
|
||
};
|
||
runcommand(root, "tree-w-ww", wdw,
|
||
(30i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 0);
|
||
expectexit(&outw, 0);
|
||
assert(same(outc.stdout, outw.stdout));
|
||
assert(same(outc.stdout, seq));
|
||
runcommand(root, "tree-w-warm", wdc,
|
||
(30i64 * (time.second: i64)): time.duration, &outc);
|
||
expectexit(&outc, 0);
|
||
assert(same(outc.stdout, seq));
|
||
// The retained executable is separate from the persistent action store, so
|
||
// -c can suppress execution while reusing the same complete graph.
|
||
let wcbin: str = strings.concat(root, "/tree-wc-c.test");
|
||
let wwbin: str = strings.concat(root, "/tree-wc-ww.test");
|
||
let wcc: []str = [driver("ww"), "test", "-c", "-w", wdroot,
|
||
"-o", wcbin, "-I", tree, strings.concat(tree, "/alpha")];
|
||
let wcw: []str = [driver("ww_ww"), "test", "-c", "-w", wdroot,
|
||
"-o", wwbin, "-I", tree, strings.concat(tree, "/alpha")];
|
||
runcommand(root, "tree-wc-c", wcc,
|
||
(30i64 * (time.second: i64)): time.duration, &outc);
|
||
runcommand(root, "tree-wc-ww", wcw,
|
||
(30i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 0);
|
||
expectexit(&outw, 0);
|
||
assert(outc.stdout.len == 0 && outc.stderr.len == 0);
|
||
assert(outw.stdout.len == 0 && outw.stderr.len == 0);
|
||
assert(os.exists(wcbin) && os.exists(wwbin));
|
||
assert(same(readfile(wcbin), readfile(wwbin)));
|
||
assert(same(outc.stderr, outw.stderr));
|
||
clean(wcbin); clean(wwbin);
|
||
|
||
let bare: str = strings.concat(root, "/bare");
|
||
let barez: str = strings.concat(root, "/zz-bare");
|
||
assert(os.mkdir(bare, 448i32) == 0);
|
||
assert(os.mkdir(strings.concat(bare, "/sub"), 448i32) == 0);
|
||
assert(os.mkdir(barez, 448i32) == 0);
|
||
let barec: []str = [driver("ww"), "test",
|
||
strings.concat(bare, "/...")];
|
||
let barew: []str = [driver("ww_ww"), "test",
|
||
strings.concat(bare, "/...")];
|
||
runcommand(root, "tree-bare-c", barec,
|
||
(10i64 * (time.second: i64)): time.duration, &outc);
|
||
runcommand(root, "tree-bare-ww", barew,
|
||
(10i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 1);
|
||
expectexit(&outw, 1);
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(has(outc.stderr, strings.concat("ww: warning: \"", bare,
|
||
"/...\" matched no packages\n")));
|
||
assert(has(outc.stderr, "ww test: no packages to test\n"));
|
||
let baredirectc: []str = [driver("ww"), "test", bare];
|
||
let baredirectw: []str = [driver("ww_ww"), "test", bare];
|
||
runcommand(root, "tree-bare-direct-c", baredirectc, time.second, &outc);
|
||
runcommand(root, "tree-bare-direct-ww", baredirectw, time.second, &outw);
|
||
expectexit(&outc, 1);
|
||
expectexit(&outw, 1);
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(has(outc.stderr, "directory contains no WW package sources\n"));
|
||
let quotedpattern: str = strings.concat(root, "/no\"...");
|
||
let quotedc: []str = [driver("ww"), "test", quotedpattern];
|
||
let quotedw: []str = [driver("ww_ww"), "test", quotedpattern];
|
||
runcommand(root, "tree-quoted-unmatched-c", quotedc, time.second, &outc);
|
||
runcommand(root, "tree-quoted-unmatched-ww", quotedw, time.second, &outw);
|
||
expectexit(&outc, 1);
|
||
expectexit(&outw, 1);
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(has(outc.stderr, strings.concat("ww: warning: \"", root,
|
||
"/no\\\"...\" matched no packages\n")));
|
||
// strconv.Quote escapes valid but non-printing Unicode, not only malformed
|
||
// bytes and ASCII controls. U+2028 is a stable discriminator.
|
||
let nonprintingbytes: []u8 = alloc([], (root.len + 8): u64)!;
|
||
let qi: i32 = 0;
|
||
for (qi < root.len) { append(nonprintingbytes, root[qi]); qi += 1; };
|
||
append(nonprintingbytes, '/': u8); append(nonprintingbytes, 'n': u8);
|
||
append(nonprintingbytes, 0xe2u8); append(nonprintingbytes, 0x80u8);
|
||
append(nonprintingbytes, 0xa8u8); append(nonprintingbytes, '.': u8);
|
||
append(nonprintingbytes, '.': u8); append(nonprintingbytes, '.': u8);
|
||
let nonprintingpattern: str = strings.frombytes(nonprintingbytes);
|
||
let nonprintingc: []str = [driver("ww"), "test", nonprintingpattern];
|
||
let nonprintingw: []str = [driver("ww_ww"), "test", nonprintingpattern];
|
||
runcommand(root, "tree-nonprinting-unmatched-c", nonprintingc,
|
||
time.second, &outc);
|
||
runcommand(root, "tree-nonprinting-unmatched-ww", nonprintingw,
|
||
time.second, &outw);
|
||
expectexit(&outc, 1);
|
||
expectexit(&outw, 1);
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(has(outc.stderr, "\\u2028...\" matched no packages\n"));
|
||
let mixedc: []str = [driver("ww"), "test", "-I", tree,
|
||
strings.concat(barez, "/..."), spec, strings.concat(bare, "/...")];
|
||
let mixedw: []str = [driver("ww_ww"), "test", "-I", tree,
|
||
strings.concat(bare, "/..."), spec, strings.concat(barez, "/...")];
|
||
runcommand(root, "tree-unmatched-order-c", mixedc,
|
||
(30i64 * (time.second: i64)): time.duration, &outc);
|
||
runcommand(root, "tree-unmatched-order-ww", mixedw,
|
||
(30i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 0);
|
||
expectexit(&outw, 0);
|
||
assert(same(outc.stdout, seq));
|
||
assert(same(outw.stdout, seq));
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(has(outc.stderr, strings.concat("ww: warning: \"", bare,
|
||
"/...\" matched no packages\n")));
|
||
let barewarning: str = strings.concat("ww: warning: \"", bare,
|
||
"/...\" matched no packages\n");
|
||
let barezwarning: str = strings.concat("ww: warning: \"", barez,
|
||
"/...\" matched no packages\n");
|
||
assert(pos(outc.stderr, barewarning) < pos(outc.stderr, barezwarning));
|
||
|
||
// Pinned pkgpattern rejects invalid UTF-8 patterns before regexp
|
||
// construction, so they match nothing and receive the ordinary quoted
|
||
// warning. Candidate names still use regexp's DecodeRune behavior.
|
||
let invalidpatternbytes: []u8 = alloc([], (root.len + 6): u64)!;
|
||
let mpi: i32 = 0;
|
||
for (mpi < root.len) {
|
||
append(invalidpatternbytes, root[mpi]); mpi += 1;
|
||
};
|
||
append(invalidpatternbytes, '/': u8); append(invalidpatternbytes, '.': u8);
|
||
append(invalidpatternbytes, '.': u8); append(invalidpatternbytes, '.': u8);
|
||
append(invalidpatternbytes, 0xffu8);
|
||
let invalidpattern: str = strings.frombytes(invalidpatternbytes);
|
||
let invalidpatternc: []str = [driver("ww"), "test", invalidpattern];
|
||
let invalidpatternw: []str = [driver("ww_ww"), "test", invalidpattern];
|
||
runcommand(root, "tree-invalid-pattern-c", invalidpatternc,
|
||
time.second, &outc);
|
||
runcommand(root, "tree-invalid-pattern-ww", invalidpatternw,
|
||
time.second, &outw);
|
||
expectexit(&outc, 1);
|
||
expectexit(&outw, 1);
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(has(outc.stderr, "...\\xff\" matched no packages\n"));
|
||
|
||
// U+FFFD in a valid pattern matches a malformed candidate filename byte as
|
||
// regexp does, while the wildcard below does not consume a newline.
|
||
let utfbase: str = strings.concat(root, "/utf-names");
|
||
assert(os.mkdir(utfbase, 448i32) == 0);
|
||
let invalidnamebytes: []u8 = alloc([], (utfbase.len + 2): u64)!;
|
||
mpi = 0;
|
||
for (mpi < utfbase.len) {
|
||
append(invalidnamebytes, utfbase[mpi]); mpi += 1;
|
||
};
|
||
append(invalidnamebytes, '/': u8); append(invalidnamebytes, 0xffu8);
|
||
let invalidname: str = strings.frombytes(invalidnamebytes);
|
||
assert(os.mkdir(invalidname, 448i32) == 0);
|
||
writefile(strings.concat(invalidname, "/case_test.ww"), strings.concat(
|
||
"package invalidname;\n@test fn replacement_match() void = {",
|
||
" assert(true); };\n"));
|
||
let replacementbytes: []u8 = alloc([], (utfbase.len + 8): u64)!;
|
||
mpi = 0;
|
||
for (mpi < utfbase.len) {
|
||
append(replacementbytes, utfbase[mpi]); mpi += 1;
|
||
};
|
||
append(replacementbytes, '/': u8); append(replacementbytes, 0xefu8);
|
||
append(replacementbytes, 0xbfu8); append(replacementbytes, 0xbdu8);
|
||
append(replacementbytes, '.': u8); append(replacementbytes, '.': u8);
|
||
append(replacementbytes, '.': u8);
|
||
let replacementpattern: str = strings.frombytes(replacementbytes);
|
||
let replacementc: []str = [driver("ww"), "test", replacementpattern];
|
||
let replacementw: []str = [driver("ww_ww"), "test", replacementpattern];
|
||
runcommand(root, "tree-replacement-pattern-c", replacementc,
|
||
(30i64 * (time.second: i64)): time.duration, &outc);
|
||
runcommand(root, "tree-replacement-pattern-ww", replacementw,
|
||
(30i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 0);
|
||
expectexit(&outw, 0);
|
||
assert(same(outc.stdout, outw.stdout));
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(has(outc.stdout, "invalidname.replacement_match ... ok\n"));
|
||
|
||
let newlinebase: str = strings.concat(root, "/newline-names");
|
||
assert(os.mkdir(newlinebase, 448i32) == 0);
|
||
let newlinedir: str = strings.concat(newlinebase, "/line\nbreak");
|
||
assert(os.mkdir(newlinedir, 448i32) == 0);
|
||
writefile(strings.concat(newlinedir, "/case_test.ww"),
|
||
"package newline;\n@test fn must_not_match() void = { assert(false); };\n");
|
||
let newlinepattern: str = strings.concat(newlinebase, "/line...break");
|
||
let newlinec: []str = [driver("ww"), "test", newlinepattern];
|
||
let newlinew: []str = [driver("ww_ww"), "test", newlinepattern];
|
||
runcommand(root, "tree-newline-pattern-c", newlinec, time.second, &outc);
|
||
runcommand(root, "tree-newline-pattern-ww", newlinew, time.second, &outw);
|
||
expectexit(&outc, 1);
|
||
expectexit(&outw, 1);
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(has(outc.stderr, "line...break\" matched no packages\n"));
|
||
let barebuildc: []str = [driver("ww"), "build",
|
||
strings.concat(bare, "/...")];
|
||
let barebuildw: []str = [driver("ww_ww"), "build",
|
||
strings.concat(bare, "/...")];
|
||
runcommand(root, "tree-empty-build-c", barebuildc, time.second, &outc);
|
||
runcommand(root, "tree-empty-build-ww", barebuildw, time.second, &outw);
|
||
expectexit(&outc, 0);
|
||
expectexit(&outw, 0);
|
||
assert(same(outc.stdout, outw.stdout));
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(has(outc.stderr, "matched no packages\n"));
|
||
|
||
// A wildcard recognizes a test-only directory, so it is not unmatched;
|
||
// build then omits it while test keeps its isolated variant. Direct build
|
||
// remains an explicit unusable-package error.
|
||
let testonly: str = strings.concat(tree, "/beta/...");
|
||
let testonlyc: []str = [driver("ww"), "build", "-I", tree, testonly];
|
||
let testonlyw: []str = [driver("ww_ww"), "build", "-I", tree, testonly];
|
||
runcommand(root, "tree-test-only-build-c", testonlyc, time.second, &outc);
|
||
runcommand(root, "tree-test-only-build-ww", testonlyw, time.second, &outw);
|
||
expectexit(&outc, 0);
|
||
expectexit(&outw, 0);
|
||
assert(outc.stdout.len == 0 && outc.stderr.len == 0);
|
||
assert(same(outc.stdout, outw.stdout));
|
||
assert(same(outc.stderr, outw.stderr));
|
||
let testdirectc: []str = [driver("ww"), "build",
|
||
strings.concat(tree, "/beta/inner")];
|
||
let testdirectw: []str = [driver("ww_ww"), "build",
|
||
strings.concat(tree, "/beta/inner")];
|
||
runcommand(root, "tree-test-only-direct-c", testdirectc, time.second, &outc);
|
||
runcommand(root, "tree-test-only-direct-ww", testdirectw, time.second, &outw);
|
||
expectexit(&outc, 1);
|
||
expectexit(&outw, 1);
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(has(outc.stderr, "directory contains no WW package sources\n"));
|
||
|
||
let malformed: str = strings.concat(root, "/malformed");
|
||
let malformedgood: str = strings.concat(malformed, "/good");
|
||
let malformedbad: str = strings.concat(malformed, "/bad");
|
||
assert(os.mkdir(malformed, 448i32) == 0);
|
||
assert(os.mkdir(malformedgood, 448i32) == 0);
|
||
assert(os.mkdir(malformedbad, 448i32) == 0);
|
||
writefile(strings.concat(malformedgood, "/good.ww"),
|
||
"package good;\nexport fn value() int = { return 1; };\n");
|
||
let loop: str = strings.concat(root, "/loop");
|
||
let loopav: []str = ["/bin/ln", "-s", loop, loop];
|
||
runcommand(root, "recursive-root-loop", loopav, time.second, &linkout);
|
||
expectexit(&linkout, 0);
|
||
let rejecttrace: str = strings.concat(root, "/pattern-reject.trace");
|
||
let rejectcompiler: str = strings.concat(root, "/pattern-reject-w6c.sh");
|
||
writefile(rejecttrace, "");
|
||
writeexecutable(rejectcompiler, strings.concat(
|
||
"#!/bin/sh\nprintf 'compiler\\n' >> \"$WW_PATTERN_REJECT_TRACE\"\n",
|
||
"exit 99\n"));
|
||
let baseenv: []str = os.getenvs();
|
||
let rejectenv: []str = alloc([], (baseenv.len + 2): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < baseenv.len) {
|
||
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_PATTERN_REJECT_TRACE=")) {
|
||
append(rejectenv, baseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(rejectenv, strings.concat("WW_W6C=", rejectcompiler));
|
||
append(rejectenv, strings.concat("WW_PATTERN_REJECT_TRACE=", rejecttrace));
|
||
let rejectspec: str = strings.concat(malformed, "/...");
|
||
let warmworkc: str = strings.concat(root, "/warm-reject-work-c");
|
||
let warmworkw: str = strings.concat(root, "/warm-reject-work-ww");
|
||
let warmc: []str = [driver("ww"), "build", "-w", warmworkc,
|
||
"-I", malformed, rejectspec];
|
||
let warmw: []str = [driver("ww_ww"), "build", "-w", warmworkw,
|
||
"-I", malformed, rejectspec];
|
||
// Seed genuine committed package, voucher, and tool state before making a
|
||
// second selected directory malformed.
|
||
runcommand(root, "tree-malformed-seed-c", warmc,
|
||
(30i64 * (time.second: i64)): time.duration, &outc);
|
||
runcommand(root, "tree-malformed-seed-ww", warmw,
|
||
(30i64 * (time.second: i64)): time.duration, &outw);
|
||
expectexit(&outc, 0);
|
||
expectexit(&outw, 0);
|
||
assert(same(outc.stdout, outw.stdout));
|
||
assert(same(outc.stderr, outw.stderr));
|
||
let warmsuffixes: []str = ["/good.unit.ww", "/good.wwi", "/good.s",
|
||
"/good.o", "/good.a", "/.wwtool.stamp", "/.wwtool.ww",
|
||
"/.wwtool.w6c", "/.wwtool.w6a"];
|
||
let warmcstate: []str = alloc([], warmsuffixes.len: u64)!;
|
||
let warmwstate: []str = alloc([], warmsuffixes.len: u64)!;
|
||
let wi: i32 = 0;
|
||
for (wi < warmsuffixes.len) {
|
||
append(warmcstate, readfile(strings.concat(warmworkc,
|
||
warmsuffixes[wi])));
|
||
append(warmwstate, readfile(strings.concat(warmworkw,
|
||
warmsuffixes[wi])));
|
||
wi += 1;
|
||
};
|
||
writefile(strings.concat(warmworkc, "/marker"), "c-warm\n");
|
||
writefile(strings.concat(warmworkw, "/marker"), "ww-warm\n");
|
||
writefile(strings.concat(malformedbad, "/bad.ww"),
|
||
"this selected source has no package clause\n");
|
||
|
||
let rejectworkc: str = strings.concat(root, "/cold-reject-work-c");
|
||
let rejectworkw: str = strings.concat(root, "/cold-reject-work-ww");
|
||
let rejectc: []str = [driver("ww"), "build", "-w", rejectworkc,
|
||
"-I", malformed, rejectspec];
|
||
let rejectw: []str = [driver("ww_ww"), "build", "-w", rejectworkw,
|
||
"-I", malformed, rejectspec];
|
||
runcommandenv(root, "tree-malformed-c", rejectc, rejectenv,
|
||
time.second, &outc);
|
||
runcommandenv(root, "tree-malformed-ww", rejectw, rejectenv,
|
||
time.second, &outw);
|
||
expectexit(&outc, 1);
|
||
expectexit(&outw, 1);
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(has(outc.stderr, "invalid or missing package clause\n"));
|
||
assert(readfile(rejecttrace).len == 0);
|
||
assert(!os.exists(rejectworkc) && !os.exists(rejectworkw));
|
||
// Warm rejection preserves all previously committed package, voucher,
|
||
// tool, and caller-owned marker bytes while still reaching no producer.
|
||
runcommandenv(root, "tree-malformed-warm-c", warmc, rejectenv,
|
||
time.second, &outc);
|
||
runcommandenv(root, "tree-malformed-warm-ww", warmw, rejectenv,
|
||
time.second, &outw);
|
||
expectexit(&outc, 1);
|
||
expectexit(&outw, 1);
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(has(outc.stderr, "invalid or missing package clause\n"));
|
||
assert(readfile(rejecttrace).len == 0);
|
||
assert(same(readfile(strings.concat(warmworkc, "/marker")), "c-warm\n"));
|
||
assert(same(readfile(strings.concat(warmworkw, "/marker")), "ww-warm\n"));
|
||
wi = 0;
|
||
for (wi < warmsuffixes.len) {
|
||
assert(same(warmcstate[wi], readfile(strings.concat(warmworkc,
|
||
warmsuffixes[wi]))));
|
||
assert(same(warmwstate[wi], readfile(strings.concat(warmworkw,
|
||
warmsuffixes[wi]))));
|
||
wi += 1;
|
||
};
|
||
|
||
// A valid package clause that fails during source-import graph loading is
|
||
// also rejected before private work/output directories are materialized.
|
||
let graphbad: str = strings.concat(root, "/graph-bad");
|
||
let graphapp: str = strings.concat(graphbad, "/app");
|
||
assert(os.mkdir(graphbad, 448i32) == 0);
|
||
assert(os.mkdir(graphapp, 448i32) == 0);
|
||
writefile(strings.concat(graphapp, "/main.ww"),
|
||
"package main;\nimport nowhere;\nfn main() void = { };\n");
|
||
let graphspec: str = strings.concat(graphbad, "/...");
|
||
let graphworkc: str = strings.concat(root, "/graph-work-c");
|
||
let graphworkw: str = strings.concat(root, "/graph-work-ww");
|
||
let graphoutc: str = strings.concat(root, "/graph-out-c/");
|
||
let graphoutw: str = strings.concat(root, "/graph-out-ww/");
|
||
let graphc: []str = [driver("ww"), "build", "-w", graphworkc,
|
||
"-o", graphoutc, "-I", graphbad, graphspec];
|
||
let graphw: []str = [driver("ww_ww"), "build", "-w", graphworkw,
|
||
"-o", graphoutw, "-I", graphbad, graphspec];
|
||
runcommandenv(root, "tree-graph-reject-c", graphc, rejectenv,
|
||
time.second, &outc);
|
||
runcommandenv(root, "tree-graph-reject-ww", graphw, rejectenv,
|
||
time.second, &outw);
|
||
expectexit(&outc, 1);
|
||
expectexit(&outw, 1);
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(has(outc.stderr, "cannot find package nowhere\n"));
|
||
assert(readfile(rejecttrace).len == 0);
|
||
assert(!os.exists(graphworkc) && !os.exists(graphworkw));
|
||
assert(!os.exists(graphoutc) && !os.exists(graphoutw));
|
||
// If output creation succeeds but a later workdir prefix is unusable, the
|
||
// driver reclaims only the output prefixes it just minted. Both stages make
|
||
// the same pre-producer failure and leave the caller tree unchanged.
|
||
let setupblock: str = strings.concat(root, "/setup-block");
|
||
let setupwork: str = strings.concat(setupblock, "/child");
|
||
let setupoutroot: str = strings.concat(root, "/setup-output");
|
||
let setuppreserved: str = strings.concat(root, "/setup-preserved");
|
||
assert(os.mkdir(setuppreserved, 448i32) == 0);
|
||
let setupout: str = strings.concat(setupoutroot,
|
||
"/../setup-preserved/nested/");
|
||
writefile(setupblock, "not a directory\n");
|
||
let setupc: []str = [driver("ww"), "build", "-w", setupwork,
|
||
"-o", setupout, "-I", commands, commandspec];
|
||
let setupw: []str = [driver("ww_ww"), "build", "-w", setupwork,
|
||
"-o", setupout, "-I", commands, commandspec];
|
||
runcommandenv(root, "tree-setup-rollback-c", setupc, rejectenv,
|
||
time.second, &outc);
|
||
expectexit(&outc, 1);
|
||
assert(has(outc.stderr, strings.concat("ww: workdir ", setupwork,
|
||
" is not a directory\n")));
|
||
assert(readfile(rejecttrace).len == 0);
|
||
assert(!os.exists(setupoutroot));
|
||
assert(os.exists(setuppreserved));
|
||
assert(!os.exists(strings.concat(setuppreserved, "/nested")));
|
||
assert(!os.exists(setupwork));
|
||
assert(same(readfile(setupblock), "not a directory\n"));
|
||
runcommandenv(root, "tree-setup-rollback-ww", setupw, rejectenv,
|
||
time.second, &outw);
|
||
expectexit(&outw, 1);
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(readfile(rejecttrace).len == 0);
|
||
assert(!os.exists(setupoutroot));
|
||
assert(os.exists(setuppreserved));
|
||
assert(!os.exists(strings.concat(setuppreserved, "/nested")));
|
||
assert(!os.exists(setupwork));
|
||
assert(same(readfile(setupblock), "not a directory\n"));
|
||
let loopc: []str = [driver("ww"), "test",
|
||
strings.concat(loop, "/...")];
|
||
let loopw: []str = [driver("ww_ww"), "test",
|
||
strings.concat(loop, "/...")];
|
||
runcommandenv(root, "tree-loop-c", loopc, rejectenv, time.second, &outc);
|
||
runcommandenv(root, "tree-loop-ww", loopw, rejectenv, time.second, &outw);
|
||
expectexit(&outc, 1);
|
||
expectexit(&outw, 1);
|
||
assert(same(outc.stderr, outw.stderr));
|
||
assert(has(outc.stderr, "cannot canonicalize package directory\n"));
|
||
assert(readfile(rejecttrace).len == 0);
|
||
clean(root);
|
||
};
|
||
|
||
@test fn persistent_package_request_workdir_is_stable() void = {
|
||
let root: str = fresh();
|
||
let tree: str = deeppunctuationpath(root, "request tree");
|
||
assert(tree.len > 255);
|
||
let adir: str = strings.concat(tree, "/a");
|
||
let bdir: str = strings.concat(tree, "/b");
|
||
let a: str = strings.concat(adir, "/foo");
|
||
let b: str = strings.concat(bdir, "/foo");
|
||
let workc: str = strings.concat(root, "/work-c");
|
||
let workww: str = strings.concat(root, "/work-ww");
|
||
mkdirall(tree);
|
||
assert(os.mkdir(adir, 448i32) == 0);
|
||
assert(os.mkdir(bdir, 448i32) == 0);
|
||
assert(os.mkdir(a, 448i32) == 0);
|
||
assert(os.mkdir(b, 448i32) == 0);
|
||
assert(os.mkdir(workc, 448i32) == 0);
|
||
assert(os.mkdir(workww, 448i32) == 0);
|
||
writefile(strings.concat(a, "/foo.ww"),
|
||
"package foo;\nfn value() i32 = { return 1; };\n");
|
||
writefile(strings.concat(a, "/foo_test.ww"),
|
||
"package foo;\n@test fn first() void = { assert(value() == 1); };\n");
|
||
writefile(strings.concat(b, "/foo.ww"),
|
||
"package foo;\nfn value() i32 = { return 2; };\n");
|
||
writefile(strings.concat(b, "/foo_test.ww"),
|
||
"package foo;\n@test fn second() void = { assert(value() == 2); };\n");
|
||
let av: []str = [driver("ww"), "test", "-j", "2", "-w", workc,
|
||
"-I", tree,
|
||
strings.concat(tree, "/...")];
|
||
let out: commandout;
|
||
runcommand(root, "workkey-cold", av,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let cold: str = strings.dup(out.stdout);
|
||
assert(has(cold, strings.concat("ok ", a,
|
||
" [foo]\n")));
|
||
assert(has(cold, strings.concat("ok ", b,
|
||
" [foo]\n")));
|
||
let ckey: str = workc;
|
||
assert(os.exists(ckey));
|
||
let aaction: str = "a.foo-internal-test";
|
||
let baction: str = "b.foo-internal-test";
|
||
assert(os.exists(strings.concat(ckey, "/", aaction, ".unit.ww")));
|
||
assert(os.exists(strings.concat(ckey, "/", baction, ".unit.ww")));
|
||
let aunit: str = readfile(strings.concat(ckey, "/", aaction, ".unit.ww"));
|
||
let bunit: str = readfile(strings.concat(ckey, "/", baction, ".unit.ww"));
|
||
let awwi: str = readfile(strings.concat(ckey, "/", aaction, ".wwi"));
|
||
let bwwi: str = readfile(strings.concat(ckey, "/", baction, ".wwi"));
|
||
let aa: str = readfile(strings.concat(ckey, "/", aaction, ".a"));
|
||
let ba: str = readfile(strings.concat(ckey, "/", baction, ".a"));
|
||
assert(has(aunit, "return 1") && !has(aunit, "return 2"));
|
||
assert(has(bunit, "return 2") && !has(bunit, "return 1"));
|
||
runcommand(root, "workkey-warm", av,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(same(cold, out.stdout));
|
||
let wwav: []str = [driver("ww_ww"), "test", "-j", "1", "-w", workww,
|
||
"-I", tree,
|
||
strings.concat(tree, "/...")];
|
||
runcommand(root, "workkey-ww", wwav,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(same(cold, out.stdout));
|
||
let wwkey: str = workww;
|
||
assert(same(aunit, readfile(strings.concat(wwkey, "/", aaction,
|
||
".unit.ww"))));
|
||
assert(same(bunit, readfile(strings.concat(wwkey, "/", baction,
|
||
".unit.ww"))));
|
||
assert(same(awwi, readfile(strings.concat(wwkey, "/", aaction, ".wwi"))));
|
||
assert(same(bwwi, readfile(strings.concat(wwkey, "/", baction, ".wwi"))));
|
||
assert(same(aa, readfile(strings.concat(wwkey, "/", aaction, ".a"))));
|
||
assert(same(ba, readfile(strings.concat(wwkey, "/", baction, ".a"))));
|
||
// The shared semantic-action store and warm action set are unchanged by an
|
||
// equivalent recursive-root spelling.
|
||
let equivalentav: []str = [driver("ww_ww"), "test", "-j", "2", "-w",
|
||
workww, "-I", tree, strings.concat(tree, "/./...")];
|
||
runcommand(root, "workkey-equivalent", equivalentav,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout, strings.concat("ok ", a,
|
||
" [foo]\n")));
|
||
assert(has(out.stdout, strings.concat("ok ", b,
|
||
" [foo]\n")));
|
||
assert(os.exists(wwkey));
|
||
assert(same(aunit, readfile(strings.concat(wwkey, "/", aaction,
|
||
".unit.ww"))));
|
||
assert(same(bunit, readfile(strings.concat(wwkey, "/", baction,
|
||
".unit.ww"))));
|
||
assert(same(awwi, readfile(strings.concat(wwkey, "/", aaction, ".wwi"))));
|
||
assert(same(bwwi, readfile(strings.concat(wwkey, "/", baction, ".wwi"))));
|
||
assert(same(aa, readfile(strings.concat(wwkey, "/", aaction, ".a"))));
|
||
assert(same(ba, readfile(strings.concat(wwkey, "/", baction, ".a"))));
|
||
|
||
// Outside every active import root, equal declaration leaves remain two
|
||
// deterministic command-local identities derived from canonical dirs.
|
||
let localleft: str = deeppunctuationpath(root, "local left");
|
||
let localright: str = deeppunctuationpath(root, "local right");
|
||
mkdirall(localleft);
|
||
mkdirall(localright);
|
||
writefile(strings.concat(localleft, "/foo.ww"),
|
||
"package foo;\nexport fn value() i32 = { return 11; };\n");
|
||
writefile(strings.concat(localright, "/foo.ww"),
|
||
"package foo;\nexport fn value() i32 = { return 22; };\n");
|
||
let localids: []str = [localidentity(localleft, "foo"),
|
||
localidentity(localright, "foo")];
|
||
assert(!same(localids[0], localids[1]));
|
||
assert(localids[0].len > 255 && localids[1].len > 255);
|
||
let localstorage: []str = [packagestoragekey(localids[0], localleft, 0, 0),
|
||
packagestoragekey(localids[1], localright, 0, 0)];
|
||
assert(!same(localstorage[0], localstorage[1]));
|
||
let stages: []str = ["ww", "ww_ww"];
|
||
let localrefs: []str = ["", "", "", "", "", ""];
|
||
let si: i32 = 0;
|
||
for (si < stages.len) {
|
||
let leftout: str = strings.concat(root, "/local-", stages[si],
|
||
"-left.test");
|
||
let rightout: str = strings.concat(root, "/local-", stages[si],
|
||
"-right.test");
|
||
let lav: []str = [driver(stages[si]), "test", "-c",
|
||
"--ww-package-build",
|
||
"--ww-package-test", "build", "foo", "foo", "-", "-",
|
||
localleft, leftout,
|
||
"-",
|
||
strings.concat(root, "/local-", stages[si], "-left.status"),
|
||
"--ww-package-test", "build", "foo", "foo", "-", "-",
|
||
localright, rightout,
|
||
"-",
|
||
strings.concat(root, "/local-", stages[si], "-right.status"),
|
||
localleft];
|
||
runcommand(root, strings.concat("local-command-union-", stages[si]),
|
||
lav, (60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let localwork: str = strings.concat(leftout, ".sepwork/");
|
||
let li: i32 = 0;
|
||
for (li < localids.len) {
|
||
let suffixes: []str = [".unit.ww", ".wwi", ".a"];
|
||
let xi: i32 = 0;
|
||
for (xi < suffixes.len) {
|
||
let artifact: str = readfile(strings.concat(localwork,
|
||
localstorage[li], suffixes[xi]));
|
||
if (xi == 0) {
|
||
assert(has(artifact, strings.concat("//ww:module-reset ",
|
||
localids[li], "\n")));
|
||
} else { if (xi == 1) {
|
||
assert(strings.hasprefix(artifact, strings.concat(
|
||
"//ww:module ", localids[li], "\n")));
|
||
}; };
|
||
let refi: i32 = li * 3 + xi;
|
||
if (si == 0) { localrefs[refi] = strings.dup(artifact); }
|
||
else { assert(same(localrefs[refi], artifact)); };
|
||
xi += 1;
|
||
};
|
||
li += 1;
|
||
};
|
||
si += 1;
|
||
};
|
||
clean(root);
|
||
};
|
||
|
||
fn runtimepath(relative: str) str = {
|
||
return strings.concat(repo(), "/test/package/runtime/", relative);
|
||
};
|
||
|
||
// 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", "-o", extralib,
|
||
extra];
|
||
let out: commandout;
|
||
runcommand(root, "link-flags-library", libav,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let cbin: str = strings.concat(root, "/c-main");
|
||
let wwbin: str = strings.concat(root, "/ww-main");
|
||
let cav: []str = [driver("ww"), "build", "-L", root,
|
||
"-l", "extra", "-o", cbin, pkg];
|
||
let baseenv: []str = os.getenvs();
|
||
let cenv: []str = alloc([], (baseenv.len + 3): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < baseenv.len) {
|
||
if (!strings.hasprefix(baseenv[ei], "WW_W6L=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_LINK_FLAG_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_LINK_FLAG_W6L=")) {
|
||
append(cenv, baseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(cenv, strings.concat("WW_W6L=", cwrapper));
|
||
append(cenv, strings.concat("WW_LINK_FLAG_TRACE=", linktrace));
|
||
append(cenv, strings.concat("WW_LINK_FLAG_W6L=", driver("w6l")));
|
||
runcommandenv(root, "link-flags-c", cav, cenv,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let ctraced: str = readfile(linktrace);
|
||
assert(has(ctraced, strings.concat(" -L ", root, " -l extra")));
|
||
|
||
// Keep this row on default sibling discovery: a copied driver beside a
|
||
// tracing linker and exact compiler/assembler siblings proves WWstage uses
|
||
// the same separate flag/value linker argv contract as Cstage.
|
||
let tracedir: str = strings.concat(repo(), "/out/link-flag-trace-",
|
||
workescape(root));
|
||
assert(os.mkdir(tracedir, 448i32) == 0);
|
||
let tracedriver: str = strings.concat(tracedir, "/ww_ww");
|
||
writeexecutable(tracedriver, readfile(driver("ww_ww")));
|
||
writeexecutable(strings.concat(tracedir, "/w6c_ww"),
|
||
readfile(driver("w6c_ww")));
|
||
writeexecutable(strings.concat(tracedir, "/w6a_ww"),
|
||
readfile(driver("w6a_ww")));
|
||
writeexecutable(strings.concat(tracedir, "/w6l_ww"), strings.concat(
|
||
"#!/bin/sh\n",
|
||
"printf '%s\\n' \"$*\" >> \"$WW_LINK_FLAG_TRACE\"\n",
|
||
"exec \"$WW_LINK_FLAG_W6L_WW\" \"$@\"\n"));
|
||
let wenv: []str = alloc([], (baseenv.len + 2): u64)!;
|
||
ei = 0;
|
||
for (ei < baseenv.len) {
|
||
if (!strings.hasprefix(baseenv[ei], "WW_LINK_FLAG_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_LINK_FLAG_W6L_WW=")) {
|
||
append(wenv, baseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(wenv, strings.concat("WW_LINK_FLAG_TRACE=", linktrace));
|
||
append(wenv, strings.concat("WW_LINK_FLAG_W6L_WW=", driver("w6l_ww")));
|
||
let twav: []str = [tracedriver, "build", "-L", root,
|
||
"-l", "extra", "-o", wwbin, pkg];
|
||
runcommandenv(root, "link-flags-ww", twav, wenv,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let wtraced: str = readfile(linktrace);
|
||
let wwdelta: str = strings.sub(wtraced, ctraced.len, wtraced.len);
|
||
assert(has(wwdelta, strings.concat(" -L ", root, " -l extra")));
|
||
assert(!has(wwdelta, strings.concat(" -L", root)));
|
||
assert(!has(wwdelta, " -lextra"));
|
||
assert(same(readfile(cbin), readfile(wwbin)));
|
||
let crun: []str = [cbin];
|
||
let wrun: []str = [wwbin];
|
||
runcommand(root, "link-flags-c-run", crun,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
runcommand(root, "link-flags-ww-run", wrun,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
clean(tracedir);
|
||
clean(root);
|
||
};
|
||
|
||
// The published test binary's OWN command surface: repeated glob args,
|
||
// -list (with and without a glob), and -timeout-ms. Only a direct
|
||
// invocation of the `-c` artifact proves these; every driver route
|
||
// parses its flags before the binary sees them.
|
||
@test fn published_binary_cli() void = {
|
||
let root: str = fresh();
|
||
let out: commandout;
|
||
let stem: str = strings.concat(root, "/rtsuccess");
|
||
let cav: []str = [driver("ww"), "test", "-c", "-o", stem,
|
||
runtimepath("success_test.ww")];
|
||
runcommand(root, "publish", cav,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
|
||
let gav: []str = [stem, "alpha*", "delta*"];
|
||
runcommand(root, "globs", gav,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout, "alpha_pass ... ok\n"));
|
||
assert(has(out.stdout, "delta_pass ... ok\n"));
|
||
assert(has(out.stdout,
|
||
"2 passed, 0 failed, 0 skipped, 0 harness errors\n"));
|
||
assert(has(out.stdout,
|
||
"4 discovered, 2 selected, 2 started, 2 completed\n"));
|
||
|
||
let lav: []str = [stem, "-list"];
|
||
runcommand(root, "list", lav,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(same(out.stdout,
|
||
"alpha_pass\nbeta_skip\ngamma_expected_abort\ndelta_pass\n"));
|
||
|
||
let lgav: []str = [stem, "-list", "gamma*"];
|
||
runcommand(root, "list-glob", lgav,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(same(out.stdout, "gamma_expected_abort\n"));
|
||
|
||
let tav: []str = [stem, "-timeout-ms=50", "alpha*"];
|
||
runcommand(root, "cli-timeout", tav,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout,
|
||
"1 passed, 0 failed, 0 skipped, 0 harness errors\n"));
|
||
clean(root);
|
||
};
|
||
|
||
// Direct-binary timeout classification and descendant handling. The
|
||
// runcommand deadline bounds the no-hang claims: a runner waiting on a
|
||
// SIGTERM-blocking or process-group-escaped pipe writer would trip it.
|
||
@test fn direct_binary_timeout_and_descendants() void = {
|
||
let root: str = fresh();
|
||
let out: commandout;
|
||
let tstem: str = strings.concat(root, "/rttimeout");
|
||
let tcav: []str = [driver("ww"), "test", "-c", "-o", tstem,
|
||
runtimepath("timeout_test.ww")];
|
||
runcommand(root, "publish-timeout", tcav,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let trun: []str = [tstem, "-timeout-ms=50"];
|
||
runcommand(root, "run-timeout", trun,
|
||
(20i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stdout, "hang_times_out ... FAIL(timeout)\n"));
|
||
|
||
let lstem: str = strings.concat(root, "/rtlinger");
|
||
let lcav: []str = [driver("ww"), "test", "-c", "-o", lstem,
|
||
runtimepath("lingering_descendant_test.ww")];
|
||
runcommand(root, "publish-linger", lcav,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let lrun: []str = [lstem, "-timeout-ms=250"];
|
||
runcommand(root, "run-linger", lrun,
|
||
(20i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout,
|
||
"returning_test_clears_descendant ... ok\n"));
|
||
|
||
let estem: str = strings.concat(root, "/rtescaped");
|
||
let ecav: []str = [driver("ww"), "test", "-c", "-o", estem,
|
||
runtimepath("escaped_descendant_test.ww")];
|
||
runcommand(root, "publish-escaped", ecav,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let erun: []str = [estem, "-timeout-ms=250"];
|
||
runcommand(root, "run-escaped", erun,
|
||
(20i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout,
|
||
"escaped_writer_does_not_hold_runner ... ok\n"));
|
||
clean(root);
|
||
};
|
||
|
||
@test fn vendor_directory_import_resolution() void = {
|
||
let root: str = fresh();
|
||
let source: str = strings.concat(root, "/source");
|
||
let runtime: str = strings.concat(root, "/runtime");
|
||
let tools: str = strings.concat(root, "/tools");
|
||
let leaf: str = strings.concat(source, "/lib/leaf");
|
||
let nearest: str = strings.concat(source,
|
||
"/domain/app/vendor/lib/math");
|
||
let outer: str = strings.concat(source, "/domain/vendor/lib/math");
|
||
let rootvendor: str = strings.concat(source, "/vendor/lib/math");
|
||
let ordinary: str = strings.concat(source, "/lib/math");
|
||
let client: str = strings.concat(source, "/domain/app/client");
|
||
let one: str = strings.concat(source, "/domain/app/one");
|
||
let two: str = strings.concat(source, "/domain/app/two");
|
||
let outerclient: str = strings.concat(source, "/domain/other/client");
|
||
let rootclient: str = strings.concat(source, "/other/client");
|
||
let emptyclient: str = strings.concat(source, "/domain/empty/client");
|
||
let testonlyclient: str = strings.concat(source,
|
||
"/domain/testonly/client");
|
||
let bareclient: str = strings.concat(source, "/domain/bare/client");
|
||
let outsider: str = strings.concat(source, "/outsider");
|
||
let prefixclient: str = strings.concat(source, "/domainx/client");
|
||
let directclient: str = strings.concat(source, "/domain/app/direct");
|
||
let directhijack: str = strings.concat(directclient,
|
||
"/vendor/domain/app/vendor/lib/math");
|
||
let nested: str = strings.concat(source,
|
||
"/nested/owner/vendor/outer/vendor/lib/math");
|
||
let nestedclient: str = strings.concat(source,
|
||
"/nested/owner/vendor/outer/client");
|
||
let ordinaryclient: str = strings.concat(source, "/ordinaryclient");
|
||
let vendorx: str = strings.concat(source, "/vendorx/lib/math");
|
||
let terminalvendor: str = strings.concat(source, "/terminal/vendor");
|
||
let physicalclient: str = strings.concat(source,
|
||
"/domain/app/physicalclient");
|
||
let aliasclient: str = strings.concat(source, "/aliasclient");
|
||
let physicaldirect: str = strings.concat(source,
|
||
"/domain/app/physicaldirect");
|
||
let aliasdirect: str = strings.concat(source, "/aliasdirect");
|
||
let escapephysical: str = strings.concat(root, "/escape/client");
|
||
let escapeclient: str = strings.concat(source, "/domain/app/escape");
|
||
let shapeclient: str = strings.concat(source, "/shape/owner/client");
|
||
let shapevendor: str = strings.concat(source,
|
||
"/shape/owner/vendor/lib/math");
|
||
let shapevault: str = strings.concat(root, "/vault/deep/different");
|
||
let testclient: str = strings.concat(source, "/domain/app/testclient");
|
||
let testbad: str = strings.concat(source, "/testbad");
|
||
let supportcase: str = strings.concat(source, "/supportcase");
|
||
let toolsource: str = strings.concat(root, "/toolsource");
|
||
let toolsupport: str = strings.concat(toolsource, "/test");
|
||
let toolvendor: str = strings.concat(toolsource,
|
||
"/test/vendor/fnmatch");
|
||
let vendortest: str = strings.concat(source,
|
||
"/literal/owner/vendor/lib/math");
|
||
let nestedvendortest: str = strings.concat(vendortest,
|
||
"/vendor/lib/math");
|
||
let alpha: str = strings.concat(source, "/alpha/client");
|
||
let beta: str = strings.concat(source, "/beta/client");
|
||
let alphavendor: str = strings.concat(alpha, "/vendor/lib/math");
|
||
let betavendor: str = strings.concat(beta, "/vendor/lib/math");
|
||
let pairvault: str = strings.concat(root, "/pair-vault/math");
|
||
let boundary: str = strings.concat(root, "/boundary");
|
||
let active: str = strings.concat(boundary, "/active");
|
||
let boundaryclient: str = strings.concat(active, "/domain/app");
|
||
let boundaryordinary: str = strings.concat(active, "/lib/math");
|
||
let boundaryabove: str = strings.concat(boundary, "/vendor/lib/math");
|
||
let cleanroot: str = strings.concat(root, "/clean");
|
||
let cleanclient: str = strings.concat(cleanroot, "/domain/app");
|
||
let cleanordinary: str = strings.concat(cleanroot, "/lib/math");
|
||
let foldvendorupper: str = strings.concat(source,
|
||
"/domain/fold/vendor/lib/Foo");
|
||
let foldvendorlower: str = strings.concat(source,
|
||
"/domain/fold/vendor/lib/foo");
|
||
let foldvendorleft: str = strings.concat(source, "/domain/fold/left");
|
||
let foldvendorright: str = strings.concat(source, "/domain/fold/right");
|
||
let foldvendorclient: str = strings.concat(source, "/domain/fold/client");
|
||
let foldvendorreverseclient: str = strings.concat(source,
|
||
"/domain/fold/reverseclient");
|
||
let foldaliasvendorlib: str = strings.concat(source,
|
||
"/domain/foldalias/vendor/lib");
|
||
let foldaliasvendorupper: str = strings.concat(foldaliasvendorlib, "/Foo");
|
||
let foldaliasvendorlower: str = strings.concat(foldaliasvendorlib, "/foo");
|
||
let foldaliasvendorleft: str = strings.concat(source,
|
||
"/domain/foldalias/left");
|
||
let foldaliasvendorright: str = strings.concat(source,
|
||
"/domain/foldalias/right");
|
||
let foldaliasvendorclient: str = strings.concat(source,
|
||
"/domain/foldalias/client");
|
||
let foldaliasvendorvault: str = strings.concat(root, "/fold-vendor-vault");
|
||
|
||
let dirs: []str = [leaf, nearest, outer, rootvendor, ordinary, client,
|
||
one, two, outerclient, rootclient, emptyclient,
|
||
strings.concat(emptyclient, "/vendor/lib/math/fake.ww"),
|
||
testonlyclient, strings.concat(testonlyclient, "/vendor/lib/math"),
|
||
bareclient, strings.concat(bareclient, "/vendor/lib/math"), outsider,
|
||
prefixclient, directclient, directhijack, nested, nestedclient,
|
||
ordinaryclient,
|
||
vendorx, terminalvendor, physicalclient, physicaldirect, escapephysical,
|
||
shapeclient, strings.concat(source, "/shape/owner/vendor/lib"),
|
||
shapevault, testclient, testbad, supportcase, toolsupport,
|
||
toolvendor, vendortest, nestedvendortest,
|
||
alpha, beta, strings.concat(alpha, "/vendor/lib"),
|
||
strings.concat(beta, "/vendor/lib"), pairvault, boundaryclient,
|
||
boundaryordinary, boundaryabove, cleanclient, cleanordinary,
|
||
foldvendorupper, foldvendorlower, foldvendorleft, foldvendorright,
|
||
foldvendorclient, foldvendorreverseclient, foldaliasvendorlib, foldaliasvendorleft,
|
||
foldaliasvendorright, foldaliasvendorclient, foldaliasvendorvault,
|
||
runtime, tools];
|
||
let di: i32 = 0;
|
||
for (di < dirs.len) { mkdirall(dirs[di]); di += 1; };
|
||
writefile(strings.concat(runtime, "/libwwrt.a"),
|
||
readfile(strings.concat(repo(), "/out/lib/libwwrt.a")));
|
||
|
||
writefile(strings.concat(leaf, "/leaf.ww"), strings.concat(
|
||
"package leaf;\n",
|
||
"export fn value() i32 = { return 1; };\n"));
|
||
let neara: str = strings.concat(
|
||
"package math;\nimport lib.leaf;\n",
|
||
"export fn value() i32 = { return leaf.value() + 10; };\n");
|
||
let nearz: str = strings.concat(
|
||
"package math;\n",
|
||
"fn private_value() i32 = { return 99; };\n");
|
||
writefile(strings.concat(nearest, "/a.ww"), neara);
|
||
writefile(strings.concat(nearest, "/z.ww"), nearz);
|
||
writefile(strings.concat(outer, "/math.ww"),
|
||
"package math;\nexport fn value() i32 = { return 22; };\n");
|
||
writefile(strings.concat(rootvendor, "/math.ww"),
|
||
"package math;\nexport fn value() i32 = { return 33; };\n");
|
||
writefile(strings.concat(ordinary, "/math.ww"),
|
||
"package math;\nexport fn value() i32 = { return 44; };\n");
|
||
let mainbody: str = strings.concat(
|
||
"package main;\nimport lib.math;\n",
|
||
"fn main() i32 = { return math.value(); };\n");
|
||
writefile(strings.concat(client, "/main.ww"), mainbody);
|
||
writefile(strings.concat(one, "/main.ww"), mainbody);
|
||
writefile(strings.concat(two, "/main.ww"), mainbody);
|
||
writefile(strings.concat(outerclient, "/main.ww"), mainbody);
|
||
writefile(strings.concat(rootclient, "/main.ww"), mainbody);
|
||
writefile(strings.concat(emptyclient, "/main.ww"), mainbody);
|
||
writefile(strings.concat(testonlyclient, "/main.ww"), mainbody);
|
||
writefile(strings.concat(testonlyclient,
|
||
"/vendor/lib/math/only_test.ww"),
|
||
"package math;\n@test fn only() void = { assert(true); };\n");
|
||
writefile(strings.concat(bareclient, "/main.ww"), mainbody);
|
||
writefile(strings.concat(bareclient, "/vendor/lib/math/.ww"),
|
||
"package math;\n");
|
||
let expanded: str = "domain.app.vendor.lib.math";
|
||
let directbody: str = strings.concat(
|
||
"package main;\nimport ", expanded, ";\n",
|
||
"fn main() i32 = { return math.value(); };\n");
|
||
writefile(strings.concat(outsider, "/main.ww"), directbody);
|
||
writefile(strings.concat(prefixclient, "/main.ww"), directbody);
|
||
writefile(strings.concat(directclient, "/main.ww"), directbody);
|
||
writefile(strings.concat(directhijack, "/math.ww"),
|
||
"package math;\nexport fn value() i32 = { return 99; };\n");
|
||
|
||
writefile(strings.concat(nested, "/math.ww"),
|
||
"package math;\nexport fn value() i32 = { return 77; };\n");
|
||
writefile(strings.concat(nestedclient, "/main.ww"), mainbody);
|
||
writefile(strings.concat(vendorx, "/math.ww"),
|
||
"package math;\nexport fn value() i32 = { return 4; };\n");
|
||
writefile(strings.concat(terminalvendor, "/vendor.ww"),
|
||
"package vendor;\nexport fn value() i32 = { return 8; };\n");
|
||
writefile(strings.concat(ordinaryclient, "/main.ww"), strings.concat(
|
||
"package main;\nimport vendorx.lib.math;\nimport terminal.vendor;\n",
|
||
"fn main() i32 = { return math.value() + vendor.value(); };\n"));
|
||
|
||
writefile(strings.concat(physicalclient, "/main.ww"), mainbody);
|
||
assert(os.symlink(physicalclient, aliasclient) == 0);
|
||
writefile(strings.concat(physicaldirect, "/main.ww"), directbody);
|
||
assert(os.symlink(physicaldirect, aliasdirect) == 0);
|
||
writefile(strings.concat(escapephysical, "/main.ww"), mainbody);
|
||
assert(os.symlink(escapephysical, escapeclient) == 0);
|
||
writefile(strings.concat(shapevault, "/math.ww"),
|
||
"package math;\nexport fn value() i32 = { return 17; };\n");
|
||
assert(os.symlink(shapevault, shapevendor) == 0);
|
||
writefile(strings.concat(shapeclient, "/main.ww"), mainbody);
|
||
|
||
writefile(strings.concat(testclient, "/testclient.ww"), strings.concat(
|
||
"package testclient;\nimport lib.math;\n",
|
||
"export fn value() i32 = { return math.value(); };\n"));
|
||
writefile(strings.concat(testclient, "/same_test.ww"), strings.concat(
|
||
"package testclient;\nimport lib.math;\n",
|
||
"@test fn same_vendor() void = { assert(math.value() == 11); };\n"));
|
||
writefile(strings.concat(testclient, "/external_test.ww"), strings.concat(
|
||
"package testclient_test;\nimport lib.math;\n",
|
||
"import domain.app.testclient;\n",
|
||
"@test fn external_vendor() void = {\n",
|
||
" assert(math.value() + testclient.value() == 22);\n};\n"));
|
||
writefile(strings.concat(testbad, "/testbad.ww"),
|
||
"package testbad;\nexport fn value() i32 = { return 1; };\n");
|
||
writefile(strings.concat(testbad, "/same_test.ww"), strings.concat(
|
||
"package testbad;\nimport ", expanded, ";\n",
|
||
"@test fn forbidden_vendor() void = { assert(false); };\n"));
|
||
writefile(strings.concat(supportcase, "/supportcase.ww"),
|
||
"package supportcase;\nexport fn value() i32 = { return 1; };\n");
|
||
writefile(strings.concat(supportcase, "/supportcase_test.ww"),
|
||
"package supportcase;\n@test fn support_runs() void = { assert(value() == 1); };\n");
|
||
writefile(strings.concat(toolsupport, "/run.ww"),
|
||
readfile(strings.concat(repo(), "/lib/test/run.ww")));
|
||
writefile(strings.concat(toolvendor, "/fnmatch.ww"),
|
||
readfile(strings.concat(repo(), "/lib/fnmatch/fnmatch.ww")));
|
||
let tooldeps: []str = ["ascii", "bytes", "encoding", "os", "rt",
|
||
"strings", "time", "types"];
|
||
di = 0;
|
||
for (di < tooldeps.len) {
|
||
assert(os.symlink(strings.concat(repo(), "/lib/", tooldeps[di]),
|
||
strings.concat(toolsource, "/", tooldeps[di])) == 0);
|
||
di += 1;
|
||
};
|
||
writefile(strings.concat(vendortest, "/math.ww"),
|
||
"package math;\nexport fn value() i32 = { return 7; };\n");
|
||
writefile(strings.concat(vendortest, "/math_test.ww"), strings.concat(
|
||
"package math_test;\nimport lib.math;\n",
|
||
"@test fn nested_external_vendor() void = {\n",
|
||
" assert(math.value() == 9);\n};\n"));
|
||
writefile(strings.concat(nestedvendortest, "/math.ww"),
|
||
"package math;\nexport fn value() i32 = { return 9; };\n");
|
||
|
||
writefile(strings.concat(pairvault, "/math.ww"),
|
||
"package math;\nexport fn value() i32 = { return 61; };\n");
|
||
assert(os.symlink(pairvault, alphavendor) == 0);
|
||
assert(os.symlink(pairvault, betavendor) == 0);
|
||
writefile(strings.concat(alpha, "/main.ww"), mainbody);
|
||
writefile(strings.concat(beta, "/main.ww"), mainbody);
|
||
writefile(strings.concat(boundaryclient, "/main.ww"), mainbody);
|
||
writefile(strings.concat(boundaryordinary, "/math.ww"),
|
||
"package math;\nexport fn value() i32 = { return 55; };\n");
|
||
writefile(strings.concat(boundaryabove, "/math.ww"),
|
||
"package math;\nexport fn value() i32 = { return 66; };\n");
|
||
writefile(strings.concat(cleanclient, "/main.ww"), mainbody);
|
||
writefile(strings.concat(cleanordinary, "/math.ww"),
|
||
"package math;\nexport fn value() i32 = { return 44; };\n");
|
||
writefile(strings.concat(foldvendorupper, "/pkg.ww"),
|
||
"package upper;\nexport fn value() i32 = { return 1; };\n");
|
||
writefile(strings.concat(foldvendorlower, "/pkg.ww"),
|
||
"package lower;\nexport fn value() i32 = { return 2; };\n");
|
||
writefile(strings.concat(foldvendorleft, "/pkg.ww"), strings.concat(
|
||
"package left;\nimport _ lib.Foo;\n",
|
||
"export fn value() i32 = { return 1; };\n"));
|
||
writefile(strings.concat(foldvendorright, "/pkg.ww"), strings.concat(
|
||
"package right;\nimport _ lib.foo;\n",
|
||
"export fn value() i32 = { return 2; };\n"));
|
||
writefile(strings.concat(foldvendorclient, "/main.ww"), strings.concat(
|
||
"package main;\nimport _ domain.fold.left;\n",
|
||
"import _ domain.fold.right;\nfn main() i32 = { return 0; };\n"));
|
||
writefile(strings.concat(foldvendorreverseclient, "/main.ww"), strings.concat(
|
||
"package main;\nimport _ domain.fold.right;\n",
|
||
"import _ domain.fold.left;\nfn main() i32 = { return 0; };\n"));
|
||
writefile(strings.concat(foldaliasvendorvault, "/pkg.ww"),
|
||
"package shared;\nexport fn value() i32 = { return 1; };\n");
|
||
assert(os.symlink(foldaliasvendorvault, foldaliasvendorupper) == 0);
|
||
assert(os.symlink(foldaliasvendorvault, foldaliasvendorlower) == 0);
|
||
writefile(strings.concat(foldaliasvendorleft, "/pkg.ww"), strings.concat(
|
||
"package left;\nimport _ lib.Foo;\n",
|
||
"export fn value() i32 = { return 1; };\n"));
|
||
writefile(strings.concat(foldaliasvendorright, "/pkg.ww"), strings.concat(
|
||
"package right;\nimport _ lib.foo;\n",
|
||
"export fn value() i32 = { return 2; };\n"));
|
||
writefile(strings.concat(foldaliasvendorclient, "/main.ww"), strings.concat(
|
||
"package main;\nimport _ domain.foldalias.left;\n",
|
||
"import _ domain.foldalias.right;\nfn main() i32 = { return 0; };\n"));
|
||
|
||
let compilerwrapper: str = strings.concat(tools, "/w6c.sh");
|
||
let assemblerwrapper: str = strings.concat(tools, "/w6a.sh");
|
||
let linkerwrapper: str = strings.concat(tools, "/w6l.sh");
|
||
writeexecutable(compilerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf 'BEGIN' >> \"$WW_VENDOR_COMPILER_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||
"\"$WW_VENDOR_COMPILER_TRACE\"; done\n",
|
||
"printf '\\n' >> \"$WW_VENDOR_COMPILER_TRACE\"\n",
|
||
"exec \"$WW_VENDOR_REAL_COMPILER\" \"$@\"\n"));
|
||
writeexecutable(assemblerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf 'BEGIN' >> \"$WW_VENDOR_ASSEMBLER_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||
"\"$WW_VENDOR_ASSEMBLER_TRACE\"; done\n",
|
||
"printf '\\n' >> \"$WW_VENDOR_ASSEMBLER_TRACE\"\n",
|
||
"exec \"$WW_VENDOR_REAL_ASSEMBLER\" \"$@\"\n"));
|
||
writeexecutable(linkerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf 'BEGIN' >> \"$WW_VENDOR_LINKER_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||
"\"$WW_VENDOR_LINKER_TRACE\"; done\n",
|
||
"printf '\\n' >> \"$WW_VENDOR_LINKER_TRACE\"\n",
|
||
"exec \"$WW_VENDOR_REAL_LINKER\" \"$@\"\n"));
|
||
|
||
let stages: []str = ["ww", "ww_ww"];
|
||
let compilers: []str = ["w6c", "w6c_ww"];
|
||
let assemblers: []str = ["w6a", "w6a_ww"];
|
||
let linkers: []str = ["w6l", "w6l_ww"];
|
||
let works: []str = [strings.concat(root, "/c-work"),
|
||
strings.concat(root, "/ww-work")];
|
||
let bins: []str = [strings.concat(root, "/c-bin"),
|
||
strings.concat(root, "/ww-bin")];
|
||
let compilertraces: []str = [strings.concat(root, "/c-compiler"),
|
||
strings.concat(root, "/ww-compiler")];
|
||
let assemblertraces: []str = [strings.concat(root, "/c-assembler"),
|
||
strings.concat(root, "/ww-assembler")];
|
||
let linkertraces: []str = [strings.concat(root, "/c-linker"),
|
||
strings.concat(root, "/ww-linker")];
|
||
let coreartifacts: []str = ["lib.leaf", expanded, "domain.app.client"];
|
||
let coresuffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"];
|
||
let references: []str = alloc([],
|
||
(coreartifacts.len * coresuffixes.len): u64)!;
|
||
let ri: i32 = 0;
|
||
for (ri < coreartifacts.len * coresuffixes.len) {
|
||
append(references, ""); ri += 1;
|
||
};
|
||
let referencebin: str = "";
|
||
let referencecompiler: str = "";
|
||
let referenceassembler: str = "";
|
||
let referencelinker: str = "";
|
||
let referencedirectdiag: str = "";
|
||
let referenceoutsidediag: str = "";
|
||
let combinedrefs: []str = ["", "", "", "", "", ""];
|
||
let referencetestout: str = "";
|
||
let referencetesterr: str = "";
|
||
let testrefs: []str = ["", "", "", "", "", ""];
|
||
let referencesupportout: str = "";
|
||
let supportrefs: []str = ["", "", ""];
|
||
let referenceorderdiag: str = "";
|
||
let referenceescapeddiag: str = "";
|
||
let referencealiasdirectdiag: str = "";
|
||
let referenceprefixdiag: str = "";
|
||
let candidate_diag_refs: []str = ["", ""];
|
||
let foldvendorrefs: []str = ["", "", ""];
|
||
let foldvendormultiref: str = "";
|
||
let supplementalrefs: []str = ["", "", "", "", "", "", "", "",
|
||
""];
|
||
let referencevendortestout: str = "";
|
||
let referencevendortesterr: str = "";
|
||
let referencetestbaddiag: str = "";
|
||
let baseenv: []str = os.getenvs();
|
||
let si: i32 = 0;
|
||
for (si < stages.len) {
|
||
assert(os.mkdir(works[si], 448i32) == 0);
|
||
writefile(compilertraces[si], "");
|
||
writefile(assemblertraces[si], "");
|
||
writefile(linkertraces[si], "");
|
||
let env: []str = alloc([], (baseenv.len + 11): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < baseenv.len) {
|
||
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6A=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6L=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_LIB=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_SRCLIB=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_VENDOR_COMPILER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_VENDOR_ASSEMBLER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_VENDOR_LINKER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_VENDOR_REAL_COMPILER=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_VENDOR_REAL_ASSEMBLER=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_VENDOR_REAL_LINKER=")) {
|
||
append(env, baseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(env, strings.concat("WW_W6C=", compilerwrapper));
|
||
append(env, strings.concat("WW_W6A=", assemblerwrapper));
|
||
append(env, strings.concat("WW_W6L=", linkerwrapper));
|
||
append(env, strings.concat("WW_LIB=", runtime));
|
||
append(env, strings.concat("WW_VENDOR_COMPILER_TRACE=",
|
||
compilertraces[si]));
|
||
append(env, strings.concat("WW_VENDOR_ASSEMBLER_TRACE=",
|
||
assemblertraces[si]));
|
||
append(env, strings.concat("WW_VENDOR_LINKER_TRACE=",
|
||
linkertraces[si]));
|
||
append(env, strings.concat("WW_VENDOR_REAL_COMPILER=",
|
||
driver(compilers[si])));
|
||
append(env, strings.concat("WW_VENDOR_REAL_ASSEMBLER=",
|
||
driver(assemblers[si])));
|
||
append(env, strings.concat("WW_VENDOR_REAL_LINKER=",
|
||
driver(linkers[si])));
|
||
|
||
let out: commandout;
|
||
let foldtargets: []str = [foldvendorclient, foldvendorreverseclient,
|
||
foldaliasvendorclient];
|
||
let foldlabels: []str = ["distinct", "reverse-imports", "one-dir"];
|
||
let foldwants: []str = [
|
||
"ww: case-insensitive import collision: \"domain.fold.vendor.lib.Foo\" and \"domain.fold.vendor.lib.foo\"\n",
|
||
"ww: case-insensitive import collision: \"domain.fold.vendor.lib.Foo\" and \"domain.fold.vendor.lib.foo\"\n",
|
||
"ww: case-insensitive import collision: \"domain.foldalias.vendor.lib.Foo\" and \"domain.foldalias.vendor.lib.foo\"\n",
|
||
];
|
||
let fi: i32 = 0;
|
||
for (fi < foldtargets.len) {
|
||
rewritefile(compilertraces[si], "");
|
||
rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
let foldwork: str = strings.concat(root, "/vendor-fold-",
|
||
foldlabels[fi], "-", stages[si], "-work");
|
||
let foldout: str = strings.concat(root, "/vendor-fold-",
|
||
foldlabels[fi], "-", stages[si]);
|
||
assert(os.mkdir(foldwork, 448i32) == 0);
|
||
let foldav: []str = [driver(stages[si]), "build", "-w",
|
||
foldwork, "-I", source, "-o", foldout, foldtargets[fi]];
|
||
runcommandenv(root, strings.concat("vendor-fold-", foldlabels[fi],
|
||
"-", stages[si]), foldav, env,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(out.stdout.len == 0 && same(out.stderr, foldwants[fi]));
|
||
assert(readfile(compilertraces[si]).len == 0);
|
||
assert(readfile(assemblertraces[si]).len == 0);
|
||
assert(readfile(linkertraces[si]).len == 0);
|
||
assert(!os.exists(foldout));
|
||
assert(!os.exists(strings.concat(foldout, ".new")));
|
||
assert(!os.exists(strings.concat(foldout, ".sepwork")));
|
||
assert(!os.exists(strings.concat(foldwork, "/.wwtool.stamp")));
|
||
assert(directoryisempty(foldwork));
|
||
if (si == 0) { foldvendorrefs[fi] = strings.dup(out.stderr); }
|
||
else { assert(same(foldvendorrefs[fi], out.stderr)); };
|
||
fi += 1;
|
||
};
|
||
assert(same(foldvendorrefs[0], foldvendorrefs[1]));
|
||
|
||
// A command-global multi-product request is one rejection transaction.
|
||
// Reversing its descriptors cannot publish the unrelated product or
|
||
// mutate either pre-existing status.
|
||
let foldmultiwork: str = strings.concat(root, "/vendor-fold-multi-",
|
||
stages[si], "-work");
|
||
assert(os.mkdir(foldmultiwork, 448i32) == 0);
|
||
let foldallowedout: str = strings.concat(root, "/vendor-fold-allowed-",
|
||
stages[si]);
|
||
let foldcollisionout: str = strings.concat(root,
|
||
"/vendor-fold-collision-", stages[si]);
|
||
let foldallowedstatus: str = strings.concat(foldallowedout, ".status");
|
||
let foldcollisionstatus: str = strings.concat(foldcollisionout, ".status");
|
||
writefile(foldallowedstatus, "sentinel-allowed\n");
|
||
writefile(foldcollisionstatus, "sentinel-collision\n");
|
||
let foldforward: []str = [driver(stages[si]), "test", "-c", "-w",
|
||
foldmultiwork, "-I", source, "--ww-package-build",
|
||
"--ww-package-test", "build", "main", "main", "-", "-", client,
|
||
foldallowedout, "-", foldallowedstatus,
|
||
"--ww-package-test", "build", "main", "main", "-", "-",
|
||
foldvendorclient, foldcollisionout, "-", foldcollisionstatus, client];
|
||
let foldreverse: []str = [driver(stages[si]), "test", "-c", "-w",
|
||
foldmultiwork, "-I", source, "--ww-package-build",
|
||
"--ww-package-test", "build", "main", "main", "-", "-",
|
||
foldvendorclient, foldcollisionout, "-", foldcollisionstatus,
|
||
"--ww-package-test", "build", "main", "main", "-", "-", client,
|
||
foldallowedout, "-", foldallowedstatus, client];
|
||
let foldrequests: [][]str = [foldforward, foldreverse];
|
||
let foldmultidiags: []str = ["", ""];
|
||
let foldorderlabels: []str = ["forward", "reverse"];
|
||
let foi: i32 = 0;
|
||
for (foi < foldrequests.len) {
|
||
rewritefile(compilertraces[si], "");
|
||
rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
runcommandenv(root, strings.concat("vendor-fold-multi-", stages[si],
|
||
"-", foldorderlabels[foi]), foldrequests[foi], env,
|
||
(180i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(out.stdout.len == 0 && same(out.stderr, foldwants[0]));
|
||
foldmultidiags[foi] = strings.dup(out.stderr);
|
||
assert(readfile(compilertraces[si]).len == 0);
|
||
assert(readfile(assemblertraces[si]).len == 0);
|
||
assert(readfile(linkertraces[si]).len == 0);
|
||
assert(same(readfile(foldallowedstatus), "sentinel-allowed\n"));
|
||
assert(same(readfile(foldcollisionstatus), "sentinel-collision\n"));
|
||
assert(!os.exists(strings.concat(foldallowedstatus, ".new")));
|
||
assert(!os.exists(strings.concat(foldcollisionstatus, ".new")));
|
||
assert(!os.exists(foldallowedout));
|
||
assert(!os.exists(strings.concat(foldallowedout, ".new")));
|
||
assert(!os.exists(strings.concat(foldallowedout, ".sepwork")));
|
||
assert(!os.exists(strings.concat(foldallowedout, ".result")));
|
||
assert(!os.exists(strings.concat(foldallowedout, ".result.new")));
|
||
assert(!os.exists(foldcollisionout));
|
||
assert(!os.exists(strings.concat(foldcollisionout, ".new")));
|
||
assert(!os.exists(strings.concat(foldcollisionout, ".sepwork")));
|
||
assert(!os.exists(strings.concat(foldcollisionout, ".result")));
|
||
assert(!os.exists(strings.concat(foldcollisionout, ".result.new")));
|
||
assert(directoryisempty(foldmultiwork));
|
||
foi += 1;
|
||
};
|
||
assert(same(foldmultidiags[0], foldmultidiags[1]));
|
||
if (si == 0) { foldvendormultiref = strings.dup(foldmultidiags[0]); }
|
||
else { assert(same(foldvendormultiref, foldmultidiags[0])); };
|
||
|
||
let av: []str = [driver(stages[si]), "build", "-w", works[si],
|
||
"-I", source, "-o", bins[si], client];
|
||
runcommandenv(root, strings.concat("vendor-cold-", stages[si]), av,
|
||
env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
let wantnear: str = strings.concat(
|
||
"//ww:module-reset ", expanded, "\n", neara, "\n",
|
||
"//ww:module-reset ", expanded, "\n", nearz, "\n",
|
||
"//ww:vendor-dir ", hexbytes(nearest), "\n");
|
||
let nearunit: str = readfile(strings.concat(works[si], "/", expanded,
|
||
".unit.ww"));
|
||
assert(strings.hasprefix(nearunit, wantnear));
|
||
assert(has(nearunit, "//ww:direct-export lib.leaf "));
|
||
let clientunit: str = readfile(strings.concat(works[si],
|
||
"/domain.app.client.unit.ww"));
|
||
assert(has(clientunit, strings.concat("//ww:import-map lib.math ",
|
||
expanded, " ", hexbytes(nearest), "\n")));
|
||
let ai: i32 = 0;
|
||
for (ai < coreartifacts.len) {
|
||
let xi: i32 = 0;
|
||
for (xi < coresuffixes.len) {
|
||
let bytes: str = readfile(strings.concat(works[si], "/",
|
||
coreartifacts[ai], coresuffixes[xi]));
|
||
let index: i32 = ai * coresuffixes.len + xi;
|
||
if (si == 0) { references[index] = strings.dup(bytes); }
|
||
else { assert(same(references[index], bytes)); };
|
||
xi += 1;
|
||
};
|
||
ai += 1;
|
||
};
|
||
let ctrace: str = readfile(compilertraces[si]);
|
||
let atrace: str = readfile(assemblertraces[si]);
|
||
let ltrace: str = readfile(linkertraces[si]);
|
||
assert(occurrences(ctrace, "\n") == 3);
|
||
assert(occurrences(atrace, "\n") == 4);
|
||
assert(occurrences(ltrace, "\n") == 1);
|
||
assert(has(ltrace, strings.concat("BEGIN<-o><", bins[si], ".new>")));
|
||
let dispatchline: str = strings.concat("BEGIN<-o><", works[si],
|
||
"/domain.app.client.init.o.new><", works[si],
|
||
"/domain.app.client.init.s.new>");
|
||
assert(same(linecontaining(atrace,
|
||
"/domain.app.client.init.o.new"), dispatchline));
|
||
let nearline: str = linecontaining(ctrace,
|
||
strings.concat(expanded, ".unit.new"));
|
||
assert(occurrences(nearline, "<--import><lib.leaf>") == 1);
|
||
assert(!has(nearline, "domain.app.client.wwi"));
|
||
let clientline: str = linecontaining(ctrace,
|
||
"domain.app.client.unit.new");
|
||
assert(occurrences(clientline, strings.concat("<--import><", expanded,
|
||
"><", works[si], "/", expanded, ".wwi.new>")) == 1);
|
||
assert(occurrences(clientline, strings.concat(
|
||
"<--import-map><lib.math><", expanded, ">")) == 1);
|
||
assert(!has(clientline, "lib.leaf.wwi"));
|
||
assert(occurrences(ltrace, strings.concat("<", works[si],
|
||
"/domain.app.client.a.new>")) == 1);
|
||
assert(occurrences(ltrace, strings.concat("<", works[si], "/",
|
||
expanded, ".a.new>")) == 1);
|
||
assert(occurrences(ltrace, strings.concat("<", works[si],
|
||
"/lib.leaf.a.new>")) == 1);
|
||
assert(!has(ltrace, ".wwi>"));
|
||
let nc: str = normalizedtrace(ctrace,
|
||
strings.concat(works[si], "/"), bins[si]);
|
||
let na: str = normalizedtrace(atrace,
|
||
strings.concat(works[si], "/"), bins[si]);
|
||
let nl: str = normalizedtrace(ltrace,
|
||
strings.concat(works[si], "/"), bins[si]);
|
||
if (si == 0) {
|
||
referencecompiler = strings.dup(nc);
|
||
referenceassembler = strings.dup(na);
|
||
referencelinker = strings.dup(nl);
|
||
referencebin = strings.dup(readfile(bins[si]));
|
||
} else {
|
||
assert(same(referencecompiler, nc));
|
||
assert(same(referenceassembler, na));
|
||
assert(same(referencelinker, nl));
|
||
assert(same(referencebin, readfile(bins[si])));
|
||
};
|
||
let runav: []str = [bins[si]];
|
||
runcommand(root, strings.concat("vendor-run-", stages[si]), runav,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 11);
|
||
|
||
let voucher: str = readfile(strings.concat(works[si], "/",
|
||
expanded, ".unit.ww"));
|
||
let toolww: str = readfile(strings.concat(works[si], "/.wwtool.ww"));
|
||
let toolc: str = readfile(strings.concat(works[si], "/.wwtool.w6c"));
|
||
let toola: str = readfile(strings.concat(works[si], "/.wwtool.w6a"));
|
||
let stamp: str = readfile(strings.concat(works[si], "/.wwtool.stamp"));
|
||
rewritefile(compilertraces[si], "");
|
||
rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
runcommandenv(root, strings.concat("vendor-warm-", stages[si]), av,
|
||
env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(readfile(compilertraces[si]).len == 0);
|
||
assert(readfile(assemblertraces[si]).len == 0);
|
||
assert(occurrences(readfile(linkertraces[si]), "\n") == 1);
|
||
|
||
rewritefile(compilertraces[si], "");
|
||
rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
let badout: str = strings.concat(root, "/warm-bad-", stages[si]);
|
||
let badav: []str = [driver(stages[si]), "build", "-w", works[si],
|
||
"-I", source, "-o", badout, outsider];
|
||
runcommandenv(root, strings.concat("vendor-warm-bad-", stages[si]),
|
||
badav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
let outsidediag: str = strings.concat(outsider,
|
||
"/main.ww:2:1: error: use of vendored package not allowed\n");
|
||
assert(same(out.stderr, outsidediag));
|
||
if (si == 0) { referenceoutsidediag = strings.dup(out.stderr); }
|
||
else { assert(same(referenceoutsidediag, out.stderr)); };
|
||
assert(readfile(compilertraces[si]).len == 0);
|
||
assert(readfile(assemblertraces[si]).len == 0);
|
||
assert(readfile(linkertraces[si]).len == 0);
|
||
assert(!os.exists(badout));
|
||
assert(same(voucher, readfile(strings.concat(works[si], "/",
|
||
expanded, ".unit.ww"))));
|
||
assert(same(toolww, readfile(strings.concat(works[si], "/.wwtool.ww"))));
|
||
assert(same(toolc, readfile(strings.concat(works[si], "/.wwtool.w6c"))));
|
||
assert(same(toola, readfile(strings.concat(works[si], "/.wwtool.w6a"))));
|
||
assert(same(stamp, readfile(strings.concat(works[si],
|
||
"/.wwtool.stamp"))));
|
||
ai = 0;
|
||
for (ai < coreartifacts.len) {
|
||
let xi: i32 = 0;
|
||
for (xi < coresuffixes.len) {
|
||
let index: i32 = ai * coresuffixes.len + xi;
|
||
assert(same(references[index], readfile(strings.concat(
|
||
works[si], "/", coreartifacts[ai], coresuffixes[xi]))));
|
||
xi += 1;
|
||
};
|
||
ai += 1;
|
||
};
|
||
|
||
let coldout: str = strings.concat(root, "/cold-bad-", stages[si]);
|
||
let coldav: []str = [driver(stages[si]), "build", "-I", source,
|
||
"-o", coldout, outsider];
|
||
runcommandenv(root, strings.concat("vendor-cold-bad-", stages[si]),
|
||
coldav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(same(out.stderr, outsidediag));
|
||
assert(readfile(compilertraces[si]).len == 0);
|
||
assert(readfile(assemblertraces[si]).len == 0);
|
||
assert(readfile(linkertraces[si]).len == 0);
|
||
assert(!os.exists(coldout));
|
||
assert(!os.exists(strings.concat(coldout, ".sepwork")));
|
||
|
||
let directout: str = strings.concat(root, "/direct-bad-", stages[si]);
|
||
let directav: []str = [driver(stages[si]), "build", "-I", source,
|
||
"-o", directout, directclient];
|
||
runcommandenv(root, strings.concat("vendor-direct-bad-", stages[si]),
|
||
directav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
let directdiag: str = strings.concat(directclient,
|
||
"/main.ww:2:1: error: ", expanded,
|
||
" must be imported as lib.math\n");
|
||
assert(same(out.stderr, directdiag));
|
||
if (si == 0) { referencedirectdiag = strings.dup(out.stderr); }
|
||
else { assert(same(referencedirectdiag, out.stderr)); };
|
||
assert(readfile(compilertraces[si]).len == 0);
|
||
assert(readfile(assemblertraces[si]).len == 0);
|
||
assert(readfile(linkertraces[si]).len == 0);
|
||
assert(!os.exists(directout));
|
||
assert(!os.exists(strings.concat(directout, ".sepwork")));
|
||
|
||
// The lexical importer is outside domain/app, but the symlink target is
|
||
// physically inside it. Visibility therefore passes and the later
|
||
// effective-spelling diagnostic proves physical importer containment.
|
||
let aliasdirectout: str = strings.concat(root,
|
||
"/alias-direct-bad-", stages[si]);
|
||
let aliasdirectav: []str = [driver(stages[si]), "build", "-I", source,
|
||
"-o", aliasdirectout, aliasdirect];
|
||
runcommandenv(root, strings.concat("vendor-alias-direct-bad-",
|
||
stages[si]), aliasdirectav, env,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
let aliasdirectdiag: str = strings.concat(aliasdirect,
|
||
"/main.ww:2:1: error: ", expanded,
|
||
" must be imported as lib.math\n");
|
||
assert(same(out.stderr, aliasdirectdiag));
|
||
if (si == 0) { referencealiasdirectdiag = strings.dup(out.stderr); }
|
||
else { assert(same(referencealiasdirectdiag, out.stderr)); };
|
||
assert(readfile(compilertraces[si]).len == 0);
|
||
assert(readfile(assemblertraces[si]).len == 0);
|
||
assert(readfile(linkertraces[si]).len == 0);
|
||
assert(!os.exists(aliasdirectout));
|
||
assert(!os.exists(strings.concat(aliasdirectout, ".sepwork")));
|
||
|
||
// Remaining resolution rows reuse the stage workdir, but every output
|
||
// remains independently executable and compared across stages below.
|
||
let targets: []str = [outerclient, rootclient, emptyclient,
|
||
boundaryclient, cleanclient, nestedclient, ordinaryclient,
|
||
aliasclient, shapeclient];
|
||
let roots: []str = [source, source, source, active, cleanroot, source,
|
||
source, source, source];
|
||
let codes: []i32 = [22, 33, 22, 55, 44, 77, 12, 33, 17];
|
||
let labels: []str = ["outer", "root", "empty", "boundary", "ordinary",
|
||
"nested", "names", "alias", "shape"];
|
||
let ti: i32 = 0;
|
||
for (ti < targets.len) {
|
||
let output: str = strings.concat(root, "/", labels[ti], "-",
|
||
stages[si]);
|
||
let tav: []str = [driver(stages[si]), "build", "-w", works[si],
|
||
"-I", roots[ti], "-o", output, targets[ti]];
|
||
runcommandenv(root, strings.concat("vendor-", labels[ti], "-",
|
||
stages[si]), tav, env,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let rr: []str = [output];
|
||
runcommand(root, strings.concat("vendor-run-", labels[ti], "-",
|
||
stages[si]), rr,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, codes[ti]);
|
||
if (si == 0) {
|
||
supplementalrefs[ti] = strings.dup(readfile(output));
|
||
} else {
|
||
assert(same(supplementalrefs[ti], readfile(output)));
|
||
};
|
||
ti += 1;
|
||
};
|
||
|
||
let escapedout: str = strings.concat(root, "/escape-bad-", stages[si]);
|
||
let escapedav: []str = [driver(stages[si]), "build", "-I", source,
|
||
"-o", escapedout, escapeclient];
|
||
runcommandenv(root, strings.concat("vendor-escape-bad-", stages[si]),
|
||
escapedav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, "use of vendored package not allowed\n"));
|
||
if (si == 0) { referenceescapeddiag = strings.dup(out.stderr); }
|
||
else { assert(same(referenceescapeddiag, out.stderr)); };
|
||
let prefixout: str = strings.concat(root, "/prefix-bad-", stages[si]);
|
||
let prefixav: []str = [driver(stages[si]), "build", "-I", source,
|
||
"-o", prefixout, prefixclient];
|
||
runcommandenv(root, strings.concat("vendor-prefix-bad-", stages[si]),
|
||
prefixav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, "use of vendored package not allowed\n"));
|
||
if (si == 0) { referenceprefixdiag = strings.dup(out.stderr); }
|
||
else { assert(same(referenceprefixdiag, out.stderr)); };
|
||
|
||
let candidateclients: []str = [testonlyclient, bareclient];
|
||
let candidatefiles: []str = ["only_test.ww", ".ww"];
|
||
let ci: i32 = 0;
|
||
for (ci < candidateclients.len) {
|
||
let candidateout: str = strings.concat(root, "/candidate-bad-",
|
||
stages[si], "-", candidatefiles[ci]);
|
||
let cav: []str = [driver(stages[si]), "build", "-I", source,
|
||
"-o", candidateout, candidateclients[ci]];
|
||
runcommandenv(root, strings.concat("vendor-candidate-bad-",
|
||
stages[si], "-", candidatefiles[ci]), cav, env,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, "/vendor/lib/math: directory contains no WW package sources\n"));
|
||
assert(!os.exists(strings.concat(candidateout, ".sepwork")));
|
||
if (si == 0) {
|
||
candidate_diag_refs[ci] = strings.dup(out.stderr);
|
||
} else {
|
||
assert(same(candidate_diag_refs[ci], out.stderr));
|
||
};
|
||
ci += 1;
|
||
};
|
||
|
||
let selected: str = strings.concat(root, "/selected-", stages[si], ".a");
|
||
let selectedav: []str = [driver(stages[si]), "build", "-I", source,
|
||
"-o", selected, nearest];
|
||
runcommandenv(root, strings.concat("vendor-selected-", stages[si]),
|
||
selectedav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(same(references[1 * coresuffixes.len + 4], readfile(selected)));
|
||
assert(same(references[1 * coresuffixes.len + 1],
|
||
readfile(strings.concat(selected, ".wwi"))));
|
||
|
||
// One command-global union shares one vendored action between three
|
||
// importers, keeps an outer physical copy distinct, and also keeps two
|
||
// expanded identities distinct when their lexical vendor routes converge
|
||
// through symlinks on one physical directory.
|
||
rewritefile(compilertraces[si], "");
|
||
rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
let combinedwork: str = strings.concat(root, "/combined-work-",
|
||
stages[si]);
|
||
assert(os.mkdir(combinedwork, 448i32) == 0);
|
||
let combinedtargets: []str = [client, one, two, outerclient, alpha,
|
||
beta, pairvault];
|
||
let combinedpackages: []str = ["main", "main", "main", "main",
|
||
"main", "main", "math"];
|
||
let combinedcodes: []i32 = [11, 11, 11, 22, 61, 61];
|
||
let combinedoutputs: []str = [
|
||
strings.concat(root, "/combined-client-", stages[si]),
|
||
strings.concat(root, "/combined-one-", stages[si]),
|
||
strings.concat(root, "/combined-two-", stages[si]),
|
||
strings.concat(root, "/combined-outer-", stages[si]),
|
||
strings.concat(root, "/combined-alpha-", stages[si]),
|
||
strings.concat(root, "/combined-beta-", stages[si]),
|
||
strings.concat(root, "/combined-local-", stages[si])];
|
||
let combinedstatuses: []str = [
|
||
strings.concat(combinedoutputs[0], ".status"),
|
||
strings.concat(combinedoutputs[1], ".status"),
|
||
strings.concat(combinedoutputs[2], ".status"),
|
||
strings.concat(combinedoutputs[3], ".status"),
|
||
strings.concat(combinedoutputs[4], ".status"),
|
||
strings.concat(combinedoutputs[5], ".status"),
|
||
strings.concat(combinedoutputs[6], ".status")];
|
||
let combinedav: []str = alloc([],
|
||
(9 + combinedtargets.len * 10): u64)!;
|
||
append(combinedav, driver(stages[si]));
|
||
append(combinedav, "test"); append(combinedav, "-c");
|
||
append(combinedav, "-w"); append(combinedav, combinedwork);
|
||
append(combinedav, "-I"); append(combinedav, source);
|
||
append(combinedav, "--ww-package-build");
|
||
let ui: i32 = 0;
|
||
for (ui < combinedtargets.len) {
|
||
append(combinedav, "--ww-package-test");
|
||
append(combinedav, "build");
|
||
append(combinedav, combinedpackages[ui]);
|
||
append(combinedav, combinedpackages[ui]);
|
||
append(combinedav, "-");
|
||
append(combinedav, "-");
|
||
append(combinedav, combinedtargets[ui]);
|
||
append(combinedav, combinedoutputs[ui]);
|
||
append(combinedav, "-");
|
||
append(combinedav, combinedstatuses[ui]);
|
||
ui += 1;
|
||
};
|
||
append(combinedav, client);
|
||
runcommandenv(root, strings.concat("vendor-combined-", stages[si]),
|
||
combinedav, env, (180i64 * (time.second: i64)): time.duration,
|
||
&out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
let combinedtrace: str = readfile(compilertraces[si]);
|
||
assert(occurrences(combinedtrace, strings.concat("/", expanded,
|
||
".unit.new")) == 1);
|
||
assert(occurrences(combinedtrace,
|
||
"/domain.vendor.lib.math.unit.new") == 1);
|
||
assert(occurrences(combinedtrace,
|
||
"/alpha.client.vendor.lib.math.unit.new") == 1);
|
||
assert(occurrences(combinedtrace,
|
||
"/beta.client.vendor.lib.math.unit.new") == 1);
|
||
assert(has(combinedtrace, localidentity(pairvault, "math")));
|
||
assert(occurrences(combinedtrace, "/lib.leaf.unit.new") == 1);
|
||
let combinedidentities: []str = [expanded,
|
||
"alpha.client.vendor.lib.math", "beta.client.vendor.lib.math"];
|
||
let combinedsuffixes: []str = [".wwi", ".a"];
|
||
ui = 0;
|
||
for (ui < combinedidentities.len) {
|
||
let xi: i32 = 0;
|
||
for (xi < combinedsuffixes.len) {
|
||
let bytes: str = readfile(strings.concat(combinedwork, "/",
|
||
combinedidentities[ui], combinedsuffixes[xi]));
|
||
let index: i32 = ui * combinedsuffixes.len + xi;
|
||
if (si == 0) {
|
||
combinedrefs[index] = strings.dup(bytes);
|
||
} else {
|
||
assert(same(combinedrefs[index], bytes));
|
||
};
|
||
xi += 1;
|
||
};
|
||
ui += 1;
|
||
};
|
||
assert(os.exists(strings.concat(combinedwork,
|
||
"/domain.vendor.lib.math.wwi")));
|
||
assert(os.exists(strings.concat(combinedwork,
|
||
"/domain.vendor.lib.math.a")));
|
||
ui = 0;
|
||
for (ui < combinedcodes.len) {
|
||
let cav: []str = [combinedoutputs[ui]];
|
||
runcommand(root, strings.concat("vendor-combined-run-", stages[si],
|
||
"-", labels[ui]), cav,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, combinedcodes[ui]);
|
||
ui += 1;
|
||
};
|
||
assert(!os.exists(combinedoutputs[6]));
|
||
assert(same(readfile(combinedstatuses[6]), "ok\n"));
|
||
|
||
// A target interned by the allowed product is still checked at the
|
||
// forbidden source edge. Reversing product descriptors changes neither
|
||
// the diagnostic nor any status/tool state.
|
||
let orderwork: str = strings.concat(root, "/vendor-order-work-",
|
||
stages[si]);
|
||
assert(os.mkdir(orderwork, 448i32) == 0);
|
||
let orderallowed: str = strings.concat(root, "/vendor-order-allowed-",
|
||
stages[si]);
|
||
let orderforbidden: str = strings.concat(root,
|
||
"/vendor-order-forbidden-", stages[si]);
|
||
let orderallowedstatus: str = strings.concat(orderallowed, ".status");
|
||
let orderforbiddenstatus: str = strings.concat(orderforbidden,
|
||
".status");
|
||
writefile(orderallowedstatus, "sentinel-allowed\n");
|
||
writefile(orderforbiddenstatus, "sentinel-forbidden\n");
|
||
let orderforward: []str = [driver(stages[si]), "test", "-c", "-w",
|
||
orderwork, "-I", source, "--ww-package-build",
|
||
"--ww-package-test", "build", "main", "main", "-", "-", client,
|
||
orderallowed, "-", orderallowedstatus,
|
||
"--ww-package-test", "build", "main", "main", "-", "-", outsider,
|
||
orderforbidden, "-", orderforbiddenstatus, client];
|
||
let orderreverse: []str = [driver(stages[si]), "test", "-c", "-w",
|
||
orderwork, "-I", source, "--ww-package-build",
|
||
"--ww-package-test", "build", "main", "main", "-", "-", outsider,
|
||
orderforbidden, "-", orderforbiddenstatus,
|
||
"--ww-package-test", "build", "main", "main", "-", "-", client,
|
||
orderallowed, "-", orderallowedstatus, client];
|
||
let orderrequests: [][]str = [orderforward, orderreverse];
|
||
let orderdiags: []str = ["", ""];
|
||
let oi: i32 = 0;
|
||
for (oi < orderrequests.len) {
|
||
rewritefile(compilertraces[si], "");
|
||
rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
runcommandenv(root, strings.concat("vendor-order-", stages[si],
|
||
"-", labels[oi]), orderrequests[oi], env,
|
||
(180i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(same(out.stderr, outsidediag));
|
||
orderdiags[oi] = strings.dup(out.stderr);
|
||
assert(readfile(compilertraces[si]).len == 0);
|
||
assert(readfile(assemblertraces[si]).len == 0);
|
||
assert(readfile(linkertraces[si]).len == 0);
|
||
assert(same(readfile(orderallowedstatus), "sentinel-allowed\n"));
|
||
assert(same(readfile(orderforbiddenstatus),
|
||
"sentinel-forbidden\n"));
|
||
assert(!os.exists(strings.concat(orderwork, "/.wwtool.stamp")));
|
||
assert(!os.exists(orderallowed) && !os.exists(orderforbidden));
|
||
oi += 1;
|
||
};
|
||
assert(same(orderdiags[0], orderdiags[1]));
|
||
if (si == 0) { referenceorderdiag = strings.dup(orderdiags[0]); }
|
||
else { assert(same(referenceorderdiag, orderdiags[0])); };
|
||
|
||
// Production, same-package test, and external test sources all retain
|
||
// their short spelling while consuming the one expanded direct export.
|
||
let testworkroot: str = strings.concat(root, "/vendor-test-work-",
|
||
stages[si]);
|
||
assert(os.mkdir(testworkroot, 448i32) == 0);
|
||
rewritefile(compilertraces[si], "");
|
||
rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
let testav: []str = [driver(stages[si]), "test", "-w", testworkroot,
|
||
"-I", source, testclient];
|
||
runcommandenv(root, strings.concat("vendor-tests-", stages[si]), testav,
|
||
env, (180i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout, "same_vendor ... ok\n"));
|
||
assert(has(out.stdout, "external_vendor ... ok\n"));
|
||
if (si == 0) {
|
||
referencetestout = strings.dup(out.stdout);
|
||
referencetesterr = strings.dup(out.stderr);
|
||
} else {
|
||
assert(same(referencetestout, out.stdout));
|
||
assert(same(referencetesterr, out.stderr));
|
||
};
|
||
let testwork: str = strings.concat(testworkroot, "/");
|
||
let testactions: []str = ["domain.app.testclient-internal-test",
|
||
"domain.app.testclient_test-external-test"];
|
||
let testsuffixes: []str = [".unit.ww", ".wwi", ".a"];
|
||
ui = 0;
|
||
for (ui < testactions.len) {
|
||
let xi: i32 = 0;
|
||
for (xi < testsuffixes.len) {
|
||
let bytes: str = readfile(strings.concat(testwork,
|
||
testactions[ui], testsuffixes[xi]));
|
||
let index: i32 = ui * testsuffixes.len + xi;
|
||
if (si == 0) { testrefs[index] = strings.dup(bytes); }
|
||
else { assert(same(testrefs[index], bytes)); };
|
||
xi += 1;
|
||
};
|
||
assert(has(readfile(strings.concat(testwork, testactions[ui],
|
||
".unit.ww")), strings.concat("//ww:import-map lib.math ",
|
||
expanded, " ", hexbytes(nearest), "\n")));
|
||
ui += 1;
|
||
};
|
||
let testtrace: str = readfile(compilertraces[si]);
|
||
assert(occurrences(testtrace, strings.concat("/", expanded,
|
||
".unit.new")) == 1);
|
||
let internalline: str = linecontaining(testtrace,
|
||
"domain.app.testclient-internal-test.unit.new");
|
||
let externalline: str = linecontaining(testtrace,
|
||
"domain.app.testclient_test-external-test.unit.new");
|
||
assert(occurrences(internalline, strings.concat("<--import><", expanded,
|
||
"><", testwork, expanded, ".wwi.new>")) == 1);
|
||
assert(occurrences(externalline, strings.concat("<--import><", expanded,
|
||
"><", testwork, expanded, ".wwi.new>")) == 1);
|
||
assert(occurrences(internalline, strings.concat(
|
||
"<--import-map><lib.math><", expanded, ">")) == 1);
|
||
assert(occurrences(externalline, strings.concat(
|
||
"<--import-map><lib.math><", expanded, ">")) == 1);
|
||
assert(occurrences(externalline, strings.concat(
|
||
"<--import><domain.app.testclient><", testwork,
|
||
"domain.app.testclient-internal-test.wwi.new>")) == 1);
|
||
assert(!has(internalline, "lib.leaf.wwi"));
|
||
assert(!has(externalline, "lib.leaf.wwi"));
|
||
let mainline: str = linecontaining(testtrace,
|
||
"domain.app.testclient-test-main.unit.new");
|
||
assert(!has(mainline, expanded));
|
||
assert(occurrences(mainline, "<--test-target-package>") == 2);
|
||
assert(pos(mainline,
|
||
"<--test-target-package><domain.app.testclient>")
|
||
< pos(mainline,
|
||
"<--test-target-package><domain.app.testclient_test>"));
|
||
assert(has(mainline, strings.concat(
|
||
"<--import><domain.app.testclient><", testwork,
|
||
"domain.app.testclient-internal-test.wwi.new>")));
|
||
assert(has(mainline, strings.concat(
|
||
"<--import><domain.app.testclient_test><", testwork,
|
||
"domain.app.testclient_test-external-test.wwi.new>")));
|
||
let testlinktrace: str = readfile(linkertraces[si]);
|
||
assert(occurrences(testlinktrace, "\n") == 1);
|
||
assert(has(testlinktrace,
|
||
"/domain.app.testclient-test-main.a.new>"));
|
||
assert(!has(testlinktrace, ".wwi>"));
|
||
|
||
rewritefile(compilertraces[si], "");
|
||
rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
runcommandenv(root, strings.concat("vendor-tests-warm-", stages[si]),
|
||
testav, env, (180i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(same(referencetestout, out.stdout));
|
||
assert(same(referencetesterr, out.stderr));
|
||
assert(readfile(compilertraces[si]).len == 0);
|
||
assert(readfile(assemblertraces[si]).len == 0);
|
||
assert(occurrences(readfile(linkertraces[si]), "\n") == 1);
|
||
|
||
// A directly selected vendored test root is legal, and its external
|
||
// source still performs the ordinary nearest-vendor search first.
|
||
rewritefile(compilertraces[si], "");
|
||
rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
let vendortestav: []str = [driver(stages[si]), "test", "-I", source,
|
||
vendortest];
|
||
runcommandenv(root, strings.concat("vendor-literal-test-", stages[si]),
|
||
vendortestav, env,
|
||
(180i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout, "nested_external_vendor ... ok\n"));
|
||
if (si == 0) {
|
||
referencevendortestout = strings.dup(out.stdout);
|
||
referencevendortesterr = strings.dup(out.stderr);
|
||
} else {
|
||
assert(same(referencevendortestout, out.stdout));
|
||
assert(same(referencevendortesterr, out.stderr));
|
||
};
|
||
assert(has(readfile(compilertraces[si]),
|
||
"literal.owner.vendor.lib.math.vendor.lib.math.unit.ww"));
|
||
|
||
// A cold forbidden public test request is rejected before any
|
||
// persistent tool or package state is written.
|
||
let testbadworkroot: str = strings.concat(root, "/vendor-test-bad-work-",
|
||
stages[si]);
|
||
assert(os.mkdir(testbadworkroot, 448i32) == 0);
|
||
rewritefile(compilertraces[si], "");
|
||
rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
let testbadav: []str = [driver(stages[si]), "test", "-w",
|
||
testbadworkroot, "-I", source, testbad];
|
||
runcommandenv(root, strings.concat("vendor-test-bad-", stages[si]),
|
||
testbadav, env, (180i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
let testbaddiag: str = strings.concat(testbad,
|
||
"/same_test.ww:2:1: error: use of vendored package not allowed\n");
|
||
assert(has(out.stderr, testbaddiag));
|
||
if (si == 0) { referencetestbaddiag = strings.dup(out.stderr); }
|
||
else { assert(same(referencetestbaddiag, out.stderr)); };
|
||
assert(!os.exists(strings.concat(testbadworkroot, "/.wwtool.stamp")));
|
||
assert(readfile(compilertraces[si]).len == 0);
|
||
assert(readfile(assemblertraces[si]).len == 0);
|
||
assert(readfile(linkertraces[si]).len == 0);
|
||
|
||
// The coordinator's synthetic support/main edges stay synthetic, while
|
||
// imports written in the real test-support source use this same resolver.
|
||
let supportenv: []str = alloc([], (env.len + 1): u64)!;
|
||
ei = 0;
|
||
for (ei < env.len) { append(supportenv, env[ei]); ei += 1; };
|
||
append(supportenv, strings.concat("WW_SRCLIB=", toolsource));
|
||
let supportworkroot: str = strings.concat(root, "/vendor-support-work-",
|
||
stages[si]);
|
||
assert(os.mkdir(supportworkroot, 448i32) == 0);
|
||
rewritefile(compilertraces[si], "");
|
||
rewritefile(assemblertraces[si], "");
|
||
rewritefile(linkertraces[si], "");
|
||
let supportav: []str = [driver(stages[si]), "test", "-w",
|
||
supportworkroot, "-I", source, supportcase];
|
||
runcommandenv(root, strings.concat("vendor-support-", stages[si]),
|
||
supportav, supportenv,
|
||
(240i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout, "support_runs ... ok\n"));
|
||
assert(out.stderr.len == 0);
|
||
if (si == 0) { referencesupportout = strings.dup(out.stdout); }
|
||
else { assert(same(referencesupportout, out.stdout)); };
|
||
let supportwork: str = strings.concat(supportworkroot, "/");
|
||
let supportunit: str = readfile(strings.concat(supportwork,
|
||
"test.unit.ww"));
|
||
assert(has(supportunit, strings.concat("//ww:import-map fnmatch ",
|
||
"test.vendor.fnmatch ", hexbytes(toolvendor), "\n")));
|
||
assert(has(readfile(strings.concat(supportwork,
|
||
"test.vendor.fnmatch.unit.ww")), strings.concat(
|
||
"//ww:vendor-dir ", hexbytes(toolvendor), "\n")));
|
||
let supporttrace: str = readfile(compilertraces[si]);
|
||
let supportline: str = linecontaining(supporttrace, "/test.unit.new");
|
||
assert(occurrences(supportline,
|
||
"<--import><test.vendor.fnmatch>") == 1);
|
||
assert(occurrences(supportline,
|
||
"<--import-map><fnmatch><test.vendor.fnmatch>") == 1);
|
||
assert(!has(supportline, "/ascii.wwi>"));
|
||
assert(occurrences(supporttrace,
|
||
"/test.vendor.fnmatch.unit.new") == 1);
|
||
let supportmainline: str = linecontaining(supporttrace,
|
||
"supportcase-test-main.unit.new");
|
||
assert(!has(supportmainline, "test.vendor.fnmatch"));
|
||
assert(!has(readfile(linkertraces[si]), ".wwi>"));
|
||
let supportsuffixes: []str = [".unit.ww", ".wwi", ".a"];
|
||
ui = 0;
|
||
for (ui < supportsuffixes.len) {
|
||
let bytes: str = readfile(strings.concat(supportwork,
|
||
"test.vendor.fnmatch", supportsuffixes[ui]));
|
||
if (si == 0) { supportrefs[ui] = strings.dup(bytes); }
|
||
else { assert(same(supportrefs[ui], bytes)); };
|
||
ui += 1;
|
||
};
|
||
|
||
si += 1;
|
||
};
|
||
clean(root);
|
||
};
|
||
|
||
// The package-level -o contract: -c -o names the single package's
|
||
// artifact in place of the fixed <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 declared_name_identity_and_file_import_scope() void = {
|
||
let root: str = fresh();
|
||
let source: str = strings.concat(root, "/source");
|
||
let tools: str = strings.concat(root, "/tools");
|
||
let codec: str = strings.concat(source, "/acme/codec");
|
||
let bridge: str = strings.concat(source, "/acme/bridge");
|
||
let direct: str = strings.concat(source, "/cmd/direct");
|
||
let app: str = strings.concat(source, "/cmd/app");
|
||
let scopeone: str = strings.concat(source, "/scope/one");
|
||
let scopetwo: str = strings.concat(source, "/scope/two");
|
||
let scoped: str = strings.concat(source, "/cmd/scoped");
|
||
let repeated: str = strings.concat(source, "/cmd/repeated");
|
||
let commitcodec: str = strings.concat(source, "/commit/codec");
|
||
let commitapp: str = strings.concat(source, "/cmd/commit");
|
||
let leafbad: str = strings.concat(source, "/cmd/leafbad");
|
||
let leak: str = strings.concat(source, "/cmd/leak");
|
||
let duplicate: str = strings.concat(source, "/cmd/duplicate");
|
||
let collision: str = strings.concat(source, "/cmd/collision");
|
||
let crosscollision: str = strings.concat(source, "/cmd/crosscollision");
|
||
let conflict: str = strings.concat(source, "/conflict/pkg");
|
||
let leafmain: str = strings.concat(source, "/domain/main");
|
||
let program: str = strings.concat(source, "/domain/program");
|
||
let leafmainuser: str = strings.concat(source, "/cmd/leafmain");
|
||
let programuser: str = strings.concat(source, "/cmd/programuser");
|
||
let testcodec: str = strings.concat(source, "/testpkg/codec");
|
||
let testhelper: str = strings.concat(source, "/testonly/helper");
|
||
let vendored: str = strings.concat(source,
|
||
"/vend/vendor/short/codec");
|
||
let vendorclient: str = strings.concat(source, "/vend/client");
|
||
let dirs: []str = [source, tools, codec, bridge, direct, app, scopeone,
|
||
scopetwo, scoped, repeated, commitcodec, commitapp, leafbad, leak,
|
||
duplicate, collision,
|
||
crosscollision, conflict, leafmain, program, leafmainuser, programuser, testcodec,
|
||
testhelper, vendored, vendorclient];
|
||
let di: i32 = 0;
|
||
for (di < dirs.len) { mkdirall(dirs[di]); di += 1; };
|
||
|
||
let wirebody: str = strings.concat(
|
||
"package wire;\n",
|
||
"export type Thing = i32;\n",
|
||
"export fn value() i32 = { return 42; };\n");
|
||
let cablebody: str = strings.concat(
|
||
"package cable;\n",
|
||
"export type Thing = i32;\n",
|
||
"export fn value() i32 = { return 42; };\n");
|
||
let codecfile: str = strings.concat(codec, "/codec.ww");
|
||
writefile(codecfile, wirebody);
|
||
let commitcodecfile: str = strings.concat(commitcodec, "/codec.ww");
|
||
let commitappfile: str = strings.concat(commitapp, "/main.ww");
|
||
writefile(commitcodecfile, wirebody);
|
||
writefile(commitappfile, strings.concat(
|
||
"package main;\nimport commit.codec;\n",
|
||
"fn main() i32 = { return wire.value(); };\n"));
|
||
let bridgefile: str = strings.concat(bridge, "/bridge.ww");
|
||
let stablebridgebody: str = strings.concat(
|
||
"package bridge;\nimport stable acme.codec;\n",
|
||
"export type BridgedThing = stable.Thing;\n",
|
||
"export fn bridged() i32 = { return stable.value(); };\n");
|
||
let steadybridgebody: str = strings.concat(
|
||
"package bridge;\nimport steady acme.codec;\n",
|
||
"export type BridgedThing = steady.Thing;\n",
|
||
"export fn bridged() i32 = { return steady.value(); };\n");
|
||
writefile(bridgefile, stablebridgebody);
|
||
writefile(strings.concat(direct, "/main.ww"), strings.concat(
|
||
"package main;\nimport acme.codec;\n",
|
||
"fn main() i32 = { return wire.value(); };\n"));
|
||
writefile(strings.concat(app, "/main.ww"), strings.concat(
|
||
"package main;\nimport acme.bridge;\n",
|
||
"fn main() i32 = { return bridge.bridged(); };\n"));
|
||
|
||
writefile(strings.concat(scopeone, "/one.ww"),
|
||
"package same;\nexport fn value() i32 = { return 20; };\n");
|
||
writefile(strings.concat(scopetwo, "/two.ww"),
|
||
"package same;\nexport fn value() i32 = { return 22; };\n");
|
||
writefile(strings.concat(scoped, "/a.ww"), strings.concat(
|
||
"package main;\nimport shared scope.one;\n",
|
||
"fn left() i32 = { return shared.value(); };\n"));
|
||
writefile(strings.concat(scoped, "/b.ww"), strings.concat(
|
||
"package main;\nimport shared scope.two;\n",
|
||
"fn right() i32 = { return shared.value(); };\n"));
|
||
writefile(strings.concat(scoped, "/main.ww"),
|
||
"package main;\nfn main() i32 = { return left() + right(); };\n");
|
||
|
||
writefile(strings.concat(repeated, "/a.ww"), strings.concat(
|
||
"package main;\nimport stable acme.codec;\n",
|
||
"fn first() i32 = { return stable.value(); };\n"));
|
||
writefile(strings.concat(repeated, "/b.ww"), strings.concat(
|
||
"package main;\nimport acme.codec;\n",
|
||
"fn second() i32 = { return wire.value(); };\n"));
|
||
writefile(strings.concat(repeated, "/main.ww"), strings.concat(
|
||
"package main;\n",
|
||
"fn main() i32 = { return first() - second(); };\n"));
|
||
|
||
writefile(strings.concat(leafbad, "/main.ww"), strings.concat(
|
||
"package main;\nimport acme.codec;\n",
|
||
"fn main() i32 = { return codec.value(); };\n"));
|
||
writefile(strings.concat(leak, "/a.ww"), strings.concat(
|
||
"package main;\nimport acme.codec;\n",
|
||
"fn anchor() i32 = { return 1; };\n"));
|
||
writefile(strings.concat(leak, "/b.ww"),
|
||
"package main;\nfn leaked() i32 = { return wire.value(); };\n");
|
||
writefile(strings.concat(leak, "/main.ww"),
|
||
"package main;\nfn main() i32 = { return leaked(); };\n");
|
||
writefile(strings.concat(duplicate, "/main.ww"), strings.concat(
|
||
"package main;\nimport scope.one;\nimport scope.two;\n",
|
||
"fn main() i32 = { return same.value(); };\n"));
|
||
writefile(strings.concat(collision, "/main.ww"), strings.concat(
|
||
"package main;\nimport scope.one;\n",
|
||
"fn same() i32 = { return 0; };\n",
|
||
"fn main() i32 = { return same(); };\n"));
|
||
writefile(strings.concat(crosscollision, "/a.ww"), strings.concat(
|
||
"package main;\nimport scope.one;\n",
|
||
"fn fromdep() i32 = { return same.value(); };\n"));
|
||
writefile(strings.concat(crosscollision, "/b.ww"), strings.concat(
|
||
"package main;\nfn same() i32 = { return 0; };\n",
|
||
"fn main() i32 = { return fromdep(); };\n"));
|
||
writefile(strings.concat(conflict, "/a.ww"),
|
||
"package first;\nexport fn a() i32 = { return 1; };\n");
|
||
writefile(strings.concat(conflict, "/b.ww"),
|
||
"package second;\nexport fn b() i32 = { return 2; };\n");
|
||
|
||
writefile(strings.concat(leafmain, "/utility.ww"),
|
||
"package utility;\nexport fn value() i32 = { return 7; };\n");
|
||
writefile(strings.concat(program, "/program.ww"),
|
||
"package main;\nexport fn value() i32 = { return 9; };\n");
|
||
writefile(strings.concat(leafmainuser, "/main.ww"), strings.concat(
|
||
"package main;\nimport domain.main;\n",
|
||
"fn main() i32 = { return utility.value(); };\n"));
|
||
writefile(strings.concat(programuser, "/main.ww"), strings.concat(
|
||
"package main;\nimport stable domain.program;\n",
|
||
"fn main() i32 = { return stable.value(); };\n"));
|
||
|
||
writefile(strings.concat(testhelper, "/helper.ww"),
|
||
"package helper;\nexport fn value() i32 = { return 1; };\n");
|
||
writefile(strings.concat(testcodec, "/codec.ww"), wirebody);
|
||
writefile(strings.concat(testcodec, "/internal_test.ww"), strings.concat(
|
||
"package wire;\nimport assist testonly.helper;\n",
|
||
"@test fn internal_name_uses_declaration() void = {\n",
|
||
" assert(value() + assist.value() == 43);\n};\n"));
|
||
writefile(strings.concat(testcodec, "/external_test.ww"), strings.concat(
|
||
"package wire_test;\nimport prod testpkg.codec;\n",
|
||
"@test fn external_name_uses_declaration() void = {\n",
|
||
" assert(prod.value() == 42);\n};\n"));
|
||
|
||
writefile(strings.concat(vendored, "/codec.ww"),
|
||
"package cable;\nexport fn value() i32 = { return 13; };\n");
|
||
writefile(strings.concat(vendorclient, "/a.ww"), strings.concat(
|
||
"package main;\nimport stable short.codec;\n",
|
||
"fn vendora() i32 = { return stable.value(); };\n"));
|
||
writefile(strings.concat(vendorclient, "/b.ww"), strings.concat(
|
||
"package main;\nimport short.codec;\n",
|
||
"fn vendorb() i32 = { return cable.value(); };\n"));
|
||
writefile(strings.concat(vendorclient, "/main.ww"),
|
||
"package main;\nfn main() i32 = { return vendora(); };\n");
|
||
|
||
let compilerwrapper: str = strings.concat(tools, "/w6c.sh");
|
||
writeexecutable(compilerwrapper, strings.concat(
|
||
"#!/bin/sh\n",
|
||
"printf 'BEGIN' >> \"$WW_NAME_COMPILER_TRACE\"\n",
|
||
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
||
"\"$WW_NAME_COMPILER_TRACE\"; done\n",
|
||
"printf '\\n' >> \"$WW_NAME_COMPILER_TRACE\"\n",
|
||
"exec \"$WW_NAME_REAL_COMPILER\" \"$@\"\n"));
|
||
|
||
let stages: []str = ["ww", "ww_ww"];
|
||
let compilers: []str = ["w6c", "w6c_ww"];
|
||
let assemblers: []str = ["w6a", "w6a_ww"];
|
||
let linkers: []str = ["w6l", "w6l_ww"];
|
||
let works: []str = [strings.concat(root, "/c-work"),
|
||
strings.concat(root, "/ww-work")];
|
||
let traces: []str = [strings.concat(root, "/c-compiler"),
|
||
strings.concat(root, "/ww-compiler")];
|
||
let directbins: []str = [strings.concat(root, "/c-direct"),
|
||
strings.concat(root, "/ww-direct")];
|
||
let appbins: []str = [strings.concat(root, "/c-app"),
|
||
strings.concat(root, "/ww-app")];
|
||
let scopedbins: []str = [strings.concat(root, "/c-scoped"),
|
||
strings.concat(root, "/ww-scoped")];
|
||
let repeatedbins: []str = [strings.concat(root, "/c-repeated"),
|
||
strings.concat(root, "/ww-repeated")];
|
||
let commitworks: []str = [strings.concat(root, "/c-commit-work"),
|
||
strings.concat(root, "/ww-commit-work")];
|
||
let commitcoldbins: []str = [strings.concat(root, "/c-commit-cold"),
|
||
strings.concat(root, "/ww-commit-cold")];
|
||
let commitrejectbins: []str = [strings.concat(root, "/c-commit-reject"),
|
||
strings.concat(root, "/ww-commit-reject")];
|
||
let commitfixedbins: []str = [strings.concat(root, "/c-commit-fixed"),
|
||
strings.concat(root, "/ww-commit-fixed")];
|
||
let leafmainbins: []str = [strings.concat(root, "/c-leafmain"),
|
||
strings.concat(root, "/ww-leafmain")];
|
||
let vendorbins: []str = [strings.concat(root, "/c-vendor"),
|
||
strings.concat(root, "/ww-vendor")];
|
||
let actions: []str = ["acme.codec", "acme.bridge", "cmd.direct",
|
||
"cmd.app", "scope.one", "scope.two", "cmd.scoped",
|
||
"cmd.repeated", "domain.main", "cmd.leafmain",
|
||
"vend.vendor.short.codec", "vend.client"];
|
||
let suffixes: []str = [".unit.ww", ".wwi", ".a"];
|
||
let artifactrefs: []str = alloc([], (actions.len * suffixes.len): u64)!;
|
||
let ari: i32 = 0;
|
||
for (ari < actions.len * suffixes.len) {
|
||
append(artifactrefs, "");
|
||
ari += 1;
|
||
};
|
||
let binaryrefs: []str = ["", "", "", "", "", ""];
|
||
let diagnosticrefs: []str = ["", "", "", "", "", ""];
|
||
let directtraceref: str = "";
|
||
let scopedtraceref: str = "";
|
||
let repeatedtraceref: str = "";
|
||
let changedtraceref: str = "";
|
||
let aliastraceref: str = "";
|
||
let commitbindingref: str = "";
|
||
let commitbinref: str = "";
|
||
let initialwwiref: str = "";
|
||
let testoutref: str = "";
|
||
let baseenv: []str = os.getenvs();
|
||
let si: i32 = 0;
|
||
for (si < stages.len) {
|
||
rewritefile(codecfile, wirebody);
|
||
rewritefile(bridgefile, stablebridgebody);
|
||
rewritefile(commitcodecfile, wirebody);
|
||
rewritefile(commitappfile, strings.concat(
|
||
"package main;\nimport commit.codec;\n",
|
||
"fn main() i32 = { return wire.value(); };\n"));
|
||
assert(os.mkdir(works[si], 448i32) == 0);
|
||
assert(os.mkdir(commitworks[si], 448i32) == 0);
|
||
writefile(traces[si], "");
|
||
let env: []str = alloc([], (baseenv.len + 7): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < baseenv.len) {
|
||
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6A=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6L=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_SRCLIB=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_NAME_COMPILER_TRACE=")
|
||
&& !strings.hasprefix(baseenv[ei],
|
||
"WW_NAME_REAL_COMPILER=")) {
|
||
append(env, baseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(env, strings.concat("WW_W6C=", compilerwrapper));
|
||
append(env, strings.concat("WW_W6A=", driver(assemblers[si])));
|
||
append(env, strings.concat("WW_W6L=", driver(linkers[si])));
|
||
append(env, strings.concat("WW_SRCLIB=", repo(), "/lib"));
|
||
append(env, strings.concat("WW_NAME_COMPILER_TRACE=", traces[si]));
|
||
append(env, strings.concat("WW_NAME_REAL_COMPILER=",
|
||
driver(compilers[si])));
|
||
|
||
// A directly selected library keeps canonical action/artifact identity
|
||
// even though its declaration is unrelated to the path leaf.
|
||
let out: commandout;
|
||
let selectav: []str = [driver(stages[si]), "build", "-w", works[si],
|
||
"-I", source, codec];
|
||
runcommandenv(root, strings.concat("name-select-", stages[si]),
|
||
selectav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
assert(os.exists(strings.concat(works[si], "/acme.codec.wwi")));
|
||
assert(!os.exists(strings.concat(works[si], "/wire.wwi")));
|
||
let initialwwi: str = readfile(strings.concat(works[si],
|
||
"/acme.codec.wwi"));
|
||
assert(has(initialwwi, "//ww:module acme.codec\npackage wire;\n"));
|
||
if (si == 0) { initialwwiref = strings.dup(initialwwi); }
|
||
else { assert(same(initialwwiref, initialwwi)); };
|
||
|
||
rewritefile(traces[si], "");
|
||
let directav: []str = [driver(stages[si]), "build", "-w", works[si],
|
||
"-I", source, "-o", directbins[si], direct];
|
||
runcommandenv(root, strings.concat("name-direct-", stages[si]),
|
||
directav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
let directtrace: str = readfile(traces[si]);
|
||
let directline: str = linecontaining(directtrace,
|
||
"cmd.direct.unit.new");
|
||
assert(occurrences(directline, "<--import><acme.codec>") == 1);
|
||
assert(!has(directline, "wire.wwi"));
|
||
let ndirect: str = normalizedtrace(directtrace,
|
||
strings.concat(works[si], "/"), directbins[si]);
|
||
if (si == 0) { directtraceref = strings.dup(ndirect); }
|
||
else { assert(same(directtraceref, ndirect)); };
|
||
let runav: []str = [directbins[si]];
|
||
runcommand(root, strings.concat("name-direct-run-", stages[si]), runav,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 42);
|
||
|
||
// The package and command form one publication transaction. A changed
|
||
// declared name is visible to the importer through the staged .wwi, but
|
||
// its resulting semantic rejection must preserve the complete prior
|
||
// generation and force the next request to reconsider both actions.
|
||
let commitcoldav: []str = [driver(stages[si]), "build", "-w",
|
||
commitworks[si], "-I", source, "-o", commitcoldbins[si], commitapp];
|
||
runcommandenv(root, strings.concat("name-commit-cold-", stages[si]),
|
||
commitcoldav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let commitrun: []str = [commitcoldbins[si]];
|
||
runcommand(root, strings.concat("name-commit-cold-run-", stages[si]),
|
||
commitrun, (30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 42);
|
||
let commitpaths: []str = ["/.wwtool.ww", "/.wwtool.w6c",
|
||
"/.wwtool.w6a", "/.wwtool.stamp", "/commit.codec.unit.ww",
|
||
"/commit.codec.wwi", "/commit.codec.s", "/commit.codec.o",
|
||
"/commit.codec.a", "/cmd.commit.unit.ww", "/cmd.commit.wwi",
|
||
"/cmd.commit.s", "/cmd.commit.o", "/cmd.commit.a",
|
||
"/cmd.commit.init.unit.ww", "/cmd.commit.init.s",
|
||
"/cmd.commit.init.o"];
|
||
let commitsnapshots: []str = alloc([], commitpaths.len: u64)!;
|
||
let cpi: i32 = 0;
|
||
for (cpi < commitpaths.len) {
|
||
append(commitsnapshots, readfile(strings.concat(commitworks[si],
|
||
commitpaths[cpi])));
|
||
cpi += 1;
|
||
};
|
||
let commitcoldbytes: str = readfile(commitcoldbins[si]);
|
||
rewritefile(commitcodecfile, cablebody);
|
||
let commitrejectav: []str = [driver(stages[si]), "build", "-w",
|
||
commitworks[si], "-I", source, "-o", commitrejectbins[si],
|
||
commitapp];
|
||
let partialsuffixes: []str = [".unit.new", ".wwi.new", ".s.new",
|
||
".o.new", ".a.new", ".init.unit.new", ".init.s.new",
|
||
".init.o.new"];
|
||
let rejectedactions: []str = ["commit.codec", "cmd.commit"];
|
||
let attemptlabels: []str = ["0", "1"];
|
||
let attempt: i32 = 0;
|
||
for (attempt < 2) {
|
||
rewritefile(traces[si], "");
|
||
runcommandenv(root, strings.concat("name-commit-reject-",
|
||
stages[si], "-", attemptlabels[attempt]),
|
||
commitrejectav, env,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, "imported as cable and not used"));
|
||
assert(has(out.stderr, "undefined: wire"));
|
||
assert(has(out.stderr, "ww: w6c failed for cmd.commit\n"));
|
||
let ncommitbinding: str = normalizedtrace(out.stderr,
|
||
strings.concat(commitworks[si], "/"), commitrejectbins[si]);
|
||
if (si == 0 && attempt == 0) {
|
||
commitbindingref = strings.dup(ncommitbinding);
|
||
} else { assert(same(commitbindingref, ncommitbinding)); };
|
||
let rejecttrace: str = readfile(traces[si]);
|
||
assert(occurrences(rejecttrace,
|
||
"/commit.codec.unit.new") == 1);
|
||
assert(occurrences(rejecttrace,
|
||
"/cmd.commit.unit.new") == 1);
|
||
cpi = 0;
|
||
for (cpi < commitpaths.len) {
|
||
assert(same(commitsnapshots[cpi], readfile(strings.concat(
|
||
commitworks[si], commitpaths[cpi]))));
|
||
cpi += 1;
|
||
};
|
||
assert(same(commitcoldbytes, readfile(commitcoldbins[si])));
|
||
let rai: i32 = 0;
|
||
for (rai < rejectedactions.len) {
|
||
let psi: i32 = 0;
|
||
for (psi < partialsuffixes.len) {
|
||
assert(!os.exists(strings.concat(commitworks[si], "/",
|
||
rejectedactions[rai], partialsuffixes[psi])));
|
||
psi += 1;
|
||
};
|
||
rai += 1;
|
||
};
|
||
assert(!os.exists(commitrejectbins[si]));
|
||
assert(!os.exists(strings.concat(commitrejectbins[si], ".new")));
|
||
attempt += 1;
|
||
};
|
||
rewritefile(commitappfile, strings.concat(
|
||
"package main;\nimport commit.codec;\n",
|
||
"fn main() i32 = { return cable.value(); };\n"));
|
||
let commitfixedav: []str = [driver(stages[si]), "build", "-w",
|
||
commitworks[si], "-I", source, "-o", commitfixedbins[si], commitapp];
|
||
runcommandenv(root, strings.concat("name-commit-fixed-", stages[si]),
|
||
commitfixedav, env,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let commitfixedrun: []str = [commitfixedbins[si]];
|
||
runcommand(root, strings.concat("name-commit-fixed-run-", stages[si]),
|
||
commitfixedrun, (30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 42);
|
||
assert(has(readfile(strings.concat(commitworks[si],
|
||
"/commit.codec.wwi")),
|
||
"//ww:module commit.codec\npackage cable;\n"));
|
||
assert(same(commitcoldbytes, readfile(commitcoldbins[si])));
|
||
let commitbinbytes: str = readfile(commitfixedbins[si]);
|
||
if (si == 0) { commitbinref = strings.dup(commitbinbytes); }
|
||
else { assert(same(commitbinref, commitbinbytes)); };
|
||
|
||
// The same declared qualifier belongs to each importing source file,
|
||
// not to the package-wide namespace.
|
||
rewritefile(traces[si], "");
|
||
let scopedav: []str = [driver(stages[si]), "build", "-w", works[si],
|
||
"-I", source, "-o", scopedbins[si], scoped];
|
||
runcommandenv(root, strings.concat("name-scoped-", stages[si]),
|
||
scopedav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let scopedtrace: str = readfile(traces[si]);
|
||
let scopedline: str = linecontaining(scopedtrace,
|
||
"cmd.scoped.unit.new");
|
||
assert(occurrences(scopedline, "<--import><scope.one>") == 1);
|
||
assert(occurrences(scopedline, "<--import><scope.two>") == 1);
|
||
let nscoped: str = normalizedtrace(scopedtrace,
|
||
strings.concat(works[si], "/"), scopedbins[si]);
|
||
if (si == 0) { scopedtraceref = strings.dup(nscoped); }
|
||
else { assert(same(scopedtraceref, nscoped)); };
|
||
let scopedrun: []str = [scopedbins[si]];
|
||
runcommand(root, strings.concat("name-scoped-run-", stages[si]),
|
||
scopedrun, (30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 42);
|
||
|
||
// Two per-file binding records for one canonical package still form
|
||
// exactly one direct graph edge and one compiler input.
|
||
rewritefile(traces[si], "");
|
||
let repeatedav: []str = [driver(stages[si]), "build", "-w", works[si],
|
||
"-I", source, "-o", repeatedbins[si], repeated];
|
||
runcommandenv(root, strings.concat("name-repeated-", stages[si]),
|
||
repeatedav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let repeatedunit: str = readfile(strings.concat(works[si],
|
||
"/cmd.repeated.unit.ww"));
|
||
assert(occurrences(repeatedunit, "import stable acme.codec;") == 1);
|
||
assert(occurrences(repeatedunit, "import acme.codec;") == 1);
|
||
let repeatedtrace: str = readfile(traces[si]);
|
||
let repeatedline: str = linecontaining(repeatedtrace,
|
||
"cmd.repeated.unit.new");
|
||
assert(occurrences(repeatedline, "<--import><acme.codec>") == 1);
|
||
assert(occurrences(repeatedtrace, "acme.codec.unit.new") == 0);
|
||
let nrepeated: str = normalizedtrace(repeatedtrace,
|
||
strings.concat(works[si], "/"), repeatedbins[si]);
|
||
if (si == 0) { repeatedtraceref = strings.dup(nrepeated); }
|
||
else { assert(same(repeatedtraceref, nrepeated)); };
|
||
let repeatedrun: []str = [repeatedbins[si]];
|
||
runcommand(root, strings.concat("name-repeated-run-", stages[si]),
|
||
repeatedrun, (30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
|
||
// A path ending in main remains importable when its declaration is not
|
||
// main. A different path declaring main is rejected by loaded name.
|
||
let leafmainav: []str = [driver(stages[si]), "build", "-w", works[si],
|
||
"-I", source, "-o", leafmainbins[si], leafmainuser];
|
||
runcommandenv(root, strings.concat("name-leafmain-", stages[si]),
|
||
leafmainav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let leafmainrun: []str = [leafmainbins[si]];
|
||
runcommand(root, strings.concat("name-leafmain-run-", stages[si]),
|
||
leafmainrun, (30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 7);
|
||
|
||
// Compiler-owned binding failures are deterministic across stages and
|
||
// never commit an action voucher/artifact or publish a binary.
|
||
let rejectwork: str = strings.concat(root, "/reject-work-", stages[si]);
|
||
assert(os.mkdir(rejectwork, 448i32) == 0);
|
||
let rejecttargets: []str = [leafbad, leak, duplicate, collision,
|
||
crosscollision, programuser];
|
||
let rejectactions: []str = ["cmd.leafbad", "cmd.leak",
|
||
"cmd.duplicate", "cmd.collision", "cmd.crosscollision",
|
||
"cmd.programuser"];
|
||
let rejectneedles: []str = ["undefined: codec",
|
||
"package 'wire' is not directly imported",
|
||
"same redeclared in this block",
|
||
"same already declared through import of package same",
|
||
"same already declared through import of package same",
|
||
"package domain.program is a program, not an importable package"];
|
||
let rj: i32 = 0;
|
||
for (rj < rejecttargets.len) {
|
||
let badbin: str = strings.concat(root, "/reject-bin-", stages[si],
|
||
"-", rejectactions[rj]);
|
||
let badav: []str = [driver(stages[si]), "build", "-w", rejectwork,
|
||
"-I", source, "-o", badbin, rejecttargets[rj]];
|
||
runcommandenv(root, strings.concat("name-reject-", stages[si],
|
||
"-", rejectactions[rj]), badav, env,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, rejectneedles[rj]));
|
||
let normalized: str = normalizedtrace(out.stderr,
|
||
strings.concat(rejectwork, "/"), badbin);
|
||
if (rj == 2) {
|
||
assert(same(normalized, strings.concat(
|
||
"$WORK/cmd.duplicate.unit.new:4:1: error: same redeclared in this block\n",
|
||
"\t$WORK/cmd.duplicate.unit.new:3:1: other declaration of same\n",
|
||
"$WORK/cmd.duplicate.unit.new:4:1: error: \"scope.two\" imported as same and not used\n",
|
||
"ww: w6c failed for cmd.duplicate\n")));
|
||
};
|
||
if (rj == 3) {
|
||
assert(same(normalized, strings.concat(
|
||
"$WORK/cmd.collision.unit.new:3:1: error: \"scope.one\" imported as same and not used\n",
|
||
"$WORK/cmd.collision.unit.new:4:1: error: same already declared through import of package same (\"scope.one\")\n",
|
||
"\t$WORK/cmd.collision.unit.new:3:1: other declaration of same\n",
|
||
"ww: w6c failed for cmd.collision\n")));
|
||
};
|
||
if (si == 0) { diagnosticrefs[rj] = strings.dup(normalized); }
|
||
else { assert(same(diagnosticrefs[rj], normalized)); };
|
||
assert(!os.exists(badbin));
|
||
let committed: []str = [".unit.ww", ".wwi", ".s", ".o", ".a",
|
||
".unit.new", ".wwi.new", ".s.new", ".o.new", ".a.new"];
|
||
let ci: i32 = 0;
|
||
for (ci < committed.len) {
|
||
assert(!os.exists(strings.concat(rejectwork, "/",
|
||
rejectactions[rj], committed[ci])));
|
||
ci += 1;
|
||
};
|
||
rj += 1;
|
||
};
|
||
|
||
let conflictav: []str = [driver(stages[si]), "build", "-w",
|
||
rejectwork, "-I", source, conflict];
|
||
runcommandenv(root, strings.concat("name-conflict-", stages[si]),
|
||
conflictav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, "conflicting package names"));
|
||
assert(!os.exists(strings.concat(rejectwork,
|
||
"/conflict.pkg.unit.ww")));
|
||
|
||
// Production/internal/external naming uses the declaration, while every
|
||
// action and generated-main artifact retains canonical variant identity.
|
||
let testwork: str = strings.concat(root, "/test-work-", stages[si]);
|
||
assert(os.mkdir(testwork, 448i32) == 0);
|
||
rewritefile(traces[si], "");
|
||
let testav: []str = [driver(stages[si]), "test", "-w", testwork,
|
||
"-I", source, testcodec];
|
||
runcommandenv(root, strings.concat("name-test-", stages[si]), testav,
|
||
env, (240i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout, "internal_name_uses_declaration ... ok\n"));
|
||
assert(has(out.stdout, "external_name_uses_declaration ... ok\n"));
|
||
assert(out.stderr.len == 0);
|
||
if (si == 0) { testoutref = strings.dup(out.stdout); }
|
||
else { assert(same(testoutref, out.stdout)); };
|
||
assert(has(out.stdout, strings.concat("ok ", testcodec,
|
||
" [wire]\n")));
|
||
assert(!os.exists(strings.concat(testwork,
|
||
"/testpkg.codec.unit.ww")));
|
||
let internalunit: str = readfile(strings.concat(testwork,
|
||
"/testpkg.codec-internal-test.unit.ww"));
|
||
let externalunit: str = readfile(strings.concat(testwork,
|
||
"/testpkg.codec_test-external-test.unit.ww"));
|
||
assert(has(internalunit, "export type Thing = i32;"));
|
||
assert(has(internalunit, "export fn value()"));
|
||
assert(has(internalunit, "import assist testonly.helper;"));
|
||
assert(!has(externalunit, "testonly.helper"));
|
||
assert(has(externalunit, "package wire_test;"));
|
||
assert(has(externalunit,
|
||
"//ww:direct-export testpkg.codec "));
|
||
assert(os.exists(strings.concat(testwork,
|
||
"/testpkg.codec-test-main.a")));
|
||
assert(!os.exists(strings.concat(testwork,
|
||
"/testpkg.codec-internal-test-main.a")));
|
||
assert(!os.exists(strings.concat(testwork,
|
||
"/testpkg.codec_test-external-test-main.a")));
|
||
assert(os.exists(strings.concat(testwork, "/test.a")));
|
||
let testtrace: str = readfile(traces[si]);
|
||
let externalline: str = linecontaining(testtrace,
|
||
"/testpkg.codec_test-external-test.unit.new");
|
||
assert(has(externalline, strings.concat(
|
||
"<--import><testpkg.codec><", testwork,
|
||
"/testpkg.codec-internal-test.wwi.new>")));
|
||
let mainline: str = linecontaining(testtrace,
|
||
"/testpkg.codec-test-main.unit.new");
|
||
assert(occurrences(mainline, "<--test-target-package>") == 2);
|
||
assert(pos(mainline, "<--test-target-package><testpkg.codec>")
|
||
< pos(mainline,
|
||
"<--test-target-package><testpkg.codec_test>"));
|
||
let mainunit: str = readfile(strings.concat(testwork,
|
||
"/testpkg.codec-test-main.unit.ww"));
|
||
assert(has(mainunit, "import testpkg.codec;"));
|
||
assert(has(mainunit, "import testpkg.codec_test;"));
|
||
assert(has(mainunit, "import test;"));
|
||
assert(!has(mainunit, "testonly.helper"));
|
||
|
||
let recursivebuildwork: str = strings.concat(root,
|
||
"/recursive-build-", stages[si]);
|
||
assert(os.mkdir(recursivebuildwork, 448i32) == 0);
|
||
let recursivebuildav: []str = [driver(stages[si]), "build", "-w",
|
||
recursivebuildwork, "-I", source,
|
||
strings.concat(source, "/testpkg/...")];
|
||
runcommandenv(root, strings.concat("name-recursive-build-", stages[si]),
|
||
recursivebuildav, env,
|
||
(180i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(os.exists(strings.concat(recursivebuildwork,
|
||
"/testpkg.codec.wwi")));
|
||
let recursivetestwork: str = strings.concat(root,
|
||
"/recursive-test-", stages[si]);
|
||
assert(os.mkdir(recursivetestwork, 448i32) == 0);
|
||
let recursivetestav: []str = [driver(stages[si]), "test", "-w",
|
||
recursivetestwork, "-I", source,
|
||
strings.concat(source, "/testpkg/...")];
|
||
runcommandenv(root, strings.concat("name-recursive-test-", stages[si]),
|
||
recursivetestav, env,
|
||
(240i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout, "external_name_uses_declaration ... ok\n"));
|
||
|
||
// Vendor expansion owns the action/map identity; the loaded declaration
|
||
// independently supplies cable as the source-visible qualifier.
|
||
rewritefile(traces[si], "");
|
||
let vendorav: []str = [driver(stages[si]), "build", "-w", works[si],
|
||
"-I", source, "-o", vendorbins[si], vendorclient];
|
||
runcommandenv(root, strings.concat("name-vendor-", stages[si]),
|
||
vendorav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let vendorunit: str = readfile(strings.concat(works[si],
|
||
"/vend.client.unit.ww"));
|
||
assert(occurrences(vendorunit, "import stable short.codec;") == 1);
|
||
assert(occurrences(vendorunit, "import short.codec;") == 1);
|
||
assert(has(vendorunit, strings.concat(
|
||
"//ww:import-map short.codec vend.vendor.short.codec ",
|
||
hexbytes(vendored), "\n")));
|
||
assert(occurrences(vendorunit,
|
||
"//ww:import-map short.codec vend.vendor.short.codec ") == 1);
|
||
let vendortrace: str = readfile(traces[si]);
|
||
let vendorline: str = linecontaining(vendortrace,
|
||
"vend.client.unit.new");
|
||
assert(occurrences(vendorline,
|
||
"<--import><vend.vendor.short.codec>") == 1);
|
||
assert(occurrences(vendorline, strings.concat(
|
||
"<--import-map><short.codec><vend.vendor.short.codec>")) == 1);
|
||
let vendorwwi: str = readfile(strings.concat(works[si],
|
||
"/vend.vendor.short.codec.wwi"));
|
||
assert(has(vendorwwi, strings.concat(
|
||
"//ww:module vend.vendor.short.codec\npackage cable;\n")));
|
||
assert(!os.exists(strings.concat(works[si], "/short.codec.a")));
|
||
let vendorrun: []str = [vendorbins[si]];
|
||
runcommand(root, strings.concat("name-vendor-run-", stages[si]),
|
||
vendorrun, (30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 13);
|
||
|
||
// A stable explicit alias survives a dependency declared-name change.
|
||
// Canonicalized exported type references keep the bridge interface
|
||
// unchanged, so reconsideration stops after the direct importer.
|
||
rewritefile(traces[si], "");
|
||
let appav: []str = [driver(stages[si]), "build", "-w", works[si],
|
||
"-I", source, "-o", appbins[si], app];
|
||
runcommandenv(root, strings.concat("name-app-cold-", stages[si]), appav,
|
||
env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let apprun: []str = [appbins[si]];
|
||
runcommand(root, strings.concat("name-app-run-", stages[si]), apprun,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 42);
|
||
let stablebridgewwi: str = readfile(strings.concat(works[si],
|
||
"/acme.bridge.wwi"));
|
||
assert(has(stablebridgewwi,
|
||
"import __wwi_61636d652e636f646563 acme.codec;\n"));
|
||
assert(has(stablebridgewwi,
|
||
"export type BridgedThing = __wwi_61636d652e636f646563.Thing;\n"));
|
||
assert(!has(stablebridgewwi, "stable.Thing"));
|
||
rewritefile(traces[si], "");
|
||
runcommandenv(root, strings.concat("name-app-warm-", stages[si]), appav,
|
||
env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(readfile(traces[si]).len == 0);
|
||
rewritefile(codecfile, cablebody);
|
||
rewritefile(traces[si], "");
|
||
runcommandenv(root, strings.concat("name-app-renamed-", stages[si]),
|
||
appav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let changedtrace: str = readfile(traces[si]);
|
||
assert(occurrences(changedtrace, "\n") == 2);
|
||
assert(occurrences(changedtrace, "/acme.codec.unit.new") == 1);
|
||
assert(occurrences(changedtrace, "/acme.bridge.unit.new") == 1);
|
||
assert(!has(changedtrace, "/cmd.app.unit.new"));
|
||
let nchanged: str = normalizedtrace(changedtrace,
|
||
strings.concat(works[si], "/"), appbins[si]);
|
||
if (si == 0) { changedtraceref = strings.dup(nchanged); }
|
||
else { assert(same(changedtraceref, nchanged)); };
|
||
let renamedwwi: str = readfile(strings.concat(works[si],
|
||
"/acme.codec.wwi"));
|
||
assert(has(renamedwwi, "//ww:module acme.codec\npackage cable;\n"));
|
||
assert(!has(renamedwwi, "package wire;"));
|
||
assert(same(stablebridgewwi, readfile(strings.concat(works[si],
|
||
"/acme.bridge.wwi"))));
|
||
runcommand(root, strings.concat("name-app-renamed-run-", stages[si]),
|
||
apprun, (30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 42);
|
||
|
||
// Editing only the local alias rebuilds its owning source action. It
|
||
// cannot rename canonical exports or force the downstream command.
|
||
rewritefile(bridgefile, steadybridgebody);
|
||
rewritefile(traces[si], "");
|
||
runcommandenv(root, strings.concat("name-app-alias-edit-", stages[si]),
|
||
appav, env, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let aliastrace: str = readfile(traces[si]);
|
||
assert(occurrences(aliastrace, "\n") == 1);
|
||
assert(occurrences(aliastrace, "/acme.bridge.unit.new") == 1);
|
||
assert(!has(aliastrace, "/acme.codec.unit.new"));
|
||
assert(!has(aliastrace, "/cmd.app.unit.new"));
|
||
assert(same(stablebridgewwi, readfile(strings.concat(works[si],
|
||
"/acme.bridge.wwi"))));
|
||
let nalias: str = normalizedtrace(aliastrace,
|
||
strings.concat(works[si], "/"), appbins[si]);
|
||
if (si == 0) { aliastraceref = strings.dup(nalias); }
|
||
else { assert(same(aliastraceref, nalias)); };
|
||
runcommand(root, strings.concat("name-app-alias-edit-run-", stages[si]),
|
||
apprun, (30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 42);
|
||
|
||
// New semantic artifacts and binaries are byte-identical across the
|
||
// independently cold Cstage and WWstage work roots.
|
||
ari = 0;
|
||
for (ari < actions.len) {
|
||
let xi: i32 = 0;
|
||
for (xi < suffixes.len) {
|
||
let index: i32 = ari * suffixes.len + xi;
|
||
let bytes: str = readfile(strings.concat(works[si], "/",
|
||
actions[ari], suffixes[xi]));
|
||
if (si == 0) { artifactrefs[index] = strings.dup(bytes); }
|
||
else { assert(same(artifactrefs[index], bytes)); };
|
||
xi += 1;
|
||
};
|
||
ari += 1;
|
||
};
|
||
let stagebins: []str = [directbins[si], appbins[si], scopedbins[si],
|
||
repeatedbins[si], leafmainbins[si], vendorbins[si]];
|
||
let bini: i32 = 0;
|
||
for (bini < stagebins.len) {
|
||
let bytes: str = readfile(stagebins[bini]);
|
||
if (si == 0) { binaryrefs[bini] = strings.dup(bytes); }
|
||
else { assert(same(binaryrefs[bini], bytes)); };
|
||
bini += 1;
|
||
};
|
||
si += 1;
|
||
};
|
||
clean(root);
|
||
};
|
||
|
||
@test fn explicit_import_alias_binding_modes() void = {
|
||
let root: str = fresh();
|
||
let source: str = strings.concat(root, "/source");
|
||
let one: str = strings.concat(source, "/pkg/one");
|
||
let two: str = strings.concat(source, "/pkg/two");
|
||
let positives: []str = ["default", "alias", "reuse", "repeatok",
|
||
"repeatedfiles", "blankalias", "blankrepeat", "blankdefault",
|
||
"blankexplicit", "blankfiles"];
|
||
let negatives: []str = ["barefn", "baretype", "baredef", "bareconst",
|
||
"barevar", "aliasbad", "aliasleak", "repeatdup", "aliasdup",
|
||
"defaultalias", "declcollision", "unusedalias", "blankqual",
|
||
"blankbare", "blankordinaryunused"];
|
||
let dirs: []str = [one, two];
|
||
let i: i32 = 0;
|
||
for (i < positives.len) {
|
||
append(dirs, strings.concat(source, "/cmd/", positives[i]));
|
||
i += 1;
|
||
};
|
||
i = 0;
|
||
for (i < negatives.len) {
|
||
append(dirs, strings.concat(source, "/cmd/", negatives[i]));
|
||
i += 1;
|
||
};
|
||
i = 0;
|
||
for (i < dirs.len) { mkdirall(dirs[i]); i += 1; };
|
||
|
||
writefile(strings.concat(one, "/one.ww"), strings.concat(
|
||
"package wire;\n",
|
||
"export fn value() i32 = { return 11; };\n",
|
||
"export type Thing = i32;\n",
|
||
"export def DEFINED: i32 = 13;\n",
|
||
"export const CONSTANT: i32 = 17;\n",
|
||
"export let VARIABLE: i32 = 19;\n"));
|
||
writefile(strings.concat(two, "/two.ww"),
|
||
"package cable;\nexport fn value() i32 = { return 29; };\n");
|
||
let defaultbody: str = strings.concat(
|
||
"package main;\nimport pkg.one;\n",
|
||
"fn typed(v: wire.Thing) i32 = { return v: i32; };\n",
|
||
"fn main() i32 = { return wire.value() + wire.DEFINED + ",
|
||
"wire.CONSTANT + wire.VARIABLE + typed(23); };\n");
|
||
writefile(strings.concat(source, "/cmd/default/main.ww"), defaultbody);
|
||
writefile(strings.concat(source, "/cmd/alias/main.ww"), strings.concat(
|
||
"package main;\nimport stable pkg.one;\n",
|
||
"fn typed(v: stable.Thing) i32 = { return v: i32; };\n",
|
||
"fn main() i32 = { return stable.value() + stable.DEFINED + ",
|
||
"stable.CONSTANT + stable.VARIABLE + typed(23); };\n"));
|
||
writefile(strings.concat(source, "/cmd/reuse/a.ww"), strings.concat(
|
||
"package main;\nimport shared pkg.one;\n",
|
||
"fn left() i32 = { return shared.value(); };\n"));
|
||
writefile(strings.concat(source, "/cmd/reuse/b.ww"), strings.concat(
|
||
"package main;\nimport shared pkg.two;\n",
|
||
"fn right() i32 = { return shared.value(); };\n"));
|
||
writefile(strings.concat(source, "/cmd/reuse/main.ww"),
|
||
"package main;\nfn main() i32 = { return left() + right(); };\n");
|
||
writefile(strings.concat(source, "/cmd/repeatok/main.ww"), strings.concat(
|
||
"package main;\nimport pkg.one;\nimport first pkg.one;\n",
|
||
"import second pkg.one;\n",
|
||
"fn main() i32 = { return wire.value() + first.value() + ",
|
||
"second.value(); };\n"));
|
||
writefile(strings.concat(source, "/cmd/repeatedfiles/a.ww"), strings.concat(
|
||
"package main;\nimport left pkg.one;\n",
|
||
"fn first() i32 = { return left.value(); };\n"));
|
||
writefile(strings.concat(source, "/cmd/repeatedfiles/b.ww"), strings.concat(
|
||
"package main;\nimport right pkg.one;\n",
|
||
"fn second() i32 = { return right.value(); };\n"));
|
||
writefile(strings.concat(source, "/cmd/repeatedfiles/main.ww"),
|
||
"package main;\nfn main() i32 = { return first() + second(); };\n");
|
||
writefile(strings.concat(source, "/cmd/blankalias/main.ww"),
|
||
"package main;\nimport _ pkg.one;\nfn main() i32 = { return 0; };\n");
|
||
writefile(strings.concat(source, "/cmd/blankrepeat/main.ww"),
|
||
strings.concat("package main;\nimport _ pkg.one;\n",
|
||
"import _ pkg.one;\nfn main() i32 = { return 0; };\n"));
|
||
writefile(strings.concat(source, "/cmd/blankdefault/main.ww"),
|
||
strings.concat("package main;\nimport _ pkg.one;\nimport pkg.one;\n",
|
||
"fn main() i32 = { return wire.value(); };\n"));
|
||
writefile(strings.concat(source, "/cmd/blankexplicit/main.ww"),
|
||
strings.concat("package main;\nimport _ pkg.one;\n",
|
||
"import stable pkg.one;\n",
|
||
"fn main() i32 = { return stable.value(); };\n"));
|
||
writefile(strings.concat(source, "/cmd/blankfiles/a.ww"),
|
||
"package main;\nimport _ pkg.one;\n");
|
||
writefile(strings.concat(source, "/cmd/blankfiles/b.ww"),
|
||
"package main;\nimport _ pkg.one;\n");
|
||
writefile(strings.concat(source, "/cmd/blankfiles/main.ww"),
|
||
"package main;\nfn main() i32 = { return 0; };\n");
|
||
|
||
let bareexprs: []str = ["value()", "take(7)", "DEFINED", "CONSTANT",
|
||
"VARIABLE"];
|
||
let barenames: []str = ["barefn", "baretype", "baredef", "bareconst",
|
||
"barevar"];
|
||
i = 0;
|
||
for (i < barenames.len) {
|
||
let pre: str = "";
|
||
if (i == 1) { pre = "fn take(v: Thing) i32 = { return v: i32; };\n"; };
|
||
writefile(strings.concat(source, "/cmd/", barenames[i], "/main.ww"),
|
||
strings.concat("package main;\nimport pkg.one;\n", pre,
|
||
"fn main() i32 = { return ", bareexprs[i], "; };\n"));
|
||
i += 1;
|
||
};
|
||
writefile(strings.concat(source, "/cmd/aliasbad/main.ww"), strings.concat(
|
||
"package main;\nimport stable pkg.one;\n",
|
||
"fn bad(v: wire.Thing) i32 = { return v: i32; };\n",
|
||
"fn declared() i32 = { return wire.value(); };\n",
|
||
"fn leaf() i32 = { return one.value(); };\n",
|
||
"fn bare() i32 = { return value(); };\n",
|
||
"fn main() i32 = { return stable.value(); };\n"));
|
||
writefile(strings.concat(source, "/cmd/aliasleak/a.ww"), strings.concat(
|
||
"package main;\nimport stable pkg.one;\n",
|
||
"fn anchor() i32 = { return stable.value(); };\n"));
|
||
writefile(strings.concat(source, "/cmd/aliasleak/b.ww"),
|
||
"package main;\nfn leaked() i32 = { return stable.value(); };\n");
|
||
writefile(strings.concat(source, "/cmd/aliasleak/main.ww"),
|
||
"package main;\nfn main() i32 = { return leaked(); };\n");
|
||
writefile(strings.concat(source, "/cmd/repeatdup/main.ww"), strings.concat(
|
||
"package main;\nimport pkg.one;\nimport pkg.one;\n",
|
||
"fn main() i32 = { return wire.value(); };\n"));
|
||
writefile(strings.concat(source, "/cmd/aliasdup/main.ww"), strings.concat(
|
||
"package main;\nimport stable pkg.one;\nimport stable pkg.two;\n",
|
||
"fn main() i32 = { return stable.value(); };\n"));
|
||
writefile(strings.concat(source, "/cmd/defaultalias/main.ww"), strings.concat(
|
||
"package main;\nimport pkg.one;\nimport wire pkg.two;\n",
|
||
"fn main() i32 = { return wire.value(); };\n"));
|
||
writefile(strings.concat(source, "/cmd/declcollision/main.ww"), strings.concat(
|
||
"package main;\nimport stable pkg.one;\n",
|
||
"fn stable() i32 = { return 0; };\n",
|
||
"fn main() i32 = { return stable(); };\n"));
|
||
writefile(strings.concat(source, "/cmd/unusedalias/main.ww"),
|
||
"package main;\nimport stable pkg.one;\nfn main() i32 = { return 0; };\n");
|
||
writefile(strings.concat(source, "/cmd/blankqual/main.ww"),
|
||
strings.concat("package main;\nimport _ pkg.one;\n",
|
||
"fn main() i32 = { return wire.value(); };\n"));
|
||
writefile(strings.concat(source, "/cmd/blankbare/main.ww"),
|
||
strings.concat("package main;\nimport _ pkg.one;\n",
|
||
"fn main() i32 = { return value(); };\n"));
|
||
writefile(strings.concat(source, "/cmd/blankordinaryunused/main.ww"),
|
||
strings.concat("package main;\nimport _ pkg.one;\nimport pkg.one;\n",
|
||
"fn main() i32 = { return 0; };\n"));
|
||
|
||
let stages: []str = ["ww", "ww_ww"];
|
||
let expected: []i32 = [83, 83, 40, 33, 22, 0, 0, 11, 11, 0];
|
||
let binaryrefs: []str = ["", "", "", "", "", "", "", "", "", ""];
|
||
let diagrefs: []str = ["", "", "", "", "", "", "", "", "",
|
||
"", "", "", "", "", ""];
|
||
let needles: []str = ["undefined: value", "unknown type 'Thing'",
|
||
"undefined: DEFINED", "undefined: CONSTANT", "undefined: VARIABLE",
|
||
"unknown type 'wire.Thing'", "package 'stable' is not directly imported",
|
||
"wire redeclared in this block", "stable redeclared in this block",
|
||
"wire redeclared in this block",
|
||
"stable already declared through import of package stable",
|
||
"\"pkg.one\" imported as stable and not used", "undefined: wire",
|
||
"undefined: value", "\"pkg.one\" imported as wire and not used"];
|
||
let exactdiags: []str = [
|
||
strings.concat(
|
||
"$WORK/cmd.barefn.unit.new:3:1: error: \"pkg.one\" imported as wire and not used\n",
|
||
"$WORK/cmd.barefn.unit.new:4:26: error: undefined: value\n",
|
||
"ww: w6c failed for cmd.barefn\n"),
|
||
strings.concat(
|
||
"$WORK/cmd.baretype.unit.new:3:1: error: \"pkg.one\" imported as wire and not used\n",
|
||
"$WORK/cmd.baretype.unit.new:4:12: error: unknown type 'Thing'\n",
|
||
"ww: w6c failed for cmd.baretype\n"),
|
||
strings.concat(
|
||
"$WORK/cmd.baredef.unit.new:3:1: error: \"pkg.one\" imported as wire and not used\n",
|
||
"$WORK/cmd.baredef.unit.new:4:26: error: undefined: DEFINED\n",
|
||
"ww: w6c failed for cmd.baredef\n"),
|
||
strings.concat(
|
||
"$WORK/cmd.bareconst.unit.new:3:1: error: \"pkg.one\" imported as wire and not used\n",
|
||
"$WORK/cmd.bareconst.unit.new:4:26: error: undefined: CONSTANT\n",
|
||
"ww: w6c failed for cmd.bareconst\n"),
|
||
strings.concat(
|
||
"$WORK/cmd.barevar.unit.new:3:1: error: \"pkg.one\" imported as wire and not used\n",
|
||
"$WORK/cmd.barevar.unit.new:4:26: error: undefined: VARIABLE\n",
|
||
"ww: w6c failed for cmd.barevar\n"),
|
||
"", "",
|
||
strings.concat(
|
||
"$WORK/cmd.repeatdup.unit.new:4:1: error: wire redeclared in this block\n",
|
||
"\t$WORK/cmd.repeatdup.unit.new:3:1: other declaration of wire\n",
|
||
"$WORK/cmd.repeatdup.unit.new:4:1: error: \"pkg.one\" imported as wire and not used\n",
|
||
"ww: w6c failed for cmd.repeatdup\n"),
|
||
strings.concat(
|
||
"$WORK/cmd.aliasdup.unit.new:4:1: error: stable redeclared in this block\n",
|
||
"\t$WORK/cmd.aliasdup.unit.new:3:1: other declaration of stable\n",
|
||
"$WORK/cmd.aliasdup.unit.new:4:1: error: \"pkg.two\" imported as stable and not used\n",
|
||
"ww: w6c failed for cmd.aliasdup\n"),
|
||
strings.concat(
|
||
"$WORK/cmd.defaultalias.unit.new:4:1: error: wire redeclared in this block\n",
|
||
"\t$WORK/cmd.defaultalias.unit.new:3:1: other declaration of wire\n",
|
||
"$WORK/cmd.defaultalias.unit.new:4:1: error: \"pkg.two\" imported as wire and not used\n",
|
||
"ww: w6c failed for cmd.defaultalias\n"),
|
||
strings.concat(
|
||
"$WORK/cmd.declcollision.unit.new:3:1: error: \"pkg.one\" imported as stable and not used\n",
|
||
"$WORK/cmd.declcollision.unit.new:4:1: error: stable already declared through import of package stable (\"pkg.one\")\n",
|
||
"\t$WORK/cmd.declcollision.unit.new:3:1: other declaration of stable\n",
|
||
"ww: w6c failed for cmd.declcollision\n"),
|
||
strings.concat(
|
||
"$WORK/cmd.unusedalias.unit.new:3:1: error: \"pkg.one\" imported as stable and not used\n",
|
||
"ww: w6c failed for cmd.unusedalias\n"),
|
||
"", "", "",
|
||
];
|
||
let si: i32 = 0;
|
||
for (si < stages.len) {
|
||
let work: str = strings.concat(root, "/work-", stages[si]);
|
||
let rejectwork: str = strings.concat(root, "/reject-", stages[si]);
|
||
mkdirall(work); mkdirall(rejectwork);
|
||
i = 0;
|
||
for (i < positives.len) {
|
||
let target: str = strings.concat(source, "/cmd/", positives[i]);
|
||
let binary: str = strings.concat(root, "/", stages[si], "-",
|
||
positives[i]);
|
||
let av: []str = [driver(stages[si]), "build", "-w", work, "-I",
|
||
source, "-o", binary, target];
|
||
let out: commandout;
|
||
runcommand(root, strings.concat("alias-positive-", stages[si], "-",
|
||
positives[i]), av,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let runav: []str = [binary];
|
||
runcommand(root, strings.concat("alias-positive-run-", stages[si],
|
||
"-", positives[i]), runav,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, expected[i]);
|
||
let bytes: str = readfile(binary);
|
||
if (si == 0) { binaryrefs[i] = strings.dup(bytes); }
|
||
else { assert(same(binaryrefs[i], bytes)); };
|
||
i += 1;
|
||
};
|
||
let aliasunit: str = readfile(strings.concat(work, "/cmd.alias.unit.ww"));
|
||
assert(has(aliasunit, "import stable pkg.one;"));
|
||
let aliaswwi: str = readfile(strings.concat(work, "/cmd.alias.wwi"));
|
||
assert(has(aliaswwi, "import __wwi_706b672e6f6e65 pkg.one;"));
|
||
assert(!has(aliaswwi, "import stable"));
|
||
assert(os.exists(strings.concat(work, "/pkg.one.a")));
|
||
assert(!os.exists(strings.concat(work, "/stable.a")));
|
||
let repeatunit: str = readfile(strings.concat(work,
|
||
"/cmd.repeatok.unit.ww"));
|
||
assert(occurrences(repeatunit, "pkg.one;") == 3);
|
||
let repeatedunit: str = readfile(strings.concat(work,
|
||
"/cmd.repeatedfiles.unit.ww"));
|
||
assert(occurrences(repeatedunit, "pkg.one;") == 2);
|
||
let blankunit: str = readfile(strings.concat(work,
|
||
"/cmd.blankalias.unit.ww"));
|
||
assert(has(blankunit, "import _ pkg.one;"));
|
||
let blankwwi: str = readfile(strings.concat(work,
|
||
"/cmd.blankalias.wwi"));
|
||
assert(!has(blankwwi, "import _") && !has(blankwwi, "__ww.."));
|
||
let blankrepeat: str = readfile(strings.concat(work,
|
||
"/cmd.blankrepeat.unit.ww"));
|
||
assert(occurrences(blankrepeat, "import _ pkg.one;") == 2);
|
||
assert(occurrences(blankrepeat,
|
||
"//ww:direct-export pkg.one ") == 1);
|
||
let blankfiles: str = readfile(strings.concat(work,
|
||
"/cmd.blankfiles.unit.ww"));
|
||
assert(occurrences(blankfiles, "import _ pkg.one;") == 2);
|
||
assert(occurrences(blankfiles,
|
||
"//ww:direct-export pkg.one ") == 1);
|
||
let blankdispatch: str = readfile(strings.concat(work,
|
||
"/cmd.blankdefault.init.unit.ww"));
|
||
assert(occurrences(blankdispatch,
|
||
"//ww:init-call __ww..pkg.p.pkg.one.v0.r0.init") == 1);
|
||
|
||
i = 0;
|
||
for (i < negatives.len) {
|
||
let target: str = strings.concat(source, "/cmd/", negatives[i]);
|
||
let binary: str = strings.concat(root, "/reject-bin-", stages[si],
|
||
"-", negatives[i]);
|
||
let av: []str = [driver(stages[si]), "build", "-w", rejectwork,
|
||
"-I", source, "-o", binary, target];
|
||
let out: commandout;
|
||
runcommand(root, strings.concat("alias-negative-", stages[si], "-",
|
||
negatives[i]), av,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, needles[i]));
|
||
if (i == 5) {
|
||
assert(has(out.stderr, "undefined: wire"));
|
||
assert(has(out.stderr, "undefined: one"));
|
||
assert(has(out.stderr, "undefined: value"));
|
||
};
|
||
if (i < 5) {
|
||
let unusedpos: i32 = pos(out.stderr,
|
||
"\"pkg.one\" imported as wire and not used");
|
||
let namepos: i32 = pos(out.stderr, needles[i]);
|
||
assert(unusedpos >= 0 && namepos > unusedpos);
|
||
};
|
||
if (i == 7 || i == 8 || i == 9) {
|
||
let redeclared: i32 = pos(out.stderr, "redeclared in this block");
|
||
let unused: i32 = pos(out.stderr, "imported as");
|
||
assert(redeclared >= 0 && unused > redeclared);
|
||
};
|
||
let normalized: str = normalizedtrace(out.stderr,
|
||
strings.concat(rejectwork, "/"), binary);
|
||
if (exactdiags[i].len > 0) {
|
||
assert(same(normalized, exactdiags[i]));
|
||
};
|
||
if (si == 0) { diagrefs[i] = strings.dup(normalized); }
|
||
else { assert(same(diagrefs[i], normalized)); };
|
||
assert(!os.exists(binary));
|
||
let action: str = strings.concat("cmd.", negatives[i]);
|
||
let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a",
|
||
".unit.new", ".wwi.new", ".s.new", ".o.new", ".a.new"];
|
||
let j: i32 = 0;
|
||
for (j < suffixes.len) {
|
||
assert(!os.exists(strings.concat(rejectwork, "/", action,
|
||
suffixes[j])));
|
||
j += 1;
|
||
};
|
||
i += 1;
|
||
};
|
||
si += 1;
|
||
};
|
||
clean(root);
|
||
};
|
||
|
||
@test fn compile_artifact_naming() void = {
|
||
let root: str = fresh();
|
||
let pdir: str = strings.concat(root, "/pkg");
|
||
assert(os.mkdir(pdir, 493i32) == 0);
|
||
writefile(strings.concat(pdir, "/pkg.ww"),
|
||
"package family;\nexport fn v() i32 = { return 7; };\n");
|
||
writefile(strings.concat(pdir, "/same_test.ww"), strings.concat(
|
||
"package family;\nimport os;\n",
|
||
"@test fn seven() void = { let av: []str = os.args();",
|
||
" if (av.len != 0) { os.write(os.STDOUT_FILENO, av[0].ptr,",
|
||
" av[0].len: u64); os.write(os.STDOUT_FILENO, \"\\n\".ptr, 1u64); };",
|
||
" assert(v() == 7); };\n"));
|
||
let adir: str = strings.concat(root, "/a");
|
||
assert(os.mkdir(adir, 493i32) == 0);
|
||
writefile(strings.concat(adir, "/a.ww"),
|
||
"package other;\nexport fn v() i32 = { return 1; };\n");
|
||
writefile(strings.concat(adir, "/a_test.ww"),
|
||
"package other;\n@test fn one() void = { assert(v() == 1); };\n");
|
||
let none: str = strings.concat(root, "/znone");
|
||
assert(os.mkdir(none, 493i32) == 0);
|
||
writefile(strings.concat(none, "/none.ww"),
|
||
"package renamed;\nexport fn v() i32 = { return 9; };\n");
|
||
// '_' roots are excluded from recursive package discovery.
|
||
let outdir: str = strings.concat(root, "/_out");
|
||
assert(os.mkdir(outdir, 493i32) == 0);
|
||
let out: commandout;
|
||
let drvs: []str = ["ww", "ww_ww"];
|
||
let namedbytes: str = "";
|
||
let defaultbytes: str = "";
|
||
let fanabytes: str = "";
|
||
let fanpkgbytes: str = "";
|
||
let i: i32 = 0;
|
||
for (i < 2) {
|
||
let named: str = strings.concat(outdir, "/out_", drvs[i],
|
||
".bin");
|
||
let coav: []str = [driver(drvs[i]), "test", "-c", "-I", root,
|
||
"-o", named, pdir];
|
||
runcommand(root, strings.concat("nameco_", drvs[i]), coav,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let fi: os.filestat;
|
||
match (os.stat(&fi, named)) {
|
||
case void => void;
|
||
case let e: os.oserror => abort("-c -o artifact missing");
|
||
};
|
||
assert(((fi.mode: u32) & 73u32) != 0u32);
|
||
assert(!has(out.stdout, "seven ... "));
|
||
match (os.stat(&fi, strings.concat(pdir, "/pkg.test"))) {
|
||
case void => abort("-c -o still published the fixed stem");
|
||
case let e: os.oserror => void;
|
||
};
|
||
if (i == 0) { namedbytes = readfile(named); }
|
||
else { assert(same(namedbytes, readfile(named))); };
|
||
let runav: []str = [named];
|
||
runcommand(root, strings.concat("namerun_", drvs[i]), runav,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout, "seven ... "));
|
||
|
||
let runnamed: str = strings.concat(outdir, "/run_", drvs[i], ".bin");
|
||
let nocav: []str = [driver(drvs[i]), "test", "-I", root,
|
||
"-o", runnamed, pdir];
|
||
runcommand(root, strings.concat("noc_", drvs[i]), nocav,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stderr.len == 0 && os.exists(runnamed));
|
||
assert(has(out.stdout, "seven ... "));
|
||
assert(has(out.stdout, "/package.test\n"));
|
||
assert(!has(out.stdout, runnamed));
|
||
|
||
let defaultbin: str = strings.concat(root, "/pkg.test");
|
||
let defaultav: []str = [driver(drvs[i]), "test", "-c", "-I", root,
|
||
pdir];
|
||
runcommanddir(root, strings.concat("default_", drvs[i]), root,
|
||
defaultav,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(os.exists(defaultbin));
|
||
assert(!os.exists(strings.concat(root, "/family.test")));
|
||
assert(!os.exists(strings.concat(pdir, "/family.test")));
|
||
if (i == 0) { defaultbytes = readfile(defaultbin); }
|
||
else { assert(same(defaultbytes, readfile(defaultbin))); };
|
||
clean(defaultbin);
|
||
|
||
let fanroot: str = strings.concat(root, "/_fan_", drvs[i]);
|
||
let fanarg: str = strings.concat(fanroot, "/nested/");
|
||
let fanav: []str = [driver(drvs[i]), "test", "-c", "-j", "2",
|
||
"-I", root, "-o", fanarg, strings.concat(root, "/...")];
|
||
runcommand(root, strings.concat("fan_", drvs[i]), fanav,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let fanabin: str = strings.concat(fanarg, "a.test");
|
||
let fanpkg: str = strings.concat(fanarg, "pkg.test");
|
||
assert(os.exists(fanabin) && os.exists(fanpkg));
|
||
assert(!os.exists(strings.concat(fanarg, "znone.test")));
|
||
assert(has(out.stdout, strings.concat("? ", none, " [no tests]\n")));
|
||
if (i == 0) {
|
||
fanabytes = readfile(fanabin);
|
||
fanpkgbytes = readfile(fanpkg);
|
||
} else {
|
||
assert(same(fanabytes, readfile(fanabin)));
|
||
assert(same(fanpkgbytes, readfile(fanpkg)));
|
||
};
|
||
|
||
let badout: str = strings.concat(outdir, "/multi-file-", drvs[i]);
|
||
let mulav: []str = [driver(drvs[i]), "test", "-c", "-o",
|
||
badout, strings.concat(root, "/...")];
|
||
runcommand(root, strings.concat("multi_", drvs[i]), mulav,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr,
|
||
"with multiple packages, -o must refer to a directory or /dev/null"));
|
||
assert(!os.exists(badout));
|
||
|
||
let nullcompile: []str = [driver(drvs[i]), "test", "-c", "-I",
|
||
root, "-o", "/dev/null", pdir];
|
||
runcommand(root, strings.concat("null-compile_", drvs[i]), nullcompile,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(!has(out.stdout, "seven ... "));
|
||
assert(!os.exists("/dev/null.new"));
|
||
let nullrun: []str = [driver(drvs[i]), "test", "-I", root,
|
||
"-o", "/dev/null", pdir];
|
||
runcommand(root, strings.concat("null-run_", drvs[i]), nullrun,
|
||
(30i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout, "seven ... "));
|
||
i += 1;
|
||
};
|
||
clean(root);
|
||
};
|
||
|
||
@test fn test_binary_publication_transaction() void = {
|
||
let root: str = fresh();
|
||
let suite: str = strings.concat(root, "/suite");
|
||
let leftsame: str = strings.concat(suite, "/left/same");
|
||
let rightsame: str = strings.concat(suite, "/right/same");
|
||
let alpha: str = strings.concat(suite, "/alpha");
|
||
let beta: str = strings.concat(suite, "/beta");
|
||
let failed: str = strings.concat(suite, "/failed");
|
||
let none: str = strings.concat(suite, "/none");
|
||
let persist: str = strings.concat(suite, "/persist");
|
||
mkdirall(leftsame); mkdirall(rightsame); mkdirall(alpha);
|
||
mkdirall(beta); mkdirall(failed); mkdirall(none); mkdirall(persist);
|
||
writefile(strings.concat(leftsame, "/same.ww"),
|
||
"package declared_left;\nfn value() i32 = { return 1; };\n");
|
||
writefile(strings.concat(leftsame, "/same_test.ww"), strings.concat(
|
||
"package declared_left;\n",
|
||
"@test fn left() void = { assert(value() == 1); };\n"));
|
||
writefile(strings.concat(rightsame, "/same.ww"),
|
||
"package declared_right;\nfn value() i32 = { return 2; };\n");
|
||
writefile(strings.concat(rightsame, "/same_test.ww"), strings.concat(
|
||
"package declared_right;\n",
|
||
"@test fn right() void = { assert(value() == 2); };\n"));
|
||
writefile(strings.concat(alpha, "/alpha.ww"),
|
||
"package alpha_decl;\nfn value() i32 = { return 3; };\n");
|
||
writefile(strings.concat(alpha, "/alpha_test.ww"), strings.concat(
|
||
"package alpha_decl;\n",
|
||
"@test fn alpha_ok() void = { assert(value() == 3); };\n"));
|
||
writefile(strings.concat(beta, "/beta.ww"),
|
||
"package beta_decl;\nfn value() i32 = { return 4; };\n");
|
||
writefile(strings.concat(beta, "/beta_test.ww"), strings.concat(
|
||
"package beta_decl;\n",
|
||
"@test fn beta_ok() void = { assert(value() == 4); };\n"));
|
||
writefile(strings.concat(failed, "/failed.ww"),
|
||
"package failed_decl;\nfn value() i32 = { return 5; };\n");
|
||
writefile(strings.concat(failed, "/failed_test.ww"), strings.concat(
|
||
"package failed_decl;\n",
|
||
"@test fn runtime_failure() void = { assert(false); };\n"));
|
||
writefile(strings.concat(none, "/none.ww"),
|
||
"package none_decl;\nfn value() i32 = { return 6; };\n");
|
||
let persisttest: str = strings.concat(persist, "/persist_test.ww");
|
||
let persistbase: str = strings.concat(
|
||
"package persist_decl;\n",
|
||
"@test fn persistent_base() void = { assert(true); };\n");
|
||
let persistchanged: str = strings.concat(
|
||
"package persist_decl;\n",
|
||
"@test fn persistent_changed() void = { assert(1 + 1 == 2); };\n");
|
||
writefile(strings.concat(persist, "/persist.ww"),
|
||
"package persist_decl;\nfn value() i32 = { return 7; };\n");
|
||
writefile(persisttest, persistbase);
|
||
|
||
let compilerwrapper: str = strings.concat(root, "/publish-w6c.sh");
|
||
let linkerwrapper: str = strings.concat(root, "/publish-w6l.sh");
|
||
writeexecutable(compilerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_PUBLISH_CTRACE\"\n",
|
||
"exec \"$WW_PUBLISH_REAL_C\" \"$@\"\n"));
|
||
writeexecutable(linkerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf '%s\\n' \"$*\" >> \"$WW_PUBLISH_LTRACE\"\n",
|
||
"if test -n \"$WW_PUBLISH_FAIL\"; then\n",
|
||
" for arg do case \"$arg\" in *\"$WW_PUBLISH_FAIL\"*)\n",
|
||
" printf 'injected publication linker failure\\n' >&2\n",
|
||
" exit 97;; esac; done\nfi\n",
|
||
"exec \"$WW_PUBLISH_REAL_L\" \"$@\"\n"));
|
||
let stages: []str = ["ww", "ww_ww"];
|
||
let compilers: []str = ["w6c", "w6c_ww"];
|
||
let linkers: []str = ["w6l", "w6l_ww"];
|
||
let tags: []str = ["c", "ww"];
|
||
let duplicatediag: str = "";
|
||
let rollbackdiag: str = "";
|
||
let missingdiag: str = "";
|
||
let occupieddiag: str = "";
|
||
let failurebytes: str = "";
|
||
let persistbasebytes: str = "";
|
||
let persistchangedbytes: str = "";
|
||
let baseenv: []str = os.getenvs();
|
||
let out: commandout;
|
||
let si: i32 = 0;
|
||
for (si < stages.len) {
|
||
let ctrace: str = strings.concat(root, "/publish-", tags[si],
|
||
"-w6c.trace");
|
||
let ltrace: str = strings.concat(root, "/publish-", tags[si],
|
||
"-w6l.trace");
|
||
writefile(ctrace, ""); writefile(ltrace, "");
|
||
let env: []str = alloc([], (baseenv.len + 7): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < baseenv.len) {
|
||
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6L=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_PUBLISH_CTRACE=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_PUBLISH_LTRACE=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_PUBLISH_REAL_C=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_PUBLISH_REAL_L=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_PUBLISH_FAIL=")) {
|
||
append(env, baseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(env, strings.concat("WW_W6C=", compilerwrapper));
|
||
append(env, strings.concat("WW_W6L=", linkerwrapper));
|
||
append(env, strings.concat("WW_PUBLISH_CTRACE=", ctrace));
|
||
append(env, strings.concat("WW_PUBLISH_LTRACE=", ltrace));
|
||
append(env, strings.concat("WW_PUBLISH_REAL_C=",
|
||
driver(compilers[si])));
|
||
append(env, strings.concat("WW_PUBLISH_REAL_L=",
|
||
driver(linkers[si])));
|
||
append(env, "WW_PUBLISH_FAIL=");
|
||
|
||
// Equal import leaves are rejected before tools or output creation,
|
||
// independent of their unequal declared package names.
|
||
let duplicateout: str = strings.concat(root, "/duplicate-output/");
|
||
let duplicateav: []str = [driver(stages[si]), "test", "-c",
|
||
"-j", "2", "-I", suite, "-o", duplicateout,
|
||
leftsame, rightsame];
|
||
runcommandenvdir(root, strings.concat("publish-duplicate-", tags[si]),
|
||
duplicateav, env, root,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(out.stdout.len == 0);
|
||
assert(has(out.stderr,
|
||
"ww test: cannot write test binary same.test for multiple packages:\n"));
|
||
assert(has(out.stderr, strings.concat(leftsame, "\n")));
|
||
assert(has(out.stderr, strings.concat(rightsame, "\n")));
|
||
assert(readfile(ctrace).len == 0 && readfile(ltrace).len == 0);
|
||
assert(!os.exists(duplicateout));
|
||
if (si == 0) { duplicatediag = strings.dup(out.stderr); }
|
||
else { assert(same(duplicatediag, out.stderr)); };
|
||
|
||
// /dev/null is Go's exact multi-package discard exception: both
|
||
// products build, neither publishes, and duplicate names are harmless.
|
||
let nullav: []str = [driver(stages[si]), "test", "-c", "-j", "2",
|
||
"-I", suite, "-o", "/dev/null", leftsame, rightsame];
|
||
runcommandenvdir(root, strings.concat("publish-null-", tags[si]),
|
||
nullav, env, root,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
assert(readfile(ctrace).len != 0);
|
||
assert(occurrences(readfile(ltrace), "\n") == 2);
|
||
assert(!os.exists("/dev/null.new"));
|
||
|
||
// An occupied retained-binary stage is a pre-tool rejection. The
|
||
// caller's old destination and occupied stage both remain untouched.
|
||
let occupied: str = strings.concat(root, "/occupied");
|
||
if (si == 0) {
|
||
assert(os.mkdir(occupied, 448i32) == 0);
|
||
writefile(strings.concat(occupied, "/alpha.test"), "old-alpha\n");
|
||
writefile(strings.concat(occupied, "/alpha.test.new"),
|
||
"occupied-stage\n");
|
||
};
|
||
rewritefile(ctrace, ""); rewritefile(ltrace, "");
|
||
let occupiedav: []str = [driver(stages[si]), "test", "-c", "-I",
|
||
suite, "-o", strings.concat(occupied, "/"), alpha];
|
||
runcommandenvdir(root, strings.concat("publish-occupied-", tags[si]),
|
||
occupiedav, env, root,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, strings.concat(
|
||
"ww: product staging path already exists: ", occupied,
|
||
"/alpha.test.new\n")));
|
||
assert(same(readfile(strings.concat(occupied, "/alpha.test")),
|
||
"old-alpha\n"));
|
||
assert(same(readfile(strings.concat(occupied, "/alpha.test.new")),
|
||
"occupied-stage\n"));
|
||
assert(readfile(ctrace).len == 0 && readfile(ltrace).len == 0);
|
||
if (si == 0) { occupieddiag = strings.dup(out.stderr); }
|
||
else { assert(same(occupieddiag, out.stderr)); };
|
||
|
||
// Link failure after an earlier sibling has staged a retained copy
|
||
// rejects the whole request and preserves every old destination.
|
||
let rollback: str = strings.concat(root, "/rollback-", tags[si]);
|
||
assert(os.mkdir(rollback, 448i32) == 0);
|
||
writefile(strings.concat(rollback, "/alpha.test"), "alpha-sentinel\n");
|
||
writefile(strings.concat(rollback, "/beta.test"), "beta-sentinel\n");
|
||
rewritefile(ctrace, ""); rewritefile(ltrace, "");
|
||
env[env.len - 1] = "WW_PUBLISH_FAIL=beta-test-main";
|
||
let rollbackav: []str = [driver(stages[si]), "test", "-c", "-j",
|
||
"1", "-I", suite, "-o", strings.concat(rollback, "/"),
|
||
alpha, beta];
|
||
runcommandenvdir(root, strings.concat("publish-rollback-", tags[si]),
|
||
rollbackav, env, root,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, "injected publication linker failure\n"));
|
||
assert(has(out.stderr, "ww: w6l failed\n"));
|
||
assert(same(readfile(strings.concat(rollback, "/alpha.test")),
|
||
"alpha-sentinel\n"));
|
||
assert(same(readfile(strings.concat(rollback, "/beta.test")),
|
||
"beta-sentinel\n"));
|
||
assert(!directoryhasnew(rollback));
|
||
assert(occurrences(readfile(ltrace), "\n") == 2);
|
||
if (si == 0) { rollbackdiag = strings.dup(out.stderr); }
|
||
else { assert(same(rollbackdiag, out.stderr)); };
|
||
|
||
// Output directories minted for a rejected request are rolled back.
|
||
let missingroot: str = strings.concat(root, "/missing-", tags[si]);
|
||
let missingout: str = strings.concat(missingroot, "/nested/");
|
||
rewritefile(ctrace, ""); rewritefile(ltrace, "");
|
||
env[env.len - 1] = "WW_PUBLISH_FAIL=alpha-test-main";
|
||
let missingav: []str = [driver(stages[si]), "test", "-c", "-j",
|
||
"1", "-I", suite, "-o", missingout, alpha];
|
||
runcommandenvdir(root, strings.concat("publish-missing-", tags[si]),
|
||
missingav, env, root,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(!os.exists(missingroot));
|
||
assert(occurrences(readfile(ltrace), "\n") == 1);
|
||
if (si == 0) { missingdiag = strings.dup(out.stderr); }
|
||
else { assert(same(missingdiag, out.stderr)); };
|
||
|
||
// A package with no tests never claims a binary or creates a requested
|
||
// output hierarchy, although its production package is still checked.
|
||
let noneroot: str = strings.concat(root, "/none-output-", tags[si]);
|
||
let noneav: []str = [driver(stages[si]), "test", "-c", "-I", suite,
|
||
"-o", strings.concat(noneroot, "/nested/"), none];
|
||
env[env.len - 1] = "WW_PUBLISH_FAIL=";
|
||
runcommandenvdir(root, strings.concat("publish-none-", tags[si]),
|
||
noneav, env, root,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout, strings.concat("? ", none,
|
||
" [no tests]\n")));
|
||
assert(out.stderr.len == 0 && !os.exists(noneroot));
|
||
|
||
// Publication commits before execution. A failing test therefore leaves
|
||
// a runnable retained binary while its product failure stays on stdout.
|
||
let failurebin: str = strings.concat(root, "/failure-", tags[si],
|
||
".test");
|
||
let failureav: []str = [driver(stages[si]), "test", "-I", suite,
|
||
"-o", failurebin, failed];
|
||
runcommandenvdir(root, strings.concat("publish-runtime-", tags[si]),
|
||
failureav, env, root,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(os.exists(failurebin) && !os.exists(strings.concat(failurebin,
|
||
".new")));
|
||
assert(has(out.stdout,
|
||
"failed_decl.runtime_failure ... FAIL (exit 1)\n"));
|
||
assert(out.stderr.len == 0);
|
||
let fi: os.filestat;
|
||
match (os.stat(&fi, failurebin)) {
|
||
case void => void;
|
||
case let e: os.oserror => abort("retained failure binary missing");
|
||
};
|
||
assert(((fi.mode: u32) & 73u32) != 0u32);
|
||
if (si == 0) { failurebytes = readfile(failurebin); }
|
||
else { assert(same(failurebytes, readfile(failurebin))); };
|
||
|
||
// -w keeps semantic actions persistent while -c and -o remain execution
|
||
// and presentation policy. Compile-only can cold-build and invalidate;
|
||
// an unchanged running request reuses those actions and the same bytes.
|
||
rewritefile(persisttest, persistbase);
|
||
let persistwork: str = strings.concat(root, "/persist-work-", tags[si]);
|
||
let persistcompilebin: str = strings.concat(root, "/persist-compile-",
|
||
tags[si], ".test");
|
||
let persistbin: str = strings.concat(root, "/persist-run-", tags[si],
|
||
".test");
|
||
rewritefile(ctrace, ""); rewritefile(ltrace, "");
|
||
let persistcompileav: []str = [driver(stages[si]), "test", "-c",
|
||
"-w", persistwork, "-I", suite, "-o", persistcompilebin,
|
||
persist];
|
||
let persistav: []str = [driver(stages[si]), "test", "-w",
|
||
persistwork, "-I", suite, "-o", persistbin, persist];
|
||
runcommandenvdir(root, strings.concat("publish-persist-cold-c-", tags[si]),
|
||
persistcompileav, env, root,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
assert(readfile(ctrace).len != 0);
|
||
let basebytes: str = readfile(persistcompilebin);
|
||
if (si == 0) { persistbasebytes = strings.dup(basebytes); }
|
||
else { assert(same(persistbasebytes, basebytes)); };
|
||
rewritefile(ctrace, ""); rewritefile(ltrace, "");
|
||
runcommandenvdir(root, strings.concat("publish-persist-warm-run-", tags[si]),
|
||
persistav, env, root,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout, "persistent_base ... ok\n"));
|
||
assert(readfile(ctrace).len == 0);
|
||
assert(same(basebytes, readfile(persistbin)));
|
||
rewritefile(persisttest, persistchanged);
|
||
rewritefile(ctrace, ""); rewritefile(ltrace, "");
|
||
runcommandenvdir(root, strings.concat("publish-persist-change-c-", tags[si]),
|
||
persistcompileav, env, root,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
assert(readfile(ctrace).len != 0);
|
||
let changedbytes: str = readfile(persistcompilebin);
|
||
assert(!same(basebytes, changedbytes));
|
||
if (si == 0) { persistchangedbytes = strings.dup(changedbytes); }
|
||
else { assert(same(persistchangedbytes, changedbytes)); };
|
||
rewritefile(ctrace, ""); rewritefile(ltrace, "");
|
||
runcommandenvdir(root, strings.concat("publish-persist-change-run-",
|
||
tags[si]), persistav, env, root,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stdout, "persistent_changed ... ok\n"));
|
||
assert(readfile(ctrace).len == 0);
|
||
assert(same(changedbytes, readfile(persistbin)));
|
||
assert(!os.exists(strings.concat(persistcompilebin, ".new")));
|
||
assert(!os.exists(strings.concat(persistbin, ".new")));
|
||
assert(!directoryhasnew(persistwork));
|
||
si += 1;
|
||
};
|
||
assert(os.remove(strings.concat(root,
|
||
"/occupied/alpha.test.new")) == 0);
|
||
clean(root);
|
||
};
|
||
|
||
// A single directory root used to bypass the package coordinator and treat
|
||
// every non-null -o spelling as one file. Exercise the Go output-directory
|
||
// branch at that dispatch boundary while the established graph transaction
|
||
// remains responsible for actions, publication, and rollback.
|
||
@test fn single_root_build_output_directory() void = {
|
||
let root: str = fresh();
|
||
let source: str = strings.concat(root, "/source");
|
||
let base: str = strings.concat(source, "/base");
|
||
let alpha: str = strings.concat(source, "/alpha");
|
||
let library: str = strings.concat(source, "/library");
|
||
let bad: str = strings.concat(source, "/bad");
|
||
let badlibrary: str = strings.concat(source, "/badlibrary");
|
||
let duplicateone: str = strings.concat(source, "/duplicate-one/tool");
|
||
let duplicatetwo: str = strings.concat(source, "/duplicate-two/tool");
|
||
let duplicatethree: str = strings.concat(source, "/duplicate-three/tool");
|
||
mkdirall(base); mkdirall(alpha); mkdirall(library); mkdirall(bad);
|
||
mkdirall(badlibrary); mkdirall(duplicateone); mkdirall(duplicatetwo);
|
||
mkdirall(duplicatethree);
|
||
writefile(strings.concat(base, "/base.ww"), strings.concat(
|
||
"package base;\n",
|
||
"export fn value() i32 = { return 4; };\n"));
|
||
let alphafile: str = strings.concat(alpha, "/main.ww");
|
||
let alphaoriginal: str = strings.concat(
|
||
"package main;\nimport base;\n",
|
||
"fn main() i32 = { return base.value(); };\n");
|
||
let alphachanged: str = strings.concat(
|
||
"package main;\nimport base;\n",
|
||
"fn main() i32 = { return base.value() + 1; };\n");
|
||
let alphafailing: str = strings.concat(
|
||
"package main;\nimport base;\n",
|
||
"fn main() i32 = { return base.value() + 2; };\n");
|
||
writefile(alphafile, alphaoriginal);
|
||
writefile(strings.concat(alpha, "/main_test.ww"),
|
||
"package main;\n@test fn valid() void = { assert(true); };\n");
|
||
writefile(strings.concat(library, "/library.ww"), strings.concat(
|
||
"package renamed;\n",
|
||
"export fn value() i32 = { return 7; };\n"));
|
||
writefile(strings.concat(bad, "/main.ww"), strings.concat(
|
||
"package main;\nimport _ missing.pkg;\n",
|
||
"fn main() i32 = { return 0; };\n"));
|
||
writefile(strings.concat(badlibrary, "/library.ww"), strings.concat(
|
||
"package renamed;\nimport _ missing.pkg;\n",
|
||
"export fn value() i32 = { return 0; };\n"));
|
||
writefile(strings.concat(duplicateone, "/main.ww"),
|
||
"package main;\nfn main() i32 = { return 12; };\n");
|
||
writefile(strings.concat(duplicatetwo, "/main.ww"), strings.concat(
|
||
"package main;\nimport _ missing.pkg;\n",
|
||
"fn main() i32 = { return 13; };\n"));
|
||
writefile(strings.concat(duplicatethree, "/main.ww"),
|
||
"package main;\nfn main() i32 = { return 14; };\n");
|
||
let logical: str = strings.concat(source, "/logical");
|
||
assert(os.symlink(alpha, logical) == 0);
|
||
let raw: str = strings.concat(source, "/raw.ww");
|
||
writefile(raw, "package main;\nfn main() i32 = { return 9; };\n");
|
||
let rawlibrary: str = strings.concat(source, "/rawlibrary.ww");
|
||
writefile(rawlibrary,
|
||
"package renamed;\nexport fn value() i32 = { return 10; };\n");
|
||
let rawbad: str = strings.concat(source, "/rawbad.ww");
|
||
writefile(rawbad, strings.concat(
|
||
"package main;\nimport _ missing.pkg;\n",
|
||
"fn main() i32 = { return 11; };\n"));
|
||
let longoutputbytes: []u8 = alloc([], os.PATH_MAX: u64)!;
|
||
let longi: i32 = 0;
|
||
for (longi < root.len) {
|
||
append(longoutputbytes, root[longi]); longi += 1;
|
||
};
|
||
append(longoutputbytes, '/': u8);
|
||
for (longoutputbytes.len < os.PATH_MAX - 2) {
|
||
append(longoutputbytes, 'o': u8);
|
||
};
|
||
append(longoutputbytes, '/': u8);
|
||
assert(longoutputbytes.len == os.PATH_MAX - 1);
|
||
let longoutputdir: str = strings.frombytes(longoutputbytes);
|
||
|
||
let compilerwrapper: str = strings.concat(root, "/output-dir-w6c.sh");
|
||
let assemblerwrapper: str = strings.concat(root, "/output-dir-w6a.sh");
|
||
let linkerwrapper: str = strings.concat(root, "/output-dir-w6l.sh");
|
||
writeexecutable(compilerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf 'compile %s\\n' \"$*\" >> \"$WW_ODIR_CTRACE\"\n",
|
||
"if test \"$WW_ODIR_COMPILE_MODE\" = fail; then\n",
|
||
" printf 'injected output-directory compiler failure\\n' >&2\n",
|
||
" exit 96\nfi\nexec \"$WW_ODIR_REAL_C\" \"$@\"\n"));
|
||
writeexecutable(assemblerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf 'assemble %s\\n' \"$*\" >> \"$WW_ODIR_ATRACE\"\n",
|
||
"exec \"$WW_ODIR_REAL_A\" \"$@\"\n"));
|
||
writeexecutable(linkerwrapper, strings.concat(
|
||
"#!/bin/sh\nprintf 'link %s\\n' \"$*\" >> \"$WW_ODIR_LTRACE\"\n",
|
||
"case \"$WW_ODIR_LINK_MODE\" in\n",
|
||
" fail) printf 'injected output-directory linker failure\\n' >&2;",
|
||
" exit 97;;\n",
|
||
" signal) kill -TERM \"$$\";;\nesac\n",
|
||
"exec \"$WW_ODIR_REAL_L\" \"$@\"\n"));
|
||
|
||
let stages: []str = ["ww", "ww_ww"];
|
||
let compilers: []str = ["w6c", "w6c_ww"];
|
||
let assemblers: []str = ["w6a", "w6a_ww"];
|
||
let linkers: []str = ["w6l", "w6l_ww"];
|
||
let tags: []str = ["c", "ww"];
|
||
let umasklauncher: str = driver("package-umaskexec");
|
||
let baseenv: []str = os.getenvs();
|
||
let routebinref: str = "";
|
||
let rawbinref: str = "";
|
||
let testbinref: str = "";
|
||
let changedbinref: str = "";
|
||
let asmref: str = "";
|
||
let nomainref: str = "";
|
||
let importref: str = "";
|
||
let implicitref: str = "";
|
||
let implicitrawref: str = "";
|
||
let implicitimportref: str = "";
|
||
let compilerfailref: str = "";
|
||
let linkerfailref: str = "";
|
||
let linkersignalref: str = "";
|
||
let rawrollbackref: str = "";
|
||
let rawnonmainref: str = "";
|
||
let rawlongref: str = "";
|
||
let rawlongimportref: str = "";
|
||
let dirlongref: str = "";
|
||
let dirlongimportref: str = "";
|
||
let mixedimportref: str = "";
|
||
let nomainimportref: str = "";
|
||
let duplicateimportref: str = "";
|
||
let duplicateref: str = "";
|
||
let recursiveimplicitref: str = "";
|
||
let recursiveimplicitimportref: str = "";
|
||
let coldctraceref: str = "";
|
||
let coldatraceref: str = "";
|
||
let coldltraceref: str = "";
|
||
let warmltraceref: str = "";
|
||
let changedctraceref: str = "";
|
||
let changedatraceref: str = "";
|
||
let changedltraceref: str = "";
|
||
let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a",
|
||
".init.unit.ww", ".init.s", ".init.o"];
|
||
let artifactrefs: []str = ["", "", "", "", "", "", "", ""];
|
||
let out: commandout;
|
||
let si: i32 = 0;
|
||
for (si < stages.len) {
|
||
rewritefile(alphafile, alphaoriginal);
|
||
let ctrace: str = strings.concat(root, "/", tags[si], "-odir-c.trace");
|
||
let atrace: str = strings.concat(root, "/", tags[si], "-odir-a.trace");
|
||
let ltrace: str = strings.concat(root, "/", tags[si], "-odir-l.trace");
|
||
writefile(ctrace, ""); writefile(atrace, ""); writefile(ltrace, "");
|
||
let env: []str = alloc([], (baseenv.len + 11): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < baseenv.len) {
|
||
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6A=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6L=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_ODIR_")) {
|
||
append(env, baseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(env, strings.concat("WW_W6C=", compilerwrapper));
|
||
append(env, strings.concat("WW_W6A=", assemblerwrapper));
|
||
append(env, strings.concat("WW_W6L=", linkerwrapper));
|
||
append(env, strings.concat("WW_ODIR_CTRACE=", ctrace));
|
||
append(env, strings.concat("WW_ODIR_ATRACE=", atrace));
|
||
append(env, strings.concat("WW_ODIR_LTRACE=", ltrace));
|
||
append(env, strings.concat("WW_ODIR_REAL_C=", driver(compilers[si])));
|
||
append(env, strings.concat("WW_ODIR_REAL_A=", driver(assemblers[si])));
|
||
append(env, strings.concat("WW_ODIR_REAL_L=", driver(linkers[si])));
|
||
let compilemodeindex: i32 = env.len;
|
||
append(env, "WW_ODIR_COMPILE_MODE=");
|
||
let linkmodeindex: i32 = env.len;
|
||
append(env, "WW_ODIR_LINK_MODE=");
|
||
|
||
// An existing directory without a trailing slash remains a directory.
|
||
// The literal root's path leaf, not its declared name or source file,
|
||
// names the command; unrelated directory contents survive publication.
|
||
let existing: str = strings.concat(root, "/existing-", tags[si]);
|
||
assert(os.mkdir(existing, 448i32) == 0);
|
||
writefile(strings.concat(existing, "/sentinel"), "existing-sentinel\n");
|
||
let existingav: []str = [driver(stages[si]), "build", "-I", source,
|
||
"-o", existing, alpha];
|
||
runcommandenvdir(root, strings.concat("odir-existing-", tags[si]),
|
||
existingav, env, root,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
assert(same(readfile(strings.concat(existing, "/sentinel")),
|
||
"existing-sentinel\n"));
|
||
let existingbin: str = strings.concat(existing, "/alpha");
|
||
assert(os.exists(existingbin));
|
||
let routebytes: str = readfile(existingbin);
|
||
if (si == 0) { routebinref = strings.dup(routebytes); }
|
||
else { assert(same(routebinref, routebytes)); };
|
||
let runav: []str = [existingbin];
|
||
runcommand(root, strings.concat("odir-existing-run-", tags[si]),
|
||
runav, time.second, &out);
|
||
expectexit(&out, 4);
|
||
assert(!directoryhasnew(existing));
|
||
|
||
// A missing trailing-slash hierarchy is created, and logical package
|
||
// resolution retains the same command identity and bytes.
|
||
let missingroot: str = strings.concat(root, "/missing-", tags[si]);
|
||
let missingout: str = strings.concat(missingroot, "/nested/");
|
||
let missingav: []str = [driver(stages[si]), "build", "-I", source,
|
||
"-o", missingout, "alpha"];
|
||
runcommandenvdir(root, strings.concat("odir-missing-", tags[si]),
|
||
missingav, env, root,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
assert(same(routebytes, readfile(strings.concat(missingout, "alpha"))));
|
||
|
||
// A contextual request's final import component is presentation
|
||
// metadata. Canonicalizing a symlink target must not leak the physical
|
||
// directory leaf into explicit or implicit command names.
|
||
let logicalout: str = strings.concat(root, "/logical-output-", tags[si]);
|
||
assert(os.mkdir(logicalout, 448i32) == 0);
|
||
let logicalav: []str = [driver(stages[si]), "build", "-I", source,
|
||
"-o", logicalout, "logical"];
|
||
runcommandenvdir(root, strings.concat("odir-logical-", tags[si]),
|
||
logicalav, env, root,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(same(routebytes,
|
||
readfile(strings.concat(logicalout, "/logical"))));
|
||
assert(!os.exists(strings.concat(logicalout, "/alpha")));
|
||
let logicalcaller: str = strings.concat(root, "/logical-caller-",
|
||
tags[si]);
|
||
let logicalwork: str = strings.concat(root, "/logical-work-",
|
||
tags[si]);
|
||
mkdirall(logicalcaller); mkdirall(logicalwork);
|
||
let logicaldefaultav: []str = [driver(stages[si]), "build", "-w",
|
||
logicalwork, "-I", source, "logical"];
|
||
runcommandenvdir(root, strings.concat("odir-logical-default-", tags[si]),
|
||
logicaldefaultav, env, logicalcaller,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(same(routebytes,
|
||
readfile(strings.concat(logicalcaller, "/logical"))));
|
||
assert(!os.exists(strings.concat(logicalcaller, "/alpha")));
|
||
|
||
// BuildInstallFunc creates a missing caller output parent from 0777;
|
||
// the launcher fixes umask at 000 so every minted prefix is observable.
|
||
let modeparent: str = strings.concat(root, "/mode-parent-", tags[si]);
|
||
let modeout: str = strings.concat(modeparent, "/nested/");
|
||
let modeav: []str = [umasklauncher, "000", driver(stages[si]),
|
||
"build", "-I", source, "-o", modeout, "alpha"];
|
||
runcommandenvdir(root, strings.concat("odir-mode-", tags[si]), modeav,
|
||
env, root, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(permissionmode(modeparent) == 511u32);
|
||
assert(permissionmode(strings.concat(modeparent, "/nested")) == 511u32);
|
||
assert(permissionmode(strings.concat(modeout, "alpha")) == 511u32);
|
||
assert(same(routebytes, readfile(strings.concat(modeout, "alpha"))));
|
||
|
||
// stat follows a symlinked output directory. With no explicit root the
|
||
// default dot is inserted into the delegated request exactly once.
|
||
let symlinktarget: str = strings.concat(root, "/symlink-target-",
|
||
tags[si]);
|
||
let symlinkout: str = strings.concat(root, "/symlink-out-", tags[si]);
|
||
assert(os.mkdir(symlinktarget, 448i32) == 0);
|
||
writefile(strings.concat(symlinktarget, "/sentinel"),
|
||
"symlink-sentinel\n");
|
||
assert(os.symlink(symlinktarget, symlinkout) == 0);
|
||
let defaultav: []str = [driver(stages[si]), "build", "-I", source,
|
||
"-o", symlinkout];
|
||
runcommandenvdir(root, strings.concat("odir-default-", tags[si]),
|
||
defaultav, env, alpha,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
assert(same(readfile(strings.concat(symlinktarget, "/sentinel")),
|
||
"symlink-sentinel\n"));
|
||
assert(same(routebytes,
|
||
readfile(strings.concat(symlinkout, "/alpha"))));
|
||
|
||
// Raw command-line files remain driver-owned. Existing and missing
|
||
// output directories both derive the source basename without .ww.
|
||
let rawexisting: str = strings.concat(root, "/raw-existing-", tags[si]);
|
||
assert(os.mkdir(rawexisting, 448i32) == 0);
|
||
writefile(strings.concat(rawexisting, "/sentinel"), "raw-sentinel\n");
|
||
let rawexistingav: []str = [driver(stages[si]), "build", "-o",
|
||
rawexisting, raw];
|
||
runcommandenvdir(root, strings.concat("odir-raw-existing-", tags[si]),
|
||
rawexistingav, env, root,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let rawbin: str = strings.concat(rawexisting, "/raw");
|
||
let rawbytes: str = readfile(rawbin);
|
||
if (si == 0) { rawbinref = strings.dup(rawbytes); }
|
||
else { assert(same(rawbinref, rawbytes)); };
|
||
let rawrun: []str = [rawbin];
|
||
runcommand(root, strings.concat("odir-raw-run-", tags[si]), rawrun,
|
||
time.second, &out);
|
||
expectexit(&out, 9);
|
||
assert(same(readfile(strings.concat(rawexisting, "/sentinel")),
|
||
"raw-sentinel\n"));
|
||
let rawmissingroot: str = strings.concat(root, "/raw-missing-",
|
||
tags[si]);
|
||
let rawmissingout: str = strings.concat(rawmissingroot, "/nested/");
|
||
let rawmissingav: []str = [driver(stages[si]), "build", "-o",
|
||
rawmissingout, raw];
|
||
runcommandenvdir(root, strings.concat("odir-raw-missing-", tags[si]),
|
||
rawmissingav, env, root,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(same(rawbytes, readfile(strings.concat(rawmissingout, "raw"))));
|
||
|
||
// The directory form requests command actions, not an archive name.
|
||
// A raw non-main root therefore rejects after loading and before tools or
|
||
// caller-directory creation, just like a directory-package root.
|
||
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
|
||
let rawlibroot: str = strings.concat(root, "/raw-library-", tags[si]);
|
||
let rawlibout: str = strings.concat(rawlibroot, "/nested/");
|
||
let rawlibav: []str = [driver(stages[si]), "build", "-o", rawlibout,
|
||
rawlibrary];
|
||
runcommandenvdir(root, strings.concat("odir-raw-library-", tags[si]),
|
||
rawlibav, env, root, time.second, &out);
|
||
expectexit(&out, 1);
|
||
assert(same(out.stderr, "ww: no main packages to build\n"));
|
||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0
|
||
&& readfile(ltrace).len == 0);
|
||
assert(!os.exists(rawlibroot));
|
||
if (si == 0) { rawnonmainref = strings.dup(out.stderr); }
|
||
else { assert(same(rawnonmainref, out.stderr)); };
|
||
|
||
// Raw destination joining is output preflight, not loading. A bad import
|
||
// therefore wins over the derived PATH_MAX failure; a valid command then
|
||
// reports that path error before tools or directory creation.
|
||
let rawlongbadav: []str = [driver(stages[si]), "build", "-o",
|
||
longoutputdir, rawbad];
|
||
runcommandenvdir(root, strings.concat("odir-raw-long-import-", tags[si]),
|
||
rawlongbadav, env, root, time.second, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, "cannot find package missing.pkg\n"));
|
||
assert(!has(out.stderr, "command output path is too long"));
|
||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0
|
||
&& readfile(ltrace).len == 0);
|
||
if (si == 0) { rawlongimportref = strings.dup(out.stderr); }
|
||
else { assert(same(rawlongimportref, out.stderr)); };
|
||
let rawlongav: []str = [driver(stages[si]), "build", "-o",
|
||
longoutputdir, raw];
|
||
runcommandenvdir(root, strings.concat("odir-raw-long-", tags[si]),
|
||
rawlongav, env, root, time.second, &out);
|
||
expectexit(&out, 1);
|
||
assert(same(out.stderr, "ww: command output path is too long\n"));
|
||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0
|
||
&& readfile(ltrace).len == 0);
|
||
if (si == 0) { rawlongref = strings.dup(out.stderr); }
|
||
else { assert(same(rawlongref, out.stderr)); };
|
||
|
||
// The coordinator also defers its derived command destination until the
|
||
// shared driver has loaded every import. The valid twin pins the retained
|
||
// PATH_MAX rejection after the same no-tool boundary.
|
||
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
|
||
let dirlongbadav: []str = [driver(stages[si]), "build", "-I", source,
|
||
"-o", longoutputdir, bad];
|
||
runcommandenvdir(root, strings.concat("odir-directory-long-import-",
|
||
tags[si]), dirlongbadav, env, root, time.second, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, "cannot find package missing.pkg\n"));
|
||
assert(!has(out.stderr, "command output path is too long"));
|
||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0
|
||
&& readfile(ltrace).len == 0);
|
||
if (si == 0) { dirlongimportref = strings.dup(out.stderr); }
|
||
else { assert(same(dirlongimportref, out.stderr)); };
|
||
let dirlongav: []str = [driver(stages[si]), "build", "-I", source,
|
||
"-o", longoutputdir, alpha];
|
||
runcommandenvdir(root, strings.concat("odir-directory-long-", tags[si]),
|
||
dirlongav, env, root, time.second, &out);
|
||
expectexit(&out, 1);
|
||
assert(same(out.stderr, "ww: command output path is too long\n"));
|
||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0
|
||
&& readfile(ltrace).len == 0);
|
||
if (si == 0) { dirlongref = strings.dup(out.stderr); }
|
||
else { assert(same(dirlongref, out.stderr)); };
|
||
|
||
// Test retains its already-established directory semantics; dispatching
|
||
// build through the coordinator must not disturb the adjacent branch.
|
||
let testout: str = strings.concat(root, "/test-output-", tags[si]);
|
||
assert(os.mkdir(testout, 448i32) == 0);
|
||
let testav: []str = [driver(stages[si]), "test", "-c", "-I", source,
|
||
"-o", testout, alpha];
|
||
runcommandenvdir(root, strings.concat("odir-test-control-", tags[si]),
|
||
testav, env, root,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let testbin: str = strings.concat(testout, "/alpha.test");
|
||
let testbytes: str = readfile(testbin);
|
||
if (si == 0) { testbinref = strings.dup(testbytes); }
|
||
else { assert(same(testbinref, testbytes)); };
|
||
|
||
// Repeated command roots deduplicate. A mixed independent non-main root
|
||
// is loaded but omitted from the command-only action list; dependencies
|
||
// of the retained command still build normally.
|
||
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
|
||
let mixedout: str = strings.concat(root, "/mixed-", tags[si]);
|
||
let mixedwork: str = strings.concat(root, "/mixed-work-", tags[si]);
|
||
assert(os.mkdir(mixedout, 448i32) == 0);
|
||
let mixedav: []str = [driver(stages[si]), "build", "-w", mixedwork,
|
||
"-I", source,
|
||
"-o", mixedout, alpha, alpha, library];
|
||
runcommandenvdir(root, strings.concat("odir-mixed-", tags[si]),
|
||
mixedav, env, root,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(os.exists(strings.concat(mixedout, "/alpha")));
|
||
assert(!os.exists(strings.concat(mixedout, "/library")));
|
||
assert(occurrences(readfile(ctrace), "\n") == 2);
|
||
assert(occurrences(readfile(atrace), "\n") == 3);
|
||
assert(occurrences(readfile(ltrace), "\n") == 1);
|
||
assert(!has(readfile(ctrace), "library.unit.ww"));
|
||
assert(!os.exists(strings.concat(mixedwork, "/library.a")));
|
||
|
||
// Filtering occurs only after loading every selected root. A structural
|
||
// import failure in an otherwise skipped non-main sibling therefore
|
||
// outranks actions and output mutation.
|
||
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
|
||
let mixedbadout: str = strings.concat(root, "/mixed-bad-", tags[si]);
|
||
assert(os.mkdir(mixedbadout, 448i32) == 0);
|
||
writefile(strings.concat(mixedbadout, "/sentinel"),
|
||
"mixed-bad-sentinel\n");
|
||
let mixedbadav: []str = [driver(stages[si]), "build", "-I", source,
|
||
"-o", mixedbadout, alpha, badlibrary];
|
||
runcommandenvdir(root, strings.concat("odir-mixed-bad-", tags[si]),
|
||
mixedbadav, env, root, time.second, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, "cannot find package missing.pkg\n"));
|
||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0
|
||
&& readfile(ltrace).len == 0);
|
||
assert(same(readfile(strings.concat(mixedbadout, "/sentinel")),
|
||
"mixed-bad-sentinel\n"));
|
||
if (si == 0) { mixedimportref = strings.dup(out.stderr); }
|
||
else { assert(same(mixedimportref, out.stderr)); };
|
||
|
||
// Duplicate caller-visible basenames are output preflight too. A load
|
||
// failure in either selected command wins; two valid commands then reject
|
||
// the ambiguous publication without invoking any producer.
|
||
let duplicateout: str = strings.concat(root, "/duplicate-output");
|
||
if (si == 0) {
|
||
assert(os.mkdir(duplicateout, 448i32) == 0);
|
||
writefile(strings.concat(duplicateout, "/sentinel"),
|
||
"duplicate-sentinel\n");
|
||
};
|
||
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
|
||
let duplicatebadav: []str = [driver(stages[si]), "build", "-I", source,
|
||
"-o", duplicateout, duplicateone, duplicatetwo];
|
||
runcommandenvdir(root, strings.concat("odir-duplicate-import-", tags[si]),
|
||
duplicatebadav, env, root, time.second, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, "cannot find package missing.pkg\n"));
|
||
assert(!has(out.stderr, "multiple commands produce output basename"));
|
||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0
|
||
&& readfile(ltrace).len == 0);
|
||
assert(same(readfile(strings.concat(duplicateout, "/sentinel")),
|
||
"duplicate-sentinel\n"));
|
||
if (si == 0) { duplicateimportref = strings.dup(out.stderr); }
|
||
else { assert(same(duplicateimportref, out.stderr)); };
|
||
let duplicateav: []str = [driver(stages[si]), "build", "-I", source,
|
||
"-o", duplicateout, duplicateone, duplicatethree];
|
||
runcommandenvdir(root, strings.concat("odir-duplicate-", tags[si]),
|
||
duplicateav, env, root, time.second, &out);
|
||
expectexit(&out, 1);
|
||
assert(same(out.stderr, strings.concat(
|
||
"ww: multiple commands produce output basename \"tool\" in directory \"",
|
||
duplicateout, "\"\n")));
|
||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0
|
||
&& readfile(ltrace).len == 0);
|
||
assert(same(readfile(strings.concat(duplicateout, "/sentinel")),
|
||
"duplicate-sentinel\n"));
|
||
assert(!directoryhasnew(duplicateout));
|
||
if (si == 0) { duplicateref = strings.dup(out.stderr); }
|
||
else { assert(same(duplicateref, out.stderr)); };
|
||
|
||
// No-main and import failures are loading/planning rejections. They run
|
||
// no tools, preserve existing contents, and do not mint missing output.
|
||
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
|
||
let badnonmainout: str = strings.concat(root, "/bad-nonmain-", tags[si]);
|
||
assert(os.mkdir(badnonmainout, 448i32) == 0);
|
||
writefile(strings.concat(badnonmainout, "/sentinel"),
|
||
"bad-nonmain-sentinel\n");
|
||
let badnonmainav: []str = [driver(stages[si]), "build", "-I", source,
|
||
"-o", badnonmainout, badlibrary];
|
||
runcommandenvdir(root, strings.concat("odir-nonmain-import-", tags[si]),
|
||
badnonmainav, env, root, time.second, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, "cannot find package missing.pkg\n"));
|
||
assert(!has(out.stderr, "no main packages to build"));
|
||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0
|
||
&& readfile(ltrace).len == 0);
|
||
assert(same(readfile(strings.concat(badnonmainout, "/sentinel")),
|
||
"bad-nonmain-sentinel\n"));
|
||
if (si == 0) { nomainimportref = strings.dup(out.stderr); }
|
||
else { assert(same(nomainimportref, out.stderr)); };
|
||
|
||
let nonmainout: str = strings.concat(root, "/nonmain-", tags[si]);
|
||
assert(os.mkdir(nonmainout, 448i32) == 0);
|
||
writefile(strings.concat(nonmainout, "/sentinel"), "nonmain-sentinel\n");
|
||
let nonmainav: []str = [driver(stages[si]), "build", "-I", source,
|
||
"-o", nonmainout, library];
|
||
runcommandenvdir(root, strings.concat("odir-nonmain-", tags[si]),
|
||
nonmainav, env, root, time.second, &out);
|
||
expectexit(&out, 1);
|
||
assert(same(out.stderr, "ww: no main packages to build\n"));
|
||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0
|
||
&& readfile(ltrace).len == 0);
|
||
assert(same(readfile(strings.concat(nonmainout, "/sentinel")),
|
||
"nonmain-sentinel\n"));
|
||
if (si == 0) { nomainref = strings.dup(out.stderr); }
|
||
else { assert(same(nomainref, out.stderr)); };
|
||
|
||
let badroot: str = strings.concat(root, "/bad-output-", tags[si]);
|
||
let badout: str = strings.concat(badroot, "/nested/");
|
||
let badav: []str = [driver(stages[si]), "build", "-I", source,
|
||
"-o", badout, bad];
|
||
runcommandenvdir(root, strings.concat("odir-import-", tags[si]), badav,
|
||
env, root, time.second, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, "cannot find package missing.pkg\n"));
|
||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0
|
||
&& readfile(ltrace).len == 0);
|
||
assert(!os.exists(badroot));
|
||
if (si == 0) { importref = strings.dup(out.stderr); }
|
||
else { assert(same(importref, out.stderr)); };
|
||
|
||
// The same pinned branch runs after a lone command's default output
|
||
// name is synthesized. An existing directory is then a load-complete
|
||
// rejection, while a non-main package has no default public output.
|
||
let implicitcaller: str = strings.concat(root, "/implicit-caller-",
|
||
tags[si]);
|
||
let implicitout: str = strings.concat(implicitcaller, "/alpha");
|
||
mkdirall(implicitout);
|
||
writefile(strings.concat(implicitout, "/sentinel"),
|
||
"implicit-sentinel\n");
|
||
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
|
||
let implicitav: []str = [driver(stages[si]), "build", "-I", source,
|
||
alpha];
|
||
runcommandenvdir(root, strings.concat("odir-implicit-", tags[si]),
|
||
implicitav, env, implicitcaller, time.second, &out);
|
||
expectexit(&out, 1);
|
||
assert(same(out.stderr,
|
||
"ww: build output \"alpha\" already exists and is a directory\n"));
|
||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0
|
||
&& readfile(ltrace).len == 0);
|
||
assert(same(readfile(strings.concat(implicitout, "/sentinel")),
|
||
"implicit-sentinel\n"));
|
||
if (si == 0) { implicitref = strings.dup(out.stderr); }
|
||
else { assert(same(implicitref, out.stderr)); };
|
||
|
||
let implicitrawcaller: str = strings.concat(root,
|
||
"/implicit-raw-caller-", tags[si]);
|
||
let implicitrawout: str = strings.concat(implicitrawcaller, "/raw");
|
||
mkdirall(implicitrawout);
|
||
writefile(strings.concat(implicitrawout, "/sentinel"),
|
||
"implicit-raw-sentinel\n");
|
||
let implicitrawav: []str = [driver(stages[si]), "build", raw];
|
||
runcommandenvdir(root, strings.concat("odir-implicit-raw-", tags[si]),
|
||
implicitrawav, env, implicitrawcaller, time.second, &out);
|
||
expectexit(&out, 1);
|
||
assert(same(out.stderr,
|
||
"ww: build output \"raw\" already exists and is a directory\n"));
|
||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0
|
||
&& readfile(ltrace).len == 0);
|
||
assert(same(readfile(strings.concat(implicitrawout, "/sentinel")),
|
||
"implicit-raw-sentinel\n"));
|
||
if (si == 0) { implicitrawref = strings.dup(out.stderr); }
|
||
else { assert(same(implicitrawref, out.stderr)); };
|
||
|
||
let implicitbadcaller: str = strings.concat(root,
|
||
"/implicit-bad-caller-", tags[si]);
|
||
let implicitbadout: str = strings.concat(implicitbadcaller, "/bad");
|
||
mkdirall(implicitbadout);
|
||
let implicitbadav: []str = [driver(stages[si]), "build", "-I", source,
|
||
bad];
|
||
runcommandenvdir(root, strings.concat("odir-implicit-bad-", tags[si]),
|
||
implicitbadav, env, implicitbadcaller, time.second, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, "cannot find package missing.pkg\n"));
|
||
assert(!has(out.stderr, "already exists and is a directory"));
|
||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0
|
||
&& readfile(ltrace).len == 0);
|
||
if (si == 0) { implicitimportref = strings.dup(out.stderr); }
|
||
else { assert(same(implicitimportref, out.stderr)); };
|
||
|
||
// Recursive selection reaches the same load-before-output boundary. Its
|
||
// implicit basename collision is deferred until all selected imports pass.
|
||
let recursivebadcaller: str = strings.concat(root,
|
||
"/recursive-bad-caller-", tags[si]);
|
||
let recursivebadout: str = strings.concat(recursivebadcaller, "/bad");
|
||
mkdirall(recursivebadout);
|
||
writefile(strings.concat(recursivebadout, "/sentinel"),
|
||
"recursive-bad-sentinel\n");
|
||
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
|
||
let recursivebadav: []str = [driver(stages[si]), "build", "-I", source,
|
||
strings.concat(bad, "/...")];
|
||
runcommandenvdir(root, strings.concat("odir-recursive-import-", tags[si]),
|
||
recursivebadav, env, recursivebadcaller, time.second, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, "cannot find package missing.pkg\n"));
|
||
assert(!has(out.stderr, "already exists and is a directory"));
|
||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0
|
||
&& readfile(ltrace).len == 0);
|
||
assert(same(readfile(strings.concat(recursivebadout, "/sentinel")),
|
||
"recursive-bad-sentinel\n"));
|
||
if (si == 0) { recursiveimplicitimportref = strings.dup(out.stderr); }
|
||
else { assert(same(recursiveimplicitimportref, out.stderr)); };
|
||
|
||
let recursivecaller: str = strings.concat(root, "/recursive-caller-",
|
||
tags[si]);
|
||
let recursiveout: str = strings.concat(recursivecaller, "/alpha");
|
||
mkdirall(recursiveout);
|
||
writefile(strings.concat(recursiveout, "/sentinel"),
|
||
"recursive-sentinel\n");
|
||
let recursiveav: []str = [driver(stages[si]), "build", "-I", source,
|
||
strings.concat(alpha, "/...")];
|
||
runcommandenvdir(root, strings.concat("odir-recursive-", tags[si]),
|
||
recursiveav, env, recursivecaller, time.second, &out);
|
||
expectexit(&out, 1);
|
||
assert(same(out.stderr,
|
||
"ww: build output \"alpha\" already exists and is a directory\n"));
|
||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0
|
||
&& readfile(ltrace).len == 0);
|
||
assert(same(readfile(strings.concat(recursiveout, "/sentinel")),
|
||
"recursive-sentinel\n"));
|
||
assert(!directoryhasnew(recursiveout));
|
||
if (si == 0) { recursiveimplicitref = strings.dup(out.stderr); }
|
||
else { assert(same(recursiveimplicitref, out.stderr)); };
|
||
|
||
let implicitlibcaller: str = strings.concat(root,
|
||
"/implicit-library-caller-", tags[si]);
|
||
let implicitlibout: str = strings.concat(implicitlibcaller, "/library");
|
||
let implicitlibwork: str = strings.concat(root,
|
||
"/implicit-library-work-", tags[si]);
|
||
mkdirall(implicitlibout); mkdirall(implicitlibwork);
|
||
writefile(strings.concat(implicitlibout, "/sentinel"),
|
||
"implicit-library-sentinel\n");
|
||
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
|
||
let implicitlibav: []str = [driver(stages[si]), "build", "-w",
|
||
implicitlibwork, "-I", source, library];
|
||
runcommandenvdir(root, strings.concat("odir-implicit-library-", tags[si]),
|
||
implicitlibav, env, implicitlibcaller,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(occurrences(readfile(ctrace), "\n") == 1);
|
||
assert(occurrences(readfile(atrace), "\n") == 1);
|
||
assert(readfile(ltrace).len == 0);
|
||
assert(same(readfile(strings.concat(implicitlibout, "/sentinel")),
|
||
"implicit-library-sentinel\n"));
|
||
assert(!os.exists(strings.concat(implicitlibout, "/library.a")));
|
||
|
||
// -S remains action-only. With the coordinator-owned -w tree it emits
|
||
// semantic assembly but no command publication beneath -o.
|
||
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
|
||
let asmout: str = strings.concat(root, "/asm-output-", tags[si]);
|
||
let asmwork: str = strings.concat(root, "/asm-work-", tags[si]);
|
||
assert(os.mkdir(asmout, 448i32) == 0);
|
||
writefile(strings.concat(asmout, "/sentinel"), "asm-sentinel\n");
|
||
let asmav: []str = [driver(stages[si]), "build", "-S", "-w", asmwork,
|
||
"-I", source, "-o", asmout, "alpha"];
|
||
runcommandenvdir(root, strings.concat("odir-asm-", tags[si]), asmav,
|
||
env, root,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(readfile(ctrace).len != 0 && readfile(atrace).len == 0
|
||
&& readfile(ltrace).len == 0);
|
||
assert(!os.exists(strings.concat(asmout, "/alpha")));
|
||
assert(same(readfile(strings.concat(asmout, "/sentinel")),
|
||
"asm-sentinel\n"));
|
||
let asmbytes: str = readfile(strings.concat(asmwork, "/alpha.s"));
|
||
if (si == 0) { asmref = strings.dup(asmbytes); }
|
||
else { assert(same(asmref, asmbytes)); };
|
||
|
||
// WW's -S boundary stops before Go's install action. Directory form still
|
||
// selects command actions, but publication-only path, collision, default,
|
||
// and parent-creation checks are inapplicable.
|
||
let asmmissingroot: str = strings.concat(root, "/asm-missing-", tags[si]);
|
||
let asmmissingout: str = strings.concat(asmmissingroot, "/nested/");
|
||
let asmmissingav: []str = [driver(stages[si]), "build", "-S", "-w",
|
||
asmwork, "-I", source, "-o", asmmissingout, alpha];
|
||
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
|
||
runcommandenvdir(root, strings.concat("odir-asm-missing-", tags[si]),
|
||
asmmissingav, env, root, time.second, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
assert(!os.exists(asmmissingroot));
|
||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0
|
||
&& readfile(ltrace).len == 0);
|
||
|
||
let asmlongav: []str = [driver(stages[si]), "build", "-S", "-w",
|
||
asmwork, "-I", source, "-o", longoutputdir, alpha];
|
||
runcommandenvdir(root, strings.concat("odir-asm-long-", tags[si]),
|
||
asmlongav, env, root, time.second, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0
|
||
&& readfile(ltrace).len == 0);
|
||
|
||
let asmduplicatework: str = strings.concat(root, "/asm-duplicate-work-",
|
||
tags[si]);
|
||
let asmduplicateav: []str = [driver(stages[si]), "build", "-S", "-w",
|
||
asmduplicatework, "-I", source, "-o", duplicateout,
|
||
duplicateone, duplicatethree];
|
||
runcommandenvdir(root, strings.concat("odir-asm-duplicate-", tags[si]),
|
||
asmduplicateav, env, root,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
assert(occurrences(readfile(ctrace), "\n") == 2);
|
||
assert(readfile(atrace).len == 0 && readfile(ltrace).len == 0);
|
||
assert(same(readfile(strings.concat(duplicateout, "/sentinel")),
|
||
"duplicate-sentinel\n"));
|
||
|
||
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
|
||
let asmdefaultav: []str = [driver(stages[si]), "build", "-S", "-w",
|
||
asmwork, "-I", source, alpha];
|
||
runcommandenvdir(root, strings.concat("odir-asm-default-", tags[si]),
|
||
asmdefaultav, env, implicitcaller, time.second, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0
|
||
&& readfile(ltrace).len == 0);
|
||
assert(same(readfile(strings.concat(implicitout, "/sentinel")),
|
||
"implicit-sentinel\n"));
|
||
|
||
let asmnonmainroot: str = strings.concat(root, "/asm-nonmain-", tags[si]);
|
||
let asmnonmainout: str = strings.concat(asmnonmainroot, "/nested/");
|
||
let asmnonmainav: []str = [driver(stages[si]), "build", "-S", "-w",
|
||
asmwork, "-I", source, "-o", asmnonmainout, library];
|
||
runcommandenvdir(root, strings.concat("odir-asm-nonmain-", tags[si]),
|
||
asmnonmainav, env, root, time.second, &out);
|
||
expectexit(&out, 1);
|
||
assert(same(out.stderr, "ww: no main packages to build\n"));
|
||
assert(!os.exists(asmnonmainroot));
|
||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0
|
||
&& readfile(ltrace).len == 0);
|
||
|
||
// Persistent action storage is independent of output disposition. Cold
|
||
// and invalidated requests compile normally; warm commands still relink.
|
||
rewritefile(alphafile, alphaoriginal);
|
||
let work: str = strings.concat(root, "/persist-work-", tags[si]);
|
||
let persistout: str = strings.concat(root, "/persist-output-", tags[si]);
|
||
assert(os.mkdir(persistout, 448i32) == 0);
|
||
writefile(strings.concat(persistout, "/sentinel"), "persist-sentinel\n");
|
||
let persistav: []str = [driver(stages[si]), "build", "-w", work,
|
||
"-I", source, "-o", persistout, "alpha"];
|
||
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
|
||
runcommandenvdir(root, strings.concat("odir-cold-", tags[si]), persistav,
|
||
env, root,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(occurrences(readfile(ctrace), "\n") == 2);
|
||
assert(occurrences(readfile(atrace), "\n") == 3);
|
||
assert(occurrences(readfile(ltrace), "\n") == 1);
|
||
let persistbin: str = strings.concat(persistout, "/alpha");
|
||
let coldbytes: str = readfile(persistbin);
|
||
let normalizedc: str = normalizedtrace(readfile(ctrace),
|
||
strings.concat(work, "/"), strings.concat(persistout, "/"));
|
||
let normalizeda: str = normalizedtrace(readfile(atrace),
|
||
strings.concat(work, "/"), strings.concat(persistout, "/"));
|
||
let normalizedl: str = normalizedtrace(readfile(ltrace),
|
||
strings.concat(work, "/"), strings.concat(persistout, "/"));
|
||
if (si == 0) {
|
||
coldctraceref = strings.dup(normalizedc);
|
||
coldatraceref = strings.dup(normalizeda);
|
||
coldltraceref = strings.dup(normalizedl);
|
||
} else {
|
||
assert(same(coldctraceref, normalizedc));
|
||
assert(same(coldatraceref, normalizeda));
|
||
assert(same(coldltraceref, normalizedl));
|
||
};
|
||
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
|
||
runcommandenvdir(root, strings.concat("odir-warm-", tags[si]), persistav,
|
||
env, root,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0);
|
||
assert(occurrences(readfile(ltrace), "\n") == 1);
|
||
assert(same(coldbytes, readfile(persistbin)));
|
||
let warmtrace: str = normalizedtrace(readfile(ltrace),
|
||
strings.concat(work, "/"), strings.concat(persistout, "/"));
|
||
if (si == 0) { warmltraceref = strings.dup(warmtrace); }
|
||
else { assert(same(warmltraceref, warmtrace)); };
|
||
|
||
rewritefile(alphafile, alphachanged);
|
||
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
|
||
runcommandenvdir(root, strings.concat("odir-invalidated-", tags[si]),
|
||
persistav, env, root,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(occurrences(readfile(ctrace), "\n") == 1);
|
||
assert(occurrences(readfile(atrace), "\n") == 1);
|
||
assert(occurrences(readfile(ltrace), "\n") == 1);
|
||
let changedbytes: str = readfile(persistbin);
|
||
assert(!same(coldbytes, changedbytes));
|
||
if (si == 0) { changedbinref = strings.dup(changedbytes); }
|
||
else { assert(same(changedbinref, changedbytes)); };
|
||
let persistrunav: []str = [persistbin];
|
||
runcommand(root, strings.concat("odir-invalidated-run-", tags[si]),
|
||
persistrunav, time.second, &out);
|
||
expectexit(&out, 5);
|
||
normalizedc = normalizedtrace(readfile(ctrace),
|
||
strings.concat(work, "/"), strings.concat(persistout, "/"));
|
||
normalizeda = normalizedtrace(readfile(atrace),
|
||
strings.concat(work, "/"), strings.concat(persistout, "/"));
|
||
normalizedl = normalizedtrace(readfile(ltrace),
|
||
strings.concat(work, "/"), strings.concat(persistout, "/"));
|
||
if (si == 0) {
|
||
changedctraceref = strings.dup(normalizedc);
|
||
changedatraceref = strings.dup(normalizeda);
|
||
changedltraceref = strings.dup(normalizedl);
|
||
} else {
|
||
assert(same(changedctraceref, normalizedc));
|
||
assert(same(changedatraceref, normalizeda));
|
||
assert(same(changedltraceref, normalizedl));
|
||
};
|
||
let ai: i32 = 0;
|
||
for (ai < suffixes.len) {
|
||
let artifact: str = readfile(strings.concat(work, "/alpha",
|
||
suffixes[ai]));
|
||
if (si == 0) { artifactrefs[ai] = strings.dup(artifact); }
|
||
else { assert(same(artifactrefs[ai], artifact)); };
|
||
ai += 1;
|
||
};
|
||
assert((permissionmode(persistbin) & 73u32) != 0u32);
|
||
assert(!os.exists(strings.concat(persistout, "/alpha.sepwork")));
|
||
|
||
// Compiler failure, linker failure, and linker interruption cannot
|
||
// replace the prior command or commit an invalid persistent generation.
|
||
let kept: []str = alloc([], suffixes.len: u64)!;
|
||
ai = 0;
|
||
for (ai < suffixes.len) {
|
||
append(kept, strings.dup(readfile(strings.concat(work, "/alpha",
|
||
suffixes[ai]))));
|
||
ai += 1;
|
||
};
|
||
rewritefile(alphafile, alphafailing);
|
||
env[compilemodeindex] = "WW_ODIR_COMPILE_MODE=fail";
|
||
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
|
||
runcommandenvdir(root, strings.concat("odir-compiler-fail-", tags[si]),
|
||
persistav, env, root,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr,
|
||
"injected output-directory compiler failure\n"));
|
||
assert(has(out.stderr, "ww: w6c failed for alpha\n"));
|
||
assert(readfile(atrace).len == 0 && readfile(ltrace).len == 0);
|
||
assert(same(changedbytes, readfile(persistbin)));
|
||
if (si == 0) { compilerfailref = strings.dup(out.stderr); }
|
||
else { assert(same(compilerfailref, out.stderr)); };
|
||
env[compilemodeindex] = "WW_ODIR_COMPILE_MODE=";
|
||
env[linkmodeindex] = "WW_ODIR_LINK_MODE=fail";
|
||
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
|
||
runcommandenvdir(root, strings.concat("odir-linker-fail-", tags[si]),
|
||
persistav, env, root,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, "injected output-directory linker failure\n"));
|
||
assert(has(out.stderr, "ww: w6l failed\n"));
|
||
assert(same(changedbytes, readfile(persistbin)));
|
||
if (si == 0) { linkerfailref = strings.dup(out.stderr); }
|
||
else { assert(same(linkerfailref, out.stderr)); };
|
||
env[linkmodeindex] = "WW_ODIR_LINK_MODE=signal";
|
||
rewritefile(ctrace, ""); rewritefile(atrace, ""); rewritefile(ltrace, "");
|
||
runcommandenvdir(root, strings.concat("odir-linker-signal-", tags[si]),
|
||
persistav, env, root,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, "ww: w6l failed\n"));
|
||
assert(same(changedbytes, readfile(persistbin)));
|
||
if (si == 0) { linkersignalref = strings.dup(out.stderr); }
|
||
else { assert(same(linkersignalref, out.stderr)); };
|
||
ai = 0;
|
||
for (ai < suffixes.len) {
|
||
assert(same(kept[ai], readfile(strings.concat(work, "/alpha",
|
||
suffixes[ai]))));
|
||
ai += 1;
|
||
};
|
||
assert(!directoryhasnew(work) && !directoryhasnew(persistout));
|
||
assert(!directoryhasfragment(work, ".wwtxn."));
|
||
assert(!directoryhasfragment(persistout, ".wwtxn."));
|
||
assert(same(readfile(strings.concat(persistout, "/sentinel")),
|
||
"persist-sentinel\n"));
|
||
|
||
// The raw direct route uses the same creation ledger. A late failure
|
||
// removes every directory minted for this request and leaves no stage.
|
||
env[linkmodeindex] = "WW_ODIR_LINK_MODE=fail";
|
||
let rawfailroot: str = strings.concat(root, "/raw-fail-", tags[si]);
|
||
let rawfailout: str = strings.concat(rawfailroot, "/nested/");
|
||
let rawfailav: []str = [driver(stages[si]), "build", "-o",
|
||
rawfailout, raw];
|
||
runcommandenvdir(root, strings.concat("odir-raw-fail-", tags[si]),
|
||
rawfailav, env, root,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, "injected output-directory linker failure\n"));
|
||
assert(!os.exists(rawfailroot));
|
||
if (si == 0) { rawrollbackref = strings.dup(out.stderr); }
|
||
else { assert(same(rawrollbackref, out.stderr)); };
|
||
env[linkmodeindex] = "WW_ODIR_LINK_MODE=";
|
||
assert(!directoryhasfragment(root, ".wwtxn."));
|
||
si += 1;
|
||
};
|
||
|
||
// Distinct Cstage and WWstage requests may build simultaneously without
|
||
// sharing output, work, stage, transaction, or process ownership.
|
||
rewritefile(alphafile, alphaoriginal);
|
||
let concurrentcout: str = strings.concat(root, "/concurrent-c-output");
|
||
let concurrentwout: str = strings.concat(root, "/concurrent-ww-output");
|
||
let concurrentcwork: str = strings.concat(root, "/concurrent-c-work");
|
||
let concurrentwwork: str = strings.concat(root, "/concurrent-ww-work");
|
||
mkdirall(concurrentcout); mkdirall(concurrentwout);
|
||
writefile(strings.concat(concurrentcout, "/sentinel"), "c-sentinel\n");
|
||
writefile(strings.concat(concurrentwout, "/sentinel"), "ww-sentinel\n");
|
||
let cav: []str = [driver("ww"), "build", "-w", concurrentcwork, "-I",
|
||
source, "-o", concurrentcout, "alpha"];
|
||
let wav: []str = [driver("ww_ww"), "build", "-w", concurrentwwork,
|
||
"-I", source, "-o", concurrentwout, "alpha"];
|
||
let cc: exec.command;
|
||
cc.path = cav[0]; cc.argv = cav; cc.env = os.getenvs(); cc.dir = root;
|
||
cc.stdoutpath = strings.concat(root, "/concurrent-c.stdout");
|
||
cc.stderrpath = strings.concat(root, "/concurrent-c.stderr");
|
||
cc.deadline = time.add(time.now(time.clock.monotonic),
|
||
(120i64 * (time.second: i64)): time.duration);
|
||
cc.grace = (100i64 * (time.millisecond: i64)): time.duration;
|
||
let wc: exec.command;
|
||
wc.path = wav[0]; wc.argv = wav; wc.env = os.getenvs(); wc.dir = root;
|
||
wc.stdoutpath = strings.concat(root, "/concurrent-ww.stdout");
|
||
wc.stderrpath = strings.concat(root, "/concurrent-ww.stderr");
|
||
wc.deadline = cc.deadline;
|
||
wc.grace = cc.grace;
|
||
let cp: exec.process;
|
||
let wp: exec.process;
|
||
exec.start(&cp, &cc); exec.start(&wp, &wc);
|
||
let cdone: bool = false;
|
||
let wdone: bool = false;
|
||
for (!cdone || !wdone) {
|
||
if (!cdone) { cdone = exec.poll(&cp); };
|
||
if (!wdone) { wdone = exec.poll(&wp); };
|
||
if (!cdone || !wdone) {
|
||
time.sleep(time.millisecond, time.clock.monotonic);
|
||
};
|
||
};
|
||
assert(cp.result.errno == 0 && cp.result.cleanuperrno == 0);
|
||
assert(wp.result.errno == 0 && wp.result.cleanuperrno == 0);
|
||
assert(cp.result.termination == exec.termination.EXIT && cp.result.code == 0);
|
||
assert(wp.result.termination == exec.termination.EXIT && wp.result.code == 0);
|
||
assert(readfile(cc.stdoutpath).len == 0 && readfile(cc.stderrpath).len == 0);
|
||
assert(readfile(wc.stdoutpath).len == 0 && readfile(wc.stderrpath).len == 0);
|
||
assert(same(readfile(strings.concat(concurrentcout, "/alpha")),
|
||
readfile(strings.concat(concurrentwout, "/alpha"))));
|
||
assert(same(readfile(strings.concat(concurrentcout, "/sentinel")),
|
||
"c-sentinel\n"));
|
||
assert(same(readfile(strings.concat(concurrentwout, "/sentinel")),
|
||
"ww-sentinel\n"));
|
||
assert(!directoryhasnew(concurrentcout)
|
||
&& !directoryhasnew(concurrentwout));
|
||
assert(!directoryhasnew(concurrentcwork)
|
||
&& !directoryhasnew(concurrentwwork));
|
||
assert(!directoryhasfragment(root, ".wwtxn."));
|
||
clean(root);
|
||
};
|
||
|
||
@test fn build_output_permissions_follow_umask() void = {
|
||
let root: str = fresh();
|
||
let source: str = strings.concat(root, "/source");
|
||
let alpha: str = strings.concat(source, "/alpha");
|
||
let fan: str = strings.concat(source, "/fan");
|
||
let fanalpha: str = strings.concat(fan, "/alpha");
|
||
let fanbeta: str = strings.concat(fan, "/beta");
|
||
let fanlib: str = strings.concat(fan, "/library");
|
||
let library: str = strings.concat(source, "/library");
|
||
let check: str = strings.concat(source, "/check");
|
||
mkdirall(alpha); mkdirall(fanalpha); mkdirall(fanbeta);
|
||
mkdirall(fanlib); mkdirall(library); mkdirall(check);
|
||
let alphafile: str = strings.concat(alpha, "/main.ww");
|
||
let alphabase: str = strings.concat(
|
||
"package main;\n",
|
||
"fn main() i32 = { return 21; };\n");
|
||
let alphachanged: str = strings.concat(
|
||
"package main;\n",
|
||
"fn main() i32 = { return 22; };\n");
|
||
writefile(alphafile, alphabase);
|
||
writefile(strings.concat(fanalpha, "/main.ww"),
|
||
"package main;\nfn main() i32 = { return 31; };\n");
|
||
writefile(strings.concat(fanbeta, "/main.ww"),
|
||
"package main;\nfn main() i32 = { return 32; };\n");
|
||
writefile(strings.concat(fanlib, "/library.ww"),
|
||
"package library;\nexport fn value() i32 = { return 33; };\n");
|
||
writefile(strings.concat(library, "/library.ww"),
|
||
"package library;\nexport fn value() i32 = { return 41; };\n");
|
||
let raw: str = strings.concat(source, "/raw.ww");
|
||
writefile(raw, "package main;\nfn main() i32 = { return 23; };\n");
|
||
writefile(strings.concat(check, "/check.ww"),
|
||
"package check;\nfn value() i32 = { return 1; };\n");
|
||
writefile(strings.concat(check, "/check_test.ww"), strings.concat(
|
||
"package check;\n",
|
||
"@test fn retained() void = { assert(value() == 1); };\n"));
|
||
|
||
let launcher: str = driver("package-umaskexec");
|
||
let stages: []str = ["ww", "ww_ww"];
|
||
let tags: []str = ["c", "ww"];
|
||
let linkers: []str = ["w6l", "w6l_ww"];
|
||
let coldref: str = "";
|
||
let changedref: str = "";
|
||
let rawref: str = "";
|
||
let defaultref: str = "";
|
||
let fanalpharef: str = "";
|
||
let fanbetaref: str = "";
|
||
let archiverefs: []str = ["", ""];
|
||
let testref: str = "";
|
||
let faildiagref: str = "";
|
||
let occupieddiagref: str = "";
|
||
let linkerwrapper: str = strings.concat(root, "/mode-w6l.sh");
|
||
writeexecutable(linkerwrapper, strings.concat(
|
||
"#!/bin/sh\n",
|
||
"for arg do case \"$arg\" in */beta.new)\n",
|
||
" printf 'injected build-mode linker failure\\n' >&2\n",
|
||
" exit 97;; esac; done\n",
|
||
"exec \"$WW_MODE_REAL_LINKER\" \"$@\"\n"));
|
||
let baseenv: []str = os.getenvs();
|
||
let si: i32 = 0;
|
||
for (si < stages.len) {
|
||
let work: str = strings.concat(root, "/work-", tags[si]);
|
||
mkdirall(work);
|
||
let binary: str = strings.concat(root, "/command-", tags[si]);
|
||
let coldav: []str = [launcher, "000", driver(stages[si]), "build",
|
||
"-w", work, "-I", source, "-o", binary, alpha];
|
||
let out: commandout;
|
||
runcommand(root, strings.concat("mode-cold-", tags[si]), coldav,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
assert(permissionmode(binary) == 511u32);
|
||
assert(!os.exists(strings.concat(binary, ".new")));
|
||
assert(!os.exists(strings.concat(binary, "-go-tmp-umask")));
|
||
let coldbytes: str = readfile(binary);
|
||
let coldarchive: str = readfile(strings.concat(work, "/alpha.a"));
|
||
if (si == 0) { coldref = strings.dup(coldbytes); }
|
||
else { assert(same(coldref, coldbytes)); };
|
||
let runav: []str = [binary];
|
||
runcommand(root, strings.concat("mode-cold-run-", tags[si]), runav,
|
||
time.second, &out);
|
||
expectexit(&out, 21);
|
||
|
||
// Umask is request-local publication metadata: the warm graph stays
|
||
// byte-stable while replacing the same output with the current mode.
|
||
let warmav: []str = [launcher, "077", driver(stages[si]), "build",
|
||
"-w", work, "-I", source, "-o", binary, alpha];
|
||
runcommand(root, strings.concat("mode-warm-", tags[si]), warmav,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
assert(permissionmode(binary) == 448u32);
|
||
assert(same(coldbytes, readfile(binary)));
|
||
assert(same(coldarchive, readfile(strings.concat(work, "/alpha.a"))));
|
||
assert(!directoryhasnew(work));
|
||
|
||
// Source invalidation changes the command bytes, not the creation rule.
|
||
rewritefile(alphafile, alphachanged);
|
||
let changedav: []str = [launcher, "027", driver(stages[si]), "build",
|
||
"-w", work, "-I", source, "-o", binary, alpha];
|
||
runcommand(root, strings.concat("mode-changed-", tags[si]), changedav,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(permissionmode(binary) == 488u32);
|
||
let changedbytes: str = readfile(binary);
|
||
assert(!same(coldbytes, changedbytes));
|
||
if (si == 0) { changedref = strings.dup(changedbytes); }
|
||
else { assert(same(changedref, changedbytes)); };
|
||
let changedrun: []str = [binary];
|
||
runcommand(root, strings.concat("mode-changed-run-", tags[si]),
|
||
changedrun, time.second, &out);
|
||
expectexit(&out, 22);
|
||
rewritefile(alphafile, alphabase);
|
||
|
||
// A default command output and an explicit raw-file output use the same
|
||
// link-install permission, independent of their naming route.
|
||
let defaultdir: str = strings.concat(root, "/default-", tags[si]);
|
||
let defaultwork: str = strings.concat(root, "/default-work-", tags[si]);
|
||
mkdirall(defaultdir); mkdirall(defaultwork);
|
||
let defaultav: []str = [launcher, "000", driver(stages[si]),
|
||
"build", "-w", defaultwork, "-I", source, alpha];
|
||
runcommanddir(root, strings.concat("mode-default-", tags[si]),
|
||
defaultdir, defaultav,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let defaultbin: str = strings.concat(defaultdir, "/alpha");
|
||
assert(permissionmode(defaultbin) == 511u32);
|
||
let defaultbytes: str = readfile(defaultbin);
|
||
if (si == 0) { defaultref = strings.dup(defaultbytes); }
|
||
else { assert(same(defaultref, defaultbytes)); };
|
||
|
||
let rawbin: str = strings.concat(root, "/raw-", tags[si]);
|
||
let rawav: []str = [launcher, "007", driver(stages[si]), "build",
|
||
"-o", rawbin, raw];
|
||
runcommand(root, strings.concat("mode-raw-", tags[si]), rawav,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(permissionmode(rawbin) == 504u32);
|
||
let rawbytes: str = readfile(rawbin);
|
||
if (si == 0) { rawref = strings.dup(rawbytes); }
|
||
else { assert(same(rawref, rawbytes)); };
|
||
|
||
// Directory fan-out creates each command independently, skips the
|
||
// library product, and leaves no request-private publication residue.
|
||
let fanout: str = strings.concat(root, "/fanout-", tags[si]);
|
||
let fanwork: str = strings.concat(root, "/fanwork-", tags[si]);
|
||
mkdirall(fanout); mkdirall(fanwork);
|
||
let fanav: []str = [launcher, "027", driver(stages[si]), "build",
|
||
"-w", fanwork, "-I", source, "-o", fanout,
|
||
strings.concat(fan, "/...")];
|
||
runcommand(root, strings.concat("mode-fan-", tags[si]), fanav,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
let fanalphabin: str = strings.concat(fanout, "/alpha");
|
||
let fanbetabin: str = strings.concat(fanout, "/beta");
|
||
assert(permissionmode(fanalphabin) == 488u32);
|
||
assert(permissionmode(fanbetabin) == 488u32);
|
||
assert(!os.exists(strings.concat(fanout, "/library")));
|
||
assert(!directoryhasnew(fanout) && !directoryhasnew(fanwork));
|
||
let fanalphabytes: str = readfile(fanalphabin);
|
||
let fanbetabytes: str = readfile(fanbetabin);
|
||
if (si == 0) {
|
||
fanalpharef = strings.dup(fanalphabytes);
|
||
fanbetaref = strings.dup(fanbetabytes);
|
||
} else {
|
||
assert(same(fanalpharef, fanalphabytes));
|
||
assert(same(fanbetaref, fanbetabytes));
|
||
};
|
||
|
||
// The non-link branch of BuildInstallFunc uses 0666. WW's required
|
||
// adjacent interface follows the archive's data-file permission.
|
||
let libwork: str = strings.concat(root, "/libwork-", tags[si]);
|
||
mkdirall(libwork);
|
||
let archive: str = strings.concat(root, "/library-", tags[si], ".a");
|
||
let libav: []str = [launcher, "000", driver(stages[si]), "build",
|
||
"-w", libwork, "-I", source, "-o", archive, library];
|
||
runcommand(root, strings.concat("mode-library-", tags[si]), libav,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(permissionmode(archive) == 438u32);
|
||
assert(permissionmode(strings.concat(archive, ".wwi")) == 438u32);
|
||
archiverefs[si] = strings.dup(readfile(archive));
|
||
let libwarm: []str = [launcher, "077", driver(stages[si]), "build",
|
||
"-w", libwork, "-I", source, "-o", archive, library];
|
||
runcommand(root, strings.concat("mode-library-warm-", tags[si]), libwarm,
|
||
(60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(permissionmode(archive) == 384u32);
|
||
assert(permissionmode(strings.concat(archive, ".wwi")) == 384u32);
|
||
assert(same(archiverefs[si], readfile(archive)));
|
||
assert(!os.exists(strings.concat(archive, ".new")));
|
||
assert(!os.exists(strings.concat(archive, ".wwi.new")));
|
||
|
||
// Test-binary retention is a separate publication branch, but the
|
||
// ordinary linked-executable rule remains identical and byte-stable.
|
||
let testbin: str = strings.concat(root, "/check-", tags[si], ".test");
|
||
let testav: []str = [launcher, "000", driver(stages[si]), "test",
|
||
"-c", "-I", source, "-o", testbin, check];
|
||
runcommand(root, strings.concat("mode-test-", tags[si]), testav,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(permissionmode(testbin) == 511u32);
|
||
let testbytes: str = readfile(testbin);
|
||
if (si == 0) { testref = strings.dup(testbytes); }
|
||
else { assert(same(testref, testbytes)); };
|
||
let testwarm: []str = [launcher, "077", driver(stages[si]), "test",
|
||
"-c", "-I", source, "-o", testbin, check];
|
||
runcommand(root, strings.concat("mode-test-warm-", tags[si]), testwarm,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(permissionmode(testbin) == 448u32);
|
||
assert(same(testbytes, readfile(testbin)));
|
||
assert(!os.exists(strings.concat(testbin, ".new")));
|
||
|
||
// Assembly-only builds do not publish an inode to which the permission
|
||
// contract could apply.
|
||
let asmdir: str = strings.concat(root, "/asm-dir-", tags[si]);
|
||
let asmwork: str = strings.concat(root, "/asm-work-", tags[si]);
|
||
mkdirall(asmdir); mkdirall(asmwork);
|
||
let asmav: []str = [launcher, "000", driver(stages[si]), "build",
|
||
"-S", "-w", asmwork, "-I", source, alpha];
|
||
runcommanddir(root, strings.concat("mode-asm-", tags[si]), asmdir,
|
||
asmav, (60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(!os.exists(strings.concat(asmdir, "/alpha")));
|
||
|
||
// A later link failure rolls back already staged siblings. Existing
|
||
// destination bytes and modes survive; neither transaction nor action
|
||
// stages remain.
|
||
let failout: str = strings.concat(root, "/failout-", tags[si]);
|
||
let failwork: str = strings.concat(root, "/failwork-", tags[si]);
|
||
mkdirall(failout); mkdirall(failwork);
|
||
writefile(strings.concat(failout, "/alpha"), "old-alpha\n");
|
||
writefile(strings.concat(failout, "/beta"), "old-beta\n");
|
||
let failenv: []str = alloc([], (baseenv.len + 2): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < baseenv.len) {
|
||
if (!strings.hasprefix(baseenv[ei], "WW_W6L=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_MODE_REAL_LINKER=")) {
|
||
append(failenv, baseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(failenv, strings.concat("WW_W6L=", linkerwrapper));
|
||
append(failenv, strings.concat("WW_MODE_REAL_LINKER=",
|
||
driver(linkers[si])));
|
||
let failav: []str = [launcher, "000", driver(stages[si]), "build",
|
||
"-w", failwork, "-I", source, "-o", failout,
|
||
strings.concat(fan, "/...")];
|
||
runcommandenv(root, strings.concat("mode-failure-", tags[si]), failav,
|
||
failenv, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, "injected build-mode linker failure\n"));
|
||
assert(has(out.stderr, "ww: w6l failed\n"));
|
||
assert(same(readfile(strings.concat(failout, "/alpha")),
|
||
"old-alpha\n"));
|
||
assert(same(readfile(strings.concat(failout, "/beta")),
|
||
"old-beta\n"));
|
||
assert(permissionmode(strings.concat(failout, "/alpha")) == 384u32);
|
||
assert(permissionmode(strings.concat(failout, "/beta")) == 384u32);
|
||
assert(!directoryhasnew(failout) && !directoryhasnew(failwork));
|
||
let faildiag: str = normalizedtrace(out.stderr,
|
||
strings.concat(failwork, "/"), failout);
|
||
if (si == 0) { faildiagref = strings.dup(faildiag); }
|
||
else { assert(same(faildiagref, faildiag)); };
|
||
|
||
// An occupied caller-visible stage is rejected before production and
|
||
// leaves both the prior output and the occupied inode untouched.
|
||
let occupied: str = strings.concat(root, "/occupied");
|
||
let occupiedstage: str = strings.concat(occupied, ".new");
|
||
if (si == 0) {
|
||
writefile(occupied, "old-output\n");
|
||
writefile(occupiedstage, "occupied-stage\n");
|
||
};
|
||
let occupiedav: []str = [launcher, "000", driver(stages[si]),
|
||
"build", "-I", source, "-o", occupied, alpha];
|
||
runcommand(root, strings.concat("mode-occupied-", tags[si]), occupiedav,
|
||
time.second, &out);
|
||
expectexit(&out, 1);
|
||
assert(same(readfile(occupied), "old-output\n"));
|
||
assert(same(readfile(occupiedstage), "occupied-stage\n"));
|
||
assert(permissionmode(occupied) == 384u32);
|
||
assert(permissionmode(occupiedstage) == 384u32);
|
||
if (si == 0) { occupieddiagref = strings.dup(out.stderr); }
|
||
else { assert(same(occupieddiagref, out.stderr)); };
|
||
si += 1;
|
||
};
|
||
assert(same(archiverefs[0], archiverefs[1]));
|
||
let artifacts: []str = [".unit.ww", ".wwi", ".s", ".o", ".a",
|
||
".init.unit.ww", ".init.s", ".init.o"];
|
||
let ai: i32 = 0;
|
||
for (ai < artifacts.len) {
|
||
assert(same(readfile(strings.concat(root, "/work-c/alpha",
|
||
artifacts[ai])), readfile(strings.concat(root,
|
||
"/work-ww/alpha", artifacts[ai]))));
|
||
ai += 1;
|
||
};
|
||
|
||
// Two independent drivers execute simultaneously with different umasks.
|
||
// Their process-local masks cannot leak across stages, works, or outputs.
|
||
let parallelcwork: str = strings.concat(root, "/parallel-c-work");
|
||
let parallelwwwork: str = strings.concat(root, "/parallel-ww-work");
|
||
mkdirall(parallelcwork); mkdirall(parallelwwwork);
|
||
let parallelcbin: str = strings.concat(root, "/parallel-c");
|
||
let parallelwwbin: str = strings.concat(root, "/parallel-ww");
|
||
let cav: []str = [launcher, "002", driver("ww"), "build", "-w",
|
||
parallelcwork, "-I", source, "-o", parallelcbin, fanalpha];
|
||
let wav: []str = [launcher, "077", driver("ww_ww"), "build", "-w",
|
||
parallelwwwork, "-I", source, "-o", parallelwwbin, fanbeta];
|
||
let cc: exec.command;
|
||
cc.path = launcher; cc.argv = cav; cc.env = os.getenvs(); cc.dir = repo();
|
||
cc.stdoutpath = strings.concat(root, "/parallel-c.stdout");
|
||
cc.stderrpath = strings.concat(root, "/parallel-c.stderr");
|
||
cc.deadline = time.add(time.now(time.clock.monotonic),
|
||
(120i64 * (time.second: i64)): time.duration);
|
||
cc.grace = (100i64 * (time.millisecond: i64)): time.duration;
|
||
let wc: exec.command;
|
||
wc.path = launcher; wc.argv = wav; wc.env = os.getenvs(); wc.dir = repo();
|
||
wc.stdoutpath = strings.concat(root, "/parallel-ww.stdout");
|
||
wc.stderrpath = strings.concat(root, "/parallel-ww.stderr");
|
||
wc.deadline = time.add(time.now(time.clock.monotonic),
|
||
(120i64 * (time.second: i64)): time.duration);
|
||
wc.grace = (100i64 * (time.millisecond: i64)): time.duration;
|
||
let cp: exec.process;
|
||
let wp: exec.process;
|
||
exec.start(&cp, &cc); exec.start(&wp, &wc);
|
||
let cdone: bool = false;
|
||
let wdone: bool = false;
|
||
for (!cdone || !wdone) {
|
||
if (!cdone) { cdone = exec.poll(&cp); };
|
||
if (!wdone) { wdone = exec.poll(&wp); };
|
||
if (!cdone || !wdone) {
|
||
time.sleep(time.millisecond, time.clock.monotonic);
|
||
};
|
||
};
|
||
assert(cp.result.errno == 0 && cp.result.cleanuperrno == 0);
|
||
assert(wp.result.errno == 0 && wp.result.cleanuperrno == 0);
|
||
assert(cp.result.termination == exec.termination.EXIT && cp.result.code == 0);
|
||
assert(wp.result.termination == exec.termination.EXIT && wp.result.code == 0);
|
||
assert(readfile(cc.stdoutpath).len == 0 && readfile(cc.stderrpath).len == 0);
|
||
assert(readfile(wc.stdoutpath).len == 0 && readfile(wc.stderrpath).len == 0);
|
||
assert(permissionmode(parallelcbin) == 509u32);
|
||
assert(permissionmode(parallelwwbin) == 448u32);
|
||
assert(!directoryhasnew(parallelcwork));
|
||
assert(!directoryhasnew(parallelwwwork));
|
||
clean(root);
|
||
};
|
||
|
||
@test fn exact_null_output_discards_build_products() void = {
|
||
let root: str = fresh();
|
||
let source: str = strings.concat(root, "/source");
|
||
let base: str = strings.concat(source, "/base");
|
||
let alpha: str = strings.concat(source, "/alpha");
|
||
let beta: str = strings.concat(source, "/beta");
|
||
let library: str = strings.concat(source, "/library");
|
||
let bad: str = strings.concat(source, "/bad");
|
||
let absent: str = strings.concat(source, "/absent");
|
||
mkdirall(base); mkdirall(alpha); mkdirall(beta);
|
||
mkdirall(library); mkdirall(bad); mkdirall(absent);
|
||
writefile(strings.concat(base, "/base.ww"), strings.concat(
|
||
"package base;\n",
|
||
"export fn value() i32 = { return 4; };\n"));
|
||
let alphafile: str = strings.concat(alpha, "/main.ww");
|
||
let alphaoriginal: str = strings.concat(
|
||
"package main;\nimport base;\n",
|
||
"fn main() i32 = { return base.value(); };\n");
|
||
let alphachanged: str = strings.concat(
|
||
"package main;\nimport base;\n",
|
||
"fn main() i32 = { return base.value() + 1; };\n");
|
||
let alphafailing: str = strings.concat(
|
||
"package main;\nimport base;\n",
|
||
"fn main() i32 = { return base.value() + 2; };\n");
|
||
writefile(alphafile, alphaoriginal);
|
||
writefile(strings.concat(beta, "/main.ww"),
|
||
"package main;\nfn main() i32 = { return 6; };\n");
|
||
writefile(strings.concat(library, "/library.ww"), strings.concat(
|
||
"package odd;\n",
|
||
"export fn value() i32 = { return 7; };\n"));
|
||
writefile(strings.concat(bad, "/main.ww"), strings.concat(
|
||
"package main;\nimport _ missing.pkg;\n",
|
||
"fn main() i32 = { return 0; };\n"));
|
||
let raw: str = strings.concat(source, "/raw.ww");
|
||
writefile(raw, "package main;\nfn main() i32 = { return 9; };\n");
|
||
|
||
let compilerwrapper: str = strings.concat(root, "/null-w6c.sh");
|
||
let assemblerwrapper: str = strings.concat(root, "/null-w6a.sh");
|
||
let linkerwrapper: str = strings.concat(root, "/null-w6l.sh");
|
||
writeexecutable(compilerwrapper, strings.concat(
|
||
"#!/bin/sh\n",
|
||
"printf '%s\\n' \"$*\" >> \"$WW_NULL_CTRACE\"\n",
|
||
"exec \"$WW_NULL_REAL_C\" \"$@\"\n"));
|
||
writeexecutable(assemblerwrapper, strings.concat(
|
||
"#!/bin/sh\n",
|
||
"printf '%s\\n' \"$*\" >> \"$WW_NULL_ATRACE\"\n",
|
||
"exec \"$WW_NULL_REAL_A\" \"$@\"\n"));
|
||
writeexecutable(linkerwrapper, strings.concat(
|
||
"#!/bin/sh\n",
|
||
"printf '%s\\n' \"$*\" >> \"$WW_NULL_LTRACE\"\n",
|
||
"out= take=\n",
|
||
"for arg do\n",
|
||
" if test \"$take\" = yes; then out=$arg; take=; continue; fi\n",
|
||
" if test \"$arg\" = -o; then take=yes; fi\n",
|
||
"done\n",
|
||
"printf '%s' \"$out\" > \"$WW_NULL_LAST_OUTPUT\"\n",
|
||
"case \"$WW_NULL_LINK_MODE\" in\n",
|
||
" fail) printf 'injected null-output linker failure\\n' >&2; exit 97;;\n",
|
||
" signal) kill -TERM \"$$\";;\n",
|
||
"esac\n",
|
||
"\"$WW_NULL_REAL_L\" \"$@\"\n",
|
||
"rc=$?\n",
|
||
"if test \"$rc\" -eq 0 && test -n \"$WW_NULL_CAPTURE\"; then\n",
|
||
" cp \"$out\" \"$WW_NULL_CAPTURE\" || exit 98\n",
|
||
"fi\n",
|
||
"exit \"$rc\"\n"));
|
||
|
||
let nullbefore: os.filestat;
|
||
match (os.stat(&nullbefore, "/dev/null")) {
|
||
case void => void;
|
||
case let e: os.oserror => abort("cannot stat /dev/null");
|
||
};
|
||
assert(((nullbefore.mode as u32) & 61440u32)
|
||
== (os.mode.CHR as u32));
|
||
let stages: []str = ["ww", "ww_ww"];
|
||
let compilers: []str = ["w6c", "w6c_ww"];
|
||
let assemblers: []str = ["w6a", "w6a_ww"];
|
||
let linkers: []str = ["w6l", "w6l_ww"];
|
||
let tags: []str = ["c", "ww"];
|
||
let coldref: str = "";
|
||
let changedref: str = "";
|
||
let rawref: str = "";
|
||
let lookalikeref: str = "";
|
||
let faildiagref: str = "";
|
||
let signaldiagref: str = "";
|
||
let missingdiagref: str = "";
|
||
let emptydiagref: str = "";
|
||
let rejectdiagref: str = "";
|
||
let testcompileref: str = "";
|
||
let testoutrefs: []str = ["", "", ""];
|
||
let testerrrefs: []str = ["", "", ""];
|
||
let baseenv: []str = os.getenvs();
|
||
let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"];
|
||
let out: commandout;
|
||
let si: i32 = 0;
|
||
for (si < stages.len) {
|
||
rewritefile(alphafile, alphaoriginal);
|
||
let work: str = strings.concat(root, "/work-", tags[si]);
|
||
mkdirall(work);
|
||
let ctrace: str = strings.concat(root, "/", tags[si], "-c.trace");
|
||
let atrace: str = strings.concat(root, "/", tags[si], "-a.trace");
|
||
let ltrace: str = strings.concat(root, "/", tags[si], "-l.trace");
|
||
let lastout: str = strings.concat(root, "/", tags[si], "-last.out");
|
||
let capture: str = strings.concat(root, "/", tags[si], "-capture");
|
||
writefile(ctrace, ""); writefile(atrace, "");
|
||
writefile(ltrace, ""); writefile(lastout, "");
|
||
let env: []str = alloc([], (baseenv.len + 12): u64)!;
|
||
let ei: i32 = 0;
|
||
for (ei < baseenv.len) {
|
||
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6A=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_W6L=")
|
||
&& !strings.hasprefix(baseenv[ei], "WW_NULL_")) {
|
||
append(env, baseenv[ei]);
|
||
};
|
||
ei += 1;
|
||
};
|
||
append(env, strings.concat("WW_W6C=", compilerwrapper));
|
||
append(env, strings.concat("WW_W6A=", assemblerwrapper));
|
||
append(env, strings.concat("WW_W6L=", linkerwrapper));
|
||
append(env, strings.concat("WW_NULL_CTRACE=", ctrace));
|
||
append(env, strings.concat("WW_NULL_ATRACE=", atrace));
|
||
append(env, strings.concat("WW_NULL_LTRACE=", ltrace));
|
||
append(env, strings.concat("WW_NULL_REAL_C=",
|
||
driver(compilers[si])));
|
||
append(env, strings.concat("WW_NULL_REAL_A=",
|
||
driver(assemblers[si])));
|
||
append(env, strings.concat("WW_NULL_REAL_L=",
|
||
driver(linkers[si])));
|
||
let linkmodeindex: i32 = env.len;
|
||
append(env, "WW_NULL_LINK_MODE=");
|
||
let captureindex: i32 = env.len;
|
||
append(env, strings.concat("WW_NULL_CAPTURE=", capture));
|
||
append(env, strings.concat("WW_NULL_LAST_OUTPUT=", lastout));
|
||
|
||
// Exact null is no-install policy. Loading, dependency production,
|
||
// assembly, archiving, and the command link still occur normally.
|
||
let coldav: []str = [driver(stages[si]), "build", "-w", work,
|
||
"-I", source, "-o", "/dev/null", alpha];
|
||
runcommandenvdir(root, strings.concat("null-cold-", tags[si]), coldav,
|
||
env, root, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
assert(occurrences(readfile(ctrace), "\n") == 2);
|
||
assert(occurrences(readfile(atrace), "\n") == 3);
|
||
assert(occurrences(readfile(ltrace), "\n") == 1);
|
||
assert(has(readfile(ctrace), "/base.unit.new"));
|
||
assert(has(readfile(ctrace), "/alpha.unit.new"));
|
||
discardedlinkcleaned(lastout);
|
||
let coldbytes: str = readfile(capture);
|
||
if (si == 0) { coldref = strings.dup(coldbytes); }
|
||
else { assert(same(coldref, coldbytes)); };
|
||
let runav: []str = [capture];
|
||
runcommand(root, strings.concat("null-cold-run-", tags[si]), runav,
|
||
time.second, &out);
|
||
expectexit(&out, 4);
|
||
let originalunit: str = readfile(strings.concat(work,
|
||
"/alpha.unit.ww"));
|
||
|
||
// Unchanged actions are warm-reused, but the command links again into
|
||
// fresh private storage because null output is not a result cache.
|
||
rewritefile(ctrace, ""); rewritefile(atrace, "");
|
||
rewritefile(ltrace, ""); rewritefile(lastout, "");
|
||
rewritefile(capture, "");
|
||
runcommandenvdir(root, strings.concat("null-warm-", tags[si]), coldav,
|
||
env, root, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0);
|
||
assert(occurrences(readfile(ltrace), "\n") == 1);
|
||
assert(same(coldbytes, readfile(capture)));
|
||
discardedlinkcleaned(lastout);
|
||
|
||
// Source change invalidates the owning action and linked bytes while
|
||
// preserving the same action names and persistent representation.
|
||
rewritefile(alphafile, alphachanged);
|
||
rewritefile(ctrace, ""); rewritefile(atrace, "");
|
||
rewritefile(ltrace, ""); rewritefile(lastout, "");
|
||
rewritefile(capture, "");
|
||
runcommandenvdir(root, strings.concat("null-change-", tags[si]), coldav,
|
||
env, root, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(occurrences(readfile(ctrace), "\n") == 1);
|
||
assert(occurrences(readfile(atrace), "\n") == 1);
|
||
assert(occurrences(readfile(ltrace), "\n") == 1);
|
||
assert(!same(originalunit, readfile(strings.concat(work,
|
||
"/alpha.unit.ww"))));
|
||
let changedbytes: str = readfile(capture);
|
||
assert(!same(coldbytes, changedbytes));
|
||
if (si == 0) { changedref = strings.dup(changedbytes); }
|
||
else { assert(same(changedref, changedbytes)); };
|
||
discardedlinkcleaned(lastout);
|
||
let kept: []str = alloc([], suffixes.len: u64)!;
|
||
let xi: i32 = 0;
|
||
for (xi < suffixes.len) {
|
||
append(kept, strings.dup(readfile(strings.concat(work,
|
||
"/alpha", suffixes[xi]))));
|
||
xi += 1;
|
||
};
|
||
|
||
// A late producer failure and a producer signal cannot publish and
|
||
// cannot commit an invalidated persistent generation.
|
||
rewritefile(alphafile, alphafailing);
|
||
rewritefile(ctrace, ""); rewritefile(atrace, "");
|
||
rewritefile(ltrace, ""); rewritefile(lastout, "");
|
||
env[linkmodeindex] = "WW_NULL_LINK_MODE=fail";
|
||
runcommandenvdir(root, strings.concat("null-fail-", tags[si]), coldav,
|
||
env, root, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, "injected null-output linker failure\n"));
|
||
assert(has(out.stderr, "ww: w6l failed\n"));
|
||
assert(occurrences(readfile(ltrace), "\n") == 1);
|
||
discardedlinkcleaned(lastout);
|
||
xi = 0;
|
||
for (xi < suffixes.len) {
|
||
assert(same(kept[xi], readfile(strings.concat(work,
|
||
"/alpha", suffixes[xi]))));
|
||
xi += 1;
|
||
};
|
||
assert(!directoryhasnew(work));
|
||
if (si == 0) { faildiagref = strings.dup(out.stderr); }
|
||
else { assert(same(faildiagref, out.stderr)); };
|
||
|
||
rewritefile(ctrace, ""); rewritefile(atrace, "");
|
||
rewritefile(ltrace, ""); rewritefile(lastout, "");
|
||
env[linkmodeindex] = "WW_NULL_LINK_MODE=signal";
|
||
runcommandenvdir(root, strings.concat("null-signal-", tags[si]), coldav,
|
||
env, root, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, "ww: w6l failed\n"));
|
||
discardedlinkcleaned(lastout);
|
||
xi = 0;
|
||
for (xi < suffixes.len) {
|
||
assert(same(kept[xi], readfile(strings.concat(work,
|
||
"/alpha", suffixes[xi]))));
|
||
xi += 1;
|
||
};
|
||
assert(!directoryhasnew(work));
|
||
if (si == 0) { signaldiagref = strings.dup(out.stderr); }
|
||
else { assert(same(signaldiagref, out.stderr)); };
|
||
env[linkmodeindex] = "WW_NULL_LINK_MODE=";
|
||
|
||
// Import loading and empty-pattern handling precede output planning.
|
||
// The exact null exception changes neither diagnostic nor tool order.
|
||
rewritefile(ctrace, ""); rewritefile(atrace, "");
|
||
rewritefile(ltrace, ""); rewritefile(lastout, "");
|
||
let badav: []str = [driver(stages[si]), "build", "-I", source,
|
||
"-o", "/dev/null", bad];
|
||
runcommandenvdir(root, strings.concat("null-import-", tags[si]), badav,
|
||
env, root, (60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 1);
|
||
assert(has(out.stderr, "cannot find package missing.pkg\n"));
|
||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0
|
||
&& readfile(ltrace).len == 0 && readfile(lastout).len == 0);
|
||
if (si == 0) { missingdiagref = strings.dup(out.stderr); }
|
||
else { assert(same(missingdiagref, out.stderr)); };
|
||
|
||
let emptyroot: str = strings.concat(source, "/absent/...");
|
||
let emptyav: []str = [driver(stages[si]), "build", "-o",
|
||
"/dev/null", emptyroot];
|
||
runcommandenvdir(root, strings.concat("null-empty-", tags[si]), emptyav,
|
||
env, root, time.second, &out);
|
||
expectexit(&out, 0);
|
||
assert(has(out.stderr, "matched no packages\n"));
|
||
assert(readfile(ctrace).len == 0 && readfile(ltrace).len == 0);
|
||
if (si == 0) { emptydiagref = strings.dup(out.stderr); }
|
||
else { assert(same(emptydiagref, out.stderr)); };
|
||
|
||
// One discard request may contain commands and a non-main library.
|
||
// Every action runs, only commands link, and no basename is published.
|
||
rewritefile(alphafile, alphaoriginal);
|
||
rewritefile(ctrace, ""); rewritefile(atrace, "");
|
||
rewritefile(ltrace, ""); rewritefile(lastout, "");
|
||
let multiav: []str = [driver(stages[si]), "build", "-I", source,
|
||
"-o", "/dev/null", alpha, beta, library];
|
||
runcommandenvdir(root, strings.concat("null-multi-", tags[si]), multiav,
|
||
env, root, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
assert(occurrences(readfile(ctrace), "\n") == 4);
|
||
assert(occurrences(readfile(atrace), "\n") == 6);
|
||
assert(occurrences(readfile(ltrace), "\n") == 2);
|
||
assert(!os.exists(strings.concat(root, "/alpha")));
|
||
assert(!os.exists(strings.concat(root, "/beta")));
|
||
assert(!os.exists(strings.concat(root, "/library")));
|
||
assert(!os.exists("/dev/null.new"));
|
||
assert(!os.exists("/dev/null.sepwork"));
|
||
|
||
// Assembly-only keeps the same loading/action exception, has no link,
|
||
// and needs no persistent workdir solely to make discarded files visible.
|
||
rewritefile(ctrace, ""); rewritefile(atrace, "");
|
||
rewritefile(ltrace, ""); rewritefile(lastout, "");
|
||
let asmav: []str = [driver(stages[si]), "build", "-S", "-I", source,
|
||
"-o", "/dev/null", alpha, beta, library];
|
||
runcommandenvdir(root, strings.concat("null-asm-", tags[si]), asmav,
|
||
env, root, (120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(occurrences(readfile(ctrace), "\n") == 4);
|
||
assert(readfile(atrace).len == 0 && readfile(ltrace).len == 0);
|
||
|
||
// The direct raw-file route links privately too. A wrapper capture proves
|
||
// the linked bytes are runnable before the driver's cleanup.
|
||
rewritefile(ctrace, ""); rewritefile(atrace, "");
|
||
rewritefile(ltrace, ""); rewritefile(lastout, "");
|
||
rewritefile(capture, "");
|
||
let rawav: []str = [driver(stages[si]), "build", "-o",
|
||
"/dev/null", raw];
|
||
runcommandenvdir(root, strings.concat("null-raw-", tags[si]), rawav,
|
||
env, root, (60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(occurrences(readfile(ltrace), "\n") == 1);
|
||
discardedlinkcleaned(lastout);
|
||
let rawbytes: str = readfile(capture);
|
||
if (si == 0) { rawref = strings.dup(rawbytes); }
|
||
else { assert(same(rawref, rawbytes)); };
|
||
let rawrun: []str = [capture];
|
||
runcommand(root, strings.concat("null-raw-run-", tags[si]), rawrun,
|
||
time.second, &out);
|
||
expectexit(&out, 9);
|
||
|
||
// Null recognition is exact. A regular lookalike is retained with its
|
||
// established adjacent scratch; a multi-root lookalike still rejects.
|
||
let lookalike: str = strings.concat(root, "/null-like-", tags[si]);
|
||
rewritefile(ctrace, ""); rewritefile(atrace, "");
|
||
rewritefile(ltrace, ""); rewritefile(lastout, "");
|
||
env[captureindex] = "WW_NULL_CAPTURE=";
|
||
let lookav: []str = [driver(stages[si]), "build", "-o",
|
||
lookalike, raw];
|
||
runcommandenvdir(root, strings.concat("null-look-", tags[si]), lookav,
|
||
env, root, (60i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(os.exists(lookalike));
|
||
assert(os.exists(strings.concat(lookalike, ".sepwork")));
|
||
let lookbytes: str = readfile(lookalike);
|
||
if (si == 0) { lookalikeref = strings.dup(lookbytes); }
|
||
else { assert(same(lookalikeref, lookbytes)); };
|
||
let rejectout: str = strings.concat(root, "/multi-like-", tags[si]);
|
||
rewritefile(ctrace, ""); rewritefile(atrace, "");
|
||
rewritefile(ltrace, "");
|
||
let rejectav: []str = [driver(stages[si]), "build", "-I", source,
|
||
"-o", rejectout, alpha, beta];
|
||
runcommandenvdir(root, strings.concat("null-reject-", tags[si]),
|
||
rejectav, env, root, time.second, &out);
|
||
expectexit(&out, 2);
|
||
assert(has(out.stderr, "cannot use -o with multiple packages\n"));
|
||
assert(readfile(ctrace).len == 0 && readfile(atrace).len == 0
|
||
&& readfile(ltrace).len == 0);
|
||
if (si == 0) { rejectdiagref = strings.dup(out.stderr); }
|
||
else { assert(same(rejectdiagref, out.stderr)); };
|
||
env[captureindex] = strings.concat("WW_NULL_CAPTURE=", capture);
|
||
|
||
// Raw test compilation has the same private-output rule. Running forms
|
||
// preserve ordinary success, assertion, and signal outcomes.
|
||
let compileav: []str = [driver(stages[si]), "test", "-c", "-o",
|
||
"/dev/null", runtimepath("success_test.ww")];
|
||
rewritefile(ctrace, ""); rewritefile(atrace, "");
|
||
rewritefile(ltrace, ""); rewritefile(lastout, "");
|
||
rewritefile(capture, "");
|
||
runcommandenvdir(root, strings.concat("null-test-c-", tags[si]),
|
||
compileav, env, root,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, 0);
|
||
assert(out.stdout.len == 0 && out.stderr.len == 0);
|
||
assert(occurrences(readfile(ltrace), "\n") == 1);
|
||
discardedlinkcleaned(lastout);
|
||
let testbytes: str = readfile(capture);
|
||
if (si == 0) { testcompileref = strings.dup(testbytes); }
|
||
else { assert(same(testcompileref, testbytes)); };
|
||
|
||
let runtargets: []str = ["success_test.ww", "fail_test.ww",
|
||
"signal_test.ww"];
|
||
let exitcodes: []i32 = [0, 1, 1];
|
||
let needles: []str = ["alpha_pass ... ok\n",
|
||
"assertion_failure ... FAIL (exit 1)\n",
|
||
"signal_is_not_exit ... FAIL (signal 15)\n"];
|
||
let ri: i32 = 0;
|
||
for (ri < runtargets.len) {
|
||
let testav: []str = [driver(stages[si]), "test"];
|
||
append(testav, "-o"); append(testav, "/dev/null");
|
||
append(testav, runtimepath(runtargets[ri]));
|
||
rewritefile(ctrace, ""); rewritefile(atrace, "");
|
||
rewritefile(ltrace, ""); rewritefile(lastout, "");
|
||
runcommandenvdir(root, strings.concat("null-test-run-", tags[si],
|
||
"-", boundarypkgname(ri)), testav, env, root,
|
||
(120i64 * (time.second: i64)): time.duration, &out);
|
||
expectexit(&out, exitcodes[ri]);
|
||
assert(has(out.stdout, needles[ri]));
|
||
assert(occurrences(readfile(ltrace), "\n") == 1);
|
||
discardedlinkcleaned(lastout);
|
||
if (si == 0) {
|
||
testoutrefs[ri] = strings.dup(out.stdout);
|
||
testerrrefs[ri] = strings.dup(out.stderr);
|
||
} else {
|
||
assert(same(testoutrefs[ri], out.stdout));
|
||
assert(same(testerrrefs[ri], out.stderr));
|
||
};
|
||
ri += 1;
|
||
};
|
||
assert(!os.exists("/dev/null.new"));
|
||
assert(!os.exists("/dev/null.sepwork"));
|
||
si += 1;
|
||
};
|
||
|
||
// Persisted semantic artifacts remain byte-identical across stages. Output
|
||
// disposition is request metadata and does not enter these identities.
|
||
let actions: []str = ["base", "alpha"];
|
||
let ai: i32 = 0;
|
||
for (ai < actions.len) {
|
||
let xi: i32 = 0;
|
||
for (xi < suffixes.len) {
|
||
assert(same(readfile(strings.concat(root, "/work-c/",
|
||
actions[ai], suffixes[xi])), readfile(strings.concat(root,
|
||
"/work-ww/", actions[ai], suffixes[xi]))));
|
||
xi += 1;
|
||
};
|
||
ai += 1;
|
||
};
|
||
|
||
// Independent Cstage and WWstage discard requests can execute together.
|
||
// Their private products and persistent generations remain isolated.
|
||
let pcwork: str = strings.concat(root, "/parallel-c-work");
|
||
let pwwork: str = strings.concat(root, "/parallel-ww-work");
|
||
mkdirall(pcwork); mkdirall(pwwork);
|
||
let pcav: []str = [driver("ww"), "build", "-w", pcwork,
|
||
"-I", source, "-o", "/dev/null", alpha, beta, library];
|
||
let pwav: []str = [driver("ww_ww"), "build", "-w", pwwork,
|
||
"-I", source, "-o", "/dev/null", alpha, beta, library];
|
||
let pc: exec.command;
|
||
pc.path = pcav[0]; pc.argv = pcav; pc.env = os.getenvs(); pc.dir = root;
|
||
pc.stdoutpath = strings.concat(root, "/parallel-c.stdout");
|
||
pc.stderrpath = strings.concat(root, "/parallel-c.stderr");
|
||
pc.deadline = time.add(time.now(time.clock.monotonic),
|
||
(120i64 * (time.second: i64)): time.duration);
|
||
pc.grace = (100i64 * (time.millisecond: i64)): time.duration;
|
||
let pw: exec.command;
|
||
pw.path = pwav[0]; pw.argv = pwav; pw.env = os.getenvs(); pw.dir = root;
|
||
pw.stdoutpath = strings.concat(root, "/parallel-ww.stdout");
|
||
pw.stderrpath = strings.concat(root, "/parallel-ww.stderr");
|
||
pw.deadline = time.add(time.now(time.clock.monotonic),
|
||
(120i64 * (time.second: i64)): time.duration);
|
||
pw.grace = (100i64 * (time.millisecond: i64)): time.duration;
|
||
let pcp: exec.process;
|
||
let pwp: exec.process;
|
||
exec.start(&pcp, &pc); exec.start(&pwp, &pw);
|
||
let pcdone: bool = false;
|
||
let pwdone: bool = false;
|
||
for (!pcdone || !pwdone) {
|
||
if (!pcdone) { pcdone = exec.poll(&pcp); };
|
||
if (!pwdone) { pwdone = exec.poll(&pwp); };
|
||
if (!pcdone || !pwdone) {
|
||
time.sleep(time.millisecond, time.clock.monotonic);
|
||
};
|
||
};
|
||
assert(pcp.result.errno == 0 && pcp.result.cleanuperrno == 0);
|
||
assert(pwp.result.errno == 0 && pwp.result.cleanuperrno == 0);
|
||
assert(pcp.result.termination == exec.termination.EXIT
|
||
&& pcp.result.code == 0);
|
||
assert(pwp.result.termination == exec.termination.EXIT
|
||
&& pwp.result.code == 0);
|
||
assert(readfile(pc.stdoutpath).len == 0 && readfile(pc.stderrpath).len == 0);
|
||
assert(readfile(pw.stdoutpath).len == 0 && readfile(pw.stderrpath).len == 0);
|
||
assert(!directoryhasnew(pcwork) && !directoryhasnew(pwwork));
|
||
ai = 0;
|
||
let parallel: []str = ["base", "alpha", "beta", "library"];
|
||
for (ai < parallel.len) {
|
||
let xi: i32 = 0;
|
||
for (xi < suffixes.len) {
|
||
assert(same(readfile(strings.concat(pcwork, "/", parallel[ai],
|
||
suffixes[xi])), readfile(strings.concat(pwwork, "/",
|
||
parallel[ai], suffixes[xi]))));
|
||
xi += 1;
|
||
};
|
||
ai += 1;
|
||
};
|
||
|
||
let nullafter: os.filestat;
|
||
match (os.stat(&nullafter, "/dev/null")) {
|
||
case void => void;
|
||
case let e: os.oserror => abort("cannot restat /dev/null");
|
||
};
|
||
assert((nullafter.mode as u32) == (nullbefore.mode as u32));
|
||
assert(nullafter.inode == nullbefore.inode);
|
||
assert(nullafter.sz == nullbefore.sz);
|
||
assert(!os.exists("/dev/null.new"));
|
||
assert(!os.exists("/dev/null.sepwork"));
|
||
clean(root);
|
||
};
|