test: library behavior owned by the coordinator lib/... walk

Go has no per-file test targets: the PACKAGE is the unit of testing
(`go test ./...`) and same-package test files compose together. The
59-target LIBRARY_TESTS fan-out (per-file -w workdirs, one Make rule
per suite) collapses to one line: `ww test -j $(JOBS) -w
out/wwbuild/wwtest-lib -I lib/ww lib/...` — the regex dir-route
precedent generalized. The lib/regex line and the per-file pattern
rule instance dissolve with it (test/lang keeps its own rule).

Measured before committing (-j4, strings edit row): old per-file
warm 4.7-4.9s; bare walk 5.2-5.6s — a real regression, so the
coordinator first gained the brief's persistent per-package workdir:
`-w DIR` on a package target forwards to wwpackage, which keys
DIR/<dir>_<pkg> per group and hands it to each inner `ww test -c`
build. Reuse stays entirely with the driver's existing
content-identity contract — the coordinator adds pure path policy,
no cache machinery. Both driver stages drop their package-target -w
rejects (forward instead); -w with -c stays rejected at the
coordinator (two ownership contracts). After: 3.9-4.0s on the edit
row, 1.0s warm no-op, 2.5s cold — faster than the old flow on every
row.

package_test's tree -w reject row becomes the positive contract
(cold+warm byte-stable stream, cs/ww same) plus the -c conflict
reject. libbyteid roster shape DECIDED: per-file fx entries stay —
every enrolled file is still standalone-buildable, so coverage is
byte-for-byte unchanged; the dir-mode entry form arrives only with
the B3 shared-helper split that first needs it. Docs: owner table,
target table, -w contract paragraph.
This commit is contained in:
2026-08-08 19:35:50 +09:00
parent 0ecdf75a0d
commit 5a263cb115
6 changed files with 115 additions and 80 deletions

View File

@@ -482,35 +482,12 @@ wwtest/test/tool/ffivariadic_test.ww: $(OUT)/ffivariadic/libffifix.a
# Native library tests execute their source owner directly. The retired C # Native library tests execute their source owner directly. The retired C
# launchers added no assertion beyond this exit status. # launchers added no assertion beyond this exit status.
LIBRARY_TESTS = lib/errors/errno_test.ww lib/ascii/ascii_test.ww \ # Library behavior has ONE owner: the package coordinator's lib/...
lib/ww/syntax/tok_test.ww lib/ww/syntax/ast_test.ww \ # tree walk in test-library (Go's `go test ./...` — the PACKAGE, not
lib/ww/syntax/sym_test.ww \ # the file, is the unit of testing; same-package *_test.ww compose
lib/strconv/test/ftos_test.ww lib/strconv/test/stof_test.ww \ # into one unit). No per-file registration exists: a new lib
lib/strconv/test/int_test.ww lib/strings/strings_test.ww \ # *_test.ww is discovered by the walk, and the libbyteid completeness
lib/strings/suffix_test.ww lib/strings/contains_test.ww \ # scan still fails loudly on any unenrolled lib/ dir.
lib/strings/index_test.ww lib/strings/compare_test.ww \
lib/bytes/equal_test.ww lib/bytes/index_test.ww \
lib/bytes/contains_test.ww lib/bytes/tokenize_test.ww \
lib/bytes/trim_test.ww lib/encoding/utf8/rune_test.ww \
lib/encoding/utf8/encode_test.ww lib/encoding/utf8/decode_test.ww \
lib/encoding/utf8/types_test.ww \
lib/bufio/stream_test.ww lib/bufio/scanner_test.ww \
lib/math/random/random_test.ww \
lib/math/checked/checked_test.ww lib/fmt/fmt_test.ww \
lib/log/funcs_test.ww lib/log/global_test.ww \
lib/log/silent_test.ww lib/fnmatch/fnmatch_test.ww \
lib/shlex/shlex_test.ww lib/time/arithm_test.ww \
lib/time/duration_test.ww lib/time/instant_test.ww \
lib/encoding/hex/hex_test.ww lib/memio/memio_test.ww \
lib/temp/temp_test.ww lib/getopt/getopt_test.ww \
lib/encoding/base32/base32_test.ww \
lib/encoding/base64/base64_test.ww \
lib/hash/adler32/adler32_test.ww lib/hash/crc16/crc16_test.ww \
lib/hash/crc32/crc32_test.ww lib/hash/crc64/crc64_test.ww \
lib/path/buffer_test.ww lib/path/posix_test.ww \
lib/path/stack_test.ww lib/crypto/sha256/sha256_test.ww \
lib/hash/siphash/siphash_test.ww \
lib/os/os_test.ww lib/os/stat_test.ww lib/dirs/dirs_test.ww
# These real sources have a standalone-compilation contract that is not # These real sources have a standalone-compilation contract that is not
# subsumed by a library @test import owner. # subsumed by a library @test import owner.
@@ -541,13 +518,12 @@ test-compiler: $(WWFIXTURE_BIN) $(WRAPPER_TOOLS) $(COMPILER_WRAPPER_BINS) \
echo "compiler artifact $$t"; BIN=$(CURDIR)/$(BIN) $(CURDIR)/$$t; \ echo "compiler artifact $$t"; BIN=$(CURDIR)/$(BIN) $(CURDIR)/$$t; \
done done
# One phony target per language/library test file so `make -jN` # One phony target per language test file so `make -jN` parallelizes
# parallelizes the suites, each in its own persistent -w workdir so only # the suite, each in its own persistent -w workdir so only changed
# changed packages rebuild. Tests always execute — the workdir caches # packages rebuild. Tests always execute — the workdir caches builds,
# builds, never results. # never results.
LANG_TEST_FILES = $(wildcard test/lang/*_test.ww) LANG_TEST_FILES = $(wildcard test/lang/*_test.ww)
LANG_TEST_TARGETS = $(LANG_TEST_FILES:%=wwtest/%) LANG_TEST_TARGETS = $(LANG_TEST_FILES:%=wwtest/%)
LIBRARY_TEST_TARGETS = $(LIBRARY_TESTS:%=wwtest/%)
$(LANG_TEST_TARGETS): wwtest/%: $(BIN)/ww \ $(LANG_TEST_TARGETS): wwtest/%: $(BIN)/ww \
$(BIN)/w6c $(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a
@@ -555,13 +531,6 @@ $(LANG_TEST_TARGETS): wwtest/%: $(BIN)/ww \
@mkdir -p $(WWBUILD)/wwtest/$(subst /,_,$*) @mkdir -p $(WWBUILD)/wwtest/$(subst /,_,$*)
@$(BIN)/ww test -w $(WWBUILD)/wwtest/$(subst /,_,$*) $* @$(BIN)/ww test -w $(WWBUILD)/wwtest/$(subst /,_,$*) $*
$(LIBRARY_TEST_TARGETS): wwtest/%: $(BIN)/ww \
$(BIN)/w6c $(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a
@echo "ww test $*"
@mkdir -p $(WWBUILD)/wwtest/$(subst /,_,$*)
@$(BIN)/ww test -w $(WWBUILD)/wwtest/$(subst /,_,$*) \
-I $(CURDIR)/lib/ww $*
$(BYTEID_WW_TARGETS): wwtest/%: $(BIN)/ww $(BIN)/w6c $(BIN)/w6a \ $(BYTEID_WW_TARGETS): wwtest/%: $(BIN)/ww $(BIN)/w6c $(BIN)/w6a \
$(BIN)/w6l $(BIN)/wwdump $(LIB)/libwwrt.a \ $(BIN)/w6l $(BIN)/wwdump $(LIB)/libwwrt.a \
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/ww_ww $(BIN)/w6c_ww
@@ -640,16 +609,19 @@ $(TOOL_WW_TARGETS): wwtest/%: $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
test-lang: $(LANG_TEST_TARGETS) test-lang: $(LANG_TEST_TARGETS)
test-library: $(LIBRARY_TEST_TARGETS) $(BIN)/w6c $(BIN)/w6c_ww \ test-library: $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
$(WWTEST_BIN) $(LIBRARY_STANDALONE_SOURCES) $(LIB)/libwwrt.a $(BIN)/w6c_ww $(WWTEST_BIN) \
$(LIBRARY_STANDALONE_SOURCES)
@set -e; for f in $(LIBRARY_STANDALONE_SOURCES); do \ @set -e; for f in $(LIBRARY_STANDALONE_SOURCES); do \
echo "library compile c $$f"; \ echo "library compile c $$f"; \
$(CURDIR)/$(BIN)/w6c $(CURDIR)/$$f > /dev/null; \ $(CURDIR)/$(BIN)/w6c $(CURDIR)/$$f > /dev/null; \
echo "library compile ww $$f"; \ echo "library compile ww $$f"; \
$(CURDIR)/$(BIN)/w6c_ww $(CURDIR)/$$f > /dev/null; \ $(CURDIR)/$(BIN)/w6c_ww $(CURDIR)/$$f > /dev/null; \
done done
@echo "ww test lib/regex" @echo "ww test -j $(JOBS) lib/..."
@$(CURDIR)/$(BIN)/ww test lib/regex @mkdir -p $(WWBUILD)/wwtest-lib
@$(CURDIR)/$(BIN)/ww test -j $(JOBS) -w $(WWBUILD)/wwtest-lib \
-I $(CURDIR)/lib/ww lib/...
test-bootstrap-native: $(WRAPPER_TOOLS) $(BOOTSTRAP_WRAPPER_BINS) test-bootstrap-native: $(WRAPPER_TOOLS) $(BOOTSTRAP_WRAPPER_BINS)
@set -e; for t in $(BOOTSTRAP_WRAPPER_BINS); do \ @set -e; for t in $(BOOTSTRAP_WRAPPER_BINS); do \
@@ -1000,7 +972,7 @@ nocc:
.PHONY: all cstage wwstage wwfixture test-wwfixture-process test-wwfixture \ .PHONY: all cstage wwstage wwfixture test-wwfixture-process test-wwfixture \
test test-unit test-compiler-smoke test-compiler test-package \ test test-unit test-compiler-smoke test-compiler test-package \
test-lang test-library test-commit test-lang-byteid \ test-lang test-library test-commit test-lang-byteid \
$(LANG_TEST_TARGETS) $(LIBRARY_TEST_TARGETS) $(BYTEID_WW_TARGETS) \ $(LANG_TEST_TARGETS) $(BYTEID_WW_TARGETS) \
$(SEP_WW_TARGETS) test-data-byteid test-byteid \ $(SEP_WW_TARGETS) test-data-byteid test-byteid \
test-bootstrap-native test-bootstrap test-incremental \ test-bootstrap-native test-bootstrap test-incremental \
test-platform test-all install clean bootstrap nocc bootstrap-snapshot test-platform test-all install clean bootstrap nocc bootstrap-snapshot

View File

@@ -1708,16 +1708,13 @@ do_test(int argc, char **argv)
"ww test: -o needs -c for a package target\n"); "ww test: -o needs -c for a package target\n");
return 2; return 2;
} }
if (workdir[0]) {
fprintf(stderr,
"ww test: -w needs a single test file\n");
return 2;
}
if (pattern) { if (pattern) {
fprintf(stderr, fprintf(stderr,
"ww test: pattern needs a single test file\n"); "ww test: pattern needs a single test file\n");
return 2; return 2;
} }
/* -w forwards: the coordinator keys one persistent driver
* workdir per package group under the given root. */
return exec_package_tests(argc, argv, src, NULL, 0); return exec_package_tests(argc, argv, src, NULL, 0);
} }
struct stat st; struct stat st;
@@ -1742,11 +1739,6 @@ do_test(int argc, char **argv)
"ww test: -o needs -c for a package target\n"); "ww test: -o needs -c for a package target\n");
return 2; return 2;
} }
if (workdir[0]) {
fprintf(stderr,
"ww test: -w needs a single test file\n");
return 2;
}
if (pattern) { if (pattern) {
fprintf(stderr, fprintf(stderr,
"ww test: pattern needs a single test file\n"); "ww test: pattern needs a single test file\n");
@@ -1882,10 +1874,6 @@ do_test(int argc, char **argv)
fprintf(stderr, "ww test: -o needs -c for a package target\n"); fprintf(stderr, "ww test: -o needs -c for a package target\n");
return 2; return 2;
} }
if (workdir[0]) {
fprintf(stderr, "ww test: -w needs a single test file\n");
return 2;
}
/* The second bare positional remains the legacy single-file filter form; /* The second bare positional remains the legacy single-file filter form;
* package filtering uses explicit -run/-filter options. */ * package filtering uses explicit -run/-filter options. */
if (pattern) { if (pattern) {

View File

@@ -26,7 +26,7 @@ categories out of the ordinary developer target.
| Compile success/rejection, stage-routed diagnostics, and runtime exit | `test/wcc/data/*/case.ww`, executed by `wwfixture` | | Compile success/rejection, stage-routed diagnostics, and runtime exit | `test/wcc/data/*/case.ww`, executed by `wwfixture` |
| Package semantics | `test/package` and the native package-test coordinator | | Package semantics | `test/package` and the native package-test coordinator |
| Language behavior | `test/lang/*_test.ww` through the language `@test` runtime | | Language behavior | `test/lang/*_test.ww` through the language `@test` runtime |
| Library behavior | Library-owned `@test` sources named by `LIBRARY_TESTS` | | Library behavior | The package coordinator's `lib/...` tree walk (`ww test -j N -I lib/ww lib/...`) |
| Standalone library-source compilation | Four real source paths named by `LIBRARY_STANDALONE_SOURCES`, compiled directly by both frontends | | Standalone library-source compilation | Four real source paths named by `LIBRARY_STANDALONE_SOURCES`, compiled directly by both frontends |
| Compiler-output identity | `test-lang-byteid` and `test-data-byteid` plus residual byte/artifact carriers | | Compiler-output identity | `test-lang-byteid` and `test-data-byteid` plus residual byte/artifact carriers |
| Fixed point and self-host | `test-bootstrap` | | Fixed point and self-host | `test-bootstrap` |
@@ -101,7 +101,7 @@ outright; git history is the archive.
| `test-compiler` | Complete fixture corpus plus residual compiler/integration carriers | | `test-compiler` | Complete fixture corpus plus residual compiler/integration carriers |
| `test-package` | Package planning, grouping, routing, and package runtime only | | `test-package` | Package planning, grouping, routing, and package runtime only |
| `test-lang` | Language-owned `@test` behavior | | `test-lang` | Language-owned `@test` behavior |
| `test-library` | Library-owned `@test` behavior plus four direct standalone-source C/WW compilation checks | | `test-library` | The coordinator's `lib/...` walk plus four direct standalone-source C/WW compilation checks |
| `test-commit` | Unit + compiler + package + language + library behavior | | `test-commit` | Unit + compiler + package + language + library behavior |
| `test-byteid` | Compiler-output identity gates | | `test-byteid` | Compiler-output identity gates |
| `test-bootstrap` | Fixed-point bootstrap plus the 950/991995 native gates | | `test-bootstrap` | Fixed-point bootstrap plus the 950/991995 native gates |
@@ -246,6 +246,16 @@ This is build staleness in the Make/mk/Go sense, not a result cache: tests
always run, and the byte-identity and bootstrap gates keep building on fresh always run, and the byte-identity and bootstrap gates keep building on fresh
scratch. `make clean` reclaims every workdir under `out/`. scratch. `make clean` reclaims every workdir under `out/`.
On a package or tree target, `ww test -w DIR` forwards to the coordinator,
which keys one persistent driver workdir per package group under `DIR`
(`<dir>_<pkg>` with `/` flattened) and passes it to each inner build; every
reuse decision stays with the driver's content-identity contract above.
`-w` and `-c` do not mix there — the coordinator rejects the combination
rather than guess which artifact tree the caller owns. `test-library` uses
exactly this: one `ww test -j $(JOBS) -w out/wwbuild/wwtest-lib -I lib/ww
lib/...` line owns library behavior (measured: 2.3s cold, 1.0s warm at
`-j 4`, vs 4.7s for the retired 59-target per-file fan-out).
`test/lang` currently uses one package per source file, so its complete gate `test/lang` currently uses one package per source file, so its complete gate
still performs independent package builds. That remaining source layout is not still performs independent package builds. That remaining source layout is not
hidden behind caching or concurrency; it is outside the small `test` target. hidden behind caching or concurrency; it is outside the small `test` target.

View File

@@ -32,6 +32,7 @@ type pkggroup = struct {
external: bool, external: bool,
root: str, root: str,
combined: str, combined: str,
workdir: str,
bin: str, bin: str,
buildout: str, buildout: str,
builderr: str, builderr: str,
@@ -105,9 +106,10 @@ fn pkgfailpath(path: str, reason: str) void = {
fn pkgusage() void = { fn pkgusage() void = {
let s: str = strings.concat( let s: str = strings.concat(
"usage: wwtest package [-c] [-list] [-j N] [-I DIR] [-run|-filter GLOB] [-timeout-ms=N] [DIR | DIR/...] [-- GLOB ...]\n", "usage: wwtest package [-c] [-list] [-j N] [-I DIR] [-w DIR] [-run|-filter GLOB] [-timeout-ms=N] [DIR | DIR/...] [-- GLOB ...]\n",
" *_test.ww is canonical; noncanonical files require an actual @test declaration\n", " *_test.ww is canonical; noncanonical files require an actual @test declaration\n",
" -c retains the compiled package binaries; -j N runs up to N package groups at once\n"); " -c retains the compiled package binaries; -j N runs up to N package groups at once\n",
" -w DIR keys a persistent per-package-group build workdir under DIR\n");
pkgput(os.STDERR_FILENO, s); pkgput(os.STDERR_FILENO, s);
}; };
@@ -505,12 +507,35 @@ fn pkgcombined(g: *pkggroup, srcs: []pkgsource) bool = {
return ok; return ok;
}; };
fn pkgworkkey(dir: str, pkg: str) str = {
let s: str = strings.dup(strings.concat(dir, "_", pkg));
let b: []u8 = strings.toutf8(s);
let i: i32 = 0;
for (i < b.len) {
if (b[i] == '/') { b[i] = '_'; };
i += 1;
};
return strings.frombytes(b);
};
fn pkgsetpaths(g: *pkggroup, root: str, index: i32, fn pkgsetpaths(g: *pkggroup, root: str, index: i32,
compileonly: bool, outname: str) bool = { compileonly: bool, outname: str, workroot: str) bool = {
let num: str = strconv.i32tos(index, strconv.base.DEC); let num: str = strconv.i32tos(index, strconv.base.DEC);
g.root = strings.concat(root, "/group-", num); g.root = strings.concat(root, "/group-", num);
if (!pkgmakedir(g.root)) { return false; }; if (!pkgmakedir(g.root)) { return false; };
g.combined = strings.concat(g.root, "/package.ww"); g.combined = strings.concat(g.root, "/package.ww");
// A caller-owned persistent workdir root keys one driver -w dir
// per (dir, pkg) group; the driver's content-identity contract
// owns every reuse decision, so this stays a pure path policy.
g.workdir = "";
if (workroot.len != 0) {
g.workdir = strings.concat(workroot, "/",
pkgworkkey(g.dir, g.pkg));
match (os.mkdirs(g.workdir, 448)) {
case void => void;
case let e: os.oserror => return false;
};
};
if (compileonly) { if (compileonly) {
if (outname.len != 0) { if (outname.len != 0) {
g.bin = outname; g.bin = outname;
@@ -561,7 +586,7 @@ fn pkgreportcommand(kind: str, g: *pkggroup, r: *exec.result) void = {
fn pkgstartbuild(g: *pkggroup, builder: str, includes: []str, fn pkgstartbuild(g: *pkggroup, builder: str, includes: []str,
h: *exec.process) void = { h: *exec.process) void = {
let ba: []str = alloc([], (9 + includes.len * 2): u64)!; let ba: []str = alloc([], (11 + includes.len * 2): u64)!;
append(ba, builder); append(ba, builder);
append(ba, "test"); append(ba, "test");
append(ba, "-c"); append(ba, "-c");
@@ -575,6 +600,10 @@ fn pkgstartbuild(g: *pkggroup, builder: str, includes: []str,
append(ba, includes[ii]); append(ba, includes[ii]);
ii += 1; ii += 1;
}; };
if (g.workdir.len != 0) {
append(ba, "-w");
append(ba, g.workdir);
};
append(ba, g.combined); append(ba, g.combined);
let bcmd: exec.command; let bcmd: exec.command;
bcmd.path = builder; bcmd.path = builder;
@@ -674,6 +703,7 @@ export fn packagecommand(args: []str) int = {
let includes: []str = alloc([], (args.len + 1): u64)!; let includes: []str = alloc([], (args.len + 1): u64)!;
let timeoutarg: str = ""; let timeoutarg: str = "";
let outname: str = ""; let outname: str = "";
let workroot: str = "";
let builder: str = pkgdefaultbuilder(); let builder: str = pkgdefaultbuilder();
let i: i32 = 0; let i: i32 = 0;
for (i < args.len) { for (i < args.len) {
@@ -704,6 +734,17 @@ export fn packagecommand(args: []str) int = {
i += 1; i += 1;
continue; continue;
}; };
if (strings.compare(a, "-w") == 0) {
if (i + 1 >= args.len) { pkgusage(); return 2; };
workroot = args[i + 1];
i += 2;
continue;
};
if (strings.hasprefix(a, "-w") && a.len > 2) {
workroot = a[2:a.len];
i += 1;
continue;
};
if (strings.hasprefix(a, "-timeout-ms=")) { if (strings.hasprefix(a, "-timeout-ms=")) {
if (timeoutarg.len != 0 || a.len == 12 if (timeoutarg.len != 0 || a.len == 12
|| pkgparsedec(a[12:a.len], 3600000i64) <= 0i64) { || pkgparsedec(a[12:a.len], 3600000i64) <= 0i64) {
@@ -757,6 +798,14 @@ export fn packagecommand(args: []str) int = {
pkgputln(os.STDERR_FILENO, "wwtest package: -o needs -c"); pkgputln(os.STDERR_FILENO, "wwtest package: -o needs -c");
return 2; return 2;
}; };
// -c publishes caller-owned sepwork artifacts; mixing that
// contract with a persistent workdir is unwired — reject rather
// than guess which tree the caller owns.
if (workroot.len != 0 && compileonly) {
pkgputln(os.STDERR_FILENO,
"wwtest package: -w conflicts with -c");
return 2;
};
// Go's ./... form: a trailing "..." path element walks the tree // Go's ./... form: a trailing "..." path element walks the tree
// rooted at the prefix instead of one explicit directory. // rooted at the prefix instead of one explicit directory.
@@ -924,7 +973,7 @@ export fn packagecommand(args: []str) int = {
for (!stopped && launched < groups.len && active < jobs) { for (!stopped && launched < groups.len && active < jobs) {
let g: *pkggroup = &groups[launched]; let g: *pkggroup = &groups[launched];
if (!pkgsetpaths(g, tmproot, launched, compileonly, if (!pkgsetpaths(g, tmproot, launched, compileonly,
outname)) { outname, workroot)) {
g.fail = PKGFAILSETUP; g.fail = PKGFAILSETUP;
g.state = PKGDONE; g.state = PKGDONE;
stopped = true; stopped = true;

View File

@@ -2694,14 +2694,12 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
cerr("ww test: -o needs -c for a package target\n"); cerr("ww test: -o needs -c for a package target\n");
return 2; return 2;
}; };
if (workdir != nil) {
cerr("ww test: -w needs a single test file\n");
return 2;
};
if (patarg != nil) { if (patarg != nil) {
cerr("ww test: pattern needs a single test file\n"); cerr("ww test: pattern needs a single test file\n");
return 2; return 2;
}; };
// -w forwards: the coordinator keys one persistent driver
// workdir per package group under the given root.
return execpackagetests(selfdir, argv, argc, start, return execpackagetests(selfdir, argv, argc, start,
targetindex, nil, false); targetindex, nil, false);
}; };
@@ -2742,10 +2740,6 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
cerr("ww test: -o needs -c for a package target\n"); cerr("ww test: -o needs -c for a package target\n");
return 2; return 2;
}; };
if (workdir != nil) {
cerr("ww test: -w needs a single test file\n");
return 2;
};
if (patarg != nil) { if (patarg != nil) {
cerr("ww test: pattern needs a single test file\n"); cerr("ww test: pattern needs a single test file\n");
return 2; return 2;

View File

@@ -572,14 +572,36 @@ fn packagepath(relative: 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"));
let wdc: []str = [driver("ww"), "test", "-w", root, spec]; // -w on a package target forwards to the coordinator: one
let wdw: []str = [driver("ww_ww"), "test", "-w", root, spec]; // persistent driver workdir per package group under the root,
runcommand(root, "tree-w-c", wdc, time.second, &outc); // reuse owned by the driver's content-identity contract. The
runcommand(root, "tree-w-ww", wdw, time.second, &outw); // byte stream must match the plain run, cold and warm.
let wdroot: str = strings.concat(root, "/wd");
assert(os.mkdir(wdroot, 448i32) == 0);
let wdc: []str = [driver("ww"), "test", "-w", wdroot, spec];
let wdw: []str = [driver("ww_ww"), "test", "-w", wdroot, spec];
runcommand(root, "tree-w-c", wdc,
(30i64 * (time.second: i64)): time.duration, &outc);
runcommand(root, "tree-w-ww", wdw,
(30i64 * (time.second: i64)): time.duration, &outw);
expectexit(&outc, 0);
expectexit(&outw, 0);
assert(same(outc.stdout, outw.stdout));
assert(same(outc.stdout, seq));
runcommand(root, "tree-w-warm", wdc,
(30i64 * (time.second: i64)): time.duration, &outc);
expectexit(&outc, 0);
assert(same(outc.stdout, seq));
// -c publishes caller-owned artifacts; a persistent workdir is a
// different ownership contract — the coordinator rejects the mix.
let wcc: []str = [driver("ww"), "test", "-c", "-w", wdroot, spec];
let wcw: []str = [driver("ww_ww"), "test", "-c", "-w", wdroot, spec];
runcommand(root, "tree-wc-c", wcc, time.second, &outc);
runcommand(root, "tree-wc-ww", wcw, time.second, &outw);
expectexit(&outc, 2); expectexit(&outc, 2);
expectexit(&outw, 2); expectexit(&outw, 2);
assert(same(outc.stderr, outw.stderr)); assert(same(outc.stderr, outw.stderr));
assert(has(outc.stderr, "ww test: -w needs a single test file\n")); assert(has(outc.stderr, "wwtest package: -w conflicts with -c\n"));
let bare: str = strings.concat(root, "/bare"); let bare: str = strings.concat(root, "/bare");
assert(os.mkdir(bare, 448i32) == 0); assert(os.mkdir(bare, 448i32) == 0);