Index: D6-core-simpletest.patch
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/simpletest/Attic/D6-core-simpletest.patch,v
retrieving revision 1.1.2.9
diff -u -p -r1.1.2.9 D6-core-simpletest.patch
--- D6-core-simpletest.patch	27 Jan 2010 01:35:47 -0000	1.1.2.9
+++ D6-core-simpletest.patch	22 Aug 2010 23:46:58 -0000
@@ -1,93 +1,199 @@
+Index: install.php
+===================================================================
+RCS file: /cvs/drupal/drupal/install.php,v
+retrieving revision 1.113.2.12
+diff -u -p -r1.113.2.12 install.php
+--- install.php	9 May 2010 14:13:31 -0000	1.113.2.12
++++ install.php	22 Aug 2010 23:34:33 -0000
+@@ -20,6 +20,14 @@ function install_main() {
+   require_once './includes/bootstrap.inc';
+   drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);
+ 
++  // The user agent header is used to pass a database prefix in the request when
++  // running tests. However, for security reasons, it is imperative that no
++  // installation be permitted using such a prefix.
++  if (isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], "simpletest") !== FALSE) {
++    header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden');
++    exit;
++  }
++
+   // This must go after drupal_bootstrap(), which unsets globals!
+   global $profile, $install_locale, $conf;
+ 
 Index: includes/bootstrap.inc
 ===================================================================
 RCS file: /cvs/drupal/drupal/includes/bootstrap.inc,v
-retrieving revision 1.206.2.13
-diff -u -r1.206.2.13 bootstrap.inc
---- includes/bootstrap.inc	14 Sep 2009 13:33:39 -0000	1.206.2.13
-+++ includes/bootstrap.inc	27 Jan 2010 01:32:00 -0000
-@@ -991,7 +991,7 @@
+retrieving revision 1.206.2.29
+diff -u -p -r1.206.2.29 bootstrap.inc
+--- includes/bootstrap.inc	6 Aug 2010 11:50:24 -0000	1.206.2.29
++++ includes/bootstrap.inc	22 Aug 2010 23:34:34 -0000
+@@ -318,7 +318,7 @@ function timer_stop($name) {
+  *   The path of the matching directory.
+  */
+ function conf_path($require_settings = TRUE, $reset = FALSE) {
+-  static $conf = '';
++  $conf = &drupal_static(__FUNCTION__, '');
+ 
+   if ($conf && !$reset) {
+     return $conf;
+@@ -947,23 +947,35 @@ function request_uri() {
+ function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NOTICE, $link = NULL) {
+   global $user, $base_root;
+ 
+-  // Prepare the fields to be logged
+-  $log_message = array(
+-    'type'        => $type,
+-    'message'     => $message,
+-    'variables'   => $variables,
+-    'severity'    => $severity,
+-    'link'        => $link,
+-    'user'        => $user,
+-    'request_uri' => $base_root . request_uri(),
+-    'referer'     => referer_uri(),
+-    'ip'          => ip_address(),
+-    'timestamp'   => time(),
+-    );
+-
+-  // Call the logging hooks to log/process the message
+-  foreach (module_implements('watchdog') as $module) {
+-    module_invoke($module, 'watchdog', $log_message);
++  static $in_error_state = FALSE;
++
++  // It is possible that the error handling will itself trigger an error. In that case, we could
++  // end up in an infinite loop. To avoid that, we implement a simple static semaphore.
++  if (!$in_error_state && function_exists('module_implements')) {
++    $in_error_state = TRUE;
++
++    // Prepare the fields to be logged
++    $log_message = array(
++      'type'        => $type,
++      'message'     => $message,
++      'variables'   => $variables,
++      'severity'    => $severity,
++      'link'        => $link,
++      'user'        => $user,
++      'request_uri' => $base_root . request_uri(),
++      'referer'     => referer_uri(),
++      'ip'          => ip_address(),
++      'timestamp'   => time(),
++      );
++
++    // Call the logging hooks to log/process the message
++    foreach (module_implements('watchdog') as $module) {
++      module_invoke($module, 'watchdog', $log_message);
++    }
++
++    // It is critical that the semaphore is only cleared here, in the parent
++    // watchdog() call (not outside the loop), to prevent recursive execution.
++    $in_error_state = FALSE;
+   }
  }
  
- function _drupal_bootstrap($phase) {
--  global $conf;
-+  global $conf, $db_prefix;
+@@ -1100,15 +1112,35 @@ function drupal_anonymous_user($session 
+  *     DRUPAL_BOOTSTRAP_LANGUAGE: identify the language used on the page.
+  *     DRUPAL_BOOTSTRAP_PATH: set $_GET['q'] to Drupal path of request.
+  *     DRUPAL_BOOTSTRAP_FULL: Drupal is fully loaded, validate and fix input data.
++ *
++ * @return
++ *   The most recently completed phase.
+  */
+-function drupal_bootstrap($phase) {
+-  static $phases = array(DRUPAL_BOOTSTRAP_CONFIGURATION, DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE, DRUPAL_BOOTSTRAP_DATABASE, DRUPAL_BOOTSTRAP_ACCESS, DRUPAL_BOOTSTRAP_SESSION, DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE, DRUPAL_BOOTSTRAP_LANGUAGE, DRUPAL_BOOTSTRAP_PATH, DRUPAL_BOOTSTRAP_FULL), $phase_index = 0;
++function drupal_bootstrap($phase = NULL) {
++  // Not drupal_static(), because does not depend on any run-time information.
++  static $phases = array(
++    DRUPAL_BOOTSTRAP_CONFIGURATION,
++    DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE,
++    DRUPAL_BOOTSTRAP_DATABASE,
++    DRUPAL_BOOTSTRAP_ACCESS,
++    DRUPAL_BOOTSTRAP_SESSION,
++    DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE,
++    DRUPAL_BOOTSTRAP_LANGUAGE,
++    DRUPAL_BOOTSTRAP_PATH,
++    DRUPAL_BOOTSTRAP_FULL,
++  );
++  static $phase_index = 0;
++  // Not drupal_static(), because it's impossible to roll back to an earlier
++  // bootstrap state.
++  static $current_phase = -1;
  
+   while ($phase >= $phase_index && isset($phases[$phase_index])) {
+     $current_phase = $phases[$phase_index];
+     unset($phases[$phase_index++]);
+     _drupal_bootstrap($current_phase);
+   }
++
++  return $current_phase;
+ }
+ 
+ function _drupal_bootstrap($phase) {
+@@ -1117,6 +1149,13 @@ function _drupal_bootstrap($phase) {
    switch ($phase) {
  
-@@ -1017,6 +1017,18 @@
+     case DRUPAL_BOOTSTRAP_CONFIGURATION:
++      // Include backported functions from Drupal 7 required for SimpleTest.
++      require_once './includes/simpletest.inc';
++
++      // Set the Drupal custom error handler.
++      set_error_handler('_drupal_error_handler');
++      set_exception_handler('_drupal_exception_handler');
++
+       drupal_unset_globals();
+       // Start a page timer:
+       timer_start('page');
+@@ -1138,6 +1177,26 @@ function _drupal_bootstrap($phase) {
        break;
  
      case DRUPAL_BOOTSTRAP_DATABASE:
 +      // The user agent header is used to pass a database prefix in the request when
 +      // running tests. However, for security reasons, it is imperative that we
 +      // validate we ourselves made the request.
-+      $GLOBALS['simpletest_installed'] = TRUE;
 +      if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match("/^(simpletest\d+);/", $_SERVER['HTTP_USER_AGENT'], $matches)) {
 +        if (!drupal_valid_test_ua($_SERVER['HTTP_USER_AGENT'])) {
-+          header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden');
++          header('HTTP/1.1 403 Forbidden');
 +          exit;
 +        }
-+        $db_prefix .= $matches[1];
++
++        // The first part of the user agent is the prefix itself.
++        $test_prefix = $matches[1];
++
++        // Set the test run id for use in other parts of Drupal.
++        $test_info = &$GLOBALS['drupal_test_info'];
++        $test_info['test_run_id'] = $test_prefix;
++        $test_info['in_child_site'] = TRUE;
++
++        $GLOBALS['db_prefix'] = $test_prefix;
 +      }
 +
        // Initialize the default database.
        require_once './includes/database.inc';
        db_set_active();
-@@ -1205,3 +1217,46 @@
+@@ -1254,7 +1313,7 @@ function drupal_init_language() {
+  * @param $reset Boolean to request a reset of the list.
+  */
+ function language_list($field = 'language', $reset = FALSE) {
+-  static $languages = NULL;
++  $languages = &drupal_static(__FUNCTION__);
  
-   return $ip_address;
- }
-+
-+/**
-+ * Validate the HMAC and timestamp of a user agent header from simpletest.
-+ */
-+function drupal_valid_test_ua($user_agent) {
-+//  global $dbatabases;
-+  global $db_url;
-+
-+  list($prefix, $time, $salt, $hmac) = explode(';', $user_agent);
-+  $check_string =  $prefix . ';' . $time . ';' . $salt;
-+  // We use the database credentials from settings.php to make the HMAC key, since
-+  // the database is not yet initialized and we can't access any Drupal variables.
-+  // The file properties add more entropy not easily accessible to others.
-+//  $filepath = DRUPAL_ROOT . '/includes/bootstrap.inc';
-+  $filepath = './includes/bootstrap.inc';
-+//  $key = sha1(serialize($databases) . filectime($filepath) . fileinode($filepath), TRUE);
-+  $key = sha1(serialize($db_url) . filectime($filepath) . fileinode($filepath), TRUE);
-+  // The HMAC must match.
-+  return $hmac == base64_encode(hash_hmac('sha1', $check_string, $key, TRUE));
-+}
-+
-+/**
-+ * Generate a user agent string with a HMAC and timestamp for simpletest.
-+ */
-+function drupal_generate_test_ua($prefix) {
-+//  global $dbatabases;
-+  global $db_url;
-+  static $key;
-+
-+  if (!isset($key)) {
-+    // We use the database credentials to make the HMAC key, since we
-+    // check the HMAC before the database is initialized. filectime()
-+    // and fileinode() are not easily determined from remote.
-+//    $filepath = DRUPAL_ROOT . '/includes/bootstrap.inc';
-+    $filepath = './includes/bootstrap.inc';
-+//    $key = sha1(serialize($databases) . filectime($filepath) . fileinode($filepath), TRUE);
-+    $key = sha1(serialize($db_url) . filectime($filepath) . fileinode($filepath), TRUE);
-+  }
-+   // Generate a moderately secure HMAC based on the database credentials.
-+   $salt = uniqid('', TRUE);
-+   $check_string = $prefix . ';' . time() . ';' . $salt;
-+   return  $check_string . ';' . base64_encode(hash_hmac('sha1', $check_string, $key, TRUE));
-+}
+   // Reset language list
+   if ($reset) {
+@@ -1312,7 +1371,7 @@ function language_default($property = NU
+  *   IP address of client machine, adjusted for reverse proxy.
+  */
+ function ip_address() {
+-  static $ip_address = NULL;
++  $ip_address = &drupal_static(__FUNCTION__);
+ 
+   if (!isset($ip_address)) {
+     $ip_address = $_SERVER['REMOTE_ADDR'];
 Index: includes/common.inc
 ===================================================================
 RCS file: /cvs/drupal/drupal/includes/common.inc,v
-retrieving revision 1.756.2.75
-diff -u -r1.756.2.75 common.inc
---- includes/common.inc	16 Dec 2009 21:14:02 -0000	1.756.2.75
-+++ includes/common.inc	27 Jan 2010 01:32:00 -0000
-@@ -520,7 +520,7 @@
+retrieving revision 1.756.2.97
+diff -u -p -r1.756.2.97 common.inc
+--- includes/common.inc	11 Aug 2010 21:10:41 -0000	1.756.2.97
++++ includes/common.inc	22 Aug 2010 23:34:36 -0000
+@@ -531,7 +531,7 @@ function drupal_http_request($url, $head
    // same time won't interfere with each other as they would if the database
    // prefix were stored statically in a file or database variable.
    if (is_string($db_prefix) && preg_match("/^simpletest\d+$/", $db_prefix, $matches)) {
@@ -96,37 +202,101 @@ diff -u -r1.756.2.75 common.inc
    }
  
    foreach ($headers as $header => $value) {
-@@ -2597,14 +2597,24 @@
+@@ -2660,14 +2660,23 @@ function _drupal_bootstrap_full() {
+   require_once './includes/form.inc';
    require_once './includes/mail.inc';
    require_once './includes/actions.inc';
-   // Set the Drupal custom error handler.
+-  // Set the Drupal custom error handler.
 -  set_error_handler('drupal_error_handler');
-+  set_error_handler('_drupal_error_handler');
-+  set_exception_handler('_drupal_exception_handler');
++  require_once './includes/errors.inc';
++
    // Emit the correct charset HTTP header.
    drupal_set_header('Content-Type: text/html; charset=utf-8');
    // Detect string handling method
    unicode_check();
    // Undo magic quotes
    fix_gpc_magic();
--  // Load all enabled modules
 +
-+  if (isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'simpletest') !== FALSE) {
-+    // Valid SimpleTest user-agent, log fatal errors to test specific file
-+    // directory. The user-agent is validated in DRUPAL_BOOTSTRAP_DATABASE
-+    // phase so as long as it is a SimpleTest user-agent it is valid.
++  $test_info = &$GLOBALS['drupal_test_info'];
++  if (!empty($test_info['in_child_site'])) {
++    // Running inside the simpletest child site, log fatal errors to test
++    // specific file directory.
 +    ini_set('log_errors', 1);
 +    ini_set('error_log', file_directory_path() . '/error.log');
 +  }
 +
-+// Load all enabled modules
+   // Load all enabled modules
    module_load_all();
    // Let all modules take action before menu system handles the request
-   // We do not want this while running update.php.
-@@ -3721,3 +3731,264 @@
-   }
-   variable_set('css_js_query_string', $new_character . substr($string_history, 0, 19));
- }
+@@ -3241,54 +3250,6 @@ function drupal_common_theme() {
+  */
+ 
+ /**
+- * Get the schema definition of a table, or the whole database schema.
+- *
+- * The returned schema will include any modifications made by any
+- * module that implements hook_schema_alter().
+- *
+- * @param $table
+- *   The name of the table. If not given, the schema of all tables is returned.
+- * @param $rebuild
+- *   If true, the schema will be rebuilt instead of retrieved from the cache.
+- */
+-function drupal_get_schema($table = NULL, $rebuild = FALSE) {
+-  static $schema = array();
+-
+-  if (empty($schema) || $rebuild) {
+-    // Try to load the schema from cache.
+-    if (!$rebuild && $cached = cache_get('schema')) {
+-      $schema = $cached->data;
+-    }
+-    // Otherwise, rebuild the schema cache.
+-    else {
+-      $schema = array();
+-      // Load the .install files to get hook_schema.
+-      module_load_all_includes('install');
+-
+-      // Invoke hook_schema for all modules.
+-      foreach (module_implements('schema') as $module) {
+-        $current = module_invoke($module, 'schema');
+-        _drupal_initialize_schema($module, $current);
+-        $schema = array_merge($schema, $current);
+-      }
+-
+-      drupal_alter('schema', $schema);
+-      cache_set('schema', $schema);
+-    }
+-  }
+-
+-  if (!isset($table)) {
+-    return $schema;
+-  }
+-  elseif (isset($schema[$table])) {
+-    return $schema[$table];
+-  }
+-  else {
+-    return FALSE;
+-  }
+-}
+-
+-/**
+  * Create all tables that a module defines in its hook_schema().
+  *
+  * Note: This function does not pass the module's schema through
+Index: includes/errors.inc
+===================================================================
+RCS file: includes/errors.inc
+diff -N includes/errors.inc
+--- /dev/null	1 Jan 1970 00:00:00 -0000
++++ includes/errors.inc	22 Aug 2010 23:34:36 -0000
+@@ -0,0 +1,286 @@
++<?php
++// $Id$
++
++/**
++ * @file
++ * Backport functions from SimpleTest and Drupal 7 core.
++ */
 +
 +/**
 + * Error reporting level: display no errors.
@@ -144,6 +314,37 @@ diff -u -r1.756.2.75 common.inc
 +define('ERROR_REPORTING_DISPLAY_ALL', 2);
 +
 +/**
++ * Map PHP error constants to watchdog severity levels.
++ * The error constants are documented at
++ * http://php.net/manual/en/errorfunc.constants.php
++ */
++function drupal_error_levels() {
++  $types = array(
++    E_ERROR => array('Error', WATCHDOG_ERROR),
++    E_WARNING => array('Warning', WATCHDOG_WARNING),
++    E_PARSE => array('Parse error', WATCHDOG_ERROR),
++    E_NOTICE => array('Notice', WATCHDOG_NOTICE),
++    E_CORE_ERROR => array('Core error', WATCHDOG_ERROR),
++    E_CORE_WARNING => array('Core warning', WATCHDOG_WARNING),
++    E_COMPILE_ERROR => array('Compile error', WATCHDOG_ERROR),
++    E_COMPILE_WARNING => array('Compile warning', WATCHDOG_WARNING),
++    E_USER_ERROR => array('User error', WATCHDOG_ERROR),
++    E_USER_WARNING => array('User warning', WATCHDOG_WARNING),
++    E_USER_NOTICE => array('User notice', WATCHDOG_NOTICE),
++    E_STRICT => array('Strict warning', WATCHDOG_DEBUG),
++    E_RECOVERABLE_ERROR => array('Recoverable fatal error', WATCHDOG_ERROR),
++  );
++  // E_DEPRECATED and E_USER_DEPRECATED were added in PHP 5.3.0.
++  if (defined('E_DEPRECATED')) {
++    $types[E_DEPRECATED] = array('Deprecated function', WATCHDOG_DEBUG);
++  }
++  if (defined('E_USER_DEPRECATED')) {
++    $types[E_USER_DEPRECATED] = array('User deprecated function', WATCHDOG_DEBUG);
++  }
++  return $types;
++}
++
++/**
 + * Custom PHP error handler.
 + *
 + * @param $error_level
@@ -157,58 +358,31 @@ diff -u -r1.756.2.75 common.inc
 + * @param $context
 + *   An array that points to the active symbol table at the point the error occurred.
 + */
-+function _drupal_error_handler($error_level, $message, $filename, $line, $context) {
++function _drupal_error_handler_real($error_level, $message, $filename, $line, $context) {
 +  if ($error_level & error_reporting()) {
-+    // All these constants are documented at http://php.net/manual/en/errorfunc.constants.php
-+    $types = array(
-+      E_ERROR => 'Error',
-+      E_WARNING => 'Warning',
-+      E_PARSE => 'Parse error',
-+      E_NOTICE => 'Notice',
-+      E_CORE_ERROR => 'Core error',
-+      E_CORE_WARNING => 'Core warning',
-+      E_COMPILE_ERROR => 'Compile error',
-+      E_COMPILE_WARNING => 'Compile warning',
-+      E_USER_ERROR => 'User error',
-+      E_USER_WARNING => 'User warning',
-+      E_USER_NOTICE => 'User notice',
-+      E_STRICT => 'Strict warning',
-+      E_RECOVERABLE_ERROR => 'Recoverable fatal error'
-+    );
++    $types = drupal_error_levels();
++    list($severity_msg, $severity_level) = $types[$error_level];
 +    $caller = _drupal_get_last_caller(debug_backtrace());
 +
 +    // We treat recoverable errors as fatal.
 +    _drupal_log_error(array(
-+      '%type' => isset($types[$error_level]) ? $types[$error_level] : 'Unknown error',
++      '%type' => isset($types[$error_level]) ? $severity_msg : 'Unknown error',
 +      '%message' => $message,
 +      '%function' => $caller['function'],
 +      '%file' => $caller['file'],
 +      '%line' => $caller['line'],
++      'severity_level' => $severity_level,
 +    ), $error_level == E_RECOVERABLE_ERROR);
 +  }
 +}
 +
 +/**
-+ * Custom PHP exception handler.
-+ *
-+ * Uncaught exceptions are those not enclosed in a try/catch block. They are
-+ * always fatal: the execution of the script will stop as soon as the exception
-+ * handler exits.
-+ *
-+ * @param $exception
-+ *   The exception object that was thrown.
-+ */
-+function _drupal_exception_handler($exception) {
-+  // Log the message to the watchdog and return an error page to the user.
-+  _drupal_log_error(_drupal_decode_exception($exception), TRUE);
-+}
-+
-+/**
 + * Decode an exception, especially to retrive the correct caller.
 + *
 + * @param $exception
 + *   The exception object that was thrown.
-+ * @return An error in the format expected by _drupal_log_error().
++ * @return
++ *   An error in the format expected by _drupal_log_error().
 + */
 +function _drupal_decode_exception($exception) {
 +  $message = $exception->getMessage();
@@ -219,11 +393,10 @@ diff -u -r1.756.2.75 common.inc
 +
 +  // For PDOException errors, we try to return the initial caller,
 +  // skipping internal functions of the database layer.
-+  if (is_a($exception, 'PDOException')) {
++  if ($exception instanceof PDOException) {
 +    // The first element in the stack is the call, the second element gives us the caller.
 +    // We skip calls that occurred in one of the classes of the database layer
 +    // or in one of its global functions.
-+//    $db_functions = array('db_query', 'pager_query', 'db_query_range', 'db_query_temporary', 'update_sql');
 +    $db_functions = array('db_query', '_db_query', 'pager_query', 'db_query_range', 'db_query_temporary', 'update_sql');
 +    while (!empty($backtrace[1]) && ($caller = $backtrace[1]) &&
 +        ((isset($caller['class']) && (strpos($caller['class'], 'Query') !== FALSE || strpos($caller['class'], 'Database') !== FALSE || strpos($caller['class'], 'PDO') !== FALSE)) ||
@@ -243,10 +416,46 @@ diff -u -r1.756.2.75 common.inc
 +    '%function' => $caller['function'],
 +    '%file' => $caller['file'],
 +    '%line' => $caller['line'],
++    'severity_level' => WATCHDOG_ERROR,
 +  );
 +}
 +
 +/**
++ * Render an error message for an exception without any possibility of a further exception occuring.
++ *
++ * @param $exception
++ *   The exception object that was thrown.
++ * @return
++ *   An error message.
++ */
++function _drupal_render_exception_safe($exception) {
++  return check_plain(strtr('%type: %message in %function (line %line of %file).', _drupal_decode_exception($exception)));
++}
++
++/**
++ * Determines whether an error should be displayed.
++ *
++ * When in maintenance mode or when error_level is ERROR_REPORTING_DISPLAY_ALL,
++ * all errors should be displayed. For ERROR_REPORTING_DISPLAY_SOME, $error
++ * will be examined to determine if it should be displayed.
++ *
++ * @param $error
++ *   Optional error to examine for ERROR_REPORTING_DISPLAY_SOME.
++ *
++ * @return
++ *   TRUE if an error should be displayed.
++ */
++function error_displayable($error = NULL) {
++  $error_level = variable_get('error_level', ERROR_REPORTING_DISPLAY_ALL);
++  $updating = (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'update');
++  $all_errors_displayed = ($error_level == ERROR_REPORTING_DISPLAY_ALL);
++  $error_needs_display = ($error_level == ERROR_REPORTING_DISPLAY_SOME &&
++    isset($error) && $error['%type'] != 'Notice' && $error['%type'] != 'Strict warning');
++
++  return ($updating || $all_errors_displayed || $error_needs_display);
++}
++
++/**
 + * Log a PHP error or exception, display an error page in fatal cases.
 + *
 + * @param $error
@@ -257,8 +466,7 @@ diff -u -r1.756.2.75 common.inc
 +function _drupal_log_error($error, $fatal = FALSE) {
 +  // Initialize a maintenance theme if the boostrap was not complete.
 +  // Do it early because drupal_set_message() triggers a drupal_theme_initialize().
-+//  if ($fatal && (drupal_get_bootstrap_phase() != DRUPAL_BOOTSTRAP_FULL)) {
-+  if ($fatal) { // Assumed full bootstrap since common.inc loaded.
++  if ($fatal && (drupal_get_bootstrap_phase() != DRUPAL_BOOTSTRAP_FULL)) {
 +    unset($GLOBALS['theme']);
 +    if (!defined('MAINTENANCE_MODE')) {
 +      define('MAINTENANCE_MODE', 'error');
@@ -268,7 +476,8 @@ diff -u -r1.756.2.75 common.inc
 +
 +  // When running inside the testing framework, we relay the errors
 +  // to the tested site by the way of HTTP headers.
-+  if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match("/^simpletest\d+;/", $_SERVER['HTTP_USER_AGENT']) && !headers_sent() && (!defined('SIMPLETEST_COLLECT_ERRORS') || SIMPLETEST_COLLECT_ERRORS)) {
++  $test_info = &$GLOBALS['drupal_test_info'];
++  if (!empty($test_info['in_child_site']) && !headers_sent() && (!defined('SIMPLETEST_COLLECT_ERRORS') || SIMPLETEST_COLLECT_ERRORS)) {
 +    // $number does not use drupal_static as it should not be reset
 +    // as it uniquely identifies each PHP error.
 +    static $number = 0;
@@ -285,18 +494,20 @@ diff -u -r1.756.2.75 common.inc
 +    $number++;
 +  }
 +
-+  try {
-+    watchdog('php', '%type: %message in %function (line %line of %file).', $error, WATCHDOG_ERROR);
-+  }
-+  catch (Exception $e) {
-+    // Ignore any additional watchdog exception, as that probably means
-+    // that the database was not initialized correctly.
-+  }
++  watchdog('php', '%type: %message in %function (line %line of %file).', $error, $error['severity_level']);
 +
 +  if ($fatal) {
 +    drupal_set_header('500 Service unavailable (with message)');
 +  }
 +
++  if (drupal_is_cli()) {
++    if ($fatal) {
++      // When called from CLI, simply output a plain text message.
++      print html_entity_decode(strip_tags(t('%type: %message in %function (line %line of %file).', $error))). "\n";
++      exit;
++    }
++  }
++
 +  if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
 +    if ($fatal) {
 +      // When called from JavaScript, simply output the error message.
@@ -307,9 +518,7 @@ diff -u -r1.756.2.75 common.inc
 +  else {
 +    // Display the message if the current error reporting level allows this type
 +    // of message to be displayed, and unconditionnaly in update.php.
-+    $error_level = variable_get('error_level', ERROR_REPORTING_DISPLAY_ALL);
-+    $display_error = $error_level == ERROR_REPORTING_DISPLAY_ALL || ($error_level == ERROR_REPORTING_DISPLAY_SOME && $error['%type'] != 'Notice');
-+    if ($display_error || (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'update')) {
++    if (error_displayable($error)) {
 +      $class = 'error';
 +
 +      // If error type is 'User notice' then treat it as debug information
@@ -343,7 +552,7 @@ diff -u -r1.756.2.75 common.inc
 +function _drupal_get_last_caller($backtrace) {
 +  // Errors that occur inside PHP internal functions do not generate
 +  // information about file and line. Ignore black listed functions.
-+  $blacklist = array('debug');
++  $blacklist = array('debug', '_drupal_error_handler', '_drupal_exception_handler');
 +  while (($backtrace && !isset($backtrace[0]['line'])) ||
 +         (isset($backtrace[1]['function']) && in_array($backtrace[1]['function'], $blacklist))) {
 +    array_shift($backtrace);
@@ -367,6 +576,516 @@ diff -u -r1.756.2.75 common.inc
 +  }
 +  return $call;
 +}
+Index: includes/module.inc
+===================================================================
+RCS file: /cvs/drupal/drupal/includes/module.inc,v
+retrieving revision 1.115.2.3
+diff -u -p -r1.115.2.3 module.inc
+--- includes/module.inc	16 Nov 2009 17:17:35 -0000	1.115.2.3
++++ includes/module.inc	22 Aug 2010 23:34:36 -0000
+@@ -312,6 +312,10 @@ function module_enable($module_list) {
+     module_list(TRUE, FALSE);
+     // Force to regenerate the stored list of hook implementations.
+     module_implements('', FALSE, TRUE);
++    // Run a registry rebuild.
++    if (module_exists('autoload')) {
++      autoload_registry_update();
++    }
+   }
+ 
+   foreach ($invoke_modules as $module) {
+@@ -353,6 +357,10 @@ function module_disable($module_list) {
+     module_list(TRUE, FALSE);
+     // Force to regenerate the stored list of hook implementations.
+     module_implements('', FALSE, TRUE);
++    // Run a registry rebuild.
++    if (module_exists('autoload')) {
++      autoload_registry_update();
++    }
+   }
+ 
+   // If there remains no more node_access module, rebuilding will be
+Index: includes/simpletest.inc
+===================================================================
+RCS file: includes/simpletest.inc
+diff -N includes/simpletest.inc
+--- /dev/null	1 Jan 1970 00:00:00 -0000
++++ includes/simpletest.inc	22 Aug 2010 23:34:36 -0000
+@@ -0,0 +1,570 @@
++<?php
++// $Id$
++
++/**
++ * Backport functions from SimpleTest and Drupal 7 core.
++ */
++
++/**
++ * Log an exception.
++ *
++ * This is a wrapper function for watchdog() which automatically decodes an
++ * exception.
++ *
++ * @param $type
++ *   The category to which this message belongs.
++ * @param $exception
++ *   The exception that is going to be logged.
++ * @param $message
++ *   The message to store in the log. If empty, a text that contains all useful
++ *   information about the passed in exception is used.
++ * @param $variables
++ *   Array of variables to replace in the message on display. Defaults to the
++ *   return value of drupal_decode_exception().
++ * @param $severity
++ *   The severity of the message, as per RFC 3164.
++ * @param $link
++ *   A link to associate with the message.
++ *
++ * @see watchdog()
++ * @see drupal_decode_exception()
++ */
++function watchdog_exception($type, Exception $exception, $message = NULL, $variables = array(), $severity = WATCHDOG_ERROR, $link = NULL) {
++
++   // Use a default value if $message is not set.
++   if (empty($message)) {
++     $message = '%type: %message in %function (line %line of %file).';
++   }
++   // $variables must be an array so that we can add the exception information.
++   if (!is_array($variables)) {
++     $variables = array();
++   }
++
++   require_once './includes/errors.inc';
++   $variables += _drupal_decode_exception($exception);
++   watchdog($type, $message, $variables, $severity, $link);
++}
++
++/**
++ * Returns a string of highly randomized bytes (over the full 8-bit range).
++ *
++ * This function is better than simply calling mt_rand() or any other built-in
++ * PHP function because it can return a long string of bytes (compared to < 4
++ * bytes normally from mt_rand()) and uses the best available pseudo-random source.
++ *
++ * @param $count
++ *   The number of characters (bytes) to return in the string.
++ */
++function drupal_random_bytes($count)  {
++  // $random_state does not use drupal_static as it stores random bytes.
++  static $random_state, $bytes;
++  // Initialize on the first call. The contents of $_SERVER includes a mix of
++  // user-specific and system information that varies a little with each page.
++  if (!isset($random_state)) {
++    $random_state = print_r($_SERVER, TRUE);
++    if (function_exists('getmypid')) {
++      // Further initialize with the somewhat random PHP process ID.
++      $random_state .= getmypid();
++    }
++    $bytes = '';
++  }
++  if (strlen($bytes) < $count) {
++    // /dev/urandom is available on many *nix systems and is considered the
++    // best commonly available pseudo-random source.
++    if ($fh = @fopen('/dev/urandom', 'rb')) {
++      // PHP only performs buffered reads, so in reality it will always read
++      // at least 4096 bytes. Thus, it costs nothing extra to read and store
++      // that much so as to speed any additional invocations.
++      $bytes .= fread($fh, max(4096, $count));
++      fclose($fh);
++    }
++    // If /dev/urandom is not available or returns no bytes, this loop will
++    // generate a good set of pseudo-random bytes on any system.
++    // Note that it may be important that our $random_state is passed
++    // through hash() prior to being rolled into $output, that the two hash()
++    // invocations are different, and that the extra input into the first one -
++    // the microtime() - is prepended rather than appended. This is to avoid
++    // directly leaking $random_state via the $output stream, which could
++    // allow for trivial prediction of further "random" numbers.
++    while (strlen($bytes) < $count) {
++      $random_state = hash('sha256', microtime() . mt_rand() . $random_state);
++      $bytes .= hash('sha256', mt_rand() . $random_state, TRUE);
++    }
++  }
++  $output = substr($bytes, 0, $count);
++  $bytes = substr($bytes, $count);
++  return $output;
++}
++
++/**
++ * Calculate a base-64 encoded, URL-safe sha-256 hmac.
++ *
++ * @param $data
++ *   String to be validated with the hmac.
++ * @param $key
++ *   A secret string key.
++ *
++ * @return
++ *   A base-64 encoded sha-256 hmac, with + replaced with -, / with _ and
++ *   any = padding characters removed.
++ */
++function drupal_hmac_base64($data, $key) {
++  $hmac = base64_encode(hash_hmac('sha256', $data, $key, TRUE));
++  // Modify the hmac so it's safe to use in URLs.
++  return strtr($hmac, array('+' => '-', '/' => '_', '=' => ''));
++}
++
++/**
++ * Calculate a base-64 encoded, URL-safe sha-256 hash.
++ *
++ * @param $data
++ *   String to be hashed.
++ *
++ * @return
++ *   A base-64 encoded sha-256 hash, with + replaced with -, / with _ and
++ *   any = padding characters removed.
++ */
++function drupal_hash_base64($data) {
++  $hash = base64_encode(hash('sha256', $data, TRUE));
++  // Modify the hash so it's safe to use in URLs.
++  return strtr($hash, array('+' => '-', '/' => '_', '=' => ''));
++}
++
++/**
++ * Returns the current bootstrap phase for this Drupal process.
++ *
++ * The current phase is the one most recently completed by drupal_bootstrap().
++ *
++ * @see drupal_bootstrap()
++ */
++function drupal_get_bootstrap_phase() {
++  return drupal_bootstrap();
++}
++
++/**
++ * Custom PHP error handler.
++ *
++ * @param $error_level
++ *   The level of the error raised.
++ * @param $message
++ *   The error message.
++ * @param $filename
++ *   The filename that the error was raised in.
++ * @param $line
++ *   The line number the error was raised at.
++ * @param $context
++ *   An array that points to the active symbol table at the point the error occurred.
++ */
++function _drupal_error_handler($error_level, $message, $filename, $line, $context) {
++  require_once './includes/errors.inc';
++  _drupal_error_handler_real($error_level, $message, $filename, $line, $context);
++}
++
++/**
++ * Custom PHP exception handler.
++ *
++ * Uncaught exceptions are those not enclosed in a try/catch block. They are
++ * always fatal: the execution of the script will stop as soon as the exception
++ * handler exits.
++ *
++ * @param $exception
++ *   The exception object that was thrown.
++ */
++function _drupal_exception_handler($exception) {
++  require_once './includes/errors.inc';
++
++  try {
++    // Log the message to the watchdog and return an error page to the user.
++    _drupal_log_error(_drupal_decode_exception($exception), TRUE);
++  }
++  catch (Exception $exception2) {
++    // Another uncaught exception was thrown while handling the first one.
++    // If we are displaying errors, then do so with no possibility of a further uncaught exception being thrown.
++    if (error_displayable()) {
++      print '<h1>Additional uncaught exception thrown while handling exception.</h1>';
++      print '<h2>Original</h2><p>' . _drupal_render_exception_safe($exception) . '</p>';
++      print '<h2>Additional</h2><p>' . _drupal_render_exception_safe($exception2) . '</p><hr />';
++    }
++  }
++}
++
++/**
++ * Validate the HMAC and timestamp of a user agent header from simpletest.
++ */
++function drupal_valid_test_ua($user_agent) {
++  global $drupal_hash_salt;
++
++  list($prefix, $time, $salt, $hmac) = explode(';', $user_agent);
++  $check_string =  $prefix . ';' . $time . ';' . $salt;
++  // We use the salt from settings.php to make the HMAC key, since
++  // the database is not yet initialized and we can't access any Drupal variables.
++  // The file properties add more entropy not easily accessible to others.
++  $filepath = './includes/bootstrap.inc';
++  $key = /*$drupal_hash_salt .*/ filectime($filepath) . fileinode($filepath);
++  $time_diff = time() - $time;
++  // Since we are making a local request a 5 second time window is allowed,
++  // and the HMAC must match.
++  return ($time_diff >= 0) && ($time_diff <= 5) && ($hmac == drupal_hmac_base64($check_string, $key));
++}
++
++/**
++ * Generate a user agent string with a HMAC and timestamp for simpletest.
++ */
++function drupal_generate_test_ua($prefix) {
++  global $drupal_hash_salt;
++  static $key;
++
++  if (!isset($key)) {
++    // We use the salt from settings.php to make the HMAC key, since
++    // the database is not yet initialized and we can't access any Drupal variables.
++    // The file properties add more entropy not easily accessible to others.
++    $filepath = './includes/bootstrap.inc';
++    $key = /*$drupal_hash_salt .*/ filectime($filepath) . fileinode($filepath);
++  }
++   // Generate a moderately secure HMAC based on the database credentials.
++   $salt = uniqid('', TRUE);
++   $check_string = $prefix . ';' . time() . ';' . $salt;
++   return  $check_string . ';' . drupal_hmac_base64($check_string, $key);
++}
++
++/**
++ * @ingroup schemaapi
++ * @{
++ */
++
++/**
++ * Get the schema definition of a table, or the whole database schema.
++ *
++ * The returned schema will include any modifications made by any
++ * module that implements hook_schema_alter().
++ *
++ * @param $table
++ *   The name of the table. If not given, the schema of all tables is returned.
++ * @param $rebuild
++ *   If true, the schema will be rebuilt instead of retrieved from the cache.
++ */
++function drupal_get_schema($table = NULL, $rebuild = FALSE) {
++  static $schema = array();
++
++  if (empty($schema) || $rebuild) {
++    // Try to load the schema from cache.
++    if (!$rebuild && $cached = cache_get('schema')) {
++      $schema = $cached->data;
++    }
++    // Otherwise, rebuild the schema cache.
++    else {
++      $schema = array();
++      // Load the .install files to get hook_schema.
++      // On some databases this function may be called before bootstrap has
++      // been completed, so we force the functions we need to load just in case.
++      if (function_exists('module_load_all_includes')) {
++
++        // There is currently a bug in module_list() where it caches what it
++        // was last called with, which is not always what you want.
++        // module_load_all_includes() calls module_list(), but if this function
++        // is called very early in the bootstrap process then it will be
++        // uninitialized and therefore return no modules. Instead, we have to
++        // "prime" module_list() here to to values we want, specifically
++        // "yes rebuild the list and don't limit to bootstrap".
++        // TODO: Remove this call after http://drupal.org/node/222109 is fixed.
++        module_list(TRUE, FALSE);
++        module_load_all_includes('install');
++      }
++
++      // Invoke hook_schema for all modules.
++      foreach (module_implements('schema') as $module) {
++        // Cast the result of hook_schema() to an array, as a NULL return value
++        // would cause array_merge() to set the $schema variable to NULL as well.
++        // That would break modules which use $schema further down the line.
++        $current = (array) module_invoke($module, 'schema');
++        _drupal_initialize_schema($module, $current);
++        $schema = array_merge($schema, $current);
++      }
++
++      drupal_alter('schema', $schema);
++      // If the schema is empty, avoid saving it: some database engines require
++      // the schema to perform queries, and this could lead to infinite loops.
++      if (!empty($schema) && (drupal_get_bootstrap_phase() == DRUPAL_BOOTSTRAP_FULL)) {
++        cache_set('schema', $schema);
++      }
++    }
++  }
++
++  if (!isset($table)) {
++    return $schema;
++  }
++  elseif (isset($schema[$table])) {
++    return $schema[$table];
++  }
++  else {
++    return FALSE;
++  }
++}
++
++/**
++ * @} End of "ingroup schemaapi".
++ */
++
++/**
++ * Central static variable storage.
++ *
++ * All functions requiring a static variable to persist or cache data within
++ * a single page request are encouraged to use this function unless it is
++ * absolutely certain that the static variable will not need to be reset during
++ * the page request. By centralizing static variable storage through this
++ * function, other functions can rely on a consistent API for resetting any
++ * other function's static variables.
++ *
++ * Example:
++ * @code
++ * function language_list($field = 'language') {
++ *   $languages = &drupal_static(__FUNCTION__);
++ *   if (!isset($languages)) {
++ *     // If this function is being called for the first time after a reset,
++ *     // query the database and execute any other code needed to retrieve
++ *     // information about the supported languages.
++ *     ...
++ *   }
++ *   if (!isset($languages[$field])) {
++ *     // If this function is being called for the first time for a particular
++ *     // index field, then execute code needed to index the information already
++ *     // available in $languages by the desired field.
++ *     ...
++ *   }
++ *   // Subsequent invocations of this function for a particular index field
++ *   // skip the above two code blocks and quickly return the already indexed
++ *   // information.
++ *   return $languages[$field];
++ * }
++ * function locale_translate_overview_screen() {
++ *   // When building the content for the translations overview page, make
++ *   // sure to get completely fresh information about the supported languages.
++ *   drupal_static_reset('language_list');
++ *   ...
++ * }
++ * @endcode
++ *
++ * In a few cases, a function can have certainty that there is no legitimate
++ * use-case for resetting that function's static variable. This is rare,
++ * because when writing a function, it's hard to forecast all the situations in
++ * which it will be used. A guideline is that if a function's static variable
++ * does not depend on any information outside of the function that might change
++ * during a single page request, then it's ok to use the "static" keyword
++ * instead of the drupal_static() function.
++ *
++ * Example:
++ * @code
++ * function actions_do(...) {
++ *   // $stack tracks the number of recursive calls.
++ *   static $stack;
++ *   $stack++;
++ *   if ($stack > variable_get('actions_max_stack', 35)) {
++ *     ...
++ *     return;
++ *   }
++ *   ...
++ *   $stack--;
++ * }
++ * @endcode
++ *
++ * In a few cases, a function needs a resettable static variable, but the
++ * function is called many times (100+) during a single page request, so
++ * every microsecond of execution time that can be removed from the function
++ * counts. These functions can use a more cumbersome, but faster variant of
++ * calling drupal_static(). It works by storing the reference returned by
++ * drupal_static() in the calling function's own static variable, thereby
++ * removing the need to call drupal_static() for each iteration of the function.
++ * Conceptually, it replaces:
++ * @code
++ * $foo = &drupal_static(__FUNCTION__);
++ * @endcode
++ * with:
++ * @code
++ * // Unfortunately, this does not work.
++ * static $foo = &drupal_static(__FUNCTION__);
++ * @endcode
++ * However, the above line of code does not work, because PHP only allows static
++ * variables to be initializied by literal values, and does not allow static
++ * variables to be assigned to references.
++ * - http://php.net/manual/en/language.variables.scope.php#language.variables.scope.static
++ * - http://php.net/manual/en/language.variables.scope.php#language.variables.scope.references
++ * The example below shows the syntax needed to work around both limitations.
++ * For benchmarks and more information, see http://drupal.org/node/619666.
++ *
++ * Example:
++ * @code
++ * function user_access($string, $account = NULL) {
++ *   // Use the advanced drupal_static() pattern, since this is called very often.
++ *   static $drupal_static_fast;
++ *   if (!isset($drupal_static_fast)) {
++ *     $drupal_static_fast['perm'] = &drupal_static(__FUNCTION__);
++ *   }
++ *   $perm = &$drupal_static_fast['perm'];
++ *   ...
++ * }
++ * @endcode
++ *
++ * @param $name
++ *   Globally unique name for the variable. For a function with only one static,
++ *   variable, the function name (e.g. via the PHP magic __FUNCTION__ constant)
++ *   is recommended. For a function with multiple static variables add a
++ *   distinguishing suffix to the function name for each one.
++ * @param $default_value
++ *   Optional default value.
++ * @param $reset
++ *   TRUE to reset a specific named variable, or all variables if $name is NULL.
++ *   Resetting every variable should only be used, for example, for running
++ *   unit tests with a clean environment. Should be used only though via
++ *   function drupal_static_reset() and the return value should not be used in
++ *   this case.
++ *
++ * @return
++ *   Returns a variable by reference.
++ *
++ * @see drupal_static_reset()
++ */
++function &drupal_static($name, $default_value = NULL, $reset = FALSE) {
++  static $data = array(), $default = array();
++  // First check if dealing with a previously defined static variable.
++  if (isset($data[$name]) || array_key_exists($name, $data)) {
++    // Non-NULL $name and both $data[$name] and $default[$name] statics exist.
++    if ($reset) {
++      // Reset pre-existing static variable to its default value.
++      $data[$name] = $default[$name];
++    }
++    return $data[$name];
++  }
++  // Neither $data[$name] nor $default[$name] static variables exist.
++  if (isset($name)) {
++    if ($reset) {
++      // Reset was called before a default is set and yet a variable must be
++      // returned.
++      return $data;
++    }
++    // First call with new non-NULL $name. Initialize a new static variable.
++    $default[$name] = $data[$name] = $default_value;
++    return $data[$name];
++  }
++  // Reset all: ($name == NULL). This needs to be done one at a time so that
++  // references returned by earlier invocations of drupal_static() also get
++  // reset.
++  foreach ($default as $name => $value) {
++    $data[$name] = $value;
++  }
++  // As the function returns a reference, the return should always be a
++  // variable.
++  return $data;
++}
++
++/**
++ * Reset one or all centrally stored static variable(s).
++ *
++ * @param $name
++ *   Name of the static variable to reset. Omit to reset all variables.
++ */
++function drupal_static_reset($name = NULL) {
++  drupal_static($name, NULL, TRUE);
++}
++
++/**
++ * Detect whether the current script is running in a command-line environment.
++ */
++function drupal_is_cli() {
++  return (!isset($_SERVER['SERVER_SOFTWARE']) && (php_sapi_name() == 'cli' || (is_numeric($_SERVER['argc']) && $_SERVER['argc'] > 0)));
++}
 +
 +/**
 + * Debug function used for outputting debug information.
@@ -388,25 +1107,106 @@ diff -u -r1.756.2.75 common.inc
 +  $string = $print_r ? print_r($data, TRUE) : var_export($data, TRUE);
 +  trigger_error(trim($label ? "$label: $string" : $string));
 +}
-Index: install.php
-===================================================================
-RCS file: /cvs/drupal/drupal/install.php,v
-retrieving revision 1.113.2.9
-diff -u -r1.113.2.9 install.php
---- install.php	27 Apr 2009 10:50:35 -0000	1.113.2.9
-+++ install.php	27 Jan 2010 01:32:00 -0000
-@@ -20,6 +20,14 @@
-   require_once './includes/bootstrap.inc';
-   drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);
- 
-+  // The user agent header is used to pass a database prefix in the request when
-+  // running tests. However, for security reasons, it is imperative that no
-+  // installation be permitted using such a prefix.
-+  if (isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], "simpletest") !== FALSE) {
-+    header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden');
-+    exit;
++
++/**
++ * Finds all tables that are like the specified base table name.
++ *
++ * @param $table_expression
++ *   An SQL expression, for example "simpletest%%" (without the quotes).
++ *   BEWARE: this is not prefixed, the caller should take care of that.
++ *
++ * @return
++ *   Array, both the keys and the values are the matching tables.
++ */
++function db_find_tables($table_expression) {
++  global $db_url;
++
++  // Get the database name.
++  $url = parse_url(is_array($db_url) ? $db_url['default'] : $db_url);
++  $database = substr($url['path'], 1);
++
++  switch ($GLOBALS['db_type']) {
++    default:
++      $query = db_query("SELECT table_name FROM information_schema.tables WHERE (table_schema = '%s' OR table_catalog = '%s') AND table_name LIKE '%s'", array(
++        ':table_schema' => $database,
++        ':table_catelog' => $database,
++        ':table_name' => $table_expression,
++      ));
++      break;
 +  }
 +
-   // This must go after drupal_bootstrap(), which unsets globals!
-   global $profile, $install_locale, $conf;
++  $tables = array();
++  if (isset($query)) {
++    while ($table = db_result($query)) {
++      $tables[] = $table;
++    }
++  }
++  return $tables;
++}
++
++/**
++ * Recursively delete all files and directories in the specified filepath.
++ *
++ * If the specified path is a directory then the function will call itself
++ * recursively to process the contents. Once the contents have been removed the
++ * directory will also be removed.
++ *
++ * If the specified path is a file then it will be passed to
++ * file_unmanaged_delete().
++ *
++ * Note that this only deletes visible files with write permission.
++ *
++ * @param $path
++ *   A string containing either an URI or a file or directory path.
++ *
++ * @return
++ *   TRUE for success or if path does not exist, FALSE in the event of an
++ *   error.
++ *
++ * @see file_unmanaged_delete()
++ */
++function file_unmanaged_delete_recursive($path) {
++  if (is_dir($path)) {
++    $dir = dir($path);
++    while (($entry = $dir->read()) !== FALSE) {
++      if ($entry == '.' || $entry == '..') {
++        continue;
++      }
++      $entry_path = $path . '/' . $entry;
++      file_unmanaged_delete_recursive($entry_path);
++    }
++    $dir->close();
++    return rmdir($path);
++  }
++  elseif (is_file($path)) {
++    return unlink($path);
++  }
++}
+Index: misc/drupal.js
+===================================================================
+RCS file: /cvs/drupal/drupal/misc/drupal.js,v
+retrieving revision 1.41.2.4
+diff -u -p -r1.41.2.4 drupal.js
+--- misc/drupal.js	21 Jul 2009 08:59:10 -0000	1.41.2.4
++++ misc/drupal.js	22 Aug 2010 23:34:37 -0000
+@@ -253,17 +253,17 @@ Drupal.getSelection = function (element)
+  */
+ Drupal.ahahError = function(xmlhttp, uri) {
+   if (xmlhttp.status == 200) {
+-    if (jQuery.trim($(xmlhttp.responseText).text())) {
++    if (jQuery.trim(xmlhttp.responseText)) {
+       var message = Drupal.t("An error occurred. \n@uri\n@text", {'@uri': uri, '@text': xmlhttp.responseText });
+     }
+     else {
+-      var message = Drupal.t("An error occurred. \n@uri\n(no information available).", {'@uri': uri, '@text': xmlhttp.responseText });
++      var message = Drupal.t("An error occurred. \n@uri\n(no information available).", {'@uri': uri });
+     }
+   }
+   else {
+     var message = Drupal.t("An HTTP error @status occurred. \n@uri", {'@uri': uri, '@status': xmlhttp.status });
+   }
+-  return message;
++  return message.replace(/\n/g, '<br />');
+ }
  
+ // Global Killswitch on the <html> element
Index: INSTALL.txt
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/simpletest/Attic/INSTALL.txt,v
retrieving revision 1.6.4.18
diff -u -p -r1.6.4.18 INSTALL.txt
--- INSTALL.txt	5 Jan 2010 19:44:22 -0000	1.6.4.18
+++ INSTALL.txt	22 Aug 2010 23:46:58 -0000
@@ -14,17 +14,11 @@ INSTALLATION
 1. Apply the D6-core-simpletest.patch file to the Drupal 6 core.
 
 2. (Optional)
-   Apply the "Show fatal errors in tests" patch to misc/drupal.js if you want
-   the reason for test failure to be displayed on the batch API screen. The
-   patch can be found at:
-   http://drupal.org/files/issues/simpletest_drupal.js_.patch.
-
-3. (Optional)
    Move/Copy the run-tests.sh file into the Drupal scripts folder. The script
    allows the tests to be run from the command line.
 
-4. Go to Administer >> Site building >> Modules (admin/build/modules) and
+3. Go to Administer >> Site building >> Modules (admin/build/modules) and
    enable the SimpleTest module.
 
-5. Go to Administer >> Site building >> Testing (admin/build/testing) to
+4. Go to Administer >> Site building >> Testing (admin/build/testing) to
    begin using the module.
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.47
diff -u -p -r1.2.2.3.2.47 drupal_web_test_case.php
--- drupal_web_test_case.php	27 Jan 2010 01:55:29 -0000	1.2.2.3.2.47
+++ drupal_web_test_case.php	22 Aug 2010 23:46:59 -0000
@@ -1,16 +1,17 @@
 <?php
 // $Id: drupal_web_test_case.php,v 1.2.2.3.2.47 2010/01/27 01:55:29 boombatower Exp $
-// Core: Id: drupal_web_test_case.php,v 1.146 2009/08/31 18:30:26 webchick Exp $
 
 /**
- * @file
- * Provide required modifications to Drupal 7 core DrupalWebTestCase in order
- * for it to function properly in Drupal 6.
+ * Global variable that holds information about the tests being run.
  *
- * Copyright 2008-2009 by Jimmy Berry ("boombatower", http://drupal.org/user/214218)
+ * An array, with the following keys:
+ *  - 'test_run_id': the ID of the test being run, in the form 'simpletest_%"
+ *  - 'in_child_site': TRUE if the current request is a cURL request from
+ *     the parent site.
+ *
+ * @var array
  */
-
-module_load_include('function.inc', 'simpletest');
+global $drupal_test_info;
 
 /**
  * Base class for Drupal tests.
@@ -26,11 +27,18 @@ abstract class DrupalTestCase {
   protected $testId;
 
   /**
+   * The database prefix of this test run.
+   *
+   * @var string
+   */
+  protected $databasePrefix = NULL;
+
+  /**
    * The original database prefix, before it was changed for testing purposes.
    *
    * @var string
    */
-  protected $originalPrefix = NULL;
+  protected $originalDatabasePrefix = NULL;
 
   /**
    * The original file directory, before it was changed for testing purposes.
@@ -42,7 +50,7 @@ abstract class DrupalTestCase {
   /**
    * Time limit for the test.
    */
-  protected $timeLimit = 180;
+  protected $timeLimit = 240;
 
   /**
    * Current results of this test case.
@@ -74,7 +82,7 @@ abstract class DrupalTestCase {
   protected $skipClasses = array(__CLASS__ => TRUE);
 
   /**
-   * Constructor for DrupalWebTestCase.
+   * Constructor for DrupalTestCase.
    *
    * @param $test_id
    *   Tests with the same id are reported together.
@@ -101,8 +109,6 @@ abstract class DrupalTestCase {
    *   is the caller function itself.
    */
   protected function assert($status, $message = '', $group = 'Other', array $caller = NULL) {
-    global $db_prefix;
-
     // Convert boolean status to string status.
     if (is_bool($status)) {
       $status = $status ? 'pass' : 'fail';
@@ -116,10 +122,6 @@ abstract class DrupalTestCase {
       $caller = $this->getAssertionCall();
     }
 
-    // Switch to non-testing database to store results in.
-    $current_db_prefix = $db_prefix;
-    $db_prefix = $this->originalPrefix;
-
     // Creation assertion array that can be displayed while tests are running.
     $this->assertions[] = $assertion = array(
       'test_id' => $this->testId,
@@ -133,15 +135,13 @@ abstract class DrupalTestCase {
     );
 
     // Store assertion for display after the test has completed.
-//    db_insert('simpletest')
-//      ->fields($assertion)
-//      ->execute();
+    $current_db_prefix = $GLOBALS['db_prefix'];
+    $GLOBALS['db_prefix'] = $this->originalPrefix;
     db_query("INSERT INTO {simpletest}
               (test_id, test_class, status, message, message_group, function, line, file)
               VALUES (%d, '%s', '%s', '%s', '%s', '%s', %d, '%s')", array_values($assertion));
+    $GLOBALS['db_prefix'] = $current_db_prefix;
 
-    // Return to testing prefix.
-    $db_prefix = $current_db_prefix;
     // We do not use a ternary operator here to allow a breakpoint on
     // test failure.
     if ($status == 'pass') {
@@ -162,7 +162,11 @@ abstract class DrupalTestCase {
    * the method behaves just like DrupalTestCase::assert() in terms of storing
    * the assertion.
    *
+   * @return
+   *   Message ID of the stored assertion.
+   *
    * @see DrupalTestCase::assert()
+   * @see DrupalTestCase::deleteAssert()
    */
   public static function insertAssert($test_id, $test_class, $status, $message = '', $group = 'Other', array $caller = array()) {
     // Convert boolean status to string status.
@@ -187,12 +191,26 @@ abstract class DrupalTestCase {
       'file' => $caller['file'],
     );
 
-//    db_insert('simpletest')
-//      ->fields($assertion)
-//      ->execute();
     db_query("INSERT INTO {simpletest}
               (test_id, test_class, status, message, message_group, function, line, file)
               VALUES (%d, '%s', '%s', '%s', '%s', '%s', %d, '%s')", array_values($assertion));
+    $message_id = db_last_insert_id('simpletest', 'message_id');
+    return $message_id;
+  }
+
+  /**
+   * Delete an assertion record by message ID.
+   *
+   * @param $message_id
+   *   Message ID of the assertion to delete.
+   * @return
+   *   TRUE if the assertion was deleted, FALSE otherwise.
+   *
+   * @see DrupalTestCase::insertAssert()
+   */
+  public static function deleteAssert($message_id) {
+    db_query("DELETE FROM {simpletest} WHERE message_id = %d", $message_id);
+    return (bool) db_affected_rows();
   }
 
   /**
@@ -230,7 +248,7 @@ abstract class DrupalTestCase {
    *   TRUE if the assertion succeeded, FALSE otherwise.
    */
   protected function assertTrue($value, $message = '', $group = 'Other') {
-    return $this->assert((bool) $value, $message ? $message : t('Value is TRUE'), $group);
+    return $this->assert((bool) $value, $message ? $message : t('Value @value is TRUE.', array('@value' => var_export($value, TRUE))), $group);
   }
 
   /**
@@ -246,7 +264,7 @@ abstract class DrupalTestCase {
    *   TRUE if the assertion succeeded, FALSE otherwise.
    */
   protected function assertFalse($value, $message = '', $group = 'Other') {
-    return $this->assert(!$value, $message ? $message : t('Value is FALSE'), $group);
+    return $this->assert(!$value, $message ? $message : t('Value @value is FALSE.', array('@value' => var_export($value, TRUE))), $group);
   }
 
   /**
@@ -262,7 +280,7 @@ abstract class DrupalTestCase {
    *   TRUE if the assertion succeeded, FALSE otherwise.
    */
   protected function assertNull($value, $message = '', $group = 'Other') {
-    return $this->assert(!isset($value), $message ? $message : t('Value is NULL'), $group);
+    return $this->assert(!isset($value), $message ? $message : t('Value @value is NULL.', array('@value' => var_export($value, TRUE))), $group);
   }
 
   /**
@@ -278,7 +296,7 @@ abstract class DrupalTestCase {
    *   TRUE if the assertion succeeded, FALSE otherwise.
    */
   protected function assertNotNull($value, $message = '', $group = 'Other') {
-    return $this->assert(isset($value), $message ? $message : t('Value is not NULL'), $group);
+    return $this->assert(isset($value), $message ? $message : t('Value @value is not NULL.', array('@value' => var_export($value, TRUE))), $group);
   }
 
   /**
@@ -296,7 +314,7 @@ abstract class DrupalTestCase {
    *   TRUE if the assertion succeeded, FALSE otherwise.
    */
   protected function assertEqual($first, $second, $message = '', $group = 'Other') {
-    return $this->assert($first == $second, $message ? $message : t('First value is equal to second value'), $group);
+    return $this->assert($first == $second, $message ? $message : t('Value @first is equal to value @second.', array('@first' => var_export($first, TRUE), '@second' => var_export($second, TRUE))), $group);
   }
 
   /**
@@ -314,7 +332,7 @@ abstract class DrupalTestCase {
    *   TRUE if the assertion succeeded, FALSE otherwise.
    */
   protected function assertNotEqual($first, $second, $message = '', $group = 'Other') {
-    return $this->assert($first != $second, $message ? $message : t('First value is not equal to second value'), $group);
+    return $this->assert($first != $second, $message ? $message : t('Value @first is not equal to value @second.', array('@first' => var_export($first, TRUE), '@second' => var_export($second, TRUE))), $group);
   }
 
   /**
@@ -332,7 +350,7 @@ abstract class DrupalTestCase {
    *   TRUE if the assertion succeeded, FALSE otherwise.
    */
   protected function assertIdentical($first, $second, $message = '', $group = 'Other') {
-    return $this->assert($first === $second, $message ? $message : t('First value is identical to second value'), $group);
+    return $this->assert($first === $second, $message ? $message : t('Value @first is identical to value @second.', array('@first' => var_export($first, TRUE), '@second' => var_export($second, TRUE))), $group);
   }
 
   /**
@@ -350,7 +368,7 @@ abstract class DrupalTestCase {
    *   TRUE if the assertion succeeded, FALSE otherwise.
    */
   protected function assertNotIdentical($first, $second, $message = '', $group = 'Other') {
-    return $this->assert($first !== $second, $message ? $message : t('First value is not identical to second value'), $group);
+    return $this->assert($first !== $second, $message ? $message : t('Value @first is not identical to value @second.', array('@first' => var_export($first, TRUE), '@second' => var_export($second, TRUE))), $group);
   }
 
   /**
@@ -412,18 +430,28 @@ abstract class DrupalTestCase {
 
     // HTTP auth settings (<username>:<password>) for the simpletest browser
     // when sending requests to the test site.
-    $username = variable_get('simpletest_username', NULL);
-    $password = variable_get('simpletest_password', NULL);
+    $this->httpauth_method = variable_get('simpletest_httpauth_method', CURLAUTH_BASIC);
+    $username = variable_get('simpletest_httpauth_username', NULL);
+    $password = variable_get('simpletest_httpauth_password', NULL);
     if ($username && $password) {
       $this->httpauth_credentials = $username . ':' . $password;
     }
 
     set_error_handler(array($this, 'errorHandler'));
-    $methods = array();
+    $class = get_class($this);
     // Iterate through all the methods in this class.
-    foreach (get_class_methods(get_class($this)) as $method) {
+    foreach (get_class_methods($class) 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
+        // that testing completed.
+        $method_info = new ReflectionMethod($class, $method);
+        $caller = array(
+          'file' => $method_info->getFileName(),
+          'line' => $method_info->getStartLine(),
+          'function' => $class . '->' . $method . '()',
+        );
+        $completion_check_id = DrupalTestCase::insertAssert($this->testId, $class, FALSE, t('The test did not complete due to a fatal error.'), 'Completion check', $caller);
         $this->setUp();
         try {
           $this->$method();
@@ -433,6 +461,8 @@ abstract class DrupalTestCase {
           $this->exceptionHandler($e);
         }
         $this->tearDown();
+        // Remove the completion check record.
+        DrupalTestCase::deleteAssert($completion_check_id);
       }
     }
     // Clear out the error messages and restore error handler.
@@ -441,11 +471,10 @@ abstract class DrupalTestCase {
   }
 
   /**
-   * Handle errors.
+   * Handle errors during test runs.
    *
    * Because this is registered in set_error_handler(), it has to be public.
    * @see set_error_handler
-   *
    */
   public function errorHandler($severity, $message, $file = NULL, $line = NULL) {
     if ($severity & error_reporting()) {
@@ -479,7 +508,8 @@ abstract class DrupalTestCase {
       'line' => $exception->getLine(),
       'file' => $exception->getFile(),
     ));
-    $this->error($exception->getMessage(), 'Uncaught exception', _drupal_get_last_caller($backtrace));
+    require_once './includes/errors.inc';
+    $this->error(t('%type: %message in %function (line %line of %file).', _drupal_decode_exception($exception)), 'Uncaught exception', _drupal_get_last_caller($backtrace));
   }
 
   /**
@@ -490,45 +520,41 @@ abstract class DrupalTestCase {
    * is not restricted.
    *
    * @param $length
-   *   Length of random string to generate which will be appended to $db_prefix.
+   *   Length of random string to generate.
    * @return
    *   Randomly generated string.
    */
   public static function randomString($length = 8) {
-    global $db_prefix;
-
     $str = '';
     for ($i = 0; $i < $length; $i++) {
       $str .= chr(mt_rand(32, 126));
     }
-    return str_replace('simpletest', 's', $db_prefix) . $str;
+    return $str;
   }
 
   /**
    * Generates a random string containing letters and numbers.
    *
-   * The letters may be upper or lower case. This method is better for
-   * restricted inputs that do not accept certain characters. For example,
-   * when testing input fields that require machine readable values (ie without
-   * spaces and non-standard characters) this method is best.
+   * The string will always start with a letter. The letters may be upper or
+   * lower case. This method is better for restricted inputs that do not
+   * accept certain characters. For example, when testing input fields that
+   * require machine readable values (i.e. without spaces and non-standard
+   * characters) this method is best.
    *
    * @param $length
-   *   Length of random string to generate which will be appended to $db_prefix.
+   *   Length of random string to generate.
    * @return
    *   Randomly generated string.
    */
   public static function randomName($length = 8) {
-    global $db_prefix;
-
     $values = array_merge(range(65, 90), range(97, 122), range(48, 57));
     $max = count($values) - 1;
-    $str = '';
-    for ($i = 0; $i < $length; $i++) {
+    $str = chr(mt_rand(97, 122));
+    for ($i = 1; $i < $length; $i++) {
       $str .= chr($values[mt_rand(0, $max)]);
     }
-    return str_replace('simpletest', 's', $db_prefix) . $str;
+    return $str;
   }
-
 }
 
 /**
@@ -536,8 +562,7 @@ abstract class DrupalTestCase {
  *
  * These tests can not access the database nor files. Calling any Drupal
  * function that needs the database will throw exceptions. These include
- * watchdog(), function_exists(), module_implements(),
- * module_invoke_all() etc.
+ * watchdog(), module_implements(), module_invoke_all() etc.
  */
 class DrupalUnitTestCase extends DrupalTestCase {
 
@@ -549,18 +574,22 @@ class DrupalUnitTestCase extends DrupalT
     $this->skipClasses[__CLASS__] = TRUE;
   }
 
-  function setUp() {
-    global $db_prefix, $conf;
+  protected function setUp() {
+    global $conf;
 
-    // Store necessary current values before switching to prefixed database.
-    $this->originalPrefix = $db_prefix;
+    // Store necessary current values before switching to the test environment.
+    $this->originalPrefix = $GLOBALS['db_prefix'];
     $this->originalFileDirectory = file_directory_path();
 
+    // Reset all statics so that test is performed with a clean environment.
+    drupal_static_reset();
+
     // Generate temporary prefixed database to ensure that tests have a clean starting point.
-//    $db_prefix = Database::getConnection()->prefixTables('{simpletest' . mt_rand(1000, 1000000) . '}');
-    $db_prefix = $db_prefix . 'simpletest' . mt_rand(1000, 1000000);
-//    $conf['file_public_path'] = $this->originalFileDirectory . '/' . $db_prefix;
-    $conf['file_directory_path'] = $this->originalFileDirectory . '/simpletest/' . substr($db_prefix, 10);
+    $this->databasePrefix = db_prefix_tables('{simpletest' . mt_rand(1000, 1000000) . '}');
+    $conf['file_directory_path'] = $this->originalFileDirectory . '/' . $this->databasePrefix;
+
+    // Set user agent to be consistent with web test case.
+    $_SERVER['HTTP_USER_AGENT'] = $this->databasePrefix;
 
     // If locale is enabled then t() will try to access the database and
     // subsequently will fail as the database is not accessible.
@@ -572,17 +601,16 @@ class DrupalUnitTestCase extends DrupalT
     }
   }
 
-  function tearDown() {
-    global $db_prefix, $conf;
-    if (preg_match('/simpletest\d+/', $db_prefix)) {
-//      $conf['file_public_path'] = $this->originalFileDirectory;
-      $conf['file_directory_path'] = $this->originalFileDirectory;
-      // Return the database prefix to the original.
-      $db_prefix = $this->originalPrefix;
-      // Restore modules if necessary.
-      if (isset($this->originalModuleList)) {
-        module_list(TRUE, FALSE, FALSE, $this->originalModuleList);
-      }
+  protected function tearDown() {
+    global $conf;
+
+    // Get back to the original connection.
+    $GLOBALS['db_prefix'] = $this->originalPrefix;
+
+    $conf['file_directory_path'] = $this->originalFileDirectory;
+    // Restore modules if necessary.
+    if (isset($this->originalModuleList)) {
+      module_list(TRUE, FALSE, FALSE, $this->originalModuleList);
     }
   }
 }
@@ -663,6 +691,18 @@ class DrupalWebTestCase extends DrupalTe
   protected $originalUser = NULL;
 
   /**
+   * The original shutdown handlers array, before it was cleaned for testing purposes.
+   *
+   * @var array
+   */
+  protected $originalShutdownCallbacks = array();
+
+  /**
+   * HTTP authentication method
+   */
+  protected $httpauth_method = CURLAUTH_BASIC;
+
+  /**
    * HTTP authentication credentials (<username>:<password>).
    */
   protected $httpauth_credentials = NULL;
@@ -678,6 +718,16 @@ class DrupalWebTestCase extends DrupalTe
   protected $session_id = NULL;
 
   /**
+   * Whether the files were copied to the test files directory.
+   */
+  protected $generatedTestFiles = FALSE;
+
+  /**
+   * The number of redirects followed during the handling of a request.
+   */
+  protected $redirect_count;
+
+  /**
    * Constructor for DrupalWebTestCase.
    */
   function __construct($test_id = NULL) {
@@ -695,10 +745,6 @@ class DrupalWebTestCase extends DrupalTe
    *   A node object matching $title.
    */
   function drupalGetNodeByTitle($title) {
-//    $nodes = node_load_multiple(array(), array('title' => $title));
-//    // Load the first node returned from the database.
-//    $returned_node = reset($nodes);
-//    return $returned_node;
     return node_load(array('title' => $title));
   }
 
@@ -714,13 +760,11 @@ class DrupalWebTestCase extends DrupalTe
   protected function drupalCreateNode($settings = array()) {
     // Populate defaults array.
     $settings += array(
-//      'body'      => array(FIELD_LANGUAGE_NONE => array(array())),
       'body'      => $this->randomName(32),
+      'format'    => FILTER_FORMAT_DEFAULT,
       'title'     => $this->randomName(8),
       'comment'   => 2,
-//      'changed'   => REQUEST_TIME,
       'changed'   => time(),
-      'format'    => FILTER_FORMAT_DEFAULT,
       'moderate'  => 0,
       'promote'   => 0,
       'revision'  => 1,
@@ -729,7 +773,8 @@ class DrupalWebTestCase extends DrupalTe
       'sticky'    => 0,
       'type'      => 'page',
       'revisions' => NULL,
-      'taxonomy'  => NULL,
+      'language'  => '',
+      //'taxonomy'  => NULL,
     );
 
     // Use the original node's created time for existing nodes.
@@ -749,21 +794,10 @@ class DrupalWebTestCase extends DrupalTe
       }
     }
 
-//    // Merge body field value and format separately.
-//    $body = array(
-//      'value' => $this->randomName(32),
-//      'format' => FILTER_FORMAT_DEFAULT
-//    );
-//    $settings['body'][FIELD_LANGUAGE_NONE][0] += $body;
-
     $node = (object) $settings;
     node_save($node);
 
     // Small hack to link revisions to our test user.
-//    db_update('node_revision')
-//      ->fields(array('uid' => $node->uid))
-//      ->condition('vid', $node->vid)
-//      ->execute();
     db_query('UPDATE {node_revisions} SET uid = %d WHERE vid = %d', $node->uid, $node->vid);
     return $node;
   }
@@ -780,9 +814,7 @@ class DrupalWebTestCase extends DrupalTe
   protected function drupalCreateContentType($settings = array()) {
     // Find a non-existent random type name.
     do {
-//      $name = strtolower($this->randomName(8));
-//    } while (node_type_get_type($name));
-      $name = strtolower($this->randomName(3, 'type_'));
+      $name = strtolower($this->randomName(8));
     } while (node_get_types('type', $name));
 
     // Populate defaults array.
@@ -807,11 +839,11 @@ class DrupalWebTestCase extends DrupalTe
       'locked' => 0,
     );
     $type = $forced + $settings + $defaults;
-    $type = (object)$type;
+    $type = (object) $type;
 
     $saved_type = node_type_save($type);
     node_types_rebuild();
-    menu_rebuild(); // Drupal 6.
+    menu_rebuild();
 
     $this->assertEqual($saved_type, SAVED_NEW, t('Created content type %type.', array('%type' => $type->type)));
 
@@ -832,22 +864,42 @@ class DrupalWebTestCase extends DrupalTe
    *   List of files that match filter.
    */
   protected function drupalGetTestFiles($type, $size = NULL) {
-    $files = array();
+    if (empty($this->generatedTestFiles)) {
+      // Generate binary test files.
+      $lines = array(64, 1024);
+      $count = 0;
+      foreach ($lines as $line) {
+        simpletest_generate_file('binary-' . $count++, 64, $line, 'binary');
+      }
+
+      // Generate text test files.
+      $lines = array(16, 256, 1024, 2048, 20480);
+      $count = 0;
+      foreach ($lines as $line) {
+        simpletest_generate_file('text-' . $count++, 64, $line);
+      }
+
+      // Copy other test files from simpletest.
+      $original = drupal_get_path('module', 'simpletest') . '/files';
+      $files = file_scan_directory($original, '/(html|image|javascript|php|sql)-.*/');
+      $destination_path = file_directory_path();
+      foreach ($files as $file) {
+        file_copy($file->filename, $destination_path);
+      }
+
+      $this->generatedTestFiles = TRUE;
+    }
 
+    $files = array();
     // Make sure type is valid.
     if (in_array($type, array('binary', 'html', 'image', 'javascript', 'php', 'sql', 'text'))) {
-      // Use original file directory instead of one created during setUp().
-      $path = $this->originalFileDirectory . '/simpletest';
-//      $files = file_scan_directory($path, '/' . $type . '\-.*/');
-      $files = file_scan_directory($path, '' . $type . '\-.*');
+      $files = file_scan_directory(file_directory_path(), '/' . $type . '\-.*/');
 
       // If size is set then remove any files that are not of that size.
       if ($size !== NULL) {
         foreach ($files as $file) {
-//          $stats = stat($file->uri);
           $stats = stat($file->filename);
           if ($stats['size'] != $size) {
-//            unset($files[$file->uri]);
             unset($files[$file->filename]);
           }
         }
@@ -861,7 +913,6 @@ class DrupalWebTestCase extends DrupalTe
    * Compare two files based on size and file name.
    */
   protected function drupalCompareFiles($file1, $file2) {
-//    $compare_size = filesize($file1->uri) - filesize($file2->uri);
     $compare_size = filesize($file1->filename) - filesize($file2->filename);
     if ($compare_size) {
       // Sort by file size.
@@ -931,22 +982,14 @@ class DrupalWebTestCase extends DrupalTe
     }
 
     // Create new role.
-//    $role = new stdClass();
-//    $role->name = $name;
-//    user_role_save($role);
-//    user_role_set_permissions($role->name, $permissions);
     db_query("INSERT INTO {role} (name) VALUES ('%s')", $name);
     $role = db_fetch_object(db_query("SELECT * FROM {role} WHERE name = '%s'", $name));
 
     $this->assertTrue(isset($role->rid), t('Created role of name: @name, id: @rid', array('@name' => $name, '@rid' => (isset($role->rid) ? $role->rid : t('-n/a-')))), t('Role'));
     if ($role && !empty($role->rid)) {
-//      $count = db_query('SELECT COUNT(*) FROM {role_permission} WHERE rid = :rid', array(':rid' => $role->rid))->fetchField();
-//      $this->assertTrue($count == count($permissions), t('Created permissions: @perms', array('@perms' => implode(', ', $permissions))), t('Role'));
-
-      // Assign permissions to role and mark it for clean-up.
       db_query("INSERT INTO {permission} (rid, perm) VALUES (%d, '%s')", $role->rid, implode(', ', $permissions));
-      $perm = db_result(db_query("SELECT perm FROM {permission} WHERE rid = %d", $role->rid));
-      $this->assertTrue(count(explode(', ', $perm)) == count($permissions), t('Created permissions: @perms', array('@perms' => implode(', ', $permissions))), t('Role'));
+      $count = explode(', ', db_result(db_query("SELECT perm FROM {permission} WHERE rid = %d", $role->rid)));
+      $this->assertTrue($count == count($permissions), t('Created permissions: @perms', array('@perms' => implode(', ', $permissions))), t('Role'));
       return $role->rid;
     }
     else {
@@ -965,11 +1008,9 @@ class DrupalWebTestCase extends DrupalTe
    *   TRUE or FALSE depending on whether the permissions are valid.
    */
   protected function checkPermissions(array $permissions, $reset = FALSE) {
-//    $available = &drupal_static(__FUNCTION__);
-    static $available;
+    $available = &drupal_static(__FUNCTION__);
 
     if (!isset($available) || $reset) {
-//      $available = array_keys(module_invoke_all('permission'));
       $available = module_invoke_all('perm');
     }
 
@@ -993,7 +1034,7 @@ class DrupalWebTestCase extends DrupalTe
    * populated with data of the logged in user. If you need full access to the
    * user object after logging in, it must be updated manually. If you also need
    * access to the plain-text password of the user (set by drupalCreateUser()),
-   * e.g. to login the same user again, then it must be re-assigned manually.
+   * e.g. to log in the same user again, then it must be re-assigned manually.
    * For example:
    * @code
    *   // Create a user.
@@ -1006,7 +1047,7 @@ class DrupalWebTestCase extends DrupalTe
    * @endcode
    *
    * @param $user
-   *   User object representing the user to login.
+   *   User object representing the user to log in.
    *
    * @see drupalCreateUser()
    */
@@ -1045,8 +1086,8 @@ class DrupalWebTestCase extends DrupalTe
     // Make a request to the logout page, and redirect to the user page, the
     // idea being if you were properly logged out you should be seeing a login
     // screen.
-//    $this->drupalGet('user/logout', array('query' => 'destination=user'));
-    $this->drupalGet('logout', array('query' => 'destination=user'));
+    $this->drupalGet('logout');
+    $this->drupalGet('user');
     $pass = $this->assertField('name', t('Username field found.'), t('Logout'));
     $pass = $pass && $this->assertField('pass', t('Password field found.'), t('Logout'));
 
@@ -1063,103 +1104,106 @@ class DrupalWebTestCase extends DrupalTe
    * is created with the same name as the database prefix.
    *
    * @param ...
-   *   List of modules to enable for the duration of the test.
+   *   List of modules to enable for the duration of the test. This can be
+   *   either a single array or a variable number of string arguments.
    */
   protected function setUp() {
-    global $db_prefix, $user, $language;
+    global $user, $language, $conf;
+
+    // Generate a temporary prefixed database to ensure that tests have a clean starting point.
+    $this->databasePrefix = 'simpletest' . mt_rand(1000, 1000000);
+    db_query("UPDATE {simpletest_test_id} SET last_prefix = '%s' WHERE test_id = %d", $this->databasePrefix, $this->testId);
+
+    // Replace the current prefix.
+    $this->originalPrefix = $GLOBALS['db_prefix'];
+    $GLOBALS['db_prefix'] = $this->databasePrefix;
 
     // Store necessary current values before switching to prefixed database.
     $this->originalLanguage = $language;
-//    $this->originalLanguageDefault = variable_get('language_default');
-    $this->originalPrefix = $db_prefix;
+    $this->originalLanguageDefault = variable_get('language_default', NULL);
     $this->originalFileDirectory = file_directory_path();
-//    $this->originalProfile = drupal_get_profile();
+    $this->originalProfile = variable_get('install_profile', 'standard');
     $clean_url_original = variable_get('clean_url', 0);
 
-    // Must reset locale here, since schema calls t(). (Drupal 6)
-    if (module_exists('locale')) {
-      $language = (object) array('language' => 'en', 'name' => 'English', 'native' => 'English', 'direction' => 0, 'enabled' => 1, 'plurals' => 0, 'formula' => '', 'domain' => '', 'prefix' => '', 'weight' => 0, 'javascript' => '');
-      locale(NULL, NULL, TRUE);
-    }
-
-    // Generate temporary prefixed database to ensure that tests have a clean starting point.
-//    $db_prefix_new = Database::getConnection()->prefixTables('{simpletest' . mt_rand(1000, 1000000) . '}');
-    $db_prefix_new = $db_prefix . 'simpletest' . mt_rand(1000, 1000000);
-
-    // Workaround to insure we init the theme layer before going into prefixed
-    // environment. (Drupal 6)
-    $this->pass(t('Starting run with db_prefix %prefix', array('%prefix' => $db_prefix_new)), 'System');
-
-//    db_update('simpletest_test_id')
-//      ->fields(array('last_prefix' => $db_prefix_new))
-//      ->condition('test_id', $this->testId)
-//      ->execute();
-    db_query("UPDATE {simpletest_test_id}
-              SET last_prefix = '%s'
-              WHERE test_id = %d", $db_prefix_new, $this->testId);
-    $db_prefix = $db_prefix_new;
-
     // Create test directory ahead of installation so fatal errors and debug
     // information can be logged during installation process.
-    $directory = $this->originalFileDirectory . '/simpletest/' . substr($db_prefix, 10);
-//    file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
-    file_check_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
+    // Use temporary files directory with the same prefix as the database.
+    $public_files_directory  = $this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10);
+    $temp_files_directory    = $public_files_directory . '/temp';
+
+    // Create the directories
+    file_check_directory($public_files_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
+    file_check_directory($temp_files_directory, FILE_CREATE_DIRECTORY);
+    $this->generatedTestFiles = FALSE;
 
     // Log fatal errors.
     ini_set('log_errors', 1);
-    ini_set('error_log', $directory . '/error.log');
+    ini_set('error_log', $public_files_directory . '/error.log');
+
+    // Workaround to insure we init the theme layer before going into prefixed
+    // environment. (Drupal 6)
+    //$this->pass(t('Starting run with db_prefix %prefix', array('%prefix' => $this->databasePrefix)), 'System');
+
+    // Reset all statics and variables to perform tests in a clean environment.
+    $conf = array();
+    drupal_static_reset();
+
+    // Set the test information for use in other parts of Drupal.
+    $test_info = &$GLOBALS['drupal_test_info'];
+    $test_info['test_run_id'] = $this->databasePrefix;
+    $test_info['in_child_site'] = FALSE;
 
-//    include_once DRUPAL_ROOT . '/includes/install.inc';
     include_once './includes/install.inc';
     drupal_install_system();
 
-//    $this->preloadRegistry();
+    // Set path variables.
+    variable_set('file_directory_path', $public_files_directory);
+    variable_set('file_directory_temp', $temp_files_directory);
 
-//    // Include the default profile
-//    variable_set('install_profile', 'default');
-//    $profile_details = install_profile_info('default', 'en');
+    // Include the default profile.
+    variable_set('install_profile', 'default');
+    $modules = drupal_verify_profile('default', 'en');
 
-    // Add the specified modules to the list of modules in the default profile.
     // Install the modules specified by the default profile.
-//    drupal_install_modules($profile_details['dependencies'], TRUE);
-    drupal_install_modules(drupal_verify_profile('default', 'en'));
-
-//    node_type_clear();
+    drupal_install_modules($modules);
 
-    // Install additional modules one at a time in order to make sure that the
-    // list of modules is updated between each module's installation.
+    // Install modules needed for this test. This could have been passed in as
+    // either a single array argument or a variable number of string arguments.
+    // @todo Remove this compatibility layer in Drupal 8, and only accept
+    // $modules as a single array argument.
     $modules = func_get_args();
-    foreach ($modules as $module) {
-//      drupal_install_modules(array($module), TRUE);
-      drupal_install_modules(array($module));
+    if (isset($modules[0]) && is_array($modules[0])) {
+      $modules = $modules[0];
+    }
+    if ($modules) {
+      drupal_install_modules($modules);
     }
-
-    // Because the schema is static cached, we need to flush
-    // it between each run. If we don't, then it will contain
-    // stale data for the previous run's database prefix and all
-    // calls to it will fail.
-    drupal_get_schema(NULL, TRUE);
 
     // Run default profile tasks.
-//    $install_state = array();
-//    drupal_install_modules(array('default'), TRUE);
     $task = 'profile';
     default_profile_tasks($task, '');
 
     // Rebuild caches.
-//    node_types_rebuild();
+    drupal_static_reset();
+    drupal_flush_all_caches();
+
+    // Drupal 6 specific cache clears.
+    user_access(NULL, NULL, TRUE);
+
+    // Register actions declared by any modules.
     actions_synchronize();
-    _drupal_flush_css_js();
+
+    // Reload global $conf array and permissions.
     $this->refreshVariables();
     $this->checkPermissions(array(), TRUE);
-    user_access(NULL, NULL, TRUE); // Drupal 6.
+
+    // Reset statically cached schema for new database prefix.
+    drupal_get_schema(NULL, TRUE);
 
     // Log in with a clean $user.
     $this->originalUser = $user;
-//    drupal_save_session(FALSE);
-//    $user = user_load(1);
     session_save_session(FALSE);
-    $user = user_load(array('uid' => 1));
+    $user = user_load(1);
 
     // Restore necessary variables.
     variable_set('install_profile', 'default');
@@ -1167,43 +1211,15 @@ class DrupalWebTestCase extends DrupalTe
     variable_set('install_task', 'profile-finished');
     variable_set('clean_url', $clean_url_original);
     variable_set('site_mail', 'simpletest@example.com');
-//    // Set up English language.
-//    unset($GLOBALS['conf']['language_default']);
-//    $language = language_default();
+    // Set up English language.
+    unset($GLOBALS['conf']['language_default']);
+    $language = language_default();
 
     // Use the test mail class instead of the default mail handler class.
-//    variable_set('mail_sending_system', array('default-system' => 'TestingMailSystem'));
     variable_set('smtp_library', drupal_get_path('module', 'simpletest') . '/simpletest.mail.inc');
 
-    // Use temporary files directory with the same prefix as the database.
-//    $public_files_directory  = $this->originalFileDirectory . '/' . $db_prefix;
-//    $private_files_directory = $public_files_directory . '/private';
-    $directory = $this->originalFileDirectory . '/' . $db_prefix;
-
-    // Set path variables
-//    variable_set('file_public_path', $public_files_directory);
-//    variable_set('file_private_path', $private_files_directory);
-    variable_set('file_directory_path', $directory);
-
-    // Create the directories
-//    $directory = file_directory_path('public');
-//    file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
-//    file_prepare_directory($private_files_directory, FILE_CREATE_DIRECTORY);
-    file_check_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
-
-//    drupal_set_time_limit($this->timeLimit);
-    set_time_limit($this->timeLimit);
-  }
-
-//  /**
-//   * This method is called by DrupalWebTestCase::setUp, and preloads the
-//   * registry from the testing site to cut down on the time it takes to
-//   * setup a clean environment for the current test run.
-//   */
-//  protected function preloadRegistry() {
-//    db_query('INSERT INTO {registry} SELECT * FROM ' . $this->originalPrefix . 'registry');
-//    db_query('INSERT INTO {registry_file} SELECT * FROM ' . $this->originalPrefix . 'registry_file');
-//  }
+    @set_time_limit($this->timeLimit);
+  }
 
   /**
    * Refresh the in-memory set of variables. Useful after a page request is made
@@ -1220,7 +1236,6 @@ class DrupalWebTestCase extends DrupalTe
   protected function refreshVariables() {
     global $conf;
     cache_clear_all('variables', 'cache');
-//    $conf = variable_initialize();
     $conf = variable_init();
   }
 
@@ -1233,68 +1248,52 @@ class DrupalWebTestCase extends DrupalTe
 
     // In case a fatal error occured that was not in the test process read the
     // log to pick up any fatal errors.
-    $db_prefix_temp = $db_prefix;
-    $db_prefix = $this->originalPrefix;
-    simpletest_log_read($this->testId, $db_prefix, get_class($this), TRUE);
-    $db_prefix = $db_prefix_temp;
+    simpletest_log_read($this->testId, $this->databasePrefix, get_class($this), TRUE);
 
     $emailCount = count(variable_get('drupal_test_email_collector', array()));
     if ($emailCount) {
-      $message = format_plural($emailCount, t('!count e-mail was sent during this test.'), t('!count e-mails were sent during this test.'), array('!count' => $emailCount));
+      $message = format_plural($emailCount, '1 e-mail was sent during this test.', '@count e-mails were sent during this test.');
       $this->pass($message, t('E-mail'));
     }
 
-    if (preg_match('/simpletest\d+/', $db_prefix)) {
-      // Delete temporary files directory.
-//      file_unmanaged_delete_recursive(file_directory_path());
-      simpletest_clean_temporary_directory(file_directory_path());
-
-      // Remove all prefixed tables (all the tables in the schema).
-      $schema = drupal_get_schema(NULL, TRUE);
-      $ret = array();
-      foreach ($schema as $name => $table) {
-        db_drop_table($ret, $name);
-      }
-
-      // Return the database prefix to the original.
-      $db_prefix = $this->originalPrefix;
+    // Delete temporary files directory.
+    file_unmanaged_delete_recursive($this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10));
 
-      // Return the user to the original one.
-      $user = $this->originalUser;
-//      drupal_save_session(TRUE);
-      session_save_session(TRUE);
+    // Remove all prefixed tables (all the tables in the schema).
+    $schema = drupal_get_schema(NULL, TRUE);
+    $ret = array();
+    foreach ($schema as $name => $table) {
+      db_drop_table($ret, $name);
+    }
 
-      // Bring back default language. (Drupal 6)
-      if (module_exists('locale')) {
-        drupal_init_language();
-        locale(NULL, NULL, TRUE);
-      }
+    // Get back to the original connection.
+    $db_prefix = $this->originalPrefix;
+    $this->databasePrefix = NULL;
 
-      // Ensure that internal logged in variable and cURL options are reset.
-      $this->loggedInUser = FALSE;
-      $this->additionalCurlOptions = array();
+    // Return the user to the original one.
+    $user = $this->originalUser;
+    session_save_session(TRUE);
+
+    // Ensure that internal logged in variable and cURL options are reset.
+    $this->loggedInUser = FALSE;
+    $this->additionalCurlOptions = array();
+
+    // Reload module list and implementations to ensure that test module hooks
+    // aren't called after tests.
+    module_list(TRUE);
+    module_implements('', '', TRUE);
 
-      // Reload module list and implementations to ensure that test module hooks
-      // aren't called after tests.
-      module_list(TRUE);
-//      module_implements('', FALSE, TRUE);
-      module_implements('', '', TRUE);
-
-      // Reset the Field API.
-//      field_cache_clear();
-
-      // Rebuild caches.
-      $this->refreshVariables();
-
-//      // Reset language.
-//      $language = $this->originalLanguage;
-//      if ($this->originalLanguageDefault) {
-//        $GLOBALS['conf']['language_default'] = $this->originalLanguageDefault;
-//      }
+    // Rebuild caches.
+    $this->refreshVariables();
 
-      // Close the CURL handler.
-      $this->curlClose();
+    // Reset language.
+    $language = $this->originalLanguage;
+    if ($this->originalLanguageDefault) {
+      $GLOBALS['conf']['language_default'] = $this->originalLanguageDefault;
     }
+
+    // Close the CURL handler.
+    $this->curlClose();
   }
 
   /**
@@ -1306,21 +1305,22 @@ class DrupalWebTestCase extends DrupalTe
    * See the description of $curl_options for other options.
    */
   protected function curlInitialize() {
-    global $base_url, $db_prefix;
+    global $base_url;
 
     if (!isset($this->curlHandle)) {
       $this->curlHandle = curl_init();
-      $curl_options = $this->additionalCurlOptions + array(
+      $curl_options = array(
         CURLOPT_COOKIEJAR => $this->cookieFile,
         CURLOPT_URL => $base_url,
-        CURLOPT_FOLLOWLOCATION => TRUE,
-        CURLOPT_MAXREDIRS => 5,
+        CURLOPT_FOLLOWLOCATION => FALSE,
         CURLOPT_RETURNTRANSFER => TRUE,
         CURLOPT_SSL_VERIFYPEER => FALSE, // Required to make the tests run on https.
         CURLOPT_SSL_VERIFYHOST => FALSE, // Required to make the tests run on https.
         CURLOPT_HEADERFUNCTION => array(&$this, 'curlHeaderCallback'),
+        CURLOPT_USERAGENT => $this->databasePrefix,
       );
       if (isset($this->httpauth_credentials)) {
+        $curl_options[CURLOPT_HTTPAUTH] = $this->httpauth_method;
         $curl_options[CURLOPT_USERPWD] = $this->httpauth_credentials;
       }
       curl_setopt_array($this->curlHandle, $this->additionalCurlOptions + $curl_options);
@@ -1330,22 +1330,42 @@ class DrupalWebTestCase extends DrupalTe
     }
     // We set the user agent header on each request so as to use the current
     // time and a new uniqid.
-    if (preg_match('/simpletest\d+/', $db_prefix, $matches)) {
+    if (preg_match('/simpletest\d+/', $this->databasePrefix, $matches)) {
       curl_setopt($this->curlHandle, CURLOPT_USERAGENT, drupal_generate_test_ua($matches[0]));
     }
   }
 
   /**
-   * Performs a cURL exec with the specified options after calling curlConnect().
+   * Initializes and executes a cURL request.
    *
    * @param $curl_options
-   *   Custom cURL options.
+   *   An associative array of cURL options to set, where the keys are constants
+   *   defined by the cURL library. For a list of valid options, see
+   *   http://www.php.net/manual/function.curl-setopt.php
+   * @param $redirect
+   *   FALSE if this is an initial request, TRUE if this request is the result
+   *   of a redirect.
+   *
    * @return
-   *   Content returned from the exec.
+   *   The content returned from the call to curl_exec().
+   *
+   * @see curlInitialize()
    */
-  protected function curlExec($curl_options) {
+  protected function curlExec($curl_options, $redirect = FALSE) {
     $this->curlInitialize();
+
+    // cURL incorrectly handles URLs with a fragment by including the
+    // fragment in the request to the server, causing some web servers
+    // to reject the request citing "400 - Bad Request". To prevent
+    // this, we strip the fragment from the request.
+    // TODO: Remove this for Drupal 8, since fixed in curl 7.20.0.
+    if (!empty($curl_options[CURLOPT_URL]) && strpos($curl_options[CURLOPT_URL], '#')) {
+      $original_url = $curl_options[CURLOPT_URL];
+      $curl_options[CURLOPT_URL] = strtok($curl_options[CURLOPT_URL], '#');
+    }
+
     $url = empty($curl_options[CURLOPT_URL]) ? curl_getinfo($this->curlHandle, CURLINFO_EFFECTIVE_URL) : $curl_options[CURLOPT_URL];
+
     if (!empty($curl_options[CURLOPT_POST])) {
       // This is a fix for the Curl library to prevent Expect: 100-continue
       // headers in POST requests, that may cause unexpected HTTP response
@@ -1356,19 +1376,40 @@ class DrupalWebTestCase extends DrupalTe
     }
     curl_setopt_array($this->curlHandle, $this->additionalCurlOptions + $curl_options);
 
-    // Reset headers and the session ID.
-    $this->session_id = NULL;
-    $this->headers = array();
+    if (!$redirect) {
+      // Reset headers, the session ID and the redirect counter.
+      $this->session_id = NULL;
+      $this->headers = array();
+      $this->redirect_count = 0;
+    }
+
+    $content = curl_exec($this->curlHandle);
+    $status = curl_getinfo($this->curlHandle, CURLINFO_HTTP_CODE);
+
+    // cURL incorrectly handles URLs with fragments, so instead of
+    // letting cURL handle redirects we take of them ourselves to
+    // to prevent fragments being sent to the web server as part
+    // of the request.
+    // TODO: Remove this for Drupal 8, since fixed in curl 7.20.0.
+    if (in_array($status, array(300, 301, 302, 303, 305, 307)) && $this->redirect_count < variable_get('simpletest_maximum_redirects', 5)) {
+      if ($this->drupalGetHeader('location')) {
+        $this->redirect_count++;
+        $curl_options = array();
+        $curl_options[CURLOPT_URL] = $this->drupalGetHeader('location');
+        $curl_options[CURLOPT_HTTPGET] = TRUE;
+        return $this->curlExec($curl_options, TRUE);
+      }
+    }
 
-    $this->drupalSetContent(curl_exec($this->curlHandle), curl_getinfo($this->curlHandle, CURLINFO_EFFECTIVE_URL));
+    $this->drupalSetContent($content, isset($original_url) ? $original_url : curl_getinfo($this->curlHandle, CURLINFO_EFFECTIVE_URL));
     $message_vars = array(
       '!method' => !empty($curl_options[CURLOPT_NOBODY]) ? 'HEAD' : (empty($curl_options[CURLOPT_POSTFIELDS]) ? 'GET' : 'POST'),
-      '@url' => $url,
-      '@status' => curl_getinfo($this->curlHandle, CURLINFO_HTTP_CODE),
-      '!length' => format_size(strlen($this->content))
+      '@url' => isset($original_url) ? $original_url : $url,
+      '@status' => $status,
+      '!length' => format_size(strlen($this->drupalGetContent()))
     );
     $message = t('!method @url returned @status (!length).', $message_vars);
-    $this->assertTrue($this->content !== FALSE, $message, t('Browser'));
+    $this->assertTrue($this->drupalGetContent() !== FALSE, $message, t('Browser'));
     return $this->drupalGetContent();
   }
 
@@ -1393,7 +1434,7 @@ class DrupalWebTestCase extends DrupalTe
       call_user_func_array(array(&$this, 'error'), unserialize(urldecode($matches[1])));
     }
 
-    // Save the session cookie, if set.
+    // Save cookies.
     if (preg_match('/^Set-Cookie: ' . preg_quote($this->session_name) . '=([a-z90-9]+)/', $header, $matches)) {
       if ($matches[1] != 'deleted') {
         $this->session_id = $matches[1];
@@ -1427,7 +1468,7 @@ class DrupalWebTestCase extends DrupalTe
     if (!$this->elements) {
       // DOM can load HTML soup. But, HTML soup can throw warnings, suppress
       // them.
-      @$htmlDom = DOMDocument::loadHTML($this->content);
+      @$htmlDom = DOMDocument::loadHTML($this->drupalGetContent());
       if ($htmlDom) {
         $this->pass(t('Valid HTML found on "@path"', array('@path' => $this->getUrl())), t('Browser'));
         // It's much easier to work with simplexml than DOM, luckily enough
@@ -1465,7 +1506,7 @@ class DrupalWebTestCase extends DrupalTe
     $this->refreshVariables(); // Ensure that any changes to variables in the other thread are picked up.
 
     // Replace original page output with new output from redirected page(s).
-    if (($new = $this->checkForMetaRefresh())) {
+    if ($new = $this->checkForMetaRefresh()) {
       $out = $new;
     }
     $this->verbose('GET request to: ' . $path .
@@ -1483,6 +1524,7 @@ class DrupalWebTestCase extends DrupalTe
    *   NULL to post to the current page. For multi-stage forms you can set the
    *   path to NULL and have it post to the last received page. Example:
    *
+   *   @code
    *   // First step in form.
    *   $edit = array(...);
    *   $this->drupalPost('some_url', $edit, t('Save'));
@@ -1490,6 +1532,7 @@ class DrupalWebTestCase extends DrupalTe
    *   // Second step in form.
    *   $edit = array(...);
    *   $this->drupalPost(NULL, $edit, t('Save'));
+   *   @endcode
    * @param  $edit
    *   Field data in an associative array. Changes the current input fields
    *   (where possible) to the values indicated. A checkbox can be set to
@@ -1499,43 +1542,60 @@ class DrupalWebTestCase extends DrupalTe
    *
    *   Multiple select fields can be set using name[] and setting each of the
    *   possible values. Example:
+   *   @code
    *   $edit = array();
    *   $edit['name[]'] = array('value1', 'value2');
+   *   @endcode
    * @param $submit
-   *   Value of the submit button.
+   *   Value of the submit button whose click is to be emulated. For example,
+   *   t('Save'). The processing of the request depends on this value. For
+   *   example, a form may have one button with the value t('Save') and another
+   *   button with the value t('Delete'), and execute different code depending
+   *   on which one is clicked.
    * @param $options
    *   Options to be forwarded to url().
    * @param $headers
    *   An array containing additional HTTP request headers, each formatted as
    *   "name: value".
+   * @param $form_html_id
+   *   (optional) HTML ID of the form to be submitted. On some pages
+   *   there are many identical forms, so just using the value of the submit
+   *   button is not enough. For example: 'trigger-node-presave-assign-form'.
+   *   Note that this is not the Drupal $form_id, but rather the HTML ID of the
+   *   form, which is typically the same thing but with hyphens replacing the
+   *   underscores.
    */
-  protected function drupalPost($path, $edit, $submit, array $options = array(), array $headers = array()) {
+  protected function drupalPost($path, $edit, $submit, array $options = array(), array $headers = array(), $form_html_id = NULL) {
     $submit_matches = FALSE;
+    $ajax = FALSE;
     if (isset($path)) {
-      $html = $this->drupalGet($path, $options);
+      $this->drupalGet($path, $options);
     }
     if ($this->parse()) {
       $edit_save = $edit;
       // Let's iterate over all the forms.
-      $forms = $this->xpath('//form');
+      $xpath = "//form";
+      if (!empty($form_html_id)) {
+        $xpath .= "[@id='" . $form_html_id . "']";
+      }
+      $forms = $this->xpath($xpath);
       foreach ($forms as $form) {
         // We try to set the fields of this form as specified in $edit.
         $edit = $edit_save;
         $post = array();
         $upload = array();
-        $submit_matches = $this->handleForm($post, $edit, $upload, $submit, $form);
+        $submit_matches = $this->handleForm($post, $edit, $upload, $ajax ? NULL : $submit, $form);
         $action = isset($form['action']) ? $this->getAbsoluteUrl($form['action']) : $this->getUrl();
 
         // We post only if we managed to handle every field in edit and the
         // submit button matches.
-        if (!$edit && $submit_matches) {
+        if (!$edit && ($submit_matches || !isset($submit))) {
           $post_array = $post;
           if ($upload) {
             // TODO: cURL handles file uploads for us, but the implementation
             // is broken. This is a less than elegant workaround. Alternatives
             // are being explored at #253506.
             foreach ($upload as $key => $file) {
-//              $file = drupal_realpath($file);
               $file = realpath($file);
               if ($file && is_file($file)) {
                 $post[$key] = '@' . $file;
@@ -1556,7 +1616,7 @@ class DrupalWebTestCase extends DrupalTe
           $this->refreshVariables();
 
           // Replace original page output with new output from redirected page(s).
-          if (($new = $this->checkForMetaRefresh())) {
+          if ($new = $this->checkForMetaRefresh()) {
             $out = $new;
           }
           $this->verbose('POST request to: ' . $path .
@@ -1570,12 +1630,21 @@ class DrupalWebTestCase extends DrupalTe
       foreach ($edit as $name => $value) {
         $this->fail(t('Failed to set field @name to @value', array('@name' => $name, '@value' => $value)));
       }
-      $this->assertTrue($submit_matches, t('Found the @submit button', array('@submit' => $submit)));
+      if (!$ajax && isset($submit)) {
+        $this->assertTrue($submit_matches, t('Found the @submit button', array('@submit' => $submit)));
+      }
       $this->fail(t('Found the requested form fields at @path', array('@path' => $path)));
     }
   }
 
   /**
+   * Runs cron in the Drupal installed by Simpletest.
+   */
+  protected function cronRun() {
+    $this->drupalGet($GLOBALS['base_url'] . '/cron.php', array('external' => TRUE));
+  }
+
+  /**
    * Check for meta refresh tag and if found call drupalGet() recursively. This
    * function looks for the http-equiv attribute to be set to "Refresh"
    * and is case-sensitive.
@@ -1584,7 +1653,7 @@ class DrupalWebTestCase extends DrupalTe
    *   Either the new page content or FALSE.
    */
   protected function checkForMetaRefresh() {
-    if ($this->drupalGetContent() != '' && $this->parse()) {
+    if (strpos($this->drupalGetContent(), '<meta ') && $this->parse()) {
       $refresh = $this->xpath('//meta[@http-equiv="Refresh"]');
       if (!empty($refresh)) {
         // Parse the content attribute of the meta tag for the format:
@@ -1635,15 +1704,15 @@ class DrupalWebTestCase extends DrupalTe
    */
   protected function handleForm(&$post, &$edit, &$upload, $submit, $form) {
     // Retrieve the form elements.
-    $elements = $form->xpath('.//input|.//textarea|.//select');
+    $elements = $form->xpath('.//input[not(@disabled)]|.//textarea[not(@disabled)]|.//select[not(@disabled)]');
     $submit_matches = FALSE;
     foreach ($elements as $element) {
       // SimpleXML objects need string casting all the time.
       $name = (string) $element['name'];
       // This can either be the type of <input> or the name of the tag itself
       // for <select> or <textarea>.
-      $type = isset($element['type']) ? (string)$element['type'] : $element->getName();
-      $value = isset($element['value']) ? (string)$element['value'] : '';
+      $type = isset($element['type']) ? (string) $element['type'] : $element->getName();
+      $value = isset($element['value']) ? (string) $element['value'] : '';
       $done = FALSE;
       if (isset($edit[$name])) {
         switch ($type) {
@@ -1675,22 +1744,37 @@ class DrupalWebTestCase extends DrupalTe
             break;
           case 'select':
             $new_value = $edit[$name];
-            $index = 0;
-            $key = preg_replace('/\[\]$/', '', $name);
             $options = $this->getAllOptions($element);
-            foreach ($options as $option) {
-              if (is_array($new_value)) {
-                $option_value= (string)$option['value'];
-                if (in_array($option_value, $new_value)) {
-                  $post[$key . '[' . $index++ . ']'] = $option_value;
-                  $done = TRUE;
-                  unset($edit[$name]);
+            if (is_array($new_value)) {
+              // Multiple select box.
+              if (!empty($new_value)) {
+                $index = 0;
+                $key = preg_replace('/\[\]$/', '', $name);
+                foreach ($options as $option) {
+                  $option_value = (string) $option['value'];
+                  if (in_array($option_value, $new_value)) {
+                    $post[$key . '[' . $index++ . ']'] = $option_value;
+                    $done = TRUE;
+                    unset($edit[$name]);
+                  }
                 }
               }
-              elseif ($new_value == $option['value']) {
-                $post[$name] = $new_value;
-                unset($edit[$name]);
+              else {
+                // No options selected: do not include any POST data for the
+                // element.
                 $done = TRUE;
+                unset($edit[$name]);
+              }
+            }
+            else {
+              // Single select box.
+              foreach ($options as $option) {
+                if ($new_value == $option['value']) {
+                  $post[$name] = $new_value;
+                  unset($edit[$name]);
+                  $done = TRUE;
+                  break;
+                }
               }
             }
             break;
@@ -1703,7 +1787,7 @@ class DrupalWebTestCase extends DrupalTe
       if (!isset($post[$name]) && !$done) {
         switch ($type) {
           case 'textarea':
-            $post[$name] = (string)$element;
+            $post[$name] = (string) $element;
             break;
           case 'select':
             $single = empty($element['multiple']);
@@ -1717,10 +1801,10 @@ class DrupalWebTestCase extends DrupalTe
               if ($option['selected'] || ($first && $single)) {
                 $first = FALSE;
                 if ($single) {
-                  $post[$name] = (string)$option['value'];
+                  $post[$name] = (string) $option['value'];
                 }
                 else {
-                  $post[$key . '[' . $index++ . ']'] = (string)$option['value'];
+                  $post[$key . '[' . $index++ . ']'] = (string) $option['value'];
                 }
               }
             }
@@ -1729,7 +1813,7 @@ class DrupalWebTestCase extends DrupalTe
             break;
           case 'submit':
           case 'image':
-            if ($submit == $value) {
+            if (isset($submit) && $submit == $value) {
               $post[$name] = $value;
               $submit_matches = TRUE;
             }
@@ -1749,6 +1833,48 @@ class DrupalWebTestCase extends DrupalTe
   }
 
   /**
+   * Builds an XPath query.
+   *
+   * Builds an XPath query by replacing placeholders in the query by the value
+   * of the arguments.
+   *
+   * XPath 1.0 (the version supported by libxml2, the underlying XML library
+   * used by PHP) doesn't support any form of quotation. This function
+   * simplifies the building of XPath expression.
+   *
+   * @param $xpath
+   *   An XPath query, possibly with placeholders in the form ':name'.
+   * @param $args
+   *   An array of arguments with keys in the form ':name' matching the
+   *   placeholders in the query. The values may be either strings or numeric
+   *   values.
+   * @return
+   *   An XPath query with arguments replaced.
+   */
+  protected function buildXPathQuery($xpath, array $args = array()) {
+    // Replace placeholders.
+    foreach ($args as $placeholder => $value) {
+      // XPath 1.0 doesn't support a way to escape single or double quotes in a
+      // string literal. We split double quotes out of the string, and encode
+      // them separately.
+      if (is_string($value)) {
+        // Explode the text at the quote characters.
+        $parts = explode('"', $value);
+
+        // Quote the parts.
+        foreach ($parts as &$part) {
+          $part = '"' . $part . '"';
+        }
+
+        // Return the string.
+        $value = count($parts) > 1 ? 'concat(' . implode(', \'"\', ', $parts) . ')' : $parts[0];
+      }
+      $xpath = preg_replace('/' . preg_quote($placeholder) . '\b/', $value, $xpath);
+    }
+    return $xpath;
+  }
+
+  /**
    * Perform an xpath search on the contents of the internal browser. The search
    * is relative to the root element (HTML tag normally) of the page.
    *
@@ -1759,11 +1885,14 @@ class DrupalWebTestCase extends DrupalTe
    *   format and return values see the SimpleXML documentation,
    *   http://us.php.net/manual/function.simplexml-element-xpath.php.
    */
-  protected function xpath($xpath) {
+  protected function xpath($xpath, array $arguments = array()) {
     if ($this->parse()) {
+      $xpath = $this->buildXPathQuery($xpath, $arguments);
       return $this->elements->xpath($xpath);
     }
-    return FALSE;
+    else {
+      return FALSE;
+    }
   }
 
   /**
@@ -1806,8 +1935,8 @@ class DrupalWebTestCase extends DrupalTe
    *   TRUE if the assertion succeeded, FALSE otherwise.
    */
   protected function assertLink($label, $index = 0, $message = '', $group = 'Other') {
-    $links = $this->xpath('//a[text()="' . $label . '"]');
-    $message = ($message ?  $message : t('Link with label "!label" found.', array('!label' => $label)));
+    $links = $this->xpath('//a[normalize-space(text())=:label]', array(':label' => $label));
+    $message = ($message ?  $message : t('Link with label %label found.', array('%label' => $label)));
     return $this->assert(isset($links[$index]), $message, $group);
   }
 
@@ -1826,8 +1955,48 @@ class DrupalWebTestCase extends DrupalTe
    *   TRUE if the assertion succeeded, FALSE otherwise.
    */
   protected function assertNoLink($label, $message = '', $group = 'Other') {
-    $links = $this->xpath('//a[text()="' . $label . '"]');
-    $message = ($message ?  $message : t('Link with label "!label" not found.', array('!label' => $label)));
+    $links = $this->xpath('//a[normalize-space(text())=:label]', array(':label' => $label));
+    $message = ($message ?  $message : t('Link with label %label not found.', array('%label' => $label)));
+    return $this->assert(empty($links), $message, $group);
+  }
+
+  /**
+   * Pass if a link containing a given href (part) is found.
+   *
+   * @param $href
+   *   The full or partial value of the 'href' attribute of the anchor tag.
+   * @param $index
+   *   Link position counting from zero.
+   * @param $message
+   *   Message to display.
+   * @param $group
+   *   The group this message belongs to, defaults to 'Other'.
+   *
+   * @return
+   *   TRUE if the assertion succeeded, FALSE otherwise.
+   */
+  protected function assertLinkByHref($href, $index = 0, $message = '', $group = 'Other') {
+    $links = $this->xpath('//a[contains(@href, :href)]', array(':href' => $href));
+    $message = ($message ?  $message : t('Link containing href %href found.', array('%href' => $href)));
+    return $this->assert(isset($links[$index]), $message, $group);
+  }
+
+  /**
+   * Pass if a link containing a given href (part) is not found.
+   *
+   * @param $href
+   *   The full or partial value of the 'href' attribute of the anchor tag.
+   * @param $message
+   *   Message to display.
+   * @param $group
+   *   The group this message belongs to, defaults to 'Other'.
+   *
+   * @return
+   *   TRUE if the assertion succeeded, FALSE otherwise.
+   */
+  protected function assertNoLinkByHref($href, $message = '', $group = 'Other') {
+    $links = $this->xpath('//a[contains(@href, :href)]', array(':href' => $href));
+    $message = ($message ?  $message : t('No link containing href %href found.', array('%href' => $href)));
     return $this->assert(empty($links), $message, $group);
   }
 
@@ -1848,15 +2017,15 @@ class DrupalWebTestCase extends DrupalTe
    */
   protected function clickLink($label, $index = 0) {
     $url_before = $this->getUrl();
-    $urls = $this->xpath('//a[text()="' . $label . '"]');
+    $urls = $this->xpath('//a[normalize-space(text())=:label]', array(':label' => $label));
 
     if (isset($urls[$index])) {
       $url_target = $this->getAbsoluteUrl($urls[$index]['href']);
     }
 
-    $this->assertTrue(isset($urls[$index]), t('Clicked link "!label" (!url_target) from !url_before', array('!label' => $label, '!url_target' => $url_target, '!url_before' => $url_before)), t('Browser'));
+    $this->assertTrue(isset($urls[$index]), t('Clicked link %label (@url_target) from @url_before', array('%label' => $label, '@url_target' => $url_target, '@url_before' => $url_before)), t('Browser'));
 
-    if (isset($urls[$index])) {
+    if (isset($url_target)) {
       return $this->drupalGet($url_target);
     }
     return FALSE;
@@ -1866,26 +2035,28 @@ class DrupalWebTestCase extends DrupalTe
    * Takes a path and returns an absolute path.
    *
    * @param $path
-   *   The path, can be a Drupal path or a site-relative path. It might have a
-   *   query, too. Can even be an absolute path which is just passed through.
+   *   A path from the internal browser content.
    * @return
-   *   An absolute path.
+   *   The $path with $base_url prepended, if necessary.
    */
   protected function getAbsoluteUrl($path) {
-    $options = array('absolute' => TRUE);
+    global $base_url, $base_path;
+
     $parts = parse_url($path);
-    // This is more crude than the menu_is_external but enough here.
     if (empty($parts['host'])) {
-      $path = $parts['path'];
-      $base_path = base_path();
-      $n = strlen($base_path);
-      if (substr($path, 0, $n) == $base_path) {
-        $path = substr($path, $n);
-      }
-      if (isset($parts['query'])) {
-        $options['query'] = $parts['query'];
+      // Ensure that we have a string (and no xpath object).
+      $path = (string) $path;
+      // Strip $base_path, if existent.
+      $length = strlen($base_path);
+      if (substr($path, 0, $length) === $base_path) {
+        $path = substr($path, $length);
+      }
+      // Ensure that we have an absolute path.
+      if ($path[0] !== '/') {
+        $path = '/' . $path;
       }
-      $path = url($path, $options);
+      // Finally, prepend the $base_url.
+      $path = $base_url . $path;
     }
     return $path;
   }
@@ -2007,7 +2178,6 @@ class DrupalWebTestCase extends DrupalTe
 
     foreach ($captured_emails as $message) {
       foreach ($filter as $key => $value) {
-//        if (!isset($message[$key]) || $message[$key] != $value) {
         if (!isset($message['params'][$key]) || $message['params'][$key] != $value) {
           continue 2;
         }
@@ -2049,9 +2219,9 @@ class DrupalWebTestCase extends DrupalTe
    */
   protected function assertRaw($raw, $message = '', $group = 'Other') {
     if (!$message) {
-      $message = t('Raw "@raw" found', array('@raw' => check_plain($raw)));
+      $message = t('Raw "@raw" found', array('@raw' => $raw));
     }
-    return $this->assert(strpos($this->content, $raw) !== FALSE, $message, $group);
+    return $this->assert(strpos($this->drupalGetContent(), $raw) !== FALSE, $message, $group);
   }
 
   /**
@@ -2069,9 +2239,9 @@ class DrupalWebTestCase extends DrupalTe
    */
   protected function assertNoRaw($raw, $message = '', $group = 'Other') {
     if (!$message) {
-      $message = t('Raw "@raw" not found', array('@raw' => check_plain($raw)));
+      $message = t('Raw "@raw" not found', array('@raw' => $raw));
     }
-    return $this->assert(strpos($this->content, $raw) === FALSE, $message, $group);
+    return $this->assert(strpos($this->drupalGetContent(), $raw) === FALSE, $message, $group);
   }
 
   /**
@@ -2126,9 +2296,9 @@ class DrupalWebTestCase extends DrupalTe
    * @return
    *   TRUE on pass, FALSE on fail.
    */
-  protected function assertTextHelper($text, $message, $group, $not_exists) {
+  protected function assertTextHelper($text, $message = '', $group, $not_exists) {
     if ($this->plainTextContent === FALSE) {
-      $this->plainTextContent = filter_xss($this->content, array());
+      $this->plainTextContent = filter_xss($this->drupalGetContent(), array());
     }
     if (!$message) {
       $message = !$not_exists ? t('"@text" found', array('@text' => $text)) : t('"@text" not found', array('@text' => $text));
@@ -2192,9 +2362,9 @@ class DrupalWebTestCase extends DrupalTe
    * @return
    *   TRUE on pass, FALSE on fail.
    */
-  protected function assertUniqueTextHelper($text, $message, $group, $be_unique) {
+  protected function assertUniqueTextHelper($text, $message = '', $group, $be_unique) {
     if ($this->plainTextContent === FALSE) {
-      $this->plainTextContent = filter_xss($this->content, array());
+      $this->plainTextContent = filter_xss($this->drupalGetContent(), array());
     }
     if (!$message) {
       $message = '"' . $text . '"' . ($be_unique ? ' found only once' : ' found more than once');
@@ -2258,7 +2428,7 @@ class DrupalWebTestCase extends DrupalTe
    * @return
    *   TRUE on pass, FALSE on fail.
    */
-  protected function assertTitle($title, $message, $group = 'Other') {
+  protected function assertTitle($title, $message = '', $group = 'Other') {
     return $this->assertEqual(current($this->xpath('//title')), $title, $message, $group);
   }
 
@@ -2274,7 +2444,7 @@ class DrupalWebTestCase extends DrupalTe
    * @return
    *   TRUE on pass, FALSE on fail.
    */
-  protected function assertNoTitle($title, $message, $group = 'Other') {
+  protected function assertNoTitle($title, $message = '', $group = 'Other') {
     return $this->assertNotEqual(current($this->xpath('//title')), $title, $message, $group);
   }
 
@@ -2292,7 +2462,7 @@ class DrupalWebTestCase extends DrupalTe
    * @return
    *   TRUE on pass, FALSE on fail.
    */
-  protected function assertFieldByXPath($xpath, $value, $message, $group = 'Other') {
+  protected function assertFieldByXPath($xpath, $value, $message = '', $group = 'Other') {
     $fields = $this->xpath($xpath);
 
     // If value specified then check array for match.
@@ -2364,7 +2534,7 @@ class DrupalWebTestCase extends DrupalTe
    * @return
    *   TRUE on pass, FALSE on fail.
    */
-  protected function assertNoFieldByXPath($xpath, $value, $message, $group = 'Other') {
+  protected function assertNoFieldByXPath($xpath, $value, $message = '', $group = 'Other') {
     $fields = $this->xpath($xpath);
 
     // If value specified then check array for match.
@@ -2465,7 +2635,7 @@ class DrupalWebTestCase extends DrupalTe
    *   TRUE on pass, FALSE on fail.
    */
   protected function assertFieldChecked($id, $message = '') {
-    $elements = $this->xpath('//input[@id="' . $id . '"]');
+    $elements = $this->xpath('//input[@id=:id]', array(':id' => $id));
     return $this->assertTrue(isset($elements[0]) && !empty($elements[0]['checked']), $message ? $message : t('Checkbox field @id is checked.', array('@id' => $id)), t('Browser'));
   }
 
@@ -2480,11 +2650,45 @@ class DrupalWebTestCase extends DrupalTe
    *   TRUE on pass, FALSE on fail.
    */
   protected function assertNoFieldChecked($id, $message = '') {
-    $elements = $this->xpath('//input[@id="' . $id . '"]');
+    $elements = $this->xpath('//input[@id=:id]', array(':id' => $id));
     return $this->assertTrue(isset($elements[0]) && empty($elements[0]['checked']), $message ? $message : t('Checkbox field @id is not checked.', array('@id' => $id)), t('Browser'));
   }
 
   /**
+   * Assert that a select option in the current page is not checked.
+   *
+   * @param $id
+   *   Id of select field to assert.
+   * @param $option
+   *   Option to assert.
+   * @param $message
+   *   Message to display.
+   * @return
+   *   TRUE on pass, FALSE on fail.
+   */
+  protected function assertOptionSelected($id, $option, $message = '') {
+    $elements = $this->xpath('//select[@id=:id]//option[@value=:option]', array(':id' => $id, ':option' => $option));
+    return $this->assertTrue(isset($elements[0]) && !empty($elements[0]['selected']), $message ? $message : t('Option @option for field @id is selected.', array('@option' => $option, '@id' => $id)), t('Browser'));
+  }
+
+  /**
+   * Assert that a select option in the current page is not checked.
+   *
+   * @param $id
+   *   Id of select field to assert.
+   * @param $option
+   *   Option to assert.
+   * @param $message
+   *   Message to display.
+   * @return
+   *   TRUE on pass, FALSE on fail.
+   */
+  protected function assertNoOptionSelected($id, $option, $message = '') {
+    $elements = $this->xpath('//select[@id=:id]//option[@value=:option]', array(':id' => $id, ':option' => $option));
+    return $this->assertTrue(isset($elements[0]) && empty($elements[0]['selected']), $message ? $message : t('Option @option for field @id is not selected.', array('@option' => $option, '@id' => $id)), t('Browser'));
+  }
+
+  /**
    * Assert that a field exists with the given name or id.
    *
    * @param $field
@@ -2517,6 +2721,36 @@ class DrupalWebTestCase extends DrupalTe
   }
 
   /**
+   * Assert that each HTML ID is used for just a single element.
+   *
+   * @param $message
+   *   Message to display.
+   * @param $group
+   *   The group this message belongs to.
+   * @param $ids_to_skip
+   *   An optional array of ids to skip when checking for duplicates. It is
+   *   always a bug to have duplicate HTML IDs, so this parameter is to enable
+   *   incremental fixing of core code. Whenever a test passes this parameter,
+   *   it should add a "todo" comment above the call to this function explaining
+   *   the legacy bug that the test wishes to ignore and including a link to an
+   *   issue that is working to fix that legacy bug.
+   * @return
+   *   TRUE on pass, FALSE on fail.
+   */
+  protected function assertNoDuplicateIds($message = '', $group = 'Other', $ids_to_skip = array()) {
+    $status = TRUE;
+    foreach ($this->xpath('//*[@id]') as $element) {
+      $id = (string) $element['id'];
+      if (isset($seen_ids[$id]) && !in_array($id, $ids_to_skip)) {
+        $this->fail(t('The HTML ID %id is unique.', array('%id' => $id)), $group);
+        $status = FALSE;
+      }
+      $seen_ids[$id] = TRUE;
+    }
+    return $this->assert($status, $message, $group);
+  }
+
+  /**
    * Helper function: construct an XPath for the given set of attributes and value.
    *
    * @param $attribute
@@ -2527,7 +2761,8 @@ class DrupalWebTestCase extends DrupalTe
    *   XPath for specified values.
    */
   protected function constructFieldXpath($attribute, $value) {
-    return '//textarea[@' . $attribute . '="' . $value . '"]|//input[@' . $attribute . '="' . $value . '"]|//select[@' . $attribute . '="' . $value . '"]';
+    $xpath = '//textarea[@' . $attribute . '=:value]|//input[@' . $attribute . '=:value]|//select[@' . $attribute . '=:value]';
+    return $this->buildXPathQuery($xpath, array(':value' => $value));
   }
 
   /**
@@ -2548,7 +2783,9 @@ class DrupalWebTestCase extends DrupalTe
   }
 
   /**
-   * Assert that the most recently sent e-mail message has a field with the given value.
+   * Asserts that the most recently sent e-mail message has the given value.
+   *
+   * The field in $name must have the content described in $value.
    *
    * @param $name
    *   Name of field or message property to assert. Examples: subject, body, id, ...
@@ -2556,6 +2793,7 @@ class DrupalWebTestCase extends DrupalTe
    *   Value of the field to assert.
    * @param $message
    *   Message to display.
+   *
    * @return
    *   TRUE on pass, FALSE on fail.
    */
@@ -2571,25 +2809,27 @@ class DrupalWebTestCase extends DrupalTe
   }
 
   /**
-   * Log verbose message in a text file.
+   * Logs verbose message in a text file.
    *
    * The a link to the vebose message will be placed in the test results via
    * as a passing assertion with the text '[verbose message]'.
    *
    * @param $message
    *   The verbose message to be stored.
+   *
    * @see simpletest_verbose()
    */
   protected function verbose($message) {
     if ($id = simpletest_verbose($message)) {
-      $this->pass(l(t('Verbose message'), $this->originalFileDirectory . '/simpletest/verbose/' . get_class($this) . '-' . $id . '.html', array('attributes' => array('target' => '_blank'))), 'Debug');
+      $url = $GLOBALS['base_url'] . '/' . $this->originalFileDirectory . '/simpletest/verbose/' . get_class($this) . '-' . $id . '.html';
+      $this->error(l(t('Verbose message'), $url, array('attributes' => array('target' => '_blank'))), 'User notice');
     }
   }
 
 }
 
 /**
- * Log verbose message in a text file.
+ * Logs verbose message in a text file.
  *
  * If verbose mode is enabled then page requests will be dumped to a file and
  * presented on the test result screen. The messages will be placed in a file
@@ -2601,15 +2841,15 @@ class DrupalWebTestCase extends DrupalTe
  *   The original file directory, before it was changed for testing purposes.
  * @param $test_class
  *   The active test case class.
+ *
  * @return
  *   The ID of the message to be placed in related assertion messages.
+ *
  * @see DrupalTestCase->originalFileDirectory
  * @see DrupalWebTestCase->verbose()
  */
 function simpletest_verbose($message, $original_file_directory = NULL, $test_class = NULL) {
-  static $file_directory = NULL, $class = NULL, $id = 1;
-//  $verbose = &drupal_static(__FUNCTION__);
-  static $verbose;
+  static $file_directory = NULL, $class = NULL, $id = 1, $verbose = NULL;
 
   // Will pass first time during setup phase, and when verbose is TRUE.
   if (!isset($original_file_directory) && !$verbose) {
@@ -2625,10 +2865,13 @@ function simpletest_verbose($message, $o
   if ($original_file_directory) {
     $file_directory = $original_file_directory;
     $class = $test_class;
-    $verbose = variable_get('simpletest_verbose', FALSE);
+    $verbose = variable_get('simpletest_verbose', TRUE);
     $directory = $file_directory . '/simpletest/verbose';
-//    return file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
-    return file_check_directory($directory, FILE_CREATE_DIRECTORY);
+    $writable = file_check_directory($directory, FILE_CREATE_DIRECTORY);
+    if ($writable && !file_exists($directory . '/.htaccess')) {
+      file_put_contents($directory . '/.htaccess', "<IfModule mod_expires.c>\nExpiresActive Off\n</IfModule>\n");
+    }
+    return $writable;
   }
   return FALSE;
 }
Index: simpletest.api.php
===================================================================
RCS file: simpletest.api.php
diff -N simpletest.api.php
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ simpletest.api.php	22 Aug 2010 23:46:59 -0000
@@ -0,0 +1,61 @@
+<?php
+// $Id: simpletest.api.php,v 1.4 2009/12/20 21:12:54 dries Exp $
+
+/**
+ * @file
+ * Hooks provided by the SimpleTest module.
+ */
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * Alter the list of tests.
+ *
+ * @param $groups
+ *   A two dimension array, the first key is the test group (as defined in
+ *   getInfo) the second is the name of the class and the value is the return
+ *   value of the getInfo method.
+ */
+function hook_simpletest_alter(&$groups) {
+  // An alternative session handler module would not want to run the original
+  // Session https handling test because it checks the sessions table in the
+  // database.
+  unset($groups['Session']['testHttpsSession']);
+}
+
+/**
+ * A test group has started.
+ *
+ * This hook is called just once at the beginning of a test group.
+ */
+function hook_test_group_started() {
+}
+
+/**
+ * A test group has finished.
+ *
+ * This hook is called just once at the end of a test group.
+ */
+function hook_test_group_finished() {
+}
+
+/**
+ * An individual test has finished.
+ *
+ * This hook is called when an individual test has finished.
+ *
+ * @param
+ *   $results The results of the test as gathered by DrupalWebTestCase.
+ *
+ * @see DrupalWebTestCase->results
+ */
+function hook_test_finished($results) {
+}
+
+
+/**
+ * @} End of "addtogroup hooks".
+ */
Index: simpletest.css
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/simpletest/Attic/simpletest.css,v
retrieving revision 1.1.6.4
diff -u -p -r1.1.6.4 simpletest.css
--- simpletest.css	5 Sep 2009 13:34:10 -0000	1.1.6.4
+++ simpletest.css	22 Aug 2010 23:46:59 -0000
@@ -1,5 +1,4 @@
 /* $Id: simpletest.css,v 1.1.6.4 2009/09/05 13:34:10 boombatower Exp $
-/* Core: Id: simpletest.css,v 1.6 2009/08/04 06:47:00 webchick Exp */
 
 /* Addon for the simpletest module */
 #simpletest {
@@ -72,3 +71,22 @@ div.simpletest-image {
   display: inline;
   cursor: pointer;
 }
+
+a.simpletest-collapse {
+  height: 0;
+  width: 0;
+  top: -99em;
+  position: absolute;
+
+}
+
+a.simpletest-collapse:focus,
+a.simpletest-collapse:hover {
+  font-size: 80%;
+  top: 0px;
+  height: auto;
+  width: auto;
+  overflow: visible;
+  position: relative;
+  z-index: 1000;
+}
Index: simpletest.info
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/simpletest/Attic/simpletest.info,v
retrieving revision 1.4.2.2.2.8
diff -u -p -r1.4.2.2.2.8 simpletest.info
--- simpletest.info	4 Jan 2010 22:38:18 -0000	1.4.2.2.2.8
+++ simpletest.info	22 Aug 2010 23:46:59 -0000
@@ -1,43 +1,13 @@
 ; $Id: simpletest.info,v 1.4.2.2.2.8 2010/01/04 22:38:18 boombatower Exp $
-; Core: Id: simpletest.info,v 1.10 2009/08/31 18:30:26 webchick Exp
-name = "SimpleTest"
+name = SimpleTest
 description = "Provides a framework for unit and functional testing."
-; package = Core
 package = Development
-; version = VERSION
-; core = 7.x
+dependencies[] = autoload
 core = 6.x
-php = 5.2 ; Drupal 6
+php = 5.2
 files[] = simpletest.module
 files[] = simpletest.pages.inc
 files[] = simpletest.install
 files[] = simpletest.test
 files[] = drupal_web_test_case.php
-
-; Drupal 6
-files[] = tests/block.test
-
-; Tests in tests directory.
-; files[] = tests/actions.test
-; files[] = tests/batch.test
-; files[] = tests/bootstrap.test
-; files[] = tests/browser.test
-; files[] = tests/cache.test
-; files[] = tests/common.test
-; files[] = tests/database_test.test
-; files[] = tests/error.test
-; files[] = tests/file.test
-; files[] = tests/filetransfer.test
-; files[] = tests/form.test
-; files[] = tests/graph.test
-; files[] = tests/image.test
-; files[] = tests/lock.test
-; files[] = tests/mail.test
-; files[] = tests/menu.test
-; files[] = tests/module.test
-; files[] = tests/registry.test
-; files[] = tests/schema.test
-; files[] = tests/session.test
-; files[] = tests/theme.test
-; files[] = tests/unicode.test
-; files[] = tests/xmlrpc.test
+files[] = tests/bootstrap.test
Index: simpletest.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/simpletest/Attic/simpletest.install,v
retrieving revision 1.4.2.3.2.22
diff -u -p -r1.4.2.3.2.22 simpletest.install
--- simpletest.install	14 Dec 2009 23:36:34 -0000	1.4.2.3.2.22
+++ simpletest.install	22 Aug 2010 23:46:59 -0000
@@ -1,120 +1,33 @@
 <?php
 // $Id: simpletest.install,v 1.4.2.3.2.22 2009/12/14 23:36:34 boombatower Exp $
-// Core: Id: simpletest.install,v 1.26 2009/08/17 19:14:41 webchick Exp
 
 /**
  * @file
- * Backport of Drupal 7 simpletest.install with modifications, see BACKPORT.txt.
- *
- * Copyright 2008-2009 by Jimmy Berry ("boombatower", http://drupal.org/user/214218)
+ * Install, update and uninstall functions for the simpletest module.
  */
 
 /**
- * Implementation of hook_install().
+ * Implements hook_install().
  */
 function simpletest_install() {
+  // Install schema.
   drupal_install_schema('simpletest');
-  // Check for files directory.
-//  $path = 'public://simpletest';
-//  if (file_prepare_directory($path, FILE_CREATE_DIRECTORY)) {
-  $path = file_directory_path() . '/simpletest';
-  if (file_check_directory($path, FILE_CREATE_DIRECTORY)) {
-    // Generate binary and text test files.
-    $generated = FALSE;
-    if (simpletest_get_file_count($path, 'binary') == 0) {
-      $lines = array(64, 1024);
-      foreach ($lines as $line) {
-        simpletest_generate_file('binary', 64, $line, 'binary');
-      }
-      $generated = TRUE;
-    }
-
-    if (simpletest_get_file_count($path, 'text') == 0) {
-      $lines = array(16, 256, 1024, 2048, 20480);
-      foreach ($lines as $line) {
-        simpletest_generate_file('text', 64, $line);
-      }
-      $generated = TRUE;
-    }
-
-    // Copy other test files for consistency.
-    $original = drupal_get_path('module', 'simpletest') . '/files';
-//    $files = file_scan_directory($original, '/(html|image|javascript|php|sql)-.*/');
-    $files = file_scan_directory($original, '(html|image|javascript|php|sql)-.*');
-
-    // If there are more files in SimpleTest's files directory than the site's
-    // files directory, restore all the files. This situation might occur when
-    // an errant test deletes one or more files from the site's files
-    // directory. It serves a convenience to developers so that they can get
-    // the test files back easily.
-//    if (count($files) > count(file_scan_directory($path, '/(html|image|javascript|php|sql)-.*/'))) {
-    if (count($files) > count(file_scan_directory($path, '(html|image|javascript|php|sql)-.*'))) {
-      foreach ($files as $file) {
-//        file_unmanaged_copy($file->uri, $path, FILE_EXISTS_REPLACE);
-        copy($file->filename, $path . '/' . $file->basename);
-      }
-      $generated = TRUE;
-    }
-
-    if ($generated) {
-      drupal_set_message('Extra test files generated/copied.');
-    }
-  }
-}
-
-/**
- * Generate test file.
- */
-function simpletest_generate_file($filename, $width, $lines, $type = 'binary-text') {
-  $size = $width * $lines - $lines;
-
-  // Generate random text
-  $text = '';
-  for ($i = 0; $i < $size; $i++) {
-    switch ($type) {
-      case 'text':
-        $text .= chr(rand(32, 126));
-        break;
-      case 'binary':
-        $text .= chr(rand(0, 31));
-        break;
-      case 'binary-text':
-      default:
-        $text .= rand(0, 1);
-        break;
-    }
-  }
-  $text = wordwrap($text, $width - 1, "\n", TRUE) . "\n"; // Add \n for symetrical file.
 
-  // Create filename.
-  $path = file_directory_path() . '/simpletest/';
-  $count = simpletest_get_file_count($path, $filename);
-  file_put_contents($path . $filename . '-' . ($count + 1) . '.txt', $text);
-}
-
-/**
- * Get the number of files that have the specified filename base.
- */
-function simpletest_get_file_count($directory, $filename) {
-  $files = scandir($directory);
-  $count = 0;
-  foreach ($files as $file) {
-    if (preg_match('/' . $filename . '.*?/', $file)) {
-      $count++;
-    }
-  }
-  return $count;
+  // Create the SimpleTest directory.
+  $path = file_directory_path() . '/simpletest';
+  file_check_directory($path, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
 }
 
 /**
- * Implementation of hook_uninstall().
+ * Implements hook_uninstall().
  */
 function simpletest_uninstall() {
   simpletest_clean_environment();
 
   // Remove settings variables.
-  variable_del('simpletest_username');
-  variable_del('simpletest_password');
+  variable_del('simpletest_httpauth_method');
+  variable_del('simpletest_httpauth_username');
+  variable_del('simpletest_httpauth_password');
   variable_del('simpletest_clear_results');
   variable_del('simpletest_verbose');
 
@@ -123,13 +36,7 @@ function simpletest_uninstall() {
 
   // Remove generated files.
   $path = file_directory_path() . '/simpletest';
-//  $files = file_scan_directory($path, '/.*/');
-  $files = file_scan_directory($path, '.*');
-  foreach ($files as $file) {
-//    file_unmanaged_delete($file->uri);
-    unlink($file->filename);
-  }
-  rmdir($path);
+  file_unmanaged_delete_recursive($path);
 }
 
 /**
@@ -142,6 +49,8 @@ function simpletest_requirements($phase)
   $has_curl = function_exists('curl_init');
   $has_hash = function_exists('hash_hmac');
   $has_domdocument = class_exists('DOMDocument');
+  $open_basedir = ini_get('open_basedir');
+  $patch_installed = isset($GLOBALS['simpletest_installed']);
 
   $requirements['curl'] = array(
     'title' => $t('cURL'),
@@ -166,28 +75,30 @@ 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. Please 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('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')));
   }
 
-  // Drupal 6.
-  // Check that the global variable is defined signifying that the code was inserted correctly.
-  if (isset($GLOBALS['simpletest_installed'])) {
-     $requirements['simpletest_settings'] = array(
-      'title' => $t('SimpleTest code addition'),
-      'value' => t('Found'),
-      'severity' => REQUIREMENT_OK
-    );
+  // SimpleTest currently needs 2 cURL options which are incompatible with
+  // having PHP's open_basedir restriction set.
+  // See http://drupal.org/node/674304.
+  $requirements['php_open_basedir'] = array(
+    'title' => $t('PHP open_basedir restriction'),
+    'value' => $open_basedir ? $t('Enabled') : $t('Disabled'),
+  );
+  if ($open_basedir) {
+    $requirements['php_open_basedir']['severity'] = REQUIREMENT_ERROR;
+    $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'));
   }
-  else {
-    $requirements['simpletest_settings'] = array(
-      'title' => $t('SimpleTest code addition'),
-      'value' => t('Not-found'),
-      'severity' => REQUIREMENT_ERROR,
-      'description' => $t('SimpleTest could not be installed. Must apply core patch, please see
-                           <a href="@install">INSTALL.txt</a>.',
-                           array('%settings' => realpath(conf_path() . '/settings.php'),
-                                 '@install' => base_path() . drupal_get_path('module', 'simpletest') . '/INSTALL.txt')),
-    );
+
+  // Check that the global variable is defined signifying that the patch
+  // was correctly applied to the Drupal 6 core.
+  $requirements['simpletest_patch'] = array(
+    'title' => $t('SimpleTest code addition'),
+    'value' => $patch_installed ? $t('Found') : $t('Not found'),
+  );
+  if (!$patch_installed) {
+    $requirements['simpletest_patch']['severity'] = REQUIREMENT_ERROR;
+    $requirements['simpletest_patch']['description'] = $t('SimpleTest could not be installed. Must apply core patch, please see <a href="@install">INSTALL.txt</a>.', array('@install' => base_path() . drupal_get_path('module', 'simpletest') . '/INSTALL.txt'));
   }
 
   return $requirements;
@@ -306,3 +217,16 @@ function simpletest_update_6200() {
 
   return $ret;
 }
+
+/**
+ * Renamed authentication username and password variables.
+ */
+function simpletest_update_6201() {
+  $username = variable_get('simpletest_username', '');
+  $password = variable_get('simpletest_password', '');
+  variable_set('simpletest_httpauth_username', $username);
+  variable_set('simpletest_httpauth_password', $password);
+  variable_del('simpletest_username', '');
+  variable_del('simpletest_password', '');
+  return array();
+}
Index: simpletest.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/simpletest/Attic/simpletest.js,v
retrieving revision 1.2.4.6
diff -u -p -r1.2.4.6 simpletest.js
--- simpletest.js	14 Dec 2009 23:29:36 -0000	1.2.4.6
+++ simpletest.js	22 Aug 2010 23:46:59 -0000
@@ -1,26 +1,19 @@
 // $Id: simpletest.js,v 1.2.4.6 2009/12/14 23:29:36 boombatower Exp $
-// Core: Id: simpletest.js,v 1.11 2009/04/27 20:19:37 webchick Exp
-//(function ($) {
+(function ($) {
 
 /**
  * Add the cool table collapsing on the testing overview page.
  */
-//Drupal.behaviors.simpleTestMenuCollapse = {
-//  attach: function (context, settings) {
 Drupal.behaviors.simpleTestMenuCollapse = function() {
     var timeout = null;
     // Adds expand-collapse functionality.
     $('div.simpletest-image:not(.simpletest-image-processed)').addClass('simpletest-image-processed').each(function () {
-//      direction = settings.simpleTest[$(this).attr('id')].imageDirection;
-//      $(this).html(settings.simpleTest.images[direction]);
       direction = Drupal.settings.simpleTest[$(this).attr('id')].imageDirection;
       $(this).html(Drupal.settings.simpleTest.images[direction]);
     });
 
     // Adds group toggling functionality to arrow images.
     $('div.simpletest-image:not(.simpletest-click-processed)').addClass('simpletest-click-processed').click(function () {
-//      var trs = $(this).parents('tbody').children('.' + settings.simpleTest[this.id].testClass);
-//      var direction = settings.simpleTest[this.id].imageDirection;
       var trs = $(this).parents('tbody').children('.' + Drupal.settings.simpleTest[this.id].testClass);
       var direction = Drupal.settings.simpleTest[this.id].imageDirection;
       var row = direction ? trs.size() - 1 : 0;
@@ -54,24 +47,18 @@ Drupal.behaviors.simpleTestMenuCollapse 
       rowToggle();
 
       // Toggle the arrow image next to the test group title.
-//      $(this).html(settings.simpleTest.images[(direction ? 0 : 1)]);
-//      settings.simpleTest[this.id].imageDirection = !direction;
       $(this).html(Drupal.settings.simpleTest.images[(direction ? 0 : 1)]);
       Drupal.settings.simpleTest[this.id].imageDirection = !direction;
 
     });
-//  }
 };
 
 /**
  * Select/deselect all the inner checkboxes when the outer checkboxes are
  * selected/deselected.
  */
-//Drupal.behaviors.simpleTestSelectAll = {
-//  attach: function (context, settings) {
 Drupal.behaviors.simpleTestSelectAll = function() {
     $('td.simpletest-select-all:not(.simpletest-select-processed)').addClass('simpletest-select-processed').each(function () {
-//      var testCheckboxes = settings.simpleTest['simpletest-test-group-' + $(this).attr('id')].testNames;
       var testCheckboxes = Drupal.settings.simpleTest['simpletest-test-group-' + $(this).attr('id')].testNames;
       var groupCheckbox = $('<input type="checkbox" class="form-checkbox" id="' + $(this).attr('id') + '-select-all" />');
 
@@ -108,7 +95,6 @@ Drupal.behaviors.simpleTestSelectAll = f
       updateGroupCheckbox();
       $(this).append(groupCheckbox);
     });
-//  }
 };
 
-//})(jQuery);
+})(jQuery);
Index: simpletest.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/simpletest/Attic/simpletest.module,v
retrieving revision 1.33.2.4.2.22
diff -u -p -r1.33.2.4.2.22 simpletest.module
--- simpletest.module	15 Mar 2010 22:32:49 -0000	1.33.2.4.2.22
+++ simpletest.module	22 Aug 2010 23:46:59 -0000
@@ -4,32 +4,32 @@
 
 /**
  * @file
- * Backport of Drupal 7 simpletest.module with modifications, see BACKPORT.txt.
- *
- * Copyright 2008-2009 by Jimmy Berry ("boombatower", http://drupal.org/user/214218)
+ * Provides testing functionality.
  */
 
 /**
- * Implementation of hook_help().
+ * Implements hook_help().
  */
 function simpletest_help($path, $arg) {
   switch ($path) {
     case 'admin/help#simpletest':
-      $output  = '<p>' . t('The SimpleTest module is a framework for running automated unit tests in Drupal. It can be used to verify a working state of Drupal before and after any code changes, or as a means for developers to write and execute tests for their modules.') . '</p>';
-//      $output .= '<p>' . t('Visit <a href="@admin-simpletest">Administer >> Structure >> SimpleTest</a> to display a list of available tests. For comprehensive testing, select <em>all</em> tests, or individually select tests for more targeted testing. Note that it might take several minutes for all tests to complete.)', array('@admin-simpletest' => url('admin/config/development/testing'))) . '</p>';
-      $output .= '<p>' . t('Visit <a href="@admin-simpletest">Administer >> Structure >> SimpleTest</a> to display a list of available tests. For comprehensive testing, select <em>all</em> tests, or individually select tests for more targeted testing. Note that it might take several minutes for all tests to complete.)', array('@admin-simpletest' => url('admin/build/testing'))) . '</p>';
-      $output .= '<p>' . t('After the tests have run, a message will be displayed next to each test group indicating whether tests within it passed, failed, or had exceptions. A pass means that a test returned the expected results, while fail means that it did not. An exception normally indicates an error outside of the test, such as a PHP warning or notice. If there were fails or exceptions, the results are expanded, and the tests that had issues will be indicated in red or pink rows. Use these results to refine your code and tests until all tests return a pass.') . '</p>';
-      $output .= '<p>' . t('For more information on creating and modifying your own tests, see the <a href="@simpletest-api">SimpleTest API Documentation</a> in the Drupal handbook.', array('@simpletest-api' => 'http://drupal.org/simpletest')) . '</p>';
-      $output .= '<p>' . t('For more information, see the online handbook entry for <a href="@simpletest">SimpleTest module</a>.', array('@simpletest' => 'http://drupal.org/handbook/modules/simpletest')) . '</p>';
+      $output = '';
+      $output .= '<h3>' . t('About') . '</h3>';
+      $output .= '<p>' . t('The SimpleTest module provides a framework for running automated unit tests. It can be used to verify a working state of Drupal before and after any code changes, or as a means for developers to write and execute tests for their modules. For more information, see the online handbook entry for <a href="@simpletest">SimpleTest module</a>.', array('@simpletest' => 'http://drupal.org/handbook/modules/simpletest', '@blocks' => url('admin/build/block'))) . '</p>';
+      $output .= '<h3>' . t('Uses') . '</h3>';
+      $output .= '<dl>';
+      $output .= '<dt>' . t('Running tests') . '</dt>';
+      $output .= '<dd>' . t('Visit the <a href="@admin-simpletest">Testing page</a> to display a list of available tests. For comprehensive testing, select <em>all</em> tests, or individually select tests for more targeted testing. Note that it might take several minutes for all tests to complete. For more information on creating and modifying your own tests, see the <a href="@simpletest-api">Testing API Documentation</a> in the Drupal handbook.', array('@simpletest-api' => 'http://drupal.org/simpletest', '@admin-simpletest' => url('admin/build/testing'))) . '</dd>';
+      $output .= '<dd>' . t('After the tests run, a message will be displayed next to each test group indicating whether tests within it passed, failed, or had exceptions. A pass means that the test returned the expected results, while fail means that it did not. An exception normally indicates an error outside of the test, such as a PHP warning or notice. If there were failures or exceptions, the results will be expanded to show details, and the tests that had failures or exceptions will be indicated in red or pink rows. You can then use these results to refine your code and tests, until all tests pass.') . '</dd>';
+      $output .= '</dl>';
       return $output;
   }
 }
 
 /**
- * Implementation of hook_menu().
+ * Implements hook_menu().
  */
 function simpletest_menu() {
-//  $items['admin/config/development/testing'] = array(
   $items['admin/build/testing'] = array(
     'title' => 'Testing',
     'page callback' => 'drupal_get_form',
@@ -38,12 +38,10 @@ function simpletest_menu() {
     'access arguments' => array('administer unit tests'),
     'file' => 'simpletest.pages.inc',
   );
-//  $items['admin/config/development/testing/list'] = array(
   $items['admin/build/testing/list'] = array(
     'title' => 'List',
     'type' => MENU_DEFAULT_LOCAL_TASK,
   );
-//  $items['admin/config/development/testing/settings'] = array(
   $items['admin/build/testing/settings'] = array(
     'title' => 'Settings',
     'page callback' => 'drupal_get_form',
@@ -52,11 +50,9 @@ function simpletest_menu() {
     'type' => MENU_LOCAL_TASK,
     'file' => 'simpletest.pages.inc',
   );
-//  $items['admin/config/development/testing/results/%'] = array(
   $items['admin/build/testing/results/%'] = array(
     'title' => 'Test result',
     'page callback' => 'drupal_get_form',
-//    'page arguments' => array('simpletest_result_form', 5),
     'page arguments' => array('simpletest_result_form', 4),
     'description' => 'View result of tests.',
     'access arguments' => array('administer unit tests'),
@@ -67,20 +63,20 @@ function simpletest_menu() {
 }
 
 /**
- * Implementation of hook_perm().
+ * Implements hook_permission().
  */
 function simpletest_perm() {
-//  return array(
-//    'administer unit tests' => array(
-//      'title' => t('Administer unit tests'),
-//      'description' => t('Manage and run automated testing. %warning', array('%warning' => t('Warning: Give to trusted roles only; this permission has security implications.'))),
-//    ),
-//  );
-  return array('administer unit tests');
+  $permissions = array(
+    'administer unit tests' => array(
+      'title' => t('Administer tests'),
+      'restrict access' => TRUE,
+    ),
+  );
+  return array_keys($permissions);
 }
 
 /**
- * Implementation of hook_theme().
+ * Implements hook_theme().
  */
 function simpletest_theme() {
   return array(
@@ -95,31 +91,6 @@ function simpletest_theme() {
   );
 }
 
-///**
-// * Implementation of hook_stream_wrappers().
-// */
-//function simpletest_test_stream_wrappers() {
-//  return array(
-//    'simpletest' => array(
-//      'name' => t('Simpletest files'),
-//      'class' => 'DrupalSimpleTestStreamWrapper',
-//      'description' => t('Stream Wrapper for Simpletest files.'),
-//    ),
-//  );
-//}
-
-///**
-// * Implement hook_js_alter().
-// */
-//function simpletest_js_alter(&$javascript) {
-//  // Since SimpleTest is a special use case for the table select, stick the
-//  // SimpleTest JavaScript above the table select.
-//  $simpletest = drupal_get_path('module', 'simpletest') . '/simpletest.js';
-//  if (array_key_exists($simpletest, $javascript) && array_key_exists('misc/tableselect.js', $javascript)) {
-//    $javascript[$simpletest]['weight'] = $javascript['misc/tableselect.js']['weight'] - 1;
-//  }
-//}
-
 function _simpletest_format_summary_line($summary) {
   $args = array(
     '@pass' => format_plural(isset($summary['#pass']) ? $summary['#pass'] : 0, '1 pass', '@count passes'),
@@ -144,15 +115,11 @@ function _simpletest_format_summary_line
  */
 function simpletest_run_tests($test_list, $reporter = 'drupal') {
   cache_clear_all();
-//  $test_id = db_insert('simpletest_test_id')
-//    ->useDefaults(array('test_id'))
-//    ->execute();
   db_query('INSERT INTO {simpletest_test_id} (test_id) VALUES (default)');
   $test_id = db_last_insert_id('simpletest_test_id', 'test_id');
 
   // Clear out the previous verbose files.
-//  file_unmanaged_delete_recursive(file_directory_path() . '/simpletest/verbose');
-  simpletest_clean_temporary_directory(file_directory_path() . '/simpletest/verbose');
+  file_unmanaged_delete_recursive(file_directory_path() . '/simpletest/verbose');
 
   // Get the info for the first test being run.
   $first_test = array_shift($test_list);
@@ -161,7 +128,7 @@ function simpletest_run_tests($test_list
   $info = $first_instance->getInfo();
 
   $batch = array(
-    'title' => t('Running SimpleTests'),
+    'title' => t('Running tests'),
     'operations' => array(
       array('_simpletest_batch_operation', array($test_list, $test_id)),
     ),
@@ -174,14 +141,7 @@ function simpletest_run_tests($test_list
 
   module_invoke_all('test_group_started');
 
-  // Normally, the forms portion of the batch API takes care of calling
-  // batch_process(), but in the process it saves the whole $form into the
-  // database (which is huge for the test selection form).
-  // By calling batch_process() directly, we skip that behavior and ensure
-  // that we don't exceed the size of data that can be sent to the database
-  // (max_allowed_packet on MySQL).
-//  batch_process('admin/config/development/testing/results/' . $test_id);
-  batch_process('admin/build/testing/results/' . $test_id);
+  return $test_id;
 }
 
 /**
@@ -204,17 +164,6 @@ function _simpletest_batch_operation($te
 
   // Perform the next test.
   $test_class = array_shift($test_list);
-
-  // Drupal 6.
-  require_once drupal_get_path('module', 'simpletest') . '/drupal_web_test_case.php';
-  $classes = simpletest_test_get_all_classes();
-
-//  require_once $classes[$test_class]['file'];
-  // Drupal 6.
-  if (!empty($classes)) {
-    require_once $classes[$test_class]['file'];
-  }
-
   $test = new $test_class($test_id);
   $test->run();
   $size = count($test_list);
@@ -246,10 +195,8 @@ function _simpletest_batch_operation($te
   $context['finished'] = 1 - $size / $max;
 }
 
-//function _simpletest_batch_finished($success, $results, $operations, $elapsed) {
 function _simpletest_batch_finished($success, $results, $operations) {
   if ($success) {
-//    drupal_set_message(t('The test run finished in @elapsed.', array('@elapsed' => $elapsed)));
     drupal_set_message(t('The test run finished.'));
   }
   else {
@@ -263,7 +210,7 @@ function _simpletest_batch_finished($suc
     simpletest_log_read($test_id, $last_prefix, $last_test_class);
 
     drupal_set_message(t('The test run did not successfully finish.'), 'error');
-    drupal_set_message(t('Please use the <em>Clean environment</em> button to clean-up temporary files and tables.'), 'warning');
+    drupal_set_message(t('Use the <em>Clean environment</em> button to clean-up temporary files and tables.'), 'warning');
   }
   module_invoke_all('test_group_finished');
 }
@@ -278,10 +225,8 @@ function _simpletest_batch_finished($suc
  *   that ran.
  */
 function simpletest_last_test_get($test_id) {
-//  $last_prefix = db_result(db_query_range('SELECT last_prefix FROM {simpletest_test_id} WHERE test_id = :test_id', array(':test_id' => $test_id), 0, 1));
-  $last_prefix = db_result(db_query_range('SELECT last_prefix FROM {simpletest_test_id} WHERE test_id = ' . $test_id, 0, 1));
-//  $last_test_class = db_result(db_query_range('SELECT test_class FROM {simpletest} WHERE test_id = :test_id ORDER BY message_id DESC', array(':test_id' => $test_id), 0, 1));
-  $last_test_class = db_result(db_query_range('SELECT test_class FROM {simpletest} WHERE test_id = ' . $test_id . ' ORDER BY message_id DESC', 0, 1));
+  $last_prefix = db_result(db_query_range('SELECT last_prefix FROM {simpletest_test_id} WHERE test_id = %d', array(':test_id' => $test_id), 0, 1));
+  $last_test_class = db_result(db_query_range('SELECT test_class FROM {simpletest} WHERE test_id = %d ORDER BY message_id DESC', array(':test_id' => $test_id), 0, 1));
   return array($last_prefix, $last_test_class);
 }
 
@@ -304,8 +249,6 @@ function simpletest_last_test_get($test_
  *   Found any entries in log.
  */
 function simpletest_log_read($test_id, $prefix, $test_class, $during_test = FALSE) {
-  require_once drupal_get_path('module', 'simpletest') . '/drupal_web_test_case.php';
-//  $log = 'public://' . ($during_test ? '' : '/simpletest/' . substr($prefix, 10)) . '/error.log';
   $log = file_directory_path() . ($during_test ? '' : '/simpletest/' . substr($prefix, 10)) . '/error.log';
   $found = FALSE;
   if (file_exists($log)) {
@@ -354,8 +297,7 @@ function simpletest_log_read($test_id, $
  * @see simpletest_registry_files_alter()
  */
 function simpletest_test_get_all() {
-//  $groups = &drupal_static(__FUNCTION__);
-  static $groups;
+  $groups = &drupal_static(__FUNCTION__);
 
   if (!$groups) {
     // Load test information from cache if available, otherwise retrieve the
@@ -365,93 +307,87 @@ function simpletest_test_get_all() {
     }
     else {
       // Select all clases in files ending with .test.
-//      $classes = db_select('registry')
-//        ->fields('registry', array('name'))
-//        ->condition('type', 'class')
-//        ->condition('filename', '%.test', 'LIKE')
-//        ->execute();
-      $classes = simpletest_test_get_all_classes();
-
-      $groups = array();
+      $classes = array();
+      $query = db_query("SELECT name FROM {autoload_registry} WHERE type = '%s' AND filename LIKE '%s'", 'class', '%%.test');
 
       // Check that each class has a getInfo() method and store the information
       // in an array keyed with the group specified in the test information.
-      foreach ($classes as $class) {
-//        $class = $class->name;
-        $class = $class['class'];
-
+      $groups = array();
+      while ($class = db_result($query)) {
+        // Test classes need to implement getInfo() to be valid.
         if (class_exists($class) && method_exists($class, 'getInfo')) {
-          // Valid test class, retrieve test information.
           $info = call_user_func(array($class, 'getInfo'));
 
-          // Initialize test groups.
-          if (!isset($groups[$info['group']])) {
-            $groups[$info['group']] = array();
+          // If this test class requires a non-existing module, skip it.
+          if (!empty($info['dependencies'])) {
+            foreach ($info['dependencies'] as $module) {
+              if (!drupal_get_filename('module', $module)) {
+                continue 2;
+              }
+            }
           }
+
           $groups[$info['group']][$class] = $info;
         }
       }
+
       // Sort the groups and tests within the groups by name.
       uksort($groups, 'strnatcasecmp');
       foreach ($groups as $group => &$tests) {
         uksort($tests, 'strnatcasecmp');
       }
 
+      // Allow modules extending core tests to disable originals.
+      drupal_alter('simpletest', $groups);
       cache_set('simpletest', $groups);
     }
   }
   return $groups;
 }
 
-function simpletest_test_get_all_classes() {
-  // Must load DrupalWebTestCase before loading any other test classes which
-  // will extend it.
-  require_once 'drupal_web_test_case.php';
-
-  $classes = array();
-  $files = module_rebuild_cache();
-  foreach ($files as $file) {
-    $directory = dirname($file->filename);
-    $test_files = file_scan_directory($directory, '\.test$', array('.', '..', 'CVS'), FALSE, FALSE);
-    $test_files += file_scan_directory($directory . '/tests', '\.test$');
-
+/**
+ * Implements hook_autoload_registry_files_alter().
+ */
+function simpletest_autoload_registry_files_alter(&$files, $modules) {
+  foreach ($modules as $module) {
+    $test_files = file_scan_directory($module->dir, '\.test$', array('.', '..', 'CVS'), FALSE, FALSE);
+    $test_files += file_scan_directory($module->dir . '/tests', '\.test$');
     foreach ($test_files as $test_file) {
-      $pre = get_declared_classes();
-      require_once $test_file->filename;
-      $post = get_declared_classes();
-
-      $classes_new = array_values(array_diff($post, $pre));
-      foreach ($classes_new as $class) {
-        $classes[$class] = array('file' => $test_file->filename, 'class' => $class);
+      if (!isset($files[$test_file->filename])) {
+        $files[$test_file->filename] = array('module' => $module->name, 'weight' => $module->weight);
       }
     }
   }
-
-  return $classes;
 }
 
-///**
-// * Implementation of hook_registry_files_alter().
-// *
-// * Add the test files for disabled modules so that we get a list containing
-// * all the avialable tests.
-// */
-//function simpletest_registry_files_alter(&$files, $modules) {
-//  foreach ($modules as $module) {
-//    // Only add test files for disabled modules, as enabled modules should
-//    // already include any test files they provide.
-//    if (!$module->status) {
-//      $dir = $module->dir;
-//      if (!empty($module->info['files'])) {
-//        foreach ($module->info['files'] as $file) {
-//          if (substr($file, -5) == '.test') {
-//            $files["$dir/$file"] = array('module' => $module->name, 'weight' => $module->weight);
-//          }
-//        }
-//      }
-//    }
-//  }
-//}
+/**
+ * Generate test file.
+ */
+function simpletest_generate_file($filename, $width, $lines, $type = 'binary-text') {
+  $size = $width * $lines - $lines;
+
+  // Generate random text
+  $text = '';
+  for ($i = 0; $i < $size; $i++) {
+    switch ($type) {
+      case 'text':
+        $text .= chr(rand(32, 126));
+        break;
+      case 'binary':
+        $text .= chr(rand(0, 31));
+        break;
+      case 'binary-text':
+      default:
+        $text .= rand(0, 1);
+        break;
+    }
+  }
+  $text = wordwrap($text, $width - 1, "\n", TRUE) . "\n"; // Add \n for symetrical file.
+
+  // Create filename.
+  file_put_contents(file_directory_path() . '/' . $filename . '.txt', $text);
+  return $filename;
+}
 
 /**
  * Remove all temporary database tables and directories.
@@ -466,26 +402,31 @@ function simpletest_clean_environment() 
   else {
     drupal_set_message(t('Clear results is disabled and the test results table will not be cleared.'), 'warning');
   }
+
+  // Detect test classes that have been added, renamed or deleted.
+  autoload_registry_rebuild();
+  cache_clear_all('simpletest', 'cache');
 }
 
 /**
  * Removed prefixed tables from the database that are left over from crashed tests.
  */
 function simpletest_clean_database() {
-//  $tables = db_find_tables(Database::getConnection()->prefixTables('{simpletest}') . '%');
-  $tables = simpletest_get_like_tables();
+  $tables = db_find_tables(db_prefix_tables('{simpletest}') . '%%');
   $schema = drupal_get_schema_unprocessed('simpletest');
+  $count = 0;
   $ret = array();
   foreach (array_diff_key($tables, $schema) as $table) {
     // Strip the prefix and skip tables without digits following "simpletest",
     // e.g. {simpletest_test_id}.
     if (preg_match('/simpletest\d+.*/', $table, $matches)) {
       db_drop_table($ret, $matches[0]);
+      $count++;
     }
   }
 
-  if (count($ret) > 0) {
-    drupal_set_message(format_plural(count($ret), 'Removed 1 leftover table.', 'Removed @count leftover tables.'));
+  if ($count > 0) {
+    drupal_set_message(format_plural($count, 'Removed 1 leftover table.', 'Removed @count leftover tables.'));
   }
   else {
     drupal_set_message(t('No leftover tables to remove.'));
@@ -493,41 +434,15 @@ function simpletest_clean_database() {
 }
 
 /**
- * Find all tables that are like the specified base table name. (Drupal 6)
- *
- * @param string $base_table Base table name.
- * @param boolean $count Return the table count instead of list of tables.
- * @return mixed Array of matching tables or count of tables.
- */
-function simpletest_get_like_tables($base_table = 'simpletest', $count = FALSE) {
-  global $db_url, $db_prefix;
-  $url = parse_url(is_array($db_url) ? $db_url['default'] : $db_url);
-  $database = substr($url['path'], 1);
-  $result = db_query("SELECT table_name FROM information_schema.tables WHERE (table_schema = '$database' OR table_catalog = '$database') AND table_name LIKE '$db_prefix$base_table%'");
-  $schema = drupal_get_schema_unprocessed('simpletest');
-
-  $tables = array();
-  while ($table = db_result($result)) {
-    if (!isset($schema[$table])) {
-      $tables[] = $table;
-    }
-  }
-  return ($count) ? count($tables) : $tables;
-}
-
-/**
  * Find all leftover temporary directories and remove them.
  */
 function simpletest_clean_temporary_directories() {
-//  $files = scandir('public://');
-  $files = scandir(file_directory_path());
+  $files = scandir(file_directory_path() . '/simpletest');
   $count = 0;
   foreach ($files as $file) {
-//    $path = 'public://' . $file;
-    $path = file_directory_path() . '/' . $file;
-    if (is_dir($path) && preg_match('/^simpletest\d+/', $file)) {
-//      file_unmanaged_delete_recursive($path);
-      simpletest_clean_temporary_directory($path);
+    $path = file_directory_path() . '/simpletest/' . $file;
+    if (is_dir($path) && is_numeric($file)) {
+      file_unmanaged_delete_recursive($path);
       $count++;
     }
   }
@@ -541,33 +456,6 @@ function simpletest_clean_temporary_dire
 }
 
 /**
- * Remove all files from specified directory and then remove directory. (Drupal 6)
- *
- * @param string $path Directory path.
- */
-function simpletest_clean_temporary_directory($path) {
-  // Drupal 6.
-  if (!is_dir($path)) {
-    return;
-  }
-
-  $files = scandir($path);
-  foreach ($files as $file) {
-    if ($file != '.' && $file != '..') {
-      $file_path = "$path/$file";
-      if (is_dir($file_path)) {
-        simpletest_clean_temporary_directory($file_path);
-      }
-      else {
-//        file_unmanaged_delete($file_path);
-        unlink($file_path);
-      }
-    }
-  }
-  rmdir($path);
-}
-
-/**
  * Clear the test result tables.
  *
  * @param $test_id
@@ -578,25 +466,15 @@ function simpletest_clean_temporary_dire
 function simpletest_clean_results_table($test_id = NULL) {
   if (variable_get('simpletest_clear_results', TRUE)) {
     if ($test_id) {
-//      $count = db_query('SELECT COUNT(test_id) FROM {simpletest_test_id} WHERE test_id = :test_id', array(':test_id' => $test_id))->fetchField();
       $count = db_result(db_query('SELECT COUNT(test_id) FROM {simpletest_test_id} WHERE test_id = %d', $test_id));
 
-//      db_delete('simpletest')
-//        ->condition('test_id', $test_id)
-//        ->execute();
-//      db_delete('simpletest_test_id')
-//        ->condition('test_id', $test_id)
-//        ->execute();
       db_query("DELETE FROM {simpletest} WHERE test_id = %d", $test_id);
       db_query("DELETE FROM {simpletest_test_id} WHERE test_id = %d", $test_id);
     }
     else {
-//      $count = db_query('SELECT COUNT(test_id) FROM {simpletest_test_id}')->fetchField();
       $count = db_result(db_query('SELECT COUNT(test_id) FROM {simpletest_test_id}'));
 
       // Clear test results.
-//      db_delete('simpletest')->execute();
-//      db_delete('simpletest_test_id')->execute();
       db_query('DELETE FROM {simpletest}');
       db_query('DELETE FROM {simpletest_test_id}');
     }
@@ -623,4 +501,3 @@ function simpletest_form_system_modules_
     }
   }
 }
-
Index: simpletest.pages.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/simpletest/Attic/simpletest.pages.inc,v
retrieving revision 1.1.2.2
diff -u -p -r1.1.2.2 simpletest.pages.inc
--- simpletest.pages.inc	14 Sep 2009 22:37:35 -0000	1.1.2.2
+++ simpletest.pages.inc	22 Aug 2010 23:46:59 -0000
@@ -9,9 +9,7 @@
 /**
  * List tests arranged in groups that can be selected and run.
  */
-function simpletest_test_form() {
-  $form = array();
-
+function simpletest_test_form(&$form_state) {
   $form['tests'] = array(
     '#type' => 'fieldset',
     '#title' => t('Tests'),
@@ -38,32 +36,34 @@ function simpletest_test_form() {
     }
   }
 
- // Operation buttons.
- $form['tests']['op'] = array(
-   '#type' => 'submit',
-   '#value' => t('Run tests'),
- );
- $form['clean'] = array(
-   '#type' => 'fieldset',
-   '#collapsible' => FALSE,
-   '#collapsed' => FALSE,
-   '#title' => t('Clean test environment'),
-   '#description' => t('Remove tables with the prefix "simpletest" and temporary directories that are left over from tests that crashed. This is intended for developers when creating tests.'),
- );
- $form['clean']['op'] = array(
-   '#type' => 'submit',
-   '#value' => t('Clean environment'),
-   '#submit' => array('simpletest_clean_environment'),
- );
+  // Operation buttons.
+  $form['tests']['op'] = array(
+    '#type' => 'submit',
+    '#value' => t('Run tests'),
+  );
+  $form['clean'] = array(
+    '#type' => 'fieldset',
+    '#collapsible' => FALSE,
+    '#collapsed' => FALSE,
+    '#title' => t('Clean test environment'),
+    '#description' => t('Remove tables with the prefix "simpletest" and temporary directories that are left over from tests that crashed. This is intended for developers when creating tests.'),
+  );
+  $form['clean']['op'] = array(
+    '#type' => 'submit',
+    '#value' => t('Clean environment'),
+    '#submit' => array('simpletest_clean_environment'),
+  );
 
   return $form;
 }
 
 /**
- * Theme the test list generated by simpletest_test_form() into a table.
+ * Returns HTML for a test list generated by simpletest_test_form() into a table.
  *
  * @param $table Form array that represent a table.
  * @return HTML output.
+ *
+ * @ingroup themeable
  */
 function theme_simpletest_test_table($table) {
   drupal_add_css(drupal_get_path('module', 'simpletest') . '/simpletest.css');
@@ -82,7 +82,7 @@ function theme_simpletest_test_table($ta
   $js = array(
     'images' => array(
       theme('image', 'misc/menu-collapsed.png', 'Expand', 'Expand'),
-      theme('image', 'misc/menu-expanded.png', 'Collapsed', 'Collapsed'),
+      theme('image', 'misc/menu-expanded.png', 'Collapsed', 'Collapse'),
     ),
   );
 
@@ -102,7 +102,6 @@ function theme_simpletest_test_table($ta
     $image_index = $collapsed ? 0 : 1;
 
     // Place-holder for checkboxes to select group of tests.
-//     $row[] = array('id' => $test_class, 'class' => array('simpletest-select-all'));
     $row[] = array('id' => $test_class, 'class' => 'simpletest-select-all');
 
     // Expand/collapse image and group title.
@@ -114,7 +113,6 @@ function theme_simpletest_test_table($ta
 
     $row[] = '&nbsp;';
 
-//    $rows[] = array('data' => $row, 'class' => array('simpletest-group'));
     $rows[] = array('data' => $row, 'class' => 'simpletest-group');
 
     // Add individual tests to group.
@@ -149,7 +147,6 @@ function theme_simpletest_test_table($ta
       $row[] = theme('indentation', 1) . '<label for="edit-' . $test_name . '">' . $title . '</label>';
       $row[] = '<div class="description">' . $description . '</div>';
 
-//      $rows[] = array('data' => $row, 'class' => array($test_class . '-test', ($collapsed ? 'js-hide' : '')));
       $rows[] = array('data' => $row, 'class' => $test_class . '-test ' . ($collapsed ? 'js-hide' : ''));
     }
     $js['simpletest-test-group-' . $test_class] = $current_js;
@@ -185,19 +182,16 @@ function _simpletest_sort_by_title($a, $
 function simpletest_test_form_submit($form, &$form_state) {
   // Get list of tests.
   $tests_list = array();
-  $classes = simpletest_test_get_all_classes(); // Drupal 6.
   foreach ($form_state['values'] as $class_name => $value) {
-    // Drupal 6.
-    if (isset($classes[$class_name]['file'])) {
-      require_once $classes[$class_name]['file'];
-    }
-
-    if (class_exists($class_name) && $value === 1) {
+    // 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;
     }
   }
   if (count($tests_list) > 0 ) {
-    simpletest_run_tests($tests_list, 'drupal');
+    $test_id = simpletest_run_tests($tests_list, 'drupal');
+    $form_state['redirect'] = 'admin/build/testing/results/' . $test_id;
   }
   else {
     drupal_set_message(t('No test(s) selected.'), 'error');
@@ -208,13 +202,10 @@ function simpletest_test_form_submit($fo
  * Test results form for $test_id.
  */
 function simpletest_result_form(&$form_state, $test_id) {
-  $form = array();
-
   // Make sure there are test results to display and a re-run is not being performed.
   $results = array();
   if (is_numeric($test_id) && !$results = simpletest_result_get($test_id)) {
     drupal_set_message(t('No test results to display.'), 'error');
-//    drupal_goto('admin/config/development/testing');
     drupal_goto('admin/build/testing');
     return $form;
   }
@@ -242,13 +233,9 @@ function simpletest_result_form(&$form_s
   );
 
   // Cycle through each test group.
-//  $header = array(t('Message'), t('Group'), t('Filename'), t('Line'), t('Function'), array('colspan' => 2, 'data' => t('Status')));
   $header = array(t('Message'), t('Group'), t('Filename'), t('Line'), t('Function'), t('Status'));
   $form['result']['results'] = array();
-  $classes = simpletest_test_get_all_classes(); // Drupal 6.
   foreach ($results as $group => $assertions) {
-    require_once $classes[$group]['file']; // Drupal 6.
-
     // Create group fieldset with summary information.
     $info = call_user_func(array($group, 'getInfo'));
     $form['result']['results'][$group] = array(
@@ -275,17 +262,11 @@ function simpletest_result_form(&$form_s
       if ($assertion->message_group == 'Debug') {
         $class = 'simpletest-debug';
       }
-//      $rows[] = array('data' => $row, 'class' => array($class));
       $rows[] = array('data' => $row, 'class' => $class);
 
       $group_summary['#' . $assertion->status]++;
       $form['result']['summary']['#' . $assertion->status]++;
     }
-//    $form['result']['results'][$group]['table'] = array(
-//      '#theme' => 'table',
-//      '#header' => $header,
-//      '#rows' => $rows,
-//    );
     $form['result']['results'][$group]['table'] = array(
       '#type' => 'markup',
       '#value' => theme('table', $header, $rows),
@@ -303,12 +284,10 @@ function simpletest_result_form(&$form_s
   $form['result']['summary']['#ok'] = $form['result']['summary']['#fail'] + $form['result']['summary']['#exception'] == 0;
 
   // Actions.
-//  $form['#action'] = url('admin/config/development/testing/results/re-run');
   $form['#action'] = url('admin/build/testing/results/re-run');
   $form['action'] = array(
     '#type' => 'fieldset',
     '#title' => t('Actions'),
-//    '#attributes' => array('class' => array('container-inline')),
     '#attributes' => array('class' => 'container-inline'),
     '#weight' => -11,
   );
@@ -340,7 +319,6 @@ function simpletest_result_form(&$form_s
   );
 
   $form['action']['return'] = array(
-//    '#markup' => l(t('Return to list'), 'admin/config/development/testing'),
     '#type' => 'markup',
     '#value' => l(t('Return to list'), 'admin/build/testing'),
   );
@@ -362,7 +340,7 @@ function simpletest_result_form_submit($
   if ($form_state['values']['filter'] == 'all') {
     $classes = array_merge($pass, $fail);
   }
-  else if ($form_state['values']['filter'] == 'pass') {
+  elseif ($form_state['values']['filter'] == 'pass') {
     $classes = $pass;
   }
   else {
@@ -380,12 +358,13 @@ function simpletest_result_form_submit($
   }
 
   simpletest_test_form_submit(array(), $form_state_execute);
+  $form_state['redirect'] = $form_state_execute['redirect'];
 }
 
 /**
- * Add wrapper div with class based on summary status.
+ * Returns HTML for the summary status of a simpletest result.
  *
- * @return HTML output.
+ * @ingroup themeable
  */
 function theme_simpletest_result_summary($form) {
   return '<div class="simpletest-' . ($form['#ok'] ? 'pass' : 'fail') . '">' . _simpletest_format_summary_line($form) . '</div>';
@@ -398,19 +377,9 @@ function theme_simpletest_result_summary
  * @return Array of results grouped by test_class.
  */
 function simpletest_result_get($test_id) {
-//  $results = db_select('simpletest')
-//    ->fields('simpletest')
-//    ->condition('test_id', $test_id)
-//    ->orderBy('test_class')
-//    ->orderBy('message_id')
-//    ->execute();
-  $results = db_query('SELECT *
-                       FROM {simpletest}
-                       WHERE test_id = %d
-                       ORDER BY test_class, message_id', $test_id);
+  $results = db_query("SELECT * FROM {simpletest} WHERE test_id = %d ORDER BY test_class, message_id", $test_id);
 
   $test_results = array();
-//  foreach ($results as $result) {
   while ($result = db_fetch_object($results)) {
     if (!isset($test_results[$result->test_class])) {
       $test_results[$result->test_class] = array();
@@ -432,10 +401,10 @@ function simpletest_result_status_image(
 
   if (!isset($map)) {
     $map = array(
-      'pass' => theme('image', 'misc/watchdog-ok.png'),
-      'fail' => theme('image', 'misc/watchdog-error.png'),
-      'exception' => theme('image', 'misc/watchdog-warning.png'),
-      'debug' => theme('image', 'misc/watchdog-warning.png'),
+      'pass' => theme('image', 'misc/watchdog-ok.png', t('Pass')),
+      'fail' => theme('image', 'misc/watchdog-error.png', t('Fail')),
+      'exception' => theme('image', 'misc/watchdog-warning.png', t('Exception')),
+      'debug' => theme('image', 'misc/watchdog-warning.png', t('Debug')),
     );
   }
   if (isset($map[$status])) {
@@ -448,8 +417,6 @@ function simpletest_result_status_image(
  * Provides settings form for SimpleTest variables.
  */
 function simpletest_settings_form(&$form_state) {
-  $form = array();
-
   $form['general'] = array(
     '#type' => 'fieldset',
     '#title' => t('General'),
@@ -457,30 +424,45 @@ function simpletest_settings_form(&$form
   $form['general']['simpletest_clear_results'] = array(
     '#type' => 'checkbox',
     '#title' => t('Clear results after each complete test suite run'),
-    '#description' => t('By default SimpleTest will clear the results after they have been viewed on the results page, but in some cases it may be useful to leave the results in the database. The results can then be viewed at <em>admin/config/development/testing/[test_id]</em>. The test ID can be found in the database, simpletest table, or kept track of when viewing the results the first time. Additionally, some modules may provide more analaysis or features that require this setting to be disabled.'),
+    '#description' => t('By default SimpleTest will clear the results after they have been viewed on the results page, but in some cases it may be useful to leave the results in the database. The results can then be viewed at <em>admin/build/testing/[test_id]</em>. The test ID can be found in the database, simpletest table, or kept track of when viewing the results the first time. Additionally, some modules may provide more analysis or features that require this setting to be disabled.'),
     '#default_value' => variable_get('simpletest_clear_results', TRUE),
   );
   $form['general']['simpletest_verbose'] = array(
     '#type' => 'checkbox',
     '#title' => t('Provide verbose information when running tests'),
     '#description' => t('The verbose data will be printed along with the standard assertions and is useful for debugging. The verbose data will be erased between each test suite run. The verbose data output is very detailed and should only be used when debugging.'),
-    '#default_value' => variable_get('simpletest_verbose', FALSE),
+    '#default_value' => variable_get('simpletest_verbose', TRUE),
   );
 
   $form['httpauth'] = array(
     '#type' => 'fieldset',
-    '#title' => t('HTTP authentication credentials'),
+    '#title' => t('HTTP authentication'),
     '#description' => t('HTTP auth settings to be used by the SimpleTest browser during testing. Useful when the site requires basic HTTP authentication.'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+  $form['httpauth']['simpletest_httpauth_method'] = array(
+    '#type' => 'select',
+    '#title' => t('Method'),
+    '#options' => array(
+      CURLAUTH_BASIC => t('Basic'),
+      CURLAUTH_DIGEST => t('Digest'),
+      CURLAUTH_GSSNEGOTIATE => t('GSS negotiate'),
+      CURLAUTH_NTLM => t('NTLM'),
+      CURLAUTH_ANY => t('Any'),
+      CURLAUTH_ANYSAFE => t('Any safe'),
+    ),
+    '#default_value' => variable_get('simpletest_httpauth_method', CURLAUTH_BASIC),
   );
-  $form['httpauth']['simpletest_username'] = array(
+  $form['httpauth']['simpletest_httpauth_username'] = array(
     '#type' => 'textfield',
     '#title' => t('Username'),
-    '#default_value' => variable_get('simpletest_username', ''),
+    '#default_value' => variable_get('simpletest_httpauth_username', ''),
   );
-  $form['httpauth']['simpletest_password'] = array(
+  $form['httpauth']['simpletest_httpauth_password'] = array(
     '#type' => 'textfield',
     '#title' => t('Password'),
-    '#default_value' => variable_get('simpletest_password', ''),
+    '#default_value' => variable_get('simpletest_httpauth_password', ''),
   );
 
   return system_settings_form($form);
Index: simpletest.test
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/simpletest/Attic/simpletest.test,v
retrieving revision 1.1.2.17
diff -u -p -r1.1.2.17 simpletest.test
--- simpletest.test	6 Nov 2009 21:23:32 -0000	1.1.2.17
+++ simpletest.test	22 Aug 2010 23:46:59 -0000
@@ -1,13 +1,5 @@
 <?php
 // $Id: simpletest.test,v 1.1.2.17 2009/11/06 21:23:32 boombatower Exp $
-// Core: Id: simpletest.test,v 1.31 2009/09/01 17:40:27 webchick Exp
-
-/**
- * @file
- * Backport of Drupal 7 simpletest.test with modifications, see BACKPORT.txt.
- *
- * Copyright 2008-2009 by Jimmy Berry ("boombatower", http://drupal.org/user/214218)
- */
 
 class SimpleTestFunctionalTest extends DrupalWebTestCase {
   /**
@@ -53,7 +45,6 @@ class SimpleTestFunctionalTest extends D
     if (!$this->inCURL()) {
       $this->drupalGet('node');
       $this->assertTrue($this->drupalGetHeader('Date'), t('An HTTP header was received.'));
-//      $this->assertTitle(t('Welcome to @site-name | @site-name', array('@site-name' => variable_get('site_name', 'Drupal'))), t('Site title matches.'));
       $this->assertTitle(variable_get('site_name', 'Drupal'), t('Site title matches.'));
       $this->assertNoTitle('Foo', t('Site title does not match.'));
       // Make sure that we are locked out of the installer when prefixing
@@ -63,7 +54,8 @@ class SimpleTestFunctionalTest extends D
       $this->drupalGet($base_url . '/install.php', array('external' => TRUE));
       $this->assertResponse(403, 'Cannot access install.php with a "simpletest" user-agent header.');
 
-      $this->drupalLogin($this->drupalCreateUser());
+      $user = $this->drupalCreateUser();
+      $this->drupalLogin($user);
       $headers = $this->drupalGetHeaders(TRUE);
       $this->assertEqual(count($headers), 2, t('There was one intermediate request.'));
       $this->assertTrue(strpos($headers[0][':status'], '302') !== FALSE, t('Intermediate response code was 302.'));
@@ -71,6 +63,17 @@ class SimpleTestFunctionalTest extends D
       $this->assertEqual($this->getUrl(), $headers[0]['location'], t('HTTP redirect was followed'));
       $this->assertFalse($this->drupalGetHeader('Location'), t('Headers from intermediate request were reset.'));
       $this->assertResponse(200, t('Response code from intermediate request was reset.'));
+
+      // Test the maximum redirection option.
+      $this->drupalLogout();
+      $edit = array(
+        'name' => $user->name,
+        'pass' => $user->pass_raw
+      );
+      variable_set('simpletest_maximum_redirects', 1);
+      $this->drupalPost('user', $edit, t('Log in'), array('query' => array('destination' => 'logout')));
+      $headers = $this->drupalGetHeaders(TRUE);
+      $this->assertEqual(count($headers), 2, t('Simpletest stopped following redirects after the first one.'));
     }
   }
 
@@ -93,7 +96,6 @@ class SimpleTestFunctionalTest extends D
       // Run twice so test_ids can be accumulated.
       for ($i = 0; $i < 2; $i++) {
         // Run this test from web interface.
-//        $this->drupalGet('admin/config/development/testing');
         $this->drupalGet('admin/build/testing');
 
         $edit = array();
@@ -152,7 +154,7 @@ class SimpleTestFunctionalTest extends D
     $this->assertAssertion(t('Created permissions: @perms', array('@perms' => $this->valid_permission)), 'Role', 'Pass', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()');
     $this->assertAssertion(t('Invalid permission %permission.', array('%permission' => $this->invalid_permission)), 'Role', 'Fail', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()');
 
-    // Check that a warning is catched by simpletest.
+    // Check that a warning is caught by simpletest.
     $this->assertAssertion('Division by zero', 'Warning', 'Fail', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()');
 
     // Check that the backtracing code works for specific assert function.
@@ -165,8 +167,7 @@ class SimpleTestFunctionalTest extends D
 
     $this->assertAssertion("Debug: 'Foo'", 'Debug', 'Fail', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()');
 
-//    $this->assertEqual('6 passes, 2 fails, 2 exceptions, and 1 debug message', $this->childTestResults['summary'], 'Stub test summary is correct');
-    $this->assertEqual('8 passes, 2 fails, 2 exceptions, and 1 debug message', $this->childTestResults['summary'], 'Stub test summary is correct');
+    $this->assertEqual('6 passes, 2 fails, 2 exceptions, and 1 debug message', $this->childTestResults['summary'], 'Stub test summary is correct');
 
     $this->test_ids[] = $test_id = $this->getTestIdFromResults();
     $this->assertTrue($test_id, t('Found test ID in results.'));
@@ -231,8 +232,7 @@ class SimpleTestFunctionalTest extends D
           $assertion['file'] = $this->asText($row->td[2]);
           $assertion['line'] = $this->asText($row->td[3]);
           $assertion['function'] = $this->asText($row->td[4]);
-//          $ok_url = file_create_url('misc/watchdog-ok.png');
-          $ok_url = '/misc/watchdog-ok.png';
+          $ok_url = base_path() . 'misc/watchdog-ok.png';
           $assertion['status'] = ($row->td[5]->img['src'] == $ok_url) ? 'Pass' : 'Fail';
           $results['assertions'][] = $assertion;
         }
@@ -243,8 +243,6 @@ class SimpleTestFunctionalTest extends D
 
   /**
    * Get the fieldset containing the results for group this test is in.
-   *
-   * @return fieldset containing the results for group this test is in.
    */
   function getResultFieldSet() {
     $fieldsets = $this->xpath('//fieldset');
@@ -274,10 +272,11 @@ class SimpleTestFunctionalTest extends D
 
   /**
    * Check if the test is being run from inside a CURL request.
-   *
-   * @return The test is being run from inside a CURL request.
    */
   function inCURL() {
+    // We cannot rely on drupal_static('drupal_test_info') here, because
+    // 'in_child_site' would be FALSE for the parent site when we are
+    // executing the tests. Default to direct detection of the HTTP headers.
     return isset($_SERVER['HTTP_USER_AGENT']) && preg_match("/^simpletest\d+/", $_SERVER['HTTP_USER_AGENT']);
   }
 }
@@ -361,3 +360,45 @@ class SimpleTestMailCaptureTestCase exte
   }
 }
 
+/**
+ * Test Folder creation
+ */
+class SimpleTestFolderTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Testing SimpleTest setUp',
+      'description' => "This test will check SimpleTest's treatment of hook_install during setUp.",
+      'group' => 'SimpleTest',
+    );
+  }
+
+  function setUp() {
+    parent::setUp(array('simpletest'));
+  }
+
+  function testFolderSetup() {
+    $path = file_directory_path() . '/simpletest';
+    $this->assertTrue(file_check_directory($path, FALSE), "Directory $path created.");
+  }
+}
+
+/**
+ * Test required modules for tests.
+ */
+class SimpleTestMissingDependentModuleUnitTest extends DrupalUnitTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Testing dependent module test',
+      'description' => 'This test should not load since it requires a module that is not found.',
+      'group' => 'SimpleTest',
+      'dependencies' => array('simpletest_missing_module'),
+    );
+  }
+
+  /**
+   * Ensure that this test will not be loaded despite its dependency.
+   */
+  function testFail() {
+    $this->fail(t('Running test with missing required module.'));
+  }
+}
Index: files/README.txt
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/simpletest/files/Attic/README.txt,v
retrieving revision 1.1.2.3
diff -u -p -r1.1.2.3 README.txt
--- files/README.txt	14 Sep 2009 23:05:19 -0000	1.1.2.3
+++ files/README.txt	22 Aug 2010 23:46:59 -0000
@@ -1,6 +1,5 @@
 $Id: README.txt,v 1.1.2.3 2009/09/14 23:05:19 boombatower Exp $
-Core: Id: README.txt,v 1.1 2008/04/20 18:23:30 dries Exp
 
 These files are use in some tests that upload files or other operations were
 a file is useful. These files are copied to the files directory as specified
-in the site settings. Other tests files are generated in order to save space.
\ No newline at end of file
+in the site settings. Other tests files are generated in order to save space.
Index: files/image-1.png
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/simpletest/files/Attic/image-1.png,v
retrieving revision 1.1.2.2
diff -u -p -r1.1.2.2 image-1.png
Binary files /tmp/cvsKrCP3w and image-1.png differ
Index: files/image-2.jpg
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/simpletest/files/Attic/image-2.jpg,v
retrieving revision 1.1.2.2
diff -u -p -r1.1.2.2 image-2.jpg
Binary files /tmp/cvsqQg0Fd and image-2.jpg differ
Index: files/image-test.jpg
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/simpletest/files/Attic/image-test.jpg,v
retrieving revision 1.1.2.1
diff -u -p -r1.1.2.1 image-test.jpg
Binary files /tmp/cvs3yZriU and image-test.jpg differ
Index: files/image-test.png
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/simpletest/files/Attic/image-test.png,v
retrieving revision 1.1.2.1
diff -u -p -r1.1.2.1 image-test.png
Binary files /tmp/cvs3CQgVA and image-test.png differ
Index: files/javascript-1.txt
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/simpletest/files/Attic/javascript-1.txt,v
retrieving revision 1.1.2.2
diff -u -p -r1.1.2.2 javascript-1.txt
--- files/javascript-1.txt	28 Mar 2009 03:27:26 -0000	1.1.2.2
+++ files/javascript-1.txt	22 Aug 2010 23:47:00 -0000
@@ -1,3 +1,3 @@
 <script>
 alert('SimpleTest PHP was executed!');
-</script>
\ No newline at end of file
+</script>
Index: files/php-1.txt
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/simpletest/files/Attic/php-1.txt,v
retrieving revision 1.1.2.2
diff -u -p -r1.1.2.2 php-1.txt
--- files/php-1.txt	28 Mar 2009 03:27:26 -0000	1.1.2.2
+++ files/php-1.txt	22 Aug 2010 23:47:00 -0000
@@ -1,3 +1,3 @@
 <?php
 print 'SimpleTest PHP was executed!';
-?>
\ No newline at end of file
+?>
