#243532: Catch notices, warnings, errors and fatal errors from the tested side.

From: Damien Tournoud <damien@tournoud.net>

Requires #304924.
---

 includes/common.inc                         |   12 ++++++++++++
 modules/simpletest/drupal_web_test_case.php |   25 +++++++++++++++++++++++++
 2 files changed, 37 insertions(+), 0 deletions(-)


diff --git includes/common.inc includes/common.inc
index 89e2ef4..ed0fc55 100644
--- includes/common.inc
+++ includes/common.inc
@@ -666,6 +666,18 @@ function _drupal_log_error($type, $message, $backtrace) {
     drupal_set_message(t('@type: %message in %function (line %line of %file).', array('@type' => $type, '%message' => $message, '%function' => $caller['function'], '%line' => $caller['line'], '%file' => $caller['file'])), 'error');
   }
 
+  // When running inside the testing framework, we relay the errors
+  // to the tested site by the way of HTTP headers.
+  if (preg_match("/^simpletest\d+/", $GLOBALS['db_prefix'])) {
+    static $number = 0;
+    $error_line = array();
+    foreach (array('message', 'filename', 'line', 'function') as $variable) {
+      $error_line[] = $variable . '=' . rawurlencode($$variable);
+    }
+    header('X-Error-' . $number . ': ' . implode('&', $error_line));
+    $number++;
+  }
+
   watchdog('php', '%error: %message in %function (line %line of %file).', array('%error' => $type, '%message' => $message, '%function' => $caller['function'], '%file' => $caller['file'], '%line' => $caller['line']), WATCHDOG_ERROR);  
 }
 
diff --git modules/simpletest/drupal_web_test_case.php modules/simpletest/drupal_web_test_case.php
index 1971d92..52d9f58 100644
--- modules/simpletest/drupal_web_test_case.php
+++ modules/simpletest/drupal_web_test_case.php
@@ -793,6 +793,7 @@ class DrupalWebTestCase {
         CURLOPT_RETURNTRANSFER => TRUE,
         CURLOPT_SSL_VERIFYPEER => FALSE,  // Required to make the tests run on https://
         CURLOPT_SSL_VERIFYHOST => FALSE,  // Required to make the tests run on https://
+        CURLOPT_HEADERFUNCTION => array($this, 'curlHeaderCallback'),
       );
       if (preg_match('/simpletest\d+/', $db_prefix)) {
         $curl_options[CURLOPT_USERAGENT] = $db_prefix;
@@ -825,6 +826,30 @@ class DrupalWebTestCase {
   }
 
   /**
+   * Reads headers and registers errors received from the tested site.
+   *
+   * @see _drupal_log_error().
+   *
+   * @param $ch the cURL handler.
+   * @param $header a header.
+   */
+  protected function curlHeaderCallback($ch, $header) {
+    if (preg_match('/^X-Error-[0-9]+: (.*)$/', $header, $matches)) {
+      $caller = array();
+      foreach (explode('&', $matches[1]) as $part) {
+        list($name, $value) = explode('=', $part);
+        $caller[$name] = rawurldecode($value);
+      }
+      if (!empty($caller['message'])) {
+        $message = $caller['message'];
+        unset($caller['message']);
+        $this->error($message, 'Other', $caller);
+      }
+    }
+    return strlen($header);
+  }
+
+  /**
    * Close the cURL handler and unset the handler.
    */
   protected function curlClose() {
