test: prove case-fold collision preflight

This commit is contained in:
2026-08-15 16:11:01 +09:00
parent 265be3f796
commit 6974bec878
4 changed files with 1037 additions and 13 deletions

View File

@@ -481,6 +481,36 @@ fn directoryhasnew(path: str) bool = {
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 byteshasat(s: str, off: i32, want: str) bool = {
if (off < 0 || off + want.len > s.len) { return false; };
let i: i32 = 0;
@@ -5749,6 +5779,23 @@ fn hexbytes(value: str) str = {
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);
@@ -5758,6 +5805,11 @@ fn hexbytes(value: str) str = {
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(
@@ -5798,6 +5850,49 @@ fn hexbytes(value: str) str = {
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:");
@@ -5808,32 +5903,46 @@ fn hexbytes(value: str) str = {
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\\n' >> \"$WW_IDENTITY_TRACE\"\n",
"#!/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\\n' >> \"$WW_IDENTITY_TRACE\"\n",
"#!/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 + 5): u64)!;
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));
append(traceenv, strings.concat("WW_IDENTITY_W6C=", driver("w6c")));
append(traceenv, strings.concat("WW_IDENTITY_W6L=", driver("w6l")));
let out: commandout;
let i: i32 = 0;
let identitydiagnostics: []str = ["", ""];
@@ -5857,6 +5966,187 @@ fn hexbytes(value: str) str = {
// 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(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(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(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) {
@@ -5889,9 +6179,25 @@ fn hexbytes(value: str) str = {
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");
@@ -5949,6 +6255,12 @@ fn hexbytes(value: str) str = {
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"));
@@ -6004,6 +6316,76 @@ fn hexbytes(value: str) str = {
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, "/.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");
@@ -6061,6 +6443,12 @@ fn hexbytes(value: str) 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) {
@@ -6513,6 +6901,267 @@ fn hexbytes(value: str) str = {
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 \".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);
let creationfullwant: str = creationwant;
if (ci != 0) {
creationfullwant = strings.concat(creationwant, "FAIL ",
foldcreation, " [main] (build exit 1)\n");
};
assert(same(out.stderr, creationfullwant));
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(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(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);
@@ -8508,6 +9157,26 @@ fn runtimepath(relative: str) str = {
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,
@@ -8523,6 +9192,9 @@ fn runtimepath(relative: str) str = {
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; };
@@ -8649,6 +9321,35 @@ fn runtimepath(relative: str) str = {
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");
@@ -8711,6 +9412,8 @@ fn runtimepath(relative: str) str = {
let referencealiasdirectdiag: str = "";
let referenceprefixdiag: str = "";
let candidate_diag_refs: []str = ["", ""];
let foldvendorrefs: []str = ["", "", ""];
let foldvendormultiref: str = "";
let supplementalrefs: []str = ["", "", "", "", "", "", "", "",
""];
let referencevendortestout: str = "";
@@ -8764,9 +9467,112 @@ fn runtimepath(relative: str) str = {
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];
let out: commandout;
runcommandenv(root, strings.concat("vendor-cold-", stages[si]), av,
env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);