Files
Garry Tanandcapy-ai[bot] 44eeeb3c9b v1.91.372.0 feat(flaky): gstack-flaky reruns a failed test command once and ledgers the flake (GSTA-158 P1) (#3612)
* feat(flaky): gstack-flaky run reruns a failed JUnit test once and ledgers the flake (GSTA-158)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix(flaky): stream output, keep unreported runner failures red, fail-closed duplicates and nonzero reruns

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix: pre-landing review fixes

gstack-flaky never greens an incomplete run: a targeted rerun needs the
runner's summary proving the first run complete, otherwise (and always for
generic runners) the rerun repeats the whole command. A rerun clears a test
only when it exits 0 and the test passes once on its first attempt. Killed
runners exit 3 (FLAKY_COMMAND_KILLED) and signals are forwarded. The first
report is restored at --junit with reruns kept beside it; ids are redacted at
every output; the ledger gets its own GSTACK_FLAKE_LEDGER_JUNIT; fixtures are
registered in FREE_FIXTURES.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix: latch cancellation and check whole-command rerun reports

A cancel forwarded to a runner that traps the signal now counts as killed for
every later attempt, so a cancelled job is never rerun or greened. A
whole-command rerun clears nothing when its report has a failed test, misses a
first-run test, or its runner counted failures outside the report.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix: whole-command rerun when coverage or runner-flaky tests can fail the run

A coverage threshold or a fail-on-flaky policy fails a run for a reason a
narrowed rerun cannot reproduce, so either forces a whole-command rerun.
--junit refuses an existing file that is not a JUnit report instead of
deleting it.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* feat(flaky): rerun the whole command by default; targeted rerun is opt-in

A rerun counts only on the runner's own full green verdict: exit 0, no failed
or missing test, nothing counted outside the report. That covers every way a
run can fail, which a narrowed rerun cannot (a coverage threshold with an lcov
reporter, a fail-on-flaky policy, a test never reached). --rerun-scope test
reruns the failed test alone, with its preconditions documented and the
complete-run checks kept as a safety net. An unreadable existing --junit file
is a usage error, not a crash.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* docs: gstack-flaky CHANGELOG entry and standalone-binaries row (GSTA-158)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-10-31 15:33:39 +00:00

138 lines
6.3 KiB
XML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<testsuites id="" name="" tests="10" failures="4" skipped="2" errors="0" time="3.23467">
<testsuite name="a.spec.ts" timestamp="2026-10-29T16:02:06.214Z" hostname="alpha" tests="5" failures="2" skipped="1" time="0.03" errors="0">
<testcase name="group one › passes" classname="a.spec.ts" time="0.011">
</testcase>
<testcase name="group one › fails always" classname="a.spec.ts" time="0.004">
<system-out>
<![CDATA[
[[ATTACHMENT|pw/test-results/a-group-one-fails-always-alpha/error-context.md]]
]]>
</system-out>
<failure message="expect(received).toBe(expected) // Object.is equality" type="expect.toBe">
<![CDATA[ [alpha] › a.spec.ts:5:7 › group one › fails always ───────────────────────────────────────────────
Error: expect(received).toBe(expected) // Object.is equality
Expected: 2
Received: 1
3 | test.describe('group one', () => {
4 | test('passes', () => { expect(1).toBe(1); });
> 5 | test('fails always', () => { expect(1).toBe(2); });
| ^
6 | test('flaky once', ({}, info) => { const f = '/tmp/pwflake-' + info.project.name; if (!fs.existsSync(f)) { fs.writeFileSync(f, '1'); expect('first').toBe('second'); } });
7 | test.skip('skipped', () => {});
8 | test('title with $(rm -rf) [x] (y)? *', () => { expect(1).toBe(1); });
at /repo/tests/a.spec.ts:5:42
Error Context: ../test-results/a-group-one-fails-always-alpha/error-context.md
]]>
</failure>
</testcase>
<testcase name="group one › flaky once" classname="a.spec.ts" time="0.009">
<system-out>
<![CDATA[
[[ATTACHMENT|pw/test-results/a-group-one-flaky-once-alpha/error-context.md]]
]]>
</system-out>
<failure message="expect(received).toBe(expected) // Object.is equality" type="expect.toBe">
<![CDATA[ [alpha] › a.spec.ts:6:7 › group one › flaky once ─────────────────────────────────────────────────
Error: expect(received).toBe(expected) // Object.is equality
Expected: "second"
Received: "first"
4 | test('passes', () => { expect(1).toBe(1); });
5 | test('fails always', () => { expect(1).toBe(2); });
> 6 | test('flaky once', ({}, info) => { const f = '/tmp/pwflake-' + info.project.name; if (!fs.existsSync(f)) { fs.writeFileSync(f, '1'); expect('first').toBe('second'); } });
| ^
7 | test.skip('skipped', () => {});
8 | test('title with $(rm -rf) [x] (y)? *', () => { expect(1).toBe(1); });
9 | });
at /repo/tests/a.spec.ts:6:152
Error Context: ../test-results/a-group-one-flaky-once-alpha/error-context.md
]]>
</failure>
</testcase>
<testcase name="group one › skipped" classname="a.spec.ts">
<properties>
<property name="skip" value="">
</property>
</properties>
<skipped>
</skipped>
</testcase>
<testcase name="group one › title with $(rm -rf) [x] (y)? *" classname="a.spec.ts" time="0.006">
</testcase>
</testsuite>
<testsuite name="a.spec.ts" timestamp="2026-10-29T16:02:06.214Z" hostname="beta" tests="5" failures="2" skipped="1" time="0.027" errors="0">
<testcase name="group one › passes" classname="a.spec.ts" time="0.005">
</testcase>
<testcase name="group one › fails always" classname="a.spec.ts" time="0.002">
<system-out>
<![CDATA[
[[ATTACHMENT|pw/test-results/a-group-one-fails-always-beta/error-context.md]]
]]>
</system-out>
<failure message="expect(received).toBe(expected) // Object.is equality" type="expect.toBe">
<![CDATA[ [beta] › a.spec.ts:5:7 › group one › fails always ────────────────────────────────────────────────
Error: expect(received).toBe(expected) // Object.is equality
Expected: 2
Received: 1
3 | test.describe('group one', () => {
4 | test('passes', () => { expect(1).toBe(1); });
> 5 | test('fails always', () => { expect(1).toBe(2); });
| ^
6 | test('flaky once', ({}, info) => { const f = '/tmp/pwflake-' + info.project.name; if (!fs.existsSync(f)) { fs.writeFileSync(f, '1'); expect('first').toBe('second'); } });
7 | test.skip('skipped', () => {});
8 | test('title with $(rm -rf) [x] (y)? *', () => { expect(1).toBe(1); });
at /repo/tests/a.spec.ts:5:42
Error Context: ../test-results/a-group-one-fails-always-beta/error-context.md
]]>
</failure>
</testcase>
<testcase name="group one › flaky once" classname="a.spec.ts" time="0.013">
<system-out>
<![CDATA[
[[ATTACHMENT|pw/test-results/a-group-one-flaky-once-beta/error-context.md]]
]]>
</system-out>
<failure message="expect(received).toBe(expected) // Object.is equality" type="expect.toBe">
<![CDATA[ [beta] › a.spec.ts:6:7 › group one › flaky once ──────────────────────────────────────────────────
Error: expect(received).toBe(expected) // Object.is equality
Expected: "second"
Received: "first"
4 | test('passes', () => { expect(1).toBe(1); });
5 | test('fails always', () => { expect(1).toBe(2); });
> 6 | test('flaky once', ({}, info) => { const f = '/tmp/pwflake-' + info.project.name; if (!fs.existsSync(f)) { fs.writeFileSync(f, '1'); expect('first').toBe('second'); } });
| ^
7 | test.skip('skipped', () => {});
8 | test('title with $(rm -rf) [x] (y)? *', () => { expect(1).toBe(1); });
9 | });
at /repo/tests/a.spec.ts:6:152
Error Context: ../test-results/a-group-one-flaky-once-beta/error-context.md
]]>
</failure>
</testcase>
<testcase name="group one › skipped" classname="a.spec.ts">
<properties>
<property name="skip" value="">
</property>
</properties>
<skipped>
</skipped>
</testcase>
<testcase name="group one › title with $(rm -rf) [x] (y)? *" classname="a.spec.ts" time="0.007">
</testcase>
</testsuite>
</testsuites>