Index: run-tests.sh
===================================================================
RCS file: /cvs/drupal/drupal/scripts/run-tests.sh,v
retrieving revision 1.40
diff -u -r1.40 run-tests.sh
--- run-tests.sh	15 Jan 2010 10:51:02 -0000	1.40
+++ run-tests.sh	6 Aug 2010 10:10:00 -0000
@@ -81,6 +81,12 @@
 // Execute tests.
 simpletest_script_command($args['concurrency'], $test_id, implode(",", $test_list));
 
+if ($args['xml']) {
+  echo $args['xml'];
+  // Save results as xml before database is cleared.
+  simpletest_script_reporter_xml_results();
+}
+
 // Retrieve the last database prefix used for testing and the last test class
 // that was run from. Use the information to read the lgo file in case any
 // fatal errors caused the test to crash.
@@ -139,6 +145,9 @@
 
   --verbose   Output detailed assertion messages in addition to summary.
 
+  --xml       Output verbose test results to a specified directory using the JUnit
+              test reporting format. Useful for integrating with Hudson.
+
   <test1>[,<test2>[,<test3> ...]]
 
               One or more tests to be run. By default, these are interpreted
@@ -184,7 +193,8 @@
     'test_names' => array(),
     // Used internally.
     'test-id' => NULL,
-    'execute-batch' => FALSE
+    'execute-batch' => FALSE,
+    'xml' => '',
   );
 
   // Override with set values.
@@ -230,6 +240,11 @@
     exit;
   }
 
+  if ($args['xml'] != '' && !is_dir($args['xml'])) {
+    simpletest_script_print_error("XML argument must specify a valid directory.");
+    exit;
+  }
+
   return array($args, $count);
 }
 
@@ -254,6 +269,12 @@
     // Extract only the PHP interpreter, not the rest of the command.
     list($php, ) = explode(' ', $_ENV['SUDO_COMMAND'], 2);
   }
+  //Windows
+  elseif (!empty($_SERVER['PHPRC'])) {
+    if(is_dir($_SERVER['PHPRC']) && file_exists($_SERVER['PHPRC']. DIRECTORY_SEPARATOR. 'php.exe')){
+        $php = $_SERVER['PHPRC']. DIRECTORY_SEPARATOR. 'php.exe';
+    }
+  }
   else {
     simpletest_script_print_error('Unable to automatically determine the path to the PHP interpreter. Supply the --php command line argument.');
     simpletest_script_help();
@@ -264,7 +285,7 @@
   if (!empty($args['url'])) {
     $parsed_url = parse_url($args['url']);
     $host = $parsed_url['host'] . (isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '');
-    $path = $parsed_url['path'];
+    $path = (isset($parsed_url['path']))? $parsed_url['path'] : NULL;
 
     // If the passed URL schema is 'https' then setup the $_SERVER variables
     // properly so that testing will run under https.
@@ -380,11 +401,38 @@
 function simpletest_script_command($concurrency, $test_id, $tests) {
   global $args, $php;
 
-  $command = "$php ./scripts/{$args['script']} --url {$args['url']}";
+  $phpExecutable    = escapeshellarg($php);
+  $script = escapeshellarg("./scripts/{$args['script']}");
+  $url    = escapeshellarg($args['url']);
+  $concurrency = escapeshellarg($concurrency);
+  $test_id = escapeshellarg($test_id);
+  $tests   = escapeshellarg($tests);
+
+  //Escape windows shell chars
+  if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'){
+    $replacements = array(' '=>'^ ', '('=>'^(', ')'=>'^)', '>'=>'^>', '<'=>'^<');
+    $phpExecutable = str_replace(array_keys($replacements), $replacements, $phpExecutable);
+    $script = str_replace(array_keys($replacements), $replacements, $script);
+    $url = str_replace(array_keys($replacements), $replacements, $url);
+    $concurrency = str_replace(array_keys($replacements), $replacements, $concurrency);
+    $test_id = str_replace(array_keys($replacements), $replacements, $test_id);
+    $tests = str_replace(array_keys($replacements), $replacements, $tests);
+  }
+
+  $command = $phpExecutable . " -f " . $script . " -- --url " . $url;
   if ($args['color']) {
     $command .= ' --color';
   }
-  $command .= " --php " . escapeshellarg($php) . " --concurrency $concurrency --test-id $test_id --execute-batch $tests";
+  $command .= " --php " . $phpExecutable . 
+                " --concurrency " . $concurrency . 
+                " --test-id " . $test_id . 
+                " --execute-batch ". $tests;
+  
+  //encapsulate windows shell call to allow multiple quoted values
+  if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'){
+    $command = 'cmd /C "' . $command . '"';
+  }
+  
   passthru($command);
 }
 
@@ -519,6 +567,92 @@
 }
 
 /**
+ * Display test results.
+ * Credits: http://www.computerminds.co.uk/hudson-and-simpletest
+ * @link http://www.computerminds.co.uk/hudson-and-simpletest
+ * @author http://www.computerminds.co.uk/hudson-and-simpletest
+ */
+function simpletest_script_reporter_xml_results() {
+  global $args, $test_id, $results_map;
+
+  echo "\n";
+  $end = timer_stop('run-tests');
+  echo "XML Test run duration: " . format_interval($end['time'] / 1000);
+  echo "\n";
+
+  //if ($args['verbose']) {
+    // Report results.
+    echo "Detailed test results:\n";
+    echo "----------------------\n";
+    echo "\n";
+
+
+    $results_map = array(
+      'pass' => 'Pass',
+      'fail' => 'Fail',
+      'exception' => 'Exception',
+    );
+
+    $results = db_query("SELECT * FROM {simpletest} WHERE test_id = :test_id ORDER BY test_class, message_id", array(':test_id' => $test_id));
+
+    $test_class = '';
+    $xml_files = array();
+
+
+    foreach ($results as $result) {
+    
+      if (isset($results_map[$result->status])) {
+        if ($result->test_class != $test_class) {
+          // Display test class every time results are for new test class.
+          if (isset($xml_files[$test_class])) {
+            file_put_contents($args['xml'] . '/' . $test_class . '.xml', $xml_files[$test_class]['doc']->saveXML());
+            unset($xml_files[$test_class]);
+          }
+          $test_class = $result->test_class;
+          if (!isset($xml_files[$test_class])) {
+            $doc = new DomDocument('1.0');
+            $root = $doc->createElement('testsuite');
+            $root = $doc->appendChild($root);
+            $xml_files[$test_class] = array('doc' => $doc, 'suite' => $root);
+          }
+        }
+        // Save the result into the XML:
+        $case = $xml_files[$test_class]['doc']->createElement('testcase');
+        $case->setAttribute('classname', $test_class);
+        list($class, $name) = explode('->', $result->function, 2);
+        $case->setAttribute('name', $name);
+
+        if ($result->status == 'fail') {
+          $fail = $xml_files[$test_class]['doc']->createElement('failure');
+          $fail->setAttribute('type', 'failure');
+          $fail->setAttribute('message', $result->message_group);
+          $text = $xml_files[$test_class]['doc']->createTextNode($result->message);
+          $fail->appendChild($text);
+          $case->appendChild($fail);
+        }
+
+
+
+
+        $xml_files[$test_class]['suite']->appendChild($case);
+
+        //simpletest_script_format_result($result);
+      }
+    }
+
+    // Save the last one:
+    if (isset($xml_files[$test_class])) {
+      file_put_contents($args['xml'] . '/' . $test_class . '.xml', $xml_files[$test_class]['doc']->saveXML());
+      unset($xml_files[$test_class]);
+    }
+
+
+
+
+  //}
+}
+
+/**
  * Format the result so that it fits within the default 80 character
  * terminal size.
  *
