Skip to content

[Bug] Parallel runner reports false "empty test suite" (exit 1 despite all tests passing) due to boolean-sum merge of numberOfTests #1738

Description

@i-bajrai

When running tests in parallel (--parallel), the merged TestResult can report hasTests() === false even though hundreds of tests ran and passed. With PHPUnit 12.5.23+ (which defaults failOnEmptyTestSuite on), this makes the process exit 1 while the summary still shows e.g. 640 passed.

Environment

  • pest 4.7.4
  • phpunit/phpunit 12.5.30
  • brianium/paratest 7.20.0
  • PHP 8.5

Root cause

In src/Plugins/Parallel/Paratest/WrapperRunner.php:327, worker results are merged with:

(int) $testResultSum->hasTests() + (int) $testResult->hasTests(),

This passes a boolean sum as the TestResult $numberOfTests constructor argument instead of summing the real per-worker test counts. The per-worker numberOfTests is itself unreliable in the streaming worker (it does not reflect the tests actually run — numberOfTestsRun does). So the merged "has tests" flag becomes true only if at least one worker happens to report hasTests() === true.

Observed worker data (single shard, all tests passing)

2 workers:

worker_01  hasTests=false  numberOfTestsRun=401
worker_02  hasTests=false  numberOfTestsRun=239

18 workers (same suite):

worker_05  hasTests=true   numberOfTestsRun=12
worker_07  hasTests=true   numberOfTestsRun=40
... (13 others hasTests=false, e.g. numberOfTestsRun=401/239/43)

So the failure is process-count dependent: with few workers, all of them can report hasTests=false, making the merged result look empty; with many workers, at least one flukes to true and it passes. A 2-core CI runner deterministically fails; a many-core dev machine passes.

Steps to reproduce

  1. A test suite large enough to span multiple parallel workers, all passing.
  2. Constrain to 2 CPU cores (e.g. a 2-core CI runner, or shim the CPU finder).
  3. vendor/bin/pest --parallel → prints N passed, but exits 1.

Expected vs actual

  • Expected: exit 0 when all tests pass and tests were actually run.
  • Actual: exit 1, treated as an empty test suite.

Suggested fix

Sum the real test counts in the merge rather than the boolean flags, e.g.:

ResultReflection::numberOfTests($testResultSum) + ResultReflection::numberOfTests($testResult),

Workaround

Set failOnEmptyTestSuite="false" in phpunit.xml.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions