Completed
Pull Request — master (#347)
by Alexis
13:17 queued 07:28
created

WebTestCase::configureVerbosityForSymfony203()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 26
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
dl 0
loc 26
ccs 0
cts 0
cp 0
rs 8.439
c 0
b 0
f 0
cc 5
eloc 15
nc 5
nop 1
crap 30

1 Method

Rating   Name   Duplication   Size   Complexity  
A WebTestCase::isDecorated() 0 4 1
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
    protected $containers;
51
    protected $kernelDir;
52
    // 5 * 1024 * 1024 KB
53
    protected $maxMemory = 5242880;
54
55
    // RUN COMMAND
56
    protected $verbosityLevel;
57
    protected $decorated;
58
59
    /**
60
     * @var array
61
     */
62
    private $firewallLogins = array();
63
64
    /**
65
     * @var array
66
     */
67
    private $excludedDoctrineTables = array();
68
69
    /**
70
     * @var array
71
     */
72
    private static $cachedMetadatas = array();
73
74
    protected static function getKernelClass()
0 ignored issues
show
Coding Style introduced by
getKernelClass uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
75
    {
76
        $dir = isset($_SERVER['KERNEL_DIR']) ? $_SERVER['KERNEL_DIR'] : static::getPhpUnitXmlDir();
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Bundle\Framework...ase::getPhpUnitXmlDir() has been deprecated with message: since 3.4 and will be removed in 4.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
77
78
        list($appname) = explode('\\', get_called_class());
79
80
        $class = $appname.'Kernel';
81
        $file = $dir.'/'.strtolower($appname).'/'.$class.'.php';
82
        if (!file_exists($file)) {
83
            return parent::getKernelClass();
84
        }
85
        require_once $file;
86
87
        return $class;
88
    }
89
90
    /**
91
     * Creates a mock object of a service identified by its id.
92
     *
93
     * @param string $id
94
     *
95
     * @return \PHPUnit_Framework_MockObject_MockBuilder
96
     */
97 1
    protected function getServiceMockBuilder($id)
98
    {
99 1
        $service = $this->getContainer()->get($id);
100
        $class = get_class($service);
101
102
        return $this->getMockBuilder($class)->disableOriginalConstructor();
103
    }
104
105
    /**
106
     * Builds up the environment to run the given command.
107
     *
108
     * @param string $name
109
     * @param array  $params
110
     * @param bool   $reuseKernel
111
     *
112
     * @return string
113
     */
114 12
    protected function runCommand($name, array $params = array(), $reuseKernel = false)
115
    {
116 12
        array_unshift($params, $name);
117
118 12
        if (!$reuseKernel) {
119 12
            if (null !== static::$kernel) {
120 9
                static::$kernel->shutdown();
121 9
            }
122
123 12
            $kernel = static::$kernel = $this->createKernel(array('environment' => $this->environment));
124 12
            $kernel->boot();
125 12
        } else {
126 2
            $kernel = $this->getContainer()->get('kernel');
127
        }
128
129 12
        $application = new Application($kernel);
130 12
        $application->setAutoExit(false);
131
132 12
        $input = new ArrayInput($params);
133 12
        $input->setInteractive(false);
134
135 12
        $fp = fopen('php://temp/maxmemory:'.$this->maxMemory, 'r+');
136 12
        $output = new StreamOutput($fp, $this->getVerbosityLevel(), $this->getDecorated());
137
138 11
        $application->run($input, $output);
139
140 11
        rewind($fp);
141
142 11
        return stream_get_contents($fp);
143
    }
144
145
    /**
146
     * Retrieves the output verbosity level.
147
     *
148
     * @see Symfony\Component\Console\Output\OutputInterface for available levels
149
     *
150
     * @return int
151
     *
152
     * @throws \OutOfBoundsException If the set value isn't accepted
153
     */
154 12
    protected function getVerbosityLevel()
155
    {
156
        // If `null`, is not yet set
157 12
        if (null === $this->verbosityLevel) {
158
            // Set the global verbosity level that is set as NORMAL by the TreeBuilder in Configuration
159 6
            $level = strtoupper($this->getContainer()->getParameter('liip_functional_test.command_verbosity'));
160 6
            $verbosity = '\Symfony\Component\Console\Output\StreamOutput::VERBOSITY_'.$level;
161
162 6
            $this->verbosityLevel = constant($verbosity);
163 6
        }
164
165
        // If string, it is set by the developer, so check that the value is an accepted one
166 12
        if (is_string($this->verbosityLevel)) {
167 6
            $level = strtoupper($this->verbosityLevel);
168 6
            $verbosity = '\Symfony\Component\Console\Output\StreamOutput::VERBOSITY_'.$level;
169
170 6
            if (!defined($verbosity)) {
171 1
                throw new \OutOfBoundsException(
172 1
                    sprintf('The set value "%s" for verbosityLevel is not valid. Accepted are: "quiet", "normal", "verbose", "very_verbose" and "debug".', $level)
173 1
                );
174
            }
175
176 5
            $this->verbosityLevel = constant($verbosity);
177 5
        }
178
179 11
        return $this->verbosityLevel;
180
    }
181
182 6
    public function setVerbosityLevel($level)
183
    {
184 6
        $this->verbosityLevel = $level;
185 6
    }
186
187
    /**
188
     * Retrieves the flag indicating if the output should be decorated or not.
189
     *
190
     * @return bool
191
     */
192 11
    protected function getDecorated()
193
    {
194 11
        if (null === $this->decorated) {
195
            // Set the global decoration flag that is set to `true` by the TreeBuilder in Configuration
196 5
            $this->decorated = $this->getContainer()->getParameter('liip_functional_test.command_decoration');
197 5
        }
198
199
        // Check the local decorated flag
200 11
        if (false === is_bool($this->decorated)) {
201
            throw new \OutOfBoundsException(
202
                sprintf('`WebTestCase::decorated` has to be `bool`. "%s" given.', gettype($this->decorated))
203
            );
204
        }
205
206 11
        return $this->decorated;
207
    }
208
209 6
    public function isDecorated($decorated)
210
    {
211 6
        $this->decorated = $decorated;
212 6
    }
213
214
    /**
215
     * Get an instance of the dependency injection container.
216
     * (this creates a kernel *without* parameters).
217
     *
218
     * @return ContainerInterface
219
     */
220 50
    protected function getContainer()
0 ignored issues
show
Coding Style introduced by
getContainer uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
221
    {
222 50
        if (!empty($this->kernelDir)) {
223
            $tmpKernelDir = isset($_SERVER['KERNEL_DIR']) ? $_SERVER['KERNEL_DIR'] : null;
224
            $_SERVER['KERNEL_DIR'] = getcwd().$this->kernelDir;
225
        }
226
227 50
        $cacheKey = $this->kernelDir.'|'.$this->environment;
228 50
        if (empty($this->containers[$cacheKey])) {
229
            $options = array(
230 49
                'environment' => $this->environment,
231 49
            );
232 49
            $kernel = $this->createKernel($options);
233 49
            $kernel->boot();
234
235 49
            $this->containers[$cacheKey] = $kernel->getContainer();
236 49
        }
237
238 50
        if (isset($tmpKernelDir)) {
239
            $_SERVER['KERNEL_DIR'] = $tmpKernelDir;
240
        }
241
242 50
        return $this->containers[$cacheKey];
243
    }
244
245
    /**
246
     * This function finds the time when the data blocks of a class definition
247
     * file were being written to, that is, the time when the content of the
248
     * file was changed.
249
     *
250
     * @param string $class The fully qualified class name of the fixture class to
251
     *                      check modification date on
252
     *
253
     * @return \DateTime|null
254
     */
255 3
    protected function getFixtureLastModified($class)
256
    {
257 3
        $lastModifiedDateTime = null;
258
259 3
        $reflClass = new \ReflectionClass($class);
260 3
        $classFileName = $reflClass->getFileName();
261
262 3
        if (file_exists($classFileName)) {
263 3
            $lastModifiedDateTime = new \DateTime();
264 3
            $lastModifiedDateTime->setTimestamp(filemtime($classFileName));
265 3
        }
266
267 3
        return $lastModifiedDateTime;
268
    }
269
270
    /**
271
     * Determine if the Fixtures that define a database backup have been
272
     * modified since the backup was made.
273
     *
274
     * @param array  $classNames The fixture classnames to check
275
     * @param string $backup     The fixture backup SQLite database file path
276
     *
277
     * @return bool TRUE if the backup was made since the modifications to the
278
     *              fixtures; FALSE otherwise
279
     */
280 7
    protected function isBackupUpToDate(array $classNames, $backup)
281
    {
282 7
        $backupLastModifiedDateTime = new \DateTime();
283 7
        $backupLastModifiedDateTime->setTimestamp(filemtime($backup));
284
285
        /** @var \Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader $loader */
286 7
        $loader = $this->getFixtureLoader($this->getContainer(), $classNames);
287
288
        // Use loader in order to fetch all the dependencies fixtures.
289 7
        foreach ($loader->getFixtures() as $className) {
290 3
            $fixtureLastModifiedDateTime = $this->getFixtureLastModified($className);
291 3
            if ($backupLastModifiedDateTime < $fixtureLastModifiedDateTime) {
292 1
                return false;
293
            }
294 7
        }
295
296 7
        return true;
297
    }
298
299
    /**
300
     * Set the database to the provided fixtures.
301
     *
302
     * Drops the current database and then loads fixtures using the specified
303
     * classes. The parameter is a list of fully qualified class names of
304
     * classes that implement Doctrine\Common\DataFixtures\FixtureInterface
305
     * so that they can be loaded by the DataFixtures Loader::addFixture
306
     *
307
     * When using SQLite this method will automatically make a copy of the
308
     * loaded schema and fixtures which will be restored automatically in
309
     * case the same fixture classes are to be loaded again. Caveat: changes
310
     * to references and/or identities may go undetected.
311
     *
312
     * Depends on the doctrine data-fixtures library being available in the
313
     * class path.
314
     *
315
     * @param array  $classNames   List of fully qualified class names of fixtures to load
316
     * @param string $omName       The name of object manager to use
317
     * @param string $registryName The service id of manager registry to use
318
     * @param int    $purgeMode    Sets the ORM purge mode
319
     *
320
     * @return null|AbstractExecutor
321
     */
322 36
    protected function loadFixtures(array $classNames, $omName = null, $registryName = 'doctrine', $purgeMode = null)
323
    {
324 36
        $container = $this->getContainer();
325
        /** @var ManagerRegistry $registry */
326 36
        $registry = $container->get($registryName);
327
        /** @var ObjectManager $om */
328 36
        $om = $registry->getManager($omName);
329 36
        $type = $registry->getName();
330
331 36
        $executorClass = 'PHPCR' === $type && class_exists('Doctrine\Bundle\PHPCRBundle\DataFixtures\PHPCRExecutor')
332 36
            ? 'Doctrine\Bundle\PHPCRBundle\DataFixtures\PHPCRExecutor'
333 36
            : 'Doctrine\\Common\\DataFixtures\\Executor\\'.$type.'Executor';
334 36
        $referenceRepository = new ProxyReferenceRepository($om);
335 36
        $cacheDriver = $om->getMetadataFactory()->getCacheDriver();
336
337 36
        if ($cacheDriver) {
338 36
            $cacheDriver->deleteAll();
339 36
        }
340
341 36
        if ('ORM' === $type) {
342 35
            $connection = $om->getConnection();
343 35
            if ($connection->getDriver() instanceof SqliteDriver) {
344 30
                $params = $connection->getParams();
345 30
                if (isset($params['master'])) {
346
                    $params = $params['master'];
347
                }
348
349 30
                $name = isset($params['path']) ? $params['path'] : (isset($params['dbname']) ? $params['dbname'] : false);
350 30
                if (!$name) {
351
                    throw new \InvalidArgumentException("Connection does not contain a 'path' or 'dbname' parameter and cannot be dropped.");
352
                }
353
354 30
                if (!isset(self::$cachedMetadatas[$omName])) {
355 10
                    self::$cachedMetadatas[$omName] = $om->getMetadataFactory()->getAllMetadata();
356 10
                    usort(self::$cachedMetadatas[$omName], function ($a, $b) {
357
                        return strcmp($a->name, $b->name);
358 10
                    });
359 10
                }
360 30
                $metadatas = self::$cachedMetadatas[$omName];
361
362 30
                if ($container->getParameter('liip_functional_test.cache_sqlite_db')) {
363 9
                    $backup = $container->getParameter('kernel.cache_dir').'/test_'.md5(serialize($metadatas).serialize($classNames)).'.db';
364 9
                    if (file_exists($backup) && file_exists($backup.'.ser') && $this->isBackupUpToDate($classNames, $backup)) {
365 7
                        $connection = $this->getContainer()->get('doctrine.orm.entity_manager')->getConnection();
366 7
                        if (null !== $connection) {
367 7
                            $connection->close();
368 7
                        }
369
370 7
                        $om->flush();
371 7
                        $om->clear();
372
373 7
                        $this->preFixtureBackupRestore($om, $referenceRepository, $backup);
374
375 7
                        copy($backup, $name);
376
377 7
                        $executor = new $executorClass($om);
378 7
                        $executor->setReferenceRepository($referenceRepository);
379 7
                        $executor->getReferenceRepository()->load($backup);
380
381 7
                        $this->postFixtureBackupRestore($backup);
382
383 7
                        return $executor;
384
                    }
385 3
                }
386
387
                // TODO: handle case when using persistent connections. Fail loudly?
388 24
                $schemaTool = new SchemaTool($om);
0 ignored issues
show
Compatibility introduced by
$om of type object<Doctrine\Common\Persistence\ObjectManager> is not a sub-type of object<Doctrine\ORM\EntityManagerInterface>. It seems like you assume a child interface of the interface Doctrine\Common\Persistence\ObjectManager to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
389 24
                $schemaTool->dropDatabase();
390 24
                if (!empty($metadatas)) {
391 24
                    $schemaTool->createSchema($metadatas);
392 24
                }
393 24
                $this->postFixtureSetup();
394
395 24
                $executor = new $executorClass($om);
396 24
                $executor->setReferenceRepository($referenceRepository);
397 24
            }
398 29
        }
399
400 30
        if (empty($executor)) {
401 6
            $purgerClass = 'Doctrine\\Common\\DataFixtures\\Purger\\'.$type.'Purger';
402 6
            if ('PHPCR' === $type) {
403 1
                $purger = new $purgerClass($om);
404 1
                $initManager = $container->has('doctrine_phpcr.initializer_manager')
405 1
                    ? $container->get('doctrine_phpcr.initializer_manager')
406 1
                    : null;
407
408 1
                $executor = new $executorClass($om, $purger, $initManager);
409 1
            } else {
410 5
                if ('ORM' === $type) {
411 5
                    $purger = new $purgerClass(null, $this->excludedDoctrineTables);
412 5
                } else {
413
                    $purger = new $purgerClass();
414
                }
415
416 5
                if (null !== $purgeMode) {
417 2
                    $purger->setPurgeMode($purgeMode);
418 2
                }
419
420 5
                $executor = new $executorClass($om, $purger);
421
            }
422
423 6
            $executor->setReferenceRepository($referenceRepository);
424 6
            $executor->purge();
425 6
        }
426
427 30
        $loader = $this->getFixtureLoader($container, $classNames);
428
429 30
        $executor->execute($loader->getFixtures(), true);
430
431 30
        if (isset($name) && isset($backup)) {
432 3
            $this->preReferenceSave($om, $executor, $backup);
433
434 3
            $executor->getReferenceRepository()->save($backup);
435 3
            copy($name, $backup);
436
437 3
            $this->postReferenceSave($om, $executor, $backup);
438 3
        }
439
440 30
        return $executor;
441
    }
442
443
    /**
444
     * Clean database.
445
     *
446
     * @param ManagerRegistry $registry
447
     * @param EntityManager   $om
448
     * @param null            $omName
449
     * @param string          $registryName
450
     * @param int             $purgeMode
451
     */
452 9
    private function cleanDatabase(ManagerRegistry $registry, EntityManager $om, $omName = null, $registryName = 'doctrine', $purgeMode = null)
453
    {
454 9
        $connection = $om->getConnection();
455
456 9
        $mysql = ('ORM' === $registry->getName()
457 9
            && $connection->getDatabasePlatform() instanceof MySqlPlatform);
458
459 9
        if ($mysql) {
460 1
            $connection->query('SET FOREIGN_KEY_CHECKS=0');
461 1
        }
462
463 9
        $this->loadFixtures(array(), $omName, $registryName, $purgeMode);
464
465 9
        if ($mysql) {
466 1
            $connection->query('SET FOREIGN_KEY_CHECKS=1');
467 1
        }
468 9
    }
469
470
    /**
471
     * Locate fixture files.
472
     *
473
     * @param array $paths
474
     *
475
     * @return array $files
476
     *
477
     * @throws \InvalidArgumentException if a wrong path is given outside a bundle
478
     */
479 10
    private function locateResources($paths)
480
    {
481 10
        $files = array();
482
483 10
        $kernel = $this->getContainer()->get('kernel');
484
485 10
        foreach ($paths as $path) {
486 10
            if ('@' !== $path[0]) {
487 3
                if (!file_exists($path)) {
488 1
                    throw new \InvalidArgumentException(sprintf('Unable to find file "%s".', $path));
489
                }
490 2
                $files[] = $path;
491
492 2
                continue;
493
            }
494
495 7
            $files[] = $kernel->locateResource($path);
496 8
        }
497
498 8
        return $files;
499
    }
500
501
    /**
502
     * @param array  $paths        Either symfony resource locators (@ BundleName/etc) or actual file paths
503
     * @param bool   $append
504
     * @param null   $omName
505
     * @param string $registryName
506
     * @param int    $purgeMode
507
     *
508
     * @return array
509
     *
510
     * @throws \BadMethodCallException
511
     */
512 10
    public function loadFixtureFiles(array $paths = array(), $append = false, $omName = null, $registryName = 'doctrine', $purgeMode = null)
513
    {
514 10
        if (!class_exists('Nelmio\Alice\Fixtures')) {
515
            // This class is available during tests, no exception will be thrown.
516
            // @codeCoverageIgnoreStart
517
            throw new \BadMethodCallException('nelmio/alice should be installed to use this method.');
518
            // @codeCoverageIgnoreEnd
519
        }
520
521
        /** @var ContainerInterface $container */
522 10
        $container = $this->getContainer();
523
524
        /** @var ManagerRegistry $registry */
525 10
        $registry = $container->get($registryName);
526
527
        /** @var EntityManager $om */
528 10
        $om = $registry->getManager($omName);
529
530 10
        if (false === $append) {
531 9
            $this->cleanDatabase($registry, $om, $omName, $registryName, $purgeMode);
532 9
        }
533
534 10
        $files = $this->locateResources($paths);
535
536
        // Check if the Hautelook AliceBundle is registered and if yes, use it instead of Nelmio Alice
537 8
        $hautelookLoaderServiceName = 'hautelook_alice.fixtures.loader';
538 8
        if ($container->has($hautelookLoaderServiceName)) {
539 3
            $loaderService = $container->get($hautelookLoaderServiceName);
540 3
            $persisterClass = class_exists('Nelmio\Alice\ORM\Doctrine') ?
541 3
                'Nelmio\Alice\ORM\Doctrine' :
542 3
                'Nelmio\Alice\Persister\Doctrine';
543
544 3
            return $loaderService->load(new $persisterClass($om), $files);
545
        }
546
547 5
        return Fixtures::load($files, $om);
548
    }
549
550
    /**
551
     * Callback function to be executed after Schema creation.
552
     * Use this to execute acl:init or other things necessary.
553
     */
554 24
    protected function postFixtureSetup()
555
    {
556 24
    }
557
558
    /**
559
     * Callback function to be executed after Schema restore.
560
     *
561
     * @return WebTestCase
562
     *
563
     * @deprecated since version 1.8, to be removed in 2.0. Use postFixtureBackupRestore method instead.
564
     */
565 7
    protected function postFixtureRestore()
566
    {
567 7
    }
568
569
    /**
570
     * Callback function to be executed before Schema restore.
571
     *
572
     * @param ObjectManager            $manager             The object manager
573
     * @param ProxyReferenceRepository $referenceRepository The reference repository
574
     *
575
     * @return WebTestCase
576
     *
577
     * @deprecated since version 1.8, to be removed in 2.0. Use preFixtureBackupRestore method instead.
578
     */
579 7
    protected function preFixtureRestore(ObjectManager $manager, ProxyReferenceRepository $referenceRepository)
0 ignored issues
show
Unused Code introduced by
The parameter $manager is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $referenceRepository is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
580
    {
581 7
    }
582
583
    /**
584
     * Callback function to be executed after Schema restore.
585
     *
586
     * @param string $backupFilePath Path of file used to backup the references of the data fixtures
587
     *
588
     * @return WebTestCase
589
     */
590 7
    protected function postFixtureBackupRestore($backupFilePath)
0 ignored issues
show
Unused Code introduced by
The parameter $backupFilePath is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
591
    {
592 7
        $this->postFixtureRestore();
0 ignored issues
show
Deprecated Code introduced by
The method Liip\FunctionalTestBundl...e::postFixtureRestore() has been deprecated with message: since version 1.8, to be removed in 2.0. Use postFixtureBackupRestore method instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
593
594 7
        return $this;
595
    }
596
597
    /**
598
     * Callback function to be executed before Schema restore.
599
     *
600
     * @param ObjectManager            $manager             The object manager
601
     * @param ProxyReferenceRepository $referenceRepository The reference repository
602
     * @param string                   $backupFilePath      Path of file used to backup the references of the data fixtures
603
     *
604
     * @return WebTestCase
605
     */
606 7
    protected function preFixtureBackupRestore(
607
        ObjectManager $manager,
608
        ProxyReferenceRepository $referenceRepository,
609
        $backupFilePath
0 ignored issues
show
Unused Code introduced by
The parameter $backupFilePath is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
610
    ) {
611 7
        $this->preFixtureRestore($manager, $referenceRepository);
0 ignored issues
show
Deprecated Code introduced by
The method Liip\FunctionalTestBundl...se::preFixtureRestore() has been deprecated with message: since version 1.8, to be removed in 2.0. Use preFixtureBackupRestore method instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
612
613 7
        return $this;
614
    }
615
616
    /**
617
     * Callback function to be executed after save of references.
618
     *
619
     * @param ObjectManager    $manager        The object manager
620
     * @param AbstractExecutor $executor       Executor of the data fixtures
621
     * @param string           $backupFilePath Path of file used to backup the references of the data fixtures
622
     *
623
     * @return WebTestCase
624
     */
625 3
    protected function postReferenceSave(ObjectManager $manager, AbstractExecutor $executor, $backupFilePath)
0 ignored issues
show
Unused Code introduced by
The parameter $manager is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $executor is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $backupFilePath is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
626
    {
627 3
    }
628
629
    /**
630
     * Callback function to be executed before save of references.
631
     *
632
     * @param ObjectManager    $manager        The object manager
633
     * @param AbstractExecutor $executor       Executor of the data fixtures
634
     * @param string           $backupFilePath Path of file used to backup the references of the data fixtures
635
     *
636
     * @return WebTestCase
637
     */
638 3
    protected function preReferenceSave(ObjectManager $manager, AbstractExecutor $executor, $backupFilePath)
0 ignored issues
show
Unused Code introduced by
The parameter $manager is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $executor is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $backupFilePath is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
639
    {
640 3
    }
641
642
    /**
643
     * Retrieve Doctrine DataFixtures loader.
644
     *
645
     * @param ContainerInterface $container
646
     * @param array              $classNames
647
     *
648
     * @return Loader
649
     */
650 36
    protected function getFixtureLoader(ContainerInterface $container, array $classNames)
651
    {
652 36
        $loaderClass = class_exists('Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader')
653 36
            ? 'Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader'
654 36
            : (class_exists('Doctrine\Bundle\FixturesBundle\Common\DataFixtures\Loader')
655
                // This class is not available during tests.
656
                // @codeCoverageIgnoreStart
657
                ? 'Doctrine\Bundle\FixturesBundle\Common\DataFixtures\Loader'
658
                // @codeCoverageIgnoreEnd
659 36
                : 'Symfony\Bundle\DoctrineFixturesBundle\Common\DataFixtures\Loader');
660
661 36
        $loader = new $loaderClass($container);
662
663 36
        foreach ($classNames as $className) {
664 11
            $this->loadFixtureClass($loader, $className);
665 36
        }
666
667 36
        return $loader;
668
    }
669
670
    /**
671
     * Load a data fixture class.
672
     *
673
     * @param Loader $loader
674
     * @param string $className
675
     */
676 11
    protected function loadFixtureClass($loader, $className)
677
    {
678 11
        $fixture = new $className();
679
680 11
        if ($loader->hasFixture($fixture)) {
681 2
            unset($fixture);
682
683 2
            return;
684
        }
685
686 11
        $loader->addFixture($fixture);
687
688 11
        if ($fixture instanceof DependentFixtureInterface) {
689 2
            foreach ($fixture->getDependencies() as $dependency) {
690 2
                $this->loadFixtureClass($loader, $dependency);
691 2
            }
692 2
        }
693 11
    }
694
695
    /**
696
     * Creates an instance of a lightweight Http client.
697
     *
698
     * If $authentication is set to 'true' it will use the content of
699
     * 'liip_functional_test.authentication' to log in.
700
     *
701
     * $params can be used to pass headers to the client, note that they have
702
     * to follow the naming format used in $_SERVER.
703
     * Example: 'HTTP_X_REQUESTED_WITH' instead of 'X-Requested-With'
704
     *
705
     * @param bool|array $authentication
706
     * @param array      $params
707
     *
708
     * @return Client
709
     */
710 55
    protected function makeClient($authentication = false, array $params = array())
711
    {
712 55
        if ($authentication) {
713 2
            if (true === $authentication) {
714
                $authentication = array(
715 1
                    'username' => $this->getContainer()
716 1
                        ->getParameter('liip_functional_test.authentication.username'),
717 1
                    'password' => $this->getContainer()
718 1
                        ->getParameter('liip_functional_test.authentication.password'),
719 1
                );
720 1
            }
721
722 2
            $params = array_merge($params, array(
723 2
                'PHP_AUTH_USER' => $authentication['username'],
724 2
                'PHP_AUTH_PW' => $authentication['password'],
725 2
            ));
726 2
        }
727
728 55
        $client = static::createClient(array('environment' => $this->environment), $params);
729
730 55
        if ($this->firewallLogins) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->firewallLogins of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
731
            // has to be set otherwise "hasPreviousSession" in Request returns false.
732 2
            $options = $client->getContainer()->getParameter('session.storage.options');
733
734 2
            if (!$options || !isset($options['name'])) {
735
                throw new \InvalidArgumentException('Missing session.storage.options#name');
736
            }
737
738 2
            $session = $client->getContainer()->get('session');
739
            // Since the namespace of the session changed in symfony 2.1, instanceof can be used to check the version.
740 2
            if ($session instanceof Session) {
741 2
                $session->setId(uniqid());
742 2
            }
743
744 2
            $client->getCookieJar()->set(new Cookie($options['name'], $session->getId()));
745
746
            /** @var $user UserInterface */
747 2
            foreach ($this->firewallLogins as $firewallName => $user) {
748 2
                $token = $this->createUserToken($user, $firewallName);
749
750
                // BC: security.token_storage is available on Symfony 2.6+
751
                // see http://symfony.com/blog/new-in-symfony-2-6-security-component-improvements
752 2
                if ($client->getContainer()->has('security.token_storage')) {
753 2
                    $tokenStorage = $client->getContainer()->get('security.token_storage');
754 2
                } else {
755
                    // This block will never be reached with Symfony 2.6+
756
                    // @codeCoverageIgnoreStart
757
                    $tokenStorage = $client->getContainer()->get('security.context');
758
                    // @codeCoverageIgnoreEnd
759
                }
760
761 2
                $tokenStorage->setToken($token);
762 2
                $session->set('_security_'.$firewallName, serialize($token));
763 2
            }
764
765 2
            $session->save();
766 2
        }
767
768 55
        return $client;
769
    }
770
771
    /**
772
     * Create User Token.
773
     *
774
     * Factory method for creating a User Token object for the firewall based on
775
     * the user object provided. By default it will be a Username/Password
776
     * Token based on the user's credentials, but may be overridden for custom
777
     * tokens in your applications.
778
     *
779
     * @param UserInterface $user         The user object to base the token off of
780
     * @param string        $firewallName name of the firewall provider to use
781
     *
782
     * @return TokenInterface The token to be used in the security context
783
     */
784 2
    protected function createUserToken(UserInterface $user, $firewallName)
785
    {
786 2
        return new UsernamePasswordToken(
787 2
            $user,
788 2
            null,
789 2
            $firewallName,
790 2
            $user->getRoles()
0 ignored issues
show
Documentation introduced by
$user->getRoles() is of type array<integer,object<Sym...Core\Role\Role>|string>, but the function expects a array<integer,object<Sym...\RoleInterface>|string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
791 2
        );
792
    }
793
794
    /**
795
     * Extracts the location from the given route.
796
     *
797
     * @param string $route    The name of the route
798
     * @param array  $params   Set of parameters
799
     * @param int    $absolute
800
     *
801
     * @return string
802
     */
803 1
    protected function getUrl($route, $params = array(), $absolute = UrlGeneratorInterface::ABSOLUTE_PATH)
804
    {
805 1
        return $this->getContainer()->get('router')->generate($route, $params, $absolute);
806
    }
807
808
    /**
809
     * Checks the success state of a response.
810
     *
811
     * @param Response $response Response object
812
     * @param bool     $success  to define whether the response is expected to be successful
813
     * @param string   $type
814
     */
815 6
    public function isSuccessful(Response $response, $success = true, $type = 'text/html')
816
    {
817 6
        HttpAssertions::isSuccessful($response, $success, $type);
818 5
    }
819
820
    /**
821
     * Executes a request on the given url and returns the response contents.
822
     *
823
     * This method also asserts the request was successful.
824
     *
825
     * @param string $path           path of the requested page
826
     * @param string $method         The HTTP method to use, defaults to GET
827
     * @param bool   $authentication Whether to use authentication, defaults to false
828
     * @param bool   $success        to define whether the response is expected to be successful
829
     *
830
     * @return string
831
     */
832 1
    public function fetchContent($path, $method = 'GET', $authentication = false, $success = true)
833
    {
834 1
        $client = $this->makeClient($authentication);
835 1
        $client->request($method, $path);
836
837 1
        $content = $client->getResponse()->getContent();
838 1
        if (is_bool($success)) {
839 1
            $this->isSuccessful($client->getResponse(), $success);
0 ignored issues
show
Bug introduced by
It seems like $client->getResponse() can be null; however, isSuccessful() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
840 1
        }
841
842 1
        return $content;
843
    }
844
845
    /**
846
     * Executes a request on the given url and returns a Crawler object.
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        Whether the response is expected to be successful
854
     *
855
     * @return Crawler
856
     */
857 1
    public function fetchCrawler($path, $method = 'GET', $authentication = false, $success = true)
858
    {
859 1
        $client = $this->makeClient($authentication);
860 1
        $crawler = $client->request($method, $path);
861
862 1
        $this->isSuccessful($client->getResponse(), $success);
0 ignored issues
show
Bug introduced by
It seems like $client->getResponse() can be null; however, isSuccessful() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
863
864 1
        return $crawler;
865
    }
866
867
    /**
868
     * @param UserInterface $user
869
     * @param string        $firewallName
870
     *
871
     * @return WebTestCase
872
     */
873 2
    public function loginAs(UserInterface $user, $firewallName)
874
    {
875 2
        $this->firewallLogins[$firewallName] = $user;
876
877 2
        return $this;
878
    }
879
880
    /**
881
     * Asserts that the HTTP response code of the last request performed by
882
     * $client matches the expected code. If not, raises an error with more
883
     * information.
884
     *
885
     * @param $expectedStatusCode
886
     * @param Client $client
887
     */
888 12
    public function assertStatusCode($expectedStatusCode, Client $client)
889
    {
890 12
        HttpAssertions::assertStatusCode($expectedStatusCode, $client);
891 9
    }
892
893
    /**
894
     * Assert that the last validation errors within $container match the
895
     * expected keys.
896
     *
897
     * @param array              $expected  A flat array of field names
898
     * @param ContainerInterface $container
899
     */
900 3
    public function assertValidationErrors(array $expected, ContainerInterface $container)
901
    {
902 3
        HttpAssertions::assertValidationErrors($expected, $container);
903 1
    }
904
905
    /**
906
     * @param array $excludedDoctrineTables
907
     */
908 1
    public function setExcludedDoctrineTables($excludedDoctrineTables)
909
    {
910 1
        $this->excludedDoctrineTables = $excludedDoctrineTables;
911 1
    }
912
}
913