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