? patches
Index: drupal_web_test_case.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/simpletest/Attic/drupal_web_test_case.php,v
retrieving revision 1.2.2.3.2.59
diff -u -p -r1.2.2.3.2.59 drupal_web_test_case.php
--- drupal_web_test_case.php	1 Dec 2010 06:55:26 -0000	1.2.2.3.2.59
+++ drupal_web_test_case.php	29 Jan 2011 17:13:17 -0000
@@ -441,8 +441,17 @@ abstract class DrupalTestCase {
 
   /**
    * Run all tests in this class.
+   *
+   * Regardless of whether $methods are passed or not, only method names
+   * starting with "test" are executed.
+   *
+   * @param $methods
+   *   (optional) A list of method names in the test case class to run; e.g.,
+   *   array('testFoo', 'testBar'). By default, all methods of the class are
+   *   taken into account, but it can be useful to only run a few selected test
+   *   methods during debugging.
    */
-  public function run() {
+  public function run(array $methods = array()) {
     // Initialize verbose debugging.
     simpletest_verbose(NULL, variable_get('file_directory_path', conf_path() . '/files'), get_class($this));
 
@@ -457,8 +466,13 @@ 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 a 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') {
         // Insert a fail record. This will be deleted on completion to ensure
@@ -572,6 +586,55 @@ abstract class DrupalTestCase {
     }
     return $str;
   }
+
+  /**
+   * Converts a list of possible parameters into a stack of permutations.
+   *
+   * Takes a list of parameters containing possible values, and converts all of
+   * them into a list of items containing every possible permutation.
+   *
+   * Example:
+   * @code
+   * $parameters = array(
+   *   'one' => array(0, 1),
+   *   'two' => array(2, 3),
+   * );
+   * $permutations = $this->permute($parameters);
+   * // Result:
+   * $permutations == array(
+   *   array('one' => 0, 'two' => 2),
+   *   array('one' => 1, 'two' => 2),
+   *   array('one' => 0, 'two' => 3),
+   *   array('one' => 1, 'two' => 3),
+   * )
+   * @endcode
+   *
+   * @param $parameters
+   *   An associative array of parameters, keyed by parameter name, and whose
+   *   values are arrays of parameter values.
+   *
+   * @return
+   *   A list of permutations, which is an array of arrays. Each inner array
+   *   contains the full list of parameters that have been passed, but with a
+   *   single value only.
+   */
+  public static function generatePermutations($parameters) {
+    $all_permutations = array(array());
+    foreach ($parameters as $parameter => $values) {
+      $new_permutations = array();
+      // Iterate over all values of the parameter.
+      foreach ($values as $value) {
+        // Iterate over all existing permutations.
+        foreach ($all_permutations as $permutation) {
+          // Add the new parameter value to existing permutations.
+          $new_permutations[] = $permutation + array($parameter => $value);
+        }
+      }
+      // Replace the old permutations with the new permutations.
+      $all_permutations = $new_permutations;
+    }
+    return $all_permutations;
+  }
 }
 
 /**
@@ -979,7 +1042,7 @@ class DrupalWebTestCase extends DrupalTe
     $edit['pass']   = user_password();
     $edit['status'] = 1;
 
-    $account = user_save('', $edit);
+    $account = user_save(NULL, $edit);
 
     $this->assertTrue(!empty($account->uid), t('User created with name %name and pass %pass', array('%name' => $edit['name'], '%pass' => $edit['pass'])), t('User login'));
     if (empty($account->uid)) {
@@ -1651,7 +1716,7 @@ class DrupalWebTestCase extends DrupalTe
         $post = array();
         $upload = array();
         $submit_matches = $this->handleForm($post, $edit, $upload, $submit, $form);
-        $action = isset($form['action']) ? $this->getAbsoluteUrl($form['action']) : $this->getUrl();
+        $action = isset($form['action']) ? $this->getAbsoluteUrl((string) $form['action']) : $this->getUrl();
 
         // We post only if we managed to handle every field in edit and the
         // submit button matches.
Index: run-tests.sh
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/simpletest/Attic/run-tests.sh,v
retrieving revision 1.1.2.9
diff -u -p -r1.1.2.9 run-tests.sh
--- run-tests.sh	1 Dec 2010 06:55:26 -0000	1.1.2.9
+++ run-tests.sh	29 Jan 2011 17:13:17 -0000
@@ -41,7 +41,7 @@ if ($args['clean']) {
 
   // Get the status messages and print them.
   $messages = array_pop(drupal_get_messages('status'));
-  foreach($messages as $text) {
+  foreach ($messages as $text) {
     echo " - " . $text . "\n";
   }
   exit;
@@ -88,9 +88,16 @@ simpletest_script_command($args['concurr
 list($last_prefix, $last_test_class) = simpletest_last_test_get($test_id);
 simpletest_log_read($test_id, $last_prefix, $last_test_class);
 
+// Stop the timer.
+simpletest_script_reporter_timer_stop();
+
 // Display results before database is cleared.
 simpletest_script_reporter_display_results();
 
+if ($args['xml']) {
+  simpletest_script_reporter_write_xml_results();
+}
+
 // Cleanup our test results.
 simpletest_clean_results_table($test_id);
 
@@ -135,7 +142,11 @@ All arguments are long options.
   --file      Run tests identified by specific file names, instead of group names.
               Specify the path and the extension (i.e. 'modules/user/user.test').
 
-  --color     Output the results with color highlighting.
+  --xml       <path>
+
+              If provided, test results will be written as xml files to this path.
+
+  --color     Output text format results with color highlighting.
 
   --verbose   Output detailed assertion messages in addition to summary.
 
@@ -184,7 +195,8 @@ function simpletest_script_parse_args() 
     'test_names' => array(),
     // Used internally.
     'test-id' => NULL,
-    'execute-batch' => FALSE
+    'execute-batch' => FALSE,
+    'xml' => '',
   );
 
   // Override with set values.
@@ -264,7 +276,7 @@ function simpletest_script_init($server_
   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'] : '';
   }
 
   $_SERVER['HTTP_HOST'] = $host;
@@ -279,7 +291,6 @@ function simpletest_script_init($server_
   $_SERVER['HTTP_USER_AGENT'] = 'Drupal command line';
 
   chdir(realpath(dirname(__FILE__) . '/..'));
-  define('DRUPAL_ROOT', getcwd());
   require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
 }
 
@@ -289,7 +300,7 @@ function simpletest_script_init($server_
 function simpletest_script_execute_batch() {
   global $args;
 
-  if (is_null($args['test-id'])) {
+  if (!isset($args['test-id'])) {
     simpletest_script_print_error("--execute-batch should not be called interactively.");
     exit;
   }
@@ -424,7 +435,7 @@ function simpletest_script_get_test_list
       // Check for valid group names and get all valid classes in group.
       foreach ($args['test_names'] as $group_name) {
         if (isset($groups[$group_name])) {
-          foreach($groups[$group_name] as $class_name => $info) {
+          foreach ($groups[$group_name] as $class_name => $info) {
             $test_list[] = $class_name;
           }
         }
@@ -443,7 +454,13 @@ function simpletest_script_get_test_list
  * Initialize the reporter.
  */
 function simpletest_script_reporter_init() {
-  global $args, $all_tests, $test_list;
+  global $args, $all_tests, $test_list, $results_map;
+
+  $results_map = array(
+    'pass' => 'Pass',
+    'fail' => 'Fail',
+    'exception' => 'Exception'
+  );
 
   echo "\n";
   echo "Drupal test run\n";
@@ -473,15 +490,90 @@ function simpletest_script_reporter_init
 }
 
 /**
- * Display test results.
+ * Display jUnit XML test results.
  */
-function simpletest_script_reporter_display_results() {
+function simpletest_script_reporter_write_xml_results() {
   global $args, $test_id, $results_map;
 
+  $results = db_query("SELECT * FROM {simpletest} WHERE test_id = %d ORDER BY test_class, message_id", $test_id);
+
+  $test_class = '';
+  $xml_files = array();
+
+  while ($result = db_fetch_object($results)) {
+    if (isset($results_map[$result->status])) {
+      if ($result->test_class != $test_class) {
+        // We've moved onto a new class, so write the last classes results to a file:
+        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);
+        }
+      }
+
+      // For convenience:
+      $dom_document = &$xml_files[$test_class]['doc'];
+
+      // Create the XML element for this test case:
+      $case = $dom_document->createElement('testcase');
+      $case->setAttribute('classname', $test_class);
+      list($class, $name) = explode('->', $result->function, 2);
+      $case->setAttribute('name', $name);
+
+      // Passes get no further attention, but failures and exceptions get to add more detail:
+      if ($result->status == 'fail') {
+        $fail = $dom_document->createElement('failure');
+        $fail->setAttribute('type', 'failure');
+        $fail->setAttribute('message', $result->message_group);
+        $text = $dom_document->createTextNode($result->message);
+        $fail->appendChild($text);
+        $case->appendChild($fail);
+      }
+      elseif ($result->status == 'exception') {
+        // In the case of an exception the $result->function may not be a class
+        // method so we record the full function name:
+        $case->setAttribute('name', $result->function);
+
+        $fail = $dom_document->createElement('error');
+        $fail->setAttribute('type', 'exception');
+        $fail->setAttribute('message', $result->message_group);
+        $full_message = $result->message . "\n\nline: " . $result->line . "\nfile: " . $result->file;
+        $text = $dom_document->createTextNode($full_message);
+        $fail->appendChild($text);
+        $case->appendChild($fail);
+      }
+      // Append the test case XML to the test suite:
+      $xml_files[$test_class]['suite']->appendChild($case);
+    }
+  }
+  // The last test case hasn't been saved to a file yet, so do that now:
+  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]);
+  }
+}
+
+/**
+ * Stop the test timer.
+ */
+function simpletest_script_reporter_timer_stop() {
   echo "\n";
   $end = timer_stop('run-tests');
   echo "Test run duration: " . format_interval($end['time'] / 1000);
   echo "\n";
+}
+
+/**
+ * Display test results.
+ */
+function simpletest_script_reporter_display_results() {
+  global $args, $test_id, $results_map;
 
   if ($args['verbose']) {
     // Report results.
@@ -489,14 +581,7 @@ function simpletest_script_reporter_disp
     echo "----------------------\n";
     echo "\n";
 
-    $results_map = array(
-      'pass' => 'Pass',
-      'fail' => 'Fail',
-      'exception' => 'Exception'
-    );
-
     $results = db_query("SELECT * FROM {simpletest} WHERE test_id = %d ORDER BY test_class, message_id", $test_id);
-
     $test_class = '';
     while ($result = db_fetch_object($results)) {
       if (isset($results_map[$result->status])) {
Index: simpletest.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/simpletest/Attic/simpletest.install,v
retrieving revision 1.4.2.3.2.27
diff -u -p -r1.4.2.3.2.27 simpletest.install
--- simpletest.install	1 Dec 2010 06:55:26 -0000	1.4.2.3.2.27
+++ simpletest.install	29 Jan 2011 17:13:17 -0000
@@ -7,9 +7,12 @@
  */
 
 /**
+ * Minimum value of PHP memory_limit for SimpleTest.
+ */
+define('SIMPLETEST_MINIMUM_PHP_MEMORY_LIMIT', '64M');
+
+/**
  * Implements hook_requirements().
- *
- * Check that the cURL extension exists for PHP.
  */
 function simpletest_requirements($phase) {
   $requirements = array();
@@ -27,7 +30,7 @@ function simpletest_requirements($phase)
   );
   if (!$has_curl) {
     $requirements['curl']['severity'] = REQUIREMENT_ERROR;
-    $requirements['curl']['description'] = $t('Simpletest could not be installed because the PHP <a href="@curl_url">cURL</a> library is not available.', array('@curl_url' => 'http://php.net/manual/en/curl.setup.php'));
+    $requirements['curl']['description'] = $t('The testing framework could not be installed because the PHP <a href="@curl_url">cURL</a> library is not available.', array('@curl_url' => 'http://php.net/manual/en/curl.setup.php'));
   }
   $requirements['hash'] = array(
     'title' => $t('hash'),
@@ -35,7 +38,7 @@ function simpletest_requirements($phase)
   );
   if (!$has_hash) {
     $requirements['hash']['severity'] = REQUIREMENT_ERROR;
-    $requirements['hash']['description'] = $t('Simpletest could not be installed because the PHP <a href="@hash_url">hash</a> extension is disabled.', array('@hash_url' => 'http://php.net/manual/en/book.hash.php'));
+    $requirements['hash']['description'] = $t('The testing framework could not be installed because the PHP <a href="@hash_url">hash</a> extension is disabled.', array('@hash_url' => 'http://php.net/manual/en/book.hash.php'));
   }
 
   $requirements['php_domdocument'] = array(
@@ -44,7 +47,7 @@ function simpletest_requirements($phase)
   );
   if (!$has_domdocument) {
     $requirements['php_domdocument']['severity'] = REQUIREMENT_ERROR;
-    $requirements['php_domdocument']['description'] = $t('SimpleTest requires the DOMDocument class to be available. Check the configure command at the <a href="@link-phpinfo">PHP info page</a>.', array('@link-phpinfo' => url('admin/reports/status/php')));
+    $requirements['php_domdocument']['description'] = $t('The testing framework requires the DOMDocument class to be available. Check the configure command at the <a href="@link-phpinfo">PHP info page</a>.', array('@link-phpinfo' => url('admin/reports/status/php')));
   }
 
   // SimpleTest currently needs 2 cURL options which are incompatible with
@@ -59,6 +62,14 @@ function simpletest_requirements($phase)
     $requirements['php_open_basedir']['description'] = $t('The testing framework requires the PHP <a href="@open_basedir-url">open_basedir</a> restriction to be disabled. Check your webserver configuration or contact your web host.', array('@open_basedir-url' => 'http://php.net/manual/en/ini.core.php#ini.open-basedir'));
   }
 
+  // Check the current memory limit. If it is set too low, SimpleTest will fail
+  // to load all tests and throw a fatal error.
+  $memory_limit = ini_get('memory_limit');
+  if ($memory_limit && $memory_limit != -1 && parse_size($memory_limit) < parse_size(SIMPLETEST_MINIMUM_PHP_MEMORY_LIMIT)) {
+    $requirements['php_memory_limit']['severity'] = REQUIREMENT_ERROR;
+    $requirements['php_memory_limit']['description'] = t('The testing framework requires the PHP memory limit to be at least %memory_minimum_limit. The current value is %memory_limit. <a href="@url">Follow these steps to continue</a>.', array('%memory_limit' => $memory_limit, '%memory_minimum_limit' => SIMPLETEST_MINIMUM_PHP_MEMORY_LIMIT, '@url' => 'http://drupal.org/node/207036'));
+  }
+
   // Check that the global variable is defined signifying that the patch
   // was correctly applied to the Drupal 6 core.
   $requirements['simpletest_patch'] = array(
Index: simpletest.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/simpletest/Attic/simpletest.module,v
retrieving revision 1.33.2.4.2.29
diff -u -p -r1.33.2.4.2.29 simpletest.module
--- simpletest.module	1 Dec 2010 06:55:26 -0000	1.33.2.4.2.29
+++ simpletest.module	29 Jan 2011 17:13:17 -0000
@@ -55,7 +55,6 @@ function simpletest_menu() {
     'page arguments' => array('simpletest_result_form', 4),
     'description' => 'View result of tests.',
     'access arguments' => array('administer unit tests'),
-    'type' => MENU_CALLBACK,
     'file' => 'simpletest.pages.inc',
   );
   return $items;

