- Patch #443154 by boombatower, pwolanin: properly report fatal errors as failures.
parent
4e96b4e5df
commit
8140ed0d84
|
@ -3443,6 +3443,14 @@ function _drupal_bootstrap_full() {
|
||||||
set_error_handler('_drupal_error_handler');
|
set_error_handler('_drupal_error_handler');
|
||||||
set_exception_handler('_drupal_exception_handler');
|
set_exception_handler('_drupal_exception_handler');
|
||||||
|
|
||||||
|
if (isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'simpletest') !== FALSE) {
|
||||||
|
// Valid SimpleTest user-agent, log fatal errors to test specific file
|
||||||
|
// directory. The user-agent is validated in DRUPAL_BOOTSTRAP_DATABASE
|
||||||
|
// phase so as long as it is a SimpleTest user-agent it is valid.
|
||||||
|
ini_set('log_errors', 1);
|
||||||
|
ini_set('error_log', file_directory_path() . '/error.log');
|
||||||
|
}
|
||||||
|
|
||||||
// Emit the correct charset HTTP header.
|
// Emit the correct charset HTTP header.
|
||||||
drupal_set_header('Content-Type', 'text/html; charset=utf-8');
|
drupal_set_header('Content-Type', 'text/html; charset=utf-8');
|
||||||
// Detect string handling method
|
// Detect string handling method
|
||||||
|
|
|
@ -138,20 +138,27 @@ abstract class DrupalTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make assertions from outside the test case.
|
* Store an assertion from outside the testing context.
|
||||||
|
*
|
||||||
|
* This is useful for inserting assertions that can only be recorded after
|
||||||
|
* the test case has been destroyed, such as PHP fatal errors. The caller
|
||||||
|
* information is not automatically gathered since the caller is most likely
|
||||||
|
* inserting the assertion on behalf of other code. In all other respects
|
||||||
|
* the method behaves just like DrupalTestCase::assert() in terms of storing
|
||||||
|
* the assertion.
|
||||||
*
|
*
|
||||||
* @see DrupalTestCase::assert()
|
* @see DrupalTestCase::assert()
|
||||||
*/
|
*/
|
||||||
public static function assertStatic($test_id, $test_class, $status, $message = '', $group = 'Other', array $caller = NULL) {
|
public static function insertAssert($test_id, $test_class, $status, $message = '', $group = 'Other', array $caller = array()) {
|
||||||
// Convert boolean status to string status.
|
// Convert boolean status to string status.
|
||||||
if (is_bool($status)) {
|
if (is_bool($status)) {
|
||||||
$status = $status ? 'pass' : 'fail';
|
$status = $status ? 'pass' : 'fail';
|
||||||
}
|
}
|
||||||
|
|
||||||
$caller += array(
|
$caller += array(
|
||||||
'function' => t('N/A'),
|
'function' => t('Unknown'),
|
||||||
'line' => -1,
|
'line' => 0,
|
||||||
'file' => t('N/A'),
|
'file' => t('Unknown'),
|
||||||
);
|
);
|
||||||
|
|
||||||
$assertion = array(
|
$assertion = array(
|
||||||
|
@ -1033,13 +1040,22 @@ class DrupalWebTestCase extends DrupalTestCase {
|
||||||
->execute();
|
->execute();
|
||||||
$db_prefix = $db_prefix_new;
|
$db_prefix = $db_prefix_new;
|
||||||
|
|
||||||
|
// Create test directory ahead of installation so fatal errors and debug
|
||||||
|
// information can be logged during installation process.
|
||||||
|
$directory = $this->originalFileDirectory . '/simpletest/' . substr($db_prefix, 10);
|
||||||
|
file_check_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
|
||||||
|
|
||||||
|
// Log fatal errors.
|
||||||
|
ini_set('log_errors', 1);
|
||||||
|
ini_set('error_log', $directory . '/error.log');
|
||||||
|
|
||||||
include_once DRUPAL_ROOT . '/includes/install.inc';
|
include_once DRUPAL_ROOT . '/includes/install.inc';
|
||||||
drupal_install_system();
|
drupal_install_system();
|
||||||
|
|
||||||
$this->preloadRegistry();
|
$this->preloadRegistry();
|
||||||
|
|
||||||
// Include the default profile
|
// Include the default profile
|
||||||
require_once("./profiles/default/default.profile");
|
require_once('./profiles/default/default.profile');
|
||||||
$profile_details = install_profile_info('default', 'en');
|
$profile_details = install_profile_info('default', 'en');
|
||||||
|
|
||||||
// Add the specified modules to the list of modules in the default profile.
|
// Add the specified modules to the list of modules in the default profile.
|
||||||
|
@ -1090,15 +1106,9 @@ class DrupalWebTestCase extends DrupalTestCase {
|
||||||
// default mail handler.
|
// default mail handler.
|
||||||
variable_set('smtp_library', drupal_get_path('module', 'simpletest') . '/drupal_web_test_case.php');
|
variable_set('smtp_library', drupal_get_path('module', 'simpletest') . '/drupal_web_test_case.php');
|
||||||
|
|
||||||
// Use temporary files directory with the same prefix as database.
|
// Use temporary files directory with the same prefix as database. The
|
||||||
variable_set('file_directory_path', $this->originalFileDirectory . '/simpletest/' . substr($db_prefix, 10));
|
// directory will have been created already.
|
||||||
$directory = file_directory_path();
|
variable_set('file_directory_path', $directory);
|
||||||
// Create the files directory.
|
|
||||||
file_check_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
|
|
||||||
|
|
||||||
// Log fatal errors.
|
|
||||||
ini_set('log_errors', 1);
|
|
||||||
ini_set('error_log', $directory . '/error.log');
|
|
||||||
|
|
||||||
set_time_limit($this->timeLimit);
|
set_time_limit($this->timeLimit);
|
||||||
}
|
}
|
||||||
|
@ -1138,6 +1148,13 @@ class DrupalWebTestCase extends DrupalTestCase {
|
||||||
protected function tearDown() {
|
protected function tearDown() {
|
||||||
global $db_prefix, $user, $language;
|
global $db_prefix, $user, $language;
|
||||||
|
|
||||||
|
// In case a fatal error occured that was not in the test process read the
|
||||||
|
// log to pick up any fatal errors.
|
||||||
|
$db_prefix_temp = $db_prefix;
|
||||||
|
$db_prefix = $this->originalPrefix;
|
||||||
|
simpletest_log_read($this->testId, $db_prefix, get_class($this), TRUE);
|
||||||
|
$db_prefix = $db_prefix_temp;
|
||||||
|
|
||||||
$emailCount = count(variable_get('simpletest_emails', array()));
|
$emailCount = count(variable_get('simpletest_emails', array()));
|
||||||
if ($emailCount) {
|
if ($emailCount) {
|
||||||
$message = format_plural($emailCount, t('!count e-mail was sent during this test.'), t('!count e-mails were sent during this test.'), array('!count' => $emailCount));
|
$message = format_plural($emailCount, t('!count e-mail was sent during this test.'), t('!count e-mails were sent during this test.'), array('!count' => $emailCount));
|
||||||
|
|
|
@ -203,7 +203,14 @@ function _simpletest_batch_finished($success, $results, $operations, $elapsed) {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Use the test_id passed as a parameter to _simpletest_batch_operation().
|
// Use the test_id passed as a parameter to _simpletest_batch_operation().
|
||||||
simpletest_log_read($operations[0][1][1]);
|
$test_id = $operations[0][1][1];
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
|
||||||
drupal_set_message(t('The test run did not successfully finish.'), 'error');
|
drupal_set_message(t('The test run did not successfully finish.'), 'error');
|
||||||
drupal_set_message(t('Please use the <em>Clean environment</em> button to clean-up temporary files and tables.'), 'warning');
|
drupal_set_message(t('Please use the <em>Clean environment</em> button to clean-up temporary files and tables.'), 'warning');
|
||||||
|
@ -211,6 +218,21 @@ function _simpletest_batch_finished($success, $results, $operations, $elapsed) {
|
||||||
module_invoke_all('test_group_finished');
|
module_invoke_all('test_group_finished');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get information about the last test that ran given a test ID.
|
||||||
|
*
|
||||||
|
* @param $test_id
|
||||||
|
* The test ID to get the last test from.
|
||||||
|
* @return
|
||||||
|
* Array containing the last database prefix used and the last test class
|
||||||
|
* that ran.
|
||||||
|
*/
|
||||||
|
function simpletest_last_test_get($test_id) {
|
||||||
|
$last_prefix = db_result(db_query_range('SELECT last_prefix FROM {simpletest_test_id} WHERE test_id = :test_id', array(':test_id' => $test_id), 0, 1));
|
||||||
|
$last_test_class = db_result(db_query_range('SELECT test_class FROM {simpletest} WHERE test_id = :test_id ORDER BY message_id DESC', array(':test_id' => $test_id), 0, 1));
|
||||||
|
return array($last_prefix, $last_test_class);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read the error log and report any errors as assertion failures.
|
* Read the error log and report any errors as assertion failures.
|
||||||
*
|
*
|
||||||
|
@ -218,28 +240,39 @@ function _simpletest_batch_finished($success, $results, $operations, $elapsed) {
|
||||||
* will have been recorded by the error handler.
|
* will have been recorded by the error handler.
|
||||||
*
|
*
|
||||||
* @param $test_id
|
* @param $test_id
|
||||||
* The test ID to read log file for.
|
* The test ID to which the log relates.
|
||||||
|
* @param $prefix
|
||||||
|
* The database prefix to which the log relates.
|
||||||
|
* @param $test_class
|
||||||
|
* The test class to which the log relates.
|
||||||
|
* @param $during_test
|
||||||
|
* Indicates that the current file directory path is a temporary file
|
||||||
|
* file directory used during testing.
|
||||||
|
* @return
|
||||||
|
* Found any entries in log.
|
||||||
*/
|
*/
|
||||||
function simpletest_log_read($test_id) {
|
function simpletest_log_read($test_id, $prefix, $test_class, $during_test = FALSE) {
|
||||||
$last_prefix = db_query('SELECT last_prefix FROM {simpletest_test_id} WHERE test_id = :test_id', array(':test_id' => $test_id))->fetchField();
|
$log = file_directory_path() . ($during_test ? '' : '/simpletest/' . substr($prefix, 10)) . '/error.log';
|
||||||
$last_prefix = substr($last_prefix, 10);
|
$found = FALSE;
|
||||||
|
|
||||||
$test_class = db_query('SELECT test_class FROM {simpletest} WHERE test_id = :test_id ORDER BY message_id', array(':test_id' => $test_id))->fetchField();
|
|
||||||
$log = file_directory_path() . "/simpletest/$last_prefix/error.log";
|
|
||||||
if (file_exists($log)) {
|
if (file_exists($log)) {
|
||||||
foreach (file($log) as $line) {
|
foreach (file($log) as $line) {
|
||||||
if (preg_match('/PHP Fatal error: (.*?) in (.*) on line (\d+)/', $line, $match)) {
|
if (preg_match('/\[.*?\] (.*?): (.*?) in (.*) on line (\d+)/', $line, $match)) {
|
||||||
|
// Parse PHP fatal errors for example: PHP Fatal error: Call to
|
||||||
|
// undefined function break_me() in /path/to/file.php on line 17
|
||||||
$caller = array(
|
$caller = array(
|
||||||
'line' => $match[3],
|
'line' => $match[4],
|
||||||
'file' => $match[2],
|
'file' => $match[3],
|
||||||
);
|
);
|
||||||
DrupalTestCase::assertStatic($test_id, $test_class, FALSE, $match[1], 'Fatal error', $caller);
|
DrupalTestCase::insertAssert($test_id, $test_class, FALSE, $match[2], $match[1], $caller);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
DrupalTestCase::assertStatic($test_id, $test_class, FALSE, $line, 'Fatal error');
|
// Unkown format, place the entire message in the log.
|
||||||
|
DrupalTestCase::insertAssert($test_id, $test_class, FALSE, $line, 'Fatal error');
|
||||||
}
|
}
|
||||||
|
$found = TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return $found;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -83,6 +83,12 @@ $test_id = db_insert('simpletest_test_id')->useDefaults(array('test_id'))->execu
|
||||||
// Execute tests.
|
// Execute tests.
|
||||||
simpletest_script_command($args['concurrency'], $test_id, implode(",", $test_list));
|
simpletest_script_command($args['concurrency'], $test_id, implode(",", $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);
|
||||||
|
|
||||||
// Display results before database is cleared.
|
// Display results before database is cleared.
|
||||||
simpletest_script_reporter_display_results();
|
simpletest_script_reporter_display_results();
|
||||||
|
|
||||||
|
@ -466,8 +472,6 @@ function simpletest_script_reporter_init() {
|
||||||
function simpletest_script_reporter_display_results() {
|
function simpletest_script_reporter_display_results() {
|
||||||
global $args, $test_id, $results_map;
|
global $args, $test_id, $results_map;
|
||||||
|
|
||||||
simpletest_log_read($test_id);
|
|
||||||
|
|
||||||
echo "\n";
|
echo "\n";
|
||||||
$end = timer_stop('run-tests');
|
$end = timer_stop('run-tests');
|
||||||
echo "Test run duration: " . format_interval($end['time'] / 1000);
|
echo "Test run duration: " . format_interval($end['time'] / 1000);
|
||||||
|
|
Loading…
Reference in New Issue