Index: modules/simpletest/simpletest.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/simpletest.module,v
retrieving revision 1.63
diff -u -r1.63 simpletest.module
--- modules/simpletest/simpletest.module	30 Jul 2009 10:46:53 -0000	1.63
+++ modules/simpletest/simpletest.module	13 Aug 2009 03:21:30 -0000
@@ -95,11 +95,16 @@
 }
 
 function _simpletest_format_summary_line($summary) {
-  return t('@pass, @fail, and @exception', array(
+  $args = array(
     '@pass' => format_plural(isset($summary['#pass']) ? $summary['#pass'] : 0, '1 pass', '@count passes'),
     '@fail' => format_plural(isset($summary['#fail']) ? $summary['#fail'] : 0, '1 fail', '@count fails'),
     '@exception' => format_plural(isset($summary['#exception']) ? $summary['#exception'] : 0, '1 exception', '@count exceptions'),
-  ));
+  );
+  if (!$summary['#debug']) {
+    return t('@pass, @fail, and @exception', $args);
+  }
+  $args['@debug'] = format_plural(isset($summary['#debug']) ? $summary['#debug'] : 0, '1 debug message', '@count debug messages');
+  return t('@pass, @fail, @exception, and @debug', $args);
 }
 
 /**
@@ -155,7 +160,7 @@
     // First iteration: initialize working values.
     $test_list = $test_list_init;
     $context['sandbox']['max'] = count($test_list);
-    $test_results = array('#pass' => 0, '#fail' => 0, '#exception' => 0);
+    $test_results = array('#pass' => 0, '#fail' => 0, '#exception' => 0, '#debug' => 0);
   }
   else {
     // Nth iteration: get the current values where we last stored them.
Index: modules/simpletest/drupal_web_test_case.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/drupal_web_test_case.php,v
retrieving revision 1.134
diff -u -r1.134 drupal_web_test_case.php
--- modules/simpletest/drupal_web_test_case.php	5 Aug 2009 15:58:35 -0000	1.134
+++ modules/simpletest/drupal_web_test_case.php	13 Aug 2009 03:21:30 -0000
@@ -42,6 +42,7 @@
     '#pass' => 0,
     '#fail' => 0,
     '#exception' => 0,
+    '#debug' => 0,
   );
 
   /**
@@ -376,6 +377,12 @@
    *   FALSE.
    */
   protected function error($message = '', $group = 'Other', array $caller = NULL) {
+    if ($group == 'User notice') {
+      // Since 'User notice' is set by trigger_error() which is used for debug
+      // set the message to a status of 'debug'.
+      return $this->assert('debug', $message, 'Debug', $caller);
+    }
+
     return $this->assert('exception', $message, $group, $caller);
   }
 
Index: modules/simpletest/simpletest.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/simpletest.pages.inc,v
retrieving revision 1.11
diff -u -r1.11 simpletest.pages.inc
--- modules/simpletest/simpletest.pages.inc	4 Aug 2009 06:47:00 -0000	1.11
+++ modules/simpletest/simpletest.pages.inc	13 Aug 2009 03:21:30 -0000
@@ -226,6 +226,7 @@
     '#pass' => 0,
     '#fail' => 0,
     '#exception' => 0,
+    '#debug' => 0,
   );
 
   // Cycle through each test group.
@@ -264,14 +265,14 @@
       $form['result']['summary']['#' . $assertion->status]++;
     }
     $form['result']['results'][$group]['table'] = array(
-      '#theme' => 'table', 
-      '#header' => $header, 
+      '#theme' => 'table',
+      '#header' => $header,
       '#rows' => $rows,
     );
 
     // Set summary information.
     $group_summary['#ok'] = $group_summary['#fail'] + $group_summary['#exception'] == 0;
-    $form['result']['results'][$group]['#collapsed'] = $group_summary['#ok'];
+    $form['result']['results'][$group]['#collapsed'] = $group_summary['#ok'] && !$group_summary['#debug'];
 
     // Store test group (class) as for use in filter.
     $filter[$group_summary['#ok'] ? 'pass' : 'fail'][] = $group;
Index: modules/simpletest/simpletest.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/simpletest.test,v
retrieving revision 1.27
diff -u -r1.27 simpletest.test
--- modules/simpletest/simpletest.test	12 Aug 2009 11:32:07 -0000	1.27
+++ modules/simpletest/simpletest.test	13 Aug 2009 03:21:30 -0000
@@ -121,6 +121,8 @@
 
     // Generates a warning inside a PHP function.
     array_key_exists(NULL, NULL);
+
+    debug('Foo', 'Debug');
   }
 
   /**
@@ -151,6 +153,10 @@
     // the function name 'array_key_exists'.
     $this->assertAssertion('array_key_exists', 'Warning', 'Fail', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()');
 
+    $this->assertAssertion("Debug: 'Foo'", 'Debug', 'Fail', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()');
+
+    $this->assertEqual('6 passes, 2 fails, 2 exceptions, and 1 debug message', $this->childTestResults['summary'], 'Stub test summary is correct');
+
     $this->test_ids[] = $test_id = $this->getTestIdFromResults();
     $this->assertTrue($test_id, t('Found test ID in results.'));
   }
@@ -202,7 +208,7 @@
     if ($this->parse()) {
       if ($fieldset = $this->getResultFieldSet()) {
         // Code assumes this is the only test in group.
-        $results['summary'] = $this->asText($fieldset->div);
+        $results['summary'] = $this->asText($fieldset->div[1]);
         $results['name'] = $this->asText($fieldset->legend);
 
         $results['assertions'] = array();
Index: modules/simpletest/tests/common.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/common.test,v
retrieving revision 1.58
diff -u -r1.58 common.test
--- modules/simpletest/tests/common.test	11 Aug 2009 11:52:46 -0000	1.58
+++ modules/simpletest/tests/common.test	13 Aug 2009 03:21:30 -0000
@@ -1024,7 +1024,7 @@
     if (count($this->collectedErrors) == 3) {
       $this->assertError($this->collectedErrors[0], 'Notice', 'error_test_generate_warnings()', 'error_test.module', 'Undefined variable: bananas');
       $this->assertError($this->collectedErrors[1], 'Warning', 'error_test_generate_warnings()', 'error_test.module', 'Division by zero');
-      $this->assertError($this->collectedErrors[2], 'User notice', 'error_test_generate_warnings()', 'error_test.module', 'Drupal is awesome');
+      $this->assertError($this->collectedErrors[2], 'User warning', 'error_test_generate_warnings()', 'error_test.module', 'Drupal is awesome');
     }
     else {
       // Give back the errors to the log report.
Index: modules/simpletest/tests/error.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/error.test,v
retrieving revision 1.4
diff -u -r1.4 error.test
--- modules/simpletest/tests/error.test	13 Jul 2009 21:51:41 -0000	1.4
+++ modules/simpletest/tests/error.test	13 Aug 2009 03:21:30 -0000
@@ -36,7 +36,7 @@
       '%file' => realpath('modules/simpletest/tests/error_test.module'),
     );
     $error_user_notice = array(
-      '%type' => 'User notice',
+      '%type' => 'User warning',
       '%message' => 'Drupal is awesome',
       '%function' => 'error_test_generate_warnings()',
       '%line' => 48,
Index: modules/simpletest/tests/error_test.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/error_test.module,v
retrieving revision 1.3
diff -u -r1.3 error_test.module
--- modules/simpletest/tests/error_test.module	30 May 2009 11:17:32 -0000	1.3
+++ modules/simpletest/tests/error_test.module	13 Aug 2009 03:21:30 -0000
@@ -45,7 +45,7 @@
   // This will generate a warning.
   $awesomely_big = 1/0;
   // This will generate a user error.
-  trigger_error("Drupal is awesome", E_USER_NOTICE);
+  trigger_error("Drupal is awesome", E_USER_WARNING);
   return "";
 }
 
Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.958
diff -u -r1.958 common.inc
--- includes/common.inc	12 Aug 2009 11:33:43 -0000	1.958
+++ includes/common.inc	13 Aug 2009 03:21:30 -0000
@@ -873,7 +873,16 @@
     $error_level = variable_get('error_level', ERROR_REPORTING_DISPLAY_ALL);
     $display_error = $error_level == ERROR_REPORTING_DISPLAY_ALL || ($error_level == ERROR_REPORTING_DISPLAY_SOME && $error['%type'] != 'Notice');
     if ($display_error || (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'update')) {
-      drupal_set_message(t('%type: %message in %function (line %line of %file).', $error), 'error');
+      $class = 'error';
+
+      // If error type is 'User notice' then treat it as debug information
+      // instead of an error message, see dd().
+      if ($error['%type'] == 'User notice') {
+        $error['%type'] = 'Debug';
+        $class = '';
+      }
+
+      drupal_set_message(t('%type: %message in %function (line %line of %file).', $error), $class);
     }
 
     if ($fatal) {
@@ -895,9 +904,11 @@
  *   An associative array with keys 'file', 'line' and 'function'.
  */
 function _drupal_get_last_caller($backtrace) {
-  // Errors that occur inside PHP internal functions
-  // do not generate information about file and line.
-  while ($backtrace && !isset($backtrace[0]['line'])) {
+  // Errors that occur inside PHP internal functions do not generate
+  // information about file and line. Ignore black listed functions.
+  $blacklist = array('debug');
+  while (($backtrace && !isset($backtrace[0]['line'])) ||
+         (isset($backtrace[1]['function']) && in_array($backtrace[1]['function'], $blacklist))) {
     array_shift($backtrace);
   }
 
@@ -2333,14 +2344,14 @@
  * In other words, if the timeout is the default 30 seconds, and 25 seconds
  * into script execution a call such as set_time_limit(20) is made, the
  * script will run for a total of 45 seconds before timing out.
- * 
+ *
  * It also means that it is possible to decrease the total time limit if
  * the sum of the new time limit and the current time spent running the
  * script is inferior to the original time limit. It is inherent to the way
  * set_time_limit() works, it should rather be called with an appropriate
  * value every time you need to allocate a certain amount of time
  * to execute a task than only once at the beginning of the script.
- * 
+ *
  * Before calling set_time_limit(), we check if this function is available
  * because it could be disabled by the server administrator. We also hide all
  * the errors that could occur when calling set_time_limit(), because it is
@@ -4917,3 +4928,24 @@
   }
   variable_set('css_js_query_string', $new_character . substr($string_history, 0, 19));
 }
+
+/**
+ * Debug function used for outputting debug information.
+ *
+ * The debug information is passed on to trigger_error() after being converted
+ * to a string using _drupal_debug_message().
+ *
+ * @param $data
+ *   Data to be output.
+ * @param $label
+ *   Label to prefix the data.
+ * @param $print_r
+ *   Flag to switch between print_r() and var_export() for data conversion to
+ *   string. Set $print_r to TRUE when dealing with a recursive data structure
+ *   as var_export() will generate an error.
+ */
+function debug($data, $label = NULL, $print_r = FALSE) {
+  // Print $data contents to string.
+  $string = $print_r ? print_r($data, TRUE) : var_export($data, TRUE);
+  trigger_error(trim($label ? "$label: $string" : $string));
+}
