#305077 by DamZ, boombatower, and cwgordon7: Rework SimpleTest backend.
parent
5ba4c53379
commit
ab07b4cd5e
|
@ -627,6 +627,35 @@ function drupal_error_handler($errno, $message, $filename, $line, $context) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the last caller (file name and line of the call, function in which the
|
||||
* call originated) from a backtrace.
|
||||
*
|
||||
* @param $backtrace
|
||||
* A standard PHP backtrace.
|
||||
* @return
|
||||
* An associative array with keys 'file', 'line' and 'function'.
|
||||
*/
|
||||
function _drupal_get_last_caller($backtrace) {
|
||||
// The first trace is the call itself.
|
||||
// It gives us the line and the file of the last call.
|
||||
$call = $backtrace[0];
|
||||
|
||||
// The second call give us the function where the call originated.
|
||||
if (isset($backtrace[1])) {
|
||||
if (isset($backtrace[1]['class'])) {
|
||||
$call['function'] = $backtrace[1]['class'] . $backtrace[1]['type'] . $backtrace[1]['function'] . '()';
|
||||
}
|
||||
else {
|
||||
$call['function'] = $backtrace[1]['function'] . '()';
|
||||
}
|
||||
}
|
||||
else {
|
||||
$call['function'] = 'main()';
|
||||
}
|
||||
return $call;
|
||||
}
|
||||
|
||||
function _fix_gpc_magic(&$item) {
|
||||
if (is_array($item)) {
|
||||
array_walk($item, '_fix_gpc_magic');
|
||||
|
|
|
@ -43,55 +43,75 @@ class DrupalWebTestCase {
|
|||
* The message string.
|
||||
* @param $group
|
||||
* WHich group this assert belongs to.
|
||||
* @param $custom_caller
|
||||
* @param $caller
|
||||
* By default, the assert comes from a function which names start with
|
||||
* 'test'. Instead, you can specify where this assert originates from
|
||||
* by passing in an associative array as $custom_caller. Key 'file' is
|
||||
* by passing in an associative array as $caller. Key 'file' is
|
||||
* the name of the source file, 'line' is the line number and 'function'
|
||||
* is the caller function itself.
|
||||
*/
|
||||
protected function _assert($status, $message = '', $group = 'Other', $custom_caller = NULL) {
|
||||
protected function _assert($status, $message = '', $group = 'Other', $caller = NULL) {
|
||||
global $db_prefix;
|
||||
|
||||
// Convert boolean status to string status.
|
||||
if (is_bool($status)) {
|
||||
$status = $status ? 'pass' : 'fail';
|
||||
}
|
||||
|
||||
// Increment summary result counter.
|
||||
$this->_results['#' . $status]++;
|
||||
if (!isset($custom_caller)) {
|
||||
$callers = debug_backtrace();
|
||||
array_shift($callers);
|
||||
foreach ($callers as $function) {
|
||||
if (substr($function['function'], 0, 6) != 'assert' && $function['function'] != 'pass' && $function['function'] != 'fail') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$function = $custom_caller;
|
||||
|
||||
// Get the function information about the call to the assertion method.
|
||||
if (!$caller) {
|
||||
$caller = $this->getAssertionCall();
|
||||
}
|
||||
|
||||
// Switch to non-testing database to store results in.
|
||||
$current_db_prefix = $db_prefix;
|
||||
$db_prefix = $this->db_prefix_original;
|
||||
db_insert('simpletest')->fields(array(
|
||||
|
||||
// Creation assertion array that can be displayed while tests are running.
|
||||
$this->_assertions[] = $assertion = array(
|
||||
'test_id' => $this->test_id,
|
||||
'test_class' => get_class($this),
|
||||
'status' => $status,
|
||||
'message' => substr($message, 0, 255), // Some messages are too long for the database.
|
||||
'message_group' => $group,
|
||||
'caller' => $function['function'],
|
||||
'line' => $function['line'],
|
||||
'file' => $function['file'],
|
||||
))->execute();
|
||||
$this->_assertions[] = array(
|
||||
'test_class' => get_class($this),
|
||||
'status' => $status,
|
||||
'message' => $message,
|
||||
'group' => $group,
|
||||
'function' => $function['function'],
|
||||
'line' => $function['line'],
|
||||
'file' => $function['file'],
|
||||
'message_group' => $group,
|
||||
'function' => $caller['function'],
|
||||
'line' => $caller['line'],
|
||||
'file' => $caller['file'],
|
||||
);
|
||||
|
||||
// Store assertion for display after the test has completed.
|
||||
db_insert('simpletest')->fields($assertion)->execute();
|
||||
|
||||
// Return to testing prefix.
|
||||
$db_prefix = $current_db_prefix;
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycles through backtrace until the first non-assertion method is found.
|
||||
*
|
||||
* @return
|
||||
* Array representing the true caller.
|
||||
*/
|
||||
protected function getAssertionCall() {
|
||||
$backtrace = debug_backtrace();
|
||||
|
||||
// The first element is the call. The second element is the caller.
|
||||
// We skip calls that occured in one of the methods of DrupalWebTestCase
|
||||
// or in an assertion function.
|
||||
while (($caller = $backtrace[1]) &&
|
||||
((isset($caller['class']) && $caller['class'] == 'DrupalWebTestCase') ||
|
||||
substr($caller['function'], 0, 6) == 'assert')) {
|
||||
// We remove that call.
|
||||
array_shift($backtrace);
|
||||
}
|
||||
|
||||
return _drupal_get_last_caller($backtrace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a value is not false (not an empty string, 0, NULL, or FALSE).
|
||||
*
|
||||
|
@ -263,11 +283,11 @@ class DrupalWebTestCase {
|
|||
* The message to display along with the assertion.
|
||||
* @param $group
|
||||
* The type of assertion - examples are "Browser", "PHP".
|
||||
* @param $custom_caller
|
||||
* @param $caller
|
||||
* The caller of the error.
|
||||
*/
|
||||
protected function error($message = '', $group = 'Other', $custom_caller = NULL) {
|
||||
return $this->_assert('exception', $message, $group, $custom_caller);
|
||||
protected function error($message = '', $group = 'Other', $caller = NULL) {
|
||||
return $this->_assert('exception', $message, $group, $caller);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -281,8 +301,13 @@ class DrupalWebTestCase {
|
|||
// If the current method starts with "test", run it - it's a test.
|
||||
if (strtolower(substr($method, 0, 4)) == 'test') {
|
||||
$this->setUp();
|
||||
$this->$method();
|
||||
// Finish up.
|
||||
try {
|
||||
$this->$method();
|
||||
// Finish up.
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$this->exceptionHandler($e);
|
||||
}
|
||||
$this->tearDown();
|
||||
}
|
||||
}
|
||||
|
@ -308,15 +333,28 @@ class DrupalWebTestCase {
|
|||
E_USER_NOTICE => 'User notice',
|
||||
E_RECOVERABLE_ERROR => 'Recoverable error',
|
||||
);
|
||||
$this->error($message, $error_map[$severity], array(
|
||||
'function' => '',
|
||||
'line' => $line,
|
||||
'file' => $file,
|
||||
));
|
||||
|
||||
$backtrace = debug_backtrace();
|
||||
$this->error($message, $error_map[$severity], _drupal_get_last_caller($backtrace));
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle exceptions.
|
||||
*
|
||||
* @see set_exception_handler
|
||||
*/
|
||||
function exceptionHandler($exception) {
|
||||
$backtrace = $exception->getTrace();
|
||||
// Push on top of the backtrace the call that generated the exception.
|
||||
array_unshift($backtrace, array(
|
||||
'line' => $exception->getLine(),
|
||||
'file' => $exception->getFile(),
|
||||
));
|
||||
$this->error($exception->getMessage(), 'Uncaught exception', _drupal_get_last_caller($backtrace));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a node based on default settings.
|
||||
*
|
||||
|
@ -405,7 +443,7 @@ class DrupalWebTestCase {
|
|||
node_types_rebuild();
|
||||
|
||||
$this->assertEqual($saved_type, SAVED_NEW, t('Created content type %type.', array('%type' => $type->type)));
|
||||
|
||||
|
||||
// Reset permissions so that permissions for this content type are available.
|
||||
$this->checkPermissions(array(), TRUE);
|
||||
|
||||
|
@ -645,7 +683,7 @@ class DrupalWebTestCase {
|
|||
|
||||
// Generate temporary prefixed database to ensure that tests have a clean starting point.
|
||||
$db_prefix = 'simpletest' . mt_rand(1000, 1000000);
|
||||
|
||||
|
||||
include_once './includes/install.inc';
|
||||
drupal_install_system();
|
||||
|
||||
|
@ -659,7 +697,7 @@ class DrupalWebTestCase {
|
|||
// stale data for the previous run's database prefix and all
|
||||
// calls to it will fail.
|
||||
drupal_get_schema(NULL, TRUE);
|
||||
|
||||
|
||||
// Run default profile tasks.
|
||||
$task = 'profile';
|
||||
default_profile_tasks($task, '');
|
||||
|
@ -732,7 +770,6 @@ class DrupalWebTestCase {
|
|||
|
||||
// Close the CURL handler.
|
||||
$this->curlClose();
|
||||
restore_error_handler();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -807,7 +844,7 @@ class DrupalWebTestCase {
|
|||
// them.
|
||||
@$htmlDom = DOMDocument::loadHTML($this->_content);
|
||||
if ($htmlDom) {
|
||||
$this->assertTrue(TRUE, t('Valid HTML found on "@path"', array('@path' => $this->getUrl())), t('Browser'));
|
||||
$this->pass(t('Valid HTML found on "@path"', array('@path' => $this->getUrl())), t('Browser'));
|
||||
// It's much easier to work with simplexml than DOM, luckily enough
|
||||
// we can just simply import our DOM tree.
|
||||
$this->elements = simplexml_import_dom($htmlDom);
|
||||
|
@ -1290,7 +1327,7 @@ class DrupalWebTestCase {
|
|||
* TRUE on pass, FALSE on fail.
|
||||
*/
|
||||
function assertText($text, $message = '', $group = 'Other') {
|
||||
return $this->assertTextHelper($text, $message, $group = 'Other', FALSE);
|
||||
return $this->assertTextHelper($text, $message, $group, FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -177,25 +177,25 @@ function simpletest_schema() {
|
|||
'default' => '',
|
||||
'description' => t('The message group this message belongs to. For example: warning, browser, user.'),
|
||||
),
|
||||
'caller' => array(
|
||||
'function' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => t('Name of the caller function or method that created this message.'),
|
||||
'description' => t('Name of the assertion function or method that created this message.'),
|
||||
),
|
||||
'line' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => t('Line number of the caller.'),
|
||||
'description' => t('Line number on which the function is called.'),
|
||||
),
|
||||
'file' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => t('Name of the file where the caller is.'),
|
||||
'description' => t('Name of the file where the function is called.'),
|
||||
),
|
||||
),
|
||||
'primary key' => array('message_id'),
|
||||
|
|
|
@ -106,7 +106,7 @@ function simpletest_test_form() {
|
|||
$result->message_group,
|
||||
basename($result->file),
|
||||
$result->line,
|
||||
$result->caller,
|
||||
$result->function,
|
||||
$map[$status],
|
||||
),
|
||||
'class' => "simpletest-$status",
|
||||
|
|
|
@ -106,17 +106,36 @@ class SimpleTestTestCase extends DrupalWebTestCase {
|
|||
$this->drupalCreateUser(array($this->invalid_permission));
|
||||
|
||||
$this->pass(t('Test ID is @id.', array('@id' => $this->test_id)));
|
||||
|
||||
// Generates a warning
|
||||
$i = 1 / 0;
|
||||
|
||||
// Call an assert function specific to that class.
|
||||
$this->assertNothing();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert nothing.
|
||||
*/
|
||||
function assertNothing() {
|
||||
$this->pass("This is nothing.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm that the stub test produced the desired results.
|
||||
*/
|
||||
function confirmStubTestResults() {
|
||||
$this->assertAssertion($this->pass, 'Other', 'Pass');
|
||||
$this->assertAssertion($this->fail, 'Other', 'Fail');
|
||||
$this->assertAssertion($this->pass, 'Other', 'Pass', 'simpletest.test', 'SimpleTestTestCase->stubTest()');
|
||||
$this->assertAssertion($this->fail, 'Other', 'Fail', 'simpletest.test', 'SimpleTestTestCase->stubTest()');
|
||||
|
||||
$this->assertAssertion(t('Created permissions: @perms', array('@perms' => $this->valid_permission)), 'Role', 'Pass');
|
||||
$this->assertAssertion(t('Invalid permission %permission.', array('%permission' => $this->invalid_permission)), 'Role', 'Fail');
|
||||
$this->assertAssertion(t('Created permissions: @perms', array('@perms' => $this->valid_permission)), 'Role', 'Pass', 'simpletest.test', 'SimpleTestTestCase->stubTest()');
|
||||
$this->assertAssertion(t('Invalid permission %permission.', array('%permission' => $this->invalid_permission)), 'Role', 'Fail', 'simpletest.test', 'SimpleTestTestCase->stubTest()');
|
||||
|
||||
// Check that a warning is catched by simpletest.
|
||||
$this->assertAssertion('Division by zero', 'Warning', 'Fail', 'simpletest.test', 'SimpleTestTestCase->stubTest()');
|
||||
|
||||
// Check that the backtracing code works for specific assert function.
|
||||
$this->assertAssertion('This is nothing.', 'Other', 'Pass', 'simpletest.test', 'SimpleTestTestCase->stubTest()');
|
||||
|
||||
$this->test_ids[] = $test_id = $this->getTestIdFromResults();
|
||||
$this->assertTrue($test_id, t('Found test ID in results.'));
|
||||
|
@ -141,20 +160,24 @@ class SimpleTestTestCase extends DrupalWebTestCase {
|
|||
* @param string $message Assertion message.
|
||||
* @param string $type Assertion type.
|
||||
* @param string $status Assertion status.
|
||||
* @param string $file File where the assertion originated.
|
||||
* @param string $functuion Function where the assertion originated.
|
||||
* @return Assertion result.
|
||||
*/
|
||||
function assertAssertion($message, $type, $status) {
|
||||
function assertAssertion($message, $type, $status, $file, $function) {
|
||||
$message = trim(strip_tags($message));
|
||||
$found = FALSE;
|
||||
foreach ($this->results['assertions'] as $assertion) {
|
||||
if ($assertion['message'] == $message &&
|
||||
$assertion['type'] == $type &&
|
||||
$assertion['status'] == $status) {
|
||||
$assertion['status'] == $status &&
|
||||
$assertion['file'] == $file &&
|
||||
$assertion['function'] == $function) {
|
||||
$found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $this->assertTrue($found, t('Found assertion {"@message", "@type", "@status"}.', array('@message' => $message, '@type' => $type, '@status' => $status)));
|
||||
return $this->assertTrue($found, t('Found assertion {"@message", "@type", "@status", "@file", "@function"}.', array('@message' => $message, '@type' => $type, '@status' => $status, "@file" => $file, "@function" => $function)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -175,6 +198,9 @@ class SimpleTestTestCase extends DrupalWebTestCase {
|
|||
$assertion = array();
|
||||
$assertion['message'] = $this->asText($row->td[0]);
|
||||
$assertion['type'] = $this->asText($row->td[1]);
|
||||
$assertion['file'] = $this->asText($row->td[2]);
|
||||
$assertion['line'] = $this->asText($row->td[3]);
|
||||
$assertion['function'] = $this->asText($row->td[4]);
|
||||
$ok_url = (url('misc/watchdog-ok.png') == 'misc/watchdog-ok.png') ? 'misc/watchdog-ok.png' : (base_path() . 'misc/watchdog-ok.png');
|
||||
$assertion['status'] = ($row->td[5]->img['src'] == $ok_url) ? 'Pass' : 'Fail';
|
||||
$results['assertions'][] = $assertion;
|
||||
|
@ -212,7 +238,7 @@ class SimpleTestTestCase extends DrupalWebTestCase {
|
|||
if (!is_object($element)) {
|
||||
return $this->fail('The element is not an element.');
|
||||
}
|
||||
return trim(strip_tags($element->asXML()));
|
||||
return trim(html_entity_decode(strip_tags($element->asXML())));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue