Summary
In run 33936334262 (PR #108), Build Java failed with a single test failure:
LaraSystemToolsTest > testRunCommand_withComplexCommand_parsesCorrectly() FAILED
org.opentest4j.AssertionFailedError at LaraSystemToolsTest.java:174
Build Java passes on every other run of the same commit; the test also passes locally.
Root cause
The test (LaraUtils/test/pt/up/fe/specs/lara/LaraSystemToolsTest.java:163) runs echo "hello world" via LaraSystemTools.runCommand with a 2-second process timeout (2000000000 ns).
SpecsSystem.executeProcess reports a timeout as exit code -1:
timedOut = !process.waitFor(timeoutNanos, TimeUnit.NANOSECONDS);
...
int returnValue = timedOut ? -1 : process.exitValue(); // SpecsSystem.java:319
so the assertThat(result.getReturnValue()).isEqualTo(0) at line 174 fails.
On CI, the test took 5.021s (vs ~0.15s locally). The timeline only fits the timeout path (there is no other wait ≥5s in the code path):
- ~2s:
process.waitFor(2s) expires — the forked echo hasn't completed exec yet
- ~3s:
outputFuture.get(...) blocks in readLine until the child finally gets scheduled, execs, and exits (SpecsSystem.java:306)
Why the child was starved for ~5s on CI but not locally:
LaraUtils/build.gradle sets maxParallelForks = availableProcessors() → on the 4-vCPU hosted runner, 4 fresh test JVMs fork simultaneously right when :test starts
- This test is the first method executed in the class, so it eats the full cold-start penalty: gradle daemon + 4 JVMs (each with a jacoco agent) initializing at once, plus a
System.gc() before every spawn (SpecsSystem.java:197)
- Freshly booted VM ⇒ cold page cache: the first
execve of echo/loader/libc faults every page from disk — locally those pages are hot in RAM
Measurements on a local machine (8 cores): warm spawn ~55ms; worst simulated case (4 fresh JVMs pinned to 4 cores, simultaneous first spawn) ~190–205ms — 10× under budget, which is why it only reproduces on CI.
Note the sibling tests use 0.5s timeouts and pass only because they run after the workers are warm — they are equally exposed.
Suggested fix
Raise the timeout in these tests (e.g., 10s), starting with testRunCommand_withComplexCommand_parsesCorrectly (and ideally the 0.5s siblings). The tests' purpose is argument parsing, not timeout enforcement, so the tight budget adds no value.
Summary
In run 33936334262 (PR #108),
Build Javafailed with a single test failure:Build Javapasses on every other run of the same commit; the test also passes locally.Root cause
The test (LaraUtils/test/pt/up/fe/specs/lara/LaraSystemToolsTest.java:163) runs
echo "hello world"viaLaraSystemTools.runCommandwith a 2-second process timeout (2000000000ns).SpecsSystem.executeProcessreports a timeout as exit code-1:so the
assertThat(result.getReturnValue()).isEqualTo(0)at line 174 fails.On CI, the test took 5.021s (vs ~0.15s locally). The timeline only fits the timeout path (there is no other wait ≥5s in the code path):
process.waitFor(2s)expires — the forkedechohasn't completedexecyetoutputFuture.get(...)blocks inreadLineuntil the child finally gets scheduled, execs, and exits (SpecsSystem.java:306)Why the child was starved for ~5s on CI but not locally:
LaraUtils/build.gradlesetsmaxParallelForks = availableProcessors()→ on the 4-vCPU hosted runner, 4 fresh test JVMs fork simultaneously right when:teststartsSystem.gc()before every spawn (SpecsSystem.java:197)execveofecho/loader/libc faults every page from disk — locally those pages are hot in RAMMeasurements on a local machine (8 cores): warm spawn ~55ms; worst simulated case (4 fresh JVMs pinned to 4 cores, simultaneous first spawn) ~190–205ms — 10× under budget, which is why it only reproduces on CI.
Note the sibling tests use 0.5s timeouts and pass only because they run after the workers are warm — they are equally exposed.
Suggested fix
Raise the timeout in these tests (e.g., 10s), starting with
testRunCommand_withComplexCommand_parsesCorrectly(and ideally the 0.5s siblings). The tests' purpose is argument parsing, not timeout enforcement, so the tight budget adds no value.