fix/java-enum-methods
17 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a654acbc88 |
fix(test-infra): grant the build-dir ACL by SID, not by name (#1532 follow-up)
The local Windows leg caught this within hours of merging #1532: on a machine that is not domain-joined, USERDOMAIN is the literal string WORKGROUP, and `WORKGROUP\test` is not a resolvable principal — WARN: build-dir DACL stamp (pre-wave) failed (user=WORKGROUP\test ...): WORKGROUP\test: No mapping between account names and security IDs was done. so the grant fails outright and the tree keeps its inherited `Authenticated Users:(M)`. The guard then correctly reports that cross-account mutation is still possible, and the whole leg stops. Since most Windows machines are not domain-joined, that is the common case, not the exotic one. #1532 was fixing a real bug in the other direction (a bare name resolves against the machine first, so a host named like its user grants to an empty principal). Both failures are name RESOLUTION failures, so this stops resolving names: the account is identified by SID, which is unambiguous everywhere. The SYSTEM and Administrators grants in these same commands have always used that form — the current user was the odd one out. Name lookup survives only as a fallback where PowerShell is unavailable. Applied to all three scripts carrying the stamp (run-tests-parallel, soak-test, memlab), since they share the failure and would otherwise drift apart. Co-Authored-By: Kiborgik <Kiborgik@users.noreply.github.com> Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com> |
||
|
|
3d613a4c9f |
fix(test): qualify build-dir owner with its domain
The Windows DACL stamps in run-tests-parallel.sh, soak-test.sh and
memlab.sh pass the current user to icacls by bare name. Git Bash resolves
whoami to coreutils, which prints an unqualified name, and icacls resolves
an unqualified name against the machine before the user. On a host whose
name equals the user's (COMPUTERNAME=BUILD with user build) the grant
lands on an empty principal:
BUILD\:(OI)(CI)(F)
Combined with the /inheritance:r on the same invocation, the directory
ends up granting no usable account. run-tests-parallel.sh then cannot
write its own log directory and the wave dies with:
build/c/test-logs/results.txt: Permission denied
FAIL: test-runner --list-suites exited nonzero
Prefix the name with USERDOMAIN when it is set, which makes the account
unambiguous, and fall back to the bare name otherwise. SYSTEM and
Administrators in the same invocations already use SID form and are
unaffected.
Signed-off-by: PETRO YAKOVYSHYN <petroyakovyshyn@gmail.com>
|
||
|
|
96a25b293e |
fix(test-harness): run extraction in the quiet tail, not the 18-job wave
The wide-flat SCALING-RATIO guard grows the input 20x and asserts the
time grows ~20x (linear) rather than ~128x (quadratic), with the bound at
40x between them. Contention does not cancel out of that ratio: the
400k-node measurement loses far more to memory pressure and scheduling
than the 20k one, so oversubscription inflates the ratio itself.
Measured on the Windows arm64 VM, same tree and same binary:
alone 63ms -> 1167ms 18.5x passes
in the 18-job wave 168ms -> 9045ms 53.8x fails
163ms -> 9019ms 55.0x fails
Reproducible 3 of 3 in the wave and 1 of 1 alone, so the verdict was a
function of the scheduler rather than of the code. The suite is ~22s;
running it alone is cheap next to a ~40min ladder.
The bound is deliberately NOT widened. The calibration note in
tests/test_extraction.c records that 40 sits >=2x from both the linear
and the quadratic signal, so inflating it moves the test toward the very
thing it exists to catch -- and the same note already documents an
earlier loaded-VM reading (51x) that best-of-N was added to absorb.
Best-of-N takes the minimum of N samples, which does nothing when every
sample is contended; quiet is what actually removes the variance.
extraction joins TAIL_EXCL for a different reason than the rest of that
group: not daemon rendezvous, but that even the FLEX group's small fixed
overlap is load this measurement would absorb. Both comments say so.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
|
||
|
|
c1425d2e27 |
ci: write the shard manifest before any suite runs; warn on absent upload
The manifest content (leg, slice, list hash) is fully determined at slicing time, so write it up front: a red run's manifest is exactly as load-bearing for the cross-shard union proof as a green one's. With that, a missing manifest at upload time can only mean the job died before the harness started (contract step / build failure) — that failure is already the job's red, so the upload warns instead of stacking a second error on top (the recurring 'No files were found with the provided path: build/c/test-logs/shard-manifest.txt' annotation). The shard-completeness job still gates the union. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com> |
||
|
|
62821375c4 |
fix: harden daemon release paths and verification
DCO / dco (push) Has been cancelled
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com> |
||
|
|
f7231aa2d9 |
test: harden the parallel runner's stamp and completion accounting
The Windows build-dir DACL stamp resets inherited ACEs before granting, verifies itself out loud, and re-stamps at the serial-tail boundary — the arm image ships explicit ACEs that survived a grant-only stamp and turned the whole suite red. Stamp verification failures now fail the run instead of warning. A suite that exits 0 without printing its summary line is counted as a failure rather than silently passing. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com> |
||
|
|
dc5649d1e6 |
test: re-stamp the Windows build-dir DACL at the serial-tail boundary
On one dry run the two arm shards split: the shard whose serial tail held the install-flow suites failed the source-directory policy minutes after its pre-wave stamp, while its sibling — same runner image, same stamp — passed with no install-flow suites to notice. Wave suites spawn Cygwin-family tooling that can rewrite the build directory's DACL behind the first stamp, so the stamp is now a function invoked both pre-wave and at the tail boundary, where the deadline-sensitive suites that depend on it actually run. Both invocations self-verify out loud. Verified on the VM from a deliberately reset (inherited) build directory through the real --par path: both stamps report clean, mini shard green. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com> |
||
|
|
acce9a4582 |
test: make the Windows build-dir DACL stamp verify itself out loud
The dry run's windows-11-arm shard failed its install-flow tests with the source-directory refusal the stamp exists to prevent — while the identical commands, run from the identically dirty state on the ARM64 VM, stamp clean and pass. The difference is invisible because the stamp was fully silenced; a silent load-bearing step cost a full CI round to even learn whether it had run. The stamp now reports each icacls failure with the user and directory, re-inspects the DACL afterwards, and prints one line in either direction: stamped clean, or the surviving cross-account grants. Verified on the VM from a deliberately reset (inherited, Authenticated- Users-writable) build directory through the real --par path: stamp reports clean, suites green. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com> |
||
|
|
04141465ee |
test: stamp the Windows build-directory DACL inside the parallel runner
The workflow pre-created build\c with a protected DACL so build products would inherit it, but the diagnostic note from the failing CI shard named the real actor: MSYS2's Cygwin layer writes POSIX-emulating DACLs — including a CREATOR OWNER (S-1-3-0) mutation grant — onto directories its tools touch, overwriting the pre-created shape during the build. The activation transaction's source-directory policy correctly refuses that grant, so the install-flow tests failed on CI while the VM (whose harness stamps AFTER building) stayed green. The stamp now runs inside run-tests-parallel.sh on Windows, after any builder has had its say and immediately before the suites — one code path for CI and the VM. Two idempotent steps, as before: protect the directory, then /reset the children onto the clean inherited set. Verified: full VM parallel run green (6669 passed, 0 failed) through the new path. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com> |
||
|
|
568e613141 |
test-infra: overlap the local ladder, open the serial tail, keep container I/O off virtiofs
Local runs left most of the machine idle: the serial tail ran sixteen suites one at a time on otherwise-idle cores, the three platform legs were launched by hand (usually sequentially), and every container-leg object write crossed the virtiofs bind mount. - run-tests-parallel.sh tail scheduling in two phases: the FLEX suites (timing-shaped but free of the shared per-account daemon runtime namespace) run CBM_TAIL_JOBS-wide (default 2), then the EXCL group — daemon-family plus the suites that drive daemon one-shots or supervisor rendezvous — runs strictly sequentially on a machine exactly as quiet as the old fully-serial tail gave it. The wave was already fed longest-first by the shard dealing order, so the drain-out no longer ends on a heavy straggler. - ladder.sh: one maintained entry point for the full local push gate with the legs overlapped — lint, the Linux container suite, and the Windows VM suite in the background, the macOS suite in the foreground, one verdict per leg, logs kept per leg. A missing prerequisite fails its leg loudly instead of silently skipping. - win.sh test-par now runs through vm-run-tests.sh (--par mode): the full parallel harness under the CI-shaped protected temp root with complete output. It previously ran under the MSYS-shared /tmp and piped through `tail -25` — the same truncated-blindness class that hid 40 Windows failures from the `test` command. - docker-compose: build artifacts and the incremental fixture cache move to named volumes on the container VM's native filesystem. Object writes over the virtiofs bind mount are the container legs' largest avoidable I/O cost, and the fixture cache now survives across container runs. - test_mem(win): the first valid full-parallel VM run proved working-set trimming beats the re-touch mitigation (19 MB resident of a 256 MB double-touch at 18 parallel suites). The RSS probe now VirtualLocks a 64 MB span — locked pages are exempt from trimming, making the measurement pressure-immune — with bounded touch-and-sample retries when the lock is unavailable. Red-to-green under the same 18-job load. - cli: the portable install's staging error now appends the activation refusal note (predicate, SID, object) like the managed path already does — a bare "activation transaction I/O failed" on a CI-only failure is undiagnosable without it. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com> |
||
|
|
d7ce789e8f |
ci: shard the C-suite legs, prove cross-shard coverage, warm compiler caches
PR iteration was bounded by single-job suite runs (~40-50 min critical path) and by every first-of-ref build starting cold. Same suites, same sanitizers, same gates — the schedule, the cache reuse, and a new runtime coverage proof change. - run-tests-parallel.sh accepts CBM_TEST_SHARD="i/N" and runs a deterministic slice of --list-suites; unset selects everything, so the path is inert outside CI. Known-heavy suites are dealt to shards in weight order first (naive modulo stacked the two slowest, store_arch and daemon_runtime, onto one job), the parallel wave and the serial tail are sliced separately so every shard keeps its own quiet tail, and the per-shard union guard proves each job ran exactly its slice. - shard-completeness job: per-shard guards cannot see a mis-plumbed CBM_TEST_SHARD (two jobs running the same slice passes every local check while a slice runs nowhere). Every leg now uploads a manifest (leg, i/N, sha of the full suite list, its slice) and one aggregation job re-proves per leg that the shards agreed on the list, indices are exactly 1..N, and the union of slices IS the list. Runs on unsharded topologies too, where each leg's single manifest must cover the list. - workflow topology (shard_suites input, off by default = byte-identical to the pre-shard matrix): ubuntu legs run 3 shards, Windows 2 (every extra Windows shard re-pays ~5 min of MSYS2 setup), macOS stays at 1 (not the critical path; mac runner concurrency ceilings are the tightest). The Windows test job also pre-creates build\c with a protected DACL so build products inherit it — workspace drive roots grant Authenticated Users Modify by inheritance, which the activation transaction's source-directory policy correctly refuses. - ccache: a final ref-less restore key lets a fresh PR start from the newest cache GitHub's scoping permits, and a nightly build-only cache-warm workflow keeps main-scoped caches at most a day stale — without it the fallback had no warm source, since nothing built in main's scope. The strictly-per-ref policy this replaces cost ~8-12 minutes on every first-of-ref build for no safety gain: CCACHE_COMPILERCHECK=content makes a stale or foreign cache able to miss but never to return wrong output. Sharded legs restore-all/ save-one (every shard builds the identical objects, so shard 1's cache carries the full set) — save volume stays flat and warm caches stop being evicted by per-shard duplicates. - suite timeouts: daemon_runtime joins the slow tier. It measures ~610s solo on arm64 under ASan (10-round loop, no hang), so the 900s default under a loaded 4-job CI runner was a slowness kill masquerading as a hang detector. Validated locally: 3-shard union over the real suite list is complete with no duplicates, one top-heavy suite per shard, and the summed 3-shard totals reproduce the unsharded run exactly (6776 passed, 0 failed, 4 skipped). Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com> |
||
|
|
0baddf7887 |
daemon: permanent lifecycle, daemon-backed CLI/hooks, real-Windows hardening, long-path launcher transactions
Daemon lifecycle and Windows correctness, verified on a real Windows 11 ARM64 VM through the maintained test-infrastructure/vm drivers, plus the macOS and Linux arm64 suites and the container lint gate. Daemon lifecycle: - daemon start/stop/status subcommands. `daemon start` launches a PERMANENT daemon (spawn shape is byte-exact argv; survives idle periods and session ends) and reports an already-active daemon instead of failing. Permanence is honored at every stop latch: last-committed-client disconnect, host initial-client window, coordinator release, and application final-session close — a permanent daemon also keeps admitting new sessions after its last one closes. - daemon stop refuses while sessions are active and lists the blocking peers (pid/role) that must finish first; an idle daemon drains through the activation-shutdown machinery with the ACK ordered after connection interrupts. A second stop is idempotent. The wire ops are no-cohort first-frame requests with peer fingerprint authentication, so stop/status never conflict with an exact-build admission gate. - One-shot CLI commands now execute through the daemon (index workers keep their local supervised path). A cold CLI run that had to spawn a temporary daemon prints a hint that `daemon start` removes the per-command startup tax; a warm daemon is recycled silently. - Hooks are connect-only fail-open: with no daemon present the hook emits a visible, rate-limited notice (Claude-dialect systemMessage plus stderr for other dialects) and always exits 0 — augmentation is never allowed to block the caller's tool use. - Version skew: a newer-build client automatically drains an older-build permanent daemon (strict semantic-version triples only; dev builds never auto-drain) and the build-conflict message names `cbm daemon stop` as the manual escape hatch. Windows IPC/runtime (real-VM verified): - ipc(win): persistent pending overlapped ConnectNamedPipe. The accept path used to destroy its listening pipe instance on every 20 ms poll timeout; a client attaching in the teardown window was severed or left on an orphaned pipe object whose HELLO no server handle could ever read, absorbing the connect until the client's own timeout expired. The pending connect now survives poll timeouts and nothing is destroyed while a client could be attaching. - ipc(win): drain-before-close for final responses. Closing a named-pipe server handle can discard a just-sent response before the peer reads it (POSIX stream sockets never lose buffered data on close). A bounded cbm_daemon_ipc_connection_drain (read-until-peer-EOF; no-op on POSIX, immediate on interrupted connections) now precedes close in runtime_worker_finish and runtime_reject_inline, so hello-conflict, capacity and disconnect acknowledgements reliably reach the peer. - runtime: CLOSE_INTENT wire frame. A Windows named-pipe client has no transport half-close, so close_begin now announces departure with an explicit frame (ordered after APPLICATION_CANCEL, before the local interrupt); the server releases the client's admission on receipt instead of waiting for the handle to close. Admission-drop timing is now identical to POSIX shutdown() semantics on every platform. - runtime(win): client close cancellation. close_begin serializes with request publication under the send lock, best-effort sends the active token's APPLICATION_CANCEL frame, then interrupts local I/O; the server cancels MCP/subprocess work promptly. Contract tests accept both correct outcomes (interrupted transport or decoded CANCELLED). - runtime: activation acknowledgement ordering. The activation ACK is the requester's license to act on "snapshotted and draining", so every connection interrupt is now initiated before the ACK is sent; a session could previously get one more request serviced after the requester observed the ACK. - service(win): deadline-bounded private-file prepare. The conflict-log prepare retry loop (100 x Sleep(2), which rounds up to the ~16 ms timer granularity) burned ~1.6 s against permanently obstructed paths, stalling hello rejections past the client's timeout. The retry budget is now a 250 ms deadline; transient share collisions still retry. - subprocess(win): cmd.exe /C payload encoder quotes metacharacters correctly (root cause of the git-on-Windows failure cluster). - watcher: SHA-256 buffer sizing (CBM_SZ_64 -> CBM_SZ_128) and a native Windows stop/unwatch cancellation test with exact-image verification. - httpd: send_all writes in bounded 64 KiB slices. A single giant nonblocking send() on Windows is absorbed wholesale into AFD kernel buffering regardless of SO_SNDBUF, so send deadlines and interrupts could never engage against a slow peer (and the full payload was pinned in nonpaged pool). Slicing restores a deterministic backpressure point; a test hook pins SO_SNDBUF for the deadline and interrupt tests. - ui/http: shutdown lifecycle — interrupt checks, response-wide send deadline, explicit connection states, refusal to free a server while a listener-owned connection is active. Windows long-path support: - Central path-aware wide conversion (canonicalize via GetFullPathNameW and prepend the extended-length prefix for absolute paths >=240) at the compat chokepoints (cbm_fopen/compat_fs/mkstemp/mkdtemp), sqlite store opens, and the daemon build-fingerprint/log paths. Deep managed installs (a 64-hex generation directory routinely exceeds MAX_PATH) now index, stage and activate correctly. - activation transaction: its own file APIs and the component-walking ancestry validators now operate in the extended-length namespace; the launcher path is canonicalized (and prefixed when deep) once at entry so every downstream exact-string comparison stays form-consistent. - Executable self-resolution uses the wide APIs (GetModuleFileNameW, GetFileAttributesW) so non-ASCII install paths survive argv[0] resolution. Windows launcher install/uninstall transaction: - FileRenameInfoEx names are NUL-terminated in an over-allocated buffer. FileNameLength governs per the contract, but filter drivers read FileName as NUL-terminated and appended adjacent heap bytes to created names — a flaky, garbage-suffixed rename target. Both the CLI and the launcher rename helpers are fixed. - Uninstall retires state via rename-aside (.cbm -> .cbm-retired-v1-<tag>-<pid>) with the retired tag shortened to 16 hex chars so the bare rename target stays under the FileRenameInfoEx NT-conversion ceiling at guard depths; 64 bits still uniquely identify the generation. - When the running launcher's mapped generation backings pin .cbm against rename, the backings are relocated to activation-<pid>-N .retired tombstones beside the install (a mapped image may be renamed, never deleted; the launcher's liveness-guarded sweep reclaims stale tombstones). Every relocation is recorded, and a FAILED uninstall reverses the moves after restoring .cbm — via MoveFileExW with extended-length paths on both arguments, since the deep generation target is beyond the handle-based rename's bare-path reach — so a restored install keeps its generation backings and stays runnable. - After a committed uninstall the retired tree's backings are relocated out so the tree is shallow enough for the detached cleanup's rd, and the cleanup's working directory strips the extended-length prefix (CreateProcessW lpCurrentDirectory silently ignores prefixed paths). - Files created under Administrators-default-owner directories (CopyFileW destinations, CREATE_NEW tombstones, probe directories) are explicitly owner-stamped so the exact-owner validators hold on runner images; guard fixtures stamp hand-built trees the same way. Diagnostics, tests and infra: - diagnostics: discovery is now an always-delivered JSON control record (new cbm_log_control) that survives CBM_LOG_LEVEL suppression and paths containing spaces; placement honors $TMPDIR with /tmp fallback via a diagnostics-local helper; the soak parser reads the JSON record; documented in docs/CONFIGURATION.md. Red-first coverage for suppressed log levels, TMPDIR-with-spaces, and native Windows output-contract assertions. - tests(win): daemon_ipc/daemon_frontend fixtures now build endpoint parents with production-shaped ancestry (LocalAppData on Windows, via th_secure_runtime_parent_new) — the runtime ancestry validation correctly refuses temp roots whose ancestors grant mutation rights to Authenticated Users (C:/msys64/tmp, GitHub-runner work dirs) — and drive the documented startup-owner publication flow before reading generation-bound endpoint addresses. This turns the 26 Windows failures previously visible in CI's full-test job green without weakening any validation. - tests(win): the launcher guard covers the full permanent-launcher contract including failed-uninstall restore and immediate reinstall after uninstall; new daemon lifecycle and reworked hook-augment guards run the start/recycle/stop flow end to end. - tests: CBM_SKIP_PERF is now actually consumed by the test runner (it was set by CI but never read, so perf suites ran everywhere); four throughput/bench suites are classified as perf, the heavy store_arch suite moved to the slow-timeout tier, and two wall-clock-sensitive assertions were rewritten as invariant checks with coarse hang-detector backstops. - build/test infra: build-dir safety contract, UI dev-proxy security contract, soak daemon-recovery contract, path-safety helper, the Windows VM worktree-sync contract wired into scripts/test.sh, and vm/win.sh guards building its clean embedded-UI product in an isolated BUILD_DIR so it cannot clobber the incremental test build. provision-windows.sh now installs Node.js for the guards UI build. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com> |
||
|
|
53d867dfc1 |
test-infra: per-suite timeout + arm64-safe mingw image (reliability)
Two reliability defects that bit this session: a wedged suite could hang the runner (and the single local build slot) forever, and the mingw image failed to build on an arm64 Docker host because its i386 Wine packages are unsatisfiable. - run-tests-parallel.sh now caps each suite with `timeout --kill-after` (900s default, 3600s for the legitimately-slow incremental suite), where `timeout` exists; a killed suite fails loudly instead of blocking. Hosts without `timeout` run uncapped as before. - Dockerfile.mingw makes Wine best-effort: the cross-COMPILE toolchain builds on any arch, and only the .exe-execution step needs Wine, so a Wine-unavailable host still gets a working compile check. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com> |
||
|
|
712d689efb |
fix(test): serialize the daemon-family suites in the parallel runner
The six suites that spawn coordinated worker subprocesses or bind local endpoints (index_supervisor, daemon_application, daemon_runtime, daemon_frontend, daemon_bootstrap, daemon_ipc) ran in the parallel wave, where the saturated 3-4-core CI runners starve their fixed readiness deadlines: index_supervisor waits at most 3 s for a worker marker while the worker is a full re-exec of the ASan runner plus the admission handshake. Both macOS legs failed the same four readiness assertions deterministically and ubuntu-latest added four daemon_frontend and one daemon_runtime timing failures, while an idle machine passes the same suites 6/6 in the wave. Moving them to the quiet serial tail follows the runner's existing rule for this class (cli/subprocess/watcher/...) and also keeps the shared per-account coordination namespace free of cross-suite admission traffic. Same suites, same tests, same union-guarded totals - only the schedule changes. Locally re-verified: 6742 passed, 0 failed, 2 skipped (120 suites, 17 serial-tail). Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com> |
||
|
|
95659b3955 |
test-par: serialize the mcp suite too (both alarm-deadline tests live there)
Flake audit: the whole corpus has exactly two wall-clock test deadlines — the EOF-shutdown alarm(5) and the issue-832 supervisor-spawn alarm(60), both in the mcp suite (the latter already flaked once under heavy local load). Serializing mcp costs ~6s of tail and closes the starvable-deadline class completely; every remaining parallel-wave suite is deadline-free. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com> |
||
|
|
c8e07a2f9f |
fix(test-par): CRLF-proof the suite list, serialize deadline-sensitive suites
First CI validation round of the parallel runner, three findings:
- Windows failed ALL 104 suites with "0 passed, 1 failed" each: the CRT
emits --list-suites lines as CRLF, so every dispatched name carried a
trailing CR ("arena\r" is an unknown suite). The union guard caught it
loudly, exactly as designed. The driver now strips CR when writing the
suite list.
- The ubuntu-gcc legs had 3 real failures in the cli suite: it spawns
subprocesses with fixed deadlines, and a fully saturated 4-core runner
starves those deadlines into flakes. Deadline-sensitive suites (cli,
subprocess, watcher, incremental, httpd, ui, index_resilience, and the
stack_overflow family) now run SEQUENTIALLY after the parallel wave on a
quiet machine — same suites, same tests, same gates, only the schedule
differs; the union guard checks the combined result set.
- Failing suites now print every FAIL site with context (the tail-30 of a
long suite log hid which tests actually failed on CI).
Local: totals unchanged (6,361 passed / 0 failed / 1 skipped), union guard
clean.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
|
||
|
|
bd939fc49a |
perf(test): run suites as parallel processes — same gates, ~2.5x faster
The runner executed all 104 suites sequentially (~10 min locally, the bulk of every CI test leg). The suites are process-isolated already (per-process mkdtemp HOME sentinel; two full parallel runs produced zero cross-suite failures), so the serialization was pure convention. - test-runner --list-suites: prints every registered suite, one per line, emitted by the SAME macro table that executes suites — the list cannot drift from the run set by construction. - scripts/run-tests-parallel.sh: runs each suite as its own process (jobs = CPU count; CBM_TEST_PAR_JOBS overrides) under a ZERO-LOSS CONTRACT: a union guard fails the gate if the set of suites that produced a result differs from --list-suites (nothing can be silently dropped, a newly added suite is picked up automatically); per-suite pass/fail/skip are summed into the sequential runner's exact summary format; any suite crash, failure, or omission exits nonzero. Per-suite wall times are printed for balance tracking. - make test-par: the parallel target. make test (sequential) is unchanged and remains the escape hatch (CBM_TEST_SEQUENTIAL=1 in test.sh). - scripts/test.sh: builds, then routes through test-par — every CI test leg gets the speedup with zero workflow-topology change (same jobs, same gates, same billing; the legs just finish sooner). - The stack_overflow suite is split into a/b/c (7+6+7 of its 20 tests, pure re-registration): as one suite it was the wall-clock critical path of any parallel run — every other suite finished underneath its ~4 minutes. Measured locally (Apple Silicon, ASan runner): sequential ~10 min vs parallel 232 s, totals identical (6,361 passed / 0 failed / 1 skipped), union guard clean. The TSan leg keeps its dedicated subset runner (sequential) — out of scope here. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com> |