Completed
Push — master ( 6fd3d5...da1402 )
by Lukas Kahwe
06:43
created

WebTestCase::configureVerbosityForSymfony20301()   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 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 22
ccs 0
cts 16
cp 0
rs 8.6737
cc 5
eloc 15
nc 5
nop 1
crap 30
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\Tools\SchemaTool;
39
use Nelmio\Alice\Fixtures;
40
41
/**
42
 * @author Lea Haensenberger
43
 * @author Lukas Kahwe Smith <[email protected]>
44
 * @author Benjamin Eberlei <[email protected]>
45
 */
46
abstract class WebTestCase extends BaseWebTestCase
47
{
48
    protected $environment = 'test';
49
    protected $containers;
50
    protected $kernelDir;
51
    // 5 * 1024 * 1024 KB
52
    protected $maxMemory = 5242880;
53
54
    // RUN COMMAND
55
    protected $verbosityLevel;
56
    protected $decorated;
57
58
    /**
59
     * @var array
60
     */
61
    private $firewallLogins = array();
62
63
    /**
64
     * @var array
65
     */
66
    private static $cachedMetadatas = array();
67
68 1
    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...
69
    {
70 1
        $dir = isset($_SERVER['KERNEL_DIR']) ? $_SERVER['KERNEL_DIR'] : self::getPhpUnitXmlDir();
71
72 1
        list($appname) = explode('\\', get_called_class());
73
74 1
        $class = $appname.'Kernel';
75 1
        $file = $dir.'/'.strtolower($appname).'/'.$class.'.php';
76 1
        if (!file_exists($file)) {
77 1
            return parent::getKernelClass();
78
        }
79
        require_once $file;
80
81
        return $class;
82
    }
83
84
    /**
85
     * Creates a mock object of a service identified by its id.
86
     *
87
     * @param string $id
88
     *
89
     * @return PHPUnit_Framework_MockObject_MockBuilder
90
     */
91
    protected function getServiceMockBuilder($id)
92
    {
93
        $service = $this->getContainer()->get($id);
94
        $class = get_class($service);
95
96
        return $this->getMockBuilder($class)->disableOriginalConstructor();
97
    }
98
99
    /**
100
     * Builds up the environment to run the given command.
101
     *
102
     * @param string $name
103
     * @param array  $params
104
     * @param bool   $reuseKernel
105
     *
106
     * @return string
107
     */
108 9
    protected function runCommand($name, array $params = array(), $reuseKernel = false)
109
    {
110 9
        array_unshift($params, $name);
111
112 9
        if (!$reuseKernel) {
113 9
            if (null !== static::$kernel) {
114 8
                static::$kernel->shutdown();
115 8
            }
116
117 9
            $kernel = static::$kernel = $this->createKernel(array('environment' => $this->environment));
118 9
            $kernel->boot();
119 9
        } else {
120 2
            $kernel = $this->getContainer()->get('kernel');
121 1
        }
122
123 9
        $application = new Application($kernel);
124 9
        $application->setAutoExit(false);
125
126 9
        if ('20301' === Kernel::VERSION_ID) {
127
            $params = $this->configureVerbosityForSymfony20301($params);
128
        }
129
130 9
        $input = new ArrayInput($params);
131 9
        $input->setInteractive(false);
132
133 9
        $fp = fopen('php://temp/maxmemory:'.$this->maxMemory, 'r+');
134 9
        $output = new StreamOutput($fp, $this->getVerbosityLevel(), $this->getDecorated());
135
136 9
        $application->run($input, $output);
137
138 9
        rewind($fp);
139
140 9
        return stream_get_contents($fp);
141
    }
142
143
    /**
144
     * Retrieves the output verbosity level.
145
     *
146
     * @see Symfony\Component\Console\Output\OutputInterface for available levels
147
     *
148
     * @return int
149
     *
150
     * @throws \OutOfBoundsException If the set value isn't accepted
151
     */
152 9
    protected function getVerbosityLevel()
153
    {
154
        // If `null`, is not yet set
155 9
        if (null === $this->verbosityLevel) {
156
            // Set the global verbosity level that is set as NORMAL by the TreeBuilder in Configuration
157 4
            $level = strtoupper($this->getContainer()->getParameter('liip_functional_test.command_verbosity'));
158 4
            $verbosity = '\Symfony\Component\Console\Output\StreamOutput::VERBOSITY_'.$level;
159
160 4
            $this->verbosityLevel = constant($verbosity);
161 4
        }
162
163
        // If string, it is set by the developer, so check that the value is an accepted one
164 9
        if (is_string($this->verbosityLevel)) {
165 5
            $level = strtoupper($this->verbosityLevel);
166 5
            $verbosity = '\Symfony\Component\Console\Output\StreamOutput::VERBOSITY_'.$level;
167
168 5
            if (null === constant($verbosity)) {
169
                throw new \OutOfBoundsException(
170
                    'The set value "%s" for verbosityLevel is not valid. Accepted are: "quiet", "normal", "verbose", "very_verbose" and "debug".
171
                    ');
172
            }
173
174 5
            $this->verbosityLevel = constant($verbosity);
175 5
        }
176
177 9
        return $this->verbosityLevel;
178
    }
179
180
    /**
181
     * In Symfony 2.3.1 the verbosity level has to be set through {Symfony\Component\Console\Input\ArrayInput} and not
182
     * in {Symfony\Component\Console\Output\OutputInterface}.
183
     *
184
     * This method builds $params to be passed to {Symfony\Component\Console\Input\ArrayInput}.
185
     *
186
     * @param array $params
187
     *
188
     * @return array
189
     */
190
    private function configureVerbosityForSymfony20301(array $params)
191
    {
192
        switch ($this->getVerbosityLevel()) {
193
            case OutputInterface::VERBOSITY_QUIET:
194
                $params['-q'] = '-q';
195
                break;
196
197
            case OutputInterface::VERBOSITY_VERBOSE:
198
                $params['-v'] = '';
199
                break;
200
201
            case OutputInterface::VERBOSITY_VERY_VERBOSE:
202
                $params['-vv'] = '';
203
                break;
204
205
            case OutputInterface::VERBOSITY_DEBUG:
206
                $params['-vvv'] = '';
207
                break;
208
        }
209
210
        return $params;
211
    }
212
213 5
    public function setVerbosityLevel($level)
214
    {
215 5
        $this->verbosityLevel = $level;
216 5
    }
217
218
    /**
219
     * Retrieves the flag indicating if the output should be decorated or not.
220
     *
221
     * @return bool
222
     */
223 9
    protected function getDecorated()
224
    {
225 9
        if (null === $this->decorated) {
226
            // Set the global decoration flag that is set to `true` by the TreeBuilder in Configuration
227 3
            $this->decorated = $this->getContainer()->getParameter('liip_functional_test.command_decoration');
228 3
        }
229
230
        // Check the local decorated flag
231 9
        if (false === is_bool($this->decorated)) {
232
            throw new \OutOfBoundsException(
233
                sprintf('`WebTestCase::decorated` has to be `bool`. "%s" given.', gettype($this->decorated))
234
            );
235
        }
236
237 9
        return $this->decorated;
238
    }
239
240 6
    public function isDecorated($decorated)
241
    {
242 6
        $this->decorated = $decorated;
243 6
    }
244
245
    /**
246
     * Get an instance of the dependency injection container.
247
     * (this creates a kernel *without* parameters).
248
     *
249
     * @return ContainerInterface
250
     */
251 29
    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...
252
    {
253 29
        if (!empty($this->kernelDir)) {
254
            $tmpKernelDir = isset($_SERVER['KERNEL_DIR']) ? $_SERVER['KERNEL_DIR'] : null;
255
            $_SERVER['KERNEL_DIR'] = getcwd().$this->kernelDir;
256
        }
257
258 29
        $cacheKey = $this->kernelDir.'|'.$this->environment;
259 29
        if (empty($this->containers[$cacheKey])) {
260
            $options = array(
261 29
                'environment' => $this->environment,
262 29
            );
263 29
            $kernel = $this->createKernel($options);
264 29
            $kernel->boot();
265
266 29
            $this->containers[$cacheKey] = $kernel->getContainer();
267 29
        }
268
269 29
        if (isset($tmpKernelDir)) {
270
            $_SERVER['KERNEL_DIR'] = $tmpKernelDir;
271
        }
272
273 29
        return $this->containers[$cacheKey];
274
    }
275
276
    /**
277
     * This function finds the time when the data blocks of a class definition
278
     * file were being written to, that is, the time when the content of the
279
     * file was changed.
280
     *
281
     * @param string $class The fully qualified class name of the fixture class to
282
     *                      check modification date on.
283
     *
284
     * @return \DateTime|null
285
     */
286 4
    protected function getFixtureLastModified($class)
287
    {
288 4
        $lastModifiedDateTime = null;
289
290 4
        $reflClass = new \ReflectionClass($class);
291 4
        $classFileName = $reflClass->getFileName();
292
293 4
        if (file_exists($classFileName)) {
294 4
            $lastModifiedDateTime = new \DateTime();
295 4
            $lastModifiedDateTime->setTimestamp(filemtime($classFileName));
296 4
        }
297
298 4
        return $lastModifiedDateTime;
299
    }
300
301
    /**
302
     * Determine if the Fixtures that define a database backup have been
303
     * modified since the backup was made.
304
     *
305
     * @param array  $classNames The fixture classnames to check
306
     * @param string $backup     The fixture backup SQLite database file path
307
     *
308
     * @return bool TRUE if the backup was made since the modifications to the
309
     *              fixtures; FALSE otherwise
310
     */
311 22
    protected function isBackupUpToDate(array $classNames, $backup)
312
    {
313 22
        $backupLastModifiedDateTime = new \DateTime();
314 22
        $backupLastModifiedDateTime->setTimestamp(filemtime($backup));
315
316 22
        foreach ($classNames as &$className) {
317 4
            $fixtureLastModifiedDateTime = $this->getFixtureLastModified($className);
318 4
            if ($backupLastModifiedDateTime < $fixtureLastModifiedDateTime) {
319
                return false;
320
            }
321 22
        }
322
323 22
        return true;
324
    }
325
326
    /**
327
     * Set the database to the provided fixtures.
328
     *
329
     * Drops the current database and then loads fixtures using the specified
330
     * classes. The parameter is a list of fully qualified class names of
331
     * classes that implement Doctrine\Common\DataFixtures\FixtureInterface
332
     * so that they can be loaded by the DataFixtures Loader::addFixture
333
     *
334
     * When using SQLite this method will automatically make a copy of the
335
     * loaded schema and fixtures which will be restored automatically in
336
     * case the same fixture classes are to be loaded again. Caveat: changes
337
     * to references and/or identities may go undetected.
338
     *
339
     * Depends on the doctrine data-fixtures library being available in the
340
     * class path.
341
     *
342
     * @param array  $classNames   List of fully qualified class names of fixtures to load
343
     * @param string $omName       The name of object manager to use
344
     * @param string $registryName The service id of manager registry to use
345
     * @param int    $purgeMode    Sets the ORM purge mode
346
     *
347
     * @return null|AbstractExecutor
348
     */
349 24
    protected function loadFixtures(array $classNames, $omName = null, $registryName = 'doctrine', $purgeMode = null)
350
    {
351 24
        $container = $this->getContainer();
352
        /** @var ManagerRegistry $registry */
353 24
        $registry = $container->get($registryName);
354 24
        $om = $registry->getManager($omName);
355 24
        $type = $registry->getName();
356
357 24
        $executorClass = 'PHPCR' === $type && class_exists('Doctrine\Bundle\PHPCRBundle\DataFixtures\PHPCRExecutor')
358 24
            ? 'Doctrine\Bundle\PHPCRBundle\DataFixtures\PHPCRExecutor'
359 24
            : 'Doctrine\\Common\\DataFixtures\\Executor\\'.$type.'Executor';
360 24
        $referenceRepository = new ProxyReferenceRepository($om);
361 24
        $cacheDriver = $om->getMetadataFactory()->getCacheDriver();
362
363 24
        if ($cacheDriver) {
364 24
            $cacheDriver->deleteAll();
365 24
        }
366
367 24
        if ('ORM' === $type) {
368 24
            $connection = $om->getConnection();
369 24
            if ($connection->getDriver() instanceof SqliteDriver) {
370 24
                $params = $connection->getParams();
371 24
                if (isset($params['master'])) {
372
                    $params = $params['master'];
373
                }
374
375 24
                $name = isset($params['path']) ? $params['path'] : (isset($params['dbname']) ? $params['dbname'] : false);
376 24
                if (!$name) {
377
                    throw new \InvalidArgumentException("Connection does not contain a 'path' or 'dbname' parameter and cannot be dropped.");
378
                }
379
380 24
                if (!isset(self::$cachedMetadatas[$omName])) {
381 1
                    self::$cachedMetadatas[$omName] = $om->getMetadataFactory()->getAllMetadata();
382
                    usort(self::$cachedMetadatas[$omName], function ($a, $b) { return strcmp($a->name, $b->name); });
383 1
                }
384 24
                $metadatas = self::$cachedMetadatas[$omName];
385
386 24
                if ($container->getParameter('liip_functional_test.cache_sqlite_db')) {
387 24
                    $backup = $container->getParameter('kernel.cache_dir').'/test_'.md5(serialize($metadatas).serialize($classNames)).'.db';
388 24
                    if (file_exists($backup) && file_exists($backup.'.ser') && $this->isBackupUpToDate($classNames, $backup)) {
389 22
                        $om->flush();
390 22
                        $om->clear();
391
392 22
                        $this->preFixtureRestore($om, $referenceRepository);
393
394 22
                        copy($backup, $name);
395
396 22
                        $executor = new $executorClass($om);
397 22
                        $executor->setReferenceRepository($referenceRepository);
398 22
                        $executor->getReferenceRepository()->load($backup);
399
400 22
                        $this->postFixtureRestore();
401
402 22
                        return $executor;
403
                    }
404 2
                }
405
406
                // TODO: handle case when using persistent connections. Fail loudly?
407 2
                $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 2
                $schemaTool->dropDatabase($name);
0 ignored issues
show
Unused Code introduced by
The call to SchemaTool::dropDatabase() has too many arguments starting with $name.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
409 2
                if (!empty($metadatas)) {
410 2
                    $schemaTool->createSchema($metadatas);
411 2
                }
412 2
                $this->postFixtureSetup();
413
414 2
                $executor = new $executorClass($om);
415 2
                $executor->setReferenceRepository($referenceRepository);
416 2
            }
417 2
        }
418
419 2
        if (empty($executor)) {
420
            $purgerClass = 'Doctrine\\Common\\DataFixtures\\Purger\\'.$type.'Purger';
421
            if ('PHPCR' === $type) {
422
                $purger = new $purgerClass($om);
423
                $initManager = $container->has('doctrine_phpcr.initializer_manager')
424
                    ? $container->get('doctrine_phpcr.initializer_manager')
425
                    : null;
426
427
                $executor = new $executorClass($om, $purger, $initManager);
428
            } else {
429
                $purger = new $purgerClass();
430
                if (null !== $purgeMode) {
431
                    $purger->setPurgeMode($purgeMode);
432
                }
433
434
                $executor = new $executorClass($om, $purger);
435
            }
436
437
            $executor->setReferenceRepository($referenceRepository);
438
            $executor->purge();
439
        }
440
441 2
        $loader = $this->getFixtureLoader($container, $classNames);
442
443 2
        $executor->execute($loader->getFixtures(), true);
444
445 2
        if (isset($name) && isset($backup)) {
446 2
            $this->preReferenceSave($om, $executor, $backup);
447
448 2
            $executor->getReferenceRepository()->save($backup);
449 2
            copy($name, $backup);
450
451 2
            $this->postReferenceSave($om, $executor, $backup);
452 2
        }
453
454 2
        return $executor;
455
    }
456
457
    /**
458
     * @param array  $paths        Either symfony resource locators (@ BundleName/etc) or actual file paths
459
     * @param bool   $append
460
     * @param null   $omName
461
     * @param string $registryName
462
     *
463
     * @return array
464
     *
465
     * @throws \BadMethodCallException
466
     */
467 2
    public function loadFixtureFiles(array $paths = array(), $append = false, $omName = null, $registryName = 'doctrine')
468
    {
469 2
        if (!class_exists('Nelmio\Alice\Fixtures')) {
470
            throw new \BadMethodCallException('nelmio/alice should be installed to use this method.');
471
        }
472
473
        /** @var ManagerRegistry $registry */
474 2
        $registry = $this->getContainer()->get($registryName);
475 2
        $om = $registry->getManager($omName);
476
477 2
        if ($append === false) {
478
            //Clean database
479 2
            $connection = $om->getConnection();
480 2
            if ($registry->getName() === 'ORM' && $connection->getDatabasePlatform() instanceof MySqlPlatform) {
481
                $connection->query('SET FOREIGN_KEY_CHECKS=0');
482
            }
483
484 2
            $this->loadFixtures(array());
485
486 2
            if ($registry->getName() === 'ORM' && $connection->getDatabasePlatform() instanceof MySqlPlatform) {
487
                $connection->query('SET FOREIGN_KEY_CHECKS=1');
488
            }
489 2
        }
490
491 2
        $files = array();
492 2
        $kernel = $this->getContainer()->get('kernel');
493 2
        foreach ($paths as $path) {
494 2
            if ($path[0] !== '@' && file_exists($path) === true) {
495 1
                $files[] = $path;
496 1
                continue;
497
            }
498
499 1
            $files[] = $kernel->locateResource($path);
500 2
        }
501
502 2
        return Fixtures::load($files, $om);
503
    }
504
505
    /**
506
     * Callback function to be executed after Schema creation.
507
     * Use this to execute acl:init or other things necessary.
508
     */
509 2
    protected function postFixtureSetup()
510
    {
511 2
    }
512
513
    /**
514
     * Callback function to be executed after Schema restore.
515
     *
516
     * @return WebTestCase
517
     */
518 22
    protected function postFixtureRestore()
519
    {
520 22
    }
521
522
    /**
523
     * Callback function to be executed before Schema restore.
524
     *
525
     * @param ObjectManager            $manager             The object manager
526
     * @param ProxyReferenceRepository $referenceRepository The reference repository
527
     *
528
     * @return WebTestCase
529
     */
530 22
    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...
531
    {
532 22
    }
533
534
    /**
535
     * Callback function to be executed after save of references.
536
     *
537
     * @param ObjectManager    $manager        The object manager
538
     * @param AbstractExecutor $executor       Executor of the data fixtures
539
     * @param string           $backupFilePath Path of file used to backup the references of the data fixtures
540
     *
541
     * @return WebTestCase
542
     */
543 2
    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...
544
    {
545 2
    }
546
547
    /**
548
     * Callback function to be executed before save of references.
549
     *
550
     * @param ObjectManager    $manager        The object manager
551
     * @param AbstractExecutor $executor       Executor of the data fixtures
552
     * @param string           $backupFilePath Path of file used to backup the references of the data fixtures
553
     *
554
     * @return WebTestCase
555
     */
556 2
    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...
557
    {
558 2
    }
559
560
    /**
561
     * Retrieve Doctrine DataFixtures loader.
562
     *
563
     * @param ContainerInterface $container
564
     * @param array              $classNames
565
     *
566
     * @return Loader
567
     */
568 2
    protected function getFixtureLoader(ContainerInterface $container, array $classNames)
569
    {
570 2
        $loaderClass = class_exists('Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader')
571 2
            ? 'Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader'
572 2
            : (class_exists('Doctrine\Bundle\FixturesBundle\Common\DataFixtures\Loader')
573
                ? 'Doctrine\Bundle\FixturesBundle\Common\DataFixtures\Loader'
574 2
                : 'Symfony\Bundle\DoctrineFixturesBundle\Common\DataFixtures\Loader');
575
576 2
        $loader = new $loaderClass($container);
577
578 2
        foreach ($classNames as $className) {
579 1
            $this->loadFixtureClass($loader, $className);
580 2
        }
581
582 2
        return $loader;
583
    }
584
585
    /**
586
     * Load a data fixture class.
587
     *
588
     * @param Loader $loader
589
     * @param string $className
590
     */
591 1
    protected function loadFixtureClass($loader, $className)
592
    {
593 1
        $fixture = new $className();
594
595 1
        if ($loader->hasFixture($fixture)) {
596
            unset($fixture);
597
598
            return;
599
        }
600
601 1
        $loader->addFixture($fixture);
602
603 1
        if ($fixture instanceof DependentFixtureInterface) {
604
            foreach ($fixture->getDependencies() as $dependency) {
605
                $this->loadFixtureClass($loader, $dependency);
606
            }
607
        }
608 1
    }
609
610
    /**
611
     * Creates an instance of a lightweight Http client.
612
     *
613
     * If $authentication is set to 'true' it will use the content of
614
     * 'liip_functional_test.authentication' to log in.
615
     *
616
     * $params can be used to pass headers to the client, note that they have
617
     * to follow the naming format used in $_SERVER.
618
     * Example: 'HTTP_X_REQUESTED_WITH' instead of 'X-Requested-With'
619
     *
620
     * @param bool|array $authentication
621
     * @param array      $params
622
     *
623
     * @return Client
624
     */
625 24
    protected function makeClient($authentication = false, array $params = array())
626
    {
627 24
        if ($authentication) {
628 5
            if ($authentication === true) {
629 3
                $authentication = $this->getContainer()->getParameter('liip_functional_test.authentication');
630 3
            }
631
632 5
            $params = array_merge($params, array(
633 5
                'PHP_AUTH_USER' => $authentication['username'],
634 5
                'PHP_AUTH_PW' => $authentication['password'],
635 5
            ));
636 5
        }
637
638 24
        $client = static::createClient(array('environment' => $this->environment), $params);
639
640 24
        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...
641
            // has to be set otherwise "hasPreviousSession" in Request returns false.
642 1
            $options = $client->getContainer()->getParameter('session.storage.options');
643
644 1
            if (!$options || !isset($options['name'])) {
645
                throw new \InvalidArgumentException('Missing session.storage.options#name');
646
            }
647
648 1
            $session = $client->getContainer()->get('session');
649
            // Since the namespace of the session changed in symfony 2.1, instanceof can be used to check the version.
650 1
            if ($session instanceof Session) {
651 1
                $session->setId(uniqid());
652 1
            }
653
654 1
            $client->getCookieJar()->set(new Cookie($options['name'], $session->getId()));
655
656
            /** @var $user UserInterface */
657 1
            foreach ($this->firewallLogins as $firewallName => $user) {
658 1
                $token = $this->createUserToken($user, $firewallName);
659
660
                // BC: security.token_storage is available on Symfony 2.6+
661
                // see http://symfony.com/blog/new-in-symfony-2-6-security-component-improvements
662 1
                if ($client->getContainer()->has('security.token_storage')) {
663 1
                    $tokenStorage = $client->getContainer()->get('security.token_storage');
664 1
                } else {
665
                    // This block will never be reached with Symfony 2.5+
666
                    // @codeCoverageIgnoreStart
667
                    $tokenStorage = $client->getContainer()->get('security.context');
668
                    // @codeCoverageIgnoreEnd
669
                }
670
671 1
                $tokenStorage->setToken($token);
672 1
                $session->set('_security_'.$firewallName, serialize($token));
673 1
            }
674
675 1
            $session->save();
676 1
        }
677
678 24
        return $client;
679
    }
680
681
    /**
682
     * Create User Token.
683
     *
684
     * Factory method for creating a User Token object for the firewall based on
685
     * the user object provided. By default it will be a Username/Password
686
     * Token based on the user's credentials, but may be overridden for custom
687
     * tokens in your applications.
688
     *
689
     * @param UserInterface $user         The user object to base the token off of
690
     * @param string        $firewallName name of the firewall provider to use
691
     *
692
     * @return TokenInterface The token to be used in the security context
693
     */
694 1
    protected function createUserToken(UserInterface $user, $firewallName)
695
    {
696 1
        return new UsernamePasswordToken(
697 1
            $user,
698 1
            null,
699 1
            $firewallName,
700 1
            $user->getRoles()
701 1
        );
702
    }
703
704
    /**
705
     * Extracts the location from the given route.
706
     *
707
     * @param string $route    The name of the route
708
     * @param array  $params   Set of parameters
709
     * @param bool   $absolute
710
     *
711
     * @return string
712
     */
713 1
    protected function getUrl($route, $params = array(), $absolute = UrlGeneratorInterface::ABSOLUTE_PATH)
714
    {
715 1
        return $this->getContainer()->get('router')->generate($route, $params, $absolute);
716
    }
717
718
    /**
719
     * Checks the success state of a response.
720
     *
721
     * @param Response $response Response object
722
     * @param bool     $success  to define whether the response is expected to be successful
723
     * @param string   $type
724
     */
725 7
    public function isSuccessful(Response $response, $success = true, $type = 'text/html')
726
    {
727
        try {
728 7
            $crawler = new Crawler();
729 7
            $crawler->addContent($response->getContent(), $type);
730 7
            if (!count($crawler->filter('title'))) {
731 2
                $title = '['.$response->getStatusCode().'] - '.$response->getContent();
732 2
            } else {
733 5
                $title = $crawler->filter('title')->text();
734
            }
735 7
        } catch (\Exception $e) {
736
            $title = $e->getMessage();
737
        }
738
739 7
        if ($success) {
740 5
            $this->assertTrue($response->isSuccessful(), 'The Response was not successful: '.$title);
741 5
        } else {
742 2
            $this->assertFalse($response->isSuccessful(), 'The Response was successful: '.$title);
743
        }
744 7
    }
745
746
    /**
747
     * Executes a request on the given url and returns the response contents.
748
     *
749
     * This method also asserts the request was successful.
750
     *
751
     * @param string $path           path of the requested page
752
     * @param string $method         The HTTP method to use, defaults to GET
753
     * @param bool   $authentication Whether to use authentication, defaults to false
754
     * @param bool   $success        to define whether the response is expected to be successful
755
     *
756
     * @return string
757
     */
758 1
    public function fetchContent($path, $method = 'GET', $authentication = false, $success = true)
759
    {
760 1
        $client = $this->makeClient($authentication);
761 1
        $client->request($method, $path);
762
763 1
        $content = $client->getResponse()->getContent();
764 1
        if (is_bool($success)) {
765 1
            $this->isSuccessful($client->getResponse(), $success);
766 1
        }
767
768 1
        return $content;
769
    }
770
771
    /**
772
     * Executes a request on the given url and returns a Crawler object.
773
     *
774
     * This method also asserts the request was successful.
775
     *
776
     * @param string $path           path of the requested page
777
     * @param string $method         The HTTP method to use, defaults to GET
778
     * @param bool   $authentication Whether to use authentication, defaults to false
779
     * @param bool   $success        Whether the response is expected to be successful
780
     *
781
     * @return Crawler
782
     */
783 1
    public function fetchCrawler($path, $method = 'GET', $authentication = false, $success = true)
784
    {
785 1
        $client = $this->makeClient($authentication);
786 1
        $crawler = $client->request($method, $path);
787
788 1
        $this->isSuccessful($client->getResponse(), $success);
789
790 1
        return $crawler;
791
    }
792
793
    /**
794
     * @param UserInterface $user
795
     *
796
     * @return WebTestCase
797
     */
798 1
    public function loginAs(UserInterface $user, $firewallName)
799
    {
800 1
        $this->firewallLogins[$firewallName] = $user;
801
802 1
        return $this;
803
    }
804
805
    /**
806
     * Asserts that the HTTP response code of the last request performed by
807
     * $client matches the expected code. If not, raises an error with more
808
     * information.
809
     *
810
     * @param $expectedStatusCode
811
     * @param Client $client
812
     */
813 13
    public function assertStatusCode($expectedStatusCode, Client $client)
814
    {
815 13
        $helpfulErrorMessage = null;
816
817 13
        if ($expectedStatusCode !== $client->getResponse()->getStatusCode()) {
818
            // Get a more useful error message, if available
819
            if ($exception = $client->getContainer()->get('liip_functional_test.exception_listener')->getLastException()) {
820
                $helpfulErrorMessage = $exception->getMessage();
821
            } elseif (count($validationErrors = $client->getContainer()->get('liip_functional_test.validator')->getLastErrors())) {
822
                $helpfulErrorMessage = "Unexpected validation errors:\n";
823
824
                foreach ($validationErrors as $error) {
825
                    $helpfulErrorMessage .= sprintf("+ %s: %s\n", $error->getPropertyPath(), $error->getMessage());
826
                }
827
            } else {
828
                $helpfulErrorMessage = substr($client->getResponse(), 0, 200);
829
            }
830
        }
831
832 13
        self::assertEquals($expectedStatusCode, $client->getResponse()->getStatusCode(), $helpfulErrorMessage);
833 13
    }
834
835
    /**
836
     * Assert that the last validation errors within $container match the
837
     * expected keys.
838
     *
839
     * @param array              $expected  A flat array of field names
840
     * @param ContainerInterface $container
841
     */
842 2
    public function assertValidationErrors(array $expected, ContainerInterface $container)
843
    {
844 2
        self::assertThat(
845 2
            $container->get('liip_functional_test.validator')->getLastErrors(),
846 2
            new ValidationErrorsConstraint($expected),
847
            'Validation errors should match.'
848 2
        );
849 1
    }
850
}
851