diff --git a/modules/simpletest/drupal_web_test_case.php b/modules/simpletest/drupal_web_test_case.php index 694880b..2c32917 100644 --- a/modules/simpletest/drupal_web_test_case.php +++ b/modules/simpletest/drupal_web_test_case.php @@ -447,7 +447,8 @@ abstract class DrupalTestCase { */ protected function verbose($message) { if ($id = simpletest_verbose($message)) { - $url = file_create_url($this->originalFileDirectory . '/simpletest/verbose/' . get_class($this) . '-' . $id . '.html'); + $class_safe = str_replace('\\', '-', get_class($this)); + $url = file_create_url($this->originalFileDirectory . '/simpletest/verbose/' . $class_safe . '-' . $id . '.html'); $this->error(l(t('Verbose message'), $url, array('attributes' => array('target' => '_blank'))), 'User notice'); } } @@ -466,7 +467,8 @@ abstract class DrupalTestCase { */ public function run(array $methods = array()) { // Initialize verbose debugging. - simpletest_verbose(NULL, variable_get('file_public_path', conf_path() . '/files'), get_class($this)); + $class = get_class($this); + simpletest_verbose(NULL, variable_get('file_public_path', conf_path() . '/files'), str_replace('\\', '-', $class)); // HTTP auth settings (:) for the simpletest browser // when sending requests to the test site. @@ -478,7 +480,6 @@ abstract class DrupalTestCase { } set_error_handler(array($this, 'errorHandler')); - $class = get_class($this); // Iterate through all the methods in this class, unless a specific list of // methods to run was passed. $class_methods = get_class_methods($class); diff --git a/modules/simpletest/lib/Drupal/simpletest/Tests/PSR0WebTest.php b/modules/simpletest/lib/Drupal/simpletest/Tests/PSR0WebTest.php new file mode 100644 index 0000000..7e7f7c2 --- /dev/null +++ b/modules/simpletest/lib/Drupal/simpletest/Tests/PSR0WebTest.php @@ -0,0 +1,32 @@ + 'PSR0 web test', + 'description' => 'Test PSR0 test classes.', + 'group' => 'SimpleTest', + ); + } + + function testArithmetics() { + $this->assert(1 + 1 == 2, '1 + 1 == 2'); + } +} diff --git a/modules/simpletest/simpletest.module b/modules/simpletest/simpletest.module index f825755..224a615 100644 --- a/modules/simpletest/simpletest.module +++ b/modules/simpletest/simpletest.module @@ -157,6 +157,7 @@ function simpletest_run_tests($test_list, $reporter = 'drupal') { * Batch operation callback. */ function _simpletest_batch_operation($test_list_init, $test_id, &$context) { + simpletest_classloader_register(); // Get working values. if (!isset($context['sandbox']['max'])) { // First iteration: initialize working values. @@ -289,6 +290,9 @@ function simpletest_log_read($test_id, $prefix, $test_class, $during_test = FALS * a static variable. In order to list tests provided by disabled modules * hook_registry_files_alter() is used to forcefully add them to the registry. * + * PSR-0 classes are found by searching the designated directory for each module + * for files matching the PSR-0 standard. + * * @return * An array of tests keyed with the groups specified in each of the tests * getInfo() method and then keyed by the test class. An example of the array @@ -309,6 +313,9 @@ function simpletest_test_get_all() { $groups = &drupal_static(__FUNCTION__); if (!$groups) { + // Register a simple class loader for PSR-0 test classes. + simpletest_classloader_register(); + // Load test information from cache if available, otherwise retrieve the // information from each tests getInfo() method. if ($cache = cache_get('simpletest', 'cache')) { @@ -318,6 +325,42 @@ function simpletest_test_get_all() { // Select all clases in files ending with .test. $classes = db_query("SELECT name FROM {registry} WHERE type = :type AND filename LIKE :name", array(':type' => 'class', ':name' => '%.test'))->fetchCol(); + // Select all PSR-0 classes in the Tests namespace of all modules. + $system_list = db_query("SELECT name, filename FROM {system}")->fetchAllKeyed(); + + foreach ($system_list as $name => $filename) { + // Build directory in which the test files would reside. + $tests_dir = DRUPAL_ROOT . '/' . dirname($filename) . '/lib/Drupal/' . $name . '/Tests'; + $tests_dir_strlen = strlen($tests_dir); + // Scan it for test files if it exists. + if (is_dir($tests_dir)) { + $files = file_scan_directory($tests_dir, '/.*\.php/'); + if (!empty($files)) { + $tests_namespace = 'Drupal\\' . $name . '\\Tests\\'; + $l = strlen($tests_namespace); + foreach ($files as $file) { + require_once $file->uri; + // Convert the file name into the namespaced class name. + if (substr($file->uri, 0, $tests_dir_strlen + 1) === $tests_dir . '/') { + $suffix = substr($file->uri, $tests_dir_strlen + 1, -4); + $suffix = str_replace('/', '\\', $suffix); + while (TRUE) { + $class = $tests_namespace . $suffix; + if (class_exists($class, FALSE)) { + $classes[] = $class; + } + $pos = strrpos($suffix, '\\'); + if (FALSE === $pos) { + break; + } + $suffix{$pos} = '_'; + } + } + } + } + } + } + // Check that each class has a getInfo() method and store the information // in an array keyed with the group specified in the test information. $groups = array(); @@ -354,6 +397,42 @@ function simpletest_test_get_all() { } /** + * Register a simple class loader that can find D8-style PSR-0 test classes. + * Other PSR-0 class loading can happen in contrib, but for test classes we need + * to do in core. + */ +function simpletest_classloader_register() { + spl_autoload_register('_simpletest_autoload'); +} + +/** + * Autoload callback to find PSR-0 test classes. + */ +function _simpletest_autoload($class) { + static $extensions; + if (substr($class, 0, 7) === 'Drupal\\') { + $pos = strpos($class, '\\', 7); + if (substr($class, $pos, 7) === '\\Tests\\') { + $extension = substr($class, 7, $pos - 7); + if (!isset($extensions)) { + $extensions = db_query("SELECT name, filename FROM {system}")->fetchAllKeyed(); + } + if (isset($extensions[$extension])) { + $nspos = strrpos($class, '\\'); + $namespace = substr($class, 0, $nspos); + $classname = substr($class, $nspos + 1); + $path = dirname($extensions[$extension]) . '/lib/' . + str_replace('\\', '/', $namespace) . '/' . + str_replace('_', '/', $classname) . '.php'; + if (file_exists($path)) { + require_once $path; + } + } + } + } +} + +/** * Implements hook_registry_files_alter(). * * Add the test files for disabled modules so that we get a list containing diff --git a/modules/simpletest/simpletest.pages.inc b/modules/simpletest/simpletest.pages.inc index d1a7e4a..9302fa2 100644 --- a/modules/simpletest/simpletest.pages.inc +++ b/modules/simpletest/simpletest.pages.inc @@ -181,8 +181,10 @@ function theme_simpletest_test_table($variables) { * Run selected tests. */ function simpletest_test_form_submit($form, &$form_state) { + simpletest_classloader_register(); // Get list of tests. $tests_list = array(); + simpletest_classloader_register(); foreach ($form_state['values'] as $class_name => $value) { // Since class_exists() will likely trigger an autoload lookup, // we do the fast check first. @@ -233,6 +235,8 @@ function simpletest_result_form($form, &$form_state, $test_id) { '#debug' => 0, ); + simpletest_classloader_register(); + // Cycle through each test group. $header = array(t('Message'), t('Group'), t('Filename'), t('Line'), t('Function'), array('colspan' => 2, 'data' => t('Status'))); $form['result']['results'] = array(); diff --git a/modules/simpletest/simpletest.test b/modules/simpletest/simpletest.test index c67b004..e581f1c 100644 --- a/modules/simpletest/simpletest.test +++ b/modules/simpletest/simpletest.test @@ -655,3 +655,69 @@ class SimpleTestOtherInstallationProfileModuleTestsTestCase extends DrupalWebTes $this->assertNoText('Installation profile module tests helper'); } } + +/** + * Verifies that tests in other installation profiles are not found. + * + * @see SimpleTestInstallationProfileModuleTestsTestCase + */ +class SimpleTestDiscoveryTestCase extends DrupalWebTestCase { + /** + * Use the Testing profile. + * + * The Testing profile contains drupal_system_listing_compatible_test.test, + * which attempts to: + * - run tests using the Minimal profile (which does not contain the + * drupal_system_listing_compatible_test.module) + * - but still install the drupal_system_listing_compatible_test.module + * contained in the Testing profile. + * + * @see DrupalSystemListingCompatibleTestCase + */ + protected $profile = 'testing'; + + public static function getInfo() { + return array( + 'name' => 'Discovery of test classes', + 'description' => 'Verifies that tests classes are discovered and can be autoloaded (class_exists).', + 'group' => 'SimpleTest', + ); + } + + function setUp() { + parent::setUp(array('simpletest')); + + $this->admin_user = $this->drupalCreateUser(array('administer unit tests')); + $this->drupalLogin($this->admin_user); + } + + /** + * Tests existence of test cases. + */ + function testDiscovery() { + $this->drupalGet('admin/config/development/testing'); + // Tests within enabled modules. + // (without these, this test wouldn't happen in the first place, so this is + // a bit pointless. We still run it for proof-of-concept.) + // This one is defined in system module. + $this->assertText('Drupal error handlers'); + // This one is defined in simpletest module. + $this->assertText('Discovery of test classes'); + // Tests within disabled modules. + if (version_compare(PHP_VERSION, '5.3') < 0) { + // Don't expect PSR-0 tests to be discovered on older PHP versions. + return; + } + // This one is provided by simpletest itself via PSR-0. + $this->assertText('PSR0 web test'); + $this->assertText('PSR0 example test: PSR-0 in disabled modules.'); + $this->assertText('PSR0 example test: PSR-0 in nested subfolders.'); + $this->assertText('PSR0 example test: PSR-0 plus underscore.'); + $edit = array( + 'Drupal\\simpletest\\Tests\\PSR0WebTest' => TRUE, + ); + $this->drupalPost(NULL, $edit, t('Run tests')); + $this->assertText('The test run finished'); + $this->assertText('1 pass, 0 fails, and 0 exceptions'); + } +} diff --git a/modules/simpletest/tests/psr_0_test/lib/Drupal/psr_0_test/Tests/ExampleTest.php b/modules/simpletest/tests/psr_0_test/lib/Drupal/psr_0_test/Tests/ExampleTest.php new file mode 100644 index 0000000..af8126f --- /dev/null +++ b/modules/simpletest/tests/psr_0_test/lib/Drupal/psr_0_test/Tests/ExampleTest.php @@ -0,0 +1,32 @@ + 'PSR0 example test: PSR-0 in disabled modules.', + 'description' => 'Test PSR-0 classes.', + 'group' => 'SimpleTest', + ); + } + + function testArithmetics() { + $this->assert(1 + 1 == 2, '1 + 1 == 2'); + } +} diff --git a/modules/simpletest/tests/psr_0_test/lib/Drupal/psr_0_test/Tests/Nested/NestedExampleTest.php b/modules/simpletest/tests/psr_0_test/lib/Drupal/psr_0_test/Tests/Nested/NestedExampleTest.php new file mode 100644 index 0000000..8485067 --- /dev/null +++ b/modules/simpletest/tests/psr_0_test/lib/Drupal/psr_0_test/Tests/Nested/NestedExampleTest.php @@ -0,0 +1,32 @@ + 'PSR0 example test: PSR-0 in nested subfolders.', + 'description' => 'Test PSR-0 classes.', + 'group' => 'SimpleTest', + ); + } + + function testArithmetics() { + $this->assert(1 + 1 == 2, '1 + 1 == 2'); + } +} diff --git a/modules/simpletest/tests/psr_0_test/lib/Drupal/psr_0_test/Tests/Nested/Underscore/UnderscoreExampleTest.php b/modules/simpletest/tests/psr_0_test/lib/Drupal/psr_0_test/Tests/Nested/Underscore/UnderscoreExampleTest.php new file mode 100644 index 0000000..9731ed4 --- /dev/null +++ b/modules/simpletest/tests/psr_0_test/lib/Drupal/psr_0_test/Tests/Nested/Underscore/UnderscoreExampleTest.php @@ -0,0 +1,32 @@ + 'PSR0 example test: PSR-0 plus underscore.', + 'description' => 'Test PSR-0 classes.', + 'group' => 'SimpleTest', + ); + } + + function testArithmetics() { + $this->assert(1 + 1 == 2, '1 + 1 == 2'); + } +} diff --git a/modules/simpletest/tests/psr_0_test/psr_0_test.info b/modules/simpletest/tests/psr_0_test/psr_0_test.info new file mode 100644 index 0000000..b2b2c7e --- /dev/null +++ b/modules/simpletest/tests/psr_0_test/psr_0_test.info @@ -0,0 +1,3 @@ +name = PSR-0 Test cases +description = Test classes to be discovered by simpletest. +core = 7.x diff --git a/modules/simpletest/tests/psr_0_test/psr_0_test.module b/modules/simpletest/tests/psr_0_test/psr_0_test.module new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/modules/simpletest/tests/psr_0_test/psr_0_test.module @@ -0,0 +1 @@ +