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\Driver\PDOSqlite\Driver as SqliteDriver; |
||
| 37 | use Doctrine\DBAL\Platforms\MySqlPlatform; |
||
| 38 | use Doctrine\ORM\EntityManager; |
||
| 39 | use Doctrine\ORM\Tools\SchemaTool; |
||
| 40 | use Nelmio\Alice\Fixtures; |
||
| 41 | |||
| 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() |
|
|
0 ignored issues
–
show
|
|||
| 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 | 37 | protected function getContainer() |
|
|
0 ignored issues
–
show
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...
|
|||
| 257 | { |
||
| 258 | 37 | if (!empty($this->kernelDir)) { |
|
| 259 | $tmpKernelDir = isset($_SERVER['KERNEL_DIR']) ? $_SERVER['KERNEL_DIR'] : null; |
||
| 260 | $_SERVER['KERNEL_DIR'] = getcwd().$this->kernelDir; |
||
| 261 | } |
||
| 262 | |||
| 263 | 37 | $cacheKey = $this->kernelDir.'|'.$this->environment; |
|
| 264 | 37 | if (empty($this->containers[$cacheKey])) { |
|
| 265 | $options = array( |
||
| 266 | 37 | 'environment' => $this->environment, |
|
| 267 | 37 | ); |
|
| 268 | 37 | $kernel = $this->createKernel($options); |
|
| 269 | 37 | $kernel->boot(); |
|
| 270 | |||
| 271 | 37 | $this->containers[$cacheKey] = $kernel->getContainer(); |
|
| 272 | 37 | } |
|
| 273 | |||
| 274 | 37 | if (isset($tmpKernelDir)) { |
|
| 275 | $_SERVER['KERNEL_DIR'] = $tmpKernelDir; |
||
| 276 | } |
||
| 277 | |||
| 278 | 37 | return $this->containers[$cacheKey]; |
|
| 279 | } |
||
| 280 | |||
| 281 | /** |
||
| 282 | * This function finds the time when the data blocks of a class definition |
||
| 283 | * file were being written to, that is, the time when the content of the |
||
| 284 | * file was changed. |
||
| 285 | * |
||
| 286 | * @param string $class The fully qualified class name of the fixture class to |
||
| 287 | * check modification date on. |
||
| 288 | * |
||
| 289 | * @return \DateTime|null |
||
| 290 | */ |
||
| 291 | 2 | protected function getFixtureLastModified($class) |
|
| 292 | { |
||
| 293 | 2 | $lastModifiedDateTime = null; |
|
| 294 | |||
| 295 | 2 | $reflClass = new \ReflectionClass($class); |
|
| 296 | 2 | $classFileName = $reflClass->getFileName(); |
|
| 297 | |||
| 298 | 2 | if (file_exists($classFileName)) { |
|
| 299 | 2 | $lastModifiedDateTime = new \DateTime(); |
|
| 300 | 2 | $lastModifiedDateTime->setTimestamp(filemtime($classFileName)); |
|
| 301 | 2 | } |
|
| 302 | |||
| 303 | 2 | return $lastModifiedDateTime; |
|
| 304 | } |
||
| 305 | |||
| 306 | /** |
||
| 307 | * Determine if the Fixtures that define a database backup have been |
||
| 308 | * modified since the backup was made. |
||
| 309 | * |
||
| 310 | * @param array $classNames The fixture classnames to check |
||
| 311 | * @param string $backup The fixture backup SQLite database file path |
||
| 312 | * |
||
| 313 | * @return bool TRUE if the backup was made since the modifications to the |
||
| 314 | * fixtures; FALSE otherwise |
||
| 315 | */ |
||
| 316 | 3 | protected function isBackupUpToDate(array $classNames, $backup) |
|
| 317 | { |
||
| 318 | 3 | $backupLastModifiedDateTime = new \DateTime(); |
|
| 319 | 3 | $backupLastModifiedDateTime->setTimestamp(filemtime($backup)); |
|
| 320 | |||
| 321 | 3 | foreach ($classNames as &$className) { |
|
| 322 | 2 | $fixtureLastModifiedDateTime = $this->getFixtureLastModified($className); |
|
| 323 | 2 | if ($backupLastModifiedDateTime < $fixtureLastModifiedDateTime) { |
|
| 324 | return false; |
||
| 325 | } |
||
| 326 | 3 | } |
|
| 327 | |||
| 328 | 3 | return true; |
|
| 329 | } |
||
| 330 | |||
| 331 | /** |
||
| 332 | * Set the database to the provided fixtures. |
||
| 333 | * |
||
| 334 | * Drops the current database and then loads fixtures using the specified |
||
| 335 | * classes. The parameter is a list of fully qualified class names of |
||
| 336 | * classes that implement Doctrine\Common\DataFixtures\FixtureInterface |
||
| 337 | * so that they can be loaded by the DataFixtures Loader::addFixture |
||
| 338 | * |
||
| 339 | * When using SQLite this method will automatically make a copy of the |
||
| 340 | * loaded schema and fixtures which will be restored automatically in |
||
| 341 | * case the same fixture classes are to be loaded again. Caveat: changes |
||
| 342 | * to references and/or identities may go undetected. |
||
| 343 | * |
||
| 344 | * Depends on the doctrine data-fixtures library being available in the |
||
| 345 | * class path. |
||
| 346 | * |
||
| 347 | * @param array $classNames List of fully qualified class names of fixtures to load |
||
| 348 | * @param string $omName The name of object manager to use |
||
| 349 | * @param string $registryName The service id of manager registry to use |
||
| 350 | * @param int $purgeMode Sets the ORM purge mode |
||
| 351 | * |
||
| 352 | * @return null|AbstractExecutor |
||
| 353 | */ |
||
| 354 | 29 | protected function loadFixtures(array $classNames, $omName = null, $registryName = 'doctrine', $purgeMode = null) |
|
| 355 | { |
||
| 356 | 29 | $container = $this->getContainer(); |
|
| 357 | /** @var ManagerRegistry $registry */ |
||
| 358 | 29 | $registry = $container->get($registryName); |
|
| 359 | /** @var ObjectManager $om */ |
||
| 360 | 29 | $om = $registry->getManager($omName); |
|
| 361 | 29 | $type = $registry->getName(); |
|
| 362 | |||
| 363 | 29 | $executorClass = 'PHPCR' === $type && class_exists('Doctrine\Bundle\PHPCRBundle\DataFixtures\PHPCRExecutor') |
|
| 364 | 29 | ? 'Doctrine\Bundle\PHPCRBundle\DataFixtures\PHPCRExecutor' |
|
| 365 | 29 | : 'Doctrine\\Common\\DataFixtures\\Executor\\'.$type.'Executor'; |
|
| 366 | 29 | $referenceRepository = new ProxyReferenceRepository($om); |
|
| 367 | 29 | $cacheDriver = $om->getMetadataFactory()->getCacheDriver(); |
|
| 368 | |||
| 369 | 29 | if ($cacheDriver) { |
|
| 370 | 29 | $cacheDriver->deleteAll(); |
|
| 371 | 29 | } |
|
| 372 | |||
| 373 | 29 | if ('ORM' === $type) { |
|
| 374 | 28 | $connection = $om->getConnection(); |
|
| 375 | 28 | if ($connection->getDriver() instanceof SqliteDriver) { |
|
| 376 | 24 | $params = $connection->getParams(); |
|
| 377 | 24 | if (isset($params['master'])) { |
|
| 378 | $params = $params['master']; |
||
| 379 | } |
||
| 380 | |||
| 381 | 24 | $name = isset($params['path']) ? $params['path'] : (isset($params['dbname']) ? $params['dbname'] : false); |
|
| 382 | 24 | if (!$name) { |
|
| 383 | throw new \InvalidArgumentException("Connection does not contain a 'path' or 'dbname' parameter and cannot be dropped."); |
||
| 384 | } |
||
| 385 | |||
| 386 | 24 | if (!isset(self::$cachedMetadatas[$omName])) { |
|
| 387 | 6 | self::$cachedMetadatas[$omName] = $om->getMetadataFactory()->getAllMetadata(); |
|
| 388 | usort(self::$cachedMetadatas[$omName], function ($a, $b) { return strcmp($a->name, $b->name); }); |
||
| 389 | 6 | } |
|
| 390 | 24 | $metadatas = self::$cachedMetadatas[$omName]; |
|
| 391 | |||
| 392 | 24 | if ($container->getParameter('liip_functional_test.cache_sqlite_db')) { |
|
| 393 | 5 | $backup = $container->getParameter('kernel.cache_dir').'/test_'.md5(serialize($metadatas).serialize($classNames)).'.db'; |
|
| 394 | 5 | if (file_exists($backup) && file_exists($backup.'.ser') && $this->isBackupUpToDate($classNames, $backup)) { |
|
| 395 | 3 | $om->flush(); |
|
| 396 | 3 | $om->clear(); |
|
| 397 | |||
| 398 | 3 | $this->preFixtureRestore($om, $referenceRepository); |
|
| 399 | |||
| 400 | 3 | copy($backup, $name); |
|
| 401 | |||
| 402 | 3 | $executor = new $executorClass($om); |
|
| 403 | 3 | $executor->setReferenceRepository($referenceRepository); |
|
| 404 | 3 | $executor->getReferenceRepository()->load($backup); |
|
| 405 | |||
| 406 | 3 | $this->postFixtureRestore(); |
|
| 407 | |||
| 408 | 3 | return $executor; |
|
| 409 | } |
||
| 410 | 2 | } |
|
| 411 | |||
| 412 | // TODO: handle case when using persistent connections. Fail loudly? |
||
| 413 | 21 | $schemaTool = new SchemaTool($om); |
|
|
0 ignored issues
–
show
$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...
|
|||
| 414 | 21 | $schemaTool->dropDatabase(); |
|
| 415 | 21 | if (!empty($metadatas)) { |
|
| 416 | 21 | $schemaTool->createSchema($metadatas); |
|
| 417 | 21 | } |
|
| 418 | 21 | $this->postFixtureSetup(); |
|
| 419 | |||
| 420 | 21 | $executor = new $executorClass($om); |
|
| 421 | 21 | $executor->setReferenceRepository($referenceRepository); |
|
| 422 | 21 | } |
|
| 423 | 25 | } |
|
| 424 | |||
| 425 | 26 | if (empty($executor)) { |
|
| 426 | 5 | $purgerClass = 'Doctrine\\Common\\DataFixtures\\Purger\\'.$type.'Purger'; |
|
| 427 | 5 | if ('PHPCR' === $type) { |
|
| 428 | 1 | $purger = new $purgerClass($om); |
|
| 429 | 1 | $initManager = $container->has('doctrine_phpcr.initializer_manager') |
|
| 430 | 1 | ? $container->get('doctrine_phpcr.initializer_manager') |
|
| 431 | 1 | : null; |
|
| 432 | |||
| 433 | 1 | $executor = new $executorClass($om, $purger, $initManager); |
|
| 434 | 1 | } else { |
|
| 435 | 4 | $purger = new $purgerClass(); |
|
| 436 | 4 | if (null !== $purgeMode) { |
|
| 437 | 1 | $purger->setPurgeMode($purgeMode); |
|
| 438 | 1 | } |
|
| 439 | |||
| 440 | 4 | $executor = new $executorClass($om, $purger); |
|
| 441 | } |
||
| 442 | |||
| 443 | 5 | $executor->setReferenceRepository($referenceRepository); |
|
| 444 | 5 | $executor->purge(); |
|
| 445 | 5 | } |
|
| 446 | |||
| 447 | 26 | $loader = $this->getFixtureLoader($container, $classNames); |
|
| 448 | |||
| 449 | 26 | $executor->execute($loader->getFixtures(), true); |
|
| 450 | |||
| 451 | 26 | if (isset($name) && isset($backup)) { |
|
| 452 | 2 | $this->preReferenceSave($om, $executor, $backup); |
|
| 453 | |||
| 454 | 2 | $executor->getReferenceRepository()->save($backup); |
|
| 455 | 2 | copy($name, $backup); |
|
| 456 | |||
| 457 | 2 | $this->postReferenceSave($om, $executor, $backup); |
|
| 458 | 2 | } |
|
| 459 | |||
| 460 | 26 | return $executor; |
|
| 461 | } |
||
| 462 | |||
| 463 | /** |
||
| 464 | * Clean database. |
||
| 465 | * |
||
| 466 | * @param ManagerRegistry $registry |
||
| 467 | * @param EntityManager $om |
||
| 468 | */ |
||
| 469 | 5 | private function cleanDatabase(ManagerRegistry $registry, EntityManager $om) |
|
| 470 | { |
||
| 471 | 5 | $connection = $om->getConnection(); |
|
| 472 | |||
| 473 | 5 | $mysql = ($registry->getName() === 'ORM' |
|
| 474 | 5 | && $connection->getDatabasePlatform() instanceof MySqlPlatform); |
|
| 475 | |||
| 476 | 5 | if ($mysql) { |
|
| 477 | 1 | $connection->query('SET FOREIGN_KEY_CHECKS=0'); |
|
| 478 | 1 | } |
|
| 479 | |||
| 480 | 5 | $this->loadFixtures(array()); |
|
| 481 | |||
| 482 | 5 | if ($mysql) { |
|
| 483 | 1 | $connection->query('SET FOREIGN_KEY_CHECKS=1'); |
|
| 484 | 1 | } |
|
| 485 | 5 | } |
|
| 486 | |||
| 487 | /** |
||
| 488 | * Locate fixture files. |
||
| 489 | * |
||
| 490 | * @param array $paths |
||
| 491 | * |
||
| 492 | * @return array $files |
||
| 493 | */ |
||
| 494 | 5 | private function locateResources($paths) |
|
| 495 | { |
||
| 496 | 5 | $files = array(); |
|
| 497 | |||
| 498 | 5 | $kernel = $this->getContainer()->get('kernel'); |
|
| 499 | |||
| 500 | 5 | foreach ($paths as $path) { |
|
| 501 | 5 | if ($path[0] !== '@' && file_exists($path) === true) { |
|
| 502 | 1 | $files[] = $path; |
|
| 503 | 1 | continue; |
|
| 504 | } |
||
| 505 | |||
| 506 | 4 | $files[] = $kernel->locateResource($path); |
|
| 507 | 5 | } |
|
| 508 | |||
| 509 | 5 | return $files; |
|
| 510 | } |
||
| 511 | |||
| 512 | /** |
||
| 513 | * @param array $paths Either symfony resource locators (@ BundleName/etc) or actual file paths |
||
| 514 | * @param bool $append |
||
| 515 | * @param null $omName |
||
| 516 | * @param string $registryName |
||
| 517 | * |
||
| 518 | * @return array |
||
| 519 | * |
||
| 520 | * @throws \BadMethodCallException |
||
| 521 | */ |
||
| 522 | 5 | public function loadFixtureFiles(array $paths = array(), $append = false, $omName = null, $registryName = 'doctrine') |
|
| 523 | { |
||
| 524 | 5 | if (!class_exists('Nelmio\Alice\Fixtures')) { |
|
| 525 | // This class is available during tests, no exception will be thrown. |
||
| 526 | // @codeCoverageIgnoreStart |
||
| 527 | throw new \BadMethodCallException('nelmio/alice should be installed to use this method.'); |
||
| 528 | // @codeCoverageIgnoreEnd |
||
| 529 | } |
||
| 530 | |||
| 531 | /** @var ContainerInterface $container */ |
||
| 532 | 5 | $container = $this->getContainer(); |
|
| 533 | |||
| 534 | /** @var ManagerRegistry $registry */ |
||
| 535 | 5 | $registry = $container->get($registryName); |
|
| 536 | |||
| 537 | /** @var EntityManager $om */ |
||
| 538 | 5 | $om = $registry->getManager($omName); |
|
| 539 | |||
| 540 | 5 | if ($append === false) { |
|
| 541 | 5 | $this->cleanDatabase($registry, $om); |
|
| 542 | 5 | } |
|
| 543 | |||
| 544 | 5 | $files = $this->locateResources($paths); |
|
| 545 | |||
| 546 | // Check if the Hautelook AliceBundle is registered and if yes, use it instead of Nelmio Alice |
||
| 547 | 5 | $hautelookLoaderServiceName = 'hautelook_alice.fixtures.loader'; |
|
| 548 | 5 | if ($container->has($hautelookLoaderServiceName)) { |
|
| 549 | 2 | $loaderService = $container->get($hautelookLoaderServiceName); |
|
| 550 | 2 | $persisterClass = class_exists('Nelmio\Alice\ORM\Doctrine') ? |
|
| 551 | 2 | 'Nelmio\Alice\ORM\Doctrine' : |
|
| 552 | 2 | 'Nelmio\Alice\Persister\Doctrine'; |
|
| 553 | |||
| 554 | 2 | return $loaderService->load(new $persisterClass($om), $files); |
|
| 555 | } |
||
| 556 | |||
| 557 | 3 | return Fixtures::load($files, $om); |
|
| 558 | } |
||
| 559 | |||
| 560 | /** |
||
| 561 | * Callback function to be executed after Schema creation. |
||
| 562 | * Use this to execute acl:init or other things necessary. |
||
| 563 | */ |
||
| 564 | 21 | protected function postFixtureSetup() |
|
| 565 | { |
||
| 566 | 21 | } |
|
| 567 | |||
| 568 | /** |
||
| 569 | * Callback function to be executed after Schema restore. |
||
| 570 | * |
||
| 571 | * @return WebTestCase |
||
| 572 | */ |
||
| 573 | 3 | protected function postFixtureRestore() |
|
| 574 | { |
||
| 575 | 3 | } |
|
| 576 | |||
| 577 | /** |
||
| 578 | * Callback function to be executed before Schema restore. |
||
| 579 | * |
||
| 580 | * @param ObjectManager $manager The object manager |
||
| 581 | * @param ProxyReferenceRepository $referenceRepository The reference repository |
||
| 582 | * |
||
| 583 | * @return WebTestCase |
||
| 584 | */ |
||
| 585 | 3 | protected function preFixtureRestore(ObjectManager $manager, ProxyReferenceRepository $referenceRepository) |
|
|
0 ignored issues
–
show
|
|||
| 586 | { |
||
| 587 | 3 | } |
|
| 588 | |||
| 589 | /** |
||
| 590 | * Callback function to be executed after save of references. |
||
| 591 | * |
||
| 592 | * @param ObjectManager $manager The object manager |
||
| 593 | * @param AbstractExecutor $executor Executor of the data fixtures |
||
| 594 | * @param string $backupFilePath Path of file used to backup the references of the data fixtures |
||
| 595 | * |
||
| 596 | * @return WebTestCase |
||
| 597 | */ |
||
| 598 | 2 | protected function postReferenceSave(ObjectManager $manager, AbstractExecutor $executor, $backupFilePath) |
|
|
0 ignored issues
–
show
|
|||
| 599 | { |
||
| 600 | 2 | } |
|
| 601 | |||
| 602 | /** |
||
| 603 | * Callback function to be executed before save of references. |
||
| 604 | * |
||
| 605 | * @param ObjectManager $manager The object manager |
||
| 606 | * @param AbstractExecutor $executor Executor of the data fixtures |
||
| 607 | * @param string $backupFilePath Path of file used to backup the references of the data fixtures |
||
| 608 | * |
||
| 609 | * @return WebTestCase |
||
| 610 | */ |
||
| 611 | 2 | protected function preReferenceSave(ObjectManager $manager, AbstractExecutor $executor, $backupFilePath) |
|
|
0 ignored issues
–
show
|
|||
| 612 | { |
||
| 613 | 2 | } |
|
| 614 | |||
| 615 | /** |
||
| 616 | * Retrieve Doctrine DataFixtures loader. |
||
| 617 | * |
||
| 618 | * @param ContainerInterface $container |
||
| 619 | * @param array $classNames |
||
| 620 | * |
||
| 621 | * @return Loader |
||
| 622 | */ |
||
| 623 | 26 | protected function getFixtureLoader(ContainerInterface $container, array $classNames) |
|
| 624 | { |
||
| 625 | 26 | $loaderClass = class_exists('Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader') |
|
| 626 | 26 | ? 'Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader' |
|
| 627 | 26 | : (class_exists('Doctrine\Bundle\FixturesBundle\Common\DataFixtures\Loader') |
|
| 628 | // This class is not available during tests. |
||
| 629 | // @codeCoverageIgnoreStart |
||
| 630 | ? 'Doctrine\Bundle\FixturesBundle\Common\DataFixtures\Loader' |
||
| 631 | // @codeCoverageIgnoreEnd |
||
| 632 | 26 | : 'Symfony\Bundle\DoctrineFixturesBundle\Common\DataFixtures\Loader'); |
|
| 633 | |||
| 634 | 26 | $loader = new $loaderClass($container); |
|
| 635 | |||
| 636 | 26 | foreach ($classNames as $className) { |
|
| 637 | 7 | $this->loadFixtureClass($loader, $className); |
|
| 638 | 26 | } |
|
| 639 | |||
| 640 | 26 | return $loader; |
|
| 641 | } |
||
| 642 | |||
| 643 | /** |
||
| 644 | * Load a data fixture class. |
||
| 645 | * |
||
| 646 | * @param Loader $loader |
||
| 647 | * @param string $className |
||
| 648 | */ |
||
| 649 | 7 | protected function loadFixtureClass($loader, $className) |
|
| 650 | { |
||
| 651 | 7 | $fixture = new $className(); |
|
| 652 | |||
| 653 | 7 | if ($loader->hasFixture($fixture)) { |
|
| 654 | 1 | unset($fixture); |
|
| 655 | |||
| 656 | 1 | return; |
|
| 657 | } |
||
| 658 | |||
| 659 | 7 | $loader->addFixture($fixture); |
|
| 660 | |||
| 661 | 7 | if ($fixture instanceof DependentFixtureInterface) { |
|
| 662 | 1 | foreach ($fixture->getDependencies() as $dependency) { |
|
| 663 | 1 | $this->loadFixtureClass($loader, $dependency); |
|
| 664 | 1 | } |
|
| 665 | 1 | } |
|
| 666 | 7 | } |
|
| 667 | |||
| 668 | /** |
||
| 669 | * Creates an instance of a lightweight Http client. |
||
| 670 | * |
||
| 671 | * If $authentication is set to 'true' it will use the content of |
||
| 672 | * 'liip_functional_test.authentication' to log in. |
||
| 673 | * |
||
| 674 | * $params can be used to pass headers to the client, note that they have |
||
| 675 | * to follow the naming format used in $_SERVER. |
||
| 676 | * Example: 'HTTP_X_REQUESTED_WITH' instead of 'X-Requested-With' |
||
| 677 | * |
||
| 678 | * @param bool|array $authentication |
||
| 679 | * @param array $params |
||
| 680 | * |
||
| 681 | * @return Client |
||
| 682 | */ |
||
| 683 | 46 | protected function makeClient($authentication = false, array $params = array()) |
|
| 684 | { |
||
| 685 | 46 | if ($authentication) { |
|
| 686 | 2 | if ($authentication === true) { |
|
| 687 | $authentication = array( |
||
| 688 | 1 | 'username' => $this->getContainer() |
|
| 689 | 1 | ->getParameter('liip_functional_test.authentication.username'), |
|
| 690 | 1 | 'password' => $this->getContainer() |
|
| 691 | 1 | ->getParameter('liip_functional_test.authentication.password'), |
|
| 692 | 1 | ); |
|
| 693 | 1 | } |
|
| 694 | |||
| 695 | 2 | $params = array_merge($params, array( |
|
| 696 | 2 | 'PHP_AUTH_USER' => $authentication['username'], |
|
| 697 | 2 | 'PHP_AUTH_PW' => $authentication['password'], |
|
| 698 | 2 | )); |
|
| 699 | 2 | } |
|
| 700 | |||
| 701 | 46 | $client = static::createClient(array('environment' => $this->environment), $params); |
|
| 702 | |||
| 703 | 46 | if ($this->firewallLogins) { |
|
|
0 ignored issues
–
show
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 Loading history...
|
|||
| 704 | // has to be set otherwise "hasPreviousSession" in Request returns false. |
||
| 705 | 2 | $options = $client->getContainer()->getParameter('session.storage.options'); |
|
| 706 | |||
| 707 | 2 | if (!$options || !isset($options['name'])) { |
|
| 708 | throw new \InvalidArgumentException('Missing session.storage.options#name'); |
||
| 709 | } |
||
| 710 | |||
| 711 | 2 | $session = $client->getContainer()->get('session'); |
|
| 712 | // Since the namespace of the session changed in symfony 2.1, instanceof can be used to check the version. |
||
| 713 | 2 | if ($session instanceof Session) { |
|
| 714 | 2 | $session->setId(uniqid()); |
|
| 715 | 2 | } |
|
| 716 | |||
| 717 | 2 | $client->getCookieJar()->set(new Cookie($options['name'], $session->getId())); |
|
| 718 | |||
| 719 | /** @var $user UserInterface */ |
||
| 720 | 2 | foreach ($this->firewallLogins as $firewallName => $user) { |
|
| 721 | 2 | $token = $this->createUserToken($user, $firewallName); |
|
| 722 | |||
| 723 | // BC: security.token_storage is available on Symfony 2.6+ |
||
| 724 | // see http://symfony.com/blog/new-in-symfony-2-6-security-component-improvements |
||
| 725 | 2 | if ($client->getContainer()->has('security.token_storage')) { |
|
| 726 | 2 | $tokenStorage = $client->getContainer()->get('security.token_storage'); |
|
| 727 | 2 | } else { |
|
| 728 | // This block will never be reached with Symfony 2.6+ |
||
| 729 | // @codeCoverageIgnoreStart |
||
| 730 | $tokenStorage = $client->getContainer()->get('security.context'); |
||
| 731 | // @codeCoverageIgnoreEnd |
||
| 732 | } |
||
| 733 | |||
| 734 | 2 | $tokenStorage->setToken($token); |
|
| 735 | 2 | $session->set('_security_'.$firewallName, serialize($token)); |
|
| 736 | 2 | } |
|
| 737 | |||
| 738 | 2 | $session->save(); |
|
| 739 | 2 | } |
|
| 740 | |||
| 741 | 46 | return $client; |
|
| 742 | } |
||
| 743 | |||
| 744 | /** |
||
| 745 | * Create User Token. |
||
| 746 | * |
||
| 747 | * Factory method for creating a User Token object for the firewall based on |
||
| 748 | * the user object provided. By default it will be a Username/Password |
||
| 749 | * Token based on the user's credentials, but may be overridden for custom |
||
| 750 | * tokens in your applications. |
||
| 751 | * |
||
| 752 | * @param UserInterface $user The user object to base the token off of |
||
| 753 | * @param string $firewallName name of the firewall provider to use |
||
| 754 | * |
||
| 755 | * @return TokenInterface The token to be used in the security context |
||
| 756 | */ |
||
| 757 | 2 | protected function createUserToken(UserInterface $user, $firewallName) |
|
| 758 | { |
||
| 759 | 2 | return new UsernamePasswordToken( |
|
| 760 | 2 | $user, |
|
| 761 | 2 | null, |
|
| 762 | 2 | $firewallName, |
|
| 763 | 2 | $user->getRoles() |
|
| 764 | 2 | ); |
|
| 765 | } |
||
| 766 | |||
| 767 | /** |
||
| 768 | * Extracts the location from the given route. |
||
| 769 | * |
||
| 770 | * @param string $route The name of the route |
||
| 771 | * @param array $params Set of parameters |
||
| 772 | * @param int $absolute |
||
| 773 | * |
||
| 774 | * @return string |
||
| 775 | */ |
||
| 776 | 1 | protected function getUrl($route, $params = array(), $absolute = UrlGeneratorInterface::ABSOLUTE_PATH) |
|
| 777 | { |
||
| 778 | 1 | return $this->getContainer()->get('router')->generate($route, $params, $absolute); |
|
| 779 | } |
||
| 780 | |||
| 781 | /** |
||
| 782 | * Checks the success state of a response. |
||
| 783 | * |
||
| 784 | * @param Response $response Response object |
||
| 785 | * @param bool $success to define whether the response is expected to be successful |
||
| 786 | * @param string $type |
||
| 787 | */ |
||
| 788 | 5 | public function isSuccessful(Response $response, $success = true, $type = 'text/html') |
|
| 789 | { |
||
| 790 | try { |
||
| 791 | 5 | $crawler = new Crawler(); |
|
| 792 | 5 | $crawler->addContent($response->getContent(), $type); |
|
| 793 | 5 | if (!count($crawler->filter('title'))) { |
|
| 794 | 1 | $title = '['.$response->getStatusCode().'] - '.$response->getContent(); |
|
| 795 | 1 | } else { |
|
| 796 | 4 | $title = $crawler->filter('title')->text(); |
|
| 797 | } |
||
| 798 | 5 | } catch (\Exception $e) { |
|
| 799 | $title = $e->getMessage(); |
||
| 800 | } |
||
| 801 | |||
| 802 | 5 | if ($success) { |
|
| 803 | 4 | $this->assertTrue($response->isSuccessful(), 'The Response was not successful: '.$title); |
|
| 804 | 4 | } else { |
|
| 805 | 1 | $this->assertFalse($response->isSuccessful(), 'The Response was successful: '.$title); |
|
| 806 | } |
||
| 807 | 5 | } |
|
| 808 | |||
| 809 | /** |
||
| 810 | * Executes a request on the given url and returns the response contents. |
||
| 811 | * |
||
| 812 | * This method also asserts the request was successful. |
||
| 813 | * |
||
| 814 | * @param string $path path of the requested page |
||
| 815 | * @param string $method The HTTP method to use, defaults to GET |
||
| 816 | * @param bool $authentication Whether to use authentication, defaults to false |
||
| 817 | * @param bool $success to define whether the response is expected to be successful |
||
| 818 | * |
||
| 819 | * @return string |
||
| 820 | */ |
||
| 821 | 1 | public function fetchContent($path, $method = 'GET', $authentication = false, $success = true) |
|
| 822 | { |
||
| 823 | 1 | $client = $this->makeClient($authentication); |
|
| 824 | 1 | $client->request($method, $path); |
|
| 825 | |||
| 826 | 1 | $content = $client->getResponse()->getContent(); |
|
| 827 | 1 | if (is_bool($success)) { |
|
| 828 | 1 | $this->isSuccessful($client->getResponse(), $success); |
|
| 829 | 1 | } |
|
| 830 | |||
| 831 | 1 | return $content; |
|
| 832 | } |
||
| 833 | |||
| 834 | /** |
||
| 835 | * Executes a request on the given url and returns a Crawler object. |
||
| 836 | * |
||
| 837 | * This method also asserts the request was successful. |
||
| 838 | * |
||
| 839 | * @param string $path path of the requested page |
||
| 840 | * @param string $method The HTTP method to use, defaults to GET |
||
| 841 | * @param bool $authentication Whether to use authentication, defaults to false |
||
| 842 | * @param bool $success Whether the response is expected to be successful |
||
| 843 | * |
||
| 844 | * @return Crawler |
||
| 845 | */ |
||
| 846 | 1 | public function fetchCrawler($path, $method = 'GET', $authentication = false, $success = true) |
|
| 847 | { |
||
| 848 | 1 | $client = $this->makeClient($authentication); |
|
| 849 | 1 | $crawler = $client->request($method, $path); |
|
| 850 | |||
| 851 | 1 | $this->isSuccessful($client->getResponse(), $success); |
|
| 852 | |||
| 853 | 1 | return $crawler; |
|
| 854 | } |
||
| 855 | |||
| 856 | /** |
||
| 857 | * @param UserInterface $user |
||
| 858 | * @param string $firewallName |
||
| 859 | * |
||
| 860 | * @return WebTestCase |
||
| 861 | */ |
||
| 862 | 2 | public function loginAs(UserInterface $user, $firewallName) |
|
| 863 | { |
||
| 864 | 2 | $this->firewallLogins[$firewallName] = $user; |
|
| 865 | |||
| 866 | 2 | return $this; |
|
| 867 | } |
||
| 868 | |||
| 869 | /** |
||
| 870 | * Asserts that the HTTP response code of the last request performed by |
||
| 871 | * $client matches the expected code. If not, raises an error with more |
||
| 872 | * information. |
||
| 873 | * |
||
| 874 | * @param $expectedStatusCode |
||
| 875 | * @param Client $client |
||
| 876 | */ |
||
| 877 | 9 | public function assertStatusCode($expectedStatusCode, Client $client) |
|
| 878 | { |
||
| 879 | 9 | $helpfulErrorMessage = null; |
|
| 880 | |||
| 881 | 9 | if ($expectedStatusCode !== $client->getResponse()->getStatusCode()) { |
|
| 882 | // Get a more useful error message, if available |
||
| 883 | if ($exception = $client->getContainer()->get('liip_functional_test.exception_listener')->getLastException()) { |
||
| 884 | $helpfulErrorMessage = $exception->getMessage(); |
||
| 885 | } elseif (count($validationErrors = $client->getContainer()->get('liip_functional_test.validator')->getLastErrors())) { |
||
| 886 | $helpfulErrorMessage = "Unexpected validation errors:\n"; |
||
| 887 | |||
| 888 | foreach ($validationErrors as $error) { |
||
| 889 | $helpfulErrorMessage .= sprintf("+ %s: %s\n", $error->getPropertyPath(), $error->getMessage()); |
||
| 890 | } |
||
| 891 | } else { |
||
| 892 | $helpfulErrorMessage = substr($client->getResponse(), 0, 200); |
||
| 893 | } |
||
| 894 | } |
||
| 895 | |||
| 896 | 9 | self::assertEquals($expectedStatusCode, $client->getResponse()->getStatusCode(), $helpfulErrorMessage); |
|
| 897 | 9 | } |
|
| 898 | |||
| 899 | /** |
||
| 900 | * Assert that the last validation errors within $container match the |
||
| 901 | * expected keys. |
||
| 902 | * |
||
| 903 | * @param array $expected A flat array of field names |
||
| 904 | * @param ContainerInterface $container |
||
| 905 | */ |
||
| 906 | 2 | public function assertValidationErrors(array $expected, ContainerInterface $container) |
|
| 907 | { |
||
| 908 | 2 | self::assertThat( |
|
| 909 | 2 | $container->get('liip_functional_test.validator')->getLastErrors(), |
|
| 910 | 2 | new ValidationErrorsConstraint($expected), |
|
| 911 | 'Validation errors should match.' |
||
| 912 | 2 | ); |
|
| 913 | 1 | } |
|
| 914 | } |
||
| 915 |
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: