Я пытаюсь сгенерировать покрытие тестового кода для моего проекта PHP с помощью PHPUnit и phpdbg, используя следующую команду:
phpdbg -dmemory_limit=512M -qrr ./bin/phpunit -c .phpunit.cover.xml
Это прекрасно работает:
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
Однако, когда я использую ту же самую команду в контейнере Docker:
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
Я получаю следующую ошибку:
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]
Я не понимаю, почему PHPUnit должен выделять 561514763337856 байт памяти. Я подозреваю, что это застревает в цикле, но почему это не происходит вне контейнера? Вот моя версия PHP на моей машине:
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
А вот и файл .phpunit.cover.xml:
<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 —
Я обнаружил, что это как-то связано с @runInSeparateProcess. Когда я удаляю тест, который имеет @runInSeparateProcess, он начинает работать. Но все же я не знаю, в чем проблема
— Edit2 —
Также я обнаружил, что если я не смонтирую директорию с кодом в контейнере Docker, все работает нормально
В вашей смонтированной папке, вероятно, есть все поставщики и файлы кэша. Попробуйте подключить только исходную папку.
Когда мы используем @runInSeparateProcess
, PHPUnit будет пытаться сериализовать включенные файлы, настройки ini, глобальные переменные и константы, чтобы передать их новому процессу. В этом случае, похоже, что PHPUnit столкнулся с рекурсивным сценарием при сериализации одного из этих элементов, который исчерпал память, доступную для процесса PHP. Нам нужно определить, что изменилось между вашей локальной средой и контейнером Docker.
Во-первых, мы можем попытаться отключить это поведение сериализации, чтобы убедиться, что мы должны идти по этому пути. Добавьте следующее @preserveGlobalState
аннотация к методу теста, который не проходит:
/**
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
public function testInSeparateProcess()
{
// ...
}
Если это решит проблему или мы получим новую ошибку, мы можем начать искать различия в контейнере Docker, которые могут вызвать проблему. Без большей наглядности в код и среду сложно предложить, с чего начать, но вот несколько идей:
php -i
из каждой среды. Ищите расширения PHP, которые существуют в одном, но не в другом. phpdbg
установить точку останова и пройти через код. Мы уже используем его для создания покрытия, но это также полезный инструмент отладки. Мы ищем элемент, который вызывает бесконечную рекурсию. Обратите внимание, что нам нужно установить точку останова до PHPUnit выполняет контрольный пример, такой как в файле начальной загрузки или в исходном коде PHPUnit (строка 810 TestCase
может работать).www-data
Пользователь в контейнере имеет тот же UID, что и пользователь, которому принадлежат файлы на хосте. Я не мог воспроизвести эту проблему, используя фиктивный тест в аналогичной среде (настолько близко, насколько я мог бы — тот же контейнер с объемом), поэтому тестируемый код может внести свой вклад в проблему.
Это ошибка phpdbg. Обратите внимание, сколько памяти он пытается выделить? Это безумие Я очень старался выяснить, где именно ошибка: если полное имя тестового сценария (каталог + имя файла сценария) превышает определенный размер, то оно переполняет стек (да, stackoverflow;) и перезаписывает целое число, которое указывает, какой объем памяти должен быть выделен. Затем начинается обычная обработка ошибок: недостаточно памяти для выделения такого безумного количества. Так что это для Krakjoe исправить. Или, может быть, я сделаю патч для него в свободное время. Если вам нужно иметь разрешение прямо сейчас: убедитесь, что путь к каталогу, в котором находится ваш скрипт, короткий.