From 9b82787b223185ca835aba9f34f300837fcebb85 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Tue, 24 Jun 2008 21:51:03 +0000 Subject: - Patch #243773 by chx, catch, boombatower, yched, dmitrig01, et al: use the batch API for running the tests instead of an all-in-one approach. Great work. --- modules/simpletest/drupal_web_test_case.php | 667 +++++++++++++++++++++------- modules/simpletest/simpletest.css | 4 +- modules/simpletest/simpletest.install | 124 ++++++ modules/simpletest/simpletest.module | 525 +++++++++++++--------- modules/simpletest/simpletest.test | 25 +- 5 files changed, 982 insertions(+), 363 deletions(-) (limited to 'modules/simpletest') diff --git a/modules/simpletest/drupal_web_test_case.php b/modules/simpletest/drupal_web_test_case.php index 0d0147302..ef3712349 100644 --- a/modules/simpletest/drupal_web_test_case.php +++ b/modules/simpletest/drupal_web_test_case.php @@ -4,7 +4,7 @@ /** * Test case for typical Drupal tests. */ -class DrupalWebTestCase extends UnitTestCase { +class DrupalWebTestCase { protected $_logged_in = FALSE; protected $_content; protected $plain_text; @@ -19,26 +19,290 @@ class DrupalWebTestCase extends UnitTestCase { protected $db_prefix_original; protected $original_file_directory; + var $_results = array('#pass' => 0, '#fail' => 0, '#exception' => 0); + + /** + * Constructor for DrupalWebTestCase. + * + * @param @test_id + * Tests with the same id are reported together. + */ + function __construct($test_id = NULL) { + $this->test_id = $test_id; + } + + /** + * This function stores the assert. Do not call directly. + * + * @param $status + * Can be 'pass', 'fail', 'exception'. TRUE is a synonym for 'pass', FALSE + * for 'fail'. + * @param $message + * The message string. + * @param $group + * WHich group this assert belongs to. + * @param $custom_caller + * By default, the assert comes from a function which names start with + * 'test'. Instead, you can specify where this assert originates from + * by passing in an associative array as $custom_caller. Key 'file' is + * the name of the source file, 'line' is the line number and 'function' + * is the caller function itself. + */ + protected function _assert($status, $message = '', $group = 'Other', $custom_caller = NULL) { + global $db_prefix; + if (is_bool($status)) { + $status = $status ? 'pass' : 'fail'; + } + $this->_results['#' . $status]++; + if (!isset($custom_caller)) { + $callers = debug_backtrace(); + array_shift($callers); + foreach ($callers as $function) { + if (substr($function['function'], 0, 6) != 'assert' && $function['function'] != 'pass' && $function['function'] != 'fail') { + break; + } + } + } + else { + $function = $custom_caller; + } + $current_db_prefix = $db_prefix; + $db_prefix = $this->db_prefix_original; + db_query("INSERT INTO {simpletest} (test_id, test_class, status, message, message_group, caller, line, file) VALUES (%d, '%s', '%s', '%s', '%s', '%s', '%s', '%s')", $this->test_id, get_class($this), $status, $message, $group, $function['function'], $function['line'], $function['file']); + $db_prefix = $current_db_prefix; + return $status; + } + + /** + * Check to see if a value is not false (not an empty string, 0, NULL, or FALSE). + * + * @param $value + * The value on which the assertion is to be done. + * @param $message + * The message to display along with the assertion. + * @param $group + * The type of assertion - examples are "Browser", "PHP". + * @return + * The status passed in. + */ + protected function assertTrue($value, $message = '', $group = 'Other') { + return $this->_assert((bool) $value, $message ? $message : t('%value is TRUE', array('%value' => $value)), $group); + } + + /** + * Check to see if a value is false (an empty string, 0, NULL, or FALSE). + * + * @param $value + * The value on which the assertion is to be done. + * @param $message + * The message to display along with the assertion. + * @param $group + * The type of assertion - examples are "Browser", "PHP". + * @return + * The status passed in. + */ + protected function assertFalse($value, $message = '', $group = 'Other') { + return $this->_assert(!$value, $message ? $message : t('%value is FALSE', array('%value' => $value)), $group); + } + + /** + * Check to see if a value is NULL. + * + * @param $value + * The value on which the assertion is to be done. + * @param $message + * The message to display along with the assertion. + * @param $group + * The type of assertion - examples are "Browser", "PHP". + * @return + * The status passed in. + */ + protected function assertNull($value, $message = '', $group = 'Other') { + return $this->_assert(!isset($value), $message ? $message : t('%value is NULL', array('%value' => $value)), $group); + } + + /** + * Check to see if a value is not NULL. + * + * @param $value + * The value on which the assertion is to be done. + * @param $message + * The message to display along with the assertion. + * @param $group + * The type of assertion - examples are "Browser", "PHP". + * @return + * The status passed in. + */ + protected function assertNotNull($value, $message = '', $group = 'Other') { + return $this->_assert(isset($value), $message ? $message : t('%value is not NULL', array('%value' => $value)), $group); + } + + /** + * Check to see if two values are equal. + * + * @param $first + * The first value to check. + * @param $second + * The second value to check. + * @param $message + * The message to display along with the assertion. + * @param $group + * The type of assertion - examples are "Browser", "PHP". + * @return + * The status passed in. + */ + protected function assertEqual($first, $second, $message = '', $group = 'Other') { + return $this->_assert($first == $second, $message ? $message : t('%first is equal to %second', array('%first' => $first, '%second' => $second)), $group); + } + + /** + * Check to see if two values are not equal. + * + * @param $first + * The first value to check. + * @param $second + * The second value to check. + * @param $message + * The message to display along with the assertion. + * @param $group + * The type of assertion - examples are "Browser", "PHP". + * @return + * The status passed in. + */ + protected function assertNotEqual($first, $second, $message = '', $group = 'Other') { + return $this->_assert($first != $second, $message ? $message : t('%first is not equal to %second', array('%first' => $first, '%second' => $second)), $group); + } + + /** + * Check to see if two values are identical. + * + * @param $first + * The first value to check. + * @param $second + * The second value to check. + * @param $message + * The message to display along with the assertion. + * @param $group + * The type of assertion - examples are "Browser", "PHP". + * @return + * The status passed in. + */ + protected function assertIdentical($first, $second, $message = '', $group = 'Other') { + return $this->_assert($first === $second, $message ? $message : t('%first is identical to %second', array('%first' => $first, '%second' => $second)), $group); + } + + /** + * Check to see if two values are not identical. + * + * @param $first + * The first value to check. + * @param $second + * The second value to check. + * @param $message + * The message to display along with the assertion. + * @param $group + * The type of assertion - examples are "Browser", "PHP". + * @return + * The status passed in. + */ + protected function assertNotIdentical($first, $second, $message = '', $group = 'Other') { + return $this->_assert($first !== $second, $message ? $message : t('%first is not identical to %second', array('%first' => $first, '%second' => $second)), $group); + } + + /** + * Fire an assertion that is always positive. + * + * @param $message + * The message to display along with the assertion. + * @param $group + * The type of assertion - examples are "Browser", "PHP". + * @return + * TRUE. + */ + protected function pass($message = NULL, $group = 'Other') { + return $this->_assert(TRUE, $message, $group); + } + /** - * Retrieve the test information from getInfo(). + * Fire an assertion that is always negative. * - * @param string $label Name of the test to be used by the SimpleTest library. + * @param $message + * The message to display along with the assertion. + * @param $group + * The type of assertion - examples are "Browser", "PHP". + * @return + * FALSE. + */ + protected function fail($message = NULL, $group = 'Other') { + return $this->_assert(FALSE, $message, $group); + } + + /** + * Fire an error assertion. + * + * @param $message + * The message to display along with the assertion. + * @param $group + * The type of assertion - examples are "Browser", "PHP". + * @param $custom_caller + * The caller of the error. + */ + protected function error($message = '', $group = 'Other', $custom_caller = NULL) { + return $this->_assert('exception', $message, $group, $custom_caller); + } + + /** + * Run all tests in this class. */ - function __construct($label = NULL) { - if (!$label) { - if (method_exists($this, 'getInfo')) { - $info = $this->getInfo(); - $label = $info['name']; + function run() { + set_error_handler(array($this, 'errorHandler')); + $this->setUp(); + $methods = array(); + // Iterate through all the methods in this class. + foreach (get_class_methods(get_class($this)) as $method) { + // If the current method starts with "test", run it - it's a test. + if (strtolower(substr($method, 0, 4)) == 'test') { + $this->$method(); } } - parent::__construct($label); + // Finish up. + $this->tearDown(); + restore_error_handler(); + } + + /** + * Handle errors. + * + * @see set_error_handler + */ + function errorHandler($severity, $message, $file = NULL, $line = NULL) { + $severity = $severity & error_reporting(); + if ($severity) { + $error_map = array( + E_STRICT => 'Run-time notice', + E_WARNING => 'Warning', + E_NOTICE => 'Notice', + E_CORE_ERROR => 'Core error', + E_CORE_WARNING => 'Core warning', + E_USER_ERROR => 'User error', + E_USER_WARNING => 'User warning', + E_USER_NOTICE => 'User notice', + E_RECOVERABLE_ERROR => 'Recoverable error', + ); + $this->error($message, $error_map[$severity], array( + 'function' => '', + 'line' => $line, + 'file' => $file, + )); + } + return TRUE; } /** * Creates a node based on default settings. * - * @param settings - * An assocative array of settings to change from the defaults, keys are + * @param $settings + * An associative array of settings to change from the defaults, keys are * node properties, for example 'body' => 'Hello, world!'. * @return object Created node object. */ @@ -62,8 +326,8 @@ class DrupalWebTestCase extends UnitTestCase { ); $defaults['teaser'] = $defaults['body']; // If we already have a node, we use the original node's created time, and this - if (isset($settings['created'])) { - $defaults['date'] = format_date($settings['created'], 'custom', 'Y-m-d H:i:s O'); + if (isset($defaults['created'])) { + $defaults['date'] = format_date($defaults['created'], 'custom', 'Y-m-d H:i:s O'); } if (empty($settings['uid'])) { global $user; @@ -82,10 +346,11 @@ class DrupalWebTestCase extends UnitTestCase { /** * Creates a custom content type based on default settings. * - * @param settings + * @param $settings * An array of settings to change from the defaults. * Example: 'type' => 'foo'. - * @return object Created content type. + * @return + * Created content type. */ function drupalCreateContentType($settings = array()) { // find a non-existent random type name. @@ -126,9 +391,12 @@ class DrupalWebTestCase extends UnitTestCase { /** * Get a list files that can be used in tests. * - * @param string $type File type, possible values: 'binary', 'html', 'image', 'javascript', 'php', 'sql', 'text'. - * @param integer $size File size in bytes to match. Please check the tests/files folder. - * @return array List of files that match filter. + * @param $type + * File type, possible values: 'binary', 'html', 'image', 'javascript', 'php', 'sql', 'text'. + * @param $size + * File size in bytes to match. Please check the tests/files folder. + * @return + * List of files that match filter. */ function drupalGetTestFiles($type, $size = NULL) { $files = array(); @@ -166,9 +434,12 @@ class DrupalWebTestCase extends UnitTestCase { /** * Generates a random string. * - * @param integer $number Number of characters in length to append to the prefix. - * @param string $prefix Prefix to use. - * @return string Randomly generated string. + * @param $number + * Number of characters in length to append to the prefix. + * @param $prefix + * Prefix to use. + * @return + * Randomly generated string. */ function randomName($number = 4, $prefix = 'simpletest_') { $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_'; @@ -185,8 +456,10 @@ class DrupalWebTestCase extends UnitTestCase { * Create a user with a given set of permissions. The permissions correspond to the * names given on the privileges page. * - * @param array $permissions Array of permission names to assign to user. - * @return A fully loaded user object with pass_raw property, or FALSE if account + * @param $permissions + * Array of permission names to assign to user. + * @return + * A fully loaded user object with pass_raw property, or FALSE if account * creation fails. */ function drupalCreateUser($permissions = NULL) { @@ -218,8 +491,10 @@ class DrupalWebTestCase extends UnitTestCase { /** * Internal helper function; Create a role with specified permissions. * - * @param array $permissions Array of permission names to assign to role. - * @return integer Role ID of newly created role, or FALSE if role creation failed. + * @param $permissions + * Array of permission names to assign to role. + * @return + * Role ID of newly created role, or FALSE if role creation failed. */ private function _drupalCreateRole($permissions = NULL) { // Generate string version of permissions list. @@ -253,9 +528,12 @@ class DrupalWebTestCase extends UnitTestCase { /** * Check to make sure that the array of permissions are valid. * - * @param array $permissions Permissions to check. - * @param boolean $reset Reset cached available permissions. - * @return boolean Valid. + * @param $permissions + * Permissions to check. + * @param $reset + * Reset cached available permissions. + * @return + * TRUE or FALSE depending on whether the permissions are valid. */ private function checkPermissions(array $permissions, $reset = FALSE) { static $available; @@ -279,9 +557,11 @@ class DrupalWebTestCase extends UnitTestCase { * user out before logging in the specified user. If no user is specified then a new * user will be created and logged in. * - * @param object $user User object representing the user to login. - * @return object User that was logged in. Useful if no user was passed in order - * to retreive the created user. + * @param $user + * User object representing the user to login. + * @return + * User that was logged in. Useful if no user was passed in order to retrieve + * the created user. */ function drupalLogin($user = NULL) { if ($this->_logged_in) { @@ -369,8 +649,6 @@ class DrupalWebTestCase extends UnitTestCase { $this->original_file_directory = file_directory_path(); variable_set('file_directory_path', file_directory_path() . '/' . $db_prefix); file_check_directory(file_directory_path(), TRUE); // Create the files directory. - - parent::setUp(); } /** @@ -423,27 +701,14 @@ class DrupalWebTestCase extends UnitTestCase { // Close the CURL handler. $this->curlClose(); + restore_error_handler(); } - parent::tearDown(); - } - - /** - * Set necessary reporter info. - */ - function run(&$reporter) { - $arr = array('class' => get_class($this)); - if (method_exists($this, 'getInfo')) { - $arr = array_merge($arr, $this->getInfo()); - } - $reporter->test_info_stack[] = $arr; - parent::run($reporter); - array_pop($reporter->test_info_stack); } /** * Initializes the cURL connection and gets a session cookie. * - * This function will add authentaticon headers as specified in + * This function will add authentication headers as specified in * simpletest_httpauth_username and simpletest_httpauth_pass variables. * Also, see the description of $curl_options among the properties. */ @@ -471,10 +736,12 @@ class DrupalWebTestCase extends UnitTestCase { } /** - * Peforms a cURL exec with the specified options after calling curlConnect(). + * Performs a cURL exec with the specified options after calling curlConnect(). * - * @param array $curl_options Custom cURL options. - * @return string Content returned from the exec. + * @param + * $curl_options Custom cURL options. + * @return + * Content returned from the exec. */ protected function curlExec($curl_options) { $this->curlConnect(); @@ -498,9 +765,10 @@ class DrupalWebTestCase extends UnitTestCase { } /** - * Parse content returned from curlExec using DOM and simplexml. + * Parse content returned from curlExec using DOM and SimpleXML. * - * @return SimpleXMLElement A SimpleXMLElement or FALSE on failure. + * @return + * A SimpleXMLElement or FALSE on failure. */ protected function parse() { if (!$this->elements) { @@ -523,9 +791,12 @@ class DrupalWebTestCase extends UnitTestCase { /** * Retrieves a Drupal path or an absolute path. * - * @param $path string Drupal path or url to load into internal browser - * @param array $options Options to be forwarded to url(). - * @return The retrieved HTML string, also available as $this->drupalGetContent() + * @param $path + * Drupal path or url to load into internal browser + * @param $options + * Options to be forwarded to url(). + * @return + * The retrieved HTML string, also available as $this->drupalGetContent() */ function drupalGet($path, $options = array()) { $options['absolute'] = TRUE; @@ -542,14 +813,14 @@ class DrupalWebTestCase extends UnitTestCase { * Execute a POST request on a Drupal page. * It will be done as usual POST request with SimpleBrowser. * - * @param string $path + * @param $path * Location of the post form. Either a Drupal path or an absolute path or * NULL to post to the current page. - * @param array $edit + * @param $edit * Field data in an assocative array. Changes the current input fields * (where possible) to the values indicated. A checkbox can be set to * TRUE to be checked and FALSE to be unchecked. - * @param string $submit + * @param $submit * Value of the submit button. * @param $tamper * If this is set to TRUE then you can post anything, otherwise hidden and @@ -604,15 +875,15 @@ class DrupalWebTestCase extends UnitTestCase { * exist and attempt to create POST data in the correct manner for the particular * field type. * - * @param array $post + * @param $post * Reference to array of post values. - * @param array $edit + * @param $edit * Reference to array of edit values to be checked against the form. - * @param string $submit + * @param $submit * Form submit button value. - * @param array $form + * @param $form * Array of form elements. - * @return boolean + * @return * Submit value matches a valid submit input in the form. */ protected function handleForm(&$post, &$edit, &$upload, $submit, $form) { @@ -622,13 +893,12 @@ class DrupalWebTestCase extends UnitTestCase { foreach ($elements as $element) { // SimpleXML objects need string casting all the time. $name = (string) $element['name']; - $id = (string) $element['id']; // This can either be the type of or the name of the tag itself // for