Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
daniel-sc opened this issue Jan 28, 2023 · 3 comments
Labels
feature/process-isolation Issues related to running tests in separate PHP processes type/bug Something is broken

Comments

@daniel-sc
Copy link

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 daniel-sc added the type/bug Something is broken label Jan 28, 2023
@sebastianbergmann sebastianbergmann added the feature/process-isolation Issues related to running tests in separate PHP processes label Jan 29, 2023
@sebastianbergmann
Copy link
Owner

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.

@daniel-sc
Copy link
Author

@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.

@sebastianbergmann sebastianbergmann changed the title @runInSeparateProcess + @preserveGlobalState disabled fails with unserialize exception when writing to php://stdout @runInSeparateProcess and @preserveGlobalState disabled fails with unserialize() exception when writing to php://stdout Mar 6, 2023
@sebastianbergmann
Copy link
Owner

Also fixed by 3291172, 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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature/process-isolation Issues related to running tests in separate PHP processes type/bug Something is broken
Projects
None yet
Development

No branches or pull requests

2 participants