Index: modules/simpletest/drupal_web_test_case.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/drupal_web_test_case.php,v
retrieving revision 1.207
diff -u -r1.207 drupal_web_test_case.php
--- modules/simpletest/drupal_web_test_case.php	31 Mar 2010 20:05:06 -0000	1.207
+++ modules/simpletest/drupal_web_test_case.php	31 Mar 2010 22:57:48 -0000
@@ -2857,6 +2857,150 @@
 }
 
 /**
+ * Base class used for writing atomic staging tests.
+ *
+ * The test cases are written in such a way that they can be run against a
+ * staging or live environment and will clean up after themselves by reverting
+ * all peformed actions. The tests should be performed through a set of actions
+ * defined in the test method. For example:
+ * <code>
+ * protected function testFoo() {
+ *   $this->actions[] = 'login';
+ * }
+ *
+ * protected function performLogin();
+ * protected function revertLogin();
+ * </code>
+ *
+ * When the variable 'simpletest_staging_url' is set the setUp() and tearDown()
+ * methods will not create an environment, but instead direct the internal
+ * browser to the staging URL. The tests will then against the specified URL
+ * instead of the local machine.
+ *
+ * The test can also be run against a standard development environment just as
+ * and other test using the DrupalCloneTestCase. If the staging ULR is not
+ * specified the test will attempt to clone the local development database and
+ * perform the actions against it.
+ */
+class DrupalStageTestCase extends DrupalWebTestCase {
+
+  /**
+   * URL variables that need to be changed when running against staging.
+   *
+   * @var array
+   */
+  protected static $URL_VARIABLES = array('base_url', 'base_secure_url', 'base_insecure_url');
+
+  /**
+   * URL of the staging server if specified, otherwise FALSE.
+   *
+   * @var string
+   */
+  protected $stagingUrl;
+
+  /**
+   * Associative array of original values for the URL variables.
+   *
+   * @var array
+   */
+  protected $originalUrls = array();
+
+  /**
+   * List of actions to perform.
+   *
+   * @var array
+   */
+  protected $actions = array();
+
+  /**
+   * Determine when to run against staging or clone environment.
+   */
+  protected function setUp() {
+    if ($this->stagingUrl || ($this->stagingUrl = variable_get('simpletest_staging_url', FALSE))) {
+      // Point the internal browser to the staging site.
+      foreach (self::$URL_VARIABLES as $variable) {
+        $this->originalUrls[$variable] = $GLOBALS[$variable];
+        $GLOBALS[$variable] = $this->stagingUrl;
+      }
+      $GLOBALS['base_secure_url'] = str_replace('http://', 'https://', $GLOBALS['base_secure_url']);
+
+      // Set to that verbose mode works properly.
+      $this->originalFileDirectory = file_directory_path();
+    }
+    else {
+      parent::setUp();
+    }
+  }
+
+  /**
+   * Perform and revert actions, then tear down based on what setUp() did.
+   */
+  protected function tearDown() {
+    // Perform all actions as part of the test and revert them.
+    $this->performActions();
+    $this->revertActions();
+
+    if ($this->stagingUrl) {
+      // Revert all URL variables to their original values.
+      foreach (self::$URL_VARIABLES as $variable) {
+        $GLOBALS[$variable] = $this->originalUrls[$variable];
+      }
+    }
+    else {
+      parent::tearDown();
+    }
+  }
+
+  /**
+   * Perform all actions listed in the $actions array.
+   */
+  protected function performActions() {
+    foreach ($this->actions as $action) {
+      call_user_func(array($this, 'perform' . ucfirst($action)));
+    }
+  }
+
+  /**
+   * Revert all actions listed in the $actions array.
+   */
+  protected function revertActions() {
+    foreach ($this->actions as $action) {
+      if (method_exists($this, 'revert' . ucfirst($action))) {
+        call_user_func(array($this, 'revert' . ucfirst($action)));
+      }
+    }
+  }
+
+  /**
+   * Set the user agent to Drupal.
+   */
+  protected function curlInitialize() {
+    parent::curlInitialize();
+    curl_setopt($this->curlHandle, CURLOPT_USERAGENT, 'Drupal (+http://drupal.org/)');
+  }
+
+  /**
+   * Temporarily revert the global URL variables so verbose links will print.
+   */
+  protected function verbose($message) {
+    if ($this->stagingUrl) {
+      foreach (self::$URL_VARIABLES as $variable) {
+        $GLOBALS[$variable] = $this->originalUrls[$variable];
+      }
+    }
+
+    parent::verbose($message);
+
+    if ($this->stagingUrl) {
+      foreach (self::$URL_VARIABLES as $variable) {
+        $GLOBALS[$variable] = $this->stagingUrl;
+      }
+      $GLOBALS['base_secure_url'] = str_replace('http://', 'https://', $GLOBALS['base_secure_url']);
+    }
+  }
+}
+
+/**
  * Log verbose message in a text file.
  *
  * If verbose mode is enabled then page requests will be dumped to a file and
