Completed
Pull Request — master (#271)
by Alexis
07:12
created

WebTestCase::copySqliteBackup()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 15
ccs 8
cts 8
cp 1
rs 9.4285
cc 1
eloc 9
nc 1
nop 5
crap 1
1
<?php
2
3
/*
4
 * This file is part of the Liip/FunctionalTestBundle
5
 *
6
 * (c) Lukas Kahwe Smith <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace Liip\FunctionalTestBundle\Test;
13
14
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase as BaseWebTestCase;
15
use Symfony\Bundle\FrameworkBundle\Console\Application;
16
use Symfony\Bundle\FrameworkBundle\Client;
17
use Symfony\Component\Console\Input\ArrayInput;
18
use Symfony\Component\Console\Output\OutputInterface;
19
use Symfony\Component\Console\Output\StreamOutput;
20
use Symfony\Component\DomCrawler\Crawler;
21
use Symfony\Component\BrowserKit\Cookie;
22
use Symfony\Component\HttpKernel\Kernel;
23
use Symfony\Component\HttpFoundation\Response;
24
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
25
use Symfony\Component\Security\Core\User\UserInterface;
26
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
27
use Symfony\Component\DependencyInjection\ContainerInterface;
28
use Symfony\Component\HttpFoundation\Session\Session;
29
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
30
use Symfony\Bridge\Doctrine\ManagerRegistry;
31
use Symfony\Bundle\DoctrineFixturesBundle\Common\DataFixtures\Loader;
32
use Doctrine\Common\Persistence\ObjectManager;
33
use Doctrine\Common\DataFixtures\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\FixturesLoader;
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 13
    protected function runCommand($name, array $params = array(), $reuseKernel = false)
111
    {
112 13
        array_unshift($params, $name);
113
114 13
        if (!$reuseKernel) {
115 13
            if (null !== static::$kernel) {
116 9
                static::$kernel->shutdown();
117 9
            }
118
119 13
            $kernel = static::$kernel = $this->createKernel(array('environment' => $this->environment));
120 13
            $kernel->boot();
121 13
        } else {
122 2
            $kernel = $this->getContainer()->get('kernel');
123
        }
124
125 13
        $application = new Application($kernel);
126 13
        $application->setAutoExit(false);
127
128
        // @codeCoverageIgnoreStart
129
        if ('20301' === Kernel::VERSION_ID) {
130
            $params = $this->configureVerbosityForSymfony20301($params);
131
        }
132
        // @codeCoverageIgnoreEnd
133
134 13
        $input = new ArrayInput($params);
135 13
        $input->setInteractive(false);
136
137 13
        $fp = fopen('php://temp/maxmemory:'.$this->maxMemory, 'r+');
138 13
        $output = new StreamOutput($fp, $this->getVerbosityLevel(), $this->getDecorated());
139
140 12
        $application->run($input, $output);
141
142 12
        rewind($fp);
143
144 12
        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 13
    protected function getVerbosityLevel()
157
    {
158
        // If `null`, is not yet set
159 13
        if (null === $this->verbosityLevel) {
160
            // Set the global verbosity level that is set as NORMAL by the TreeBuilder in Configuration
161 7
            $level = strtoupper($this->getContainer()->getParameter('liip_functional_test.command_verbosity'));
162 7
            $verbosity = '\Symfony\Component\Console\Output\StreamOutput::VERBOSITY_'.$level;
163
164 7
            $this->verbosityLevel = constant($verbosity);
165 7
        }
166
167
        // If string, it is set by the developer, so check that the value is an accepted one
168 13
        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 12
        return $this->verbosityLevel;
182
    }
183
184
    /**
185
     * In Symfony 2.3.1 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 configureVerbosityForSymfony20301(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 12
    protected function getDecorated()
230
    {
231 12
        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 12
        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 12
        return $this->decorated;
244
    }
245
246 7
    public function isDecorated($decorated)
247
    {
248 7
        $this->decorated = $decorated;
249 7
    }
250
251
    /**
252
     * Get an instance of the dependency injection container.
253
     * (this creates a kernel *without* parameters).
254
     *
255
     * @return ContainerInterface
256
     */
257 46
    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 46
        if (!empty($this->kernelDir)) {
260
            $tmpKernelDir = isset($_SERVER['KERNEL_DIR']) ? $_SERVER['KERNEL_DIR'] : null;
261
            $_SERVER['KERNEL_DIR'] = getcwd().$this->kernelDir;
262
        }
263
264 46
        $cacheKey = $this->kernelDir.'|'.$this->environment;
265 46
        if (empty($this->containers[$cacheKey])) {
266
            $options = array(
267 45
                'environment' => $this->environment,
268 45
            );
269 45
            $kernel = $this->createKernel($options);
270 45
            $kernel->boot();
271
272 45
            $this->containers[$cacheKey] = $kernel->getContainer();
273 45
        }
274
275 46
        if (isset($tmpKernelDir)) {
276
            $_SERVER['KERNEL_DIR'] = $tmpKernelDir;
277
        }
278
279 46
        return $this->containers[$cacheKey];
280
    }
281
282
    /**
283
     * Get metadatas from cache.
284
     *
285
     * @param ObjectManager $om
286
     * @param string        $omName The name of object manager to use
287
     */
288 27
    private function getCachedMetadatas(ObjectManager $om, $omName)
289
    {
290 27
        if (!isset(self::$cachedMetadatas[$omName])) {
291 10
            self::$cachedMetadatas[$omName] = $om->getMetadataFactory()->getAllMetadata();
292 10
            usort(self::$cachedMetadatas[$omName], function ($a, $b) {
293
                return strcmp($a->name, $b->name);
294 10
            });
295 10
        }
296
297 27
        return self::$cachedMetadatas[$omName];
298
    }
299
300
    /**
301
     * Copy SQLite backup file.
302
     *
303
     * @param ObjectManager            $om
304
     * @param string                   $executorClass
305
     * @param ProxyReferenceRepository $referenceRepository
306
     * @param string                   $backup              Path of the source file.
307
     * @param string                   $name                Path of the destination file.
308
     */
309 7
    private function copySqliteBackup($om, $executorClass,
310
                                      $referenceRepository, $backup, $name)
311
    {
312 7
        $this->preFixtureRestore($om, $referenceRepository);
313
314 7
        copy($backup, $name);
315
316 7
        $executor = new $executorClass($om);
317 7
        $executor->setReferenceRepository($referenceRepository);
318 7
        $executor->getReferenceRepository()->load($backup);
319
320 7
        $this->postFixtureRestore();
321
322 7
        return $executor;
323
    }
324
325
    /**
326
     * Purge database.
327
     *
328
     * @param ObjectManager            $om
329
     * @param array                    $metadatas
330
     * @param string                   $executorClass
331
     * @param ProxyReferenceRepository $referenceRepository
332
     */
333 21
    private function createSqliteSchema(ObjectManager $om,
334
                                        $metadatas, $executorClass,
335
                                        ProxyReferenceRepository $referenceRepository)
336
    {
337
        // TODO: handle case when using persistent connections. Fail loudly?
338 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...
339 21
        $schemaTool->dropDatabase();
340 21
        if (!empty($metadatas)) {
341 21
            $schemaTool->createSchema($metadatas);
342 21
        }
343 21
        $this->postFixtureSetup();
344
345 21
        $executor = new $executorClass($om);
346 21
        $executor->setReferenceRepository($referenceRepository);
347 21
    }
348
349
    /**
350
     * Set the database to the provided fixtures.
351
     *
352
     * Drops the current database and then loads fixtures using the specified
353
     * classes. The parameter is a list of fully qualified class names of
354
     * classes that implement Doctrine\Common\DataFixtures\FixtureInterface
355
     * so that they can be loaded by the DataFixtures Loader::addFixture
356
     *
357
     * When using SQLite this method will automatically make a copy of the
358
     * loaded schema and fixtures which will be restored automatically in
359
     * case the same fixture classes are to be loaded again. Caveat: changes
360
     * to references and/or identities may go undetected.
361
     *
362
     * Depends on the doctrine data-fixtures library being available in the
363
     * class path.
364
     *
365
     * @param array  $classNames   List of fully qualified class names of fixtures to load
366
     * @param string $omName       The name of object manager to use
367
     * @param string $registryName The service id of manager registry to use
368
     * @param int    $purgeMode    Sets the ORM purge mode
369
     *
370
     * @return null|AbstractExecutor
371
     */
372 32
    protected function loadFixtures(array $classNames, $omName = null, $registryName = 'doctrine', $purgeMode = null)
373
    {
374 32
        $container = $this->getContainer();
375
        /** @var ManagerRegistry $registry */
376 32
        $registry = $container->get($registryName);
377
        /** @var ObjectManager $om */
378 32
        $om = $registry->getManager($omName);
379 32
        $type = $registry->getName();
380
381 32
        $executorClass = FixturesLoader::getExecutorClass($type);
382 32
        $referenceRepository = new ProxyReferenceRepository($om);
383 32
        $cacheDriver = $om->getMetadataFactory()->getCacheDriver();
384
385 32
        if ($cacheDriver) {
386 32
            $cacheDriver->deleteAll();
387 32
        }
388
389 32
        if ('ORM' === $type) {
390 31
            $connection = $om->getConnection();
391 31
            if ($connection->getDriver() instanceof SqliteDriver) {
392 27
                $name = FixturesLoader::getNameParameter($connection);
393 27
                $metadatas = self::getCachedMetadatas($om, $omName);
394
395 27
                if ($container->getParameter('liip_functional_test.cache_sqlite_db')) {
396 9
                    $backup = $container->getParameter('kernel.cache_dir').'/test_'.md5(serialize($metadatas).serialize($classNames)).'.db';
397 9
                    if (file_exists($backup) && file_exists($backup.'.ser') && FixturesLoader::isBackupUpToDate($classNames, $backup, $container)) {
398 7
                        $om->flush();
399 7
                        $om->clear();
400
401 7
                        $executor = $this->copySqliteBackup($om,
402 7
                            $executorClass, $referenceRepository,
403 7
                            $backup, $name);
404
405 7
                        return $executor;
406
                    }
407 3
                }
408
409 21
                $this->createSqliteSchema($om, $metadatas,
410 21
                    $executorClass, $referenceRepository);
411 21
            }
412 25
        }
413
414 26
        if (empty($executor)) {
0 ignored issues
show
Bug introduced by
The variable $executor seems only to be defined at a later point. As such the call to empty() seems to always evaluate to true.

This check marks calls to isset(...) or empty(...) that are found before the variable itself is defined. These will always have the same result.

This is likely the result of code being shifted around. Consider removing these calls.

Loading history...
415 26
            $executor = FixturesLoader::purgeDatabase($om, $type, $purgeMode,
416 26
                $executorClass, $referenceRepository, $container);
417 26
        }
418
419 26
        $loader = FixturesLoader::getFixtureLoader($container, $classNames);
420
421 26
        $executor->execute($loader->getFixtures(), true);
422
423 26
        if (isset($name) && isset($backup)) {
424 3
            $this->preReferenceSave($om, $executor, $backup);
425
426 3
            $executor->getReferenceRepository()->save($backup);
427 3
            copy($name, $backup);
428
429 3
            $this->postReferenceSave($om, $executor, $backup);
430 3
        }
431
432 26
        return $executor;
433
    }
434
435
    /**
436
     * Clean database.
437
     *
438
     * @param ManagerRegistry $registry
439
     * @param EntityManager   $om
440
     */
441 6
    private function cleanDatabase(ManagerRegistry $registry, EntityManager $om)
442
    {
443 6
        $connection = $om->getConnection();
444
445 6
        $mysql = ($registry->getName() === 'ORM'
446 6
            && $connection->getDatabasePlatform() instanceof MySqlPlatform);
447
448 6
        if ($mysql) {
449 1
            $connection->query('SET FOREIGN_KEY_CHECKS=0');
450 1
        }
451
452 6
        $this->loadFixtures(array());
453
454 6
        if ($mysql) {
455 1
            $connection->query('SET FOREIGN_KEY_CHECKS=1');
456 1
        }
457 6
    }
458
459
    /**
460
     * @param array  $paths        Either symfony resource locators (@ BundleName/etc) or actual file paths
461
     * @param bool   $append
462
     * @param null   $omName
463
     * @param string $registryName
464
     *
465
     * @return array
466
     *
467
     * @throws \BadMethodCallException
468
     */
469 6
    public function loadFixtureFiles(array $paths = array(), $append = false, $omName = null, $registryName = 'doctrine')
470
    {
471 6
        if (!class_exists('Nelmio\Alice\Fixtures')) {
472
            // This class is available during tests, no exception will be thrown.
473
            // @codeCoverageIgnoreStart
474
            throw new \BadMethodCallException('nelmio/alice should be installed to use this method.');
475
            // @codeCoverageIgnoreEnd
476
        }
477
478
        /** @var ContainerInterface $container */
479 6
        $container = $this->getContainer();
480
481
        /** @var ManagerRegistry $registry */
482 6
        $registry = $container->get($registryName);
483
484
        /** @var EntityManager $om */
485 6
        $om = $registry->getManager($omName);
486
487 6
        if ($append === false) {
488 6
            $this->cleanDatabase($registry, $om);
489 6
        }
490
491 6
        $files = FixturesLoader::locateResources($paths, $container);
492
493
        // Check if the Hautelook AliceBundle is registered and if yes, use it instead of Nelmio Alice
494 6
        $hautelookLoaderServiceName = 'hautelook_alice.fixtures.loader';
495 6
        if ($container->has($hautelookLoaderServiceName)) {
496 3
            $loaderService = $container->get($hautelookLoaderServiceName);
497 3
            $persisterClass = class_exists('Nelmio\Alice\ORM\Doctrine') ?
498 3
                'Nelmio\Alice\ORM\Doctrine' :
499 3
                'Nelmio\Alice\Persister\Doctrine';
500
501 3
            return $loaderService->load(new $persisterClass($om), $files);
502
        }
503
504 3
        return Fixtures::load($files, $om);
505
    }
506
507
    /**
508
     * Callback function to be executed after Schema creation.
509
     * Use this to execute acl:init or other things necessary.
510
     */
511 21
    protected function postFixtureSetup()
512
    {
513 21
    }
514
515
    /**
516
     * Callback function to be executed after Schema restore.
517
     *
518
     * @return WebTestCase
519
     */
520 7
    protected function postFixtureRestore()
521
    {
522 7
    }
523
524
    /**
525
     * Callback function to be executed before Schema restore.
526
     *
527
     * @param ObjectManager            $manager             The object manager
528
     * @param ProxyReferenceRepository $referenceRepository The reference repository
529
     *
530
     * @return WebTestCase
531
     */
532 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...
533
    {
534 7
    }
535
536
    /**
537
     * Callback function to be executed after save of references.
538
     *
539
     * @param ObjectManager    $manager        The object manager
540
     * @param AbstractExecutor $executor       Executor of the data fixtures
541
     * @param string           $backupFilePath Path of file used to backup the references of the data fixtures
542
     *
543
     * @return WebTestCase
544
     */
545 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...
546
    {
547 3
    }
548
549
    /**
550
     * Callback function to be executed before save of references.
551
     *
552
     * @param ObjectManager    $manager        The object manager
553
     * @param AbstractExecutor $executor       Executor of the data fixtures
554
     * @param string           $backupFilePath Path of file used to backup the references of the data fixtures
555
     *
556
     * @return WebTestCase
557
     */
558 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...
559
    {
560 3
    }
561
562
    /**
563
     * Creates an instance of a lightweight Http client.
564
     *
565
     * If $authentication is set to 'true' it will use the content of
566
     * 'liip_functional_test.authentication' to log in.
567
     *
568
     * $params can be used to pass headers to the client, note that they have
569
     * to follow the naming format used in $_SERVER.
570
     * Example: 'HTTP_X_REQUESTED_WITH' instead of 'X-Requested-With'
571
     *
572
     * @param bool|array $authentication
573
     * @param array      $params
574
     *
575
     * @return Client
576
     */
577 49
    protected function makeClient($authentication = false, array $params = array())
578
    {
579 49
        if ($authentication) {
580 2
            if ($authentication === true) {
581
                $authentication = array(
582 1
                    'username' => $this->getContainer()
583 1
                        ->getParameter('liip_functional_test.authentication.username'),
584 1
                    'password' => $this->getContainer()
585 1
                        ->getParameter('liip_functional_test.authentication.password'),
586 1
                );
587 1
            }
588
589 2
            $params = array_merge($params, array(
590 2
                'PHP_AUTH_USER' => $authentication['username'],
591 2
                'PHP_AUTH_PW' => $authentication['password'],
592 2
            ));
593 2
        }
594
595 49
        $client = static::createClient(array('environment' => $this->environment), $params);
596
597 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...
598
            // has to be set otherwise "hasPreviousSession" in Request returns false.
599 2
            $options = $client->getContainer()->getParameter('session.storage.options');
600
601 2
            if (!$options || !isset($options['name'])) {
602
                throw new \InvalidArgumentException('Missing session.storage.options#name');
603
            }
604
605 2
            $session = $client->getContainer()->get('session');
606
            // Since the namespace of the session changed in symfony 2.1, instanceof can be used to check the version.
607 2
            if ($session instanceof Session) {
608 2
                $session->setId(uniqid());
609 2
            }
610
611 2
            $client->getCookieJar()->set(new Cookie($options['name'], $session->getId()));
612
613
            /** @var $user UserInterface */
614 2
            foreach ($this->firewallLogins as $firewallName => $user) {
615 2
                $token = $this->createUserToken($user, $firewallName);
616
617
                // BC: security.token_storage is available on Symfony 2.6+
618
                // see http://symfony.com/blog/new-in-symfony-2-6-security-component-improvements
619 2
                if ($client->getContainer()->has('security.token_storage')) {
620 2
                    $tokenStorage = $client->getContainer()->get('security.token_storage');
621 2
                } else {
622
                    // This block will never be reached with Symfony 2.6+
623
                    // @codeCoverageIgnoreStart
624
                    $tokenStorage = $client->getContainer()->get('security.context');
625
                    // @codeCoverageIgnoreEnd
626
                }
627
628 2
                $tokenStorage->setToken($token);
629 2
                $session->set('_security_'.$firewallName, serialize($token));
630 2
            }
631
632 2
            $session->save();
633 2
        }
634
635 49
        return $client;
636
    }
637
638
    /**
639
     * Create User Token.
640
     *
641
     * Factory method for creating a User Token object for the firewall based on
642
     * the user object provided. By default it will be a Username/Password
643
     * Token based on the user's credentials, but may be overridden for custom
644
     * tokens in your applications.
645
     *
646
     * @param UserInterface $user         The user object to base the token off of
647
     * @param string        $firewallName name of the firewall provider to use
648
     *
649
     * @return TokenInterface The token to be used in the security context
650
     */
651 2
    protected function createUserToken(UserInterface $user, $firewallName)
652
    {
653 2
        return new UsernamePasswordToken(
654 2
            $user,
655 2
            null,
656 2
            $firewallName,
657 2
            $user->getRoles()
658 2
        );
659
    }
660
661
    /**
662
     * Extracts the location from the given route.
663
     *
664
     * @param string $route    The name of the route
665
     * @param array  $params   Set of parameters
666
     * @param int    $absolute
667
     *
668
     * @return string
669
     */
670 1
    protected function getUrl($route, $params = array(), $absolute = UrlGeneratorInterface::ABSOLUTE_PATH)
671
    {
672 1
        return $this->getContainer()->get('router')->generate($route, $params, $absolute);
673
    }
674
675
    /**
676
     * Checks the success state of a response.
677
     *
678
     * @param Response $response Response object
679
     * @param bool     $success  to define whether the response is expected to be successful
680
     * @param string   $type
681
     */
682 6
    public function isSuccessful(Response $response, $success = true, $type = 'text/html')
683
    {
684 6
        HttpAssertions::isSuccessful($response, $success, $type);
685 5
    }
686
687
    /**
688
     * Executes a request on the given url and returns the response contents.
689
     *
690
     * This method also asserts the request was successful.
691
     *
692
     * @param string $path           path of the requested page
693
     * @param string $method         The HTTP method to use, defaults to GET
694
     * @param bool   $authentication Whether to use authentication, defaults to false
695
     * @param bool   $success        to define whether the response is expected to be successful
696
     *
697
     * @return string
698
     */
699 1
    public function fetchContent($path, $method = 'GET', $authentication = false, $success = true)
700
    {
701 1
        $client = $this->makeClient($authentication);
702 1
        $client->request($method, $path);
703
704 1
        $content = $client->getResponse()->getContent();
705 1
        if (is_bool($success)) {
706 1
            $this->isSuccessful($client->getResponse(), $success);
707 1
        }
708
709 1
        return $content;
710
    }
711
712
    /**
713
     * Executes a request on the given url and returns a Crawler object.
714
     *
715
     * This method also asserts the request was successful.
716
     *
717
     * @param string $path           path of the requested page
718
     * @param string $method         The HTTP method to use, defaults to GET
719
     * @param bool   $authentication Whether to use authentication, defaults to false
720
     * @param bool   $success        Whether the response is expected to be successful
721
     *
722
     * @return Crawler
723
     */
724 1
    public function fetchCrawler($path, $method = 'GET', $authentication = false, $success = true)
725
    {
726 1
        $client = $this->makeClient($authentication);
727 1
        $crawler = $client->request($method, $path);
728
729 1
        $this->isSuccessful($client->getResponse(), $success);
730
731 1
        return $crawler;
732
    }
733
734
    /**
735
     * @param UserInterface $user
736
     * @param string        $firewallName
737
     *
738
     * @return WebTestCase
739
     */
740 2
    public function loginAs(UserInterface $user, $firewallName)
741
    {
742 2
        $this->firewallLogins[$firewallName] = $user;
743
744 2
        return $this;
745
    }
746
747
    /**
748
     * Asserts that the HTTP response code of the last request performed by
749
     * $client matches the expected code. If not, raises an error with more
750
     * information.
751
     *
752
     * @param $expectedStatusCode
753
     * @param Client $client
754
     */
755 11
    public function assertStatusCode($expectedStatusCode, Client $client)
756
    {
757 11
        HttpAssertions::assertStatusCode($expectedStatusCode, $client);
758 8
    }
759
760
    /**
761
     * Assert that the last validation errors within $container match the
762
     * expected keys.
763
     *
764
     * @param array              $expected  A flat array of field names
765
     * @param ContainerInterface $container
766
     */
767 2
    public function assertValidationErrors(array $expected, ContainerInterface $container)
768
    {
769 2
        HttpAssertions::assertValidationErrors($expected, $container);
770 1
    }
771
}
772