web-dev-qa-db-fra.com

Couverture PHPUnit: La mémoire autorisée est de 536870912 octets épuisés

J'essaie de générer une couverture de test de code pour mon projet PHP avec PHPUnit et phpdbg à l'aide de la commande suivante:

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

Cela fonctionne parfaitement bien:

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

Cependant, lorsque j'utilise exactement la même commande dans un conteneur de menu fixe:

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

Je reçois l'erreur suivante:

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]

Je ne comprends pas pourquoi PHPUnit doit allouer 561514763337856 octets de mémoire. Je soupçonne que cela reste bloqué dans une boucle, mais pourquoi cela ne se produit-il pas en dehors du conteneur? Voici ma version PHP sur ma 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

Et voici le fichier .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 -

J'ai trouvé que cela avait quelque chose à voir avec @runInSeparateProcess. Lorsque je supprime le test qui a @runInSeparateProcess, il commence à fonctionner. Mais je ne sais toujours pas quel est le problème

- Edit2 -

De plus, j'ai découvert que si je ne monte pas mon répertoire de code dans le conteneur Docker, tout fonctionne correctement.

16
Sasan Rose

Votre dossier monté contient probablement tous les fournisseurs et les fichiers de cache. Essayez de monter uniquement le dossier source.

2
Asha

Lorsque nous utilisons @runInSeparateProcess, PHPUnit tentera de sérialiser les fichiers inclus, les paramètres ini, les variables globales et les constantes pour les transmettre au nouveau processus. Dans ce cas, il semblerait que PHPUnit ait rencontré un scénario récursif lors de la sérialisation de l'un de ces éléments, ce qui a épuisé la mémoire disponible pour le processus PHP. Nous devons déterminer ce qui a changé entre votre environnement local et le conteneur Docker. 

Tout d'abord, nous pouvons essayer de désactiver ce comportement de sérialisation pour vérifier que nous devons suivre ce chemin. Ajoutez l'annotation @preserveGlobalState suivante à la méthode de test qui échoue: 

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

Si cela résout le problème ou si nous obtenons une nouvelle erreur, nous pouvons commencer à rechercher des différences dans le conteneur Docker pouvant être à l'origine du problème. Sans plus de visibilité sur le code et l'environnement, il est difficile de dire par où commencer, mais voici quelques idées: 

  • Comparez la sortie de php -i de chaque environnement. Recherchez les extensions PHP qui existent dans l’une, mais pas l’autre. 
  • Utilisez phpdbg pour définir un point d'arrêt et parcourir le code. Nous l'utilisons déjà pour générer une couverture, mais c'est aussi un outil de débogage utile. Nous recherchons l'élément qui provoque la récursion infinie. Notez que nous devrons définir le point d'arrêt avant PHPUnit exécute le cas de test tel que dans le fichier d'amorçage ou le code source PHPUnit (la ligne 810 de TestCase peut fonctionner).
  • Lorsque vous montez un volume sur une liaison, assurez-vous que l'utilisateur www-data du conteneur a le même UID que l'utilisateur qui possède les fichiers sur l'hôte. 
  • Essayez d'exécuter le test sans limitation de mémoire. Évidemment, nous ne pouvons pas allouer autant que l'erreur suggère, nous pourrions en avoir besoin, mais la limite de mémoire peut masquer un autre problème sous-jacent. Nous pouvons tuer le conteneur si nécessaire. 

Je ne pouvais pas reproduire ce problème à l'aide d'un test factice dans un environnement similaire (aussi proche que possible - même conteneur avec volume), le code sous test peut donc contribuer au problème. 

2
Cy Rossignol

C'est un bogue phpdbg. Notez combien de mémoire il essaie d'allouer? C'est fou. Je me suis efforcé de déterminer exactement où se trouvait le bogue: si le nom complet du script de test (répertoire + nom du fichier de script) dépasse une certaine taille, il déborde de la pile (yeah, stackoverflow;) et écrase le nombre entier spécifiant le volume de mémoire alloué. Ensuite, la gestion normale des erreurs se déclenche: pas assez de mémoire pour allouer une telle quantité insensée. Donc, c'est à Krakjoe de le réparer. Ou peut-être que je ferai un patch pour cela pendant mon temps libre. Si vous avez besoin d'une résolution immédiate: assurez-vous que le chemin du répertoire dans lequel se trouve votre script est court.

0