Passed
Push — master ( 7683fb...3223bc )
by Alexis
03:55
created

Test/WebTestCase.php (1 issue)

Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/*
4
 * This file is part of the Liip/FunctionalTestBundle
5
 *
6
 * (c) Lukas Kahwe Smith <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace Liip\FunctionalTestBundle\Test;
13
14
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase as BaseWebTestCase;
15
use Symfony\Bundle\FrameworkBundle\Console\Application;
16
use Symfony\Bundle\FrameworkBundle\Client;
17
use Symfony\Component\Console\Input\ArrayInput;
18
use Symfony\Component\Console\Output\StreamOutput;
19
use Symfony\Component\DomCrawler\Crawler;
20
use Symfony\Component\BrowserKit\Cookie;
21
use Symfony\Component\HttpKernel\Kernel;
22
use Symfony\Component\HttpFoundation\Response;
23
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
24
use Symfony\Component\Security\Core\User\UserInterface;
25
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
26
use Symfony\Component\DependencyInjection\ContainerInterface;
27
use Symfony\Component\HttpFoundation\Session\Session;
28
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
29
use Symfony\Bridge\Doctrine\ManagerRegistry;
30
use Symfony\Bundle\DoctrineFixturesBundle\Common\DataFixtures\Loader;
31
use Doctrine\Common\Persistence\ObjectManager;
32
use Doctrine\Common\DataFixtures\DependentFixtureInterface;
33
use Doctrine\Common\DataFixtures\Executor\AbstractExecutor;
34
use Doctrine\Common\DataFixtures\ProxyReferenceRepository;
35
use Doctrine\DBAL\Driver\PDOSqlite\Driver as SqliteDriver;
36
use Doctrine\DBAL\Platforms\MySqlPlatform;
37
use Doctrine\ORM\EntityManager;
38
use Doctrine\ORM\Tools\SchemaTool;
39
use Nelmio\Alice\Fixtures;
40
use Liip\FunctionalTestBundle\Utils\HttpAssertions;
41
42
/**
43
 * @author Lea Haensenberger
44
 * @author Lukas Kahwe Smith <[email protected]>
45
 * @author Benjamin Eberlei <[email protected]>
46
 */
47
abstract class WebTestCase extends BaseWebTestCase
48
{
49
    protected $environment = 'test';
50
51
    protected $containers;
52
53
    protected $kernelDir;
54
55
    // 5 * 1024 * 1024 KB
56
    protected $maxMemory = 5242880;
57
58
    // RUN COMMAND
59
    protected $verbosityLevel;
60
61
    protected $decorated;
62
63
    /**
64
     * @var array
65
     */
66
    private $firewallLogins = array();
67
68
    /**
69
     * @var array
70
     */
71
    private $excludedDoctrineTables = array();
72
73
    /**
74
     * @var array
75
     */
76
    private static $cachedMetadatas = array();
77
78
    protected static function getKernelClass()
79
    {
80
        $dir = isset($_SERVER['KERNEL_DIR']) ? $_SERVER['KERNEL_DIR'] : static::getPhpUnitXmlDir();
81
82
        list($appname) = explode('\\', get_called_class());
83
84
        $class = $appname.'Kernel';
85
        $file = $dir.'/'.strtolower($appname).'/'.$class.'.php';
86
        if (!file_exists($file)) {
87
            return parent::getKernelClass();
88
        }
89
        require_once $file;
90
91
        return $class;
92
    }
93
94
    /**
95
     * Creates a mock object of a service identified by its id.
96
     *
97
     * @param string $id
98
     *
99
     * @return \PHPUnit_Framework_MockObject_MockBuilder
100
     */
101
    protected function getServiceMockBuilder($id)
102
    {
103
        $service = $this->getContainer()->get($id);
104
        $class = get_class($service);
105
106
        return $this->getMockBuilder($class)->disableOriginalConstructor();
107
    }
108
109
    /**
110
     * Builds up the environment to run the given command.
111
     *
112
     * @param string $name
113
     * @param array  $params
114
     * @param bool   $reuseKernel
115
     *
116
     * @return string
117
     */
118 12
    protected function runCommand($name, array $params = array(), $reuseKernel = false)
119
    {
120 12
        array_unshift($params, $name);
121
122 12
        if (!$reuseKernel) {
123 12
            if (null !== static::$kernel) {
124 9
                static::$kernel->shutdown();
125 9
            }
126
127 12
            $kernel = static::$kernel = $this->createKernel(array('environment' => $this->environment));
128 12
            $kernel->boot();
129 12
        } else {
130 2
            $kernel = $this->getContainer()->get('kernel');
131
        }
132
133 12
        $application = new Application($kernel);
134 12
        $application->setAutoExit(false);
135
136 12
        $input = new ArrayInput($params);
137 12
        $input->setInteractive(false);
138
139 12
        $fp = fopen('php://temp/maxmemory:'.$this->maxMemory, 'r+');
140 12
        $verbosityLevel = $this->getVerbosityLevel();
141
142 11
        $this->setVerbosityLevelEnv($verbosityLevel);
143 11
        $output = new StreamOutput($fp, $verbosityLevel, $this->getDecorated());
144
145 11
        $application->run($input, $output);
146
147 11
        rewind($fp);
148
149 11
        return stream_get_contents($fp);
150
    }
151
152
    /**
153
     * Retrieves the output verbosity level.
154
     *
155
     * @see \Symfony\Component\Console\Output\OutputInterface for available levels
156
     *
157
     * @return int
158
     *
159
     * @throws \OutOfBoundsException If the set value isn't accepted
160
     */
161 12
    protected function getVerbosityLevel()
162
    {
163
        // If `null`, is not yet set
164 12
        if (null === $this->verbosityLevel) {
165
            // Set the global verbosity level that is set as NORMAL by the TreeBuilder in Configuration
166 6
            $level = strtoupper($this->getContainer()->getParameter('liip_functional_test.command_verbosity'));
167 6
            $verbosity = '\Symfony\Component\Console\Output\StreamOutput::VERBOSITY_'.$level;
168
169 6
            $this->verbosityLevel = constant($verbosity);
170 6
        }
171
172
        // If string, it is set by the developer, so check that the value is an accepted one
173 12
        if (is_string($this->verbosityLevel)) {
174 6
            $level = strtoupper($this->verbosityLevel);
175 6
            $verbosity = '\Symfony\Component\Console\Output\StreamOutput::VERBOSITY_'.$level;
176
177 6
            if (!defined($verbosity)) {
178 1
                throw new \OutOfBoundsException(
179 1
                    sprintf('The set value "%s" for verbosityLevel is not valid. Accepted are: "quiet", "normal", "verbose", "very_verbose" and "debug".', $level)
180 1
                );
181
            }
182
183 5
            $this->verbosityLevel = constant($verbosity);
184 5
        }
185
186 11
        return $this->verbosityLevel;
187
    }
188
189 6
    public function setVerbosityLevel($level)
190
    {
191 6
        $this->verbosityLevel = $level;
192 6
    }
193
194
    /**
195
     * Set verbosity for Symfony 3.4+.
196
     *
197
     * @see https://github.com/symfony/symfony/pull/24425
198
     *
199
     * @param $level
200
     */
201 11
    private function setVerbosityLevelEnv($level)
202
    {
203 11
        putenv('SHELL_VERBOSITY='.$level);
204 11
    }
205
206
    /**
207
     * Retrieves the flag indicating if the output should be decorated or not.
208
     *
209
     * @return bool
210
     */
211 11
    protected function getDecorated()
212
    {
213 11
        if (null === $this->decorated) {
214
            // Set the global decoration flag that is set to `true` by the TreeBuilder in Configuration
215 5
            $this->decorated = $this->getContainer()->getParameter('liip_functional_test.command_decoration');
216 5
        }
217
218
        // Check the local decorated flag
219 11
        if (false === is_bool($this->decorated)) {
220
            throw new \OutOfBoundsException(
221
                sprintf('`WebTestCase::decorated` has to be `bool`. "%s" given.', gettype($this->decorated))
222
            );
223
        }
224
225 11
        return $this->decorated;
226
    }
227
228 6
    public function isDecorated($decorated)
229
    {
230 6
        $this->decorated = $decorated;
231 6
    }
232
233
    /**
234
     * Get an instance of the dependency injection container.
235
     * (this creates a kernel *without* parameters).
236
     *
237
     * @return ContainerInterface
238
     */
239 50
    protected function getContainer()
240
    {
241 50
        if (!empty($this->kernelDir)) {
242
            $tmpKernelDir = isset($_SERVER['KERNEL_DIR']) ? $_SERVER['KERNEL_DIR'] : null;
243
            $_SERVER['KERNEL_DIR'] = getcwd().$this->kernelDir;
244
        }
245
246 50
        $cacheKey = $this->kernelDir.'|'.$this->environment;
247 50
        if (empty($this->containers[$cacheKey])) {
248
            $options = array(
249 49
                'environment' => $this->environment,
250 49
            );
251 49
            $kernel = $this->createKernel($options);
252 49
            $kernel->boot();
253
254 49
            $this->containers[$cacheKey] = $kernel->getContainer();
255 49
        }
256
257 50
        if (isset($tmpKernelDir)) {
258
            $_SERVER['KERNEL_DIR'] = $tmpKernelDir;
259
        }
260
261 50
        return $this->containers[$cacheKey];
262
    }
263
264
    /**
265
     * This function finds the time when the data blocks of a class definition
266
     * file were being written to, that is, the time when the content of the
267
     * file was changed.
268
     *
269
     * @param string $class The fully qualified class name of the fixture class to
270
     *                      check modification date on
271
     *
272
     * @return \DateTime|null
273
     */
274 3
    protected function getFixtureLastModified($class)
275
    {
276 3
        $lastModifiedDateTime = null;
277
278 3
        $reflClass = new \ReflectionClass($class);
279 3
        $classFileName = $reflClass->getFileName();
280
281 3
        if (file_exists($classFileName)) {
282 3
            $lastModifiedDateTime = new \DateTime();
283 3
            $lastModifiedDateTime->setTimestamp(filemtime($classFileName));
284 3
        }
285
286 3
        return $lastModifiedDateTime;
287
    }
288
289
    /**
290
     * Determine if the Fixtures that define a database backup have been
291
     * modified since the backup was made.
292
     *
293
     * @param array  $classNames The fixture classnames to check
294
     * @param string $backup     The fixture backup SQLite database file path
295
     *
296
     * @return bool TRUE if the backup was made since the modifications to the
297
     *              fixtures; FALSE otherwise
298
     */
299 7
    protected function isBackupUpToDate(array $classNames, $backup)
300
    {
301 7
        $backupLastModifiedDateTime = new \DateTime();
302 7
        $backupLastModifiedDateTime->setTimestamp(filemtime($backup));
303
304
        /** @var \Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader $loader */
305 7
        $loader = $this->getFixtureLoader($this->getContainer(), $classNames);
306
307
        // Use loader in order to fetch all the dependencies fixtures.
308 7
        foreach ($loader->getFixtures() as $className) {
309 3
            $fixtureLastModifiedDateTime = $this->getFixtureLastModified($className);
310 3
            if ($backupLastModifiedDateTime < $fixtureLastModifiedDateTime) {
311 1
                return false;
312
            }
313 7
        }
314
315 7
        return true;
316
    }
317
318
    /**
319
     * Set the database to the provided fixtures.
320
     *
321
     * Drops the current database and then loads fixtures using the specified
322
     * classes. The parameter is a list of fully qualified class names of
323
     * classes that implement Doctrine\Common\DataFixtures\FixtureInterface
324
     * so that they can be loaded by the DataFixtures Loader::addFixture
325
     *
326
     * When using SQLite this method will automatically make a copy of the
327
     * loaded schema and fixtures which will be restored automatically in
328
     * case the same fixture classes are to be loaded again. Caveat: changes
329
     * to references and/or identities may go undetected.
330
     *
331
     * Depends on the doctrine data-fixtures library being available in the
332
     * class path.
333
     *
334
     * @param array  $classNames   List of fully qualified class names of fixtures to load
335
     * @param string $omName       The name of object manager to use
336
     * @param string $registryName The service id of manager registry to use
337
     * @param int    $purgeMode    Sets the ORM purge mode
338
     *
339
     * @return null|AbstractExecutor
340
     */
341 36
    protected function loadFixtures(array $classNames, $omName = null, $registryName = 'doctrine', $purgeMode = null)
342
    {
343 36
        $container = $this->getContainer();
344
        /** @var ManagerRegistry $registry */
345 36
        $registry = $container->get($registryName);
346
        /** @var ObjectManager $om */
347 36
        $om = $registry->getManager($omName);
348 36
        $type = $registry->getName();
349
350 36
        $executorClass = 'PHPCR' === $type && class_exists('Doctrine\Bundle\PHPCRBundle\DataFixtures\PHPCRExecutor')
351 36
            ? 'Doctrine\Bundle\PHPCRBundle\DataFixtures\PHPCRExecutor'
352 36
            : 'Doctrine\\Common\\DataFixtures\\Executor\\'.$type.'Executor';
353 36
        $referenceRepository = new ProxyReferenceRepository($om);
354 36
        $cacheDriver = $om->getMetadataFactory()->getCacheDriver();
355
356 36
        if ($cacheDriver) {
357 36
            $cacheDriver->deleteAll();
358 36
        }
359
360 36
        if ('ORM' === $type) {
361 35
            $connection = $om->getConnection();
362 35
            if ($connection->getDriver() instanceof SqliteDriver) {
363 30
                $params = $connection->getParams();
364 30
                if (isset($params['master'])) {
365
                    $params = $params['master'];
366
                }
367
368 30
                $name = isset($params['path']) ? $params['path'] : (isset($params['dbname']) ? $params['dbname'] : false);
369 30
                if (!$name) {
370
                    throw new \InvalidArgumentException("Connection does not contain a 'path' or 'dbname' parameter and cannot be dropped.");
371
                }
372
373 30
                if (!isset(self::$cachedMetadatas[$omName])) {
374 10
                    self::$cachedMetadatas[$omName] = $om->getMetadataFactory()->getAllMetadata();
375 10
                    usort(self::$cachedMetadatas[$omName], function ($a, $b) {
376
                        return strcmp($a->name, $b->name);
377 10
                    });
378 10
                }
379 30
                $metadatas = self::$cachedMetadatas[$omName];
380
381 30
                if ($container->getParameter('liip_functional_test.cache_sqlite_db')) {
382 9
                    $backup = $container->getParameter('kernel.cache_dir').'/test_'.md5(serialize($metadatas).serialize($classNames)).'.db';
383 9
                    if (file_exists($backup) && file_exists($backup.'.ser') && $this->isBackupUpToDate($classNames, $backup)) {
384 7
                        $connection = $this->getContainer()->get('doctrine.orm.entity_manager')->getConnection();
385 7
                        if (null !== $connection) {
386 7
                            $connection->close();
387 7
                        }
388
389 7
                        $om->flush();
390 7
                        $om->clear();
391
392 7
                        $this->preFixtureBackupRestore($om, $referenceRepository, $backup);
393
394 7
                        copy($backup, $name);
395
396 7
                        $executor = new $executorClass($om);
397 7
                        $executor->setReferenceRepository($referenceRepository);
398 7
                        $executor->getReferenceRepository()->load($backup);
399
400 7
                        $this->postFixtureBackupRestore($backup);
401
402 7
                        return $executor;
403
                    }
404 3
                }
405
406
                // TODO: handle case when using persistent connections. Fail loudly?
407 24
                $schemaTool = new SchemaTool($om);
408 24
                $schemaTool->dropDatabase();
409 24
                if (!empty($metadatas)) {
410 24
                    $schemaTool->createSchema($metadatas);
411 24
                }
412 24
                $this->postFixtureSetup();
413
414 24
                $executor = new $executorClass($om);
415 24
                $executor->setReferenceRepository($referenceRepository);
416 24
            }
417 29
        }
418
419 30
        if (empty($executor)) {
420 6
            $purgerClass = 'Doctrine\\Common\\DataFixtures\\Purger\\'.$type.'Purger';
421 6
            if ('PHPCR' === $type) {
422 1
                $purger = new $purgerClass($om);
423 1
                $initManager = $container->has('doctrine_phpcr.initializer_manager')
424 1
                    ? $container->get('doctrine_phpcr.initializer_manager')
425 1
                    : null;
426
427 1
                $executor = new $executorClass($om, $purger, $initManager);
428 1
            } else {
429 5
                if ('ORM' === $type) {
430 5
                    $purger = new $purgerClass(null, $this->excludedDoctrineTables);
431 5
                } else {
432
                    $purger = new $purgerClass();
433
                }
434
435 5
                if (null !== $purgeMode) {
436 2
                    $purger->setPurgeMode($purgeMode);
437 2
                }
438
439 5
                $executor = new $executorClass($om, $purger);
440
            }
441
442 6
            $executor->setReferenceRepository($referenceRepository);
443 6
            $executor->purge();
444 6
        }
445
446 30
        $loader = $this->getFixtureLoader($container, $classNames);
447
448 30
        $executor->execute($loader->getFixtures(), true);
449
450 30
        if (isset($name) && isset($backup)) {
451 3
            $this->preReferenceSave($om, $executor, $backup);
452
453 3
            $executor->getReferenceRepository()->save($backup);
454 3
            copy($name, $backup);
455
456 3
            $this->postReferenceSave($om, $executor, $backup);
457 3
        }
458
459 30
        return $executor;
460
    }
461
462
    /**
463
     * Clean database.
464
     *
465
     * @param ManagerRegistry $registry
466
     * @param EntityManager   $om
467
     * @param null            $omName
468
     * @param string          $registryName
469
     * @param int             $purgeMode
470
     */
471 9
    private function cleanDatabase(ManagerRegistry $registry, EntityManager $om, $omName = null, $registryName = 'doctrine', $purgeMode = null)
472
    {
473 9
        $connection = $om->getConnection();
474
475 9
        $mysql = ('ORM' === $registry->getName()
476 9
            && $connection->getDatabasePlatform() instanceof MySqlPlatform);
477
478 9
        if ($mysql) {
479 1
            $connection->query('SET FOREIGN_KEY_CHECKS=0');
480 1
        }
481
482 9
        $this->loadFixtures(array(), $omName, $registryName, $purgeMode);
483
484 9
        if ($mysql) {
485 1
            $connection->query('SET FOREIGN_KEY_CHECKS=1');
486 1
        }
487 9
    }
488
489
    /**
490
     * Locate fixture files.
491
     *
492
     * @param array $paths
493
     *
494
     * @return array $files
495
     *
496
     * @throws \InvalidArgumentException if a wrong path is given outside a bundle
497
     */
498 10
    private function locateResources($paths)
499
    {
500 10
        $files = array();
501
502 10
        $kernel = $this->getContainer()->get('kernel');
503
504 10
        foreach ($paths as $path) {
505 10
            if ('@' !== $path[0]) {
506 3
                if (!file_exists($path)) {
507 1
                    throw new \InvalidArgumentException(sprintf('Unable to find file "%s".', $path));
508
                }
509 2
                $files[] = $path;
510
511 2
                continue;
512
            }
513
514 7
            $files[] = $kernel->locateResource($path);
515 8
        }
516
517 8
        return $files;
518
    }
519
520
    /**
521
     * @param array  $paths        Either symfony resource locators (@ BundleName/etc) or actual file paths
522
     * @param bool   $append
523
     * @param null   $omName
524
     * @param string $registryName
525
     * @param int    $purgeMode
526
     *
527
     * @return array
528
     *
529
     * @throws \BadMethodCallException
530
     */
531 10
    public function loadFixtureFiles(array $paths = array(), $append = false, $omName = null, $registryName = 'doctrine', $purgeMode = null)
532
    {
533 10
        if (!class_exists('Nelmio\Alice\Fixtures')) {
534
            // This class is available during tests, no exception will be thrown.
535
            // @codeCoverageIgnoreStart
536
            throw new \BadMethodCallException('nelmio/alice should be installed to use this method.');
537
            // @codeCoverageIgnoreEnd
538
        }
539
540
        /** @var ContainerInterface $container */
541 10
        $container = $this->getContainer();
542
543
        /** @var ManagerRegistry $registry */
544 10
        $registry = $container->get($registryName);
545
546
        /** @var EntityManager $om */
547 10
        $om = $registry->getManager($omName);
548
549 10
        if (false === $append) {
550 9
            $this->cleanDatabase($registry, $om, $omName, $registryName, $purgeMode);
551 9
        }
552
553 10
        $files = $this->locateResources($paths);
554
555
        // Check if the Hautelook AliceBundle is registered and if yes, use it instead of Nelmio Alice
556 8
        $hautelookLoaderServiceName = 'hautelook_alice.fixtures.loader';
557 8
        if ($container->has($hautelookLoaderServiceName)) {
558 3
            $loaderService = $container->get($hautelookLoaderServiceName);
559 3
            $persisterClass = class_exists('Nelmio\Alice\ORM\Doctrine') ?
560 3
                'Nelmio\Alice\ORM\Doctrine' :
561 3
                'Nelmio\Alice\Persister\Doctrine';
562
563 3
            return $loaderService->load(new $persisterClass($om), $files);
564
        }
565
566 5
        return Fixtures::load($files, $om);
567
    }
568
569
    /**
570
     * Callback function to be executed after Schema creation.
571
     * Use this to execute acl:init or other things necessary.
572
     */
573 24
    protected function postFixtureSetup()
574
    {
575 24
    }
576
577
    /**
578
     * Callback function to be executed after Schema restore.
579
     *
580
     * @return WebTestCase
581
     *
582
     * @deprecated since version 1.8, to be removed in 2.0. Use postFixtureBackupRestore method instead.
583
     */
584 7
    protected function postFixtureRestore()
585
    {
586 7
    }
587
588
    /**
589
     * Callback function to be executed before Schema restore.
590
     *
591
     * @param ObjectManager            $manager             The object manager
592
     * @param ProxyReferenceRepository $referenceRepository The reference repository
593
     *
594
     * @return WebTestCase
595
     *
596
     * @deprecated since version 1.8, to be removed in 2.0. Use preFixtureBackupRestore method instead.
597
     */
598 7
    protected function preFixtureRestore(ObjectManager $manager, ProxyReferenceRepository $referenceRepository)
599
    {
600 7
    }
601
602
    /**
603
     * Callback function to be executed after Schema restore.
604
     *
605
     * @param string $backupFilePath Path of file used to backup the references of the data fixtures
606
     *
607
     * @return WebTestCase
608
     */
609 7
    protected function postFixtureBackupRestore($backupFilePath)
610
    {
611 7
        $this->postFixtureRestore();
612
613 7
        return $this;
614
    }
615
616
    /**
617
     * Callback function to be executed before Schema restore.
618
     *
619
     * @param ObjectManager            $manager             The object manager
620
     * @param ProxyReferenceRepository $referenceRepository The reference repository
621
     * @param string                   $backupFilePath      Path of file used to backup the references of the data fixtures
622
     *
623
     * @return WebTestCase
624
     */
625 7
    protected function preFixtureBackupRestore(
626
        ObjectManager $manager,
627
        ProxyReferenceRepository $referenceRepository,
628
        $backupFilePath
629
    ) {
630 7
        $this->preFixtureRestore($manager, $referenceRepository);
631
632 7
        return $this;
633
    }
634
635
    /**
636
     * Callback function to be executed after save of references.
637
     *
638
     * @param ObjectManager    $manager        The object manager
639
     * @param AbstractExecutor $executor       Executor of the data fixtures
640
     * @param string           $backupFilePath Path of file used to backup the references of the data fixtures
641
     *
642
     * @return WebTestCase
643
     */
644 3
    protected function postReferenceSave(ObjectManager $manager, AbstractExecutor $executor, $backupFilePath)
645
    {
646 3
    }
647
648
    /**
649
     * Callback function to be executed before save of references.
650
     *
651
     * @param ObjectManager    $manager        The object manager
652
     * @param AbstractExecutor $executor       Executor of the data fixtures
653
     * @param string           $backupFilePath Path of file used to backup the references of the data fixtures
654
     *
655
     * @return WebTestCase
656
     */
657 3
    protected function preReferenceSave(ObjectManager $manager, AbstractExecutor $executor, $backupFilePath)
658
    {
659 3
    }
660
661
    /**
662
     * Retrieve Doctrine DataFixtures loader.
663
     *
664
     * @param ContainerInterface $container
665
     * @param array              $classNames
666
     *
667
     * @return Loader
668
     */
669 36
    protected function getFixtureLoader(ContainerInterface $container, array $classNames)
670
    {
671 36
        $loaderClass = class_exists('Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader')
672 36
            ? 'Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader'
673 36
            : (class_exists('Doctrine\Bundle\FixturesBundle\Common\DataFixtures\Loader')
674
                // This class is not available during tests.
675
                // @codeCoverageIgnoreStart
676
                ? 'Doctrine\Bundle\FixturesBundle\Common\DataFixtures\Loader'
677
                // @codeCoverageIgnoreEnd
678 36
                : 'Symfony\Bundle\DoctrineFixturesBundle\Common\DataFixtures\Loader');
679
680 36
        $loader = new $loaderClass($container);
681
682 36
        foreach ($classNames as $className) {
683 11
            $this->loadFixtureClass($loader, $className);
684 36
        }
685
686 36
        return $loader;
687
    }
688
689
    /**
690
     * Load a data fixture class.
691
     *
692
     * @param Loader $loader
693
     * @param string $className
694
     */
695 11
    protected function loadFixtureClass($loader, $className)
696
    {
697 11
        $fixture = null;
0 ignored issues
show
$fixture is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
698
699 11
        if ($this->getContainer()->has($className)) {
700
            $fixture = $this->getContainer()->get($className);
701
        } else {
702 11
            $fixture = new $className();
703
        }
704
705 11
        if ($loader->hasFixture($fixture)) {
706 2
            unset($fixture);
707
708 2
            return;
709
        }
710
711 11
        $loader->addFixture($fixture);
712
713 11
        if ($fixture instanceof DependentFixtureInterface) {
714 2
            foreach ($fixture->getDependencies() as $dependency) {
715 2
                $this->loadFixtureClass($loader, $dependency);
716 2
            }
717 2
        }
718 11
    }
719
720
    /**
721
     * Creates an instance of a lightweight Http client.
722
     *
723
     * If $authentication is set to 'true' it will use the content of
724
     * 'liip_functional_test.authentication' to log in.
725
     *
726
     * $params can be used to pass headers to the client, note that they have
727
     * to follow the naming format used in $_SERVER.
728
     * Example: 'HTTP_X_REQUESTED_WITH' instead of 'X-Requested-With'
729
     *
730
     * @param bool|array $authentication
731
     * @param array      $params
732
     *
733
     * @return Client
734
     */
735 55
    protected function makeClient($authentication = false, array $params = array())
736
    {
737 55
        if ($authentication) {
738 2
            if (true === $authentication) {
739
                $authentication = array(
740 1
                    'username' => $this->getContainer()
741 1
                        ->getParameter('liip_functional_test.authentication.username'),
742 1
                    'password' => $this->getContainer()
743 1
                        ->getParameter('liip_functional_test.authentication.password'),
744 1
                );
745 1
            }
746
747 2
            $params = array_merge($params, array(
748 2
                'PHP_AUTH_USER' => $authentication['username'],
749 2
                'PHP_AUTH_PW' => $authentication['password'],
750 2
            ));
751 2
        }
752
753 55
        $client = static::createClient(array('environment' => $this->environment), $params);
754
755 55
        if ($this->firewallLogins) {
756
            // has to be set otherwise "hasPreviousSession" in Request returns false.
757 2
            $options = $client->getContainer()->getParameter('session.storage.options');
758
759 2
            if (!$options || !isset($options['name'])) {
760
                throw new \InvalidArgumentException('Missing session.storage.options#name');
761
            }
762
763 2
            $session = $client->getContainer()->get('session');
764
            // Since the namespace of the session changed in symfony 2.1, instanceof can be used to check the version.
765 2
            if ($session instanceof Session) {
766 2
                $session->setId(uniqid());
767 2
            }
768
769 2
            $client->getCookieJar()->set(new Cookie($options['name'], $session->getId()));
770
771
            /** @var $user UserInterface */
772 2
            foreach ($this->firewallLogins as $firewallName => $user) {
773 2
                $token = $this->createUserToken($user, $firewallName);
774
775
                // BC: security.token_storage is available on Symfony 2.6+
776
                // see http://symfony.com/blog/new-in-symfony-2-6-security-component-improvements
777 2
                if ($client->getContainer()->has('security.token_storage')) {
778 2
                    $tokenStorage = $client->getContainer()->get('security.token_storage');
779 2
                } else {
780
                    // This block will never be reached with Symfony 2.6+
781
                    // @codeCoverageIgnoreStart
782
                    $tokenStorage = $client->getContainer()->get('security.context');
783
                    // @codeCoverageIgnoreEnd
784
                }
785
786 2
                $tokenStorage->setToken($token);
787 2
                $session->set('_security_'.$firewallName, serialize($token));
788 2
            }
789
790 2
            $session->save();
791 2
        }
792
793 55
        return $client;
794
    }
795
796
    /**
797
     * Create User Token.
798
     *
799
     * Factory method for creating a User Token object for the firewall based on
800
     * the user object provided. By default it will be a Username/Password
801
     * Token based on the user's credentials, but may be overridden for custom
802
     * tokens in your applications.
803
     *
804
     * @param UserInterface $user         The user object to base the token off of
805
     * @param string        $firewallName name of the firewall provider to use
806
     *
807
     * @return TokenInterface The token to be used in the security context
808
     */
809 2
    protected function createUserToken(UserInterface $user, $firewallName)
810
    {
811 2
        return new UsernamePasswordToken(
812 2
            $user,
813 2
            null,
814 2
            $firewallName,
815 2
            $user->getRoles()
816 2
        );
817
    }
818
819
    /**
820
     * Extracts the location from the given route.
821
     *
822
     * @param string $route    The name of the route
823
     * @param array  $params   Set of parameters
824
     * @param int    $absolute
825
     *
826
     * @return string
827
     */
828 1
    protected function getUrl($route, $params = array(), $absolute = UrlGeneratorInterface::ABSOLUTE_PATH)
829
    {
830 1
        return $this->getContainer()->get('router')->generate($route, $params, $absolute);
831
    }
832
833
    /**
834
     * Checks the success state of a response.
835
     *
836
     * @param Response $response Response object
837
     * @param bool     $success  to define whether the response is expected to be successful
838
     * @param string   $type
839
     */
840 6
    public function isSuccessful(Response $response, $success = true, $type = 'text/html')
841
    {
842 6
        HttpAssertions::isSuccessful($response, $success, $type);
843 5
    }
844
845
    /**
846
     * Executes a request on the given url and returns the response contents.
847
     *
848
     * This method also asserts the request was successful.
849
     *
850
     * @param string $path           path of the requested page
851
     * @param string $method         The HTTP method to use, defaults to GET
852
     * @param bool   $authentication Whether to use authentication, defaults to false
853
     * @param bool   $success        to define whether the response is expected to be successful
854
     *
855
     * @return string
856
     */
857 1
    public function fetchContent($path, $method = 'GET', $authentication = false, $success = true)
858
    {
859 1
        $client = $this->makeClient($authentication);
860 1
        $client->request($method, $path);
861
862 1
        $content = $client->getResponse()->getContent();
863 1
        if (is_bool($success)) {
864 1
            $this->isSuccessful($client->getResponse(), $success);
865 1
        }
866
867 1
        return $content;
868
    }
869
870
    /**
871
     * Executes a request on the given url and returns a Crawler object.
872
     *
873
     * This method also asserts the request was successful.
874
     *
875
     * @param string $path           path of the requested page
876
     * @param string $method         The HTTP method to use, defaults to GET
877
     * @param bool   $authentication Whether to use authentication, defaults to false
878
     * @param bool   $success        Whether the response is expected to be successful
879
     *
880
     * @return Crawler
881
     */
882 1
    public function fetchCrawler($path, $method = 'GET', $authentication = false, $success = true)
883
    {
884 1
        $client = $this->makeClient($authentication);
885 1
        $crawler = $client->request($method, $path);
886
887 1
        $this->isSuccessful($client->getResponse(), $success);
888
889 1
        return $crawler;
890
    }
891
892
    /**
893
     * @param UserInterface $user
894
     * @param string        $firewallName
895
     *
896
     * @return WebTestCase
897
     */
898 2
    public function loginAs(UserInterface $user, $firewallName)
899
    {
900 2
        $this->firewallLogins[$firewallName] = $user;
901
902 2
        return $this;
903
    }
904
905
    /**
906
     * Asserts that the HTTP response code of the last request performed by
907
     * $client matches the expected code. If not, raises an error with more
908
     * information.
909
     *
910
     * @param $expectedStatusCode
911
     * @param Client $client
912
     */
913 12
    public function assertStatusCode($expectedStatusCode, Client $client)
914
    {
915 12
        HttpAssertions::assertStatusCode($expectedStatusCode, $client);
916 9
    }
917
918
    /**
919
     * Assert that the last validation errors within $container match the
920
     * expected keys.
921
     *
922
     * @param array              $expected  A flat array of field names
923
     * @param ContainerInterface $container
924
     */
925 3
    public function assertValidationErrors(array $expected, ContainerInterface $container)
926
    {
927 3
        HttpAssertions::assertValidationErrors($expected, $container);
928 1
    }
929
930
    /**
931
     * @param array $excludedDoctrineTables
932
     */
933 1
    public function setExcludedDoctrineTables($excludedDoctrineTables)
934
    {
935 1
        $this->excludedDoctrineTables = $excludedDoctrineTables;
936 1
    }
937
}
938