Skip to content

PHPUnit process is blocked when there's a lot of output and a test with RunInSeparateProcess #5993

Open
@MauricioFauth

Description

Q A
PHPUnit version 10.5.36
PHP version 8.3.12
Installation Method Composer
OS Debian testing with deb.sury.org repository

Summary

When there's a lot of errors triggered before running PHPUnit, for example when a installed package triggers a lot of deprecation messages when being auto loaded, and there's a test that runs in a separate process, PHPUnit will hang indefinitely.

Current behavior

The process blocks here when reading stdout:

$stdout = stream_get_contents($pipes[1]);

if (isset($pipes[1])) {
$stdout = stream_get_contents($pipes[1]);
fclose($pipes[1]);
}
if (isset($pipes[2])) {
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[2]);
}

Replacing both stream_get_contents calls (stdout and stderr) with this old code (without the timeout check) fixed the issue (removed by ccb3b24):

unset($pipes[0]);
while (true) {
$r = $pipes;
$w = null;
$e = null;
$n = @stream_select($r, $w, $e, $this->timeout);
if ($n === false) {
break;
}
if ($n === 0) {
proc_terminate($process, 9);
throw new PhpProcessException(
sprintf(
'Job execution aborted after %d seconds',
$this->timeout,
),
);
}
if ($n > 0) {
foreach ($r as $pipe) {
$pipeOffset = 0;
foreach ($pipes as $i => $origPipe) {
if ($pipe === $origPipe) {
$pipeOffset = $i;
break;
}
}
if (!$pipeOffset) {
break;
}
$line = fread($pipe, 8192);
if ($line === '' || $line === false) {
fclose($pipes[$pipeOffset]);
unset($pipes[$pipeOffset]);
} elseif ($pipeOffset === 1) {
$stdout .= $line;
} else {
$stderr .= $line;
}
}
if (empty($pipes)) {
break;
}
}
}

How to reproduce

IssueTest.php:

use PHPUnit\Framework\Attributes\RunInSeparateProcess;
use PHPUnit\Framework\TestCase;

final class IssueTest extends TestCase
{
    #[RunInSeparateProcess]
    public function testOne(): void
    {
        $this->assertTrue(true);
    }
}

file_that_trigger_errors.php:

<?php
trigger_error("error 1");
trigger_error("error 2");
trigger_error("error 3");
// ...
trigger_error("error 9997");
trigger_error("error 9998");
trigger_error("error 9999");

php.ini:

error_reporting=-1
display_errors=1
display_startup_errors=1
memory_limit=-1
zend.assertions=1
assert.exception=1

composer.json:

    "autoload-dev": { "files": [ "file_that_trigger_errors.php" ] }

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    type/bugSomething is broken

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions