ww: print final status for explicit test failures

This commit is contained in:
2026-08-21 07:39:54 +09:00
parent b5fb478613
commit 394e46ff2f
7 changed files with 388 additions and 20 deletions

View File

@@ -372,6 +372,17 @@ fn pkgputln(fd: i32, s: str) void = {
pkgput(fd, "\n");
};
// Go's final test-status action is command-owned: after an explicit ordinary
// package request records a setup, build, or run failure, it prints one final
// FAIL after the ordered package results. Implicit current-directory testing
// and compile-only testing deliberately do not use this action.
fn pkgteststatusfail(explicitstatus: bool, compileonly: bool) int = {
if (explicitstatus && !compileonly) {
pkgputln(os.STDOUT_FILENO, "FAIL");
};
return 1;
};
fn pkgputhexbyte(fd: i32, value: u8) void = {
let digits: str = "0123456789abcdef";
let encoded: [2]u8;
@@ -1704,7 +1715,8 @@ fn pkgemitinstall(g: *pkggroup) bool = {
return true;
};
fn pkgemitgroup(g: *pkggroup, compileonly: bool) bool = {
fn pkgemitgroup(g: *pkggroup, compileonly: bool,
statusfailed: *bool) bool = {
if (g.notests) {
pkgput(os.STDOUT_FILENO, "? ");
pkgput(os.STDOUT_FILENO, g.dir);
@@ -1728,6 +1740,7 @@ fn pkgemitgroup(g: *pkggroup, compileonly: bool) bool = {
pkgput(os.STDOUT_FILENO, "\n");
};
if (!pkgrunok(g)) {
*statusfailed = true;
pkgreportcommand(os.STDOUT_FILENO, "test", g, &g.runres);
return false;
};
@@ -1740,7 +1753,7 @@ fn pkgemitgroup(g: *pkggroup, compileonly: bool) bool = {
// A directory build capture is emitted once, followed by its independently
// executed products in their existing byte-sorted group order.
fn pkgemitplan(p: *pkgplan, groups: []pkggroup,
compileonly: bool, buildonly: bool) i32 = {
compileonly: bool, buildonly: bool, statusfailed: *bool) i32 = {
let buildcaptures: bool = pkgemitfile(p.buildout, os.STDOUT_FILENO);
buildcaptures = pkgemitfile(p.builderr, os.STDERR_FILENO) && buildcaptures;
if (!buildcaptures) {
@@ -1755,11 +1768,14 @@ fn pkgemitplan(p: *pkgplan, groups: []pkggroup,
pkgreportcommand(os.STDERR_FILENO, "build", &groups[i],
&p.buildres);
};
if (!buildonly && !compileonly) { *statusfailed = true; };
failed += 1;
} else if (buildonly) {
void;
} else {
if (!pkgemitgroup(&groups[i], compileonly)) { failed += 1; };
if (!pkgemitgroup(&groups[i], compileonly, statusfailed)) {
failed += 1;
};
if (!compileonly && !pkgemitinstall(&groups[i])) { failed += 1; };
};
i += 1;
@@ -1774,6 +1790,7 @@ export fn packagecommand(args: []str) int = {
let jobs: i32 = 1;
let afterdash: bool = false;
let buildrootseen: bool = false;
let explicitstatus: bool = false;
if (args.len == PKG_COUNT_MAX) {
pkgputln(os.STDERR_FILENO,
"wwtest package: package graph is too large");
@@ -1943,6 +1960,12 @@ export fn packagecommand(args: []str) int = {
i += 2;
continue;
};
if (strings.compare(a, "--ww-explicit-test-target") == 0) {
if (explicitstatus || buildonly) { pkgusage(); return 2; };
explicitstatus = true;
i += 1;
continue;
};
if (strings.compare(a, "--ww-operation") == 0) {
if (i + 1 >= args.len || strings.compare(args[i + 1], "build") != 0
|| buildonly) {
@@ -2052,7 +2075,10 @@ export fn packagecommand(args: []str) int = {
};
i += 1;
};
if (ds.errors != 0) { return 1; };
if (ds.errors != 0) {
if (ds.fatal) { return 1; };
return pkgteststatusfail(explicitstatus, compileonly);
};
pkgsortstrings(directroots.paths);
directroots.paths = pkgdedup(directroots.paths);
i = 0;
@@ -2063,7 +2089,7 @@ export fn packagecommand(args: []str) int = {
&canonicaloom)) {
if (canonicaloom) { return 1; };
pkgfailpath(ds.paths[i], "cannot canonicalize package directory");
return 1;
return pkgteststatusfail(explicitstatus, compileonly);
};
let canonicalsource: str;
if (!pkgstring(&canonicalsource, canonicaldir, "/",
@@ -2115,7 +2141,7 @@ export fn packagecommand(args: []str) int = {
let pn: str;
if (!pkgread(ds.paths[i], &body) || !pkgclause(body, &pn)) {
pkgfailpath(ds.paths[i], "invalid or missing package clause");
return 1;
return pkgteststatusfail(explicitstatus, compileonly);
};
if (!pkgstring(&s.pkg, pn)) { return 1; };
append(srcs, s);
@@ -2221,7 +2247,8 @@ export fn packagecommand(args: []str) int = {
} else {
pkgfailpath(srcs[j].path,
"test package must match production package or <package>_test");
return 1;
return pkgteststatusfail(explicitstatus,
compileonly);
};
};
j += 1;
@@ -2242,7 +2269,8 @@ export fn packagecommand(args: []str) int = {
&& strings.compare(srcs[j].pkg, secondpkg) != 0) {
pkgfailpath(srcs[j].path,
"test package must match production package or <package>_test");
return 1;
return pkgteststatusfail(explicitstatus,
compileonly);
};
};
j += 1;
@@ -2253,7 +2281,8 @@ export fn packagecommand(args: []str) int = {
if (family.len == 0) {
pkgfailpath(f.path,
"external test package has empty base name");
return 1;
return pkgteststatusfail(explicitstatus,
compileonly);
};
hasexternal = true;
externalpkg = firstpkg;
@@ -2284,7 +2313,7 @@ export fn packagecommand(args: []str) int = {
} else {
pkgfailpath(f.path,
"test package must match production package or <package>_test");
return 1;
return pkgteststatusfail(explicitstatus, compileonly);
};
};
};
@@ -2511,6 +2540,7 @@ export fn packagecommand(args: []str) int = {
let planout: str = outname;
if (buildnull) { planout = ""; };
let failed: i32 = 0;
let statusfailed: bool = false;
i = 0;
for (i < plans.len) {
if (!pkgsetplanpaths(&plans[i], groups, tmproot, i,
@@ -2675,7 +2705,8 @@ export fn packagecommand(args: []str) int = {
i = 0;
for (i < plans.len) {
failed += pkgemitplan(&plans[i], groups, compileonly, buildonly);
failed += pkgemitplan(&plans[i], groups, compileonly, buildonly,
&statusfailed);
i += 1;
};
if (!pkgremoveall(tmproot)) {
@@ -2683,6 +2714,9 @@ export fn packagecommand(args: []str) int = {
pkgputln(os.STDERR_FILENO, tmproot);
failed += 1;
};
if (statusfailed && explicitstatus && !compileonly) {
pkgputln(os.STDOUT_FILENO, "FAIL");
};
if (failed != 0) { return 1; };
return 0;
};