=== modified file 'modules/simpletest/drupal_test_suite.php'
--- modules/simpletest/drupal_test_suite.php	2008-05-06 11:21:10 +0000
+++ modules/simpletest/drupal_test_suite.php	2008-06-07 05:01:16 +0000
@@ -24,6 +24,61 @@ class DrupalTestSuite extends TestSuite 
     }
     return $this->_test_cases;
   }
+
+  function addTestCase(&$test_case) {
+    $this->add($test_case);
+  }
+
+  function addTestClass($class) {
+    $this->add($class);
+  }
+
+  function runOneByOne(&$reporter) {
+    if ($this->getSize() == 0) {
+      // Nothing to do, exit early
+      return;
+    }
+
+    if (!$this->_already_ran) {
+      $this->_already_ran = TRUE;
+      $reporter->paintGroupStart($this->getLabel(), $this->getSize());
+    }
+
+    $test = array_shift($this->_test_cases);
+    if (is_string($test)) {
+      $test = &new $test();
+    }
+
+    if (is_a($test, 'TestSuite')) {
+      // This is a test suite, run one test at a time
+      $this->_size -= $test->getSize();
+      $test_ran = $test->runOneByOne($reporter);
+      if ($test->getSize() > 0) {
+        // Requeue this test suite
+        array_unshift($this->_test_cases, $test);
+        $this->_size += $test->getSize();
+      }
+    }
+    else {
+      $this->_size -= 1;
+      $test->run($reporter);
+      $test_ran = $test->getLabel();
+    }
+
+    if ($this->getSize() == 0) {
+      $reporter->paintGroupEnd($this->getLabel());
+    }
+    return $test_ran;
+  }
+
+  /**
+   *    Number of contained test cases.
+   *    @return integer     Total count of cases in the group.
+   *    @access public
+   */
+  function getSize() {
+    return $this->_size;
+  }
 }
 
 class DrupalTests extends DrupalTestSuite {
@@ -65,9 +120,9 @@ class DrupalTests extends DrupalTestSuit
     foreach ($groups as $group_name => $group) {
       $group_test = &new DrupalTestSuite($group_name);
       foreach ($group as $key => $v) {
-        $group_test->addTestCase($group[$key]);
+        $group_test->add($group[$key]);
       }
-      $this->addTestCase($group_test);
+      $this->add($group_test);
     }
   }
 

=== modified file 'modules/simpletest/drupal_web_test_case.php'
--- modules/simpletest/drupal_web_test_case.php	2008-06-06 10:36:43 +0000
+++ modules/simpletest/drupal_web_test_case.php	2008-06-07 05:01:08 +0000
@@ -18,6 +18,8 @@ class DrupalWebTestCase extends UnitTest
   protected $curl_options = array();
   protected $db_prefix_original;
   protected $original_file_directory;
+  protected $_size = 0;
+  protected $_already_ran = FALSE;
 
   /**
    * Retrieve the test information from getInfo().
@@ -296,13 +298,13 @@ class DrupalWebTestCase extends UnitTest
   }
 
   /**
-   * Generates a random database prefix, runs the install scripts on the 
-   * prefixed database and enable the specified modules. After installation 
-   * many caches are flushed and the internal browser is setup so that the 
-   * page requests will run on the new prefix. A temporary files directory 
+   * Generates a random database prefix, runs the install scripts on the
+   * prefixed database and enable the specified modules. After installation
+   * many caches are flushed and the internal browser is setup so that the
+   * page requests will run on the new prefix. A temporary files directory
    * is created with the same name as the database prefix.
    *
-   * @param ... 
+   * @param ...
    *   List of modules to enable for the duration of the test.
    */
   function setUp() {
@@ -558,7 +560,7 @@ class DrupalWebTestCase extends UnitTest
           }
           $out = $this->curlExec(array(CURLOPT_URL => $action, CURLOPT_POST => TRUE, CURLOPT_POSTFIELDS => $post));
           // Ensure that any changes to variables in the other thread are picked up.
-          $this->refreshVariables(); 
+          $this->refreshVariables();
           return $out;
         }
       }
@@ -576,15 +578,15 @@ class DrupalWebTestCase extends UnitTest
    * 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) {

=== modified file 'modules/simpletest/simpletest.module'
--- modules/simpletest/simpletest.module	2008-05-10 07:46:22 +0000
+++ modules/simpletest/simpletest.module	2008-06-07 04:06:43 +0000
@@ -82,24 +82,23 @@ function simpletest_entrypoint() {
   simpletest_load();
   drupal_add_css(drupal_get_path('module', 'simpletest') . '/simpletest.css', 'module');
   drupal_add_js(drupal_get_path('module', 'simpletest') . '/simpletest.js', 'module');
+
   $output = drupal_get_form('simpletest_overview_form');
 
-  if (simpletest_running_output()) {
-    return simpletest_running_output() . $output;
+  if (isset($_SESSION['simpletest_reporter'])) {
+    $reporter = $_SESSION['simpletest_reporter'];
+    if (is_string($reporter)) {
+      $reporter = unserialize($_SESSION['simpletest_reporter']);
+    }
+    $output = $reporter->getOutput() . drupal_get_form('simpletest_overview_form');
+    unset($_SESSION['simpletest_reporter']);
+    return $output;
   }
   else {
     return $output;
   }
 }
 
-function simpletest_running_output($output = NULL) {
-  static $o;
-  if ($output != NULL) {
-    $o = $output;
-  }
-  return $o;
-}
-
 /**
  * Form callback;  make the form to run tests
  */
@@ -260,9 +259,10 @@ function simpletest_compare_instances(&$
 function simpletest_run_selected_tests($form, &$form_state) {
   $form_state['redirect'] = FALSE;
   $output = '';
+  $batch_mode = !preg_match("/^simpletest\d+$/", $_SERVER['HTTP_USER_AGENT']);
   switch ($form_state['values']['running_options']) {
     case 'all_tests':
-      $output = simpletest_run_tests();
+      $output = simpletest_run_tests(NULL, 'drupal', $batch_mode);
       break;
     case 'selected_tests':
       $tests_list = array();
@@ -272,7 +272,7 @@ function simpletest_run_selected_tests($
         }
       }
       if (count($tests_list) > 0 ) {
-        $output = simpletest_run_tests($tests_list);
+        $output = simpletest_run_tests($tests_list, 'drupal', $batch_mode);
         break;
       }
       // Fall through
@@ -280,7 +280,6 @@ function simpletest_run_selected_tests($
       drupal_set_message(t('No test has been selected.'), 'error');
   }
 
-  simpletest_running_output($output);
   return FALSE;
 }
 
@@ -380,14 +379,19 @@ function simpletest_clean_temporary_dire
 
 /**
  * Actually runs tests
- * @param array $test_list list of tests to run or DEFAULT NULL run all tests
- * @param boolean $html_reporter TRUE if you want results in simple html, FALSE for full drupal page
+ * @param $test_list
+ *   List of tests to run or NULL to run all tests. Defaults to NULL.
+ * @param $reporter
+ *   Which reporter to use. Allowed values are: text, xml, html and drupal,
+ *   drupal being the default.
+ * @param $batch_mode
+ *   Whether to use the batch API or not.
  */
-function simpletest_run_tests($test_list = NULL, $reporter = 'drupal') {
+function simpletest_run_tests($test_list = NULL, $reporter = 'drupal', $batch_mode = FALSE) {
   static $test_running;
   if (!$test_running) {
     $test_running = TRUE;
-    $test = simpletest_get_total_test($test_list);
+    $tests = simpletest_get_total_test($test_list);
     switch ($reporter) {
       case 'text':
         $reporter = &new TextReporter();
@@ -404,17 +408,76 @@ function simpletest_run_tests($test_list
     }
 
     cache_clear_all();
-    $results = $test->run($reporter);
-    $test_running = FALSE;
 
-    switch (get_class($reporter)) {
-      case 'TextReporter':
-      case 'XMLReporter':
-      case 'HtmlReporter':
-        return $results;
-      case 'DrupalReporter':
-        return $reporter->getOutput();
+    if ($batch_mode) {
+      $batch = array(
+        'title' => t('Running SimpleTests'),
+        'operations' => array(
+          array('_simpletest_batch_operation', array(serialize($tests), serialize($reporter))),
+        ),
+        'finished' => '_simpletest_batch_finished',
+        'redirect' => 'admin/build/testing',
+        'progress_message' => t('Processing tests.'),
+        'init_message' => t('SimpleTest is initializing...') . ' ' . format_plural($tests->getSize(), "one test case will run.", "@count test cases will run."),
+      );
+      batch_set($batch);
+      return;
     }
+    else {
+      $results = $tests->run($reporter);
+      $test_running = FALSE;
+      if (get_class($reporter) == 'DrupalReporter') {
+        $_SESSION['simpletest_reporter'] = $reporter;
+      }
+      return $results;
+    }
+  }
+}
+
+/**
+ * Batch operation callback.
+ */
+function _simpletest_batch_operation($tests_init, $reporter_init, &$context) {
+  // Ensure that all classes are loaded.
+  simpletest_get_total_test();
+  $reporter = unserialize(isset($context['sandbox']['reporter']) ? $context['sandbox']['reporter'] : $reporter_init);
+  $tests = unserialize( isset($context['sandbox']['tests']) ? $context['sandbox']['tests'] : $tests_init);
+
+  if (!isset($context['sandbox']['progress'])) {
+    $context['sandbox']['progress'] = 0;
+    $context['sandbox']['max'] = $tests->getSize();
+  }
+
+  $test_ran = $tests->runOneByOne($reporter);
+  $context['sandbox']['progress']++;
+
+  $size = $tests->getSize();
+  $context['message'] = t('Processed test %test (remaining: @count of @max)', array('%test' => $test_ran, '@count' => $size, '@max' => $context['sandbox']['max']));
+
+  // Put back the reporter and tests
+  $context['sandbox']['reporter'] = serialize($reporter);
+  $context['sandbox']['tests'] = serialize($tests);
+
+  // Multistep processing: report progress.
+  if ($size) {
+    $context['finished'] = 1 - $size / $context['sandbox']['max'];
+  }
+  else {
+    // Finished: save the reporter
+    $context['results'][] = $context['sandbox']['reporter'];
+  }
+}
+
+/**
+ * Batch finished callback.
+ */
+function _simpletest_batch_finished($success, $results, $operations) {
+  $_SESSION['simpletest_reporter'] = array_pop($results);
+  if ($success) {
+    drupal_set_message(t('The tests have finished running.'));
+  }
+  else {
+    drupal_set_message(t('The tests did not successfully finish.'), 'error');
   }
 }
 
@@ -473,5 +536,4 @@ function simpletest_settings() {
   );
 
   return system_settings_form($form);
-
 }

