Sasan Rose
Sasan Rose

Reputation: 633

PHPUnit coverage: Allowed memory size of 536870912 bytes exhausted

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

Upvotes: 28

Views: 18108

Answers (4)

Unknown
Unknown

Reputation: 436

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

Upvotes: 16

Vladimir Bashkirtsev
Vladimir Bashkirtsev

Reputation: 1354

This is phpdbg bug. Note how much memory it tries to allocate? It is insane. I tried hard to figure out where the bug exactly: if test script full name (directory + script file name) exceeds certain size then it overflows the stack (yeah, stackoverflow ;) and overwrites integer which specifies how much memory must be allocated. Then normal error handling kicks in: not enough memory to allocate such insane amount. So this is for Krakjoe to fix. Or may be I will make a patch for it in my spare time. If you need to have resolution right now: make sure that directory path your script is in is short.

Upvotes: 0

Cy Rossignol
Cy Rossignol

Reputation: 16867

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.

Upvotes: 7

Asha
Asha

Reputation: 367

Your mounted folder probably has all vendors and cache files. Try mounting only the source folder.

Upvotes: 2

Related Questions