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

WebTestCase::getVerbosityLevel()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 27
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 4

Importance

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

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

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

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

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

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