/** * Discovers all available tests in all extensions. * * @param string $extension * (optional) The name of an extension to limit discovery to; e.g., 'node'. * @param string[] $types * An array of included test types. * * @return array * An array of tests keyed by the the group name. * @code * $groups['block'] => array( * 'Drupal\block\Tests\BlockTest' => array( * 'name' => 'Drupal\block\Tests\BlockTest', * 'description' => 'Tests block UI CRUD functionality.', * 'group' => 'block', * ), * ); * @endcode * * @todo Remove singular grouping; retain list of groups in 'group' key. * @see https://www.drupal.org/node/2296615 */ public function getTestClasses($extension = NULL, array $types = []) { $reader = new SimpleAnnotationReader(); $reader->addNamespace('Drupal\\simpletest\\Annotation'); if (!isset($extension)) { if ($this->cacheBackend && ($cache = $this->cacheBackend->get('simpletest:discovery:classes'))) { return $cache->data; } } $list = array(); $classmap = $this->findAllClassFiles($extension); // Prevent expensive class loader lookups for each reflected test class by // registering the complete classmap of test classes to the class loader. // This also ensures that test classes are loaded from the discovered // pathnames; a namespace/classname mismatch will throw an exception. $this->classLoader->addClassMap($classmap); foreach ($classmap as $classname => $pathname) { $finder = MockFileFinder::create($pathname); $parser = new StaticReflectionParser($classname, $finder, TRUE); try { $info = static::getTestInfo($classname, $parser->getDocComment()); } catch (MissingGroupException $e) { // If the class name ends in Test and is not a migrate table dump. if (preg_match('/Test$/', $classname) && strpos($classname, 'migrate_drupal\\Tests\\Table') === FALSE) { throw $e; } // If the class is @group annotation just skip it. Most likely it is an // abstract class, trait or test fixture. continue; } // Skip this test class if it requires unavailable modules. // @todo PHPUnit skips tests with unmet requirements when executing a test // (instead of excluding them upfront). Refactor test runner to follow // that approach. // @see https://www.drupal.org/node/1273478 if (!empty($info['requires']['module'])) { if (array_diff($info['requires']['module'], $this->availableExtensions['module'])) { continue; } } $list[$info['group']][$classname] = $info; } // Sort the groups and tests within the groups by name. uksort($list, 'strnatcasecmp'); foreach ($list as &$tests) { uksort($tests, 'strnatcasecmp'); } // Allow modules extending core tests to disable originals. $this->moduleHandler->alter('simpletest', $list); if (!isset($extension)) { if ($this->cacheBackend) { $this->cacheBackend->set('simpletest:discovery:classes', $list); } } if ($types) { $list = NestedArray::filter($list, function ($element) use($types) { return !(is_array($element) && isset($element['type']) && !in_array($element['type'], $types)); }); } return $list; }
/** * @covers ::filter * @dataProvider providerTestFilter */ public function testFilter($array, $callable, $expected) { $this->assertEquals($expected, NestedArray::filter($array, $callable)); }