diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module index feeffd43dd62ab168417832e00f1fe026b295d63..98ffcf434bb6097caa9bcd5b93dd61544315e8f7 100644 --- a/core/modules/simpletest/simpletest.module +++ b/core/modules/simpletest/simpletest.module @@ -170,14 +170,17 @@ function simpletest_run_tests($test_list) { * @param $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. */ -function simpletest_run_phpunit_tests($test_id, array $unescaped_test_classnames) { +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); + simpletest_phpunit_run_command($unescaped_test_classnames, $phpunit_file, $status); return simpletest_phpunit_xml_to_rows($test_id, $phpunit_file); } @@ -232,11 +235,14 @@ function simpletest_phpunit_configuration_filepath() { * 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. * * @return string * The results as returned by exec(). */ -function simpletest_phpunit_run_command(array $unescaped_test_classnames, $phpunit_file) { +function simpletest_phpunit_run_command(array $unescaped_test_classnames, $phpunit_file, &$status = NULL) { $phpunit_bin = simpletest_phpunit_command(); $command = array( @@ -270,7 +276,7 @@ function simpletest_phpunit_run_command(array $unescaped_test_classnames, $phpun // exec in a subshell so that the environment is isolated when running tests // via the simpletest UI. - $ret = exec(join($command, " ")); + $ret = exec(join($command, " "), $output, $status); chdir($old_cwd); return $ret; } diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh index 7e71112c7b2349a9e281a99367a5201c3318cf4e..28fb63464d4f6ef63b3073447b86afef4f74e4d3 100644 --- a/core/scripts/run-tests.sh +++ b/core/scripts/run-tests.sh @@ -20,12 +20,16 @@ // Restricting the chunk of queries prevents memory exhaustion. const SIMPLETEST_SCRIPT_SQLITE_VARIABLE_LIMIT = 350; +const SIMPLETEST_SCRIPT_EXIT_SUCCESS = 0; +const SIMPLETEST_SCRIPT_EXIT_FAILURE = 1; +const SIMPLETEST_SCRIPT_EXIT_EXCEPTION = 2; + // Set defaults and get overrides. list($args, $count) = simpletest_script_parse_args(); if ($args['help'] || $count == 0) { simpletest_script_help(); - exit; + exit(($count == 0) ? SIMPLETEST_SCRIPT_EXIT_FAILURE : SIMPLETEST_SCRIPT_EXIT_SUCCESS); } simpletest_script_init(); @@ -38,7 +42,7 @@ simpletest_script_setup_database(); simpletest_script_run_one_test($args['test-id'], $args['execute-test']); // Sub-process exited already; this is just for clarity. - exit; + exit(SIMPLETEST_SCRIPT_EXIT_SUCCESS); } simpletest_script_setup_database(TRUE); @@ -53,7 +57,7 @@ foreach ($messages['status'] as $text) { echo " - " . $text . "\n"; } - exit; + exit(SIMPLETEST_SCRIPT_EXIT_SUCCESS); } if ($args['list']) { @@ -67,7 +71,7 @@ echo " - $class\n"; } } - exit; + exit(SIMPLETEST_SCRIPT_EXIT_SUCCESS); } $test_list = simpletest_script_get_test_list(); @@ -82,7 +86,7 @@ } // Execute tests. -simpletest_script_execute_batch($tests_to_run); +$status = simpletest_script_execute_batch($tests_to_run); // Stop the timer. simpletest_script_reporter_timer_stop(); @@ -100,7 +104,7 @@ } // Test complete, exit. -exit; +exit($status); /** * Print help text. @@ -273,7 +277,7 @@ function simpletest_script_parse_args() { else { // Argument not found in list. simpletest_script_print_error("Unknown argument '$arg'."); - exit; + exit(SIMPLETEST_SCRIPT_EXIT_FAILURE); } } else { @@ -286,7 +290,7 @@ function simpletest_script_parse_args() { // Validate the concurrency argument if (!is_numeric($args['concurrency']) || $args['concurrency'] <= 0) { simpletest_script_print_error("--concurrency must be a strictly positive integer."); - exit; + exit(SIMPLETEST_SCRIPT_EXIT_FAILURE); } return array($args, $count); @@ -318,7 +322,7 @@ function simpletest_script_init() { else { simpletest_script_print_error('Unable to automatically determine the path to the PHP interpreter. Supply the --php command line argument.'); simpletest_script_help(); - exit(); + exit(SIMPLETEST_SCRIPT_EXIT_FAILURE); } // Get URL from arguments. @@ -404,7 +408,7 @@ function simpletest_script_setup_database($new = FALSE) { $info = parse_url($args['dburl']); if (!isset($info['scheme'], $info['host'], $info['path'])) { simpletest_script_print_error('Invalid --dburl. Minimum requirement: driver://host/database'); - exit(1); + exit(SIMPLETEST_SCRIPT_EXIT_FAILURE); } $info += array( 'user' => '', @@ -439,7 +443,7 @@ function simpletest_script_setup_database($new = FALSE) { // If there is no default database connection for tests, we cannot continue. if (!isset($databases['default']['default'])) { simpletest_script_print_error('Missing default database connection for tests. Use --dburl to specify one.'); - exit(1); + exit(SIMPLETEST_SCRIPT_EXIT_FAILURE); } Database::addConnectionInfo('default', 'default', $databases['default']['default']); @@ -483,7 +487,7 @@ function simpletest_script_setup_database($new = FALSE) { } catch (\PDOException $e) { simpletest_script_print_error($databases['test-runner']['default']['driver'] . ': ' . $e->getMessage()); - exit(1); + exit(SIMPLETEST_SCRIPT_EXIT_FAILURE); } if ($new && $sqlite) { require_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'simpletest') . '/simpletest.install'; @@ -497,7 +501,7 @@ function simpletest_script_setup_database($new = FALSE) { // Verify that the Simpletest database schema exists by checking one table. if (!$schema->tableExists('simpletest')) { simpletest_script_print_error('Missing Simpletest database schema. Either install Simpletest module or use the --sqlite parameter.'); - exit(1); + exit(SIMPLETEST_SCRIPT_EXIT_FAILURE); } } @@ -507,6 +511,8 @@ function simpletest_script_setup_database($new = FALSE) { function simpletest_script_execute_batch($test_classes) { global $args, $test_ids; + $total_status = SIMPLETEST_SCRIPT_EXIT_SUCCESS; + // Multi-process execution. $children = array(); while (!empty($test_classes) || !empty($children)) { @@ -523,7 +529,10 @@ function simpletest_script_execute_batch($test_classes) { // Process phpunit tests immediately since they are fast and we don't need // to fork for them. if (is_subclass_of($test_class, 'Drupal\Tests\UnitTestCase')) { - simpletest_script_run_phpunit($test_id, $test_class); + $phpunit_status = simpletest_script_run_phpunit($test_id, $test_class); + if ($phpunit_status > $total_status) { + $total_status = $phpunit_status; + } continue; } @@ -533,7 +542,7 @@ function simpletest_script_execute_batch($test_classes) { if (!is_resource($process)) { echo "Unable to fork test process. Aborting.\n"; - exit; + exit(SIMPLETEST_SCRIPT_EXIT_SUCCESS); } // Register our new child. @@ -554,7 +563,13 @@ function simpletest_script_execute_batch($test_classes) { if (empty($status['running'])) { // The child exited, unregister it. proc_close($child['process']); - if ($status['exitcode']) { + if ($status['exitcode'] == SIMPLETEST_SCRIPT_EXIT_FAILURE) { + if ($status['exitcode'] > $total_status) { + $total_status = $status['exitcode']; + } + } + elseif ($status['exitcode']) { + $total_status = $status['exitcode']; echo 'FATAL ' . $child['class'] . ': test runner returned a non-zero error code (' . $status['exitcode'] . ').' . "\n"; if ($args['die-on-fail']) { list($db_prefix, ) = simpletest_last_test_get($child['test_id']); @@ -575,13 +590,14 @@ function simpletest_script_execute_batch($test_classes) { } } } + return $total_status; } /** * Run a group of phpunit tests. */ function simpletest_script_run_phpunit($test_id, $class) { - $results = simpletest_run_phpunit_tests($test_id, array($class)); + $results = simpletest_run_phpunit_tests($test_id, array($class), $status); simpletest_process_phpunit_results($results); // Map phpunit results to a data structure we can pass to @@ -616,6 +632,7 @@ function simpletest_script_run_phpunit($test_id, $class) { foreach ($summaries as $class => $summary) { simpletest_script_reporter_display_summary($class, $summary); } + return $status; } /** @@ -633,13 +650,16 @@ function simpletest_script_run_one_test($test_id, $test_class) { simpletest_script_reporter_display_summary($test_class, $test->results); // Finished, kill this runner. - exit(0); + if ($test->results['#fail'] || $test->results['#exception']) { + exit(SIMPLETEST_SCRIPT_EXIT_FAILURE); + } + exit(SIMPLETEST_SCRIPT_EXIT_SUCCESS); } // DrupalTestCase::run() catches exceptions already, so this is only reached // when an exception is thrown in the wrapping test runner environment. catch (Exception $e) { echo (string) $e; - exit(1); + exit(SIMPLETEST_SCRIPT_EXIT_EXCEPTION); } } @@ -785,7 +805,7 @@ function simpletest_script_get_test_list() { } simpletest_script_print_error('Test class not found: ' . $test_class); simpletest_script_print_alternatives($test_class, $all_classes, 6); - exit(1); + exit(SIMPLETEST_SCRIPT_EXIT_FAILURE); } } } @@ -794,7 +814,7 @@ function simpletest_script_get_test_list() { foreach ($args['test_names'] as $file) { if (!file_exists($file)) { simpletest_script_print_error('File not found: ' . $file); - exit; + exit(SIMPLETEST_SCRIPT_EXIT_FAILURE); } $content = file_get_contents($file); // Extract a potential namespace. @@ -827,7 +847,7 @@ function simpletest_script_get_test_list() { else { simpletest_script_print_error('Test group not found: ' . $group_name); simpletest_script_print_alternatives($group_name, array_keys($groups)); - exit(1); + exit(SIMPLETEST_SCRIPT_EXIT_FAILURE); } } } @@ -835,7 +855,7 @@ function simpletest_script_get_test_list() { if (empty($test_list)) { simpletest_script_print_error('No valid tests were specified.'); - exit; + exit(SIMPLETEST_SCRIPT_EXIT_FAILURE); } return $test_list; }