Skip to content
Snippets Groups Projects
Commit b3262427 authored by Dries Buytaert's avatar Dries Buytaert
Browse files

- Patch #1565718 by sun, jthorson: Fixed Script test runner does not clean after fatal error.

parent e24968df
No related branches found
No related tags found
2 merge requests!7452Issue #1797438. HTML5 validation is preventing form submit and not fully...,!789Issue #3210310: Adjust Database API to remove deprecated Drupal 9 code in Drupal 10
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
// Masquerade as Apache for running tests. // Masquerade as Apache for running tests.
simpletest_script_init("Apache"); simpletest_script_init("Apache");
simpletest_script_run_one_test($args['test-id'], $args['execute-test']); simpletest_script_run_one_test($args['test-id'], $args['execute-test']);
// Sub-process script execution ends here.
} }
else { else {
// Run administrative functions as CLI. // Run administrative functions as CLI.
...@@ -74,17 +75,8 @@ ...@@ -74,17 +75,8 @@
simpletest_script_reporter_init(); simpletest_script_reporter_init();
// Setup database for test results.
$test_id = db_insert('simpletest_test_id')->useDefaults(array('test_id'))->execute();
// Execute tests. // Execute tests.
simpletest_script_execute_batch($test_id, simpletest_script_get_test_list()); simpletest_script_execute_batch(simpletest_script_get_test_list());
// Retrieve the last database prefix used for testing and the last test class
// that was run from. Use the information to read the lgo file in case any
// fatal errors caused the test to crash.
list($last_prefix, $last_test_class) = simpletest_last_test_get($test_id);
simpletest_log_read($test_id, $last_prefix, $last_test_class);
// Stop the timer. // Stop the timer.
simpletest_script_reporter_timer_stop(); simpletest_script_reporter_timer_stop();
...@@ -96,8 +88,8 @@ ...@@ -96,8 +88,8 @@
simpletest_script_reporter_write_xml_results(); simpletest_script_reporter_write_xml_results();
} }
// Cleanup our test results. // Clean up all test results.
simpletest_clean_results_table($test_id); simpletest_clean_results_table();
// Test complete, exit. // Test complete, exit.
exit; exit;
...@@ -308,8 +300,8 @@ function simpletest_script_init($server_software) { ...@@ -308,8 +300,8 @@ function simpletest_script_init($server_software) {
/** /**
* Execute a batch of tests. * Execute a batch of tests.
*/ */
function simpletest_script_execute_batch($test_id, $test_classes) { function simpletest_script_execute_batch($test_classes) {
global $args; global $args, $test_ids;
// Multi-process execution. // Multi-process execution.
$children = array(); $children = array();
...@@ -320,6 +312,8 @@ function simpletest_script_execute_batch($test_id, $test_classes) { ...@@ -320,6 +312,8 @@ function simpletest_script_execute_batch($test_id, $test_classes) {
} }
// Fork a child process. // Fork a child process.
$test_id = db_insert('simpletest_test_id')->useDefaults(array('test_id'))->execute();
$test_ids[] = $test_id;
$test_class = array_shift($test_classes); $test_class = array_shift($test_classes);
$command = simpletest_script_command($test_id, $test_class); $command = simpletest_script_command($test_id, $test_class);
$process = proc_open($command, array(), $pipes, NULL, NULL, array('bypass_shell' => TRUE)); $process = proc_open($command, array(), $pipes, NULL, NULL, array('bypass_shell' => TRUE));
...@@ -332,6 +326,7 @@ function simpletest_script_execute_batch($test_id, $test_classes) { ...@@ -332,6 +326,7 @@ function simpletest_script_execute_batch($test_id, $test_classes) {
// Register our new child. // Register our new child.
$children[] = array( $children[] = array(
'process' => $process, 'process' => $process,
'test_id' => $test_id,
'class' => $test_class, 'class' => $test_class,
'pipes' => $pipes, 'pipes' => $pipes,
); );
...@@ -347,8 +342,12 @@ function simpletest_script_execute_batch($test_id, $test_classes) { ...@@ -347,8 +342,12 @@ function simpletest_script_execute_batch($test_id, $test_classes) {
// The child exited, unregister it. // The child exited, unregister it.
proc_close($child['process']); proc_close($child['process']);
if ($status['exitcode']) { if ($status['exitcode']) {
echo 'FATAL ' . $test_class . ': test runner returned a non-zero error code (' . $status['exitcode'] . ').' . "\n"; echo 'FATAL ' . $child['class'] . ': test runner returned a non-zero error code (' . $status['exitcode'] . ').' . "\n";
} }
// Free-up space by removing any potentially created resources.
simpletest_script_cleanup($child['test_id'], $child['class'], $status['exitcode']);
// Remove this child.
unset($children[$cid]); unset($children[$cid]);
} }
} }
...@@ -377,6 +376,8 @@ function simpletest_script_run_one_test($test_id, $test_class) { ...@@ -377,6 +376,8 @@ function simpletest_script_run_one_test($test_id, $test_class) {
// Finished, kill this runner. // Finished, kill this runner.
exit(0); exit(0);
} }
// DrupalTestCase::run() catches exceptions already, so this is only reached
// when an exception is thrown in the wrapping test runner environment.
catch (Exception $e) { catch (Exception $e) {
echo (string) $e; echo (string) $e;
exit(1); exit(1);
...@@ -402,6 +403,83 @@ function simpletest_script_command($test_id, $test_class) { ...@@ -402,6 +403,83 @@ function simpletest_script_command($test_id, $test_class) {
return $command; return $command;
} }
/**
* Removes all remnants of a test runner.
*
* In case a (e.g., fatal) error occurs after the test site has been fully setup
* and the error happens in many tests, the environment that executes the tests
* can easily run out of memory or disk space. This function ensures that all
* created resources are properly cleaned up after every executed test.
*
* This clean-up only exists in this script, since SimpleTest module itself does
* not use isolated sub-processes for each test being run, so a fatal error
* halts not only the test, but also the test runner (i.e., the parent site).
*
* @param int $test_id
* The test ID of the test run.
* @param string $test_class
* The class name of the test run.
* @param int $exitcode
* The exit code of the test runner.
*
* @see simpletest_script_run_one_test()
*/
function simpletest_script_cleanup($test_id, $test_class, $exitcode) {
// Retrieve the last database prefix used for testing.
list($db_prefix, ) = simpletest_last_test_get($test_id);
// If no database prefix was found, then the test was not set up correctly.
if (empty($db_prefix)) {
echo "\nFATAL $test_class: Found no database prefix for test ID $test_id. (Check whether setUp() is invoked correctly.)";
return;
}
// Do not output verbose cleanup messages in case of a positive exitcode.
$output = !empty($exitcode);
$messages = array();
$messages[] = "- Found database prefix '$db_prefix' for test ID $test_id.";
// Read the log file in case any fatal errors caused the test to crash.
simpletest_log_read($test_id, $db_prefix, $test_class);
// Check whether a test file directory was setup already.
// @see prepareEnvironment()
$public_files = variable_get('file_public_path', conf_path() . '/files');
$test_directory = $public_files . '/simpletest/' . substr($db_prefix, 10);
if (is_dir($test_directory)) {
// Output the error_log.
if (is_file($test_directory . '/error.log')) {
if ($errors = file_get_contents($test_directory . '/error.log')) {
$output = TRUE;
$messages[] = $errors;
}
}
// Delete the test files directory.
// simpletest_clean_temporary_directories() cannot be used here, since it
// would also delete file directories of other tests that are potentially
// running concurrently.
file_unmanaged_delete_recursive($test_directory);
$messages[] = "- Removed test files directory.";
}
// Clear out all database tables from the test.
$count = 0;
foreach (db_find_tables($db_prefix . '%') as $table) {
db_drop_table($table);
$count++;
}
if ($count) {
$messages[] = "- " . format_plural($count, 'Removed 1 leftover table.', 'Removed @count leftover tables.');
}
if ($output) {
echo implode("\n", $messages);
echo "\n";
}
}
/** /**
* Get list of tests based on arguments. If --all specified then * Get list of tests based on arguments. If --all specified then
* returns all available tests, otherwise reads list of tests. * returns all available tests, otherwise reads list of tests.
...@@ -504,9 +582,9 @@ function simpletest_script_reporter_init() { ...@@ -504,9 +582,9 @@ function simpletest_script_reporter_init() {
* Display jUnit XML test results. * Display jUnit XML test results.
*/ */
function simpletest_script_reporter_write_xml_results() { function simpletest_script_reporter_write_xml_results() {
global $args, $test_id, $results_map; global $args, $test_ids, $results_map;
$results = db_query("SELECT * FROM {simpletest} WHERE test_id = :test_id ORDER BY test_class, message_id", array(':test_id' => $test_id)); $results = db_query("SELECT * FROM {simpletest} WHERE test_id IN (:test_ids) ORDER BY test_class, message_id", array(':test_ids' => $test_ids));
$test_class = ''; $test_class = '';
$xml_files = array(); $xml_files = array();
...@@ -584,14 +662,14 @@ function simpletest_script_reporter_timer_stop() { ...@@ -584,14 +662,14 @@ function simpletest_script_reporter_timer_stop() {
* Display test results. * Display test results.
*/ */
function simpletest_script_reporter_display_results() { function simpletest_script_reporter_display_results() {
global $args, $test_id, $results_map; global $args, $test_ids, $results_map;
if ($args['verbose']) { if ($args['verbose']) {
// Report results. // Report results.
echo "Detailed test results\n"; echo "Detailed test results\n";
echo "---------------------\n"; echo "---------------------\n";
$results = db_query("SELECT * FROM {simpletest} WHERE test_id = :test_id ORDER BY test_class, message_id", array(':test_id' => $test_id)); $results = db_query("SELECT * FROM {simpletest} WHERE test_id IN (:test_ids) ORDER BY test_class, message_id", array(':test_ids' => $test_ids));
$test_class = ''; $test_class = '';
foreach ($results as $result) { foreach ($results as $result) {
if (isset($results_map[$result->status])) { if (isset($results_map[$result->status])) {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment