Index: modules/simpletest/drupal_web_test_case.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/drupal_web_test_case.php,v
retrieving revision 1.235
diff -u -p -r1.235 drupal_web_test_case.php
--- modules/simpletest/drupal_web_test_case.php	19 Sep 2010 18:39:18 -0000	1.235
+++ modules/simpletest/drupal_web_test_case.php	19 Sep 2010 23:04:28 -0000
@@ -432,7 +432,7 @@ abstract class DrupalTestCase {
   /**
    * Run all tests in this class.
    */
-  public function run() {
+  public function run(array $methods = array()) {
     // Initialize verbose debugging.
     simpletest_verbose(NULL, variable_get('file_public_path', conf_path() . '/files'), get_class($this));
 
@@ -447,10 +447,15 @@ abstract class DrupalTestCase {
 
     set_error_handler(array($this, 'errorHandler'));
     $class = get_class($this);
-    // Iterate through all the methods in this class.
-    foreach (get_class_methods($class) as $method) {
+    // Iterate through all the methods in this class, unless as specific list
+    // of methods to run was passed.
+    $class_methods = get_class_methods($class);
+    if ($methods) {
+      $class_methods = array_intersect($class_methods, $methods);
+    }
+    foreach ($class_methods as $method) {
       // If the current method starts with "test", run it - it's a test.
-      if (strtolower(substr($method, 0, 4)) == 'test') {
+      if (substr($method, 0, 4) == 'test') {
         // Insert a fail record. This will be deleted on completion to ensure
         // that testing completed.
         $method_info = new ReflectionMethod($class, $method);
Index: modules/simpletest/simpletest.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/simpletest.module,v
retrieving revision 1.93
diff -u -p -r1.93 simpletest.module
--- modules/simpletest/simpletest.module	1 Sep 2010 20:08:17 -0000	1.93
+++ modules/simpletest/simpletest.module	19 Sep 2010 23:47:50 -0000
@@ -123,9 +123,11 @@ function _simpletest_format_summary_line
  * @param $reporter
  *   Which reporter to use. Allowed values are: text, xml, html and drupal,
  *   drupal being the default.
+ * @param $failed_methods
+ *   An associative array whose keys are class names and whose values are the
+ *   names of methods that failed. If passed, only these methods are re-run.
  */
-function simpletest_run_tests($test_list, $reporter = 'drupal') {
-  cache_clear_all();
+function simpletest_run_tests(array $test_list, $reporter = 'drupal', array $failed_methods = array()) {
   $test_id = db_insert('simpletest_test_id')
     ->useDefaults(array('test_id'))
     ->execute();
@@ -134,15 +136,14 @@ function simpletest_run_tests($test_list
   file_unmanaged_delete_recursive('public://simpletest/verbose');
 
   // Get the info for the first test being run.
-  $first_test = array_shift($test_list);
+  $first_test = reset($test_list);
   $first_instance = new $first_test();
-  array_unshift($test_list, $first_test);
   $info = $first_instance->getInfo();
 
   $batch = array(
     'title' => t('Running tests'),
     'operations' => array(
-      array('_simpletest_batch_operation', array($test_list, $test_id)),
+      array('_simpletest_batch_operation', array($test_list, $test_id, $failed_methods)),
     ),
     'finished' => '_simpletest_batch_finished',
     'progress_message' => '',
@@ -159,7 +160,7 @@ function simpletest_run_tests($test_list
 /**
  * Batch operation callback.
  */
-function _simpletest_batch_operation($test_list_init, $test_id, &$context) {
+function _simpletest_batch_operation($test_list_init, $test_id, $failed_methods, &$context) {
   // Get working values.
   if (!isset($context['sandbox']['max'])) {
     // First iteration: initialize working values.
@@ -177,7 +178,12 @@ function _simpletest_batch_operation($te
   // Perform the next test.
   $test_class = array_shift($test_list);
   $test = new $test_class($test_id);
-  $test->run();
+  if ($failed_methods) {
+    $test->run($failed_methods[$test_class]);
+  }
+  else {
+    $test->run();
+  }
   $size = count($test_list);
   $info = $test->getInfo();
 
Index: modules/simpletest/simpletest.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/simpletest.pages.inc,v
retrieving revision 1.31
diff -u -p -r1.31 simpletest.pages.inc
--- modules/simpletest/simpletest.pages.inc	24 Jul 2010 17:28:26 -0000	1.31
+++ modules/simpletest/simpletest.pages.inc	20 Sep 2010 00:08:26 -0000
@@ -183,16 +183,20 @@ function _simpletest_sort_by_title($a, $
  */
 function simpletest_test_form_submit($form, &$form_state) {
   // Get list of tests.
-  $tests_list = array();
+  $test_list = array();
   foreach ($form_state['values'] as $class_name => $value) {
     // Since class_exists() will likely trigger an autoload lookup,
     // we do the fast check first.
     if ($value === 1 && class_exists($class_name)) {
-      $tests_list[] = $class_name;
+      $test_list[] = $class_name;
     }
   }
-  if (count($tests_list) > 0 ) {
-    $test_id = simpletest_run_tests($tests_list, 'drupal');
+  if (count($test_list) > 0 ) {
+    $methods = array();
+    if (isset($form_state['values']['failed_methods'])) {
+      $methods = $form_state['values']['failed_methods'];
+    }
+    $test_id = simpletest_run_tests($test_list, 'drupal', $methods);
     $form_state['redirect'] = 'admin/config/development/testing/results/' . $test_id;
   }
   else {
@@ -212,6 +216,9 @@ function simpletest_result_form($form, &
     return $form;
   }
 
+  // The result data for this form is volatile, so this form needs to be cached.
+  $form_state['cache'] = TRUE;
+
   // Load all classes and include CSS.
   drupal_add_css(drupal_get_path('module', 'simpletest') . '/simpletest.css');
 
@@ -237,8 +244,10 @@ function simpletest_result_form($form, &
   // Cycle through each test group.
   $header = array(t('Message'), t('Group'), t('Filename'), t('Line'), t('Function'), array('colspan' => 2, 'data' => t('Status')));
   $form['result']['results'] = array();
+  $failed_methods = array();
   foreach ($results as $group => $assertions) {
     // Create group fieldset with summary information.
+    $class_info = new ReflectionClass($group);
     $info = call_user_func(array($group, 'getInfo'));
     $form['result']['results'][$group] = array(
       '#type' => 'fieldset',
@@ -268,6 +277,14 @@ function simpletest_result_form($form, &
 
       $group_summary['#' . $assertion->status]++;
       $form['result']['summary']['#' . $assertion->status]++;
+
+      if ($assertion->status == 'fail' || $assertion->status == 'exception') {
+        preg_match('@->(\w+)\(@', $assertion->function, $matches);
+        $method = $matches[1];
+        if ($class_info->hasMethod($method)) {
+          $failed_methods[$group][$method] = $method;
+        }
+      }
     }
     $form['result']['results'][$group]['table'] = array(
       '#theme' => 'table',
@@ -302,18 +319,23 @@ function simpletest_result_form($form, &
       'all' => t('All (@count)', array('@count' => count($filter['pass']) + count($filter['fail']))),
       'pass' => t('Pass (@count)', array('@count' => count($filter['pass']))),
       'fail' => t('Fail (@count)', array('@count' => count($filter['fail']))),
+      'fail_methods' => t('Failed methods only (@count)', array('@count' => count($failed_methods))),
     ),
+    '#default_value' => $failed_methods ? 'fail_methods' : 'all',
   );
-  $form['action']['filter']['#default_value'] = ($filter['fail'] ? 'fail' : 'all');
 
-  // Catagorized test classes for to be used with selected filter value.
+  // Categorized test classes for to be used with selected filter value.
   $form['action']['filter_pass'] = array(
-    '#type' => 'hidden',
-    '#default_value' => implode(',', $filter['pass']),
+    '#type' => 'value',
+    '#value' => $filter['pass'],
   );
   $form['action']['filter_fail'] = array(
-    '#type' => 'hidden',
-    '#default_value' => implode(',', $filter['fail']),
+    '#type' => 'value',
+    '#value' => $filter['fail'],
+  );
+  $form['action']['failed_methods'] = array(
+    '#type' => 'value',
+    '#value' => $failed_methods,
   );
 
   $form['action']['op'] = array(
@@ -338,8 +360,8 @@ function simpletest_result_form($form, &
  * Re-run the tests that match the filter.
  */
 function simpletest_result_form_submit($form, &$form_state) {
-  $pass = $form_state['values']['filter_pass'] ? explode(',', $form_state['values']['filter_pass']) : array();
-  $fail = $form_state['values']['filter_fail'] ? explode(',', $form_state['values']['filter_fail']) : array();
+  $pass = $form_state['values']['filter_pass'];
+  $fail = $form_state['values']['filter_fail'];
 
   if ($form_state['values']['filter'] == 'all') {
     $classes = array_merge($pass, $fail);
@@ -360,6 +382,9 @@ function simpletest_result_form_submit($
   foreach ($classes as $class) {
     $form_state_execute['values'][$class] = 1;
   }
+  if ($form_state['values']['filter'] == 'fail_methods') {
+    $form_state_execute['values']['failed_methods'] = $form_state['values']['failed_methods'];;
+  }
 
   simpletest_test_form_submit(array(), $form_state_execute);
   $form_state['redirect'] = $form_state_execute['redirect'];
