=== modified file 'includes/common.inc'
--- includes/common.inc	2008-09-08 21:24:30 +0000
+++ includes/common.inc	2008-09-10 03:51:41 +0000
@@ -627,6 +627,34 @@ function drupal_error_handler($errno, $m
   }
 }
 
+/**
+ * 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');

=== modified file 'modules/simpletest/drupal_web_test_case.php'
--- modules/simpletest/drupal_web_test_case.php	2008-09-06 08:36:19 +0000
+++ modules/simpletest/drupal_web_test_case.php	2008-09-10 04:00:45 +0000
@@ -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);
   }
 
   /**

=== modified file 'modules/simpletest/simpletest.install'
--- modules/simpletest/simpletest.install	2008-08-16 20:57:14 +0000
+++ modules/simpletest/simpletest.install	2008-09-10 03:51:41 +0000
@@ -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'),

=== modified file 'modules/simpletest/simpletest.module'
--- modules/simpletest/simpletest.module	2008-08-21 19:36:35 +0000
+++ modules/simpletest/simpletest.module	2008-09-10 03:51:41 +0000
@@ -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",

=== modified file 'modules/simpletest/simpletest.test'
--- modules/simpletest/simpletest.test	2008-08-22 12:35:55 +0000
+++ modules/simpletest/simpletest.test	2008-09-10 03:59:45 +0000
@@ -106,17 +106,36 @@ class SimpleTestTestCase extends DrupalW
     $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 DrupalW
    * @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 DrupalW
           $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 DrupalW
     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())));
   }
 
   /**

