Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.770
diff -u -r1.770 common.inc
--- includes/common.inc	30 May 2008 17:41:51 -0000	1.770
+++ includes/common.inc	6 Jun 2008 15:24:00 -0000
@@ -24,6 +24,11 @@
  */
 define('SAVED_DELETED', 3);
 
+/*
+ * Simpletest user-agent regular expression.
+ */
+define('SIMPLETEST_USER_AGENT', '/^simpletest\d+$/');
+
 /**
  * Set content for a specified region.
  *
@@ -362,7 +367,7 @@
   }
 
   // To conserve CPU and bandwidth, omit the blocks.
-  print theme('page', $return, FALSE);
+  print drupal_response($return, FALSE);
 }
 
 /**
@@ -389,7 +394,7 @@
     drupal_set_title(t('Access denied'));
     $return = t('You are not authorized to access this page.');
   }
-  print theme('page', $return);
+  print drupal_response($return);
 }
 
 /**
@@ -3015,6 +3020,10 @@
     'form_element' => array(
       'arguments' => array('element' => NULL, 'value' => NULL),
     ),
+    'simpletest' => array(
+      'arguments' => array('content' => NULL, 'show_blocks' => TRUE, 'show_messages' => TRUE),
+      'template' => 'simpletest',
+    ),
   );
 }
 
@@ -3570,3 +3579,24 @@
   }
   variable_set('css_js_query_string', $new_character . substr($string_history, 0, 19));
 }
+
+/**
+ * Generate the Drupal output response.
+ * If the simpletest user-agent is detected, a special XML output is built.
+ *
+ * @param ...
+ *   Any additional arguments that are passed on to the theme
+ *   function selected, based on the HTTP User Agent.
+ * @return
+ *   The themed output.
+  */
+function drupal_response() {
+  $args = func_get_args();
+  if (preg_match(SIMPLETEST_USER_AGENT, $_SERVER['HTTP_USER_AGENT'])) {
+    array_unshift($args, 'simpletest');
+  }
+  else {
+    array_unshift($args, 'page');
+  }
+  return call_user_func_array('theme', $args);
+}
\ No newline at end of file
Index: includes/theme.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/theme.inc,v
retrieving revision 1.426
diff -u -r1.426 theme.inc
--- includes/theme.inc	6 Jun 2008 01:50:20 -0000	1.426
+++ includes/theme.inc	6 Jun 2008 15:24:09 -0000
@@ -2000,3 +2000,44 @@
   $variables['template_files'][] = 'block-' . $variables['block']->module . '-' . $variables['block']->delta;
 }
 
+/**
+ * Process variables for simpletest.tpl.php
+ *
+ * Prepare the values passed to the theme_simpletest function to be passed
+ * into a pluggable template engine. Collect some useful data available in
+ * the request that can  be used by the testing system to perform assertions.
+ *
+ * The $variables array contains the following arguments, which are in fact
+ * the same arguments passed to the theme_page to get the page content:
+ * - $content
+ * - $show_blocks
+ * - $show_messages
+ *
+ * @see simpletest.tpl.php
+ */
+function template_preprocess_simpletest(&$variables) {
+  global $theme, $user;
+
+  // Get messages but do not clear the queue so that the messages
+  // remain available to the page output.
+  $variables['messages'] = array();
+  foreach (drupal_get_messages(NULL, FALSE) as $type => $messages) {
+    foreach ($messages as $message) {
+      $variables['messages'][] = array('type' => $type, 'message' => $message);
+    }
+  }
+
+  $variables['base_path']         = base_path();
+  $variables['current_path']      = $_GET['q']; ;
+  $variables['path_alias']        = drupal_get_path_alias($_GET['q'], $GLOBALS['language']);
+  $variables['title']             = drupal_get_title();
+  $variables['user']              = $user->uid;
+  $variables['front_page']        = url();
+  $variables['feed_icons']        = drupal_get_feeds();
+  $variables['language']          = $GLOBALS['language'];
+  $variables['language']->dir     = $GLOBALS['language']->direction ? 'rtl' : 'ltr';
+  $variables['site_name']         = variable_get('site_name', 'Drupal');
+  $variables['css']               = drupal_add_css();
+  $variables['scripts']           = drupal_add_js();
+  $variables['content']           = theme('page', $variables['content'], $variables['show_blocks'], $variables['show_messages']);
+}
Index: index.php
===================================================================
RCS file: /cvs/drupal/drupal/index.php,v
retrieving revision 1.94
diff -u -r1.94 index.php
--- index.php	26 Dec 2007 08:46:48 -0000	1.94
+++ index.php	6 Jun 2008 15:23:40 -0000
@@ -33,7 +33,7 @@
 }
 elseif (isset($return)) {
   // Print any value (including an empty string) except NULL or undefined:
-  print theme('page', $return);
+  print drupal_response($return);
 }
 
 drupal_page_footer();
Index: modules/simpletest/drupal_web_test_case.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/drupal_web_test_case.php,v
retrieving revision 1.15
diff -u -r1.15 drupal_web_test_case.php
--- modules/simpletest/drupal_web_test_case.php	6 Jun 2008 10:36:44 -0000	1.15
+++ modules/simpletest/drupal_web_test_case.php	6 Jun 2008 15:24:14 -0000
@@ -6,9 +6,18 @@
  */
 class DrupalWebTestCase extends UnitTestCase {
   protected $_logged_in = FALSE;
+  // Raw HTML data received in the XML <content> tag.
   protected $_content;
+  // The plain text (filtered) version of the HTML content.
   protected $plain_text;
+  // Internal cURL handle.
   protected $ch;
+  // The response received after calling curlExec.
+  protected $_response;
+  // SimpleXMLElement object constructed from the response.
+  protected $xml;
+  // SimpleXMLElement object representing the HTML data in
+  // $this->xml->content
   protected $elements;
   // We do not reuse the cookies in further runs, so we do not need a file
   // but we still need cookie handling, so we set the jar to NULL
@@ -452,11 +461,12 @@
     $this->curlConnect();
     $url = empty($curl_options[CURLOPT_URL]) ? curl_getinfo($this->ch, CURLINFO_EFFECTIVE_URL) : $curl_options[CURLOPT_URL];
     curl_setopt_array($this->ch, $this->curl_options + $curl_options);
-    $this->_content = curl_exec($this->ch);
+    $this->_response = curl_exec($this->ch);
     $this->plain_text = FALSE;
     $this->elements = FALSE;
-    $this->assertTrue($this->_content !== FALSE, t('!method to !url, response is !length bytes.', array('!method' => empty($curl_options[CURLOPT_POSTFIELDS]) ? 'GET' : 'POST', '!url' => $url, '!length' => strlen($this->_content))), t('Browser'));
-    return $this->_content;
+    $this->xml = FALSE;
+    $this->assertTrue($this->_response !== FALSE, t('!method to !url, response is !length bytes.', array('!method' => empty($curl_options[CURLOPT_POSTFIELDS]) ? 'GET' : 'POST', '!url' => $url, '!length' => strlen($this->_response))), t('Browser'));
+    return $this->_response;
   }
 
   /**
@@ -470,24 +480,34 @@
   }
 
   /**
-   * Parse content returned from curlExec using DOM and simplexml.
-   *
-   * @return SimpleXMLElement A SimpleXMLElement or FALSE on failure.
+   * Parse the XML response returned from curlExec
+   * using DOM and SimpleXML.
    */
   protected function parse() {
-    if (!$this->elements) {
-      // DOM can load HTML soup. But, HTML soup can throw warnings, supress
-      // them.
-      @$htmlDom = DOMDocument::loadHTML($this->_content);
-      if ($htmlDom) {
-        $this->assertTrue(TRUE, 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);
+    if (!$this->xml) {
+      // Produces an E_WARNING error message for each error found in the XML data.
+      $this->xml = new SimpleXMLElement($this->_response);
+      if ($this->xml) {
+        $this->assertTrue(TRUE, t('Valid XML found on "@path"', array('@path' => $this->getUrl())), t('Browser'));
+        $this->_content = (string) $this->xml->content;
+        // DOM can load HTML soup. But, HTML soup can throw warnings, supress
+        // them.
+        @$htmlDom = DOMDocument::loadHTML($this->_content);
+        if ($htmlDom) {
+          $this->assertTrue(TRUE, 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);
+        }
+        else {
+          $this->fail(t('Content from "@path" parsed successfully: @content.', array('@content' => $this->_content,'@path' => $this->getUrl())), t('Browser'));
+          return FALSE;
+        }
+      }
+      else {
+        $this->fail(t('Parsed XML successfully.'), t('Browser'));
+        return FALSE;
       }
-    }
-    if (!$this->elements) {
-      $this->fail(t('Parsed page successfully.'), t('Browser'));
     }
     return $this->elements;
   }
@@ -506,7 +526,10 @@
     // options set, it might change the GET into a POST.  Make sure we clear out
     // previous options.
     $out = $this->curlExec(array(CURLOPT_URL => url($path, $options), CURLOPT_POST => FALSE, CURLOPT_POSTFIELDS => array()));
-    $this->refreshVariables(); // Ensure that any changes to variables in the other thread are picked up.
+    // Ensure that any changes to variables in the other thread are picked up.
+    $this->refreshVariables();
+    // Parse the response.
+    $this->parse();
     return $out;
   }
 
@@ -576,15 +599,15 @@
    * exist and attempt to create POST data in the correct manner for the particular
    * field type.
    *
-   * @param array $post 
+   * @param array $post
    *   Reference to array of post values.
-   * @param array $edit 
+   * @param array $edit
    *   Reference to array of edit values to be checked against the form.
-   * @param string $submit 
+   * @param string $submit
    *   Form submit button value.
-   * @param array $form 
+   * @param array $form
    *   Array of form elements.
-   * @return boolean 
+   * @return boolean
    *   Submit value matches a valid submit input in the form.
    */
   protected function handleForm(&$post, &$edit, &$upload, $submit, $form) {
Index: modules/system/simpletest.tpl.php
===================================================================
RCS file: modules/system/simpletest.tpl.php
diff -N modules/system/simpletest.tpl.php
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/system/simpletest.tpl.php	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,33 @@
+<?php
+// $Id
+
+/**
+ * @file simpletest.tpl.php
+ *
+ * Theme implementation to display a XML simpletest data output.
+ *
+ * Available variables:
+ *
+ * @TODO: Document available variables.
+ *
+ * @see template_preprocess()
+ * @see template_preprocess_simpletest()
+ */
+?>
+<simpletest>
+  <title><?php print $title; ?></title>
+  <locale lang="" dir="" negotiation="" />
+  <base-path><?php print $base_path; ?></base-path>
+  <current-path><?php print $current_path ; ?></current-path>
+  <path-alias><?php print $path_alias; ?></path-alias>
+  <user><?php print $user; ?></user>
+
+  <messages>
+    <?php foreach($messages as $message): ?>
+      <message type="<?php print $message['type']; ?>"><?php print $message['message']; ?></message>
+    <?php endforeach; ?>
+  </messages>
+
+  <content><![CDATA[<?php print $content; ?>]]></content>
+
+</simpletest>
