Skip to content

fix: give the playwright npm server time to shut down browser children#211

Open
jordanmiguel wants to merge 1 commit intopestphp:4.xfrom
jordanmiguel:fix-headed-mode-shutdown-timeout
Open

fix: give the playwright npm server time to shut down browser children#211
jordanmiguel wants to merge 1 commit intopestphp:4.xfrom
jordanmiguel:fix-headed-mode-shutdown-timeout

Conversation

@jordanmiguel
Copy link
Copy Markdown

The bug

When running a suite with --headed, the pest process hangs indefinitely after the "Tests: N passed" summary prints. On CI / in shells with a pipe it keeps the shell prompt busy; locally the user has to hit Ctrl-C to reclaim the terminal.

Repro (macOS + Linux, any project with browser tests):

./vendor/bin/pest tests/Browser --headed
# Summary prints, then hangs.

Headless mode is unaffected.

Root cause

In PlaywrightNpmServer::stop():

$this->systemProcess->stop(
    timeout: 0.1,
    signal: PHP_OS_FAMILY === 'Windows' ? null : SIGTERM,
);

Symfony's Process::stop($timeout) sends SIGTERM, waits $timeout seconds, then escalates to SIGKILL. 100ms is too short for headed Chromium — its graceful close typically takes 700–1200ms because it has to flush IPC to its GPU/renderer children, persist session state, and tear down its windows.

With the old 100ms grace period the server receives SIGKILL while Chromium is still in mid-shutdown; its stdin/stdout pipes stay open on orphan descriptors, so the parent PHP process blocks in proc_close/fclose waiting on them. That is the observable hang.

Fix

Bump the grace period to 2.0 seconds. Plenty of headroom for headed Chromium on every platform tested (macOS 14 Apple Silicon, Ubuntu 22.04 x86_64) and still bounded so a truly stuck Playwright server still gets SIGKILL.

Headless runs complete faster than the grace period, so there is no perceptible overhead — stop() returns as soon as Node exits.

Before / after

# Before
./vendor/bin/pest tests/Browser --headed
# ...Tests: 19 passed (80 assertions) in 48.23s
# hangs indefinitely, Ctrl-C required

# After
./vendor/bin/pest tests/Browser --headed
# ...Tests: 19 passed (80 assertions) in 48.23s
# returns to prompt within ~1s of summary

Notes

  • No behavioural change for passing headless runs.
  • If Chromium genuinely hangs beyond 2s (e.g. deadlock), SIGKILL still fires at T+2s — no regression vs. the current escalate-to-SIGKILL behaviour, just pushed out 1.9s.
  • Did not add a new test because the hang only reproduces with a spawned headed Chromium subprocess, which this repo's own suite does not exercise. Happy to add one (e.g. tests/CoreFeatures/HeadedShutdownTest.php that launches + closes + asserts the parent exits within N seconds) if that fits the project's testing philosophy.

…wser children

Symfony's Process::stop(0.1) sent SIGTERM and escalated to SIGKILL after
100ms. In headed mode, Chromium's graceful close typically needs ~1s; the
SIGKILL interrupted mid-teardown, leaving orphan browser processes and
stalled pipes. The parent pest process then hung on wait() for file
handles that never closed, forcing the user to Ctrl-C.

Bumps the grace period to 2.0s, which consistently clears Chromium even
in headed mode on macOS/Linux. Headless mode was unaffected; headed-mode
runs now exit cleanly.
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.

1 participant