GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#52)
by joseph
38:21 queued 34:31
created

AbstractIntegrationTest.php$0 ➔ qaGeneratedCode()   B

Complexity

Conditions 4

Size

Total Lines 67

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 67
rs 8.8076
c 0
b 0
f 0
cc 4

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php declare(strict_types=1);
2
3
namespace EdmondsCommerce\DoctrineStaticMeta;
4
5
use Composer\Autoload\ClassLoader;
6
use Doctrine\ORM\EntityManager;
7
use Doctrine\ORM\EntityManagerInterface;
8
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\CodeHelper;
9
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Command\AbstractCommand;
10
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\AbstractGenerator;
11
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\EntityGenerator;
12
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\FieldGenerator;
13
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\RelationsGenerator;
14
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\NamespaceHelper;
15
use EdmondsCommerce\DoctrineStaticMeta\Schema\Schema;
16
use EdmondsCommerce\PHPQA\Constants;
17
use Overtrue\PHPLint\Linter;
18
use PHPUnit\Framework\TestCase;
19
use Symfony\Component\Filesystem\Filesystem;
20
21
/**
22
 * Class AbstractTest
23
 *
24
 * @package EdmondsCommerce\DoctrineStaticMeta
25
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
26
 */
27
abstract class AbstractIntegrationTest extends TestCase
28
{
29
    public const TEST_TYPE                   = 'integration';
30
    public const VAR_PATH                    = __DIR__.'/../../var/testOutput/';
31
    public const WORK_DIR                    = 'override me';
32
    public const TEST_PROJECT_ROOT_NAMESPACE = 'My\\Test\\Project';
33
34
    /**
35
     * The absolute path to the Entities folder, eg:
36
     * /var/www/vhosts/doctrine-static-meta/var/{testWorkDir}/Entities
37
     *
38
     * @var string
39
     */
40
    protected $entitiesPath = '';
41
42
    /**
43
     * The absolute path to the EntityRelations folder, eg:
44
     * /var/www/vhosts/doctrine-static-meta/var/{testWorkDir}/Entity/Relations
45
     *
46
     * @var string
47
     */
48
    protected $entityRelationsPath = '';
49
50
    /**
51
     * @var Container
52
     */
53
    protected $container;
54
55
    /**
56
     * @var Filesystem
57
     */
58
    protected $filesystem;
59
60
61
    /**
62
     * Prepare working directory, ensure its empty, create entities folder and set up env variables
63
     *
64
     * The order of these actions is critical
65
     */
66
    public function setup()
67
    {
68
        if (false !== stripos(static::WORK_DIR, self::WORK_DIR)) {
69
            throw new \RuntimeException(
70
                "You must set a `public const WORK_DIR=AbstractTest::VAR_PATH.'/'"
71
                .".self::TEST_TYPE.'/folderName/';` in your test class"
72
            );
73
        }
74
        if (false === strpos(static::WORK_DIR, static::TEST_TYPE)) {
75
            throw new \RuntimeException(
76
                'Your WORK_DIR is missing the test type, should look like: '
77
                ."`public const WORK_DIR=AbstractTest::VAR_PATH.'/'"
78
                .".self::TEST_TYPE.'/folderName/';` in your test class"
79
            );
80
        }
81
        $this->entitiesPath = static::WORK_DIR
82
                              .'/'.AbstractCommand::DEFAULT_SRC_SUBFOLDER
83
                              .'/'.AbstractGenerator::ENTITIES_FOLDER_NAME;
84
        $this->getFileSystem()->mkdir($this->entitiesPath);
85
        $this->entitiesPath        = realpath($this->entitiesPath);
86
        $this->entityRelationsPath = static::WORK_DIR
87
                                     .'/'.AbstractCommand::DEFAULT_SRC_SUBFOLDER
88
                                     .'/'.AbstractGenerator::ENTITY_RELATIONS_FOLDER_NAME;
89
        $this->getFileSystem()->mkdir($this->entityRelationsPath);
90
        $this->entityRelationsPath = realpath($this->entityRelationsPath);
91
        $this->setupContainer($this->entitiesPath);
92
        $this->clearWorkDir();
93
        $this->extendAutoloader(
94
            static::TEST_PROJECT_ROOT_NAMESPACE.'\\',
95
            static::WORK_DIR.'/'.AbstractCommand::DEFAULT_SRC_SUBFOLDER
96
        );
97
    }
98
99
    /**
100
     * If PHP loads any files whilst generating, then subsequent changes to those files will not have any effect
101
     *
102
     * To resolve this, we need to clone the copied code into a new namespace before running it
103
     *
104
     * @param string $extra
105
     */
106
    protected function setupCopiedWorkDir(string $extra = 'Copied'): void
107
    {
108
        $copiedWorkDir = rtrim(static::WORK_DIR, '/').$extra.'/';
109
        if (is_dir($copiedWorkDir)) {
110
            exec('rm -rf '.$copiedWorkDir);
111
        }
112
        $this->filesystem->mkdir($copiedWorkDir);
113
        $this->filesystem->mirror(static::WORK_DIR, $copiedWorkDir);
114
        $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($copiedWorkDir));
115
        foreach ($iterator as $info) {
116
            /**
117
             * @var \SplFileInfo $info
118
             */
119
            if (false === $info->isFile()) {
120
                continue;
121
            }
122
            $contents        = file_get_contents($info->getPathname());
123
            $copiedNameSpace = $extra.static::TEST_PROJECT_ROOT_NAMESPACE;
124
            $updated         = \str_replace(
125
                [
126
                    'namespace '.static::TEST_PROJECT_ROOT_NAMESPACE,
127
                    'use '.static::TEST_PROJECT_ROOT_NAMESPACE,
128
                ],
129
                [
130
                    'namespace '.$copiedNameSpace,
131
                    'use '.$copiedNameSpace,
132
                ],
133
                $contents
134
            );
135
            file_put_contents($info->getPathname(), $updated);
136
        }
137
        $this->extendAutoloader(
138
            $extra.static::TEST_PROJECT_ROOT_NAMESPACE.'\\',
139
            $copiedWorkDir.'/'.AbstractCommand::DEFAULT_SRC_SUBFOLDER
140
        );
141
    }
142
143
    /**
144
     * When working with a copied work dir, use this function to translate the FQN of any Entities etc
145
     *
146
     * @param string $fqn
147
     * @param string $extra
148
     *
149
     * @return string
150
     * @throws Exception\DoctrineStaticMetaException
151
     */
152
    protected function getCopiedFqn(string $fqn, string $extra = 'Copied'): string
153
    {
154
        return $this->container
155
            ->get(NamespaceHelper::class)
156
            ->tidy('\\'.$extra.ltrim($fqn, '\\'));
157
    }
158
159
    /**
160
     * @return bool
161
     * @SuppressWarnings(PHPMD.Superglobals)
162
     */
163
    protected function isTravis(): bool
164
    {
165
        return isset($_SERVER['TRAVIS']);
166
    }
167
168
    /**
169
     * @param string $entitiesPath
170
     *
171
     * @throws Exception\ConfigException
172
     * @throws Exception\DoctrineStaticMetaException
173
     * @SuppressWarnings(PHPMD.Superglobals)
174
     * @SuppressWarnings(PHPMD.StaticAccess)
175
     */
176
    protected function setupContainer(string $entitiesPath)
177
    {
178
        SimpleEnv::setEnv(Config::getProjectRootDirectory().'/.env');
179
        $testConfig                                       = $_SERVER;
180
        $testConfig[ConfigInterface::PARAM_ENTITIES_PATH] = $entitiesPath;
181
        $testConfig[ConfigInterface::PARAM_DB_NAME]       .= '_test';
182
        $testConfig[ConfigInterface::PARAM_DEVMODE]       = true;
183
        $this->container                                  = new Container();
184
        $this->container->buildSymfonyContainer($testConfig);
185
    }
186
187
188
    /**
189
     * Accesses the standard Composer autoloader
190
     *
191
     * Extends the class and also providing an entry point for xdebugging
192
     *
193
     * Extends this with a PSR4 root for our WORK_DIR
194
     *
195
     * @param string $namespace
196
     * @param string $path
197
     */
198
    protected function extendAutoloader(string $namespace, string $path)
199
    {
200
        $namespace = rtrim($namespace, '\\').'\\';
201
        $loader    = new class($namespace) extends ClassLoader
202
        {
203
            /**
204
             * @var string
205
             */
206
            protected $namespace;
207
208
            public function __construct(string $namespace)
209
            {
210
                $this->namespace = $namespace;
211
            }
212
213
            public function loadClass($class)
214
            {
215
                if (false === strpos($class, $this->namespace)) {
216
                    return false;
217
                }
218
                $found = parent::loadClass($class);
219
                if (\in_array(gettype($found), ['boolean', 'NULL'], true)) {
220
                    //good spot to set a break point ;)
221
                    return false;
222
                }
223
224
                return true;
225
            }
226
        };
227
        $loader->addPsr4($namespace, $path);
228
        $loader->register();
229
    }
230
231
    protected function clearWorkDir()
232
    {
233
        $this->getFileSystem()->mkdir(static::WORK_DIR);
234
        $this->emptyDirectory(static::WORK_DIR);
235
        if (empty($this->entitiesPath)) {
236
            throw new \RuntimeException('$this->entitiesPath path is empty');
237
        }
238
        $this->getFileSystem()->mkdir($this->entitiesPath);
239
    }
240
241
    protected function getFileSystem(): Filesystem
242
    {
243
        if (null === $this->filesystem) {
244
            $this->filesystem = (null !== $this->container)
245
                ? $this->container->get(Filesystem::class)
246
                : new Filesystem();
247
        }
248
249
        return $this->filesystem;
250
    }
251
252
    protected function emptyDirectory(string $path)
253
    {
254
        $fileSystem = $this->getFileSystem();
255
        $fileSystem->remove($path);
256
        $fileSystem->mkdir($path);
257
    }
258
259
    protected function assertNoMissedReplacements(string $createdFile)
260
    {
261
        $createdFile = $this->getCodeHelper()->resolvePath($createdFile);
262
        $this->assertFileExists($createdFile);
263
        $contents = file_get_contents($createdFile);
264
        $this->assertNotContains(
265
            'template',
266
            $contents,
267
            'Found the word "template" (case insensitive) in the created file '.$createdFile,
268
            true
269
        );
270
    }
271
272
    protected function assertFileContains(string $createdFile, string $needle)
273
    {
274
        $createdFile = $this->getCodeHelper()->resolvePath($createdFile);
275
        $this->assertFileExists($createdFile);
276
        $contents = file_get_contents($createdFile);
277
        $this->assertContains(
278
            $needle,
279
            $contents,
280
            "Missing '$needle' in file '$createdFile'"
281
        );
282
    }
283
284
    protected function getEntityGenerator(): EntityGenerator
285
    {
286
        /**
287
         * @var EntityGenerator $entityGenerator
288
         */
289
        $entityGenerator = $this->container->get(EntityGenerator::class);
290
        $entityGenerator->setPathToProjectRoot(static::WORK_DIR)
291
                        ->setProjectRootNamespace(static::TEST_PROJECT_ROOT_NAMESPACE);
292
293
        return $entityGenerator;
294
    }
295
296
    protected function getRelationsGenerator(): RelationsGenerator
297
    {
298
        /**
299
         * @var RelationsGenerator $relationsGenerator
300
         */
301
        $relationsGenerator = $this->container->get(RelationsGenerator::class);
302
        $relationsGenerator->setPathToProjectRoot(static::WORK_DIR)
303
                           ->setProjectRootNamespace(static::TEST_PROJECT_ROOT_NAMESPACE);
304
305
        return $relationsGenerator;
306
    }
307
308
    protected function getFieldGenerator(): FieldGenerator
309
    {
310
        /**
311
         * @var FieldGenerator $fieldGenerator
312
         */
313
        $fieldGenerator = $this->container->get(FieldGenerator::class);
314
        $fieldGenerator->setPathToProjectRoot(static::WORK_DIR)
315
                       ->setProjectRootNamespace(static::TEST_PROJECT_ROOT_NAMESPACE);
316
317
        return $fieldGenerator;
318
    }
319
320
    /**
321
     * @return EntityManager
322
     * @throws Exception\DoctrineStaticMetaException
323
     */
324
    protected function getEntityManager(): EntityManager
325
    {
326
        return $this->container->get(EntityManagerInterface::class);
327
    }
328
329
    /**
330
     * Run QA tools against the generated code
331
     *
332
     * Can specify a custom namespace root if required
333
     *
334
     * Will run:
335
     *
336
     * - PHP linting
337
     * - PHPStan
338
     *
339
     * @SuppressWarnings(PHPMD.Superglobals)
340
     * @param null|string $namespaceRoot
341
     *
342
     * @return bool
343
     */
344
    public function qaGeneratedCode(?string $namespaceRoot = null): bool
345
    {
346
        if (isset($_SERVER[Constants::QA_QUICK_TESTS_KEY])
347
            && (int)$_SERVER[Constants::QA_QUICK_TESTS_KEY] === Constants::QA_QUICK_TESTS_ENABLED
348
        ) {
349
            return true;
350
        }
351
        //lint
352
        $path       = static::WORK_DIR;
353
        $exclude    = ['vendor'];
354
        $extensions = ['php'];
355
356
        $linter  = new Linter($path, $exclude, $extensions);
357
        $lint    = $linter->lint([], false);
358
        $message = str_replace($path, '', print_r($lint, true));
359
        $this->assertEmpty($lint, "\n\nPHP Syntax Errors in $path\n\n$message\n\n");
360
        $namespaceRoot     = ltrim($namespaceRoot ?? static::TEST_PROJECT_ROOT_NAMESPACE, '\\');
361
        $phpstanNamespace  = $namespaceRoot.'\\\\';
362
        $phpstanFolder     = static::WORK_DIR.'/'.AbstractCommand::DEFAULT_SRC_SUBFOLDER;
363
        $phpstanAutoLoader = '<?php declare(strict_types=1);
364
require __DIR__."/../../../../vendor/autoload.php";
365
366
use Composer\Autoload\ClassLoader;
367
368
$loader = new class extends ClassLoader
369
        {
370
            public function loadClass($class)
371
            {
372
                if (false === strpos($class, "'.$namespaceRoot.'")) {
373
                    return false;
374
                }
375
                $found = parent::loadClass($class);
376
                if (\in_array(gettype($found), [\'boolean\', \'NULL\'], true)) {
377
                    //good spot to set a break point ;)
378
                    return false;
379
                }
380
381
                return true;
382
            }
383
        };
384
        $loader->addPsr4(
385
            "'.$phpstanNamespace.'","'.$phpstanFolder.'"
386
        );
387
        $loader->register();
388
';
389
        file_put_contents(static::WORK_DIR.'/phpstan-autoloader.php', $phpstanAutoLoader);
390
        // A hunch that travis is not liking the no xdebug command
391
        $phpstanCommand = FullProjectBuildFunctionalTest::BASH_PHPNOXDEBUG_FUNCTION
392
                          ."\n\nphpNoXdebug bin/phpstan.phar analyse $path/src -l7 -a "
393
                          .static::WORK_DIR.'/phpstan-autoloader.php 2>&1';
394
        if ($this->isTravis()) {
395
            $phpstanCommand = "bin/phpstan.phar analyse $path/src -l7 -a "
396
                              .static::WORK_DIR.'/phpstan-autoloader.php 2>&1';
397
        }
398
        exec(
399
            $phpstanCommand,
400
            $output,
401
            $exitCode
402
        );
403
        $this->assertEquals(
404
            0,
405
            $exitCode,
406
            'PHPStan errors found in generated code at '.$path
407
            .':'."\n\n".implode("\n", $output));
408
409
410
        return true;
411
    }
412
413
    protected function getSchema(): Schema
414
    {
415
        return $this->container->get(Schema::class);
416
    }
417
418
    protected function getCodeHelper(): CodeHelper
419
    {
420
        return $this->container->get(CodeHelper::class);
421
    }
422
}
423