protected function testList(DrupalStyle $io, $group) { $testingGroups = $this->test_discovery->getTestClasses(null); if (empty($group)) { $tableHeader = [$this->trans('commands.test.debug.messages.group')]; } else { $tableHeader = [$this->trans('commands.test.debug.messages.class'), $this->trans('commands.test.debug.messages.type')]; $io->writeln(sprintf('%s: %s', $this->trans('commands.test.debug.messages.group'), $group)); } $tableRows = []; foreach ($testingGroups as $testing_group => $tests) { if (empty($group)) { $tableRows[] = [$testing_group]; continue; } if (!empty($group) && $group != $testing_group) { continue; } foreach ($tests as $test) { if (is_subclass_of($test['name'], 'PHPUnit_Framework_TestCase')) { $test['type'] = 'phpunit'; } else { $test['type'] = 'simpletest'; } $tableRows[] = [$test['name'], $test['type']]; } } $io->table($tableHeader, $tableRows, 'compact'); if ($group) { $io->success(sprintf($this->trans('commands.test.debug.messages.success-group'), $group)); } else { $io->success($this->trans('commands.test.debug.messages.success-groups')); } }
/** * @covers ::getTestInfo * @expectedException \Drupal\simpletest\Exception\MissingSummaryLineException * @expectedExceptionMessage Missing PHPDoc summary line in Drupal\field\Tests\BulkDeleteTest */ public function testTestInfoParserMissingSummary() { $classname = 'Drupal\\field\\Tests\\BulkDeleteTest'; $doc_comment = <<<EOT /** * @group field */ EOT; \Drupal\simpletest\TestDiscovery::getTestInfo($classname, $doc_comment); }
/** * @covers ::getTestInfo */ public function testTestInfoParserMissingSummary() { $classname = 'Drupal\\field\\Tests\\BulkDeleteTest'; $doc_comment = <<<EOT /** * @group field */ EOT; $info = \Drupal\simpletest\TestDiscovery::getTestInfo($classname, $doc_comment); $this->assertEmpty($info['description']); }
/** * Find and add tests to the suite for core and any extensions. * * @param string $root * Path to the root of the Drupal installation. * @param string $suite_namespace * SubNamespace used to separate test suite. Examples: Unit, Functional. */ protected function addTestsBySuiteNamespace($root, $suite_namespace) { // Core's tests are in the namespace Drupal\${suite_namespace}Tests\ and are // always inside of core/tests/Drupal/${suite_namespace}Tests. The exception // to this is Unit tests for historical reasons. if ($suite_namespace == 'Unit') { $this->addTestFiles(TestDiscovery::scanDirectory("Drupal\\Tests\\", "{$root}/core/tests/Drupal/Tests")); } else { $this->addTestFiles(TestDiscovery::scanDirectory("Drupal\\{$suite_namespace}Tests\\", "{$root}/core/tests/Drupal/{$suite_namespace}Tests")); } // Extensions' tests will always be in the namespace // Drupal\Tests\$extension_name\$suite_namespace\ and be in the // $extension_path/tests/src/$suite_namespace directory. Not all extensions // will have all kinds of tests. foreach ($this->findExtensionDirectories($root) as $extension_name => $dir) { $test_path = "{$dir}/tests/src/{$suite_namespace}"; if (is_dir($test_path)) { $this->addTestFiles(TestDiscovery::scanDirectory("Drupal\\Tests\\{$extension_name}\\{$suite_namespace}\\", $test_path)); } } }
/** * @covers ::getPhpunitTestSuite * @dataProvider providerTestGetPhpunitTestSuite */ public function testGetPhpunitTestSuite($classname, $expected) { $this->assertEquals($expected, TestDiscovery::getPhpunitTestSuite($classname)); }
/** * {@inheritdoc} */ public function buildForm(array $form, array &$form_state, $test_id = NULL) { $this->buildStatusImageMap(); // Make sure there are test results to display and a re-run is not being // performed. $results = array(); if (is_numeric($test_id) && !($results = $this->getResults($test_id))) { drupal_set_message($this->t('No test results to display.'), 'error'); return new RedirectResponse(url('admin/config/development/testing', array('absolute' => TRUE))); } // Load all classes and include CSS. $form['#attached']['css'][] = drupal_get_path('module', 'simpletest') . '/css/simpletest.module.css'; // Keep track of which test cases passed or failed. $filter = array('pass' => array(), 'fail' => array()); // Summary result widget. $form['result'] = array('#type' => 'fieldset', '#title' => $this->t('Results')); $form['result']['summary'] = $summary = array('#theme' => 'simpletest_result_summary', '#pass' => 0, '#fail' => 0, '#exception' => 0, '#debug' => 0); simpletest_classloader_register(); // Cycle through each test group. $header = array($this->t('Message'), $this->t('Group'), $this->t('Filename'), $this->t('Line'), $this->t('Function'), array('colspan' => 2, 'data' => $this->t('Status'))); $form['result']['results'] = array(); foreach ($results as $group => $assertions) { // Create group details with summary information. $info = TestDiscovery::getTestInfo(new \ReflectionClass($group)); $form['result']['results'][$group] = array('#type' => 'details', '#title' => $info['name'], '#open' => TRUE, '#description' => $info['description']); $form['result']['results'][$group]['summary'] = $summary; $group_summary =& $form['result']['results'][$group]['summary']; // Create table of assertions for the group. $rows = array(); foreach ($assertions as $assertion) { $row = array(); // Assertion messages are in code, so we assume they are safe. $row[] = SafeMarkup::set($assertion->message); $row[] = $assertion->message_group; $row[] = drupal_basename($assertion->file); $row[] = $assertion->line; $row[] = $assertion->function; $row[] = $this->statusImageMap[$assertion->status]; $class = 'simpletest-' . $assertion->status; if ($assertion->message_group == 'Debug') { $class = 'simpletest-debug'; } $rows[] = array('data' => $row, 'class' => array($class)); $group_summary['#' . $assertion->status]++; $form['result']['summary']['#' . $assertion->status]++; } $form['result']['results'][$group]['table'] = array('#type' => 'table', '#header' => $header, '#rows' => $rows); // Set summary information. $group_summary['#ok'] = $group_summary['#fail'] + $group_summary['#exception'] == 0; $form['result']['results'][$group]['#open'] = !$group_summary['#ok']; // Store test group (class) as for use in filter. $filter[$group_summary['#ok'] ? 'pass' : 'fail'][] = $group; } // Overall summary status. $form['result']['summary']['#ok'] = $form['result']['summary']['#fail'] + $form['result']['summary']['#exception'] == 0; // Actions. $form['#action'] = url('admin/config/development/testing/results/re-run'); $form['action'] = array('#type' => 'fieldset', '#title' => $this->t('Actions'), '#attributes' => array('class' => array('container-inline')), '#weight' => -11); $form['action']['filter'] = array('#type' => 'select', '#title' => 'Filter', '#options' => array('all' => $this->t('All (@count)', array('@count' => count($filter['pass']) + count($filter['fail']))), 'pass' => $this->t('Pass (@count)', array('@count' => count($filter['pass']))), 'fail' => $this->t('Fail (@count)', array('@count' => count($filter['fail']))))); $form['action']['filter']['#default_value'] = $filter['fail'] ? 'fail' : 'all'; // Categorized test classes for to be used with selected filter value. $form['action']['filter_pass'] = array('#type' => 'hidden', '#default_value' => implode(',', $filter['pass'])); $form['action']['filter_fail'] = array('#type' => 'hidden', '#default_value' => implode(',', $filter['fail'])); $form['action']['op'] = array('#type' => 'submit', '#value' => $this->t('Run tests')); $form['action']['return'] = array('#type' => 'link', '#title' => $this->t('Return to list'), '#href' => 'admin/config/development/testing'); if (is_numeric($test_id)) { simpletest_clean_results_table($test_id); } return $form; }
/** * Adds the result form to a $form. * * This is a static method so that run-tests.sh can use it to generate a * results page completely external to Drupal. This is why the UI strings are * not wrapped in t(). * * @param array $form * The form to attach the results to. * @param array $test_results * The simpletest results. * * @return array * A list of tests the passed and failed. The array has two keys, 'pass' and * 'fail'. Each contains a list of test classes. * * @see simpletest_script_open_browser() * @see run-tests.sh */ public static function addResultForm(array &$form, array $results) { // Transform the test results to be grouped by test class. $test_results = array(); foreach ($results as $result) { if (!isset($test_results[$result->test_class])) { $test_results[$result->test_class] = array(); } $test_results[$result->test_class][] = $result; } $image_status_map = static::buildStatusImageMap(); // Keep track of which test cases passed or failed. $filter = array('pass' => array(), 'fail' => array()); // Summary result widget. $form['result'] = array('#type' => 'fieldset', '#title' => 'Results', '#attributes' => array()); $form['result']['summary'] = $summary = array('#theme' => 'simpletest_result_summary', '#pass' => 0, '#fail' => 0, '#exception' => 0, '#debug' => 0); \Drupal::service('test_discovery')->registerTestNamespaces(); // Cycle through each test group. $header = array('Message', 'Group', 'Filename', 'Line', 'Function', array('colspan' => 2, 'data' => 'Status')); $form['result']['results'] = array(); foreach ($test_results as $group => $assertions) { // Create group details with summary information. $info = TestDiscovery::getTestInfo($group); $form['result']['results'][$group] = array('#type' => 'details', '#title' => $info['name'], '#open' => TRUE, '#description' => $info['description']); $form['result']['results'][$group]['summary'] = $summary; $group_summary =& $form['result']['results'][$group]['summary']; // Create table of assertions for the group. $rows = array(); foreach ($assertions as $assertion) { $row = array(); $row[] = SafeMarkup::checkAdminXss($assertion->message); $row[] = $assertion->message_group; $row[] = \Drupal::service('file_system')->basename($assertion->file); $row[] = $assertion->line; $row[] = $assertion->function; $row[] = ['data' => $image_status_map[$assertion->status]]; $class = 'simpletest-' . $assertion->status; if ($assertion->message_group == 'Debug') { $class = 'simpletest-debug'; } $rows[] = array('data' => $row, 'class' => array($class)); $group_summary['#' . $assertion->status]++; $form['result']['summary']['#' . $assertion->status]++; } $form['result']['results'][$group]['table'] = array('#type' => 'table', '#header' => $header, '#rows' => $rows); // Set summary information. $group_summary['#ok'] = $group_summary['#fail'] + $group_summary['#exception'] == 0; $form['result']['results'][$group]['#open'] = !$group_summary['#ok']; // Store test group (class) as for use in filter. $filter[$group_summary['#ok'] ? 'pass' : 'fail'][] = $group; } // Overall summary status. $form['result']['summary']['#ok'] = $form['result']['summary']['#fail'] + $form['result']['summary']['#exception'] == 0; return $filter; }
/** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { $io = new DrupalStyle($input, $output); //Registers namespaces for disabled modules. $this->test_discovery->registerTestNamespaces(); $testClass = $input->getArgument('test-class'); $testMethods = $input->getArgument('test-methods'); $url = $input->getOption('url'); if (!$url) { $io->error($this->trans('commands.test.run.messages.url-required')); return null; } $this->setEnvironment($url); // Create simpletest test id $testId = db_insert('simpletest_test_id')->useDefaults(['test_id'])->execute(); if (is_subclass_of($testClass, 'PHPUnit_Framework_TestCase')) { $io->info($this->trans('commands.test.run.messages.phpunit-pending')); return null; } else { if (!class_exists($testClass)) { $io->error(sprintf($this->trans('commands.test.run.messages.invalid-class'), $testClass)); return 1; } $test = new $testClass($testId); $io->info($this->trans('commands.test.run.messages.starting-test')); Timer::start('run-tests'); $test->run($testMethods); $end = Timer::stop('run-tests'); $io->simple($this->trans('commands.test.run.messages.test-duration') . ': ' . $this->dateFormatter->formatInterval($end['time'] / 1000)); $io->simple($this->trans('commands.test.run.messages.test-pass') . ': ' . $test->results['#pass']); $io->commentBlock($this->trans('commands.test.run.messages.test-fail') . ': ' . $test->results['#fail']); $io->commentBlock($this->trans('commands.test.run.messages.test-exception') . ': ' . $test->results['#exception']); $io->simple($this->trans('commands.test.run.messages.test-debug') . ': ' . $test->results['#debug']); $this->moduleHandler->invokeAll('test_finished', [$test->results]); $io->newLine(); $io->info($this->trans('commands.test.run.messages.test-summary')); $io->newLine(); $currentClass = null; $currentGroup = null; $currentStatus = null; $messages = $this->simpletestScriptLoadMessagesByTestIds([$testId]); foreach ($messages as $message) { if ($currentClass === null || $currentClass != $message->test_class) { $currentClass = $message->test_class; $io->comment($message->test_class); } if ($currentGroup === null || $currentGroup != $message->message_group) { $currentGroup = $message->message_group; } if ($currentStatus === null || $currentStatus != $message->status) { $currentStatus = $message->status; if ($message->status == 'fail') { $io->error($this->trans('commands.test.run.messages.group') . ':' . $message->message_group . ' ' . $this->trans('commands.test.run.messages.status') . ':' . $message->status); $io->newLine(); } else { $io->info($this->trans('commands.test.run.messages.group') . ':' . $message->message_group . ' ' . $this->trans('commands.test.run.messages.status') . ':' . $message->status); $io->newLine(); } } $io->simple($this->trans('commands.test.run.messages.file') . ': ' . str_replace($this->appRoot, '', $message->file)); $io->simple($this->trans('commands.test.run.messages.method') . ': ' . $message->function); $io->simple($this->trans('commands.test.run.messages.line') . ': ' . $message->line); $io->simple($this->trans('commands.test.run.messages.message') . ': ' . $message->message); $io->newLine(); } return null; } }