phpunit icon indicating copy to clipboard operation
phpunit copied to clipboard

`@runInSeparateProcess` and `@preserveGlobalState disabled` fails with `unserialize()` exception when writing to `php://stdout`

Open daniel-sc opened this issue 2 years ago • 2 comments

Q A
PHPUnit version 8.5.32
PHP version 7.2.34-37+ubuntu22.04.1+deb.sury.org+1
Installation Method Composer

Summary

When running a test with the annotations @runInSeparateProcess and @preserveGlobalState disabled that includes (directly or indirectly) some writing to php://stdout the test fails with ErrorException: unserialize(): Error at offset 0 of 5 bytes in /vendor/phpunit/phpunit/src/Util/PHP/AbstractPhpProcess.php:289

Current behavior

The test fails with the following output:


Runtime:       PHP 7.2.34-37+ubuntu22.04.1+deb.sury.org+1

E                                                                   1 / 1 (100%)

Time: 8.65 seconds, Memory: 8.00 MB

There was 1 error:

1) cloudbackup\RateLimitTest::testActualError
PHPUnit\Framework\Exception: TEST

Caused by
ErrorException: unserialize(): Error at offset 0 of 5 bytes in /mnt/c/dev/cloud-backup-for-podio/vendor/phpunit/phpunit/src/Util/PHP/AbstractPhpProcess.php:289
Stack trace:
#0 [internal function]: PHPUnit\Util\PHP\AbstractPhpProcess::PHPUnit\Util\PHP\{closure}(8, 'unserialize(): ...', '/mnt/c/dev/clou...', 289, Array)
#1 /mnt/c/dev/cloud-backup-for-podio/vendor/phpunit/phpunit/src/Util/PHP/AbstractPhpProcess.php(289): unserialize('TEST\n')
#2 /mnt/c/dev/cloud-backup-for-podio/vendor/phpunit/phpunit/src/Util/PHP/AbstractPhpProcess.php(187): PHPUnit\Util\PHP\AbstractPhpProcess->processChildResult(Object(cloudbackup\RateLimitTest), Object(PHPUnit\Framework\TestResult), 'TEST\n', '')
#3 /mnt/c/dev/cloud-backup-for-podio/vendor/phpunit/phpunit/src/Framework/TestCase.php(836): PHPUnit\Util\PHP\AbstractPhpProcess->runTestJob('<?php\nuse PHPUn...', Object(cloudbackup\RateLimitTest), Object(PHPUnit\Framework\TestResult))
#4 /mnt/c/dev/cloud-backup-for-podio/vendor/phpunit/phpunit/src/Framework/TestSuite.php(622): PHPUnit\Framework\TestCase->run(Object(PHPUnit\Framework\TestResult))
#5 /mnt/c/dev/cloud-backup-for-podio/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(647): PHPUnit\Framework\TestSuite->run(Object(PHPUnit\Framework\TestResult))
#6 /mnt/c/dev/cloud-backup-for-podio/vendor/phpunit/phpunit/src/TextUI/Command.php(235): PHPUnit\TextUI\TestRunner->doRun(Object(PHPUnit\Framework\TestSuite), Array, Array, true)      
#7 /mnt/c/dev/cloud-backup-for-podio/vendor/phpunit/phpunit/src/TextUI/Command.php(194): PHPUnit\TextUI\Command->run(Array, true)
#8 /mnt/c/dev/cloud-backup-for-podio/vendor/phpunit/phpunit/phpunit(98): PHPUnit\TextUI\Command::main()
#9 {main}

ERRORS!
Tests: 1, Assertions: 0, Errors: 1.

How to reproduce

/**
 * @runInSeparateProcess
 * @preserveGlobalState disabled
 */
public function testActualError()
{
    if ($fd = fopen('php://stdout', "a")) {
        fwrite($fd, "TEST\n");
        fclose($fd);
    }
    $this->assertTrue(true);
}

Expected behavior

The test should not error.

composer info

datadog/php-datadogstatsd          1.3.0              This is an extremely simple PHP datadogstatsd client
doctrine/annotations               1.14.2             Docblock Annotations Parser
doctrine/cache                     1.13.0             PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, ...    
doctrine/deprecations              v1.0.0             A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selec...    
doctrine/instantiator              1.5.0              A small, lightweight utility to instantiate objects in PHP without invoking their constructors
doctrine/lexer                     2.1.0              PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.
firebase/php-jwt                   v6.3.2             A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.
flowjs/flow-php-server             v1.2.0             PHP library for handling chunk uploads. Works with flow.js html5 file uploads.
goaop/framework                    2.3.4              Framework for aspect-oriented programming in PHP.
goaop/parser-reflection            2.1.3              Provides reflection information, based on raw source
google/apiclient                   v2.13.0            Client library for Google APIs
google/apiclient-services          v0.284.0           Client library for Google APIs
google/auth                        v1.24.0            Google Auth Library for PHP
google/cloud-core                  v1.48.1            Google Cloud PHP shared dependency, providing functionality useful to all components.
google/cloud-logging               v1.24.10           Stackdriver Logging Client for PHP
google/cloud-storage               v1.30.1            Cloud Storage Client for PHP
google/common-protos               v3.2.0             Google API Common Protos for PHP
google/crc32                       v0.1.0             Various CRC32 implementations
google/gax                         v1.18.2            Google API Core for PHP
google/grpc-gcp                    v0.2.1             gRPC GCP library for channel management
google/longrunning                 v0.2.2             Google LongRunning Client for PHP
google/protobuf                    v3.21.12           proto library for PHP
grpc/grpc                          1.42.0             gRPC library for PHP
guzzlehttp/guzzle                  6.5.8              Guzzle is a PHP HTTP client library
guzzlehttp/promises                1.5.2              Guzzle promises library
guzzlehttp/psr7                    1.9.0              PSR-7 message implementation that also provides common utility methods
jakubledl/dissect                  v1.0.1             Lexing and parsing in pure PHP
jean85/pretty-package-versions     2.0.5              A library to get pretty versions strings of installed dependencies
mikecao/flight                     v1.3.9             Flight is a fast, simple, extensible framework for PHP. Flight enables you to quickly and easily build RESTful web applicat...    
mongodb/mongodb                    1.9.0              MongoDB driver library
monolog/monolog                    2.8.0              Sends your logs to files, sockets, inboxes, databases and various web services
myclabs/deep-copy                  1.11.0             Create deep copies (clones) of your objects
nikic/php-parser                   v4.6.0             A PHP parser written in PHP
paragonie/constant_time_encoding   v2.6.3             Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)
paragonie/random_compat            v9.99.100          PHP 5.x polyfill for random_bytes() and random_int() from PHP 7
phar-io/manifest                   2.0.3              Component for reading phar.io manifest information from a PHP Archive (PHAR)
phar-io/version                    3.2.1              Library for handling version information and constraints
phpmailer/phpmailer                v5.2.28            PHPMailer is a full-featured email creation and transfer class for PHP
phpseclib/phpseclib                3.0.18             PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.
phpstan/phpstan                    1.9.14             PHPStan - PHP Static Analysis Tool
phpunit/php-code-coverage          7.0.15             Library that provides collection, processing, and rendering functionality for PHP code coverage information.
phpunit/php-file-iterator          2.0.5              FilterIterator implementation that filters files based on a list of suffixes.
phpunit/php-text-template          1.2.1              Simple template engine.
phpunit/php-timer                  2.1.3              Utility class for timing
phpunit/php-token-stream           3.1.3              Wrapper around PHP's tokenizer extension.
phpunit/phpunit                    8.5.32             The PHP Unit Testing framework.
sebastian/type                     1.1.4              Collection of value objects that represent the types of the PHP type system
sebastian/version                  2.0.1              Library that helps with managing the version number of Git-hosted PHP projects
stripe/stripe-php                  v7.0.2             Stripe PHP Library
symfony/deprecation-contracts      v2.5.2             A generic function and convention to trigger deprecation notices
symfony/finder                     v5.4.19            Finds files and directories via an intuitive fluent interface
symfony/polyfill-intl-idn          v1.27.0            Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions
symfony/polyfill-intl-normalizer   v1.27.0            Symfony polyfill for intl's Normalizer class and related functions
symfony/polyfill-php72             v1.27.0            Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions
symfony/polyfill-php80             v1.27.0            Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions
theseer/tokenizer                  1.2.1              A small library for converting tokenized PHP source code into XML and potentially other formats
twig/twig                          v1.32.0            Twig, the flexible, fast, and secure template language for PHP

daniel-sc avatar Jan 28 '23 21:01 daniel-sc

php://stdout does not obey output buffering. Output made in the child process breaks unserialization of child process results in the parent process. We work around this limitation (https://github.com/sebastianbergmann/phpunit/blob/8.5/src/Util/PHP/Template/TestCaseMethod.tpl#L5, https://github.com/sebastianbergmann/phpunit/blob/8.5/src/Util/PHP/Template/TestCaseMethod.tpl#L19, https://github.com/sebastianbergmann/phpunit/blob/8.5/src/Util/PHP/Template/TestCaseMethod.tpl#L65), but this seems to be not enough for your case.

sebastianbergmann avatar Jan 29 '23 06:01 sebastianbergmann

@sebastianbergmann thanks for lookin into this. I probably do not fully understand the technical background.

If this is not feasible to solve, maybe some documentation on https://phpunit.readthedocs.io/en/8.5/annotations.html#runinseparateprocess would help?

During my research I found some issue reports that were inconclusive, but might well be caused by the same problem: #3141, #4443, #1149. My guess is, that this often surfaces only depending on logging configuration and/or error output.

daniel-sc avatar Jan 29 '23 11:01 daniel-sc

Also fixed by 3291172e198f044a922af8036378719f71267a51, which will soon be released as part of PHPUnit 8.5.34, PHPUnit 9.6.13, and PHPUnit 10.3.5.

Issue5151Test.php

<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;

final class Issue5151Test extends TestCase
{
    /**
     * @runInSeparateProcess
     * @preserveGlobalState disabled
     */
    public function testActualError()
    {
        if ($fd = fopen('php://stdout', 'a')) {
            fwrite($fd, "test\n");
            fclose($fd);
        }

        $this->assertTrue(true);
    }
}

PHPUnit 10.3.4

PHPUnit 10.3.4 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.2.10

E                                                                   1 / 1 (100%)

Time: 00:00.065, Memory: 8.00 MB

There was 1 error:

1) Issue5151Test::testActualError
PHPUnit\Framework\Exception: test

Caused by
ErrorException: unserialize(): Error at offset 0 of 5 bytes

ERRORS!
Tests: 1, Assertions: 0, Errors: 1.

PHPUnit 10.3.5-dev

PHPUnit 10.3.4-14-g6d0cb2dbb2 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.2.10

.                                                                   1 / 1 (100%)

Time: 00:00.060, Memory: 8.00 MB

OK (1 test, 1 assertion)

sebastianbergmann avatar Sep 17 '23 12:09 sebastianbergmann