From e2184e25ac140d3b44709b7b070c1c4889122d45 Mon Sep 17 00:00:00 2001
From: Dries Buytaert <dries@buytaert.net>
Date: Mon, 25 May 2009 05:20:16 +0000
Subject: [PATCH] - Patch #464714 by chx, DamZ: speed up the tests by bringing
 unit tests backt to live.  Unit tests bootstrap faster and have the potential
 to speed up testing. We'll need help converting tests where possible.

---
 modules/simpletest/drupal_web_test_case.php | 340 ++++++++++++--------
 modules/simpletest/simpletest.module        |   2 +-
 modules/simpletest/simpletest.test          |   1 -
 modules/simpletest/tests/common.test        |   6 +-
 modules/simpletest/tests/form.test          |   2 +-
 modules/simpletest/tests/graph.test         |   2 +-
 modules/simpletest/tests/registry.test      |   3 +-
 7 files changed, 217 insertions(+), 139 deletions(-)

diff --git a/modules/simpletest/drupal_web_test_case.php b/modules/simpletest/drupal_web_test_case.php
index c632fa9311a0..496fb2476512 100644
--- a/modules/simpletest/drupal_web_test_case.php
+++ b/modules/simpletest/drupal_web_test_case.php
@@ -2,10 +2,11 @@
 // $Id$
 
 /**
- * Test case for typical Drupal tests.
+ * Base class for Drupal tests.
+ *
+ * Do not extend this class, use one of the subclasses in this file.
  */
-class DrupalWebTestCase {
-
+abstract class DrupalTestCase {
   /**
    * The test run ID.
    *
@@ -13,70 +14,6 @@ class DrupalWebTestCase {
    */
   protected $testId;
 
-  /**
-   * The URL currently loaded in the internal browser.
-   *
-   * @var string
-   */
-  protected $url;
-
-  /**
-   * The handle of the current cURL connection.
-   *
-   * @var resource
-   */
-  protected $curlHandle;
-
-  /**
-   * The headers of the page currently loaded in the internal browser.
-   *
-   * @var Array
-   */
-  protected $headers;
-
-  /**
-   * The content of the page currently loaded in the internal browser.
-   *
-   * @var string
-   */
-  protected $content;
-
-  /**
-   * The content of the page currently loaded in the internal browser (plain text version).
-   *
-   * @var string
-   */
-  protected $plainTextContent;
-
-  /**
-   * The parsed version of the page.
-   *
-   * @var SimpleXMLElement
-   */
-  protected $elements = NULL;
-
-  /**
-   * The current user logged in using the internal browser.
-   *
-   * @var bool
-   */
-  protected $loggedInUser = FALSE;
-
-  /**
-   * The current cookie file used by cURL.
-   *
-   * We do not reuse the cookies in further runs, so we do not need a file
-   * but we still need cookie handling, so we set the jar to NULL.
-   */
-  protected $cookieFile = NULL;
-
-  /**
-   * Additional cURL options.
-   *
-   * DrupalWebTestCase itself never sets this but always obeys what is set.
-   */
-  protected $additionalCurlOptions = array();
-
   /**
    * The original database prefix, before it was changed for testing purposes.
    *
@@ -92,11 +29,9 @@ class DrupalWebTestCase {
   protected $originalFileDirectory = NULL;
 
   /**
-   * The original user, before it was changed to a clean uid = 1 for testing purposes.
-   *
-   * @var object
+   * Time limit for the test.
    */
-  protected $originalUser = NULL;
+  protected $timeLimit = 180;
 
   /**
    * Current results of this test case.
@@ -117,15 +52,14 @@ class DrupalWebTestCase {
   protected $assertions = array();
 
   /**
-   * Time limit for the test.
-   */
-  protected $timeLimit = 180;
-
-  /**
-   * HTTP authentication credentials (<username>:<password>).
+   * This class is skipped when looking for the source of an assertion.
+   *
+   * When displaying which function an assert comes from, it's not too useful
+   * to see "drupalWebTestCase->drupalLogin()', we would like to see the test
+   * that called it. So we need to skip the classes defining these helper
+   * methods.
    */
-  protected $httpauth_credentials = NULL;
-
+  protected $skipClasses = array(__CLASS__ => TRUE);
 
   /**
    * Constructor for DrupalWebTestCase.
@@ -154,7 +88,7 @@ public function __construct($test_id = NULL) {
    *   the name of the source file, 'line' is the line number and 'function'
    *   is the caller function itself.
    */
-  private function assert($status, $message = '', $group = 'Other', array $caller = NULL) {
+  protected function assert($status, $message = '', $group = 'Other', array $caller = NULL) {
     global $db_prefix;
 
     // Convert boolean status to string status.
@@ -211,11 +145,11 @@ protected function getAssertionCall() {
     $backtrace = debug_backtrace();
 
     // The first element is the call. The second element is the caller.
-    // We skip calls that occurred in one of the methods of DrupalWebTestCase
+    // We skip calls that occurred in one of the methods of our base classes
     // or in an assertion function.
-    while (($caller = $backtrace[1]) &&
-          ((isset($caller['class']) && $caller['class'] == 'DrupalWebTestCase') ||
-            substr($caller['function'], 0, 6) == 'assert')) {
+   while (($caller = $backtrace[1]) &&
+         ((isset($caller['class']) && isset($this->skipClasses[$caller['class']])) ||
+           substr($caller['function'], 0, 6) == 'assert')) {
       // We remove that call.
       array_shift($backtrace);
     }
@@ -475,6 +409,197 @@ protected function exceptionHandler($exception) {
     $this->error($exception->getMessage(), 'Uncaught exception', _drupal_get_last_caller($backtrace));
   }
 
+
+  /**
+   * Generates a random string of ASCII characters of codes 32 to 126.
+   *
+   * The generated string includes alpha-numeric characters and common misc
+   * characters. Use this method when testing general input where the content
+   * is not restricted.
+   *
+   * @param $length
+   *   Length of random string to generate which will be appended to $db_prefix.
+   * @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;
+  }
+
+  /**
+   * 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.
+   *
+   * @param $length
+   *   Length of random string to generate which will be appended to $db_prefix.
+   * @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($values[mt_rand(0, $max)]);
+    }
+    return str_replace('simpletest', 's', $db_prefix) . $str;
+  }
+
+}
+
+/**
+ * Test case for Drupal unit tests.
+ *
+ * These tests can not access the database nor files. Calling any Drupal
+ * function that needs the database will throw exceptions. These include
+ * watchdog(), drupal_function_exists(), module_implements(),
+ * module_invoke_all() etc.
+ */
+class DrupalUnitTestCase extends DrupalTestCase {
+
+  /**
+   * Constructor for DrupalUnitTestCase.
+   */
+  function __construct($test_id = NULL) {
+    parent::__construct($test_id);
+    $this->skipClasses[__CLASS__] = TRUE;
+  }
+  
+  function setUp() {
+    global $db_prefix, $conf;
+
+    // Store necessary current values before switching to prefixed database.
+    $this->originalPrefix = $db_prefix;
+    $this->originalFileDirectory = file_directory_path();
+
+    // Generate temporary prefixed database to ensure that tests have a clean starting point.
+    $db_prefix = Database::getConnection()->prefixTables('{simpletest' . mt_rand(1000, 1000000) . '}');
+    $conf['file_directory_path'] = $this->originalFileDirectory . '/' . $db_prefix;
+
+    // If locale is enabled then t() will try to access the database and
+    // subsequently will fail as the database is not accessible.
+    $module_list = module_list();
+    if (isset($module_list['locale'])) {
+      $this->originalModuleList = $module_list;
+      unset($module_list['locale']);
+      module_list(TRUE, FALSE, $module_list);
+    }
+  }
+
+  function tearDown() {
+    global $db_prefix, $conf;
+    if (preg_match('/simpletest\d+/', $db_prefix)) {
+      $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, $this->originalModuleList);
+      }
+    }
+  }
+}
+
+/**
+ * Test case for typical Drupal tests.
+ */
+class DrupalWebTestCase extends DrupalTestCase {
+  /**
+   * The URL currently loaded in the internal browser.
+   *
+   * @var string
+   */
+  protected $url;
+
+  /**
+   * The handle of the current cURL connection.
+   *
+   * @var resource
+   */
+  protected $curlHandle;
+
+  /**
+   * The headers of the page currently loaded in the internal browser.
+   *
+   * @var Array
+   */
+  protected $headers;
+
+  /**
+   * The content of the page currently loaded in the internal browser.
+   *
+   * @var string
+   */
+  protected $content;
+
+  /**
+   * The content of the page currently loaded in the internal browser (plain text version).
+   *
+   * @var string
+   */
+  protected $plainTextContent;
+
+  /**
+   * The parsed version of the page.
+   *
+   * @var SimpleXMLElement
+   */
+  protected $elements = NULL;
+
+  /**
+   * The current user logged in using the internal browser.
+   *
+   * @var bool
+   */
+  protected $loggedInUser = FALSE;
+
+  /**
+   * The current cookie file used by cURL.
+   *
+   * We do not reuse the cookies in further runs, so we do not need a file
+   * but we still need cookie handling, so we set the jar to NULL.
+   */
+  protected $cookieFile = NULL;
+
+  /**
+   * Additional cURL options.
+   *
+   * DrupalWebTestCase itself never sets this but always obeys what is set.
+   */
+  protected $additionalCurlOptions = array();
+
+  /**
+   * The original user, before it was changed to a clean uid = 1 for testing purposes.
+   *
+   * @var object
+   */
+  protected $originalUser = NULL;
+
+  /**
+   * HTTP authentication credentials (<username>:<password>).
+   */
+  protected $httpauth_credentials = NULL;
+
+  /**
+   * Constructor for DrupalWebTestCase.
+   */
+  function __construct($test_id = NULL) {
+    parent::__construct($test_id);
+    $this->skipClasses[__CLASS__] = TRUE;
+  }
+
   /**
    * Get a node from the database based on its title.
    *
@@ -647,53 +772,6 @@ protected function drupalCompareFiles($file1, $file2) {
     }
   }
 
-  /**
-   * Generates a random string of ASCII characters of codes 32 to 126.
-   *
-   * The generated string includes alpha-numeric characters and common misc
-   * characters. Use this method when testing general input where the content
-   * is not restricted.
-   *
-   * @param $length
-   *   Length of random string to generate which will be appended to $db_prefix.
-   * @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;
-  }
-
-  /**
-   * 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.
-   *
-   * @param $length
-   *   Length of random string to generate which will be appended to $db_prefix.
-   * @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($values[mt_rand(0, $max)]);
-    }
-    return str_replace('simpletest', 's', $db_prefix) . $str;
-  }
-
   /**
    * Create a user with a given set of permissions. The permissions correspond to the
    * names given on the privileges page.
diff --git a/modules/simpletest/simpletest.module b/modules/simpletest/simpletest.module
index df0725f76533..58c01b6f2d1d 100644
--- a/modules/simpletest/simpletest.module
+++ b/modules/simpletest/simpletest.module
@@ -238,7 +238,7 @@ function simpletest_get_all_tests() {
     }
     $classes = array_values(array_diff(get_declared_classes(), $existing_classes));
     foreach ($classes as $key => $class) {
-      if (!method_exists($class, 'getInfo')) {
+      if (!is_subclass_of($class, 'DrupalTestCase') || !method_exists($class, 'getInfo')) {
         unset($classes[$key]);
       }
     }
diff --git a/modules/simpletest/simpletest.test b/modules/simpletest/simpletest.test
index ed1c5b92b180..7a0d698f6f7e 100644
--- a/modules/simpletest/simpletest.test
+++ b/modules/simpletest/simpletest.test
@@ -199,7 +199,6 @@ class SimpleTestFunctionalTest extends DrupalWebTestCase {
    */
   function getTestResults() {
     $results = array();
-
     if ($this->parse()) {
       if ($fieldset = $this->getResultFieldSet()) {
         // Code assumes this is the only test in group.
diff --git a/modules/simpletest/tests/common.test b/modules/simpletest/tests/common.test
index 3af7786e2995..284313725241 100644
--- a/modules/simpletest/tests/common.test
+++ b/modules/simpletest/tests/common.test
@@ -4,7 +4,7 @@
 /**
  * Tests for the l() function.
  */
-class CommonLUnitTest extends DrupalWebTestCase {
+class CommonLUnitTest extends DrupalUnitTestCase {
 
   public static function getInfo() {
     return array(
@@ -26,7 +26,7 @@ class CommonLUnitTest extends DrupalWebTestCase {
   }
 }
 
-class CommonSizeTestCase extends DrupalWebTestCase {
+class CommonSizeTestCase extends DrupalUnitTestCase {
   protected $exact_test_cases;
   protected $rounded_test_cases;
 
@@ -631,7 +631,7 @@ class DrupalRenderUnitTestCase extends DrupalWebTestCase {
 /**
  * Test for valid_url().
  */
-class ValidUrlTestCase extends DrupalWebTestCase {
+class ValidUrlTestCase extends DrupalUnitTestCase {
   public static function getInfo() {
     return array(
       'name' => t('Valid Url'),
diff --git a/modules/simpletest/tests/form.test b/modules/simpletest/tests/form.test
index c06a452d9e31..991186ba3003 100644
--- a/modules/simpletest/tests/form.test
+++ b/modules/simpletest/tests/form.test
@@ -77,7 +77,7 @@ class FormsTestCase extends DrupalWebTestCase {
 /**
  * Test form type functions for expected behavior.
  */
-class FormsTestTypeCase extends DrupalWebTestCase {
+class FormsTestTypeCase extends DrupalUnitTestCase {
   public static function getInfo() {
     return array(
       'name' => t('Form type-specific tests'),
diff --git a/modules/simpletest/tests/graph.test b/modules/simpletest/tests/graph.test
index a6dac87ef956..a5ed9314b7d9 100644
--- a/modules/simpletest/tests/graph.test
+++ b/modules/simpletest/tests/graph.test
@@ -9,7 +9,7 @@
 /**
  * Unit tests for the graph handling features.
  */
-class GraphUnitTest extends DrupalWebTestCase {
+class GraphUnitTest extends DrupalUnitTestCase {
   public static function getInfo() {
     return array(
       'name' => t('Graph'),
diff --git a/modules/simpletest/tests/registry.test b/modules/simpletest/tests/registry.test
index f8bebf905125..0ef763920aa6 100644
--- a/modules/simpletest/tests/registry.test
+++ b/modules/simpletest/tests/registry.test
@@ -135,7 +135,7 @@ CONTENTS;
 
 }
 
-class RegistrySkipBodyTestCase extends DrupalWebTestCase {
+class RegistrySkipBodyTestCase extends DrupalUnitTestCase {
   public static function getInfo() {
     return array(
       'name' => t('Skip function body test'),
@@ -148,6 +148,7 @@ class RegistrySkipBodyTestCase extends DrupalWebTestCase {
     // This string contains all three kinds of opening braces.
     $function = '<?php function foo () { $x = "{$y}"; $x = "${y}"; }';
     $tokens = token_get_all($function);
+    require_once DRUPAL_ROOT . '/includes/registry.inc';
     _registry_skip_body($tokens);
     // Consume the last }
     each($tokens);
-- 
GitLab