summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAngie Byron <webchick@24967.no-reply.drupal.org>2008-09-10 04:13:01 +0000
committerAngie Byron <webchick@24967.no-reply.drupal.org>2008-09-10 04:13:01 +0000
commitab07b4cd5efc35112163b7d74630993920bd92d5 (patch)
tree69f05e8236300e02dc11db66fe2fd651b5c4942d
parent5ba4c53379c48d563dee88cb8237eb5fc3eb72b8 (diff)
downloadbrdo-ab07b4cd5efc35112163b7d74630993920bd92d5.tar.gz
brdo-ab07b4cd5efc35112163b7d74630993920bd92d5.tar.bz2
#305077 by DamZ, boombatower, and cwgordon7: Rework SimpleTest backend.
-rw-r--r--includes/common.inc29
-rw-r--r--modules/simpletest/drupal_web_test_case.php125
-rw-r--r--modules/simpletest/simpletest.install8
-rw-r--r--modules/simpletest/simpletest.module2
-rw-r--r--modules/simpletest/simpletest.test42
5 files changed, 149 insertions, 57 deletions
diff --git a/includes/common.inc b/includes/common.inc
index 81e2ca1cd..3a00beea4 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -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');
diff --git a/modules/simpletest/drupal_web_test_case.php b/modules/simpletest/drupal_web_test_case.php
index d06b5731c..f27cee60a 100644
--- a/modules/simpletest/drupal_web_test_case.php
+++ b/modules/simpletest/drupal_web_test_case.php
@@ -43,56 +43,76 @@ 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).
*
* @param $value
@@ -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,16 +333,29 @@ 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.
*
* @param $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);
}
/**
diff --git a/modules/simpletest/simpletest.install b/modules/simpletest/simpletest.install
index 443f0e375..895e92c1d 100644
--- a/modules/simpletest/simpletest.install
+++ b/modules/simpletest/simpletest.install
@@ -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'),
diff --git a/modules/simpletest/simpletest.module b/modules/simpletest/simpletest.module
index 3decbfe76..dec43853f 100644
--- a/modules/simpletest/simpletest.module
+++ b/modules/simpletest/simpletest.module
@@ -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",
diff --git a/modules/simpletest/simpletest.test b/modules/simpletest/simpletest.test
index a2e212b57..711748d79 100644
--- a/modules/simpletest/simpletest.test
+++ b/modules/simpletest/simpletest.test
@@ -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', '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()');
- $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');
+ // 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())));
}
/**