pestphp / pestphp/pest

[Bug]: `--coverage` console report fails on macOS for large suites: `require` of a >2 GiB `coverage.php` hits the 2 GiB single-syscall limit

Open
#1,789 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
PHP
Stars
11.7k
Forks
538
Avg merge
4d 11h
Merged PRs (30d)
8

Description

What happens

On macOS, running a large suite with --coverage — either vendor/bin/pest --coverage or php artisan test --coverage (via nunomaduro/collision) — fails while building the console coverage report:

Cannot write to /.../coverage.php

…and even if the file is produced, reading it back fails symmetrically.

Root cause

The console report round-trips the merged coverage through a var_export'd PHP file and pulls it back with require:

// vendor/pestphp/pest/src/Support/Coverage.php  (mirror in nunomaduro/collision Coverage::report())
$codeCoverage = require $reportPath;   // coverage.php
unlink($reportPath);

For a large suite that coverage.php exceeds 2 GiB, because it serializes the full per-test coverage map (which test touched which line), not just the aggregate. macOS caps a single read()/write() at 2 GiB (EINVAL at ≥ 0x7fffffff bytes), so:

  • writing the file (file_put_contents of the var_export string) fails once it crosses 2 GiB, and
  • require $reportPath fails the same way — it reads the whole file in one operation.

Crucially, chunking the write into < 2 GiB pieces does not fix it, because the read is an atomic require that can't be chunked. (php-code-coverage 14.x already dropped its own PHP report format, so this var_export/require handoff is now Pest/Collision's own.)

Why the size is avoidable

The same coverage, emitted as a Clover XML report for the same suite, is a few MB. The multi-GB size is purely the per-test granularity of the PHP dump. The console --coverage report only needs aggregate per-file/line coverage — the per-test map is only needed by TIA.

Suggested direction

  • For the console-report handoff, serialize aggregate coverage (orders of magnitude smaller) rather than the full per-test map; or
  • split the handoff into < 2 GiB chunk files and require/merge each, so no single read/write crosses the macOS limit; or
  • hand the report a compact serialization (Clover/XML) instead of a var_export'd PHP file — consistent with php-code-coverage 14.x having removed its PHP format.

TIA's per-test needs (CoverageMerger) can keep their own, chunked serialization independent of the console report.

Workaround (for reference)

We bypass the console report entirely in CI/local: emit --coverage-clover and render a Pest-style per-file "uncovered lines" listing from the XML (a small Artisan command). A few MB, fast, no 2 GiB wall. Happy to share the command if useful.

Environment

  • macOS (2 GiB single-syscall read/write limit)
  • pestphp/pest 5.0.2, nunomaduro/collision 8.9.5, phpunit/php-code-coverage 14.2.3, PHP 8.5
  • Suite: ~10k tests, PCOV coverage; serialized coverage.php > 2 GB

How to Reproduce

  1. On macOS, with a suite whose serialized per-test coverage crosses 2 GiB (we hit it around ~10k tests with broad coverage — the coverage.php handoff carries the full per-test map, not just the aggregate).
  2. Run vendor/bin/pest --coverage (or php artisan test --coverage).
  3. Building the console report fails: Cannot write to /.../coverage.php on the write side, and require $reportPath fails the same way once the file exists.

The trigger is purely file size and independent of the coverage driver (PCOV here): on macOS any valid PHP file ≥ 2 GiB can be neither file_put_contents'd nor required in a single call (EINVAL at ≥ 0x7fffffff bytes), so Coverage::report() breaks the moment coverage.php crosses that line.


Pest Version

5.0.2

PHP Version

8.5.8

Operation System

macOS

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the Coverage.php console-report handoff in Pest and the mirrored Coverage::report() implementation in nunomaduro/collision, focusing on the var_export/file_put_contents and require path described in the issue. Compare the console handoff with the aggregate coverage needs and verify the chosen approach using a macOS suite whose coverage exceeds 2 GiB; done means the console report completes without the single-read/write failure.

Written by the indexing model from the issue text.

Assessment

Tech stack
php
Domain
cli, testing-qa
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.