test: cover shared directory test build plans
This commit is contained in:
@@ -2849,11 +2849,16 @@ has one test variant; the coordinator rejects one output name for a multi-varian
|
|||||||
or recursive request. Explicit `ww test FILE` retains its compatibility path.
|
or recursive request. Explicit `ww test FILE` retains its compatibility path.
|
||||||
|
|
||||||
The test coordinator still discovers requested directories, enumerates the
|
The test coordinator still discovers requested directories, enumerates the
|
||||||
test package names, schedules independent binaries, executes them, and emits
|
test package names, selects variants, executes independent binaries, and emits
|
||||||
captured results in byte-sorted package order. It no longer concatenates a
|
captured results in byte-sorted package order. It no longer concatenates a
|
||||||
generated production/test root or resolves imports. Instead it asks the Cstage
|
generated production/test root, resolves imports, or starts one package graph
|
||||||
or WWstage command to build one of two non-importable root variants from the
|
per binary. Instead it sends one ordered build request per directory to the
|
||||||
real directory:
|
Cstage or WWstage command. Its semantic selections are only the directory and
|
||||||
|
the selected variant/package identities; it also carries output destinations,
|
||||||
|
coordinator-private completion paths, import search roots, and the optional
|
||||||
|
shared work-directory policy. The command owns source selection,
|
||||||
|
package loading, compiler inputs, archive construction, and linking for both
|
||||||
|
non-importable roots:
|
||||||
|
|
||||||
- `same-test` selects the byte-sorted production files followed by the
|
- `same-test` selects the byte-sorted production files followed by the
|
||||||
byte-sorted matching `package p` test files. They form one compiler unit, so
|
byte-sorted matching `package p` test files. They form one compiler unit, so
|
||||||
@@ -2863,32 +2868,55 @@ real directory:
|
|||||||
directory. That node selects every production file, emits `p.wwi` and `p.a`,
|
directory. That node selects every production file, emits `p.wwi` and `p.a`,
|
||||||
and exposes no private declaration to the external root.
|
and exposes no private declaration to the external root.
|
||||||
|
|
||||||
|
The command loads those roots into one command-scoped package universe. The
|
||||||
|
roots retain distinct artifact keys (`__ww-test-same` and
|
||||||
|
`__ww-test-external`) that cannot collide with a legal import identity,
|
||||||
|
while imports of the same canonical production directory intern to one
|
||||||
|
production node. A deterministic dependency-first traversal of the union
|
||||||
|
therefore invokes the compiler and archiver once for every reachable canonical
|
||||||
|
production package, even when both test products need it. Each root is still
|
||||||
|
compiled once with its own selected sources and linked separately. The shared
|
||||||
|
plan is deliberately package-test-specific: it is not a generalized scheduler,
|
||||||
|
action schema, cache, or protocol.
|
||||||
|
|
||||||
|
A production action failure blocks exactly the roots that reach it. A
|
||||||
|
root-local compile or link failure does not suppress a successfully built
|
||||||
|
sibling product: the command records completion per product, and the
|
||||||
|
coordinator can run and report successful siblings while attributing each
|
||||||
|
missing product as a build failure. Directory builds and completed test
|
||||||
|
products share the coordinator's existing `-j` process bound; captured output
|
||||||
|
is still emitted only in byte-sorted directory/package order.
|
||||||
|
|
||||||
Every non-root dependency is always a production variant, so dependency
|
Every non-root dependency is always a production variant, so dependency
|
||||||
`*_test.ww` files never enter the graph. Imports that occur only in selected
|
`*_test.ww` files never enter the graph. Imports that occur only in selected
|
||||||
test files add edges only to that test root. The compiler unit for each package
|
test files add edges only to that test root. The compiler unit for each package
|
||||||
contains only its byte-sorted direct dependency `.wwi` artifacts; the final
|
contains only its byte-sorted direct dependency `.wwi` artifacts; the final
|
||||||
test link still receives the root object and the complete reverse-topological
|
test link still receives the root object and the complete reverse-topological
|
||||||
archive closure. The compiler-generated `-T` dispatcher owns the implicit
|
archive closure. The compiler-generated `-T` dispatcher owns the implicit
|
||||||
direct test-runtime support edge and remains embedded in each independent test
|
direct test-runtime support edge and remains embedded in each independently
|
||||||
root. The command resolves that edge from the selected toolchain source tree,
|
compiled test root; it is the narrow test-main variant, not a
|
||||||
not the user search path. Normally its graph qualifier is `test`, so an explicit
|
coordinator-generated graph package. The shared plan compiles the common
|
||||||
source `import test` coalesces with the same canonical package. When a real user
|
runtime production package once. The command resolves that edge from the
|
||||||
package occupies that identity, the command presents the runtime edge to the
|
selected toolchain source tree, not the user search path. Normally its graph
|
||||||
compiler under the reserved `__wwtest` qualifier. This keeps a production
|
qualifier is `test`, so an explicit source `import test` coalesces with the same
|
||||||
package named `test` available to external tests while preserving raw `w6c -T`
|
canonical package. When a real user package occupies that identity, the command
|
||||||
compatibility, whose default unresolved qualifier remains `test`.
|
presents the runtime edge to the compiler under the reserved `__wwtest`
|
||||||
|
qualifier. This keeps a production package named `test` available to external
|
||||||
|
tests while preserving raw `w6c -T` compatibility, whose default unresolved
|
||||||
|
qualifier remains `test`.
|
||||||
|
|
||||||
The selected test root and a production variant reached by its imports or test
|
The selected test roots and a production variant reached by their imports or
|
||||||
runtime closure are the only sanctioned pair of graph nodes that may share a
|
test-runtime closure are the only sanctioned graph nodes that may share a
|
||||||
physical directory. This also lets a toolchain package's own tests coexist with
|
physical directory. This also lets a toolchain package's own tests coexist with
|
||||||
the production variant required by the test runtime. A same-package root
|
the production variant required by the test runtime. A same-package root
|
||||||
already defines those production symbols, so the duplicate production archive
|
already defines those production symbols, so the production archive is omitted
|
||||||
is omitted from its final link while that node's dependency archives remain in
|
from that product's final link while the production node's dependency archives
|
||||||
the closure. All ordinary logical and physical package-identity collision
|
remain in its closure. The external product includes the production archive.
|
||||||
checks remain unchanged. Recursive
|
Variant-only archives are never linked into the other product. All ordinary
|
||||||
discovery groups by physical directory before sorting filenames, and persistent
|
logical and physical package-identity collision checks remain unchanged.
|
||||||
coordinator work directories use an injective escaped `(directory, test
|
Recursive discovery groups by physical directory before sorting filenames, and
|
||||||
package)` key. Every `*_test.ww` package variant is built even when a file only
|
persistent coordinator work directories use one injective escaped directory
|
||||||
|
plan key. Every `*_test.ww` package variant is built even when a file only
|
||||||
declares helpers and contains no `@test`, so its package clause and imports are
|
declares helpers and contains no `@test`, so its package clause and imports are
|
||||||
still checked by the shared loader. Every built variant is run, and a successful
|
still checked by the shared loader. Every built variant is run, and a successful
|
||||||
compiler-owned dispatcher with empty output is reported as `[no tests]`.
|
compiler-owned dispatcher with empty output is reported as `[no tests]`.
|
||||||
@@ -2896,9 +2924,15 @@ compiler-owned dispatcher with empty output is reported as `[no tests]`.
|
|||||||
This ownership split follows Go 1.26.5's separation of production,
|
This ownership split follows Go 1.26.5's separation of production,
|
||||||
same-package-test, external-test, and generated test-main inputs in
|
same-package-test, external-test, and generated test-main inputs in
|
||||||
[`cmd/go/internal/load/test.go`](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/load/test.go),
|
[`cmd/go/internal/load/test.go`](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/load/test.go),
|
||||||
while retaining WW's compiler-owned dispatcher. Direct compile dependencies and
|
while retaining WW's compiler-owned dispatcher. Go's loader returns the same
|
||||||
the separately expanded link closure follow the boundary in
|
cached package pointer for repeated imports, and its command-scoped builder
|
||||||
[`cmd/go/internal/work/action.go`](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/work/action.go).
|
deduplicates compile actions by package identity; WW borrows that one-universe,
|
||||||
|
multi-root ownership boundary without borrowing Go's cache machinery. Direct
|
||||||
|
compile dependencies and the separately expanded link closure follow the
|
||||||
|
boundary in
|
||||||
|
[`cmd/go/internal/work/action.go`](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/work/action.go),
|
||||||
|
with compiler and linker import configurations emitted separately in
|
||||||
|
[`cmd/go/internal/work/exec.go`](https://go.googlesource.com/go/+/refs/tags/go1.26.5/src/cmd/go/internal/work/exec.go).
|
||||||
|
|
||||||
## 12. Candidate architectures and hard-gate decision
|
## 12. Candidate architectures and hard-gate decision
|
||||||
|
|
||||||
|
|||||||
@@ -71,6 +71,17 @@ fn writefile(path: str, content: str) void = {
|
|||||||
assert(os.close(fd) == 0);
|
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,
|
fn runcommand(root: str, name: str, argv: []str,
|
||||||
lifetime: time.duration, out: *commandout) void = {
|
lifetime: time.duration, out: *commandout) void = {
|
||||||
let c: exec.command;
|
let c: exec.command;
|
||||||
@@ -169,6 +180,22 @@ fn occurrences(haystack: str, needle: str) i32 = {
|
|||||||
return count;
|
return count;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn linecontaining(text: str, needle: str) str = {
|
||||||
|
let start: i32 = 0;
|
||||||
|
let i: i32 = 0;
|
||||||
|
for (i <= text.len) {
|
||||||
|
if (i == text.len || text[i] == '\n': u8) {
|
||||||
|
let line: str;
|
||||||
|
line.ptr = text.ptr + (start: u64);
|
||||||
|
line.len = i - start;
|
||||||
|
if (has(line, needle)) { return line; };
|
||||||
|
start = i + 1;
|
||||||
|
};
|
||||||
|
i += 1;
|
||||||
|
};
|
||||||
|
abort("missing trace line");
|
||||||
|
};
|
||||||
|
|
||||||
fn expectexit(out: *commandout, code: i32) void = {
|
fn expectexit(out: *commandout, code: i32) void = {
|
||||||
assert(out.termination == exec.termination.EXIT);
|
assert(out.termination == exec.termination.EXIT);
|
||||||
assert(out.code == code);
|
assert(out.code == code);
|
||||||
@@ -617,14 +644,13 @@ fn workescape(s: str) str = {
|
|||||||
assert(has(outc.stdout,
|
assert(has(outc.stdout,
|
||||||
strings.concat(" -> ", root, "/route_test.test\n")));
|
strings.concat(" -> ", root, "/route_test.test\n")));
|
||||||
let cwhite: str = readfile(strings.concat(root,
|
let cwhite: str = readfile(strings.concat(root,
|
||||||
"/route.test.sepwork/__root.s"));
|
"/route.test.sepwork/__ww-test-same.s"));
|
||||||
let cexternal: str = readfile(strings.concat(root,
|
let cexternal: str = readfile(strings.concat(root,
|
||||||
"/route_test.test.sepwork/__root.s"));
|
"/route.test.sepwork/__ww-test-external.s"));
|
||||||
// The explicit package `-c` outputs are caller-owned artifacts. Release
|
// The explicit package `-c` outputs are caller-owned artifacts. Release
|
||||||
// the two exact C-stage trees before asking the WW driver to acquire the
|
// the shared C-stage tree before asking the WW driver to acquire the same
|
||||||
// same stems; the driver never deletes a pre-existing `.sepwork` path.
|
// stem; the driver never deletes a pre-existing `.sepwork` path.
|
||||||
clean(strings.concat(root, "/route.test.sepwork"));
|
clean(strings.concat(root, "/route.test.sepwork"));
|
||||||
clean(strings.concat(root, "/route_test.test.sepwork"));
|
|
||||||
|
|
||||||
let wc: []str = [driver("ww_ww"), "test", "-c", root];
|
let wc: []str = [driver("ww_ww"), "test", "-c", root];
|
||||||
runcommand(root, "compile-ww", wc,
|
runcommand(root, "compile-ww", wc,
|
||||||
@@ -633,9 +659,9 @@ fn workescape(s: str) str = {
|
|||||||
assert(same(outc.stdout, outw.stdout));
|
assert(same(outc.stdout, outw.stdout));
|
||||||
assert(same(outc.stderr, outw.stderr));
|
assert(same(outc.stderr, outw.stderr));
|
||||||
assert(same(cwhite, readfile(strings.concat(root,
|
assert(same(cwhite, readfile(strings.concat(root,
|
||||||
"/route.test.sepwork/__root.s"))));
|
"/route.test.sepwork/__ww-test-same.s"))));
|
||||||
assert(same(cexternal, readfile(strings.concat(root,
|
assert(same(cexternal, readfile(strings.concat(root,
|
||||||
"/route_test.test.sepwork/__root.s"))));
|
"/route.test.sepwork/__ww-test-external.s"))));
|
||||||
clean(root);
|
clean(root);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -643,8 +669,8 @@ fn workescape(s: str) str = {
|
|||||||
let root: str = fresh();
|
let root: str = fresh();
|
||||||
let implementation: str = strings.concat(root, "/implementation");
|
let implementation: str = strings.concat(root, "/implementation");
|
||||||
let api: str = strings.concat(root, "/api");
|
let api: str = strings.concat(root, "/api");
|
||||||
let testonly: str = strings.concat(root, "/testonly");
|
let testonly: str = strings.concat(root, "/__same");
|
||||||
let externalonly: str = strings.concat(root, "/externalonly");
|
let externalonly: str = strings.concat(root, "/__external");
|
||||||
let pkg: str = strings.concat(root, "/pkg");
|
let pkg: str = strings.concat(root, "/pkg");
|
||||||
assert(os.mkdir(implementation, 448i32) == 0);
|
assert(os.mkdir(implementation, 448i32) == 0);
|
||||||
assert(os.mkdir(api, 448i32) == 0);
|
assert(os.mkdir(api, 448i32) == 0);
|
||||||
@@ -662,11 +688,11 @@ fn workescape(s: str) str = {
|
|||||||
writefile(strings.concat(api, "/api_test.ww"), strings.concat(
|
writefile(strings.concat(api, "/api_test.ww"), strings.concat(
|
||||||
"package api;\n",
|
"package api;\n",
|
||||||
"TEST_DEPENDENCY_MUST_NOT_COMPILE\n"));
|
"TEST_DEPENDENCY_MUST_NOT_COMPILE\n"));
|
||||||
writefile(strings.concat(testonly, "/testonly.ww"), strings.concat(
|
writefile(strings.concat(testonly, "/__same.ww"), strings.concat(
|
||||||
"package testonly;\n",
|
"package __same;\n",
|
||||||
"export fn value() i32 = { return 1; };\n"));
|
"export fn value() i32 = { return 1; };\n"));
|
||||||
writefile(strings.concat(externalonly, "/externalonly.ww"), strings.concat(
|
writefile(strings.concat(externalonly, "/__external.ww"), strings.concat(
|
||||||
"package externalonly;\n",
|
"package __external;\n",
|
||||||
"export fn value() i32 = { return 1; };\n"));
|
"export fn value() i32 = { return 1; };\n"));
|
||||||
writefile(strings.concat(pkg, "/a.ww"), strings.concat(
|
writefile(strings.concat(pkg, "/a.ww"), strings.concat(
|
||||||
"package pkg;\nimport api;\n",
|
"package pkg;\nimport api;\n",
|
||||||
@@ -678,16 +704,16 @@ fn workescape(s: str) str = {
|
|||||||
"fn secret() i32 = { return 6; };\n",
|
"fn secret() i32 = { return 6; };\n",
|
||||||
"export fn second() i32 = { return 9; };\n"));
|
"export fn second() i32 = { return 9; };\n"));
|
||||||
writefile(strings.concat(pkg, "/same_test.ww"), strings.concat(
|
writefile(strings.concat(pkg, "/same_test.ww"), strings.concat(
|
||||||
"package pkg;\nimport testonly;\n",
|
"package pkg;\nimport __same;\n",
|
||||||
"// SAME_TEST_SOURCE\n",
|
"// SAME_TEST_SOURCE\n",
|
||||||
"@test fn same_graph() void = {\n",
|
"@test fn same_graph() void = {\n",
|
||||||
" assert(value() + testonly.value() == 42);\n",
|
" assert(value() + __same.value() == 42);\n",
|
||||||
" assert(secret() == 6);\n};\n"));
|
" assert(secret() == 6);\n};\n"));
|
||||||
writefile(strings.concat(pkg, "/external_test.ww"), strings.concat(
|
writefile(strings.concat(pkg, "/external_test.ww"), strings.concat(
|
||||||
"package pkg_test;\nimport externalonly;\nimport pkg;\n",
|
"package pkg_test;\nimport __external;\nimport pkg;\n",
|
||||||
"// EXTERNAL_TEST_SOURCE\n",
|
"// EXTERNAL_TEST_SOURCE\n",
|
||||||
"@test fn external_graph() void = {\n",
|
"@test fn external_graph() void = {\n",
|
||||||
" assert(pkg.value() + externalonly.value() == 42);\n",
|
" assert(pkg.value() + __external.value() == 42);\n",
|
||||||
" assert(pkg.second() == 9);\n};\n"));
|
" assert(pkg.second() == 9);\n};\n"));
|
||||||
|
|
||||||
let prodout: str = strings.concat(root, "/production.a");
|
let prodout: str = strings.concat(root, "/production.a");
|
||||||
@@ -702,19 +728,54 @@ fn workescape(s: str) str = {
|
|||||||
"pkg.unit.ww"));
|
"pkg.unit.ww"));
|
||||||
assert(has(produnit, "//ww:module api\n"));
|
assert(has(produnit, "//ww:module api\n"));
|
||||||
assert(!has(produnit, "//ww:module implementation\n"));
|
assert(!has(produnit, "//ww:module implementation\n"));
|
||||||
assert(!has(produnit, "testonly"));
|
assert(!has(produnit, "__same"));
|
||||||
assert(!has(produnit, "externalonly"));
|
assert(!has(produnit, "__external"));
|
||||||
assert(!os.exists(strings.concat(prodwork, "testonly.unit.ww")));
|
assert(!os.exists(strings.concat(prodwork, "__same.unit.ww")));
|
||||||
assert(!os.exists(strings.concat(prodwork, "externalonly.unit.ww")));
|
assert(!os.exists(strings.concat(prodwork, "__external.unit.ww")));
|
||||||
assert(!has(readfile(strings.concat(prodwork, "api.unit.ww")),
|
assert(!has(readfile(strings.concat(prodwork, "api.unit.ww")),
|
||||||
"TEST_DEPENDENCY_MUST_NOT_COMPILE"));
|
"TEST_DEPENDENCY_MUST_NOT_COMPILE"));
|
||||||
|
|
||||||
let samebin: str = strings.concat(pkg, "/pkg.test");
|
let samebin: str = strings.concat(pkg, "/pkg.test");
|
||||||
let externalbin: str = strings.concat(pkg, "/pkg_test.test");
|
let externalbin: str = strings.concat(pkg, "/pkg_test.test");
|
||||||
let sameworkroot: str = strings.concat(samebin, ".sepwork");
|
let sharedworkroot: str = strings.concat(samebin, ".sepwork");
|
||||||
let externalworkroot: str = strings.concat(externalbin, ".sepwork");
|
let sharedwork: str = strings.concat(sharedworkroot, "/");
|
||||||
let samework: str = strings.concat(sameworkroot, "/");
|
let compilertrace: str = strings.concat(root, "/compiler.trace");
|
||||||
let externalwork: str = strings.concat(externalworkroot, "/");
|
let linkertrace: str = strings.concat(root, "/linker.trace");
|
||||||
|
let compilerwrapper: str = strings.concat(root, "/trace-w6c.sh");
|
||||||
|
let linkerwrapper: str = strings.concat(root, "/trace-w6l.sh");
|
||||||
|
writefile(compilertrace, "");
|
||||||
|
writefile(linkertrace, "");
|
||||||
|
writeexecutable(compilerwrapper, strings.concat(
|
||||||
|
"#!/bin/sh\n",
|
||||||
|
"printf '%s\\n' \"$*\" >> \"$WW_PACKAGE_COMPILER_TRACE\"\n",
|
||||||
|
"exec \"$WW_PACKAGE_W6C\" \"$@\"\n"));
|
||||||
|
writeexecutable(linkerwrapper, strings.concat(
|
||||||
|
"#!/bin/sh\n",
|
||||||
|
"printf '%s\\n' \"$*\" >> \"$WW_PACKAGE_LINKER_TRACE\"\n",
|
||||||
|
"exec \"$WW_PACKAGE_W6L\" \"$@\"\n"));
|
||||||
|
let baseenv: []str = os.getenvs();
|
||||||
|
let traceenv: []str = alloc([], (baseenv.len + 6): u64)!;
|
||||||
|
let ei: i32 = 0;
|
||||||
|
for (ei < baseenv.len) {
|
||||||
|
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
|
||||||
|
&& !strings.hasprefix(baseenv[ei], "WW_W6L=")
|
||||||
|
&& !strings.hasprefix(baseenv[ei],
|
||||||
|
"WW_PACKAGE_COMPILER_TRACE=")
|
||||||
|
&& !strings.hasprefix(baseenv[ei],
|
||||||
|
"WW_PACKAGE_LINKER_TRACE=")
|
||||||
|
&& !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6C=")
|
||||||
|
&& !strings.hasprefix(baseenv[ei], "WW_PACKAGE_W6L=")) {
|
||||||
|
append(traceenv, baseenv[ei]);
|
||||||
|
};
|
||||||
|
ei += 1;
|
||||||
|
};
|
||||||
|
append(traceenv, strings.concat("WW_W6C=", compilerwrapper));
|
||||||
|
append(traceenv, strings.concat("WW_W6L=", linkerwrapper));
|
||||||
|
append(traceenv, strings.concat("WW_PACKAGE_COMPILER_TRACE=",
|
||||||
|
compilertrace));
|
||||||
|
append(traceenv, strings.concat("WW_PACKAGE_LINKER_TRACE=", linkertrace));
|
||||||
|
append(traceenv, strings.concat("WW_PACKAGE_W6C=", driver("w6c")));
|
||||||
|
append(traceenv, strings.concat("WW_PACKAGE_W6L=", driver("w6l")));
|
||||||
let drivers: []str = ["ww", "ww", "ww_ww"];
|
let drivers: []str = ["ww", "ww", "ww_ww"];
|
||||||
let tags: []str = ["c1", "c2", "ww"];
|
let tags: []str = ["c1", "c2", "ww"];
|
||||||
let referenceout: str = "";
|
let referenceout: str = "";
|
||||||
@@ -728,34 +789,41 @@ fn workescape(s: str) str = {
|
|||||||
for (i < drivers.len) {
|
for (i < drivers.len) {
|
||||||
let av: []str = [driver(drivers[i]), "test", "-c", "-I", root,
|
let av: []str = [driver(drivers[i]), "test", "-c", "-I", root,
|
||||||
pkg];
|
pkg];
|
||||||
runcommand(root, strings.concat("variant-build-", tags[i]), av,
|
if (i == 0) {
|
||||||
(120i64 * (time.second: i64)): time.duration, &out);
|
runcommandenv(root, strings.concat("variant-build-", tags[i]), av,
|
||||||
|
traceenv, (120i64 * (time.second: i64)): time.duration,
|
||||||
|
&out);
|
||||||
|
} else {
|
||||||
|
runcommand(root, strings.concat("variant-build-", tags[i]), av,
|
||||||
|
(120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
};
|
||||||
expectexit(&out, 0);
|
expectexit(&out, 0);
|
||||||
let buildstdout: str = strings.dup(out.stdout);
|
let buildstdout: str = strings.dup(out.stdout);
|
||||||
let buildstderr: str = strings.dup(out.stderr);
|
let buildstderr: str = strings.dup(out.stderr);
|
||||||
let sameunit: str = readfile(strings.concat(samework,
|
let sameunit: str = readfile(strings.concat(sharedwork,
|
||||||
"__root.unit.ww"));
|
"__ww-test-same.unit.ww"));
|
||||||
let externalunit: str = readfile(strings.concat(externalwork,
|
let externalunit: str = readfile(strings.concat(sharedwork,
|
||||||
"__root.unit.ww"));
|
"__ww-test-external.unit.ww"));
|
||||||
let externalproduction: str = readfile(strings.concat(externalwork,
|
let externalproduction: str = readfile(strings.concat(sharedwork,
|
||||||
"pkg.unit.ww"));
|
"pkg.unit.ww"));
|
||||||
|
assert(!os.exists(strings.concat(externalbin, ".sepwork")));
|
||||||
assert(has(sameunit, "//ww:module api\n"));
|
assert(has(sameunit, "//ww:module api\n"));
|
||||||
assert(has(sameunit, "//ww:module testonly\n"));
|
assert(has(sameunit, "//ww:module __same\n"));
|
||||||
assert(has(sameunit, "//ww:module test\n"));
|
assert(has(sameunit, "//ww:module test\n"));
|
||||||
assert(!has(sameunit, "//ww:module implementation\n"));
|
assert(!has(sameunit, "//ww:module implementation\n"));
|
||||||
assert(!has(sameunit, "//ww:module externalonly\n"));
|
assert(!has(sameunit, "//ww:module __external\n"));
|
||||||
assert(!os.exists(strings.concat(samework,
|
assert(os.exists(strings.concat(sharedwork,
|
||||||
"externalonly.unit.ww")));
|
"__external.unit.ww")));
|
||||||
assert(has(sameunit, "PACKAGE_PRODUCTION_A"));
|
assert(has(sameunit, "PACKAGE_PRODUCTION_A"));
|
||||||
assert(has(sameunit, "PACKAGE_PRODUCTION_Z"));
|
assert(has(sameunit, "PACKAGE_PRODUCTION_Z"));
|
||||||
assert(has(sameunit, "SAME_TEST_SOURCE"));
|
assert(has(sameunit, "SAME_TEST_SOURCE"));
|
||||||
assert(!has(sameunit, "EXTERNAL_TEST_SOURCE"));
|
assert(!has(sameunit, "EXTERNAL_TEST_SOURCE"));
|
||||||
assert(has(externalunit, "//ww:module pkg\n"));
|
assert(has(externalunit, "//ww:module pkg\n"));
|
||||||
assert(has(externalunit, "//ww:module externalonly\n"));
|
assert(has(externalunit, "//ww:module __external\n"));
|
||||||
assert(has(externalunit, "//ww:module test\n"));
|
assert(has(externalunit, "//ww:module test\n"));
|
||||||
assert(!has(externalunit, "//ww:module api\n"));
|
assert(!has(externalunit, "//ww:module api\n"));
|
||||||
assert(!has(externalunit, "//ww:module implementation\n"));
|
assert(!has(externalunit, "//ww:module implementation\n"));
|
||||||
assert(!has(externalunit, "//ww:module testonly\n"));
|
assert(!has(externalunit, "//ww:module __same\n"));
|
||||||
assert(has(externalunit, "EXTERNAL_TEST_SOURCE"));
|
assert(has(externalunit, "EXTERNAL_TEST_SOURCE"));
|
||||||
assert(!has(externalunit, "PACKAGE_PRODUCTION_A"));
|
assert(!has(externalunit, "PACKAGE_PRODUCTION_A"));
|
||||||
assert(!has(externalunit, "PACKAGE_PRODUCTION_Z"));
|
assert(!has(externalunit, "PACKAGE_PRODUCTION_Z"));
|
||||||
@@ -765,12 +833,61 @@ fn workescape(s: str) str = {
|
|||||||
assert(!has(externalproduction, "//ww:module implementation\n"));
|
assert(!has(externalproduction, "//ww:module implementation\n"));
|
||||||
assert(!has(externalproduction, "SAME_TEST_SOURCE"));
|
assert(!has(externalproduction, "SAME_TEST_SOURCE"));
|
||||||
assert(!has(externalproduction, "EXTERNAL_TEST_SOURCE"));
|
assert(!has(externalproduction, "EXTERNAL_TEST_SOURCE"));
|
||||||
assert(!has(readfile(strings.concat(externalwork, "api.unit.ww")),
|
assert(!has(readfile(strings.concat(sharedwork, "api.unit.ww")),
|
||||||
"TEST_DEPENDENCY_MUST_NOT_COMPILE"));
|
"TEST_DEPENDENCY_MUST_NOT_COMPILE"));
|
||||||
assert(os.exists(strings.concat(samework, "implementation.a")));
|
let artifacts: []str = ["implementation", "api", "pkg",
|
||||||
assert(os.exists(strings.concat(externalwork, "implementation.a")));
|
"__same", "__external", "test"];
|
||||||
assert(os.exists(strings.concat(externalwork, "api.a")));
|
let ai: i32 = 0;
|
||||||
assert(os.exists(strings.concat(externalwork, "pkg.a")));
|
for (ai < artifacts.len) {
|
||||||
|
assert(os.exists(strings.concat(sharedwork, artifacts[ai], ".wwi")));
|
||||||
|
assert(os.exists(strings.concat(sharedwork, artifacts[ai], ".a")));
|
||||||
|
ai += 1;
|
||||||
|
};
|
||||||
|
assert(os.exists(strings.concat(sharedwork, "__ww-test-same.o")));
|
||||||
|
assert(os.exists(strings.concat(sharedwork, "__ww-test-external.o")));
|
||||||
|
assert(!os.exists(strings.concat(sharedwork, "__ww-test-same.wwi")));
|
||||||
|
assert(!os.exists(strings.concat(sharedwork,
|
||||||
|
"__ww-test-external.wwi")));
|
||||||
|
assert(!os.exists(strings.concat(sharedwork, "__ww-test-same.a")));
|
||||||
|
assert(!os.exists(strings.concat(sharedwork,
|
||||||
|
"__ww-test-external.a")));
|
||||||
|
assert(os.exists(samebin));
|
||||||
|
assert(os.exists(externalbin));
|
||||||
|
if (i == 0) {
|
||||||
|
let ctrace: str = readfile(compilertrace);
|
||||||
|
assert(occurrences(ctrace, "implementation.unit.ww") == 1);
|
||||||
|
assert(occurrences(ctrace, "api.unit.ww") == 1);
|
||||||
|
assert(occurrences(ctrace, "pkg.unit.ww") == 1);
|
||||||
|
assert(occurrences(ctrace, "/__same.unit.ww") == 1);
|
||||||
|
assert(occurrences(ctrace, "/__external.unit.ww") == 1);
|
||||||
|
assert(occurrences(ctrace, "/test.unit.ww") == 1);
|
||||||
|
assert(occurrences(ctrace, "__ww-test-same.unit.ww") == 1);
|
||||||
|
assert(occurrences(ctrace,
|
||||||
|
"__ww-test-external.unit.ww") == 1);
|
||||||
|
assert(occurrences(ctrace,
|
||||||
|
"-T --test-support-module") == 2);
|
||||||
|
let ltrace: str = readfile(linkertrace);
|
||||||
|
assert(occurrences(ltrace, "\n") == 2);
|
||||||
|
let samelink: str = linecontaining(ltrace,
|
||||||
|
strings.concat("-o ", samebin, " "));
|
||||||
|
let externallink: str = linecontaining(ltrace,
|
||||||
|
strings.concat("-o ", externalbin, " "));
|
||||||
|
assert(has(samelink, strings.concat(sharedwork, "api.a")));
|
||||||
|
assert(has(samelink,
|
||||||
|
strings.concat(sharedwork, "implementation.a")));
|
||||||
|
assert(has(samelink, strings.concat(sharedwork, "__same.a")));
|
||||||
|
assert(!has(samelink, strings.concat(sharedwork, "pkg.a")));
|
||||||
|
assert(!has(samelink,
|
||||||
|
strings.concat(sharedwork, "__external.a")));
|
||||||
|
assert(has(externallink, strings.concat(sharedwork, "pkg.a")));
|
||||||
|
assert(has(externallink, strings.concat(sharedwork, "api.a")));
|
||||||
|
assert(has(externallink,
|
||||||
|
strings.concat(sharedwork, "implementation.a")));
|
||||||
|
assert(has(externallink,
|
||||||
|
strings.concat(sharedwork, "__external.a")));
|
||||||
|
assert(!has(externallink,
|
||||||
|
strings.concat(sharedwork, "__same.a")));
|
||||||
|
};
|
||||||
let runav: []str = [samebin];
|
let runav: []str = [samebin];
|
||||||
runcommand(root, strings.concat("variant-run-same-", tags[i]), runav,
|
runcommand(root, strings.concat("variant-run-same-", tags[i]), runav,
|
||||||
(60i64 * (time.second: i64)): time.duration, &out);
|
(60i64 * (time.second: i64)): time.duration, &out);
|
||||||
@@ -799,11 +916,39 @@ fn workescape(s: str) str = {
|
|||||||
assert(same(referenceerr, buildstderr));
|
assert(same(referenceerr, buildstderr));
|
||||||
};
|
};
|
||||||
if (i + 1 < drivers.len) {
|
if (i + 1 < drivers.len) {
|
||||||
clean(sameworkroot); clean(externalworkroot);
|
clean(sharedworkroot);
|
||||||
clean(samebin); clean(externalbin);
|
clean(samebin); clean(externalbin);
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
};
|
};
|
||||||
|
// The illegal-in-import root keys stay disjoint from the valid packages
|
||||||
|
// literally named __same and __external, including warm reuse.
|
||||||
|
let warmroot: str = strings.concat(root, "/warm");
|
||||||
|
assert(os.mkdir(warmroot, 448i32) == 0);
|
||||||
|
let warmav: []str = [driver("ww"), "test", "-w", warmroot,
|
||||||
|
"-I", root, pkg];
|
||||||
|
runcommandenv(root, "variant-warm-cold", warmav, traceenv,
|
||||||
|
(120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
let warmstdout: str = strings.dup(out.stdout);
|
||||||
|
let warmstderr: str = strings.dup(out.stderr);
|
||||||
|
let warmtrace: str = readfile(compilertrace);
|
||||||
|
let warmwork: str = strings.concat(warmroot, "/d_", workescape(pkg),
|
||||||
|
"_p_pkg/");
|
||||||
|
assert(has(readfile(strings.concat(warmwork, "__same.unit.ww")),
|
||||||
|
"package __same;"));
|
||||||
|
assert(has(readfile(strings.concat(warmwork,
|
||||||
|
"__ww-test-same.unit.ww")), "SAME_TEST_SOURCE"));
|
||||||
|
assert(has(readfile(strings.concat(warmwork, "__external.unit.ww")),
|
||||||
|
"package __external;"));
|
||||||
|
assert(has(readfile(strings.concat(warmwork,
|
||||||
|
"__ww-test-external.unit.ww")), "EXTERNAL_TEST_SOURCE"));
|
||||||
|
runcommandenv(root, "variant-warm-reuse", warmav, traceenv,
|
||||||
|
(120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(same(warmstdout, out.stdout));
|
||||||
|
assert(same(warmstderr, out.stderr));
|
||||||
|
assert(same(warmtrace, readfile(compilertrace)));
|
||||||
let aliasunit: str = strings.concat(root, "/alias.unit.ww");
|
let aliasunit: str = strings.concat(root, "/alias.unit.ww");
|
||||||
writefile(aliasunit, strings.concat(
|
writefile(aliasunit, strings.concat(
|
||||||
"//ww:module-reset __wwtest\n",
|
"//ww:module-reset __wwtest\n",
|
||||||
@@ -834,6 +979,79 @@ fn workescape(s: str) str = {
|
|||||||
clean(root);
|
clean(root);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// One failing root is a product failure, not permission for the shared build
|
||||||
|
// request to suppress an independently compilable sibling root.
|
||||||
|
@test fn sibling_test_variant_failures_are_isolated() void = {
|
||||||
|
let root: str = fresh();
|
||||||
|
let badexternal: str = strings.concat(root, "/badexternal");
|
||||||
|
let badsame: str = strings.concat(root, "/badsame");
|
||||||
|
assert(os.mkdir(badexternal, 448i32) == 0);
|
||||||
|
assert(os.mkdir(badsame, 448i32) == 0);
|
||||||
|
writefile(strings.concat(badexternal, "/badexternal.ww"), strings.concat(
|
||||||
|
"package badexternal;\n",
|
||||||
|
"fn hidden() i32 = { return 41; };\n",
|
||||||
|
"export fn shown() i32 = { return 41; };\n"));
|
||||||
|
writefile(strings.concat(badexternal, "/same_test.ww"), strings.concat(
|
||||||
|
"package badexternal;\n",
|
||||||
|
"@test fn valid_same_runs() void = { assert(hidden() == 41); };\n"));
|
||||||
|
writefile(strings.concat(badexternal, "/external_test.ww"), strings.concat(
|
||||||
|
"package badexternal_test;\nimport badexternal;\n",
|
||||||
|
"@test fn invalid_external() void = {\n",
|
||||||
|
" assert(badexternal.hidden() == 41);\n};\n"));
|
||||||
|
writefile(strings.concat(badsame, "/badsame.ww"), strings.concat(
|
||||||
|
"package badsame;\n",
|
||||||
|
"export fn shown() i32 = { return 42; };\n"));
|
||||||
|
writefile(strings.concat(badsame, "/same_test.ww"), strings.concat(
|
||||||
|
"package badsame;\n",
|
||||||
|
"@test fn invalid_same() void = { assert(missing() == 0); };\n"));
|
||||||
|
writefile(strings.concat(badsame, "/external_test.ww"), strings.concat(
|
||||||
|
"package badsame_test;\nimport badsame;\n",
|
||||||
|
"@test fn valid_external_runs() void = {\n",
|
||||||
|
" assert(badsame.shown() == 42);\n};\n"));
|
||||||
|
|
||||||
|
let stages: []str = ["ww", "ww_ww"];
|
||||||
|
let tags: []str = ["c", "ww"];
|
||||||
|
let externaldiagnostics: []str = ["", ""];
|
||||||
|
let samediagnostics: []str = ["", ""];
|
||||||
|
let out: commandout;
|
||||||
|
let i: i32 = 0;
|
||||||
|
for (i < stages.len) {
|
||||||
|
let externalav: []str = [driver(stages[i]), "test", "-I", root,
|
||||||
|
badexternal];
|
||||||
|
runcommand(root, strings.concat("isolated-external-", tags[i]),
|
||||||
|
externalav, (120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 1);
|
||||||
|
assert(has(out.stdout, "valid_same_runs ... ok\n"));
|
||||||
|
assert(has(out.stdout, "[badexternal, same-package]\n"));
|
||||||
|
assert(has(out.stderr,
|
||||||
|
"has no exported declaration 'hidden'"));
|
||||||
|
assert(has(out.stderr,
|
||||||
|
strings.concat("FAIL ", badexternal,
|
||||||
|
" [badexternal_test] (build exit 1)\n")));
|
||||||
|
assert(!has(out.stderr, strings.concat("FAIL ", badexternal,
|
||||||
|
" [badexternal]")));
|
||||||
|
externaldiagnostics[i] = strings.dup(primarydiagnostic(out.stderr));
|
||||||
|
|
||||||
|
let sameav: []str = [driver(stages[i]), "test", "-I", root,
|
||||||
|
badsame];
|
||||||
|
runcommand(root, strings.concat("isolated-same-", tags[i]), sameav,
|
||||||
|
(120i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 1);
|
||||||
|
assert(has(out.stdout, "valid_external_runs ... ok\n"));
|
||||||
|
assert(has(out.stdout, "[badsame_test, external]\n"));
|
||||||
|
assert(has(out.stderr, "undefined: missing"));
|
||||||
|
assert(has(out.stderr, strings.concat("FAIL ", badsame,
|
||||||
|
" [badsame] (build exit 1)\n")));
|
||||||
|
assert(!has(out.stderr, strings.concat("FAIL ", badsame,
|
||||||
|
" [badsame_test]")));
|
||||||
|
samediagnostics[i] = strings.dup(primarydiagnostic(out.stderr));
|
||||||
|
i += 1;
|
||||||
|
};
|
||||||
|
assert(same(externaldiagnostics[0], externaldiagnostics[1]));
|
||||||
|
assert(same(samediagnostics[0], samediagnostics[1]));
|
||||||
|
clean(root);
|
||||||
|
};
|
||||||
|
|
||||||
// The compiler-generated runner uses a reserved graph qualifier rather than
|
// The compiler-generated runner uses a reserved graph qualifier rather than
|
||||||
// claiming the user-visible import path `test`. A real multi-file production
|
// claiming the user-visible import path `test`. A real multi-file production
|
||||||
// package named test can therefore be the normal direct dependency of its
|
// package named test can therefore be the normal direct dependency of its
|
||||||
@@ -872,7 +1090,8 @@ fn workescape(s: str) str = {
|
|||||||
runcommand(root, strings.concat("named-test-build-", stages[i]), av,
|
runcommand(root, strings.concat("named-test-build-", stages[i]), av,
|
||||||
(120i64 * (time.second: i64)): time.duration, &out);
|
(120i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 0);
|
expectexit(&out, 0);
|
||||||
let rootunit: str = readfile(strings.concat(work, "__root.unit.ww"));
|
let rootunit: str = readfile(strings.concat(work,
|
||||||
|
"__ww-test-external.unit.ww"));
|
||||||
let produnit: str = readfile(strings.concat(work, "test.unit.ww"));
|
let produnit: str = readfile(strings.concat(work, "test.unit.ww"));
|
||||||
let supportunit: str = readfile(strings.concat(work,
|
let supportunit: str = readfile(strings.concat(work,
|
||||||
"__wwtest.unit.ww"));
|
"__wwtest.unit.ww"));
|
||||||
@@ -967,7 +1186,8 @@ fn workescape(s: str) str = {
|
|||||||
expectexit(&out, 0);
|
expectexit(&out, 0);
|
||||||
assert(occurrences(readfile(trace), "shared.unit.ww") == 1);
|
assert(occurrences(readfile(trace), "shared.unit.ww") == 1);
|
||||||
let work: str = strings.concat(bin, ".sepwork/");
|
let work: str = strings.concat(bin, ".sepwork/");
|
||||||
let unit: str = readfile(strings.concat(work, "__root.unit.ww"));
|
let unit: str = readfile(strings.concat(work,
|
||||||
|
"__ww-test-same.unit.ww"));
|
||||||
assert(has(unit, "//ww:module left\n"));
|
assert(has(unit, "//ww:module left\n"));
|
||||||
assert(has(unit, "//ww:module right\n"));
|
assert(has(unit, "//ww:module right\n"));
|
||||||
assert(!has(unit, "//ww:module shared\n"));
|
assert(!has(unit, "//ww:module shared\n"));
|
||||||
@@ -1185,8 +1405,8 @@ fn workescape(s: str) str = {
|
|||||||
assert(has(outc.stderr,
|
assert(has(outc.stderr,
|
||||||
"ww test: -o needs -c for a package target\n"));
|
"ww test: -o needs -c for a package target\n"));
|
||||||
|
|
||||||
// -w on a package target forwards to the coordinator: one
|
// -w on a package target forwards to the coordinator: one shared
|
||||||
// persistent driver workdir per package group under the root,
|
// persistent driver workdir per directory plan under the root,
|
||||||
// reuse owned by the driver's content-identity contract. The
|
// reuse owned by the driver's content-identity contract. The
|
||||||
// byte stream must match the plain run, cold and warm.
|
// byte stream must match the plain run, cold and warm.
|
||||||
let wdroot: str = strings.concat(root, "/wd");
|
let wdroot: str = strings.concat(root, "/wd");
|
||||||
|
|||||||
Reference in New Issue
Block a user