Completed
Pull Request — master (#293)
by Lukas Kahwe
15:55
created

WebTestCase::configureVerbosityForSymfony203()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 22
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

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

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...
821 1
        }
822
823 1
        return $content;
824
    }
825
826
    /**
827
     * Executes a request on the given url and returns a Crawler object.
828
     *
829
     * This method also asserts the request was successful.
830
     *
831
     * @param string $path           path of the requested page
832
     * @param string $method         The HTTP method to use, defaults to GET
833
     * @param bool   $authentication Whether to use authentication, defaults to false
834
     * @param bool   $success        Whether the response is expected to be successful
835
     *
836
     * @return Crawler
837
     */
838 1
    public function fetchCrawler($path, $method = 'GET', $authentication = false, $success = true)
839
    {
840 1
        $client = $this->makeClient($authentication);
841 1
        $crawler = $client->request($method, $path);
842
843 1
        $this->isSuccessful($client->getResponse(), $success);
0 ignored issues
show
Documentation introduced by
$client->getResponse() is of type object|null, but the function expects a object<Symfony\Component\HttpFoundation\Response>.

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...
844
845 1
        return $crawler;
846
    }
847
848
    /**
849
     * @param UserInterface $user
850
     * @param string        $firewallName
851
     *
852
     * @return WebTestCase
853
     */
854 2
    public function loginAs(UserInterface $user, $firewallName)
855
    {
856 2
        $this->firewallLogins[$firewallName] = $user;
857
858 2
        return $this;
859
    }
860
861
    /**
862
     * Asserts that the HTTP response code of the last request performed by
863
     * $client matches the expected code. If not, raises an error with more
864
     * information.
865
     *
866
     * @param $expectedStatusCode
867
     * @param Client $client
868
     */
869 11
    public function assertStatusCode($expectedStatusCode, Client $client)
870
    {
871 11
        HttpAssertions::assertStatusCode($expectedStatusCode, $client);
872 8
    }
873
874
    /**
875
     * Assert that the last validation errors within $container match the
876
     * expected keys.
877
     *
878
     * @param array              $expected  A flat array of field names
879
     * @param ContainerInterface $container
880
     */
881 2
    public function assertValidationErrors(array $expected, ContainerInterface $container)
882
    {
883 2
        HttpAssertions::assertValidationErrors($expected, $container);
884 1
    }
885
}
886