Skip to content

fix(runner): fail the run when a test is aborted by a pipeline error#257

Merged
roxblnfk merged 2 commits into
php-testo:1.xfrom
viktorprogger:fix/aborted-status-exit-code
Jul 3, 2026
Merged

fix(runner): fail the run when a test is aborted by a pipeline error#257
roxblnfk merged 2 commits into
php-testo:1.xfrom
viktorprogger:fix/aborted-status-exit-code

Conversation

@viktorprogger

Copy link
Copy Markdown
Contributor

Problem

When tests are Aborted (an interceptor throws during the pipeline — e.g. a DI container cannot resolve a dependency), the terminal renderer correctly prints FAILED and counts aborted tests as failures in the summary. However, the process exits with code 0.

Root cause: Status::isFailure() did not include Status::Aborted, so the failure flag was never propagated up the aggregation chain:

TestRunner (Aborted result)
  → CaseRunner: isFailure() == false → case stays Passed
    → SuiteRunner: isFailure() == false → suite stays Passed
      → Application: isFailure() == false → overall status stays Passed
        → Run command: isSuccessful() == true → exit 0

The renderer (TerminalLogger) handles this correctly already — it explicitly includes Aborted when computing the failure count for the final banner. The exit code logic does not.

Fix

Add self::Aborted to the isFailure() match in Status.php. This single change cascades through the entire aggregation chain and produces exit code 1 whenever any test is aborted.

Behaviour after fix

Scenario Before After
All tests pass exit 0 exit 0
Some tests fail (assertion) exit 1 exit 1
Some tests aborted (interceptor exception) exit 0 ❌ exit 1 ✅

@viktorprogger viktorprogger requested a review from a team as a code owner July 3, 2026 12:28
@viktorprogger viktorprogger marked this pull request as draft July 3, 2026 12:31
@roxblnfk roxblnfk force-pushed the fix/aborted-status-exit-code branch from 1d5544c to 75cfc85 Compare July 3, 2026 12:50
@roxblnfk

roxblnfk commented Jul 3, 2026

Copy link
Copy Markdown
Member

Thanks for spotting this and pushing a fix! I pushed a revised approach to this branch — sharing the reasoning here.

We agree on the goal: an aborted test (a critical interceptor failure — unknown verdict) must fail the run and exit with code 1, matching what the terminal / JUnit / TeamCity reporters already render.

Why not add Aborted to Status::isFailure(): that predicate is overloaded. Besides driving pass/fail aggregation, it's what RetryPolicyRunInterceptor checks to decide whether to retry a test:

if ($result->status->isFailure()) { /* retry */ }

An aborted test must not be retried — retrying won't fix a broken pipeline, and the verdict is unknown. Adding Aborted to isFailure():

  • makes retry start retrying aborts, breaking the statusTriggersRetry unit test (#[DataSet([Status::Aborted, false])] in plugin/retry/tests/Unit/RetryPolicyRunInterceptorTest.php);
  • also changes DataProvider/Inline/Bench aggregation and JsonReport failure listing, and shifts the public meaning of isFailure() for plugin authors.

What this branch does instead — escalate at the aggregation point, leaving isFailure() untouched:

// CaseRunner::run()
($result->status->isFailure() || $result->status === Status::Aborted)
    and $status = Status::Failed;

An aborted test now fails its case, which propagates case → suite → run → exit code through the existing chain. Retry/repeat semantics stay correct (an abort is still not retried).

Extras included:

  • Aborted gets its own red A glyph in the terminal (verbose + dots) so it reads distinctly from a genuine Error (E).
  • A self-test PipelineFailureStatusTest + a small stub apparatus (an interceptor that throws at a chosen pipeline stage — before/after $next(), at test and case level) that locks in the behaviour for both test-level aborts and case-level failures.

Verified: the new self-test (5/5) plus the retry, repeat and output suites (275/275) are green; retry is not broken.

Note: the PR title/description still mention isFailure() — worth updating to reflect the CaseRunner approach.

@codecov

codecov Bot commented Jul 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 6 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
core/Output/Terminal/Renderer/Formatter.php 0.00% 4 Missing ⚠️
core/Application/Internal/Runner/CaseRunner.php 0.00% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

An interceptor that throws (before or after $next) makes TestRunner mark the
test Status::Aborted. The terminal/JUnit/TeamCity reporters already render an
abort as a failure, yet the process exit code stayed 0: Aborted is not a
Status::isFailure(), so CaseRunner/SuiteRunner never escalated. CaseRunner now
treats an aborted test as a case failure, so the suite/run status and the exit
code agree with what the reporters print.

Kept out of Status::isFailure() on purpose — that predicate also drives retry
(RetryPolicyRunInterceptor), which must not retry an aborted test.

Also give Aborted its own red "A" glyph in the terminal (verbose + dots) so it
reads distinctly from a genuine Error ("E").

Adds a self-test (PipelineFailureStatusTest) plus a small stub apparatus: a
FailPipeline attribute that self-binds a throwing interceptor via
FallbackInterceptor (no plugin needed), covering test-level and case-level
throws before/after $next().

Assisted-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@roxblnfk roxblnfk force-pushed the fix/aborted-status-exit-code branch from 75cfc85 to 9e84713 Compare July 3, 2026 13:05
Enables the sandbox suite in testo.php (skipped under TESTO_CI) and adds a
sandbox file exercising the FailPipeline attribute at every stage — test-level
and case-level throws before/after $next() — for eyeballing the terminal report.

Assisted-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@roxblnfk roxblnfk changed the title fix: include Aborted status in isFailure() so aborted tests exit with code 1 fix(runner): fail the run when a test is aborted by a pipeline error Jul 3, 2026
@roxblnfk roxblnfk marked this pull request as ready for review July 3, 2026 13:21
@roxblnfk roxblnfk merged commit 719e10a into php-testo:1.x Jul 3, 2026
12 of 14 checks passed
@roxblnfk roxblnfk mentioned this pull request Jul 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants