Commit 96838913 authored by alexpott's avatar alexpott

Issue #2670978 by dawehner, isntall, jibran, alexpott: Allow to run just...

Issue #2670978 by dawehner, isntall, jibran, alexpott: Allow to run just specific types when running all tests
parent e6601681
......@@ -349,4 +349,26 @@ public static function mergeDeepArray(array $arrays, $preserve_integer_keys = FA
return $result;
}
/**
* Filters a nested array recursively.
*
* @param array $array
* The filtered nested array.
* @param callable|NULL $callable
* The callable to apply for filtering.
*
* @return array
* The filtered array.
*/
public static function filter(array $array, callable $callable = NULL) {
$array = is_callable($callable) ? array_filter($array, $callable) : array_filter($array);
foreach ($array as &$element) {
if (is_array($element)) {
$element = static::filter($element, $callable);
}
}
return $array;
}
}
......@@ -489,9 +489,10 @@ function simpletest_log_read($test_id, $database_prefix, $test_class) {
* each module for files matching the PSR-0 standard. Once loaded the test list
* is cached and stored in a static variable.
*
* @param string $module
* Name of a module. If set then only tests belonging to this module are
* returned.
* @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 with the groups, and then keyed by test classes.
......@@ -506,8 +507,8 @@ function simpletest_log_read($test_id, $database_prefix, $test_class) {
* );
* @endcode
*/
function simpletest_test_get_all($module = NULL) {
return \Drupal::service('test_discovery')->getTestClasses($module);
function simpletest_test_get_all($extension = NULL, array $types = []) {
return \Drupal::service('test_discovery')->getTestClasses($extension, $types);
}
/**
......
services:
test_discovery:
class: Drupal\simpletest\TestDiscovery
arguments: ['@class_loader', '@?cache.discovery']
arguments: ['@app.root', '@class_loader', '@module_handler', '@?cache.discovery']
......@@ -10,9 +10,11 @@
use Doctrine\Common\Annotations\SimpleAnnotationReader;
use Doctrine\Common\Reflection\StaticReflectionParser;
use Drupal\Component\Annotation\Reflection\MockFileFinder;
use Drupal\Component\Utility\NestedArray;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ExtensionDiscovery;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\simpletest\Exception\MissingGroupException;
use PHPUnit_Util_Test;
......@@ -49,18 +51,38 @@ class TestDiscovery {
*/
protected $availableExtensions;
/**
* The app root.
*
* @var string
*/
protected $root;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* Constructs a new test discovery.
*
* @param string $root
* The app root.
* @param $class_loader
* The class loader. Normally Composer's ClassLoader, as included by the
* front controller, but may also be decorated; e.g.,
* \Symfony\Component\ClassLoader\ApcClassLoader.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
* (optional) Backend for caching discovery results.
*/
public function __construct($class_loader, CacheBackendInterface $cache_backend = NULL) {
public function __construct($root, $class_loader, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache_backend = NULL) {
$this->root = $root;
$this->classLoader = $class_loader;
$this->moduleHandler = $module_handler;
$this->cacheBackend = $cache_backend;
}
......@@ -80,15 +102,16 @@ public function registerTestNamespaces() {
$existing = $this->classLoader->getPrefixesPsr4();
// Add PHPUnit test namespaces of Drupal core.
$this->testNamespaces['Drupal\\Tests\\'] = [DRUPAL_ROOT . '/core/tests/Drupal/Tests'];
$this->testNamespaces['Drupal\\KernelTests\\'] = [DRUPAL_ROOT . '/core/tests/Drupal/KernelTests'];
$this->testNamespaces['Drupal\\FunctionalTests\\'] = [DRUPAL_ROOT . '/core/tests/Drupal/FunctionalTests'];
$this->testNamespaces['Drupal\\Tests\\'] = [$this->root . '/core/tests/Drupal/Tests'];
$this->testNamespaces['Drupal\\KernelTests\\'] = [$this->root . '/core/tests/Drupal/KernelTests'];
$this->testNamespaces['Drupal\\FunctionalTests\\'] = [$this->root . '/core/tests/Drupal/FunctionalTests'];
$this->testNamespaces['Drupal\\FunctionalJavascriptTests\\'] = [$this->root . '/core/tests/Drupal/FunctionalJavascriptTests'];
$this->availableExtensions = array();
foreach ($this->getExtensions() as $name => $extension) {
$this->availableExtensions[$extension->getType()][$name] = $name;
$base_path = DRUPAL_ROOT . '/' . $extension->getPath();
$base_path = $this->root . '/' . $extension->getPath();
// Add namespace of disabled/uninstalled extensions.
if (!isset($existing["Drupal\\$name\\"])) {
......@@ -115,11 +138,12 @@ public function registerTestNamespaces() {
*
* @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 first @group specified in each test's
* PHPDoc comment block, and then keyed by class names. For example:
* @code
* An array of tests keyed by the the group name.
* @code
* $groups['block'] => array(
* 'Drupal\block\Tests\BlockTest' => array(
* 'name' => 'Drupal\block\Tests\BlockTest',
......@@ -127,15 +151,12 @@ public function registerTestNamespaces() {
* 'group' => 'block',
* ),
* );
* @endcode
*
* @throws \ReflectionException
* If a discovered test class does not match the expected class name.
* @endcode
*
* @todo Remove singular grouping; retain list of groups in 'group' key.
* @see https://www.drupal.org/node/2296615
*/
public function getTestClasses($extension = NULL) {
public function getTestClasses($extension = NULL, array $types = []) {
$reader = new SimpleAnnotationReader();
$reader->addNamespace('Drupal\\simpletest\\Annotation');
......@@ -190,13 +211,20 @@ public function getTestClasses($extension = NULL) {
}
// Allow modules extending core tests to disable originals.
\Drupal::moduleHandler()->alter('simpletest', $list);
$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;
}
......@@ -450,7 +478,7 @@ public static function getPhpunitTestSuite($classname) {
* An array of Extension objects, keyed by extension name.
*/
protected function getExtensions() {
$listing = new ExtensionDiscovery(DRUPAL_ROOT);
$listing = new ExtensionDiscovery($this->root);
// Ensure that tests in all profiles are discovered.
$listing->setProfileDirectories(array());
$extensions = $listing->scan('module', TRUE);
......
......@@ -7,8 +7,12 @@
namespace Drupal\Tests\simpletest\Unit;
use Composer\Autoload\ClassLoader;
use Drupal\Core\Extension\Extension;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\simpletest\TestDiscovery;
use Drupal\Tests\UnitTestCase;
use org\bovigo\vfs\vfsStream;
/**
* @coversDefaultClass \Drupal\simpletest\TestDiscovery
......@@ -256,6 +260,130 @@ public function testTestInfoParserMissingSummary() {
$this->assertEmpty($info['description']);
}
protected function setupVfsWithTestClasses() {
vfsStream::setup('drupal');
$test_file = <<<EOF
<?php
/**
* Test description
* @group example
*/
class FunctionalExampleTest {}
EOF;
vfsStream::create([
'modules' => [
'test_module' => [
'tests' => [
'src' => [
'Functional' => [
'FunctionalExampleTest.php' => $test_file,
'FunctionalExampleTest2.php' => str_replace(['FunctionalExampleTest', '@group example'], ['FunctionalExampleTest2', '@group example2'], $test_file),
],
'Kernel' => [
'KernelExampleTest3.php' => str_replace(['FunctionalExampleTest', '@group example'], ['KernelExampleTest3', '@group example2'], $test_file),
],
],
],
],
],
]);
}
/**
* @covers ::getTestClasses
*/
public function testGetTestClasses() {
$this->setupVfsWithTestClasses();
$class_loader = $this->prophesize(ClassLoader::class);
$module_handler = $this->prophesize(ModuleHandlerInterface::class);
$test_discovery = new TestTestDiscovery('vfs://drupal', $class_loader->reveal(), $module_handler->reveal());
$extensions = [
'test_module' => new Extension('vfs://drupal', 'module', 'modules/test_module/test_module.info.yml'),
];
$test_discovery->setExtensions($extensions);
$result = $test_discovery->getTestClasses();
$this->assertCount(2, $result);
$this->assertEquals([
'example' => [
'Drupal\Tests\test_module\Functional\FunctionalExampleTest' => [
'name' => 'Drupal\Tests\test_module\Functional\FunctionalExampleTest',
'description' => 'Test description',
'group' => 'example',
'type' => 'PHPUnit-Functional',
],
],
'example2' => [
'Drupal\Tests\test_module\Functional\FunctionalExampleTest2' => [
'name' => 'Drupal\Tests\test_module\Functional\FunctionalExampleTest2',
'description' => 'Test description',
'group' => 'example2',
'type' => 'PHPUnit-Functional',
],
'Drupal\Tests\test_module\Kernel\KernelExampleTest3' => [
'name' => 'Drupal\Tests\test_module\Kernel\KernelExampleTest3',
'description' => 'Test description',
'group' => 'example2',
'type' => 'PHPUnit-Kernel',
],
],
], $result);
}
/**
* @covers ::getTestClasses
*/
public function testGetTestClassesWithSelectedTypes() {
$this->setupVfsWithTestClasses();
$class_loader = $this->prophesize(ClassLoader::class);
$module_handler = $this->prophesize(ModuleHandlerInterface::class);
$test_discovery = new TestTestDiscovery('vfs://drupal', $class_loader->reveal(), $module_handler->reveal());
$extensions = [
'test_module' => new Extension('vfs://drupal', 'module', 'modules/test_module/test_module.info.yml'),
];
$test_discovery->setExtensions($extensions);
$result = $test_discovery->getTestClasses(NULL, ['PHPUnit-Kernel']);
$this->assertCount(2, $result);
$this->assertEquals([
'example' => [
],
'example2' => [
'Drupal\Tests\test_module\Kernel\KernelExampleTest3' => [
'name' => 'Drupal\Tests\test_module\Kernel\KernelExampleTest3',
'description' => 'Test description',
'group' => 'example2',
'type' => 'PHPUnit-Kernel',
],
],
], $result);
}
}
class TestTestDiscovery extends TestDiscovery {
/**
* @var \Drupal\Core\Extension\Extension[]
*/
protected $extensions = [];
public function setExtensions(array $extensions) {
$this->extensions = $extensions;
}
/**
* {@inheritdoc}
*/
protected function getExtensions() {
return $this->extensions;
}
/**
* @covers ::getPhpunitTestSuite
* @dataProvider providerTestGetPhpunitTestSuite
......
......@@ -208,6 +208,12 @@ function simpletest_script_help() {
Specify the path and the extension
(i.e. 'core/modules/user/user.test').
--types
Runs just tests from the specified test type, for example
run-tests.sh
(i.e. --types "Simpletest,PHPUnit-Functional")
--directory Run all tests found within the specified file directory.
--xml <path>
......@@ -292,6 +298,7 @@ function simpletest_script_parse_args() {
'module' => NULL,
'class' => FALSE,
'file' => FALSE,
'types' => [],
'directory' => NULL,
'color' => FALSE,
'verbose' => FALSE,
......@@ -320,6 +327,10 @@ function simpletest_script_parse_args() {
if (is_bool($args[$previous_arg])) {
$args[$matches[1]] = TRUE;
}
elseif (is_array($args[$previous_arg])) {
$value = array_shift($_SERVER['argv']);
$args[$matches[1]] = array_map('trim', explode(',', $value));
}
else {
$args[$matches[1]] = array_shift($_SERVER['argv']);
}
......@@ -894,7 +905,7 @@ function simpletest_script_get_test_list() {
$test_list = array();
if ($args['all'] || $args['module']) {
try {
$groups = simpletest_test_get_all($args['module']);
$groups = simpletest_test_get_all($args['module'], $args['types']);
}
catch (Exception $e) {
echo (string) $e;
......@@ -916,7 +927,7 @@ function simpletest_script_get_test_list() {
}
else {
try {
$groups = simpletest_test_get_all();
$groups = simpletest_test_get_all(NULL, $args['types']);
}
catch (Exception $e) {
echo (string) $e;
......@@ -1017,7 +1028,7 @@ function simpletest_script_get_test_list() {
}
else {
try {
$groups = simpletest_test_get_all();
$groups = simpletest_test_get_all(NULL, $args['types']);
}
catch (Exception $e) {
echo (string) $e;
......
......@@ -259,4 +259,30 @@ public function testMergeOutOfSequenceKeys() {
$this->assertSame($expected, $actual, 'drupal_array_merge_deep() ignores numeric key order when merging.');
}
/**
* @covers ::filter
* @dataProvider providerTestFilter
*/
public function testFilter($array, $callable, $expected) {
$this->assertEquals($expected, NestedArray::filter($array, $callable));
}
public function providerTestFilter() {
$data = [];
$data['1d-array'] = [
[0, 1, '', TRUE], NULL, [1 => 1, 3 => TRUE]
];
$data['1d-array-callable'] = [
[0, 1, '', TRUE], function ($element) { return $element === ''; }, [2 => '']
];
$data['2d-array'] = [
[[0, 1, '', TRUE], [0, 1, 2, 3]], NULL, [0 => [1 => 1, 3 => TRUE], 1 => [1 => 1, 2 => 2, 3 => 3]],
];
$data['2d-array-callable'] = [
[[0, 1, '', TRUE], [0, 1, 2, 3]], function ($element) { return is_array($element) || $element === 3; }, [0 => [], 1 => [3 => 3]],
];
return $data;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment