Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHPUnit coverage: Allowed memory size of 536870912 bytes exhausted

Tags:

I am trying to generate code test coverage for my PHP project with PHPUnit and phpdbg using the following command:

phpdbg -dmemory_limit=512M -qrr ./bin/phpunit -c .phpunit.cover.xml

This works perfectly fine:

PHPUnit 6.2.4 by Sebastian Bergmann and contributors.

........                                                            8 / 8 (100%)

Time: 114 ms, Memory: 14.00MB

OK (8 tests, 13 assertions)

Generating code coverage report in HTML format ... done

However, when I use the exact same command in a docker container:

docker run -it --name YM4UPltmiPMjObaVULwsIPIkPL2bGL0T -e USER=sasan -v "/home/sasan/Project/phpredmin:/phpredmin" -w "/phpredmin" --user "1000:www-data" php:7.0-apache phpdbg -dmemory_limit=512M -qrr ./bin/phpunit -c .phpunit.cover.xml

I get the following error:

PHPUnit 6.2.4 by Sebastian Bergmann and contributors.

[PHP Fatal error:  Allowed memory size of 536870912 bytes exhausted (tried to allocate 561514763337856 bytes) in /phpredmin/vendor/phpunit/phpunit/src/Util/GlobalState.php on line 166]

I don't understand why PHPUnit needs to allocate 561514763337856 bytes of memory. I suspect it gets stuck in a loop, but why this does not happen outside of the container? Here is my PHP version on my machine:

PHP 7.0.22-0ubuntu0.17.04.1 (cli) (built: Aug  8 2017 22:03:30) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2017 Zend Technologies
    with Zend OPcache v7.0.22-0ubuntu0.17.04.1, Copyright (c) 1999-2017, by Zend Technologies

And here is the .phpunit.cover.xml file:

<phpunit
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/6.3/phpunit.xsd"
        backupGlobals="false"
        backupStaticAttributes="false"
        bootstrap="vendor/autoload.php"
        cacheTokens="false"
        colors="false"
        convertErrorsToExceptions="true"
        convertNoticesToExceptions="true"
        convertWarningsToExceptions="true"
        processIsolation="false"
        stopOnError="true"
        stopOnFailure="true"
        stopOnIncomplete="false"
        stopOnSkipped="false"
        stopOnRisky="false"
        timeoutForSmallTests="1"
        timeoutForMediumTests="10"
        timeoutForLargeTests="60"
        verbose="false">
    <testsuites>
            <testsuite name="PhpRedmin PHP source">
            <directory>src-test/</directory>
            </testsuite>
    </testsuites>
    <logging>
        <log type="coverage-html" target="cover/" lowUpperBound="35" 
highLowerBound="70"/>
    </logging>
    <filter>
        <whitelist processUncoveredFilesFromWhitelist="true">
            <directory suffix=".php">src-test/</directory>
            <directory suffix=".php">src/</directory>
        </whitelist>
    </filter>
</phpunit>

-- Edit1 --

I found that it has something to do with @runInSeparateProcess. When I remove the test that has @runInSeparateProcess then it starts to work. But still I don't know what is the problem

-- Edit2 --

Also I found out that If I don't mount my code directory in the Docker container everything works fine

like image 207
Sasan Rose Avatar asked Sep 27 '17 12:09

Sasan Rose


2 Answers

Add '-d memory_limit=-1' before your test

if use composer use the below code

./vendor/bin/simple-phpunit -d memory_limit=-1 tests/

if you use phpdbg use the below code

phpdbg -d memory_limit=-1 -qrr vendor/bin/phpunit --coverage-text
like image 161
SHAIK ALAUDDEEN Alihasana Avatar answered Sep 23 '22 18:09

SHAIK ALAUDDEEN Alihasana


When we use @runInSeparateProcess, PHPUnit will attempt to serialize included files, ini settings, global variables, and constants to pass them to the new process. In this case, it looks like PHPUnit encountered a recursive scenario when serializing one of these items, which exhausted the memory available to the PHP process. We need to determine what changed between your local environment and the Docker container.

First, we can try disabling this serialization behavior to verify that we should proceed along this path. Add the following @preserveGlobalState annotation to the test method that fails:

/**
 * @runInSeparateProcess
 * @preserveGlobalState disabled
 */
public function testInSeparateProcess()
{
    // ...
}

If this resolves the problem, or if we get a new error, we can begin looking for differences in the Docker container that may cause the issue. Without more visibility into the code and environment, it's hard to suggest where to start, but here are some ideas:

  • Compare the output of php -i from each environment. Look out for PHP extensions that exist in one, but not the other.
  • Use phpdbg to set a breakpoint and step through the code. We're already using it to generate coverage, but it's also a useful debugging tool. We're looking for the item that causes the infinite recursion. Note that we'll need to set the breakpoint before PHPUnit executes the test case such as in the bootstrap file or PHPUnit source code (line 810 of TestCase may work).
  • When bind-mounting a volume, make sure the www-data user in the container has the same UID as the user that owns the files on the host.
  • Try running the test without the memory limitation. Obviously we can't allocate as much as the error suggests we may need, but the memory limit may mask another underlying problem. We can kill the container if needed.

I couldn't reproduce this issue using a dummy test in a similar environment (as close as I could get—same container with volume), so the code under test may contribute to the problem.

like image 31
Cy Rossignol Avatar answered Sep 19 '22 18:09

Cy Rossignol