Completed
Pull Request — master (#347)
by Alexis
08:08
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\OutputInterface;
19
use Symfony\Component\Console\Output\StreamOutput;
20
use Symfony\Component\DomCrawler\Crawler;
21
use Symfony\Component\BrowserKit\Cookie;
22
use Symfony\Component\HttpKernel\Kernel;
23
use Symfony\Component\HttpFoundation\Response;
24
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
25
use Symfony\Component\Security\Core\User\UserInterface;
26
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
27
use Symfony\Component\DependencyInjection\ContainerInterface;
28
use Symfony\Component\HttpFoundation\Session\Session;
29
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
30
use Symfony\Bridge\Doctrine\ManagerRegistry;
31
use Symfony\Bundle\DoctrineFixturesBundle\Common\DataFixtures\Loader;
32
use Doctrine\Common\Persistence\ObjectManager;
33
use Doctrine\Common\DataFixtures\DependentFixtureInterface;
34
use Doctrine\Common\DataFixtures\Executor\AbstractExecutor;
35
use Doctrine\Common\DataFixtures\ProxyReferenceRepository;
36
use Doctrine\DBAL\Driver\PDOSqlite\Driver as SqliteDriver;
37
use Doctrine\DBAL\Platforms\MySqlPlatform;
38
use Doctrine\ORM\EntityManager;
39
use Doctrine\ORM\Tools\SchemaTool;
40
use Nelmio\Alice\Fixtures;
41
use Liip\FunctionalTestBundle\Utils\HttpAssertions;
42
43
/**
44
 * @author Lea Haensenberger
45
 * @author Lukas Kahwe Smith <[email protected]>
46
 * @author Benjamin Eberlei <[email protected]>
47
 */
48
abstract class WebTestCase extends BaseWebTestCase
49
{
50
    protected $environment = 'test';
51
    protected $containers;
52
    protected $kernelDir;
53
    // 5 * 1024 * 1024 KB
54
    protected $maxMemory = 5242880;
55
56
    // RUN COMMAND
57
    protected $verbosityLevel;
58
    protected $decorated;
59
60
    /**
61
     * @var array
62
     */
63
    private $firewallLogins = array();
64
65
    /**
66
     * @var array
67
     */
68
    private $excludedDoctrineTables = array();
69
70
    /**
71
     * @var array
72
     */
73
    private static $cachedMetadatas = array();
74
75
    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...
76
    {
77
        $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...
78
79
        list($appname) = explode('\\', get_called_class());
80
81
        $class = $appname.'Kernel';
82
        $file = $dir.'/'.strtolower($appname).'/'.$class.'.php';
83
        if (!file_exists($file)) {
84
            return parent::getKernelClass();
85
        }
86
        require_once $file;
87
88
        return $class;
89
    }
90
91
    /**
92
     * Creates a mock object of a service identified by its id.
93
     *
94
     * @param string $id
95
     *
96
     * @return \PHPUnit_Framework_MockObject_MockBuilder
97
     */
98 1
    protected function getServiceMockBuilder($id)
99 1
    {
100
        $service = $this->getContainer()->get($id);
101
        $class = get_class($service);
102
103
        return $this->getMockBuilder($class)->disableOriginalConstructor();
104
    }
105
106
    /**
107
     * Builds up the environment to run the given command.
108
     *
109
     * @param string $name
110
     * @param array  $params
111
     * @param bool   $reuseKernel
112
     *
113
     * @return string
114
     */
115 12
    protected function runCommand($name, array $params = array(), $reuseKernel = false)
116
    {
117 12
        array_unshift($params, $name);
118
119 12
        if (!$reuseKernel) {
120 12
            if (null !== static::$kernel) {
121 9
                static::$kernel->shutdown();
122 9
            }
123
124 12
            $kernel = static::$kernel = $this->createKernel(array('environment' => $this->environment));
125 12
            $kernel->boot();
126 12
        } else {
127 2
            $kernel = $this->getContainer()->get('kernel');
128
        }
129
130 12
        $application = new Application($kernel);
131 12
        $application->setAutoExit(false);
132
133 12
        $input = new ArrayInput($params);
134 12
        $input->setInteractive(false);
135
136 12
        $fp = fopen('php://temp/maxmemory:'.$this->maxMemory, 'r+');
137 12
        $output = new StreamOutput($fp, $this->getVerbosityLevel(), $this->getDecorated());
138
139 11
        $application->run($input, $output);
140
141 11
        rewind($fp);
142
143 11
        return stream_get_contents($fp);
144
    }
145
146
    /**
147
     * Retrieves the output verbosity level.
148
     *
149
     * @see Symfony\Component\Console\Output\OutputInterface for available levels
150
     *
151
     * @return int
152
     *
153
     * @throws \OutOfBoundsException If the set value isn't accepted
154
     */
155 12
    protected function getVerbosityLevel()
156
    {
157
        // If `null`, is not yet set
158 12
        if (null === $this->verbosityLevel) {
159
            // Set the global verbosity level that is set as NORMAL by the TreeBuilder in Configuration
160 6
            $level = strtoupper($this->getContainer()->getParameter('liip_functional_test.command_verbosity'));
161 6
            $verbosity = '\Symfony\Component\Console\Output\StreamOutput::VERBOSITY_'.$level;
162
163 6
            $this->verbosityLevel = constant($verbosity);
164 6
        }
165
166
        // If string, it is set by the developer, so check that the value is an accepted one
167 12
        if (is_string($this->verbosityLevel)) {
168 6
            $level = strtoupper($this->verbosityLevel);
169 6
            $verbosity = '\Symfony\Component\Console\Output\StreamOutput::VERBOSITY_'.$level;
170
171 6
            if (!defined($verbosity)) {
172 1
                throw new \OutOfBoundsException(
173 1
                    sprintf('The set value "%s" for verbosityLevel is not valid. Accepted are: "quiet", "normal", "verbose", "very_verbose" and "debug".', $level)
174 1
                );
175
            }
176
177 5
            $this->verbosityLevel = constant($verbosity);
178 5
        }
179
180 11
        return $this->verbosityLevel;
181
    }
182
183 6
    public function setVerbosityLevel($level)
184
    {
185 6
        $this->verbosityLevel = $level;
186 6
    }
187
188
    /**
189
     * Retrieves the flag indicating if the output should be decorated or not.
190
     *
191
     * @return bool
192
     */
193 11
    protected function getDecorated()
194
    {
195 11
        if (null === $this->decorated) {
196
            // Set the global decoration flag that is set to `true` by the TreeBuilder in Configuration
197 5
            $this->decorated = $this->getContainer()->getParameter('liip_functional_test.command_decoration');
198 5
        }
199
200
        // Check the local decorated flag
201 11
        if (false === is_bool($this->decorated)) {
202
            throw new \OutOfBoundsException(
203
                sprintf('`WebTestCase::decorated` has to be `bool`. "%s" given.', gettype($this->decorated))
204
            );
205
        }
206
207 11
        return $this->decorated;
208
    }
209
210 6
    public function isDecorated($decorated)
211
    {
212 6
        $this->decorated = $decorated;
213 6
    }
214
215
    /**
216
     * Get an instance of the dependency injection container.
217
     * (this creates a kernel *without* parameters).
218
     *
219
     * @return ContainerInterface
220
     */
221 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...
222
    {
223 50
        if (!empty($this->kernelDir)) {
224
            $tmpKernelDir = isset($_SERVER['KERNEL_DIR']) ? $_SERVER['KERNEL_DIR'] : null;
225
            $_SERVER['KERNEL_DIR'] = getcwd().$this->kernelDir;
226
        }
227
228 50
        $cacheKey = $this->kernelDir.'|'.$this->environment;
229 50
        if (empty($this->containers[$cacheKey])) {
230
            $options = array(
231 49
                'environment' => $this->environment,
232 49
            );
233 49
            $kernel = $this->createKernel($options);
234 49
            $kernel->boot();
235
236 49
            $this->containers[$cacheKey] = $kernel->getContainer();
237 49
        }
238
239 50
        if (isset($tmpKernelDir)) {
240
            $_SERVER['KERNEL_DIR'] = $tmpKernelDir;
241
        }
242
243 50
        return $this->containers[$cacheKey];
244
    }
245
246
    /**
247
     * This function finds the time when the data blocks of a class definition
248
     * file were being written to, that is, the time when the content of the
249
     * file was changed.
250
     *
251
     * @param string $class The fully qualified class name of the fixture class to
252
     *                      check modification date on
253
     *
254
     * @return \DateTime|null
255
     */
256 3
    protected function getFixtureLastModified($class)
257
    {
258 3
        $lastModifiedDateTime = null;
259
260 3
        $reflClass = new \ReflectionClass($class);
261 3
        $classFileName = $reflClass->getFileName();
262
263 3
        if (file_exists($classFileName)) {
264 3
            $lastModifiedDateTime = new \DateTime();
265 3
            $lastModifiedDateTime->setTimestamp(filemtime($classFileName));
266 3
        }
267
268 3
        return $lastModifiedDateTime;
269
    }
270
271
    /**
272
     * Determine if the Fixtures that define a database backup have been
273
     * modified since the backup was made.
274
     *
275
     * @param array  $classNames The fixture classnames to check
276
     * @param string $backup     The fixture backup SQLite database file path
277
     *
278
     * @return bool TRUE if the backup was made since the modifications to the
279
     *              fixtures; FALSE otherwise
280
     */
281 7
    protected function isBackupUpToDate(array $classNames, $backup)
282
    {
283 7
        $backupLastModifiedDateTime = new \DateTime();
284 7
        $backupLastModifiedDateTime->setTimestamp(filemtime($backup));
285
286
        /** @var \Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader $loader */
287 7
        $loader = $this->getFixtureLoader($this->getContainer(), $classNames);
288
289
        // Use loader in order to fetch all the dependencies fixtures.
290 7
        foreach ($loader->getFixtures() as $className) {
291 3
            $fixtureLastModifiedDateTime = $this->getFixtureLastModified($className);
292 3
            if ($backupLastModifiedDateTime < $fixtureLastModifiedDateTime) {
293 1
                return false;
294
            }
295 7
        }
296
297 7
        return true;
298
    }
299
300
    /**
301
     * Set the database to the provided fixtures.
302
     *
303
     * Drops the current database and then loads fixtures using the specified
304
     * classes. The parameter is a list of fully qualified class names of
305
     * classes that implement Doctrine\Common\DataFixtures\FixtureInterface
306
     * so that they can be loaded by the DataFixtures Loader::addFixture
307
     *
308
     * When using SQLite this method will automatically make a copy of the
309
     * loaded schema and fixtures which will be restored automatically in
310
     * case the same fixture classes are to be loaded again. Caveat: changes
311
     * to references and/or identities may go undetected.
312
     *
313
     * Depends on the doctrine data-fixtures library being available in the
314
     * class path.
315
     *
316
     * @param array  $classNames   List of fully qualified class names of fixtures to load
317
     * @param string $omName       The name of object manager to use
318
     * @param string $registryName The service id of manager registry to use
319
     * @param int    $purgeMode    Sets the ORM purge mode
320
     *
321
     * @return null|AbstractExecutor
322
     */
323 36
    protected function loadFixtures(array $classNames, $omName = null, $registryName = 'doctrine', $purgeMode = null)
324
    {
325 36
        $container = $this->getContainer();
326
        /** @var ManagerRegistry $registry */
327 36
        $registry = $container->get($registryName);
328
        /** @var ObjectManager $om */
329 36
        $om = $registry->getManager($omName);
330 36
        $type = $registry->getName();
331
332 36
        $executorClass = 'PHPCR' === $type && class_exists('Doctrine\Bundle\PHPCRBundle\DataFixtures\PHPCRExecutor')
333 36
            ? 'Doctrine\Bundle\PHPCRBundle\DataFixtures\PHPCRExecutor'
334 36
            : 'Doctrine\\Common\\DataFixtures\\Executor\\'.$type.'Executor';
335 36
        $referenceRepository = new ProxyReferenceRepository($om);
336 36
        $cacheDriver = $om->getMetadataFactory()->getCacheDriver();
337
338 36
        if ($cacheDriver) {
339 36
            $cacheDriver->deleteAll();
340 36
        }
341
342 36
        if ('ORM' === $type) {
343 35
            $connection = $om->getConnection();
344 35
            if ($connection->getDriver() instanceof SqliteDriver) {
345 30
                $params = $connection->getParams();
346 30
                if (isset($params['master'])) {
347
                    $params = $params['master'];
348
                }
349
350 30
                $name = isset($params['path']) ? $params['path'] : (isset($params['dbname']) ? $params['dbname'] : false);
351 30
                if (!$name) {
352
                    throw new \InvalidArgumentException("Connection does not contain a 'path' or 'dbname' parameter and cannot be dropped.");
353
                }
354
355 30
                if (!isset(self::$cachedMetadatas[$omName])) {
356 10
                    self::$cachedMetadatas[$omName] = $om->getMetadataFactory()->getAllMetadata();
357 10
                    usort(self::$cachedMetadatas[$omName], function ($a, $b) {
358
                        return strcmp($a->name, $b->name);
359 10
                    });
360 10
                }
361 30
                $metadatas = self::$cachedMetadatas[$omName];
362
363 30
                if ($container->getParameter('liip_functional_test.cache_sqlite_db')) {
364 9
                    $backup = $container->getParameter('kernel.cache_dir').'/test_'.md5(serialize($metadatas).serialize($classNames)).'.db';
365 9
                    if (file_exists($backup) && file_exists($backup.'.ser') && $this->isBackupUpToDate($classNames, $backup)) {
366 7
                        $connection = $this->getContainer()->get('doctrine.orm.entity_manager')->getConnection();
367 7
                        if (null !== $connection) {
368 7
                            $connection->close();
369 7
                        }
370
371 7
                        $om->flush();
372 7
                        $om->clear();
373
374 7
                        $this->preFixtureBackupRestore($om, $referenceRepository, $backup);
375
376 7
                        copy($backup, $name);
377
378 7
                        $executor = new $executorClass($om);
379 7
                        $executor->setReferenceRepository($referenceRepository);
380 7
                        $executor->getReferenceRepository()->load($backup);
381
382 7
                        $this->postFixtureBackupRestore($backup);
383
384 7
                        return $executor;
385
                    }
386 3
                }
387
388
                // TODO: handle case when using persistent connections. Fail loudly?
389 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...
390 24
                $schemaTool->dropDatabase();
391 24
                if (!empty($metadatas)) {
392 24
                    $schemaTool->createSchema($metadatas);
393 24
                }
394 24
                $this->postFixtureSetup();
395
396 24
                $executor = new $executorClass($om);
397 24
                $executor->setReferenceRepository($referenceRepository);
398 24
            }
399 29
        }
400
401 30
        if (empty($executor)) {
402 6
            $purgerClass = 'Doctrine\\Common\\DataFixtures\\Purger\\'.$type.'Purger';
403 6
            if ('PHPCR' === $type) {
404 1
                $purger = new $purgerClass($om);
405 1
                $initManager = $container->has('doctrine_phpcr.initializer_manager')
406 1
                    ? $container->get('doctrine_phpcr.initializer_manager')
407 1
                    : null;
408
409 1
                $executor = new $executorClass($om, $purger, $initManager);
410 1
            } else {
411 5
                if ('ORM' === $type) {
412 5
                    $purger = new $purgerClass(null, $this->excludedDoctrineTables);
413 5
                } else {
414
                    $purger = new $purgerClass();
415
                }
416
417 5
                if (null !== $purgeMode) {
418 2
                    $purger->setPurgeMode($purgeMode);
419 2
                }
420
421 5
                $executor = new $executorClass($om, $purger);
422
            }
423
424 6
            $executor->setReferenceRepository($referenceRepository);
425 6
            $executor->purge();
426 6
        }
427
428 30
        $loader = $this->getFixtureLoader($container, $classNames);
429
430 30
        $executor->execute($loader->getFixtures(), true);
431
432 30
        if (isset($name) && isset($backup)) {
433 3
            $this->preReferenceSave($om, $executor, $backup);
434
435 3
            $executor->getReferenceRepository()->save($backup);
436 3
            copy($name, $backup);
437
438 3
            $this->postReferenceSave($om, $executor, $backup);
439 3
        }
440
441 30
        return $executor;
442
    }
443
444
    /**
445
     * Clean database.
446
     *
447
     * @param ManagerRegistry $registry
448
     * @param EntityManager   $om
449
     * @param null            $omName
450
     * @param string          $registryName
451
     * @param int             $purgeMode
452
     */
453 9
    private function cleanDatabase(ManagerRegistry $registry, EntityManager $om, $omName = null, $registryName = 'doctrine', $purgeMode = null)
454
    {
455 9
        $connection = $om->getConnection();
456
457 9
        $mysql = ('ORM' === $registry->getName()
458 9
            && $connection->getDatabasePlatform() instanceof MySqlPlatform);
459
460 9
        if ($mysql) {
461 1
            $connection->query('SET FOREIGN_KEY_CHECKS=0');
462 1
        }
463
464 9
        $this->loadFixtures(array(), $omName, $registryName, $purgeMode);
465
466 9
        if ($mysql) {
467 1
            $connection->query('SET FOREIGN_KEY_CHECKS=1');
468 1
        }
469 9
    }
470
471
    /**
472
     * Locate fixture files.
473
     *
474
     * @param array $paths
475
     *
476
     * @return array $files
477
     *
478
     * @throws \InvalidArgumentException if a wrong path is given outside a bundle
479
     */
480 10
    private function locateResources($paths)
481
    {
482 10
        $files = array();
483
484 10
        $kernel = $this->getContainer()->get('kernel');
485
486 10
        foreach ($paths as $path) {
487 10
            if ('@' !== $path[0]) {
488 3
                if (!file_exists($path)) {
489 1
                    throw new \InvalidArgumentException(sprintf('Unable to find file "%s".', $path));
490
                }
491 2
                $files[] = $path;
492
493 2
                continue;
494
            }
495
496 7
            $files[] = $kernel->locateResource($path);
497 8
        }
498
499 8
        return $files;
500
    }
501
502
    /**
503
     * @param array  $paths        Either symfony resource locators (@ BundleName/etc) or actual file paths
504
     * @param bool   $append
505
     * @param null   $omName
506
     * @param string $registryName
507
     * @param int    $purgeMode
508
     *
509
     * @return array
510
     *
511
     * @throws \BadMethodCallException
512
     */
513 10
    public function loadFixtureFiles(array $paths = array(), $append = false, $omName = null, $registryName = 'doctrine', $purgeMode = null)
514
    {
515 10
        if (!class_exists('Nelmio\Alice\Fixtures')) {
516
            // This class is available during tests, no exception will be thrown.
517
            // @codeCoverageIgnoreStart
518
            throw new \BadMethodCallException('nelmio/alice should be installed to use this method.');
519
            // @codeCoverageIgnoreEnd
520
        }
521
522
        /** @var ContainerInterface $container */
523 10
        $container = $this->getContainer();
524
525
        /** @var ManagerRegistry $registry */
526 10
        $registry = $container->get($registryName);
527
528
        /** @var EntityManager $om */
529 10
        $om = $registry->getManager($omName);
530
531 10
        if (false === $append) {
532 9
            $this->cleanDatabase($registry, $om, $omName, $registryName, $purgeMode);
533 9
        }
534
535 10
        $files = $this->locateResources($paths);
536
537
        // Check if the Hautelook AliceBundle is registered and if yes, use it instead of Nelmio Alice
538 8
        $hautelookLoaderServiceName = 'hautelook_alice.fixtures.loader';
539 8
        if ($container->has($hautelookLoaderServiceName)) {
540 3
            $loaderService = $container->get($hautelookLoaderServiceName);
541 3
            $persisterClass = class_exists('Nelmio\Alice\ORM\Doctrine') ?
542 3
                'Nelmio\Alice\ORM\Doctrine' :
543 3
                'Nelmio\Alice\Persister\Doctrine';
544
545 3
            return $loaderService->load(new $persisterClass($om), $files);
546
        }
547
548 5
        return Fixtures::load($files, $om);
549
    }
550
551
    /**
552
     * Callback function to be executed after Schema creation.
553
     * Use this to execute acl:init or other things necessary.
554
     */
555 24
    protected function postFixtureSetup()
556
    {
557 24
    }
558
559
    /**
560
     * Callback function to be executed after Schema restore.
561
     *
562
     * @return WebTestCase
563
     *
564
     * @deprecated since version 1.8, to be removed in 2.0. Use postFixtureBackupRestore method instead.
565
     */
566 7
    protected function postFixtureRestore()
567
    {
568 7
    }
569
570
    /**
571
     * Callback function to be executed before Schema restore.
572
     *
573
     * @param ObjectManager            $manager             The object manager
574
     * @param ProxyReferenceRepository $referenceRepository The reference repository
575
     *
576
     * @return WebTestCase
577
     *
578
     * @deprecated since version 1.8, to be removed in 2.0. Use preFixtureBackupRestore method instead.
579
     */
580 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...
581
    {
582 7
    }
583
584
    /**
585
     * Callback function to be executed after Schema restore.
586
     *
587
     * @param string $backupFilePath Path of file used to backup the references of the data fixtures
588
     *
589
     * @return WebTestCase
590
     */
591 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...
592
    {
593 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...
594
595 7
        return $this;
596
    }
597
598
    /**
599
     * Callback function to be executed before Schema restore.
600
     *
601
     * @param ObjectManager            $manager             The object manager
602
     * @param ProxyReferenceRepository $referenceRepository The reference repository
603
     * @param string                   $backupFilePath      Path of file used to backup the references of the data fixtures
604
     *
605
     * @return WebTestCase
606
     */
607 7
    protected function preFixtureBackupRestore(
608
        ObjectManager $manager,
609
        ProxyReferenceRepository $referenceRepository,
610
        $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...
611
    ) {
612 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...
613
614 7
        return $this;
615
    }
616
617
    /**
618
     * Callback function to be executed after save of references.
619
     *
620
     * @param ObjectManager    $manager        The object manager
621
     * @param AbstractExecutor $executor       Executor of the data fixtures
622
     * @param string           $backupFilePath Path of file used to backup the references of the data fixtures
623
     *
624
     * @return WebTestCase
625
     */
626 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...
627
    {
628 3
    }
629
630
    /**
631
     * Callback function to be executed before save of references.
632
     *
633
     * @param ObjectManager    $manager        The object manager
634
     * @param AbstractExecutor $executor       Executor of the data fixtures
635
     * @param string           $backupFilePath Path of file used to backup the references of the data fixtures
636
     *
637
     * @return WebTestCase
638
     */
639 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...
640
    {
641 3
    }
642
643
    /**
644
     * Retrieve Doctrine DataFixtures loader.
645
     *
646
     * @param ContainerInterface $container
647
     * @param array              $classNames
648
     *
649
     * @return Loader
650
     */
651 36
    protected function getFixtureLoader(ContainerInterface $container, array $classNames)
652
    {
653 36
        $loaderClass = class_exists('Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader')
654 36
            ? 'Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader'
655 36
            : (class_exists('Doctrine\Bundle\FixturesBundle\Common\DataFixtures\Loader')
656
                // This class is not available during tests.
657
                // @codeCoverageIgnoreStart
658
                ? 'Doctrine\Bundle\FixturesBundle\Common\DataFixtures\Loader'
659
                // @codeCoverageIgnoreEnd
660 36
                : 'Symfony\Bundle\DoctrineFixturesBundle\Common\DataFixtures\Loader');
661
662 36
        $loader = new $loaderClass($container);
663
664 36
        foreach ($classNames as $className) {
665 11
            $this->loadFixtureClass($loader, $className);
666 36
        }
667
668 36
        return $loader;
669
    }
670
671
    /**
672
     * Load a data fixture class.
673
     *
674
     * @param Loader $loader
675
     * @param string $className
676
     */
677 11
    protected function loadFixtureClass($loader, $className)
678
    {
679 11
        $fixture = new $className();
680
681 11
        if ($loader->hasFixture($fixture)) {
682 2
            unset($fixture);
683
684 2
            return;
685
        }
686
687 11
        $loader->addFixture($fixture);
688
689 11
        if ($fixture instanceof DependentFixtureInterface) {
690 2
            foreach ($fixture->getDependencies() as $dependency) {
691 2
                $this->loadFixtureClass($loader, $dependency);
692 2
            }
693 2
        }
694 11
    }
695
696
    /**
697
     * Creates an instance of a lightweight Http client.
698
     *
699
     * If $authentication is set to 'true' it will use the content of
700
     * 'liip_functional_test.authentication' to log in.
701
     *
702
     * $params can be used to pass headers to the client, note that they have
703
     * to follow the naming format used in $_SERVER.
704
     * Example: 'HTTP_X_REQUESTED_WITH' instead of 'X-Requested-With'
705
     *
706
     * @param bool|array $authentication
707
     * @param array      $params
708
     *
709
     * @return Client
710
     */
711 55
    protected function makeClient($authentication = false, array $params = array())
712
    {
713 55
        if ($authentication) {
714 2
            if (true === $authentication) {
715
                $authentication = array(
716 1
                    'username' => $this->getContainer()
717 1
                        ->getParameter('liip_functional_test.authentication.username'),
718 1
                    'password' => $this->getContainer()
719 1
                        ->getParameter('liip_functional_test.authentication.password'),
720 1
                );
721 1
            }
722
723 2
            $params = array_merge($params, array(
724 2
                'PHP_AUTH_USER' => $authentication['username'],
725 2
                'PHP_AUTH_PW' => $authentication['password'],
726 2
            ));
727 2
        }
728
729 55
        $client = static::createClient(array('environment' => $this->environment), $params);
730
731 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...
732
            // has to be set otherwise "hasPreviousSession" in Request returns false.
733 2
            $options = $client->getContainer()->getParameter('session.storage.options');
734
735 2
            if (!$options || !isset($options['name'])) {
736
                throw new \InvalidArgumentException('Missing session.storage.options#name');
737
            }
738
739 2
            $session = $client->getContainer()->get('session');
740
            // Since the namespace of the session changed in symfony 2.1, instanceof can be used to check the version.
741 2
            if ($session instanceof Session) {
742 2
                $session->setId(uniqid());
743 2
            }
744
745 2
            $client->getCookieJar()->set(new Cookie($options['name'], $session->getId()));
746
747
            /** @var $user UserInterface */
748 2
            foreach ($this->firewallLogins as $firewallName => $user) {
749 2
                $token = $this->createUserToken($user, $firewallName);
750
751
                // BC: security.token_storage is available on Symfony 2.6+
752
                // see http://symfony.com/blog/new-in-symfony-2-6-security-component-improvements
753 2
                if ($client->getContainer()->has('security.token_storage')) {
754 2
                    $tokenStorage = $client->getContainer()->get('security.token_storage');
755 2
                } else {
756
                    // This block will never be reached with Symfony 2.6+
757
                    // @codeCoverageIgnoreStart
758
                    $tokenStorage = $client->getContainer()->get('security.context');
759
                    // @codeCoverageIgnoreEnd
760
                }
761
762 2
                $tokenStorage->setToken($token);
763 2
                $session->set('_security_'.$firewallName, serialize($token));
764 2
            }
765
766 2
            $session->save();
767 2
        }
768
769 55
        return $client;
770
    }
771
772
    /**
773
     * Create User Token.
774
     *
775
     * Factory method for creating a User Token object for the firewall based on
776
     * the user object provided. By default it will be a Username/Password
777
     * Token based on the user's credentials, but may be overridden for custom
778
     * tokens in your applications.
779
     *
780
     * @param UserInterface $user         The user object to base the token off of
781
     * @param string        $firewallName name of the firewall provider to use
782
     *
783
     * @return TokenInterface The token to be used in the security context
784
     */
785 2
    protected function createUserToken(UserInterface $user, $firewallName)
786
    {
787 2
        return new UsernamePasswordToken(
788 2
            $user,
789 2
            null,
790 2
            $firewallName,
791 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...
792 2
        );
793
    }
794
795
    /**
796
     * Extracts the location from the given route.
797
     *
798
     * @param string $route    The name of the route
799
     * @param array  $params   Set of parameters
800
     * @param int    $absolute
801
     *
802
     * @return string
803
     */
804 1
    protected function getUrl($route, $params = array(), $absolute = UrlGeneratorInterface::ABSOLUTE_PATH)
805
    {
806 1
        return $this->getContainer()->get('router')->generate($route, $params, $absolute);
807
    }
808
809
    /**
810
     * Checks the success state of a response.
811
     *
812
     * @param Response $response Response object
813
     * @param bool     $success  to define whether the response is expected to be successful
814
     * @param string   $type
815
     */
816 6
    public function isSuccessful(Response $response, $success = true, $type = 'text/html')
817
    {
818 6
        HttpAssertions::isSuccessful($response, $success, $type);
819 5
    }
820
821
    /**
822
     * Executes a request on the given url and returns the response contents.
823
     *
824
     * This method also asserts the request was successful.
825
     *
826
     * @param string $path           path of the requested page
827
     * @param string $method         The HTTP method to use, defaults to GET
828
     * @param bool   $authentication Whether to use authentication, defaults to false
829
     * @param bool   $success        to define whether the response is expected to be successful
830
     *
831
     * @return string
832
     */
833 1
    public function fetchContent($path, $method = 'GET', $authentication = false, $success = true)
834
    {
835 1
        $client = $this->makeClient($authentication);
836 1
        $client->request($method, $path);
837
838 1
        $content = $client->getResponse()->getContent();
839 1
        if (is_bool($success)) {
840 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...
841 1
        }
842
843 1
        return $content;
844
    }
845
846
    /**
847
     * Executes a request on the given url and returns a Crawler object.
848
     *
849
     * This method also asserts the request was successful.
850
     *
851
     * @param string $path           path of the requested page
852
     * @param string $method         The HTTP method to use, defaults to GET
853
     * @param bool   $authentication Whether to use authentication, defaults to false
854
     * @param bool   $success        Whether the response is expected to be successful
855
     *
856
     * @return Crawler
857
     */
858 1
    public function fetchCrawler($path, $method = 'GET', $authentication = false, $success = true)
859
    {
860 1
        $client = $this->makeClient($authentication);
861 1
        $crawler = $client->request($method, $path);
862
863 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...
864
865 1
        return $crawler;
866
    }
867
868
    /**
869
     * @param UserInterface $user
870
     * @param string        $firewallName
871
     *
872
     * @return WebTestCase
873
     */
874 2
    public function loginAs(UserInterface $user, $firewallName)
875
    {
876 2
        $this->firewallLogins[$firewallName] = $user;
877
878 2
        return $this;
879
    }
880
881
    /**
882
     * Asserts that the HTTP response code of the last request performed by
883
     * $client matches the expected code. If not, raises an error with more
884
     * information.
885
     *
886
     * @param $expectedStatusCode
887
     * @param Client $client
888
     */
889 12
    public function assertStatusCode($expectedStatusCode, Client $client)
890
    {
891 12
        HttpAssertions::assertStatusCode($expectedStatusCode, $client);
892 9
    }
893
894
    /**
895
     * Assert that the last validation errors within $container match the
896
     * expected keys.
897
     *
898
     * @param array              $expected  A flat array of field names
899
     * @param ContainerInterface $container
900
     */
901 3
    public function assertValidationErrors(array $expected, ContainerInterface $container)
902
    {
903 3
        HttpAssertions::assertValidationErrors($expected, $container);
904 1
    }
905
906
    /**
907
     * @param array $excludedDoctrineTables
908
     */
909 1
    public function setExcludedDoctrineTables($excludedDoctrineTables)
910
    {
911 1
        $this->excludedDoctrineTables = $excludedDoctrineTables;
912 1
    }
913
}
914