diff --git a/core/lib/Drupal/Core/Test/JUnitConverter.php b/core/lib/Drupal/Core/Test/JUnitConverter.php new file mode 100644 index 0000000000000000000000000000000000000000..5af99f07d9cab87874faa4b4d548b2ad6a7ce2ec --- /dev/null +++ b/core/lib/Drupal/Core/Test/JUnitConverter.php @@ -0,0 +1,139 @@ +<?php + +namespace Drupal\Core\Test; + +/** + * Converts JUnit XML to Drupal's {simpletest} schema. + * + * This is mainly for converting PHPUnit test results. + * + * This class is @internal and not considered to be API. + */ +class JUnitConverter { + + /** + * Converts PHPUnit's JUnit XML output file to {simpletest} schema. + * + * @param int $test_id + * The current test ID. + * @param string $phpunit_xml_file + * Path to the PHPUnit XML file. + * + * @return array[] + * The results as array of rows in a format that can be inserted into the + * {simpletest} table of the results database. + * + * @internal + */ + public static function xmlToRows($test_id, $phpunit_xml_file) { + $contents = @file_get_contents($phpunit_xml_file); + if (!$contents) { + return []; + } + return static::xmlElementToRows($test_id, new \SimpleXMLElement($contents)); + } + + /** + * Parse test cases from XML to {simpletest} schema. + * + * @param int $test_id + * The current test ID. + * @param \SimpleXMLElement $element + * The XML data from the JUnit file. + * + * @return array[] + * The results as array of rows in a format that can be inserted into the + * {simpletest} table of the results database. + * + * @internal + */ + public static function xmlElementToRows($test_id, \SimpleXMLElement $element) { + $records = []; + $test_cases = static::findTestCases($element); + foreach ($test_cases as $test_case) { + $records[] = static::convertTestCaseToSimpletestRow($test_id, $test_case); + } + return $records; + } + + /** + * Finds all test cases recursively from a test suite list. + * + * @param \SimpleXMLElement $element + * The PHPUnit xml to search for test cases. + * @param \SimpleXMLElement $parent + * (Optional) The parent of the current element. Defaults to NULL. + * + * @return array + * A list of all test cases. + * + * @internal + */ + public static function findTestCases(\SimpleXMLElement $element, \SimpleXMLElement $parent = NULL) { + if (!isset($parent)) { + $parent = $element; + } + + if ($element->getName() === 'testcase' && (int) $parent->attributes()->tests > 0) { + // Add the class attribute if the test case does not have one. This is the + // case for tests using a data provider. The name of the parent testsuite + // will be in the format class::method. + if (!$element->attributes()->class) { + $name = explode('::', $parent->attributes()->name, 2); + $element->addAttribute('class', $name[0]); + } + return [$element]; + } + $test_cases = []; + foreach ($element as $child) { + $file = (string) $parent->attributes()->file; + if ($file && !$child->attributes()->file) { + $child->addAttribute('file', $file); + } + $test_cases = array_merge($test_cases, static::findTestCases($child, $element)); + } + return $test_cases; + } + + /** + * Converts a PHPUnit test case result to a {simpletest} result row. + * + * @param int $test_id + * The current test ID. + * @param \SimpleXMLElement $test_case + * The PHPUnit test case represented as XML element. + * + * @return array + * An array containing the {simpletest} result row. + * + * @internal + */ + public static function convertTestCaseToSimpletestRow($test_id, \SimpleXMLElement $test_case) { + $message = ''; + $pass = TRUE; + if ($test_case->failure) { + $lines = explode("\n", $test_case->failure); + $message = $lines[2]; + $pass = FALSE; + } + if ($test_case->error) { + $message = $test_case->error; + $pass = FALSE; + } + + $attributes = $test_case->attributes(); + + $record = [ + 'test_id' => $test_id, + 'test_class' => (string) $attributes->class, + 'status' => $pass ? 'pass' : 'fail', + 'message' => $message, + 'message_group' => 'Other', + 'function' => $attributes->class . '->' . $attributes->name . '()', + 'line' => (int) $attributes->line ?: 0, + 'file' => (string) $attributes->file, + ]; + return $record; + } + +} diff --git a/core/lib/Drupal/Core/Test/PhpUnitTestRunner.php b/core/lib/Drupal/Core/Test/PhpUnitTestRunner.php new file mode 100644 index 0000000000000000000000000000000000000000..bc7846e81970694a6d9a5dd08f138f500861a192 --- /dev/null +++ b/core/lib/Drupal/Core/Test/PhpUnitTestRunner.php @@ -0,0 +1,274 @@ +<?php + +namespace Drupal\Core\Test; + +use Drupal\Core\Database\Database; +use Drupal\Core\DependencyInjection\ContainerInjectionInterface; +use Drupal\Tests\Listeners\SimpletestUiPrinter; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\Process\PhpExecutableFinder; + +/** + * Run PHPUnit-based tests. + * + * This class runs PHPUnit-based tests and converts their JUnit results to a + * format that can be stored in the {simpletest} database schema. + * + * This class is @internal and not considered to be API. + * + * @code + * $runner = PhpUnitTestRunner::create(\Drupal::getContainer()); + * $results = $runner->runTests($test_id, $test_list['phpunit']); + * @endcode + */ +class PhpUnitTestRunner implements ContainerInjectionInterface { + + /** + * Path to the working directory. + * + * JUnit log files will be stored in this directory. + * + * @var string + */ + protected $workingDirectory; + + /** + * Path to the application root. + * + * @var string + */ + protected $appRoot; + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + (string) $container->get('app.root'), + (string) $container->get('file_system')->realpath('public://simpletest') + ); + } + + /** + * Constructs a test runner. + * + * @param string $app_root + * Path to the application root. + * @param string $working_directory + * Path to the working directory. JUnit log files will be stored in this + * directory. + */ + public function __construct($app_root, $working_directory) { + $this->appRoot = $app_root; + $this->workingDirectory = $working_directory; + } + + /** + * Returns the path to use for PHPUnit's --log-junit option. + * + * @param int $test_id + * The current test ID. + * + * @return string + * Path to the PHPUnit XML file to use for the current $test_id. + * + * @internal + */ + public function xmlLogFilePath($test_id) { + return $this->workingDirectory . '/phpunit-' . $test_id . '.xml'; + } + + /** + * Returns the command to run PHPUnit. + * + * @return string + * The command that can be run through exec(). + * + * @internal + */ + public function phpUnitCommand() { + // Load the actual autoloader being used and determine its filename using + // reflection. We can determine the vendor directory based on that filename. + $autoloader = require $this->appRoot . '/autoload.php'; + $reflector = new \ReflectionClass($autoloader); + $vendor_dir = dirname(dirname($reflector->getFileName())); + + // The file in Composer's bin dir is a *nix link, which does not work when + // extracted from a tarball and generally not on Windows. + $command = $vendor_dir . '/phpunit/phpunit/phpunit'; + if (substr(PHP_OS, 0, 3) == 'WIN') { + // On Windows it is necessary to run the script using the PHP executable. + $php_executable_finder = new PhpExecutableFinder(); + $php = $php_executable_finder->find(); + $command = $php . ' -f ' . escapeshellarg($command) . ' --'; + } + return $command; + } + + /** + * Executes the PHPUnit command. + * + * @param string[] $unescaped_test_classnames + * An array of test class names, including full namespaces, to be passed as + * a regular expression to PHPUnit's --filter option. + * @param string $phpunit_file + * A filepath to use for PHPUnit's --log-junit option. + * @param int $status + * (optional) The exit status code of the PHPUnit process will be assigned + * to this variable. + * @param string[] $output + * (optional) The output by running the phpunit command. If provided, this + * array will contain the lines output by the command. + * + * @return string + * The results as returned by exec(). + * + * @internal + */ + public function runCommand(array $unescaped_test_classnames, $phpunit_file, &$status = NULL, &$output = NULL) { + global $base_url; + // Setup an environment variable containing the database connection so that + // functional tests can connect to the database. + putenv('SIMPLETEST_DB=' . Database::getConnectionInfoAsUrl()); + + // Setup an environment variable containing the base URL, if it is available. + // This allows functional tests to browse the site under test. When running + // tests via CLI, core/phpunit.xml.dist or core/scripts/run-tests.sh can set + // this variable. + if ($base_url) { + putenv('SIMPLETEST_BASE_URL=' . $base_url); + putenv('BROWSERTEST_OUTPUT_DIRECTORY=' . $this->workingDirectory); + } + $phpunit_bin = $this->phpUnitCommand(); + + $command = [ + $phpunit_bin, + '--log-junit', + escapeshellarg($phpunit_file), + '--printer', + escapeshellarg(SimpletestUiPrinter::class), + ]; + + // Optimized for running a single test. + if (count($unescaped_test_classnames) == 1) { + $class = new \ReflectionClass($unescaped_test_classnames[0]); + $command[] = escapeshellarg($class->getFileName()); + } + else { + // Double escape namespaces so they'll work in a regexp. + $escaped_test_classnames = array_map(function ($class) { + return addslashes($class); + }, $unescaped_test_classnames); + + $filter_string = implode("|", $escaped_test_classnames); + $command = array_merge($command, [ + '--filter', + escapeshellarg($filter_string), + ]); + } + + // Need to change directories before running the command so that we can use + // relative paths in the configuration file's exclusions. + $old_cwd = getcwd(); + chdir($this->appRoot . "/core"); + + // exec in a subshell so that the environment is isolated when running tests + // via the simpletest UI. + $ret = exec(implode(" ", $command), $output, $status); + + chdir($old_cwd); + putenv('SIMPLETEST_DB='); + if ($base_url) { + putenv('SIMPLETEST_BASE_URL='); + putenv('BROWSERTEST_OUTPUT_DIRECTORY='); + } + return $ret; + } + + /** + * Executes PHPUnit tests and returns the results of the run. + * + * @param int $test_id + * The current test ID. + * @param string[] $unescaped_test_classnames + * An array of test class names, including full namespaces, to be passed as + * a regular expression to PHPUnit's --filter option. + * @param int $status + * (optional) The exit status code of the PHPUnit process will be assigned + * to this variable. + * + * @return array + * The parsed results of PHPUnit's JUnit XML output, in the format of + * {simpletest}'s schema. + * + * @internal + */ + public function runTests($test_id, array $unescaped_test_classnames, &$status = NULL) { + $phpunit_file = $this->xmlLogFilePath($test_id); + // Store ouptut from our test run. + $output = []; + $this->runCommand($unescaped_test_classnames, $phpunit_file, $status, $output); + + if ($status == TestStatus::PASS) { + return JUnitConverter::xmlToRows($test_id, $phpunit_file); + } + return [ + [ + 'test_id' => $test_id, + 'test_class' => implode(",", $unescaped_test_classnames), + 'status' => TestStatus::label($status), + 'message' => 'PHPunit Test failed to complete; Error: ' . implode("\n", $output), + 'message_group' => 'Other', + 'function' => implode(",", $unescaped_test_classnames), + 'line' => '0', + 'file' => $phpunit_file, + ], + ]; + } + + /** + * Tallies test results per test class. + * + * @param string[][] $results + * Array of results in the {simpletest} schema. Can be the return value of + * PhpUnitTestRunner::runTests(). + * + * @return int[][] + * Array of status tallies, keyed by test class name and status type. + * + * @internal + */ + public function summarizeResults(array $results) { + $summaries = []; + foreach ($results as $result) { + if (!isset($summaries[$result['test_class']])) { + $summaries[$result['test_class']] = [ + '#pass' => 0, + '#fail' => 0, + '#exception' => 0, + '#debug' => 0, + ]; + } + + switch ($result['status']) { + case 'pass': + $summaries[$result['test_class']]['#pass']++; + break; + + case 'fail': + $summaries[$result['test_class']]['#fail']++; + break; + + case 'exception': + $summaries[$result['test_class']]['#exception']++; + break; + + case 'debug': + $summaries[$result['test_class']]['#debug']++; + break; + } + } + return $summaries; + } + +} diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module index da56752cb5b7cfa19633448ad4f74a32b189ba69..e5853bb18a63e2c48a955c6090bb307bfe803d46 100644 --- a/core/modules/simpletest/simpletest.module +++ b/core/modules/simpletest/simpletest.module @@ -5,18 +5,17 @@ * Provides testing functionality. */ -use Drupal\Core\Url; use Drupal\Core\Asset\AttachedAssetsInterface; use Drupal\Core\Database\Database; use Drupal\Core\File\Exception\FileException; use Drupal\Core\Render\Element; use Drupal\Core\Routing\RouteMatchInterface; +use Drupal\Core\Test\JUnitConverter; +use Drupal\Core\Test\PhpUnitTestRunner; use Drupal\Core\Test\TestDatabase; +use Drupal\Core\Url; use Drupal\simpletest\TestDiscovery; -use Drupal\Tests\Listeners\SimpletestUiPrinter; use PHPUnit\Framework\TestCase; -use Symfony\Component\Process\PhpExecutableFinder; -use Drupal\Core\Test\TestStatus; /** * Implements hook_help(). @@ -119,8 +118,9 @@ function _simpletest_format_summary_line($summary) { /** * Runs tests. * - * @param $test_list - * List of tests to run. + * @param array[] $test_list + * List of tests to run. The top level is keyed by type of test, either + * 'simpletest' or 'phpunit'. Under that is an array of class names to run. * * @return string * The test ID. @@ -187,28 +187,16 @@ function simpletest_run_tests($test_list) { * @return array * The parsed results of PHPUnit's JUnit XML output, in the format of * {simpletest}'s schema. + * + * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use + * \Drupal\Core\Test\PhpUnitTestRunner::runTests() instead. + * + * @see https://www.drupal.org/node/2948547 */ function simpletest_run_phpunit_tests($test_id, array $unescaped_test_classnames, &$status = NULL) { - $phpunit_file = simpletest_phpunit_xml_filepath($test_id); - simpletest_phpunit_run_command($unescaped_test_classnames, $phpunit_file, $status, $output); - - $rows = []; - if ($status == TestStatus::PASS) { - $rows = simpletest_phpunit_xml_to_rows($test_id, $phpunit_file); - } - else { - $rows[] = [ - 'test_id' => $test_id, - 'test_class' => implode(",", $unescaped_test_classnames), - 'status' => TestStatus::label($status), - 'message' => 'PHPunit Test failed to complete; Error: ' . implode("\n", $output), - 'message_group' => 'Other', - 'function' => implode(",", $unescaped_test_classnames), - 'line' => '0', - 'file' => $phpunit_file, - ]; - } - return $rows; + $runner = PhpUnitTestRunner::create(\Drupal::getContainer()); + @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\PhpUnitTestRunner::runTests() instead. See https://www.drupal.org/node/2948547', E_USER_DEPRECATED); + return $runner->runTests($test_id, $unescaped_test_classnames, $status); } /** @@ -239,38 +227,16 @@ function simpletest_process_phpunit_results($phpunit_results) { * * @return array * The test result summary. A row per test class. + * + * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use + * \Drupal\Core\Test\PhpUnitTestRunner::summarizeResults() instead. + * + * @see https://www.drupal.org/node/2948547 */ function simpletest_summarize_phpunit_result($results) { - $summaries = []; - foreach ($results as $result) { - if (!isset($summaries[$result['test_class']])) { - $summaries[$result['test_class']] = [ - '#pass' => 0, - '#fail' => 0, - '#exception' => 0, - '#debug' => 0, - ]; - } - - switch ($result['status']) { - case 'pass': - $summaries[$result['test_class']]['#pass']++; - break; - - case 'fail': - $summaries[$result['test_class']]['#fail']++; - break; - - case 'exception': - $summaries[$result['test_class']]['#exception']++; - break; - - case 'debug': - $summaries[$result['test_class']]['#debug']++; - break; - } - } - return $summaries; + $runner = PhpUnitTestRunner::create(\Drupal::getContainer()); + @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\PhpUnitTestRunner::summarizeResults() instead. See https://www.drupal.org/node/2948547', E_USER_DEPRECATED); + return $runner->summarizeResults($results); } /** @@ -281,9 +247,16 @@ function simpletest_summarize_phpunit_result($results) { * * @return string * Path to the PHPUnit XML file to use for the current $test_id. + * + * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use + * \Drupal\Core\Test\PhpUnitTestRunner::xmlLogFilepath() instead. + * + * @see https://www.drupal.org/node/2948547 */ function simpletest_phpunit_xml_filepath($test_id) { - return \Drupal::service('file_system')->realpath('public://simpletest') . '/phpunit-' . $test_id . '.xml'; + $runner = PhpUnitTestRunner::create(\Drupal::getContainer()); + @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\PhpUnitTestRunner::xmlLogFilepath() instead. See https://www.drupal.org/node/2948547', E_USER_DEPRECATED); + return $runner->xmlLogFilePath($test_id); } /** @@ -319,65 +292,16 @@ function simpletest_phpunit_configuration_filepath() { * * @return string * The results as returned by exec(). + * + * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use + * \Drupal\Core\Test\PhpUnitTestRunner::runCommand() instead. + * + * @see https://www.drupal.org/node/2948547 */ function simpletest_phpunit_run_command(array $unescaped_test_classnames, $phpunit_file, &$status = NULL, &$output = NULL) { - global $base_url; - // Setup an environment variable containing the database connection so that - // functional tests can connect to the database. - putenv('SIMPLETEST_DB=' . Database::getConnectionInfoAsUrl()); - - // Setup an environment variable containing the base URL, if it is available. - // This allows functional tests to browse the site under test. When running - // tests via CLI, core/phpunit.xml.dist or core/scripts/run-tests.sh can set - // this variable. - if ($base_url) { - putenv('SIMPLETEST_BASE_URL=' . $base_url); - putenv('BROWSERTEST_OUTPUT_DIRECTORY=' . \Drupal::service('file_system')->realpath('public://simpletest')); - } - $phpunit_bin = simpletest_phpunit_command(); - - $command = [ - $phpunit_bin, - '--log-junit', - escapeshellarg($phpunit_file), - '--printer', - escapeshellarg(SimpletestUiPrinter::class), - ]; - - // Optimized for running a single test. - if (count($unescaped_test_classnames) == 1) { - $class = new \ReflectionClass($unescaped_test_classnames[0]); - $command[] = escapeshellarg($class->getFileName()); - } - else { - // Double escape namespaces so they'll work in a regexp. - $escaped_test_classnames = array_map(function ($class) { - return addslashes($class); - }, $unescaped_test_classnames); - - $filter_string = implode("|", $escaped_test_classnames); - $command = array_merge($command, [ - '--filter', - escapeshellarg($filter_string), - ]); - } - - // Need to change directories before running the command so that we can use - // relative paths in the configuration file's exclusions. - $old_cwd = getcwd(); - chdir(\Drupal::root() . "/core"); - - // exec in a subshell so that the environment is isolated when running tests - // via the simpletest UI. - $ret = exec(implode(" ", $command), $output, $status); - - chdir($old_cwd); - putenv('SIMPLETEST_DB='); - if ($base_url) { - putenv('SIMPLETEST_BASE_URL='); - putenv('BROWSERTEST_OUTPUT_DIRECTORY='); - } - return $ret; + $runner = PhpUnitTestRunner::create(\Drupal::getContainer()); + @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\PhpUnitTestRunner::runCommand() instead. See https://www.drupal.org/node/2948547', E_USER_DEPRECATED); + return $runner->runCommand($unescaped_test_classnames, $phpunit_file, $status, $output); } /** @@ -385,24 +309,16 @@ function simpletest_phpunit_run_command(array $unescaped_test_classnames, $phpun * * @return string * The command that can be run through exec(). + * + * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use + * \Drupal\Core\Test\PhpUnitTestRunner::phpUnitCommand() instead. + * + * @see https://www.drupal.org/node/2948547 */ function simpletest_phpunit_command() { - // Load the actual autoloader being used and determine its filename using - // reflection. We can determine the vendor directory based on that filename. - $autoloader = require \Drupal::root() . '/autoload.php'; - $reflector = new ReflectionClass($autoloader); - $vendor_dir = dirname(dirname($reflector->getFileName())); - - // The file in Composer's bin dir is a *nix link, which does not work when - // extracted from a tarball and generally not on Windows. - $command = escapeshellarg($vendor_dir . '/phpunit/phpunit/phpunit'); - if (substr(PHP_OS, 0, 3) == 'WIN') { - // On Windows it is necessary to run the script using the PHP executable. - $php_executable_finder = new PhpExecutableFinder(); - $php = $php_executable_finder->find(); - $command = $php . ' -f ' . $command . ' --'; - } - return $command; + $runner = PhpUnitTestRunner::create(\Drupal::getContainer()); + @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\PhpUnitTestRunner::phpUnitCommand() instead. See https://www.drupal.org/node/2948547', E_USER_DEPRECATED); + return $runner->phpUnitCommand(); } /** @@ -427,7 +343,8 @@ function _simpletest_batch_operation($test_list_init, $test_id, &$context) { // Perform the next test. $test_class = array_shift($test_list); if (is_subclass_of($test_class, TestCase::class)) { - $phpunit_results = simpletest_run_phpunit_tests($test_id, [$test_class]); + $runner = PhpUnitTestRunner::create(\Drupal::getContainer()); + $phpunit_results = $runner->runTests($test_id, [$test_class]); simpletest_process_phpunit_results($phpunit_results); $test_results[$test_class] = simpletest_summarize_phpunit_result($phpunit_results)[$test_class]; } @@ -805,18 +722,15 @@ function simpletest_mail_alter(&$message) { * The results as array of rows in a format that can be inserted into * {simpletest}. If the phpunit_xml_file does not have any contents then the * function will return NULL. + * + * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use + * \Drupal\Core\Test\JUnitConverter::xmlToRows() instead. + * + * @see https://www.drupal.org/node/2948547 */ function simpletest_phpunit_xml_to_rows($test_id, $phpunit_xml_file) { - $contents = @file_get_contents($phpunit_xml_file); - if (!$contents) { - return; - } - $records = []; - $testcases = simpletest_phpunit_find_testcases(new SimpleXMLElement($contents)); - foreach ($testcases as $testcase) { - $records[] = simpletest_phpunit_testcase_to_row($test_id, $testcase); - } - return $records; + @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\JUnitConverter::xmlToRows() instead. See https://www.drupal.org/node/2948547', E_USER_DEPRECATED); + return JUnitConverter::xmlToRows($test_id, $phpunit_xml_file) ?: NULL; } /** @@ -829,34 +743,15 @@ function simpletest_phpunit_xml_to_rows($test_id, $phpunit_xml_file) { * * @return array * A list of all test cases. + * + * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use + * \Drupal\Core\Test\JUnitConverter::findTestCases() instead. + * + * @see https://www.drupal.org/node/2948547 */ function simpletest_phpunit_find_testcases(\SimpleXMLElement $element, \SimpleXMLElement $parent = NULL) { - $testcases = []; - - if (!isset($parent)) { - $parent = $element; - } - - if ($element->getName() === 'testcase' && (int) $parent->attributes()->tests > 0) { - // Add the class attribute if the testcase does not have one. This is the - // case for tests using a data provider. The name of the parent testsuite - // will be in the format class::method. - if (!$element->attributes()->class) { - $name = explode('::', $parent->attributes()->name, 2); - $element->addAttribute('class', $name[0]); - } - $testcases[] = $element; - } - else { - foreach ($element as $child) { - $file = (string) $parent->attributes()->file; - if ($file && !$child->attributes()->file) { - $child->addAttribute('file', $file); - } - $testcases = array_merge($testcases, simpletest_phpunit_find_testcases($child, $element)); - } - } - return $testcases; + @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\JUnitConverter::findTestCases() instead. See https://www.drupal.org/node/2948547', E_USER_DEPRECATED); + return JUnitConverter::findTestCases($element, $parent); } /** @@ -864,40 +759,18 @@ function simpletest_phpunit_find_testcases(\SimpleXMLElement $element, \SimpleXM * * @param int $test_id * The current test ID. - * @param \SimpleXMLElement $testcase + * @param \SimpleXMLElement $test_case * The PHPUnit test case represented as XML element. * * @return array * An array containing the {simpletest} result row. + * + * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use + * \Drupal\Core\Test\JUnitConverter::convertTestCaseToSimpletestRow() instead. + * + * @see https://www.drupal.org/node/2948547 */ -function simpletest_phpunit_testcase_to_row($test_id, \SimpleXMLElement $testcase) { - $message = ''; - $pass = TRUE; - if ($testcase->failure) { - $lines = explode("\n", $testcase->failure); - $message = $lines[2]; - $pass = FALSE; - } - if ($testcase->error) { - $message = $testcase->error; - $pass = FALSE; - } - - $attributes = $testcase->attributes(); - - $function = $attributes->class . '->' . $attributes->name . '()'; - $record = [ - 'test_id' => $test_id, - 'test_class' => (string) $attributes->class, - 'status' => $pass ? 'pass' : 'fail', - 'message' => $message, - // @todo: Check on the proper values for this. - 'message_group' => 'Other', - 'function' => $function, - 'line' => $attributes->line ?: 0, - // There are situations when the file will not be present because a PHPUnit - // @requires has caused a test to be skipped. - 'file' => $attributes->file ?: $function, - ]; - return $record; +function simpletest_phpunit_testcase_to_row($test_id, \SimpleXMLElement $test_case) { + @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\JUnitConverter::convertTestCaseToSimpletestRow() instead. See https://www.drupal.org/node/2948547', E_USER_DEPRECATED); + return JUnitConverter::convertTestCaseToSimpletestRow($test_id, $test_case); } diff --git a/core/modules/simpletest/src/Tests/UiPhpUnitOutputTest.php b/core/modules/simpletest/src/Tests/UiPhpUnitOutputTest.php index c2ebfc92c4f26eab2e5d31381272ffd38b1a5bd7..cdbae0603a59e4f0c753f6e407a72aa6389624ef 100644 --- a/core/modules/simpletest/src/Tests/UiPhpUnitOutputTest.php +++ b/core/modules/simpletest/src/Tests/UiPhpUnitOutputTest.php @@ -9,6 +9,7 @@ * Test PHPUnit output for the Simpletest UI. * * @group simpletest + * @group legacy * * @see \Drupal\Tests\Listeners\SimpletestUiPrinter */ diff --git a/core/modules/simpletest/tests/src/Unit/PhpUnitErrorTest.php b/core/modules/simpletest/tests/src/Kernel/PhpUnitErrorTest.php similarity index 60% rename from core/modules/simpletest/tests/src/Unit/PhpUnitErrorTest.php rename to core/modules/simpletest/tests/src/Kernel/PhpUnitErrorTest.php index 25211fc40459d0b37b1c77d86cec20f73b1e8d53..4f01936eeb05b0a048dc8365af4a19da2c9d8243 100644 --- a/core/modules/simpletest/tests/src/Unit/PhpUnitErrorTest.php +++ b/core/modules/simpletest/tests/src/Kernel/PhpUnitErrorTest.php @@ -1,24 +1,30 @@ <?php -namespace Drupal\Tests\simpletest\Unit; +namespace Drupal\Tests\simpletest\Kernel; -use Drupal\Tests\UnitTestCase; +use Drupal\KernelTests\KernelTestBase; /** * Tests PHPUnit errors are getting converted to Simpletest errors. * * @group simpletest + * @group legacy */ -class PhpUnitErrorTest extends UnitTestCase { +class PhpUnitErrorTest extends KernelTestBase { + + /** + * Enable the simpletest module. + * + * @var string[] + */ + protected static $modules = ['simpletest']; /** * Test errors reported. * - * @covers ::simpletest_phpunit_xml_to_rows + * @expectedDeprecation simpletest_phpunit_xml_to_rows is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\JUnitConverter::xmlToRows() instead. See https://www.drupal.org/node/2948547 */ public function testPhpUnitXmlParsing() { - require_once __DIR__ . '/../../../simpletest.module'; - $phpunit_error_xml = __DIR__ . '/../../fixtures/phpunit_error.xml'; $res = simpletest_phpunit_xml_to_rows(1, $phpunit_error_xml); @@ -34,7 +40,7 @@ public function testPhpUnitXmlParsing() { // Make sure simpletest_phpunit_xml_to_rows() does not balk if the test // didn't run. - simpletest_phpunit_xml_to_rows(1, 'foobar'); + $this->assertNull(simpletest_phpunit_xml_to_rows(1, 'does_not_exist')); } } diff --git a/core/modules/simpletest/tests/src/Kernel/SimpletestDeprecationTest.php b/core/modules/simpletest/tests/src/Kernel/SimpletestDeprecationTest.php index 36c3d43dc7c2af336579e304813d7d64b25e1a42..3c0c89c933eba83981520ceb8c6e51ad485ee83a 100644 --- a/core/modules/simpletest/tests/src/Kernel/SimpletestDeprecationTest.php +++ b/core/modules/simpletest/tests/src/Kernel/SimpletestDeprecationTest.php @@ -34,4 +34,52 @@ public function testDeprecatedServices() { $this->assertInstanceOf(TestDiscovery::class, $this->container->get('test_discovery')); } + /** + * @expectedDeprecation simpletest_phpunit_xml_filepath is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\PhpUnitTestRunner::xmlLogFilepath() instead. See https://www.drupal.org/node/2948547 + * @expectedDeprecation simpletest_phpunit_command is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\PhpUnitTestRunner::phpUnitCommand() instead. See https://www.drupal.org/node/2948547 + * @expectedDeprecation simpletest_phpunit_find_testcases is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\JUnitConverter::findTestCases() instead. See https://www.drupal.org/node/2948547 + * @expectedDeprecation simpletest_phpunit_testcase_to_row is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\JUnitConverter::convertTestCaseToSimpletestRow() instead. See https://www.drupal.org/node/2948547 + * @expectedDeprecation simpletest_summarize_phpunit_result is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\PhpUnitTestRunner::summarizeResults() instead. See https://www.drupal.org/node/2948547 + */ + public function testDeprecatedPhpUnitFunctions() { + // We can't test the deprecation errors for the following functions because + // they cannot be mocked, and calling them would change the test results: + // - simpletest_run_phpunit_tests(). + // - simpletest_phpunit_run_command(). + // - simpletest_phpunit_xml_to_rows(). + $this->assertStringEndsWith('/phpunit-23.xml', simpletest_phpunit_xml_filepath(23)); + + $this->assertInternalType('string', simpletest_phpunit_command()); + + $this->assertEquals([], simpletest_phpunit_find_testcases(new \SimpleXMLElement('<not_testcase></not_testcase>'))); + + $this->assertEquals([ + 'test_id' => 23, + 'test_class' => '', + 'status' => 'pass', + 'message' => '', + 'message_group' => 'Other', + 'function' => '->()', + 'line' => 0, + 'file' => NULL, + ], simpletest_phpunit_testcase_to_row(23, new \SimpleXMLElement('<not_testcase></not_testcase>'))); + + $this->assertEquals( + [ + static::class => [ + '#pass' => 0, + '#fail' => 0, + '#exception' => 0, + '#debug' => 1, + ], + ], + simpletest_summarize_phpunit_result([ + [ + 'test_class' => static::class, + 'status' => 'debug', + ], + ]) + ); + } + } diff --git a/core/modules/simpletest/tests/src/Unit/SimpletestPhpunitRunCommandTest.php b/core/modules/simpletest/tests/src/Unit/SimpletestPhpunitRunCommandTest.php index 30617b9560e83edd92c25df1cac1d397a8677db4..4f90438d1da8d22db133566dec967aee5fc75cde 100644 --- a/core/modules/simpletest/tests/src/Unit/SimpletestPhpunitRunCommandTest.php +++ b/core/modules/simpletest/tests/src/Unit/SimpletestPhpunitRunCommandTest.php @@ -5,15 +5,25 @@ use Drupal\Core\Database\Database; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\File\FileSystemInterface; +use Drupal\Core\Test\PhpUnitTestRunner; use PHPUnit\Framework\TestCase; /** * Tests simpletest_run_phpunit_tests() handles PHPunit fatals correctly. * - * We don't extend Drupal\Tests\UnitTestCase here because its $root property is + * We don't extend \Drupal\Tests\UnitTestCase here because its $root property is * not static and we need it to be static here. * + * The file simpletest_phpunit_run_command_test.php contains the test class + * \Drupal\Tests\simpletest\Unit\SimpletestPhpunitRunCommandTestWillDie which + * can be made to exit with result code 2. It lives in a file which won't be + * autoloaded, so that it won't fail test runs. + * + * Here, we run SimpletestPhpunitRunCommandTestWillDie, make it die, and see + * what happens. + * * @group simpletest + * @group legacy * * @runTestsInSeparateProcesses * @preserveGlobalState disabled @@ -27,6 +37,13 @@ class SimpletestPhpunitRunCommandTest extends TestCase { */ protected static $root; + /** + * A fixture container. + * + * @var \Symfony\Component\DependencyInjection\ContainerInterface + */ + protected $fixtureContainer; + /** * {@inheritdoc} */ @@ -56,6 +73,7 @@ protected function setUp() { $file_system->realpath('public://simpletest')->willReturn(sys_get_temp_dir()); $container->set('file_system', $file_system->reveal()); \Drupal::setContainer($container); + $this->fixtureContainer = $container; } /** @@ -82,9 +100,14 @@ public function provideStatusCodes() { /** * Test the round trip for PHPUnit execution status codes. * + * Also tests backwards-compatibility of PhpUnitTestRunner::runTests(). + * * @covers ::simpletest_run_phpunit_tests * * @dataProvider provideStatusCodes + * + * @expectedDeprecation simpletest_run_phpunit_tests is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\PhpUnitTestRunner::runTests() instead. See https://www.drupal.org/node/2948547 + * @expectedDeprecation simpletest_phpunit_xml_filepath is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\PhpUnitTestRunner::xmlLogFilepath() instead. See https://www.drupal.org/node/2948547 */ public function testSimpletestPhpUnitRunCommand($status, $label) { // Add a default database connection in order for @@ -101,8 +124,17 @@ public function testSimpletestPhpUnitRunCommand($status, $label) { ); $test_id = basename(tempnam(sys_get_temp_dir(), 'xxx')); putenv('SimpletestPhpunitRunCommandTestWillDie=' . $status); - $ret = simpletest_run_phpunit_tests($test_id, [SimpletestPhpunitRunCommandTestWillDie::class]); + + // Test against simpletest_run_phpunit_tests(). + $bc_ret = simpletest_run_phpunit_tests($test_id, [SimpletestPhpunitRunCommandTestWillDie::class]); + $this->assertSame($bc_ret[0]['status'], $label); + + // Test against PhpUnitTestRunner::runTests(). + $runner = PhpUnitTestRunner::create($this->fixtureContainer); + $ret = $runner->runTests($test_id, [SimpletestPhpunitRunCommandTestWillDie::class]); $this->assertSame($ret[0]['status'], $label); + + // Unset our environmental variable. putenv('SimpletestPhpunitRunCommandTestWillDie'); unlink(simpletest_phpunit_xml_filepath($test_id)); } diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh index 3039fab006e3136bb794ad47903a2e26f822ad34..a3e94a42582b1b414dc66ed4603437ec02bab036 100755 --- a/core/scripts/run-tests.sh +++ b/core/scripts/run-tests.sh @@ -14,10 +14,10 @@ use Drupal\Core\File\Exception\FileException; use Drupal\Core\File\FileSystemInterface; use Drupal\Core\StreamWrapper\PublicStream; +use Drupal\Core\Test\PhpUnitTestRunner; use Drupal\Core\Test\TestDatabase; use Drupal\Core\Test\TestRunnerKernel; use Drupal\simpletest\Form\SimpletestResultsForm; -use Drupal\simpletest\TestBase; use Drupal\Core\Test\TestDiscovery; use PHPUnit\Framework\TestCase; use PHPUnit\Runner\Version; @@ -789,12 +789,11 @@ function simpletest_script_run_phpunit($test_id, $class) { set_time_limit($reflection->getStaticPropertyValue('runLimit')); } - $results = simpletest_run_phpunit_tests($test_id, [$class], $status); + $runner = PhpUnitTestRunner::create(\Drupal::getContainer()); + $results = $runner->runTests($test_id, [$class], $status); simpletest_process_phpunit_results($results); - // Map phpunit results to a data structure we can pass to - // _simpletest_format_summary_line. - $summaries = simpletest_summarize_phpunit_result($results); + $summaries = $runner->summarizeResults($results); foreach ($summaries as $class => $summary) { simpletest_script_reporter_display_summary($class, $summary); } diff --git a/core/tests/Drupal/Tests/Core/Test/JUnitConverterTest.php b/core/tests/Drupal/Tests/Core/Test/JUnitConverterTest.php new file mode 100644 index 0000000000000000000000000000000000000000..500f4a3b579692d9650a736f56991d02a350c8b3 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Test/JUnitConverterTest.php @@ -0,0 +1,102 @@ +<?php + +namespace Drupal\Tests\Core\Test; + +use Drupal\Core\Test\JUnitConverter; +use Drupal\Tests\UnitTestCase; +use org\bovigo\vfs\vfsStream; + +/** + * Tests Drupal\Core\Test\JUnitConverter. + * + * This test class has significant overlap with + * Drupal\Tests\simpletest\Kernel\PhpUnitErrorTest. + * + * @coversDefaultClass \Drupal\Core\Test\JUnitConverter + * + * @group Test + * @group simpletest + * + * @see \Drupal\Tests\simpletest\Kernel\PhpUnitErrorTest + */ +class JUnitConverterTest extends UnitTestCase { + + /** + * Test errors reported. + * @covers ::xmlToRows + */ + public function testXmlToRowsWithErrors() { + $phpunit_error_xml = __DIR__ . '/fixtures/phpunit_error.xml'; + + $res = JUnitConverter::xmlToRows(1, $phpunit_error_xml); + $this->assertEquals(count($res), 4, 'All testcases got extracted'); + $this->assertNotEquals($res[0]['status'], 'pass'); + $this->assertEquals($res[0]['status'], 'fail'); + + // Test nested testsuites, which appear when you use @dataProvider. + for ($i = 0; $i < 3; $i++) { + $this->assertNotEquals($res[$i + 1]['status'], 'pass'); + $this->assertEquals($res[$i + 1]['status'], 'fail'); + } + + // Make sure xmlToRows() does not balk if there are no test results. + $this->assertSame([], JUnitConverter::xmlToRows(1, 'does_not_exist')); + } + + /** + * @covers ::xmlToRows + */ + public function testXmlToRowsEmptyFile() { + // File system with an empty XML file. + vfsStream::setup('junit_test', NULL, ['empty.xml' => '']); + $this->assertArrayEquals([], JUnitConverter::xmlToRows(23, vfsStream::url('junit_test/empty.xml'))); + } + + /** + * @covers ::xmlElementToRows + */ + public function testXmlElementToRows() { + $junit = <<<EOD +<?xml version="1.0" encoding="UTF-8"?> +<testsuites> + <testsuite name="Drupal\Tests\simpletest\Unit\TestDiscoveryTest" file="/Users/paul/projects/drupal/core/modules/simpletest/tests/src/Unit/TestDiscoveryTest.php" tests="3" assertions="5" errors="0" failures="0" skipped="0" time="0.215539"> + <testcase name="testGetTestClasses" class="Drupal\Tests\simpletest\Unit\TestDiscoveryTest" classname="Drupal.Tests.simpletest.Unit.TestDiscoveryTest" file="/Users/paul/projects/drupal/core/modules/simpletest/tests/src/Unit/TestDiscoveryTest.php" line="108" assertions="2" time="0.100787"/> + </testsuite> +</testsuites> +EOD; + $simpletest = [ + [ + 'test_id' => 23, + 'test_class' => 'Drupal\Tests\simpletest\Unit\TestDiscoveryTest', + 'status' => 'pass', + 'message' => '', + 'message_group' => 'Other', + 'function' => 'Drupal\Tests\simpletest\Unit\TestDiscoveryTest->testGetTestClasses()', + 'line' => 108, + 'file' => '/Users/paul/projects/drupal/core/modules/simpletest/tests/src/Unit/TestDiscoveryTest.php', + ], + ]; + $this->assertArrayEquals($simpletest, JUnitConverter::xmlElementToRows(23, new \SimpleXMLElement($junit))); + } + + /** + * @covers ::convertTestCaseToSimpletestRow + */ + public function testConvertTestCaseToSimpletestRow() { + $junit = <<<EOD + <testcase name="testGetTestClasses" class="Drupal\Tests\simpletest\Unit\TestDiscoveryTest" classname="Drupal.Tests.simpletest.Unit.TestDiscoveryTest" file="/Users/paul/projects/drupal/core/modules/simpletest/tests/src/Unit/TestDiscoveryTest.php" line="108" assertions="2" time="0.100787"/> +EOD; + $simpletest = [ + 'test_id' => 23, + 'test_class' => 'Drupal\Tests\simpletest\Unit\TestDiscoveryTest', + 'status' => 'pass', + 'message' => '', + 'message_group' => 'Other', + 'function' => 'Drupal\Tests\simpletest\Unit\TestDiscoveryTest->testGetTestClasses()', + 'line' => 108, + 'file' => '/Users/paul/projects/drupal/core/modules/simpletest/tests/src/Unit/TestDiscoveryTest.php', + ]; + $this->assertArrayEquals($simpletest, JUnitConverter::convertTestCaseToSimpletestRow(23, new \SimpleXMLElement($junit))); + } + +} diff --git a/core/tests/Drupal/Tests/Core/Test/PhpUnitTestRunnerTest.php b/core/tests/Drupal/Tests/Core/Test/PhpUnitTestRunnerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..bd62c430c6d68ec7641e48a3278a48f31ae20e1c --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Test/PhpUnitTestRunnerTest.php @@ -0,0 +1,141 @@ +<?php + +namespace Drupal\Tests\Core\Test; + +use Drupal\Core\Test\PhpUnitTestRunner; +use Drupal\Core\Test\TestStatus; +use Drupal\Tests\UnitTestCase; + +/** + * @coversDefaultClass \Drupal\Core\Test\PhpUnitTestRunner + * @group Test + * + * @see Drupal\Tests\simpletest\Unit\SimpletestPhpunitRunCommandTest + */ +class PhpUnitTestRunnerTest extends UnitTestCase { + + /** + * Test an error in the test running phase. + * + * @covers ::runTests + */ + public function testRunTestsError() { + $test_id = 23; + $log_path = 'test_log_path'; + + // Create a mock runner. + $runner = $this->getMockBuilder(PhpUnitTestRunner::class) + ->disableOriginalConstructor() + ->setMethods(['xmlLogFilepath', 'runCommand']) + ->getMock(); + + // Set some expectations for xmlLogFilepath(). + $runner->expects($this->once()) + ->method('xmlLogFilepath') + ->willReturn($log_path); + + // We mark a failure by having runCommand() deliver a serious status code. + $runner->expects($this->once()) + ->method('runCommand') + ->willReturnCallback( + function ($unescaped_test_classnames, $phpunit_file, &$status) { + $status = TestStatus::EXCEPTION; + } + ); + + // The runTests() method expects $status by reference, so we initialize it + // to some value we don't expect back. + $status = -1; + $results = $runner->runTests($test_id, ['SomeTest'], $status); + + // Make sure our status code made the round trip. + $this->assertEquals(TestStatus::EXCEPTION, $status); + + // A serious error in runCommand() should give us a fixed set of results. + $row = reset($results); + $fail_row = [ + 'test_id' => $test_id, + 'test_class' => 'SomeTest', + 'status' => TestStatus::label(TestStatus::EXCEPTION), + 'message' => 'PHPunit Test failed to complete; Error: ', + 'message_group' => 'Other', + 'function' => 'SomeTest', + 'line' => '0', + 'file' => $log_path, + ]; + $this->assertEquals($fail_row, $row); + } + + /** + * @covers ::phpUnitCommand + */ + public function testPhpUnitCommand() { + $runner = new PhpUnitTestRunner($this->root, sys_get_temp_dir()); + $this->assertRegExp('/phpunit/', $runner->phpUnitCommand()); + } + + /** + * @covers ::xmlLogFilePath + */ + public function testXmlLogFilePath() { + $runner = new PhpUnitTestRunner($this->root, sys_get_temp_dir()); + $this->assertStringEndsWith('phpunit-23.xml', $runner->xmlLogFilePath(23)); + } + + public function providerTestSummarizeResults() { + return [ + [ + [ + [ + 'test_class' => static::class, + 'status' => 'pass', + ], + ], + '#pass', + ], + [ + [ + [ + 'test_class' => static::class, + 'status' => 'fail', + ], + ], + '#fail', + ], + [ + [ + [ + 'test_class' => static::class, + 'status' => 'exception', + ], + ], + '#exception', + ], + [ + [ + [ + 'test_class' => static::class, + 'status' => 'debug', + ], + ], + '#debug', + ], + ]; + } + + /** + * @dataProvider providerTestSummarizeResults + * @covers ::summarizeResults + */ + public function testSummarizeResults($results, $has_status) { + $runner = new PhpUnitTestRunner($this->root, sys_get_temp_dir()); + $summary = $runner->summarizeResults($results); + + $this->assertArrayHasKey(static::class, $summary); + $this->assertEquals(1, $summary[static::class][$has_status]); + foreach (array_diff(['#pass', '#fail', '#exception', '#debug'], [$has_status]) as $should_be_zero) { + $this->assertSame(0, $summary[static::class][$should_be_zero]); + } + } + +} diff --git a/core/tests/Drupal/Tests/Core/Test/fixtures/phpunit_error.xml b/core/tests/Drupal/Tests/Core/Test/fixtures/phpunit_error.xml new file mode 100644 index 0000000000000000000000000000000000000000..82386aea74c13821acc8f1be813ea564ffb37a54 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Test/fixtures/phpunit_error.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<testsuites> + <testsuite name="Drupal Unit Test Suite" tests="1" assertions="0" failures="0" errors="1" time="0.002680"> + <testsuite name="Drupal\Tests\Component\PhpStorage\FileStorageTest" file="/home/chx/www/system/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageTest.php" namespace="Drupal\Tests\Component\PhpStorage" fullPackage="Drupal.Tests.Component.PhpStorage" tests="0" assertions="0" failures="0" errors="0" time="0.000000"/> + <testsuite name="Drupal\Tests\Component\PhpStorage\MTimeProtectedFastFileStorageTest" file="/home/chx/www/system/core/tests/Drupal/Tests/Component/PhpStorage/MTimeProtectedFastFileStorageTest.php" namespace="Drupal\Tests\Component\PhpStorage" fullPackage="Drupal.Tests.Component.PhpStorage" tests="0" assertions="0" failures="0" errors="0" time="0.000000"/> + <testsuite name="Drupal\Tests\Core\Cache\BackendChainImplementationUnitTest" file="/home/chx/www/system/core/tests/Drupal/Tests/Core/Cache/BackendChainImplementationUnitTest.php" namespace="Drupal\Tests\Core\Cache" fullPackage="Drupal.Tests.Core.Cache" tests="0" assertions="0" failures="0" errors="0" time="0.000000"/> + <testsuite name="Drupal\Tests\Core\Cache\NullBackendTest" file="/home/chx/www/system/core/tests/Drupal/Tests/Core/Cache/NullBackendTest.php" namespace="Drupal\Tests\Core\Cache" fullPackage="Drupal.Tests.Core.Cache" tests="0" assertions="0" failures="0" errors="0" time="0.000000"/> + <testsuite name="Drupal\Tests\Core\Extension\ModuleHandlerUnitTest" file="/home/chx/www/system/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerUnitTest.php" namespace="Drupal\Tests\Core\Extension" fullPackage="Drupal.Tests.Core.Extension" tests="1" assertions="0" failures="0" errors="1" time="0.002680"> + <testcase name="testloadInclude" class="Drupal\Tests\Core\Extension\ModuleHandlerUnitTest" file="/home/chx/www/system/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerUnitTest.php" line="37" assertions="0" time="0.002680"> + <error type="PHPUnit_Framework_Error_Notice">Drupal\Tests\Core\Extension\ModuleHandlerUnitTest::testloadInclude +Undefined index: foo + +/home/chx/www/system/core/lib/Drupal/Core/Extension/ModuleHandler.php:219 +/home/chx/www/system/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerUnitTest.php:40 +</error> + </testcase> + </testsuite> + <testsuite name="Drupal\Tests\Core\NestedArrayUnitTest" file="/home/chx/www/system/core/tests/Drupal/Tests/Core/NestedArrayUnitTest.php" namespace="Drupal\Tests\Core" fullPackage="Drupal.Tests.Core" tests="0" assertions="0" failures="0" errors="0" time="0.000000"/> + <testsuite name="Drupal\breakpoint\Tests\BreakpointMediaQueryTest" file="/home/chx/www/system/core/modules/breakpoint/tests/Drupal/breakpoint/Tests/BreakpointMediaQueryTest.php" namespace="Drupal\breakpoint\Tests" fullPackage="Drupal.breakpoint.Tests" tests="0" assertions="0" failures="0" errors="0" time="0.000000"/> + <testsuite name="Drupal\Tests\Core\Route\RoleAccessCheckTest" file="/var/www/d8/core/tests/Drupal/Tests/Core/Route/RoleAccessCheckTestkTest.php" namespace="Drupal\Tests\Core\Route" fullPackage="Drupal.Tests.Core.Route" tests="3" assertions="3" failures="3" errors="0" time="0.009176"> + <testsuite name="Drupal\Tests\Core\Route\RoleAccessCheckTest::testRoleAccess" tests="3" assertions="3" failures="3" errors="0" time="0.009176"> + <testcase name="testRoleAccess with data set #0" assertions="1" time="0.004519"> + <failure type="PHPUnit_Framework_ExpectationFailedException">Drupal\Tests\Core\Route\RoleAccessCheckTest::testRoleAccess with data set #0 ('role_test_1', array(Drupal\user\Entity\User, Drupal\user\Entity\User)) + Access granted for user with the roles role_test_1 on path: role_test_1 + Failed asserting that false is true. + </failure> + </testcase> + <testcase name="testRoleAccess with data set #1" assertions="1" time="0.002354"> + <failure type="PHPUnit_Framework_ExpectationFailedException">Drupal\Tests\Core\Route\RoleAccessCheckTest::testRoleAccess with data set #1 ('role_test_2', array(Drupal\user\Entity\User, Drupal\user\Entity\User)) + Access granted for user with the roles role_test_2 on path: role_test_2 + Failed asserting that false is true. + </failure> + </testcase> + <testcase name="testRoleAccess with data set #2" assertions="1" time="0.002303"> + <failure type="PHPUnit_Framework_ExpectationFailedException">Drupal\Tests\Core\Route\RoleAccessCheckTest::testRoleAccess with data set #2 ('role_test_3', array(Drupal\user\Entity\User)) + Access granted for user with the roles role_test_1, role_test_2 on path: role_test_3 + Failed asserting that false is true. + </failure> + </testcase> + </testsuite> + </testsuite> + </testsuite> +</testsuites>