sebastianbergmann / phpunit

The PHP Unit Testing framework.
https://phpunit.de/
BSD 3-Clause "New" or "Revised" License
19.7k stars 2.2k forks source link

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

Open MauricioFauth opened 1 month ago

MauricioFauth commented 1 month ago
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]);

https://github.com/sebastianbergmann/phpunit/blob/0c843b0dfe6781c1f62d2b958766674ab10a4a40/src/Util/PHP/DefaultPhpProcess.php#L115-L125

Replacing both stream_get_contents calls (stdout and stderr) with this old code (without the timeout check) fixed the issue (removed by ccb3b24112153f349cec5c5b78e7186228af7aed): https://github.com/sebastianbergmann/phpunit/blob/dc7281e50005b5e01582a5f857765ecf99a347c6/src/Util/PHP/DefaultPhpProcess.php#L125-L182

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" ] }
staabm commented 1 month ago

great report.

adding back async streams would also help in not blocking the phpunit main process while workers do their thing

staabm commented 1 month ago

I can reproduce in PHPUnit 11.5.x using

IssueTest5993.php

--TEST--
PHPUnit process is blocked when there's a lot of output and a test with separate process
--INI--
error_reporting=-1
display_errors=1
display_startup_errors=1
memory_limit=-1
zend.assertions=1
assert.exception=1
--SKIPIF--
<?php
for ($i = 0; $i < 390; $i++) {
    trigger_error("error $i");
}
?>
--FILE--
<?php

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

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

using one less trigger_error it does not reproduce for me

for ($i = 0; $i < 389; $i++) {
    trigger_error("error $i");
}

when dropping the #[RunInSeparateProcess] attribute it also no longer reproduces

staabm commented 2 weeks ago

Note to self: maybe our problem is similar to https://github.com/php/php-src/pull/16456/files