diff options
Diffstat (limited to 'modules/simpletest')
145 files changed, 0 insertions, 31464 deletions
diff --git a/modules/simpletest/drupal_web_test_case.php b/modules/simpletest/drupal_web_test_case.php deleted file mode 100644 index 81264d1858d..00000000000 --- a/modules/simpletest/drupal_web_test_case.php +++ /dev/null @@ -1,3438 +0,0 @@ -<?php - -/** - * Global variable that holds information about the tests being run. - * - * 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 - */ -global $drupal_test_info; - -/** - * Base class for Drupal tests. - * - * Do not extend this class, use one of the subclasses in this file. - */ -abstract class DrupalTestCase { - /** - * The test run ID. - * - * @var string - */ - protected $testId; - - /** - * The database prefix of this test run. - * - * @var string - */ - protected $databasePrefix = NULL; - - /** - * The original file directory, before it was changed for testing purposes. - * - * @var string - */ - protected $originalFileDirectory = NULL; - - /** - * Time limit for the test. - */ - protected $timeLimit = 500; - - /** - * Current results of this test case. - * - * @var Array - */ - public $results = array( - '#pass' => 0, - '#fail' => 0, - '#exception' => 0, - '#debug' => 0, - ); - - /** - * Assertions thrown in that test case. - * - * @var Array - */ - protected $assertions = array(); - - /** - * 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 $skipClasses = array(__CLASS__ => TRUE); - - /** - * Constructor for DrupalTestCase. - * - * @param $test_id - * Tests with the same id are reported together. - */ - public function __construct($test_id = NULL) { - $this->testId = $test_id; - } - - /** - * Internal helper: stores the assert. - * - * @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 $caller - * By default, the assert comes from a function whose name starts with - * 'test'. Instead, you can specify where this assert originates from - * by passing in an associative array as $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', array $caller = NULL) { - // Convert boolean status to string status. - if (is_bool($status)) { - $status = $status ? 'pass' : 'fail'; - } - - // Increment summary result counter. - $this->results['#' . $status]++; - - // Get the function information about the call to the assertion method. - if (!$caller) { - $caller = $this->getAssertionCall(); - } - - // Creation assertion array that can be displayed while tests are running. - $this->assertions[] = $assertion = array( - 'test_id' => $this->testId, - 'test_class' => get_class($this), - 'status' => $status, - 'message' => $message, - 'message_group' => $group, - 'function' => $caller['function'], - 'line' => $caller['line'], - 'file' => $caller['file'], - ); - - // Store assertion for display after the test has completed. - Database::getConnection('default', 'simpletest_original_default') - ->insert('simpletest') - ->fields($assertion) - ->execute(); - - // We do not use a ternary operator here to allow a breakpoint on - // test failure. - if ($status == 'pass') { - return TRUE; - } - else { - return FALSE; - } - } - - /** - * Store an assertion from outside the testing context. - * - * This is useful for inserting assertions that can only be recorded after - * the test case has been destroyed, such as PHP fatal errors. The caller - * information is not automatically gathered since the caller is most likely - * inserting the assertion on behalf of other code. In all other respects - * 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. - if (is_bool($status)) { - $status = $status ? 'pass' : 'fail'; - } - - $caller += array( - 'function' => t('Unknown'), - 'line' => 0, - 'file' => t('Unknown'), - ); - - $assertion = array( - 'test_id' => $test_id, - 'test_class' => $test_class, - 'status' => $status, - 'message' => $message, - 'message_group' => $group, - 'function' => $caller['function'], - 'line' => $caller['line'], - 'file' => $caller['file'], - ); - - return db_insert('simpletest') - ->fields($assertion) - ->execute(); - } - - /** - * 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) { - return (bool) db_delete('simpletest') - ->condition('message_id', $message_id) - ->execute(); - } - - /** - * Cycles through backtrace until the first non-assertion method is found. - * - * @return - * Array representing the true caller. - */ - 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 our base classes - // or in an assertion function. - 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); - } - - return _drupal_get_last_caller($backtrace); - } - - /** - * 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 - * TRUE if the assertion succeeded, FALSE otherwise. - */ - protected function assertTrue($value, $message = '', $group = 'Other') { - return $this->assert((bool) $value, $message ? $message : t('Value @value is TRUE.', array('@value' => var_export($value, TRUE))), $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 - * TRUE if the assertion succeeded, FALSE otherwise. - */ - protected function assertFalse($value, $message = '', $group = 'Other') { - return $this->assert(!$value, $message ? $message : t('Value @value is FALSE.', array('@value' => var_export($value, TRUE))), $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 - * TRUE if the assertion succeeded, FALSE otherwise. - */ - protected function assertNull($value, $message = '', $group = 'Other') { - return $this->assert(!isset($value), $message ? $message : t('Value @value is NULL.', array('@value' => var_export($value, TRUE))), $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 - * TRUE if the assertion succeeded, FALSE otherwise. - */ - protected function assertNotNull($value, $message = '', $group = 'Other') { - return $this->assert(isset($value), $message ? $message : t('Value @value is not NULL.', array('@value' => var_export($value, TRUE))), $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 - * TRUE if the assertion succeeded, FALSE otherwise. - */ - protected function assertEqual($first, $second, $message = '', $group = 'Other') { - 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); - } - - /** - * 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 - * TRUE if the assertion succeeded, FALSE otherwise. - */ - protected function assertNotEqual($first, $second, $message = '', $group = 'Other') { - 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); - } - - /** - * 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 - * TRUE if the assertion succeeded, FALSE otherwise. - */ - protected function assertIdentical($first, $second, $message = '', $group = 'Other') { - 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); - } - - /** - * 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 - * TRUE if the assertion succeeded, FALSE otherwise. - */ - protected function assertNotIdentical($first, $second, $message = '', $group = 'Other') { - 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); - } - - /** - * 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); - } - - /** - * Fire an assertion that is always negative. - * - * @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 $caller - * The caller of the error. - * @return - * FALSE. - */ - protected function error($message = '', $group = 'Other', array $caller = NULL) { - if ($group == 'User notice') { - // Since 'User notice' is set by trigger_error() which is used for debug - // set the message to a status of 'debug'. - return $this->assert('debug', $message, 'Debug', $caller); - } - - return $this->assert('exception', $message, $group, $caller); - } - - /** - * 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)) { - $url = file_create_url($this->originalFileDirectory . '/simpletest/verbose/' . get_class($this) . '-' . $id . '.html'); - $this->error(l(t('Verbose message'), $url, array('attributes' => array('target' => '_blank'))), 'User notice'); - } - } - - /** - * Run all tests in this class. - * - * Regardless of whether $methods are passed or not, only method names - * starting with "test" are executed. - * - * @param $methods - * (optional) A list of method names in the test case class to run; e.g., - * array('testFoo', 'testBar'). By default, all methods of the class are - * taken into account, but it can be useful to only run a few selected test - * methods during debugging. - */ - public function run(array $methods = array()) { - // Initialize verbose debugging. - simpletest_verbose(NULL, variable_get('file_public_path', conf_path() . '/files'), get_class($this)); - - // HTTP auth settings (<username>:<password>) for the simpletest browser - // when sending requests to the test site. - $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')); - $class = get_class($this); - // Iterate through all the methods in this class, unless a specific list of - // methods to run was passed. - $class_methods = get_class_methods($class); - if ($methods) { - $class_methods = array_intersect($class_methods, $methods); - } - foreach ($class_methods as $method) { - // If the current method starts with "test", run it - it's a test. - if (strtolower(substr($method, 0, 4)) == 'test') { - // Insert a fail record. This will be deleted on completion to ensure - // 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(); - // Finish up. - } - catch (Exception $e) { - $this->exceptionHandler($e); - } - $this->tearDown(); - // Remove the completion check record. - DrupalTestCase::deleteAssert($completion_check_id); - } - } - // Clear out the error messages and restore error handler. - drupal_get_messages(); - restore_error_handler(); - } - - /** - * 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()) { - $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', - ); - - $backtrace = debug_backtrace(); - $this->error($message, $error_map[$severity], _drupal_get_last_caller($backtrace)); - } - return TRUE; - } - - /** - * Handle exceptions. - * - * @see set_exception_handler - */ - protected function exceptionHandler($exception) { - $backtrace = $exception->getTrace(); - // Push on top of the backtrace the call that generated the exception. - array_unshift($backtrace, array( - 'line' => $exception->getLine(), - 'file' => $exception->getFile(), - )); - require_once DRUPAL_ROOT . '/includes/errors.inc'; - // The exception message is run through check_plain() by _drupal_decode_exception(). - $this->error(t('%type: !message in %function (line %line of %file).', _drupal_decode_exception($exception)), '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. - * @return - * Randomly generated string. - */ - public static function randomString($length = 8) { - $str = ''; - for ($i = 0; $i < $length; $i++) { - $str .= chr(mt_rand(32, 126)); - } - return $str; - } - - /** - * Generates a random string containing letters and numbers. - * - * 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. - * @return - * Randomly generated string. - */ - public static function randomName($length = 8) { - $values = array_merge(range(65, 90), range(97, 122), range(48, 57)); - $max = count($values) - 1; - $str = chr(mt_rand(97, 122)); - for ($i = 1; $i < $length; $i++) { - $str .= chr($values[mt_rand(0, $max)]); - } - return $str; - } - - /** - * Converts a list of possible parameters into a stack of permutations. - * - * Takes a list of parameters containing possible values, and converts all of - * them into a list of items containing every possible permutation. - * - * Example: - * @code - * $parameters = array( - * 'one' => array(0, 1), - * 'two' => array(2, 3), - * ); - * $permutations = $this->permute($parameters); - * // Result: - * $permutations == array( - * array('one' => 0, 'two' => 2), - * array('one' => 1, 'two' => 2), - * array('one' => 0, 'two' => 3), - * array('one' => 1, 'two' => 3), - * ) - * @endcode - * - * @param $parameters - * An associative array of parameters, keyed by parameter name, and whose - * values are arrays of parameter values. - * - * @return - * A list of permutations, which is an array of arrays. Each inner array - * contains the full list of parameters that have been passed, but with a - * single value only. - */ - public static function generatePermutations($parameters) { - $all_permutations = array(array()); - foreach ($parameters as $parameter => $values) { - $new_permutations = array(); - // Iterate over all values of the parameter. - foreach ($values as $value) { - // Iterate over all existing permutations. - foreach ($all_permutations as $permutation) { - // Add the new parameter value to existing permutations. - $new_permutations[] = $permutation + array($parameter => $value); - } - } - // Replace the old permutations with the new permutations. - $all_permutations = $new_permutations; - } - return $all_permutations; - } -} - -/** - * 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(), 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; - } - - /** - * Sets up unit test environment. - * - * Unlike DrupalWebTestCase::setUp(), DrupalUnitTestCase::setUp() does not - * install modules because tests are performed without accessing the database. - * Any required files must be explicitly included by the child class setUp() - * method. - */ - protected function setUp() { - global $conf; - - // Store necessary current values before switching to the test environment. - $this->originalFileDirectory = variable_get('file_public_path', conf_path() . '/files'); - - // 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. - $this->databasePrefix = Database::getConnection()->prefixTables('{simpletest' . mt_rand(1000, 1000000) . '}'); - - // Create test directory. - $public_files_directory = $this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10); - file_prepare_directory($public_files_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); - $conf['file_public_path'] = $public_files_directory; - - // Clone the current connection and replace the current prefix. - $connection_info = Database::getConnectionInfo('default'); - Database::renameConnection('default', 'simpletest_original_default'); - foreach ($connection_info as $target => $value) { - $connection_info[$target]['prefix'] = array( - 'default' => $value['prefix']['default'] . $this->databasePrefix, - ); - } - Database::addConnectionInfo('default', 'default', $connection_info['default']); - - // 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. - $module_list = module_list(); - if (isset($module_list['locale'])) { - $this->originalModuleList = $module_list; - unset($module_list['locale']); - module_list(TRUE, FALSE, FALSE, $module_list); - } - } - - protected function tearDown() { - global $conf; - - // Get back to the original connection. - Database::removeConnection('default'); - Database::renameConnection('simpletest_original_default', 'default'); - - $conf['file_public_path'] = $this->originalFileDirectory; - // Restore modules if necessary. - if (isset($this->originalModuleList)) { - module_list(TRUE, FALSE, FALSE, $this->originalModuleList); - } - } -} - -/** - * Test case for typical Drupal tests. - */ -class DrupalWebTestCase extends DrupalTestCase { - /** - * The profile to install as a basis for testing. - * - * @var string - */ - protected $profile = 'standard'; - - /** - * 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 value of the Drupal.settings JavaScript variable for the page currently loaded in the internal browser. - * - * @var Array - */ - protected $drupalSettings; - - /** - * 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; - - /** - * 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; - - /** - * The current session name, if available. - */ - protected $session_name = NULL; - - /** - * The current session ID, if available. - */ - 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) { - parent::__construct($test_id); - $this->skipClasses[__CLASS__] = TRUE; - } - - /** - * Get a node from the database based on its title. - * - * @param $title - * A node title, usually generated by $this->randomName(). - * @param $reset - * (optional) Whether to reset the internal node_load() cache. - * - * @return - * A node object matching $title. - */ - function drupalGetNodeByTitle($title, $reset = FALSE) { - $nodes = node_load_multiple(array(), array('title' => $title), $reset); - // Load the first node returned from the database. - $returned_node = reset($nodes); - return $returned_node; - } - - /** - * Creates a node based on default settings. - * - * @param $settings - * An associative array of settings to change from the defaults, keys are - * node properties, for example 'title' => 'Hello, world!'. - * @return - * Created node object. - */ - protected function drupalCreateNode($settings = array()) { - // Populate defaults array. - $settings += array( - 'body' => array(LANGUAGE_NONE => array(array())), - 'title' => $this->randomName(8), - 'comment' => 2, - 'changed' => REQUEST_TIME, - 'moderate' => 0, - 'promote' => 0, - 'revision' => 1, - 'log' => '', - 'status' => 1, - 'sticky' => 0, - 'type' => 'page', - 'revisions' => NULL, - 'language' => LANGUAGE_NONE, - ); - - // Use the original node's created time for existing nodes. - if (isset($settings['created']) && !isset($settings['date'])) { - $settings['date'] = format_date($settings['created'], 'custom', 'Y-m-d H:i:s O'); - } - - // If the node's user uid is not specified manually, use the currently - // logged in user if available, or else the user running the test. - if (!isset($settings['uid'])) { - if ($this->loggedInUser) { - $settings['uid'] = $this->loggedInUser->uid; - } - else { - global $user; - $settings['uid'] = $user->uid; - } - } - - // Merge body field value and format separately. - $body = array( - 'value' => $this->randomName(32), - 'format' => filter_default_format(), - ); - $settings['body'][$settings['language']][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(); - return $node; - } - - /** - * Creates a custom content type based on default settings. - * - * @param $settings - * An array of settings to change from the defaults. - * Example: 'type' => 'foo'. - * @return - * Created content type. - */ - protected function drupalCreateContentType($settings = array()) { - // Find a non-existent random type name. - do { - $name = strtolower($this->randomName(8)); - } while (node_type_get_type($name)); - - // Populate defaults array. - $defaults = array( - 'type' => $name, - 'name' => $name, - 'base' => 'node_content', - 'description' => '', - 'help' => '', - 'title_label' => 'Title', - 'body_label' => 'Body', - 'has_title' => 1, - 'has_body' => 1, - ); - // Imposed values for a custom type. - $forced = array( - 'orig_type' => '', - 'old_type' => '', - 'module' => 'node', - 'custom' => 1, - 'modified' => 1, - 'locked' => 0, - ); - $type = $forced + $settings + $defaults; - $type = (object) $type; - - $saved_type = node_type_save($type); - node_types_rebuild(); - menu_rebuild(); - node_add_body_field($type); - - $this->assertEqual($saved_type, SAVED_NEW, t('Created content type %type.', array('%type' => $type->type))); - - // Reset permissions so that permissions for this content type are available. - $this->checkPermissions(array(), TRUE); - - return $type; - } - - /** - * Get a list files that can be used in tests. - * - * @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. - */ - protected function drupalGetTestFiles($type, $size = NULL) { - 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)-.*/'); - foreach ($files as $file) { - file_unmanaged_copy($file->uri, variable_get('file_public_path', conf_path() . '/files')); - } - - $this->generatedTestFiles = TRUE; - } - - $files = array(); - // Make sure type is valid. - if (in_array($type, array('binary', 'html', 'image', 'javascript', 'php', 'sql', 'text'))) { - $files = file_scan_directory('public://', '/' . $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); - if ($stats['size'] != $size) { - unset($files[$file->uri]); - } - } - } - } - usort($files, array($this, 'drupalCompareFiles')); - return $files; - } - - /** - * Compare two files based on size and file name. - */ - protected function drupalCompareFiles($file1, $file2) { - $compare_size = filesize($file1->uri) - filesize($file2->uri); - if ($compare_size) { - // Sort by file size. - return $compare_size; - } - else { - // The files were the same size, so sort alphabetically. - return strnatcmp($file1->name, $file2->name); - } - } - - /** - * Create a user with a given set of permissions. The permissions correspond to the - * names given on the privileges page. - * - * @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. - */ - protected function drupalCreateUser($permissions = array('access comments', 'access content', 'post comments', 'skip comment approval')) { - // Create a role with the given permission set. - if (!($rid = $this->drupalCreateRole($permissions))) { - return FALSE; - } - - // Create a user assigned to that role. - $edit = array(); - $edit['name'] = $this->randomName(); - $edit['mail'] = $edit['name'] . '@example.com'; - $edit['roles'] = array($rid => $rid); - $edit['pass'] = user_password(); - $edit['status'] = 1; - - $account = user_save(drupal_anonymous_user(), $edit); - - $this->assertTrue(!empty($account->uid), t('User created with name %name and pass %pass', array('%name' => $edit['name'], '%pass' => $edit['pass'])), t('User login')); - if (empty($account->uid)) { - return FALSE; - } - - // Add the raw password so that we can log in as this user. - $account->pass_raw = $edit['pass']; - return $account; - } - - /** - * Internal helper function; Create a role with specified permissions. - * - * @param $permissions - * Array of permission names to assign to role. - * @param $name - * (optional) String for the name of the role. Defaults to a random string. - * @return - * Role ID of newly created role, or FALSE if role creation failed. - */ - protected function drupalCreateRole(array $permissions, $name = NULL) { - // Generate random name if it was not passed. - if (!$name) { - $name = $this->randomName(); - } - - // Check the all the permissions strings are valid. - if (!$this->checkPermissions($permissions)) { - return FALSE; - } - - // Create new role. - $role = new stdClass(); - $role->name = $name; - user_role_save($role); - user_role_grant_permissions($role->rid, $permissions); - - $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')); - return $role->rid; - } - else { - return FALSE; - } - } - - /** - * Check to make sure that the array of permissions are valid. - * - * @param $permissions - * Permissions to check. - * @param $reset - * Reset cached available permissions. - * @return - * TRUE or FALSE depending on whether the permissions are valid. - */ - protected function checkPermissions(array $permissions, $reset = FALSE) { - $available = &drupal_static(__FUNCTION__); - - if (!isset($available) || $reset) { - $available = array_keys(module_invoke_all('permission')); - } - - $valid = TRUE; - foreach ($permissions as $permission) { - if (!in_array($permission, $available)) { - $this->fail(t('Invalid permission %permission.', array('%permission' => $permission)), t('Role')); - $valid = FALSE; - } - } - return $valid; - } - - /** - * Log in a user with the internal browser. - * - * If a user is already logged in, then the current user is logged out before - * logging in the specified user. - * - * Please note that neither the global $user nor the passed-in user object is - * 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 log in the same user again, then it must be re-assigned manually. - * For example: - * @code - * // Create a user. - * $account = $this->drupalCreateUser(array()); - * $this->drupalLogin($account); - * // Load real user object. - * $pass_raw = $account->pass_raw; - * $account = user_load($account->uid); - * $account->pass_raw = $pass_raw; - * @endcode - * - * @param $user - * User object representing the user to log in. - * - * @see drupalCreateUser() - */ - protected function drupalLogin(stdClass $user) { - if ($this->loggedInUser) { - $this->drupalLogout(); - } - - $edit = array( - 'name' => $user->name, - 'pass' => $user->pass_raw - ); - $this->drupalPost('user', $edit, t('Log in')); - - // If a "log out" link appears on the page, it is almost certainly because - // the login was successful. - $pass = $this->assertLink(t('Log out'), 0, t('User %name successfully logged in.', array('%name' => $user->name)), t('User login')); - - if ($pass) { - $this->loggedInUser = $user; - } - } - - /** - * Generate a token for the currently logged in user. - */ - protected function drupalGetToken($value = '') { - $private_key = drupal_get_private_key(); - return drupal_hmac_base64($value, $this->session_id . $private_key); - } - - /* - * Logs a user out of the internal browser, then check the login page to confirm logout. - */ - protected function drupalLogout() { - // 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'); - $this->drupalGet('user'); - $pass = $this->assertField('name', t('Username field found.'), t('Logout')); - $pass = $pass && $this->assertField('pass', t('Password field found.'), t('Logout')); - - if ($pass) { - $this->loggedInUser = FALSE; - } - } - - /** - * Generates a random database prefix, runs the install scripts on the - * prefixed database and enable the specified modules. After installation - * many caches are flushed and the internal browser is setup so that the - * page requests will run on the new prefix. A temporary files directory - * is created with the same name as the database prefix. - * - * @param ... - * 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 $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_update('simpletest_test_id') - ->fields(array('last_prefix' => $this->databasePrefix)) - ->condition('test_id', $this->testId) - ->execute(); - - // Reset all statics and variables to perform tests in a clean environment. - $conf = array(); - drupal_static_reset(); - - // Clone the current connection and replace the current prefix. - $connection_info = Database::getConnectionInfo('default'); - Database::renameConnection('default', 'simpletest_original_default'); - foreach ($connection_info as $target => $value) { - $connection_info[$target]['prefix'] = array( - 'default' => $value['prefix']['default'] . $this->databasePrefix, - ); - } - Database::addConnectionInfo('default', 'default', $connection_info['default']); - - // Store necessary current values before switching to prefixed database. - $this->originalLanguage = $language; - $this->originalLanguageDefault = variable_get('language_default'); - $this->originalFileDirectory = variable_get('file_public_path', conf_path() . '/files'); - $this->originalProfile = drupal_get_profile(); - $clean_url_original = variable_get('clean_url', 0); - - // Set to English to prevent exceptions from utf8_truncate() from t() - // during install if the current language is not 'en'. - // The following array/object conversion is copied from language_default(). - $language = (object) array('language' => 'en', 'name' => 'English', 'native' => 'English', 'direction' => 0, 'enabled' => 1, 'plurals' => 0, 'formula' => '', 'domain' => '', 'prefix' => '', 'weight' => 0, 'javascript' => ''); - - // Save and clean shutdown callbacks array because it static cached and - // will be changed by the test run. If we don't, then it will contain - // callbacks from both environments. So testing environment will try - // to call handlers from original environment. - $callbacks = &drupal_register_shutdown_function(); - $this->originalShutdownCallbacks = $callbacks; - $callbacks = array(); - - // Create test directory ahead of installation so fatal errors and debug - // information can be logged during installation process. - // Use temporary files directory with the same prefix as the database. - $public_files_directory = $this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10); - $private_files_directory = $public_files_directory . '/private'; - $temp_files_directory = $private_files_directory . '/temp'; - - // Create the directories - file_prepare_directory($public_files_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); - file_prepare_directory($private_files_directory, FILE_CREATE_DIRECTORY); - file_prepare_directory($temp_files_directory, FILE_CREATE_DIRECTORY); - $this->generatedTestFiles = FALSE; - - // Log fatal errors. - ini_set('log_errors', 1); - ini_set('error_log', $public_files_directory . '/error.log'); - - // 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'; - drupal_install_system(); - - $this->preloadRegistry(); - - // Set path variables. - variable_set('file_public_path', $public_files_directory); - variable_set('file_private_path', $private_files_directory); - variable_set('file_temporary_path', $temp_files_directory); - - // Include the testing profile. - variable_set('install_profile', $this->profile); - $profile_details = install_profile_info($this->profile, 'en'); - - // Install the modules specified by the testing profile. - module_enable($profile_details['dependencies'], FALSE); - - // 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(); - if (isset($modules[0]) && is_array($modules[0])) { - $modules = $modules[0]; - } - if ($modules) { - $success = module_enable($modules, TRUE); - $this->assertTrue($success, t('Enabled modules: %modules', array('%modules' => implode(', ', $modules)))); - } - - // Run the profile tasks. - $install_profile_module_exists = db_query("SELECT 1 FROM {system} WHERE type = 'module' AND name = :name", array( - ':name' => $this->profile, - ))->fetchField(); - if ($install_profile_module_exists) { - module_enable(array($this->profile), FALSE); - } - - // Reset/rebuild all data structures after enabling the modules. - $this->resetAll(); - - // Run cron once in that environment, as install.php does at the end of - // the installation process. - drupal_cron_run(); - - // Log in with a clean $user. - $this->originalUser = $user; - drupal_save_session(FALSE); - $user = user_load(1); - - // Restore necessary variables. - variable_set('install_task', 'done'); - variable_set('clean_url', $clean_url_original); - variable_set('site_mail', 'simpletest@example.com'); - variable_set('date_default_timezone', date_default_timezone_get()); - // 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_system', array('default-system' => 'TestingMailSystem')); - - drupal_set_time_limit($this->timeLimit); - } - - /** - * Preload the registry from the testing site. - * - * This method is called by DrupalWebTestCase::setUp(), and preloads the - * registry from the testing site to cut down on the time it takes to - * set up a clean environment for the current test run. - */ - protected function preloadRegistry() { - // Use two separate queries, each with their own connections: copy the - // {registry} and {registry_file} tables over from the parent installation - // to the child installation. - $original_connection = Database::getConnection('default', 'simpletest_original_default'); - $test_connection = Database::getConnection(); - - foreach (array('registry', 'registry_file') as $table) { - // Find the records from the parent database. - $source_query = $original_connection - ->select($table, array(), array('fetch' => PDO::FETCH_ASSOC)) - ->fields($table); - - $dest_query = $test_connection->insert($table); - - $first = TRUE; - foreach ($source_query->execute() as $row) { - if ($first) { - $dest_query->fields(array_keys($row)); - $first = FALSE; - } - // Insert the records into the child database. - $dest_query->values($row); - } - - $dest_query->execute(); - } - } - - /** - * Reset all data structures after having enabled new modules. - * - * This method is called by DrupalWebTestCase::setUp() after enabling - * the requested modules. It must be called again when additional modules - * are enabled later. - */ - protected function resetAll() { - // Reset all static variables. - drupal_static_reset(); - // Reset the list of enabled modules. - module_list(TRUE); - - // Reset cached schema for new database prefix. This must be done before - // drupal_flush_all_caches() so rebuilds can make use of the schema of - // modules enabled on the cURL side. - drupal_get_schema(NULL, TRUE); - - // Perform rebuilds and flush remaining caches. - drupal_flush_all_caches(); - - // Reload global $conf array and permissions. - $this->refreshVariables(); - $this->checkPermissions(array(), TRUE); - } - - /** - * Refresh the in-memory set of variables. Useful after a page request is made - * that changes a variable in a different thread. - * - * In other words calling a settings page with $this->drupalPost() with a changed - * value would update a variable to reflect that change, but in the thread that - * made the call (thread running the test) the changed variable would not be - * picked up. - * - * This method clears the variables cache and loads a fresh copy from the database - * to ensure that the most up-to-date set of variables is loaded. - */ - protected function refreshVariables() { - global $conf; - cache('bootstrap')->delete('variables'); - $conf = variable_initialize(); - } - - /** - * Delete created files and temporary files directory, delete the tables created by setUp(), - * and reset the database prefix. - */ - protected function tearDown() { - global $user, $language; - - // In case a fatal error occurred that was not in the test process read the - // log to pick up any fatal errors. - 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, '1 e-mail was sent during this test.', '@count e-mails were sent during this test.'); - $this->pass($message, t('E-mail')); - } - - // Delete temporary files directory. - file_unmanaged_delete_recursive($this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10)); - - // Remove all prefixed tables (all the tables in the schema). - $schema = drupal_get_schema(NULL, TRUE); - foreach ($schema as $name => $table) { - db_drop_table($name); - } - - // Get back to the original connection. - Database::removeConnection('default'); - Database::renameConnection('simpletest_original_default', 'default'); - - // Restore original shutdown callbacks array to prevent original - // environment of calling handlers from test run. - $callbacks = &drupal_register_shutdown_function(); - $callbacks = $this->originalShutdownCallbacks; - - // Return the user to the original one. - $user = $this->originalUser; - drupal_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_reset(); - - // 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; - } - - // Close the CURL handler. - $this->curlClose(); - } - - /** - * Initializes the cURL connection. - * - * If the simpletest_httpauth_credentials variable is set, this function will - * add HTTP authentication headers. This is necessary for testing sites that - * are protected by login credentials from public access. - * See the description of $curl_options for other options. - */ - protected function curlInitialize() { - global $base_url; - - if (!isset($this->curlHandle)) { - $this->curlHandle = curl_init(); - $curl_options = array( - CURLOPT_COOKIEJAR => $this->cookieFile, - CURLOPT_URL => $base_url, - 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); - - // By default, the child session name should be the same as the parent. - $this->session_name = session_name(); - } - // 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+/', $this->databasePrefix, $matches)) { - curl_setopt($this->curlHandle, CURLOPT_USERAGENT, drupal_generate_test_ua($matches[0])); - } - } - - /** - * Initializes and executes a cURL request. - * - * @param $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 - * The content returned from the call to curl_exec(). - * - * @see curlInitialize() - */ - 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 - // codes from some webservers (like lighttpd that returns a 417 error - // code). It is done by setting an empty "Expect" header field that is - // not overwritten by Curl. - $curl_options[CURLOPT_HTTPHEADER][] = 'Expect:'; - } - curl_setopt_array($this->curlHandle, $this->additionalCurlOptions + $curl_options); - - 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($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' => 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->drupalGetContent() !== FALSE, $message, t('Browser')); - return $this->drupalGetContent(); - } - - /** - * Reads headers and registers errors received from the tested site. - * - * @see _drupal_log_error(). - * - * @param $curlHandler - * The cURL handler. - * @param $header - * An header. - */ - protected function curlHeaderCallback($curlHandler, $header) { - $this->headers[] = $header; - - // Errors are being sent via X-Drupal-Assertion-* headers, - // generated by _drupal_log_error() in the exact form required - // by DrupalWebTestCase::error(). - if (preg_match('/^X-Drupal-Assertion-[0-9]+: (.*)$/', $header, $matches)) { - // Call DrupalWebTestCase::error() with the parameters from the header. - call_user_func_array(array(&$this, 'error'), unserialize(urldecode($matches[1]))); - } - - // Save cookies. - if (preg_match('/^Set-Cookie: ([^=]+)=(.+)/', $header, $matches)) { - $name = $matches[1]; - $parts = array_map('trim', explode(';', $matches[2])); - $value = array_shift($parts); - $this->cookies[$name] = array('value' => $value, 'secure' => in_array('secure', $parts)); - if ($name == $this->session_name) { - if ($value != 'deleted') { - $this->session_id = $value; - } - else { - $this->session_id = NULL; - } - } - } - - // This is required by cURL. - return strlen($header); - } - - /** - * Close the cURL handler and unset the handler. - */ - protected function curlClose() { - if (isset($this->curlHandle)) { - curl_close($this->curlHandle); - unset($this->curlHandle); - } - } - - /** - * Parse content returned from curlExec using DOM and SimpleXML. - * - * @return - * A SimpleXMLElement or FALSE on failure. - */ - protected function parse() { - if (!$this->elements) { - // DOM can load HTML soup. But, HTML soup can throw warnings, suppress - // them. - $htmlDom = new DOMDocument(); - @$htmlDom->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 - // we can just simply import our DOM tree. - $this->elements = simplexml_import_dom($htmlDom); - } - } - if (!$this->elements) { - $this->fail(t('Parsed page successfully.'), t('Browser')); - } - - return $this->elements; - } - - /** - * Retrieves a Drupal path or an absolute path. - * - * @param $path - * Drupal path or URL to load into internal browser - * @param $options - * Options to be forwarded to url(). - * @param $headers - * An array containing additional HTTP request headers, each formatted as - * "name: value". - * @return - * The retrieved HTML string, also available as $this->drupalGetContent() - */ - protected function drupalGet($path, array $options = array(), array $headers = array()) { - $options['absolute'] = TRUE; - - // We re-using a CURL connection here. If that connection still has certain - // options set, it might change the GET into a POST. Make sure we clear out - // previous options. - $out = $this->curlExec(array(CURLOPT_HTTPGET => TRUE, CURLOPT_URL => url($path, $options), CURLOPT_NOBODY => FALSE, CURLOPT_HTTPHEADER => $headers)); - $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()) { - $out = $new; - } - $this->verbose('GET request to: ' . $path . - '<hr />Ending URL: ' . $this->getUrl() . - '<hr />' . $out); - return $out; - } - - /** - * Retrieve a Drupal path or an absolute path and JSON decode the result. - */ - protected function drupalGetAJAX($path, array $options = array(), array $headers = array()) { - return drupal_json_decode($this->drupalGet($path, $options, $headers)); - } - - /** - * Execute a POST request on a Drupal page. - * It will be done as usual POST request with SimpleBrowser. - * - * @param $path - * Location of the post form. Either a Drupal path or an absolute path or - * 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')); - * - * // 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 - * TRUE to be checked and FALSE to be unchecked. Note that when a form - * contains file upload fields, other fields cannot start with the '@' - * character. - * - * 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 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. - * - * This function can also be called to emulate an Ajax submission. In this - * case, this value needs to be an array with the following keys: - * - path: A path to submit the form values to for Ajax-specific processing, - * which is likely different than the $path parameter used for retrieving - * the initial form. Defaults to 'system/ajax'. - * - triggering_element: If the value for the 'path' key is 'system/ajax' or - * another generic Ajax processing path, this needs to be set to the name - * of the element. If the name doesn't identify the element uniquely, then - * this should instead be an array with a single key/value pair, - * corresponding to the element name and value. The callback for the - * generic Ajax processing path uses this to find the #ajax information - * for the element, including which specific callback to use for - * processing the request. - * - * This can also be set to NULL in order to emulate an Internet Explorer - * submission of a form with a single text field, and pressing ENTER in that - * textfield: under these conditions, no button information is added to the - * POST data. - * @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. - * @param $extra_post - * (optional) A string of additional data to append to the POST submission. - * This can be used to add POST data for which there are no HTML fields, as - * is done by drupalPostAJAX(). This string is literally appended to the - * POST data, so it must already be urlencoded and contain a leading "&" - * (e.g., "&extra_var1=hello+world&extra_var2=you%26me"). - */ - protected function drupalPost($path, $edit, $submit, array $options = array(), array $headers = array(), $form_html_id = NULL, $extra_post = NULL) { - $submit_matches = FALSE; - $ajax = is_array($submit); - if (isset($path)) { - $this->drupalGet($path, $options); - } - if ($this->parse()) { - $edit_save = $edit; - // Let's iterate over all the forms. - $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, $ajax ? NULL : $submit, $form); - $action = isset($form['action']) ? $this->getAbsoluteUrl((string) $form['action']) : $this->getUrl(); - if ($ajax) { - $action = $this->getAbsoluteUrl(!empty($submit['path']) ? $submit['path'] : 'system/ajax'); - // Ajax callbacks verify the triggering element if necessary, so while - // we may eventually want extra code that verifies it in the - // handleForm() function, it's not currently a requirement. - $submit_matches = TRUE; - } - - // We post only if we managed to handle every field in edit and the - // submit button 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); - if ($file && is_file($file)) { - $post[$key] = '@' . $file; - } - } - } - else { - foreach ($post as $key => $value) { - // Encode according to application/x-www-form-urlencoded - // Both names and values needs to be urlencoded, according to - // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1 - $post[$key] = urlencode($key) . '=' . urlencode($value); - } - $post = implode('&', $post) . $extra_post; - } - $out = $this->curlExec(array(CURLOPT_URL => $action, CURLOPT_POST => TRUE, CURLOPT_POSTFIELDS => $post, CURLOPT_HTTPHEADER => $headers)); - // Ensure that any changes to variables in the other thread are picked up. - $this->refreshVariables(); - - // Replace original page output with new output from redirected page(s). - if ($new = $this->checkForMetaRefresh()) { - $out = $new; - } - $this->verbose('POST request to: ' . $path . - '<hr />Ending URL: ' . $this->getUrl() . - '<hr />Fields: ' . highlight_string('<?php ' . var_export($post_array, TRUE), TRUE) . - '<hr />' . $out); - return $out; - } - } - // We have not found a form which contained all fields of $edit. - foreach ($edit as $name => $value) { - $this->fail(t('Failed to set field @name to @value', array('@name' => $name, '@value' => $value))); - } - 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))); - } - } - - /** - * Execute an Ajax submission. - * - * This executes a POST as ajax.js does. It uses the returned JSON data, an - * array of commands, to update $this->content using equivalent DOM - * manipulation as is used by ajax.js. It also returns the array of commands. - * - * @param $path - * Location of the form containing the Ajax enabled element to test. Can be - * either a Drupal path or an absolute path or NULL to use the current page. - * @param $edit - * Field data in an associative array. Changes the current input fields - * (where possible) to the values indicated. - * @param $triggering_element - * The name of the form element that is responsible for triggering the Ajax - * functionality to test. May be a string or, if the triggering element is - * a button, an associative array where the key is the name of the button - * and the value is the button label. i.e.) array('op' => t('Refresh')). - * @param $ajax_path - * (optional) Override the path set by the Ajax settings of the triggering - * element. In the absence of both the triggering element's Ajax path and - * $ajax_path 'system/ajax' will be used. - * @param $options - * (optional) Options to be forwarded to url(). - * @param $headers - * (optional) An array containing additional HTTP request headers, each - * formatted as "name: value". Forwarded to drupalPost(). - * @param $form_html_id - * (optional) HTML ID of the form to be submitted, use when there is more - * than one identical form on the same page and the value of the triggering - * element is not enough to identify the form. Note this is not the Drupal - * ID of the form but rather the HTML ID of the form. - * @param $ajax_settings - * (optional) An array of Ajax settings which if specified will be used in - * place of the Ajax settings of the triggering element. - * - * @return - * An array of Ajax commands. - * - * @see drupalPost() - * @see ajax.js - */ - protected function drupalPostAJAX($path, $edit, $triggering_element, $ajax_path = NULL, array $options = array(), array $headers = array(), $form_html_id = NULL, $ajax_settings = NULL) { - // Get the content of the initial page prior to calling drupalPost(), since - // drupalPost() replaces $this->content. - if (isset($path)) { - $this->drupalGet($path, $options); - } - $content = $this->content; - $drupal_settings = $this->drupalSettings; - - // Get the Ajax settings bound to the triggering element. - if (!isset($ajax_settings)) { - if (is_array($triggering_element)) { - $xpath = '//*[@name="' . key($triggering_element) . '" and @value="' . current($triggering_element) . '"]'; - } - else { - $xpath = '//*[@name="' . $triggering_element . '"]'; - } - if (isset($form_html_id)) { - $xpath = '//form[@id="' . $form_html_id . '"]' . $xpath; - } - $element = $this->xpath($xpath); - $element_id = (string) $element[0]['id']; - $ajax_settings = $drupal_settings['ajax'][$element_id]; - } - - // Add extra information to the POST data as ajax.js does. - $extra_post = ''; - if (isset($ajax_settings['submit'])) { - foreach ($ajax_settings['submit'] as $key => $value) { - $extra_post .= '&' . urlencode($key) . '=' . urlencode($value); - } - } - foreach ($this->xpath('//*[@id]') as $element) { - $id = (string) $element['id']; - $extra_post .= '&' . urlencode('ajax_html_ids[]') . '=' . urlencode($id); - } - if (isset($drupal_settings['ajaxPageState'])) { - $extra_post .= '&' . urlencode('ajax_page_state[theme]') . '=' . urlencode($drupal_settings['ajaxPageState']['theme']); - $extra_post .= '&' . urlencode('ajax_page_state[theme_token]') . '=' . urlencode($drupal_settings['ajaxPageState']['theme_token']); - foreach ($drupal_settings['ajaxPageState']['css'] as $key => $value) { - $extra_post .= '&' . urlencode("ajax_page_state[css][$key]") . '=1'; - } - foreach ($drupal_settings['ajaxPageState']['js'] as $key => $value) { - $extra_post .= '&' . urlencode("ajax_page_state[js][$key]") . '=1'; - } - } - - // Unless a particular path is specified, use the one specified by the - // Ajax settings, or else 'system/ajax'. - if (!isset($ajax_path)) { - $ajax_path = isset($ajax_settings['url']) ? $ajax_settings['url'] : 'system/ajax'; - } - - // Submit the POST request. - $return = drupal_json_decode($this->drupalPost(NULL, $edit, array('path' => $ajax_path, 'triggering_element' => $triggering_element), $options, $headers, $form_html_id, $extra_post)); - - // Change the page content by applying the returned commands. - if (!empty($ajax_settings) && !empty($return)) { - // ajax.js applies some defaults to the settings object, so do the same - // for what's used by this function. - $ajax_settings += array( - 'method' => 'replaceWith', - ); - // DOM can load HTML soup. But, HTML soup can throw warnings, suppress - // them. - $dom = new DOMDocument(); - @$dom->loadHTML($content); - foreach ($return as $command) { - switch ($command['command']) { - case 'settings': - $drupal_settings = drupal_array_merge_deep($drupal_settings, $command['settings']); - break; - - case 'insert': - // @todo ajax.js can process commands that include a 'selector', but - // these are hard to emulate with DOMDocument. For now, we only - // implement 'insert' commands that use $ajax_settings['wrapper']. - if (!isset($command['selector'])) { - // $dom->getElementById() doesn't work when drupalPostAJAX() is - // invoked multiple times for a page, so use XPath instead. This - // also sets us up for adding support for $command['selector'] in - // the future, once we figure out how to transform a jQuery - // selector to XPath. - $xpath = new DOMXPath($dom); - $wrapperNode = $xpath->query('//*[@id="' . $ajax_settings['wrapper'] . '"]')->item(0); - if ($wrapperNode) { - // ajax.js adds an enclosing DIV to work around a Safari bug. - $newDom = new DOMDocument(); - $newDom->loadHTML('<div>' . $command['data'] . '</div>'); - $newNode = $dom->importNode($newDom->documentElement->firstChild->firstChild, TRUE); - $method = isset($command['method']) ? $command['method'] : $ajax_settings['method']; - // The "method" is a jQuery DOM manipulation function. Emulate - // each one using PHP's DOMNode API. - switch ($method) { - case 'replaceWith': - $wrapperNode->parentNode->replaceChild($newNode, $wrapperNode); - break; - case 'append': - $wrapperNode->appendChild($newNode); - break; - case 'prepend': - // If no firstChild, insertBefore() falls back to - // appendChild(). - $wrapperNode->insertBefore($newNode, $wrapperNode->firstChild); - break; - case 'before': - $wrapperNode->parentNode->insertBefore($newNode, $wrapperNode); - break; - case 'after': - // If no nextSibling, insertBefore() falls back to - // appendChild(). - $wrapperNode->parentNode->insertBefore($newNode, $wrapperNode->nextSibling); - break; - case 'html': - foreach ($wrapperNode->childNodes as $childNode) { - $wrapperNode->removeChild($childNode); - } - $wrapperNode->appendChild($newNode); - break; - } - } - } - break; - - // @todo Add suitable implementations for these commands in order to - // have full test coverage of what ajax.js can do. - case 'remove': - break; - case 'changed': - break; - case 'css': - break; - case 'data': - break; - case 'restripe': - break; - } - } - $content = $dom->saveHTML(); - } - $this->drupalSetContent($content); - $this->drupalSetSettings($drupal_settings); - return $return; - } - - /** - * Runs cron in the Drupal installed by Simpletest. - */ - protected function cronRun() { - $this->drupalGet($GLOBALS['base_url'] . '/cron.php', array('external' => TRUE, 'query' => array('cron_key' => variable_get('cron_key', 'drupal')))); - } - - /** - * 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. - * - * @return - * Either the new page content or FALSE. - */ - protected function checkForMetaRefresh() { - 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: - // "[delay]: URL=[page_to_redirect_to]". - if (preg_match('/\d+;\s*URL=(?P<url>.*)/i', $refresh[0]['content'], $match)) { - return $this->drupalGet($this->getAbsoluteUrl(decode_entities($match['url']))); - } - } - } - return FALSE; - } - - /** - * Retrieves only the headers for a Drupal path or an absolute path. - * - * @param $path - * Drupal path or URL to load into internal browser - * @param $options - * Options to be forwarded to url(). - * @param $headers - * An array containing additional HTTP request headers, each formatted as - * "name: value". - * @return - * The retrieved headers, also available as $this->drupalGetContent() - */ - protected function drupalHead($path, array $options = array(), array $headers = array()) { - $options['absolute'] = TRUE; - $out = $this->curlExec(array(CURLOPT_NOBODY => TRUE, CURLOPT_URL => url($path, $options), CURLOPT_HTTPHEADER => $headers)); - $this->refreshVariables(); // Ensure that any changes to variables in the other thread are picked up. - return $out; - } - - /** - * Handle form input related to drupalPost(). Ensure that the specified fields - * exist and attempt to create POST data in the correct manner for the particular - * field type. - * - * @param $post - * Reference to array of post values. - * @param $edit - * Reference to array of edit values to be checked against the form. - * @param $submit - * Form submit button value. - * @param $form - * Array of form elements. - * @return - * Submit value matches a valid submit input in the form. - */ - protected function handleForm(&$post, &$edit, &$upload, $submit, $form) { - // Retrieve the form elements. - $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'] : ''; - $done = FALSE; - if (isset($edit[$name])) { - switch ($type) { - case 'text': - case 'textarea': - case 'hidden': - case 'password': - $post[$name] = $edit[$name]; - unset($edit[$name]); - break; - case 'radio': - if ($edit[$name] == $value) { - $post[$name] = $edit[$name]; - unset($edit[$name]); - } - break; - case 'checkbox': - // To prevent checkbox from being checked.pass in a FALSE, - // otherwise the checkbox will be set to its value regardless - // of $edit. - if ($edit[$name] === FALSE) { - unset($edit[$name]); - continue 2; - } - else { - unset($edit[$name]); - $post[$name] = $value; - } - break; - case 'select': - $new_value = $edit[$name]; - $options = $this->getAllOptions($element); - 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]); - } - } - } - 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; - case 'file': - $upload[$name] = $edit[$name]; - unset($edit[$name]); - break; - } - } - if (!isset($post[$name]) && !$done) { - switch ($type) { - case 'textarea': - $post[$name] = (string) $element; - break; - case 'select': - $single = empty($element['multiple']); - $first = TRUE; - $index = 0; - $key = preg_replace('/\[\]$/', '', $name); - $options = $this->getAllOptions($element); - foreach ($options as $option) { - // For single select, we load the first option, if there is a - // selected option that will overwrite it later. - if ($option['selected'] || ($first && $single)) { - $first = FALSE; - if ($single) { - $post[$name] = (string) $option['value']; - } - else { - $post[$key . '[' . $index++ . ']'] = (string) $option['value']; - } - } - } - break; - case 'file': - break; - case 'submit': - case 'image': - if (isset($submit) && $submit == $value) { - $post[$name] = $value; - $submit_matches = TRUE; - } - break; - case 'radio': - case 'checkbox': - if (!isset($element['checked'])) { - break; - } - // Deliberate no break. - default: - $post[$name] = $value; - } - } - } - return $submit_matches; - } - - /** - * 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. - * - * @param $xpath - * The xpath string to use in the search. - * @return - * The return value of the xpath search. For details on the xpath string - * format and return values see the SimpleXML documentation, - * http://us.php.net/manual/function.simplexml-element-xpath.php. - */ - protected function xpath($xpath, array $arguments = array()) { - if ($this->parse()) { - $xpath = $this->buildXPathQuery($xpath, $arguments); - $result = $this->elements->xpath($xpath); - // Some combinations of PHP / libxml versions return an empty array - // instead of the documented FALSE. Forcefully convert any falsish values - // to an empty array to allow foreach(...) constructions. - return $result ? $result : array(); - } - else { - return FALSE; - } - } - - /** - * Get all option elements, including nested options, in a select. - * - * @param $element - * The element for which to get the options. - * @return - * Option elements in select. - */ - protected function getAllOptions(SimpleXMLElement $element) { - $options = array(); - // Add all options items. - foreach ($element->option as $option) { - $options[] = $option; - } - - // Search option group children. - if (isset($element->optgroup)) { - foreach ($element->optgroup as $group) { - $options = array_merge($options, $this->getAllOptions($group)); - } - } - return $options; - } - - /** - * Pass if a link with the specified label is found, and optional with the - * specified index. - * - * @param $label - * Text between the anchor tags. - * @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 assertLink($label, $index = 0, $message = '', $group = 'Other') { - $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); - } - - /** - * Pass if a link with the specified label is not found. - * - * @param $label - * Text between the anchor tags. - * @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 assertNoLink($label, $message = '', $group = 'Other') { - $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); - } - - /** - * Follows a link by name. - * - * Will click the first link found with this link text by default, or a - * later one if an index is given. Match is case insensitive with - * normalized space. The label is translated label. There is an assert - * for successful click. - * - * @param $label - * Text between the anchor tags. - * @param $index - * Link position counting from zero. - * @return - * Page on success, or FALSE on failure. - */ - protected function clickLink($label, $index = 0) { - $url_before = $this->getUrl(); - $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')); - - if (isset($url_target)) { - return $this->drupalGet($url_target); - } - return FALSE; - } - - /** - * Takes a path and returns an absolute path. - * - * @param $path - * A path from the internal browser content. - * @return - * The $path with $base_url prepended, if necessary. - */ - protected function getAbsoluteUrl($path) { - global $base_url, $base_path; - - $parts = parse_url($path); - if (empty($parts['host'])) { - // 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; - } - // Finally, prepend the $base_url. - $path = $base_url . $path; - } - return $path; - } - - /** - * Get the current url from the cURL handler. - * - * @return - * The current url. - */ - protected function getUrl() { - return $this->url; - } - - /** - * Gets the HTTP response headers of the requested page. Normally we are only - * interested in the headers returned by the last request. However, if a page - * is redirected or HTTP authentication is in use, multiple requests will be - * required to retrieve the page. Headers from all requests may be requested - * by passing TRUE to this function. - * - * @param $all_requests - * Boolean value specifying whether to return headers from all requests - * instead of just the last request. Defaults to FALSE. - * @return - * A name/value array if headers from only the last request are requested. - * If headers from all requests are requested, an array of name/value - * arrays, one for each request. - * - * The pseudonym ":status" is used for the HTTP status line. - * - * Values for duplicate headers are stored as a single comma-separated list. - */ - protected function drupalGetHeaders($all_requests = FALSE) { - $request = 0; - $headers = array($request => array()); - foreach ($this->headers as $header) { - $header = trim($header); - if ($header === '') { - $request++; - } - else { - if (strpos($header, 'HTTP/') === 0) { - $name = ':status'; - $value = $header; - } - else { - list($name, $value) = explode(':', $header, 2); - $name = strtolower($name); - } - if (isset($headers[$request][$name])) { - $headers[$request][$name] .= ',' . trim($value); - } - else { - $headers[$request][$name] = trim($value); - } - } - } - if (!$all_requests) { - $headers = array_pop($headers); - } - return $headers; - } - - /** - * Gets the value of an HTTP response header. If multiple requests were - * required to retrieve the page, only the headers from the last request will - * be checked by default. However, if TRUE is passed as the second argument, - * all requests will be processed from last to first until the header is - * found. - * - * @param $name - * The name of the header to retrieve. Names are case-insensitive (see RFC - * 2616 section 4.2). - * @param $all_requests - * Boolean value specifying whether to check all requests if the header is - * not found in the last request. Defaults to FALSE. - * @return - * The HTTP header value or FALSE if not found. - */ - protected function drupalGetHeader($name, $all_requests = FALSE) { - $name = strtolower($name); - $header = FALSE; - if ($all_requests) { - foreach (array_reverse($this->drupalGetHeaders(TRUE)) as $headers) { - if (isset($headers[$name])) { - $header = $headers[$name]; - break; - } - } - } - else { - $headers = $this->drupalGetHeaders(); - if (isset($headers[$name])) { - $header = $headers[$name]; - } - } - return $header; - } - - /** - * Gets the current raw HTML of requested page. - */ - protected function drupalGetContent() { - return $this->content; - } - - /** - * Gets the value of the Drupal.settings JavaScript variable for the currently loaded page. - */ - protected function drupalGetSettings() { - return $this->drupalSettings; - } - - /** - * Gets an array containing all e-mails sent during this test case. - * - * @param $filter - * An array containing key/value pairs used to filter the e-mails that are returned. - * @return - * An array containing e-mail messages captured during the current test. - */ - protected function drupalGetMails($filter = array()) { - $captured_emails = variable_get('drupal_test_email_collector', array()); - $filtered_emails = array(); - - foreach ($captured_emails as $message) { - foreach ($filter as $key => $value) { - if (!isset($message[$key]) || $message[$key] != $value) { - continue 2; - } - } - $filtered_emails[] = $message; - } - - return $filtered_emails; - } - - /** - * Sets the raw HTML content. This can be useful when a page has been fetched - * outside of the internal browser and assertions need to be made on the - * returned page. - * - * A good example would be when testing drupal_http_request(). After fetching - * the page the content can be set and page elements can be checked to ensure - * that the function worked properly. - */ - protected function drupalSetContent($content, $url = 'internal:') { - $this->content = $content; - $this->url = $url; - $this->plainTextContent = FALSE; - $this->elements = FALSE; - $this->drupalSettings = array(); - if (preg_match('/jQuery\.extend\(Drupal\.settings, (.*?)\);/', $content, $matches)) { - $this->drupalSettings = drupal_json_decode($matches[1]); - } - } - - /** - * Sets the value of the Drupal.settings JavaScript variable for the currently loaded page. - */ - protected function drupalSetSettings($settings) { - $this->drupalSettings = $settings; - } - - /** - * Pass if the internal browser's URL matches the given path. - * - * @param $path - * The expected system path. - * @param $options - * (optional) Any additional options to pass for $path to url(). - * @param $message - * Message to display. - * @param $group - * The group this message belongs to, defaults to 'Other'. - * - * @return - * TRUE on pass, FALSE on fail. - */ - protected function assertUrl($path, array $options = array(), $message = '', $group = 'Other') { - if (!$message) { - $message = t('Current URL is @url.', array( - '@url' => var_export(url($path, $options), TRUE), - )); - } - $options['absolute'] = TRUE; - return $this->assertEqual($this->getUrl(), url($path, $options), $message, $group); - } - - /** - * Pass if the raw text IS found on the loaded page, fail otherwise. Raw text - * refers to the raw HTML that the page generated. - * - * @param $raw - * Raw (HTML) string to look for. - * @param $message - * Message to display. - * @param $group - * The group this message belongs to, defaults to 'Other'. - * @return - * TRUE on pass, FALSE on fail. - */ - protected function assertRaw($raw, $message = '', $group = 'Other') { - if (!$message) { - $message = t('Raw "@raw" found', array('@raw' => $raw)); - } - return $this->assert(strpos($this->drupalGetContent(), $raw) !== FALSE, $message, $group); - } - - /** - * Pass if the raw text is NOT found on the loaded page, fail otherwise. Raw text - * refers to the raw HTML that the page generated. - * - * @param $raw - * Raw (HTML) string to look for. - * @param $message - * Message to display. - * @param $group - * The group this message belongs to, defaults to 'Other'. - * @return - * TRUE on pass, FALSE on fail. - */ - protected function assertNoRaw($raw, $message = '', $group = 'Other') { - if (!$message) { - $message = t('Raw "@raw" not found', array('@raw' => $raw)); - } - return $this->assert(strpos($this->drupalGetContent(), $raw) === FALSE, $message, $group); - } - - /** - * Pass if the text IS found on the text version of the page. The text version - * is the equivalent of what a user would see when viewing through a web browser. - * In other words the HTML has been filtered out of the contents. - * - * @param $text - * Plain text to look for. - * @param $message - * Message to display. - * @param $group - * The group this message belongs to, defaults to 'Other'. - * @return - * TRUE on pass, FALSE on fail. - */ - protected function assertText($text, $message = '', $group = 'Other') { - return $this->assertTextHelper($text, $message, $group, FALSE); - } - - /** - * Pass if the text is NOT found on the text version of the page. The text version - * is the equivalent of what a user would see when viewing through a web browser. - * In other words the HTML has been filtered out of the contents. - * - * @param $text - * Plain text to look for. - * @param $message - * Message to display. - * @param $group - * The group this message belongs to, defaults to 'Other'. - * @return - * TRUE on pass, FALSE on fail. - */ - protected function assertNoText($text, $message = '', $group = 'Other') { - return $this->assertTextHelper($text, $message, $group, TRUE); - } - - /** - * Helper for assertText and assertNoText. - * - * It is not recommended to call this function directly. - * - * @param $text - * Plain text to look for. - * @param $message - * Message to display. - * @param $group - * The group this message belongs to. - * @param $not_exists - * TRUE if this text should not exist, FALSE if it should. - * @return - * TRUE on pass, FALSE on fail. - */ - protected function assertTextHelper($text, $message = '', $group, $not_exists) { - if ($this->plainTextContent === FALSE) { - $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)); - } - return $this->assert($not_exists == (strpos($this->plainTextContent, $text) === FALSE), $message, $group); - } - - /** - * Pass if the text is found ONLY ONCE on the text version of the page. - * - * The text version is the equivalent of what a user would see when viewing - * through a web browser. In other words the HTML has been filtered out of - * the contents. - * - * @param $text - * Plain text to look for. - * @param $message - * Message to display. - * @param $group - * The group this message belongs to, defaults to 'Other'. - * @return - * TRUE on pass, FALSE on fail. - */ - protected function assertUniqueText($text, $message = '', $group = 'Other') { - return $this->assertUniqueTextHelper($text, $message, $group, TRUE); - } - - /** - * Pass if the text is found MORE THAN ONCE on the text version of the page. - * - * The text version is the equivalent of what a user would see when viewing - * through a web browser. In other words the HTML has been filtered out of - * the contents. - * - * @param $text - * Plain text to look for. - * @param $message - * Message to display. - * @param $group - * The group this message belongs to, defaults to 'Other'. - * @return - * TRUE on pass, FALSE on fail. - */ - protected function assertNoUniqueText($text, $message = '', $group = 'Other') { - return $this->assertUniqueTextHelper($text, $message, $group, FALSE); - } - - /** - * Helper for assertUniqueText and assertNoUniqueText. - * - * It is not recommended to call this function directly. - * - * @param $text - * Plain text to look for. - * @param $message - * Message to display. - * @param $group - * The group this message belongs to. - * @param $be_unique - * TRUE if this text should be found only once, FALSE if it should be found more than once. - * @return - * TRUE on pass, FALSE on fail. - */ - protected function assertUniqueTextHelper($text, $message = '', $group, $be_unique) { - if ($this->plainTextContent === FALSE) { - $this->plainTextContent = filter_xss($this->drupalGetContent(), array()); - } - if (!$message) { - $message = '"' . $text . '"' . ($be_unique ? ' found only once' : ' found more than once'); - } - $first_occurance = strpos($this->plainTextContent, $text); - if ($first_occurance === FALSE) { - return $this->assert(FALSE, $message, $group); - } - $offset = $first_occurance + strlen($text); - $second_occurance = strpos($this->plainTextContent, $text, $offset); - return $this->assert($be_unique == ($second_occurance === FALSE), $message, $group); - } - - /** - * Will trigger a pass if the Perl regex pattern is found in the raw content. - * - * @param $pattern - * Perl regex to look for including the regex delimiters. - * @param $message - * Message to display. - * @param $group - * The group this message belongs to. - * @return - * TRUE on pass, FALSE on fail. - */ - protected function assertPattern($pattern, $message = '', $group = 'Other') { - if (!$message) { - $message = t('Pattern "@pattern" found', array('@pattern' => $pattern)); - } - return $this->assert((bool) preg_match($pattern, $this->drupalGetContent()), $message, $group); - } - - /** - * Will trigger a pass if the perl regex pattern is not present in raw content. - * - * @param $pattern - * Perl regex to look for including the regex delimiters. - * @param $message - * Message to display. - * @param $group - * The group this message belongs to. - * @return - * TRUE on pass, FALSE on fail. - */ - protected function assertNoPattern($pattern, $message = '', $group = 'Other') { - if (!$message) { - $message = t('Pattern "@pattern" not found', array('@pattern' => $pattern)); - } - return $this->assert(!preg_match($pattern, $this->drupalGetContent()), $message, $group); - } - - /** - * Pass if the page title is the given string. - * - * @param $title - * The string the title should be. - * @param $message - * Message to display. - * @param $group - * The group this message belongs to. - * @return - * TRUE on pass, FALSE on fail. - */ - protected function assertTitle($title, $message = '', $group = 'Other') { - $actual = (string) current($this->xpath('//title')); - if (!$message) { - $message = t('Page title @actual is equal to @expected.', array( - '@actual' => var_export($actual, TRUE), - '@expected' => var_export($title, TRUE), - )); - } - return $this->assertEqual($actual, $title, $message, $group); - } - - /** - * Pass if the page title is not the given string. - * - * @param $title - * The string the title should not be. - * @param $message - * Message to display. - * @param $group - * The group this message belongs to. - * @return - * TRUE on pass, FALSE on fail. - */ - protected function assertNoTitle($title, $message = '', $group = 'Other') { - $actual = (string) current($this->xpath('//title')); - if (!$message) { - $message = t('Page title @actual is not equal to @unexpected.', array( - '@actual' => var_export($actual, TRUE), - '@unexpected' => var_export($title, TRUE), - )); - } - return $this->assertNotEqual($actual, $title, $message, $group); - } - - /** - * Asserts that a field exists in the current page by the given XPath. - * - * @param $xpath - * XPath used to find the field. - * @param $value - * (optional) Value of the field to assert. - * @param $message - * (optional) Message to display. - * @param $group - * (optional) The group this message belongs to. - * - * @return - * TRUE on pass, FALSE on fail. - */ - protected function assertFieldByXPath($xpath, $value = NULL, $message = '', $group = 'Other') { - $fields = $this->xpath($xpath); - - // If value specified then check array for match. - $found = TRUE; - if (isset($value)) { - $found = FALSE; - if ($fields) { - foreach ($fields as $field) { - if (isset($field['value']) && $field['value'] == $value) { - // Input element with correct value. - $found = TRUE; - } - elseif (isset($field->option)) { - // Select element found. - if ($this->getSelectedItem($field) == $value) { - $found = TRUE; - } - else { - // No item selected so use first item. - $items = $this->getAllOptions($field); - if (!empty($items) && $items[0]['value'] == $value) { - $found = TRUE; - } - } - } - elseif ((string) $field == $value) { - // Text area with correct text. - $found = TRUE; - } - } - } - } - return $this->assertTrue($fields && $found, $message, $group); - } - - /** - * Get the selected value from a select field. - * - * @param $element - * SimpleXMLElement select element. - * @return - * The selected value or FALSE. - */ - protected function getSelectedItem(SimpleXMLElement $element) { - foreach ($element->children() as $item) { - if (isset($item['selected'])) { - return $item['value']; - } - elseif ($item->getName() == 'optgroup') { - if ($value = $this->getSelectedItem($item)) { - return $value; - } - } - } - return FALSE; - } - - /** - * Asserts that a field does not exist in the current page by the given XPath. - * - * @param $xpath - * XPath used to find the field. - * @param $value - * (optional) Value of the field to assert. - * @param $message - * (optional) Message to display. - * @param $group - * (optional) The group this message belongs to. - * - * @return - * TRUE on pass, FALSE on fail. - */ - protected function assertNoFieldByXPath($xpath, $value = NULL, $message = '', $group = 'Other') { - $fields = $this->xpath($xpath); - - // If value specified then check array for match. - $found = TRUE; - if (isset($value)) { - $found = FALSE; - if ($fields) { - foreach ($fields as $field) { - if ($field['value'] == $value) { - $found = TRUE; - } - } - } - } - return $this->assertFalse($fields && $found, $message, $group); - } - - /** - * Asserts that a field exists in the current page with the given name and value. - * - * @param $name - * Name of field to assert. - * @param $value - * Value of the field to assert. - * @param $message - * Message to display. - * @param $group - * The group this message belongs to. - * @return - * TRUE on pass, FALSE on fail. - */ - protected function assertFieldByName($name, $value = '', $message = '') { - return $this->assertFieldByXPath($this->constructFieldXpath('name', $name), $value, $message ? $message : t('Found field by name @name', array('@name' => $name)), t('Browser')); - } - - /** - * Asserts that a field does not exist with the given name and value. - * - * @param $name - * Name of field to assert. - * @param $value - * Value of the field to assert. - * @param $message - * Message to display. - * @param $group - * The group this message belongs to. - * @return - * TRUE on pass, FALSE on fail. - */ - protected function assertNoFieldByName($name, $value = '', $message = '') { - return $this->assertNoFieldByXPath($this->constructFieldXpath('name', $name), $value, $message ? $message : t('Did not find field by name @name', array('@name' => $name)), t('Browser')); - } - - /** - * Asserts that a field exists in the current page with the given id and value. - * - * @param $id - * Id of field to assert. - * @param $value - * Value of the field to assert. - * @param $message - * Message to display. - * @param $group - * The group this message belongs to. - * @return - * TRUE on pass, FALSE on fail. - */ - protected function assertFieldById($id, $value = '', $message = '') { - return $this->assertFieldByXPath($this->constructFieldXpath('id', $id), $value, $message ? $message : t('Found field by id @id', array('@id' => $id)), t('Browser')); - } - - /** - * Asserts that a field does not exist with the given id and value. - * - * @param $id - * Id of field to assert. - * @param $value - * Value of the field to assert. - * @param $message - * Message to display. - * @param $group - * The group this message belongs to. - * @return - * TRUE on pass, FALSE on fail. - */ - protected function assertNoFieldById($id, $value = '', $message = '') { - return $this->assertNoFieldByXPath($this->constructFieldXpath('id', $id), $value, $message ? $message : t('Did not find field by id @id', array('@id' => $id)), t('Browser')); - } - - /** - * Asserts that a checkbox field in the current page is checked. - * - * @param $id - * Id of field to assert. - * @param $message - * Message to display. - * @return - * TRUE on pass, FALSE on fail. - */ - protected function assertFieldChecked($id, $message = '') { - $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')); - } - - /** - * Asserts that a checkbox field in the current page is not checked. - * - * @param $id - * Id of field to assert. - * @param $message - * Message to display. - * @return - * TRUE on pass, FALSE on fail. - */ - protected function assertNoFieldChecked($id, $message = '') { - $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')); - } - - /** - * Asserts that a select option in the current page is 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. - * - * @todo $id is unusable. Replace with $name. - */ - 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')); - } - - /** - * Asserts 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')); - } - - /** - * Asserts that a field exists with the given name or id. - * - * @param $field - * Name or id of field to assert. - * @param $message - * Message to display. - * @param $group - * The group this message belongs to. - * @return - * TRUE on pass, FALSE on fail. - */ - protected function assertField($field, $message = '', $group = 'Other') { - return $this->assertFieldByXPath($this->constructFieldXpath('name', $field) . '|' . $this->constructFieldXpath('id', $field), NULL, $message, $group); - } - - /** - * Asserts that a field does not exist with the given name or id. - * - * @param $field - * Name or id of field to assert. - * @param $message - * Message to display. - * @param $group - * The group this message belongs to. - * @return - * TRUE on pass, FALSE on fail. - */ - protected function assertNoField($field, $message = '', $group = 'Other') { - return $this->assertNoFieldByXPath($this->constructFieldXpath('name', $field) . '|' . $this->constructFieldXpath('id', $field), NULL, $message, $group); - } - - /** - * Asserts 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 - * Field attributes. - * @param $value - * Value of field. - * @return - * XPath for specified values. - */ - protected function constructFieldXpath($attribute, $value) { - $xpath = '//textarea[@' . $attribute . '=:value]|//input[@' . $attribute . '=:value]|//select[@' . $attribute . '=:value]'; - return $this->buildXPathQuery($xpath, array(':value' => $value)); - } - - /** - * Asserts the page responds with the specified response code. - * - * @param $code - * Response code. For example 200 is a successful page request. For a list - * of all codes see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html. - * @param $message - * Message to display. - * @return - * Assertion result. - */ - protected function assertResponse($code, $message = '') { - $curl_code = curl_getinfo($this->curlHandle, CURLINFO_HTTP_CODE); - $match = is_array($code) ? in_array($curl_code, $code) : $curl_code == $code; - return $this->assertTrue($match, $message ? $message : t('HTTP response expected !code, actual !curl_code', array('!code' => $code, '!curl_code' => $curl_code)), t('Browser')); - } - - /** - * Asserts the page did not return the specified response code. - * - * @param $code - * Response code. For example 200 is a successful page request. For a list - * of all codes see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html. - * @param $message - * Message to display. - * - * @return - * Assertion result. - */ - protected function assertNoResponse($code, $message = '') { - $curl_code = curl_getinfo($this->curlHandle, CURLINFO_HTTP_CODE); - $match = is_array($code) ? in_array($curl_code, $code) : $curl_code == $code; - return $this->assertFalse($match, $message ? $message : t('HTTP response not expected !code, actual !curl_code', array('!code' => $code, '!curl_code' => $curl_code)), t('Browser')); - } - - /** - * 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, ... - * @param $value - * Value of the field to assert. - * @param $message - * Message to display. - * - * @return - * TRUE on pass, FALSE on fail. - */ - protected function assertMail($name, $value = '', $message = '') { - $captured_emails = variable_get('drupal_test_email_collector', array()); - $email = end($captured_emails); - return $this->assertTrue($email && isset($email[$name]) && $email[$name] == $value, $message, t('E-mail')); - } - - /** - * Asserts that the most recently sent e-mail message has the string in it. - * - * @param $field_name - * Name of field or message property to assert: subject, body, id, ... - * @param $string - * String to search for. - * @param $email_depth - * Number of emails to search for string, starting with most recent. - * - * @return - * TRUE on pass, FALSE on fail. - */ - protected function assertMailString($field_name, $string, $email_depth) { - $mails = $this->drupalGetMails(); - $string_found = FALSE; - for ($i = sizeof($mails) -1; $i >= sizeof($mails) - $email_depth && $i >= 0; $i--) { - $mail = $mails[$i]; - // Normalize whitespace, as we don't know what the mail system might have - // done. Any run of whitespace becomes a single space. - $normalized_mail = preg_replace('/\s+/', ' ', $mail[$field_name]); - $normalized_string = preg_replace('/\s+/', ' ', $string); - $string_found = (FALSE !== strpos($normalized_mail, $normalized_string)); - if ($string_found) { - break; - } - } - return $this->assertTrue($string_found, t('Expected text found in @field of email message: "@expected".', array('@field' => $field_name, '@expected' => $string))); - } - - /** - * Asserts that the most recently sent e-mail message has the pattern in it. - * - * @param $field_name - * Name of field or message property to assert: subject, body, id, ... - * @param $regex - * Pattern to search for. - * - * @return - * TRUE on pass, FALSE on fail. - */ - protected function assertMailPattern($field_name, $regex, $message) { - $mails = $this->drupalGetMails(); - $mail = end($mails); - $regex_found = preg_match("/$regex/", $mail[$field_name]); - return $this->assertTrue($regex_found, t('Expected text found in @field of email message: "@expected".', array('@field' => $field_name, '@expected' => $regex))); - } - - /** - * Outputs to verbose the most recent $count emails sent. - * - * @param $count - * Optional number of emails to output. - */ - protected function verboseEmail($count = 1) { - $mails = $this->drupalGetMails(); - for ($i = sizeof($mails) -1; $i >= sizeof($mails) - $count && $i >= 0; $i--) { - $mail = $mails[$i]; - $this->verbose(t('Email:') . '<pre>' . print_r($mail, TRUE) . '</pre>'); - } - } -} - -/** - * 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 - * located in the simpletest directory in the original file system. - * - * @param $message - * The verbose message to be stored. - * @param $original_file_directory - * 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 = NULL; - - // Will pass first time during setup phase, and when verbose is TRUE. - if (!isset($original_file_directory) && !$verbose) { - return FALSE; - } - - if ($message && $file_directory) { - $message = '<hr />ID #' . $id . ' (<a href="' . $class . '-' . ($id - 1) . '.html">Previous</a> | <a href="' . $class . '-' . ($id + 1) . '.html">Next</a>)<hr />' . $message; - file_put_contents($file_directory . "/simpletest/verbose/$class-$id.html", $message, FILE_APPEND); - return $id++; - } - - if ($original_file_directory) { - $file_directory = $original_file_directory; - $class = $test_class; - $verbose = variable_get('simpletest_verbose', TRUE); - $directory = $file_directory . '/simpletest/verbose'; - $writable = file_prepare_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; -} diff --git a/modules/simpletest/files/README.txt b/modules/simpletest/files/README.txt deleted file mode 100644 index c8f39ad3377..00000000000 --- a/modules/simpletest/files/README.txt +++ /dev/null @@ -1,4 +0,0 @@ - -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. diff --git a/modules/simpletest/files/css_test_files/comment_hacks.css b/modules/simpletest/files/css_test_files/comment_hacks.css deleted file mode 100644 index c47e8429ac2..00000000000 --- a/modules/simpletest/files/css_test_files/comment_hacks.css +++ /dev/null @@ -1,80 +0,0 @@ -/* -* A sample css file, designed to test the effectiveness and stability -* of function <code>drupal_load_stylesheet_content()</code>. -* -*/ -/* -A large comment block to test for segfaults and speed. This is 60K a's. Extreme but useful to demonstrate flaws in comment striping regexp. aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa*/ -.test1 { display:block;} - -/* A multiline IE-mac hack (v.2) taken fron Zen theme*/ -/* Hides from IE-mac \*/ -html .clear-block { - height: 1%; -} -.clear-block { - display: block; - font:italic bold 12px/30px Georgia, serif; -} - -/* End hide from IE-mac */ -.test2 { display:block; } - -/* v1 of the commented backslash hack. This \ character between rules appears to have the effect -that macIE5 ignores the following rule. Odd, but extremely useful. */ -.bkslshv1 { background-color: #C00; } -.test3 { display:block; } - -/**************** A multiline, multistar comment *************** -****************************************************************/ -.test4 { display:block;} - -/**************************************/ -.comment-in-double-quotes:before { - content: "/* "; -} -.this_rule_must_stay { - color: #F00; - background-color: #FFF; -} -.comment-in-double-quotes:after { - content: " */"; -} -/**************************************/ -.comment-in-single-quotes:before { - content: '/*'; -} -.this_rule_must_stay { - color: #F00; - background-color: #FFF; -} -.comment-in-single-quotes:after { - content: '*/'; -} -/**************************************/ -.comment-in-mixed-quotes:before { - content: '"/*"'; -} -.this_rule_must_stay { - color: #F00; - background-color: #FFF; -} -.comment-in-mixed-quotes:after { - content: "'*/'"; -} -/**************************************/ -.comment-in-quotes-with-escaped:before { - content: '/* \" \' */'; -} -.this_rule_must_stay { - color: #F00; - background-color: #FFF; -} -.comment-in-quotes-with-escaped:after { - content: "*/ \" \ '"; -} -/************************************/ -/* -"This has to go" -'This has to go' -*/ diff --git a/modules/simpletest/files/css_test_files/comment_hacks.css.optimized.css b/modules/simpletest/files/css_test_files/comment_hacks.css.optimized.css deleted file mode 100644 index 1feb8f1bd7d..00000000000 --- a/modules/simpletest/files/css_test_files/comment_hacks.css.optimized.css +++ /dev/null @@ -1 +0,0 @@ -.test1{display:block;}html .clear-block{height:1%;}.clear-block{display:block;font:italic bold 12px/30px Georgia,serif;}.test2{display:block;}.bkslshv1{background-color:#C00;}.test3{display:block;}.test4{display:block;}.comment-in-double-quotes:before{content:"/* ";}.this_rule_must_stay{color:#F00;background-color:#FFF;}.comment-in-double-quotes:after{content:" */";}.comment-in-single-quotes:before{content:'/*';}.this_rule_must_stay{color:#F00;background-color:#FFF;}.comment-in-single-quotes:after{content:'*/';}.comment-in-mixed-quotes:before{content:'"/*"';}.this_rule_must_stay{color:#F00;background-color:#FFF;}.comment-in-mixed-quotes:after{content:"'*/'";}.comment-in-quotes-with-escaped:before{content:'/* \" \' */';}.this_rule_must_stay{color:#F00;background-color:#FFF;}.comment-in-quotes-with-escaped:after{content:"*/ \" \ '";} diff --git a/modules/simpletest/files/css_test_files/comment_hacks.css.unoptimized.css b/modules/simpletest/files/css_test_files/comment_hacks.css.unoptimized.css deleted file mode 100644 index c47e8429ac2..00000000000 --- a/modules/simpletest/files/css_test_files/comment_hacks.css.unoptimized.css +++ /dev/null @@ -1,80 +0,0 @@ -/* -* A sample css file, designed to test the effectiveness and stability -* of function <code>drupal_load_stylesheet_content()</code>. -* -*/ -/* -A large comment block to test for segfaults and speed. This is 60K a's. Extreme but useful to demonstrate flaws in comment striping regexp. aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa*/ -.test1 { display:block;} - -/* A multiline IE-mac hack (v.2) taken fron Zen theme*/ -/* Hides from IE-mac \*/ -html .clear-block { - height: 1%; -} -.clear-block { - display: block; - font:italic bold 12px/30px Georgia, serif; -} - -/* End hide from IE-mac */ -.test2 { display:block; } - -/* v1 of the commented backslash hack. This \ character between rules appears to have the effect -that macIE5 ignores the following rule. Odd, but extremely useful. */ -.bkslshv1 { background-color: #C00; } -.test3 { display:block; } - -/**************** A multiline, multistar comment *************** -****************************************************************/ -.test4 { display:block;} - -/**************************************/ -.comment-in-double-quotes:before { - content: "/* "; -} -.this_rule_must_stay { - color: #F00; - background-color: #FFF; -} -.comment-in-double-quotes:after { - content: " */"; -} -/**************************************/ -.comment-in-single-quotes:before { - content: '/*'; -} -.this_rule_must_stay { - color: #F00; - background-color: #FFF; -} -.comment-in-single-quotes:after { - content: '*/'; -} -/**************************************/ -.comment-in-mixed-quotes:before { - content: '"/*"'; -} -.this_rule_must_stay { - color: #F00; - background-color: #FFF; -} -.comment-in-mixed-quotes:after { - content: "'*/'"; -} -/**************************************/ -.comment-in-quotes-with-escaped:before { - content: '/* \" \' */'; -} -.this_rule_must_stay { - color: #F00; - background-color: #FFF; -} -.comment-in-quotes-with-escaped:after { - content: "*/ \" \ '"; -} -/************************************/ -/* -"This has to go" -'This has to go' -*/ diff --git a/modules/simpletest/files/css_test_files/css_input_with_import.css b/modules/simpletest/files/css_test_files/css_input_with_import.css deleted file mode 100644 index 87afcb35f87..00000000000 --- a/modules/simpletest/files/css_test_files/css_input_with_import.css +++ /dev/null @@ -1,30 +0,0 @@ - - -@import "import1.css"; -@import "import2.css"; - -body { - margin: 0; - padding: 0; - background: #edf5fa; - font: 76%/170% Verdana, sans-serif; - color: #494949; -} - -.this .is .a .test { - font: 1em/100% Verdana, sans-serif; - color: #494949; -} -.this -.is -.a -.test { -font: 1em/100% Verdana, sans-serif; -color: #494949; -} - -textarea, select { - font: 1em/160% Verdana, sans-serif; - color: #494949; -} - diff --git a/modules/simpletest/files/css_test_files/css_input_with_import.css.optimized.css b/modules/simpletest/files/css_test_files/css_input_with_import.css.optimized.css deleted file mode 100644 index 698d9aa6cab..00000000000 --- a/modules/simpletest/files/css_test_files/css_input_with_import.css.optimized.css +++ /dev/null @@ -1,6 +0,0 @@ -ul,select{font:1em/160% Verdana,sans-serif;color:#494949;}.ui-icon{background-image:url(images/icon.png);} -p,select{font:1em/160% Verdana,sans-serif;color:#494949;} -body{margin:0;padding:0;background:#edf5fa;font:76%/170% Verdana,sans-serif;color:#494949;}.this .is .a .test{font:1em/100% Verdana,sans-serif;color:#494949;}.this -.is -.a -.test{font:1em/100% Verdana,sans-serif;color:#494949;}textarea,select{font:1em/160% Verdana,sans-serif;color:#494949;} diff --git a/modules/simpletest/files/css_test_files/css_input_with_import.css.unoptimized.css b/modules/simpletest/files/css_test_files/css_input_with_import.css.unoptimized.css deleted file mode 100644 index 4c905f56207..00000000000 --- a/modules/simpletest/files/css_test_files/css_input_with_import.css.unoptimized.css +++ /dev/null @@ -1,30 +0,0 @@ - - - - - -body { - margin: 0; - padding: 0; - background: #edf5fa; - font: 76%/170% Verdana, sans-serif; - color: #494949; -} - -.this .is .a .test { - font: 1em/100% Verdana, sans-serif; - color: #494949; -} -.this -.is -.a -.test { -font: 1em/100% Verdana, sans-serif; -color: #494949; -} - -textarea, select { - font: 1em/160% Verdana, sans-serif; - color: #494949; -} - diff --git a/modules/simpletest/files/css_test_files/css_input_without_import.css b/modules/simpletest/files/css_test_files/css_input_without_import.css deleted file mode 100644 index 620360abc5d..00000000000 --- a/modules/simpletest/files/css_test_files/css_input_without_import.css +++ /dev/null @@ -1,69 +0,0 @@ - -/** - * @file Basic css that does not use import - */ - - -body { - margin: 0; - padding: 0; - background: #edf5fa; - font: 76%/170% Verdana, sans-serif; - color: #494949; -} - -.this .is .a .test { - font: 1em/100% Verdana, sans-serif; - color: #494949; -} - -/** - * CSS spec says that all whitespace is valid whitespace, so this selector - * should be just as good as the one above. - */ -.this -.is -.a -.test { -font: 1em/100% Verdana, sans-serif; -color: #494949; -} - -some :pseudo .thing { - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - border-radius: 3px; - filter: progid:DXImageTransform.Microsoft.Shadow(color=#000000, direction='180', strength='10'); - -ms-filter: "progid:DXImageTransform.Microsoft.Shadow(color=#000000, direction='180', strength='10')"; -} - -::-moz-selection { - background: #000; - color:#fff; - -} -::selection { - background: #000; - color: #fff; -} - -@media print { - * { - background: #000 !important; - color: #fff !important; - } - @page { - margin: 0.5cm; - } -} - -@media screen and (max-device-width: 480px) { - background: #000; - color: #fff; -} - -textarea, select { - font: 1em/160% Verdana, sans-serif; - color: #494949; -} - diff --git a/modules/simpletest/files/css_test_files/css_input_without_import.css.optimized.css b/modules/simpletest/files/css_test_files/css_input_without_import.css.optimized.css deleted file mode 100644 index c7bb9dcd17c..00000000000 --- a/modules/simpletest/files/css_test_files/css_input_without_import.css.optimized.css +++ /dev/null @@ -1,4 +0,0 @@ -body{margin:0;padding:0;background:#edf5fa;font:76%/170% Verdana,sans-serif;color:#494949;}.this .is .a .test{font:1em/100% Verdana,sans-serif;color:#494949;}.this -.is -.a -.test{font:1em/100% Verdana,sans-serif;color:#494949;}some :pseudo .thing{-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;filter:progid:DXImageTransform.Microsoft.Shadow(color=#000000,direction='180',strength='10');-ms-filter:"progid:DXImageTransform.Microsoft.Shadow(color=#000000,direction='180',strength='10')";}::-moz-selection{background:#000;color:#fff;}::selection{background:#000;color:#fff;}@media print{*{background:#000 !important;color:#fff !important;}@page{margin:0.5cm;}}@media screen and (max-device-width:480px){background:#000;color:#fff;}textarea,select{font:1em/160% Verdana,sans-serif;color:#494949;} diff --git a/modules/simpletest/files/css_test_files/css_input_without_import.css.unoptimized.css b/modules/simpletest/files/css_test_files/css_input_without_import.css.unoptimized.css deleted file mode 100644 index 620360abc5d..00000000000 --- a/modules/simpletest/files/css_test_files/css_input_without_import.css.unoptimized.css +++ /dev/null @@ -1,69 +0,0 @@ - -/** - * @file Basic css that does not use import - */ - - -body { - margin: 0; - padding: 0; - background: #edf5fa; - font: 76%/170% Verdana, sans-serif; - color: #494949; -} - -.this .is .a .test { - font: 1em/100% Verdana, sans-serif; - color: #494949; -} - -/** - * CSS spec says that all whitespace is valid whitespace, so this selector - * should be just as good as the one above. - */ -.this -.is -.a -.test { -font: 1em/100% Verdana, sans-serif; -color: #494949; -} - -some :pseudo .thing { - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - border-radius: 3px; - filter: progid:DXImageTransform.Microsoft.Shadow(color=#000000, direction='180', strength='10'); - -ms-filter: "progid:DXImageTransform.Microsoft.Shadow(color=#000000, direction='180', strength='10')"; -} - -::-moz-selection { - background: #000; - color:#fff; - -} -::selection { - background: #000; - color: #fff; -} - -@media print { - * { - background: #000 !important; - color: #fff !important; - } - @page { - margin: 0.5cm; - } -} - -@media screen and (max-device-width: 480px) { - background: #000; - color: #fff; -} - -textarea, select { - font: 1em/160% Verdana, sans-serif; - color: #494949; -} - diff --git a/modules/simpletest/files/css_test_files/import1.css b/modules/simpletest/files/css_test_files/import1.css deleted file mode 100644 index 3d5842ece79..00000000000 --- a/modules/simpletest/files/css_test_files/import1.css +++ /dev/null @@ -1,6 +0,0 @@ - -ul, select { - font: 1em/160% Verdana, sans-serif; - color: #494949; -} -.ui-icon{background-image: url(images/icon.png);}
\ No newline at end of file diff --git a/modules/simpletest/files/css_test_files/import2.css b/modules/simpletest/files/css_test_files/import2.css deleted file mode 100644 index 367eb571180..00000000000 --- a/modules/simpletest/files/css_test_files/import2.css +++ /dev/null @@ -1,5 +0,0 @@ - -p, select { - font: 1em/160% Verdana, sans-serif; - color: #494949; -} diff --git a/modules/simpletest/files/html-1.txt b/modules/simpletest/files/html-1.txt deleted file mode 100644 index 494470d1717..00000000000 --- a/modules/simpletest/files/html-1.txt +++ /dev/null @@ -1 +0,0 @@ -<h1>SimpleTest HTML</h1>
\ No newline at end of file diff --git a/modules/simpletest/files/html-2.html b/modules/simpletest/files/html-2.html deleted file mode 100644 index 494470d1717..00000000000 --- a/modules/simpletest/files/html-2.html +++ /dev/null @@ -1 +0,0 @@ -<h1>SimpleTest HTML</h1>
\ No newline at end of file diff --git a/modules/simpletest/files/image-1.png b/modules/simpletest/files/image-1.png Binary files differdeleted file mode 100644 index 09e64d6edbc..00000000000 --- a/modules/simpletest/files/image-1.png +++ /dev/null diff --git a/modules/simpletest/files/image-2.jpg b/modules/simpletest/files/image-2.jpg Binary files differdeleted file mode 100644 index ace07d078a0..00000000000 --- a/modules/simpletest/files/image-2.jpg +++ /dev/null diff --git a/modules/simpletest/files/image-test.gif b/modules/simpletest/files/image-test.gif Binary files differdeleted file mode 100644 index 432990b832d..00000000000 --- a/modules/simpletest/files/image-test.gif +++ /dev/null diff --git a/modules/simpletest/files/image-test.jpg b/modules/simpletest/files/image-test.jpg Binary files differdeleted file mode 100644 index de4eace04ec..00000000000 --- a/modules/simpletest/files/image-test.jpg +++ /dev/null diff --git a/modules/simpletest/files/image-test.png b/modules/simpletest/files/image-test.png Binary files differdeleted file mode 100644 index 39c041927e1..00000000000 --- a/modules/simpletest/files/image-test.png +++ /dev/null diff --git a/modules/simpletest/files/javascript-1.txt b/modules/simpletest/files/javascript-1.txt deleted file mode 100644 index efd44fd9360..00000000000 --- a/modules/simpletest/files/javascript-1.txt +++ /dev/null @@ -1,3 +0,0 @@ -<script> -alert('SimpleTest PHP was executed!'); -</script> diff --git a/modules/simpletest/files/javascript-2.script b/modules/simpletest/files/javascript-2.script deleted file mode 100644 index e0206ba8319..00000000000 --- a/modules/simpletest/files/javascript-2.script +++ /dev/null @@ -1,3 +0,0 @@ -<script> -alert('SimpleTest PHP was executed!'); -</script>
\ No newline at end of file diff --git a/modules/simpletest/files/php-1.txt b/modules/simpletest/files/php-1.txt deleted file mode 100644 index 52788b6feac..00000000000 --- a/modules/simpletest/files/php-1.txt +++ /dev/null @@ -1,3 +0,0 @@ -<?php -print 'SimpleTest PHP was executed!'; -?> diff --git a/modules/simpletest/files/php-2.php b/modules/simpletest/files/php-2.php deleted file mode 100644 index 615a8d78fb4..00000000000 --- a/modules/simpletest/files/php-2.php +++ /dev/null @@ -1,2 +0,0 @@ -<?php -print 'SimpleTest PHP was executed!'; diff --git a/modules/simpletest/files/sql-1.txt b/modules/simpletest/files/sql-1.txt deleted file mode 100644 index 22017e97232..00000000000 --- a/modules/simpletest/files/sql-1.txt +++ /dev/null @@ -1 +0,0 @@ -SELECT invalid_field FROM {invalid_table}
\ No newline at end of file diff --git a/modules/simpletest/files/sql-2.sql b/modules/simpletest/files/sql-2.sql deleted file mode 100644 index 22017e97232..00000000000 --- a/modules/simpletest/files/sql-2.sql +++ /dev/null @@ -1 +0,0 @@ -SELECT invalid_field FROM {invalid_table}
\ No newline at end of file diff --git a/modules/simpletest/simpletest.api.php b/modules/simpletest/simpletest.api.php deleted file mode 100644 index 04c080bfd54..00000000000 --- a/modules/simpletest/simpletest.api.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php - -/** - * @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". - */ diff --git a/modules/simpletest/simpletest.css b/modules/simpletest/simpletest.css deleted file mode 100644 index 0cf9aaa5edd..00000000000 --- a/modules/simpletest/simpletest.css +++ /dev/null @@ -1,89 +0,0 @@ - -/* Test Table */ -#simpletest-form-table th.select-all { - width: 1em; -} -th.simpletest_test { - width: 16em; -} - -.simpletest-image { - display: inline-block; - cursor: pointer; - width: 1em; -} -.simpletest-group-label label { - display: inline; - font-weight: bold; -} -.simpletest-test-label label { - margin-left: 1em; /* LTR */ -} -.simpletest-test-description .description { - margin: 0; -} -#simpletest-form-table tr td { - background-color: white; - color: #494949; -} -#simpletest-form-table tr.simpletest-group td { - background-color: #EDF5FA; - color: #494949; -} - -table#simpletest-form-table tr.simpletest-group label { - display: inline; -} - -div.message > div.item-list { - font-weight: normal; -} - -div.simpletest-pass { - color: #33a333; -} -.simpletest-fail { - color: #981010; -} - -tr.simpletest-pass.odd { - background-color: #b6ffb6; -} -tr.simpletest-pass.even { - background-color: #9bff9b; -} -tr.simpletest-fail.odd { - background-color: #ffc9c9; -} -tr.simpletest-fail.even { - background-color: #ffacac; -} -tr.simpletest-exception.odd { - background-color: #f4ea71; -} -tr.simpletest-exception.even { - background-color: #f5e742; -} -tr.simpletest-debug.odd { - background-color: #eee; -} -tr.simpletest-debug.even { - background-color: #fff; -} - -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; -} diff --git a/modules/simpletest/simpletest.info b/modules/simpletest/simpletest.info deleted file mode 100644 index fab7b5ebc11..00000000000 --- a/modules/simpletest/simpletest.info +++ /dev/null @@ -1,41 +0,0 @@ -name = Testing -description = Provides a framework for unit and functional testing. -package = Core -version = VERSION -core = 8.x -files[] = simpletest.test -files[] = drupal_web_test_case.php -configure = admin/config/development/testing/settings - -; Tests in tests directory. -files[] = tests/actions.test -files[] = tests/ajax.test -files[] = tests/batch.test -files[] = tests/bootstrap.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/password.test -files[] = tests/path.test -files[] = tests/registry.test -files[] = tests/schema.test -files[] = tests/session.test -files[] = tests/symfony.test -files[] = tests/tablesort.test -files[] = tests/theme.test -files[] = tests/unicode.test -files[] = tests/update.test -files[] = tests/xmlrpc.test -files[] = tests/upgrade/upgrade.test -files[] = tests/upgrade/upgrade_bare.test -files[] = tests/upgrade/upgrade_filled.test diff --git a/modules/simpletest/simpletest.install b/modules/simpletest/simpletest.install deleted file mode 100644 index ea847f4eaef..00000000000 --- a/modules/simpletest/simpletest.install +++ /dev/null @@ -1,182 +0,0 @@ -<?php - -/** - * @file - * Install, update and uninstall functions for the simpletest module. - */ - -/** - * Minimum value of PHP memory_limit for SimpleTest. - */ -define('SIMPLETEST_MINIMUM_PHP_MEMORY_LIMIT', '64M'); - -/** - * Implements hook_requirements(). - */ -function simpletest_requirements($phase) { - $requirements = array(); - $t = get_t(); - - $has_curl = function_exists('curl_init'); - $has_hash = function_exists('hash_hmac'); - $has_domdocument = method_exists('DOMDocument', 'loadHTML'); - $open_basedir = ini_get('open_basedir'); - - $requirements['curl'] = array( - 'title' => $t('cURL'), - 'value' => $has_curl ? $t('Enabled') : $t('Not found'), - ); - if (!$has_curl) { - $requirements['curl']['severity'] = REQUIREMENT_ERROR; - $requirements['curl']['description'] = $t('The testing framework could not be installed because the PHP <a href="@curl_url">cURL</a> library is not available.', array('@curl_url' => 'http://php.net/manual/en/curl.setup.php')); - } - $requirements['hash'] = array( - 'title' => $t('hash'), - 'value' => $has_hash ? $t('Enabled') : $t('Not found'), - ); - if (!$has_hash) { - $requirements['hash']['severity'] = REQUIREMENT_ERROR; - $requirements['hash']['description'] = $t('The testing framework could not be installed because the PHP <a href="@hash_url">hash</a> extension is disabled.', array('@hash_url' => 'http://php.net/manual/en/book.hash.php')); - } - - $requirements['php_domdocument'] = array( - 'title' => $t('PHP DOMDocument class'), - 'value' => $has_domdocument ? $t('Enabled') : $t('Not found'), - ); - if (!$has_domdocument) { - $requirements['php_domdocument']['severity'] = REQUIREMENT_ERROR; - $requirements['php_domdocument']['description'] = $t('The testing framework requires the DOMDocument class to be available. Check the configure command at the <a href="@link-phpinfo">PHP info page</a>.', array('@link-phpinfo' => url('admin/reports/status/php'))); - } - - // SimpleTest currently needs 2 cURL options which are incompatible with - // 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')); - } - - // Check the current memory limit. If it is set too low, SimpleTest will fail - // to load all tests and throw a fatal error. - $memory_limit = ini_get('memory_limit'); - if ($memory_limit && $memory_limit != -1 && parse_size($memory_limit) < parse_size(SIMPLETEST_MINIMUM_PHP_MEMORY_LIMIT)) { - $requirements['php_memory_limit']['severity'] = REQUIREMENT_ERROR; - $requirements['php_memory_limit']['description'] = $t('The testing framework requires the PHP memory limit to be at least %memory_minimum_limit. The current value is %memory_limit. <a href="@url">Follow these steps to continue</a>.', array('%memory_limit' => $memory_limit, '%memory_minimum_limit' => SIMPLETEST_MINIMUM_PHP_MEMORY_LIMIT, '@url' => 'http://drupal.org/node/207036')); - } - - return $requirements; -} - -/** - * Implements hook_schema(). - */ -function simpletest_schema() { - $schema['simpletest'] = array( - 'description' => 'Stores simpletest messages', - 'fields' => array( - 'message_id' => array( - 'type' => 'serial', - 'not null' => TRUE, - 'description' => 'Primary Key: Unique simpletest message ID.', - ), - 'test_id' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'Test ID, messages belonging to the same ID are reported together', - ), - 'test_class' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The name of the class that created this message.', - ), - 'status' => array( - 'type' => 'varchar', - 'length' => 9, - 'not null' => TRUE, - 'default' => '', - 'description' => 'Message status. Core understands pass, fail, exception.', - ), - 'message' => array( - 'type' => 'text', - 'not null' => TRUE, - 'description' => 'The message itself.', - ), - 'message_group' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The message group this message belongs to. For example: warning, browser, user.', - ), - 'function' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - 'description' => 'Name of the assertion function or method that created this message.', - ), - 'line' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'Line number on which the function is called.', - ), - 'file' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - 'description' => 'Name of the file where the function is called.', - ), - ), - 'primary key' => array('message_id'), - 'indexes' => array( - 'reporter' => array('test_class', 'message_id'), - ), - ); - $schema['simpletest_test_id'] = array( - 'description' => 'Stores simpletest test IDs, used to auto-incrament the test ID so that a fresh test ID is used.', - 'fields' => array( - 'test_id' => array( - 'type' => 'serial', - 'not null' => TRUE, - 'description' => 'Primary Key: Unique simpletest ID used to group test results together. Each time a set of tests - are run a new test ID is used.', - ), - 'last_prefix' => array( - 'type' => 'varchar', - 'length' => 60, - 'not null' => FALSE, - 'default' => '', - 'description' => 'The last database prefix used during testing.', - ), - ), - 'primary key' => array('test_id'), - ); - return $schema; -} - -/** - * Implements hook_uninstall(). - */ -function simpletest_uninstall() { - drupal_load('module', 'simpletest'); - simpletest_clean_database(); - - // Remove settings variables. - variable_del('simpletest_httpauth_method'); - variable_del('simpletest_httpauth_username'); - variable_del('simpletest_httpauth_password'); - variable_del('simpletest_clear_results'); - variable_del('simpletest_verbose'); - - // Remove generated files. - file_unmanaged_delete_recursive('public://simpletest'); -} diff --git a/modules/simpletest/simpletest.js b/modules/simpletest/simpletest.js deleted file mode 100644 index c33ef982a8b..00000000000 --- a/modules/simpletest/simpletest.js +++ /dev/null @@ -1,103 +0,0 @@ -(function ($) { - -/** - * Add the cool table collapsing on the testing overview page. - */ -Drupal.behaviors.simpleTestMenuCollapse = { - attach: function (context, settings) { - var timeout = null; - // Adds expand-collapse functionality. - $('div.simpletest-image').each(function () { - direction = settings.simpleTest[$(this).attr('id')].imageDirection; - $(this).html(settings.simpleTest.images[direction]); - }); - - // Adds group toggling functionality to arrow images. - $('div.simpletest-image').click(function () { - var trs = $(this).parents('tbody').children('.' + settings.simpleTest[this.id].testClass); - var direction = settings.simpleTest[this.id].imageDirection; - var row = direction ? trs.size() - 1 : 0; - - // If clicked in the middle of expanding a group, stop so we can switch directions. - if (timeout) { - clearTimeout(timeout); - } - - // Function to toggle an individual row according to the current direction. - // We set a timeout of 20 ms until the next row will be shown/hidden to - // create a sliding effect. - function rowToggle() { - if (direction) { - if (row >= 0) { - $(trs[row]).hide(); - row--; - timeout = setTimeout(rowToggle, 20); - } - } - else { - if (row < trs.size()) { - $(trs[row]).removeClass('js-hide').show(); - row++; - timeout = setTimeout(rowToggle, 20); - } - } - } - - // Kick-off the toggling upon a new click. - 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; - - }); - } -}; - -/** - * Select/deselect all the inner checkboxes when the outer checkboxes are - * selected/deselected. - */ -Drupal.behaviors.simpleTestSelectAll = { - attach: function (context, settings) { - $('td.simpletest-select-all').each(function () { - var testCheckboxes = settings.simpleTest['simpletest-test-group-' + $(this).attr('id')].testNames; - var groupCheckbox = $('<input type="checkbox" class="form-checkbox" id="' + $(this).attr('id') + '-select-all" />'); - - // Each time a single-test checkbox is checked or unchecked, make sure - // that the associated group checkbox gets the right state too. - var updateGroupCheckbox = function () { - var checkedTests = 0; - for (var i = 0; i < testCheckboxes.length; i++) { - $('#' + testCheckboxes[i]).each(function () { - if (($(this).attr('checked'))) { - checkedTests++; - } - }); - } - $(groupCheckbox).attr('checked', (checkedTests == testCheckboxes.length)); - }; - - // Have the single-test checkboxes follow the group checkbox. - groupCheckbox.change(function () { - var checked = !!($(this).attr('checked')); - for (var i = 0; i < testCheckboxes.length; i++) { - $('#' + testCheckboxes[i]).attr('checked', checked); - } - }); - - // Have the group checkbox follow the single-test checkboxes. - for (var i = 0; i < testCheckboxes.length; i++) { - $('#' + testCheckboxes[i]).change(function () { - updateGroupCheckbox(); - }); - } - - // Initialize status for the group checkbox correctly. - updateGroupCheckbox(); - $(this).append(groupCheckbox); - }); - } -}; - -})(jQuery); diff --git a/modules/simpletest/simpletest.module b/modules/simpletest/simpletest.module deleted file mode 100644 index 310dc3582b4..00000000000 --- a/modules/simpletest/simpletest.module +++ /dev/null @@ -1,506 +0,0 @@ -<?php - -/** - * @file - * Provides testing functionality. - */ - -/** - * Implements hook_help(). - */ -function simpletest_help($path, $arg) { - switch ($path) { - case 'admin/help#simpletest': - $output = ''; - $output .= '<h3>' . t('About') . '</h3>'; - $output .= '<p>' . t('The Testing 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">Testing module</a>.', array('@simpletest' => 'http://drupal.org/handbook/modules/simpletest', '@blocks' => url('admin/structure/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/config/development/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; - } -} - -/** - * Implements hook_menu(). - */ -function simpletest_menu() { - $items['admin/config/development/testing'] = array( - 'title' => 'Testing', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('simpletest_test_form'), - 'description' => 'Run tests against Drupal core and your modules. These tests help assure that your site code is working as designed.', - 'access arguments' => array('administer unit tests'), - 'file' => 'simpletest.pages.inc', - 'weight' => -5, - ); - $items['admin/config/development/testing/list'] = array( - 'title' => 'List', - 'type' => MENU_DEFAULT_LOCAL_TASK, - ); - $items['admin/config/development/testing/settings'] = array( - 'title' => 'Settings', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('simpletest_settings_form'), - 'access arguments' => array('administer unit tests'), - 'type' => MENU_LOCAL_TASK, - 'file' => 'simpletest.pages.inc', - ); - $items['admin/config/development/testing/results/%'] = array( - 'title' => 'Test result', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('simpletest_result_form', 5), - 'description' => 'View result of tests.', - 'access arguments' => array('administer unit tests'), - 'file' => 'simpletest.pages.inc', - ); - return $items; -} - -/** - * Implements hook_permission(). - */ -function simpletest_permission() { - return array( - 'administer unit tests' => array( - 'title' => t('Administer tests'), - 'restrict access' => TRUE, - ), - ); -} - -/** - * Implements hook_theme(). - */ -function simpletest_theme() { - return array( - 'simpletest_test_table' => array( - 'render element' => 'table', - 'file' => 'simpletest.pages.inc', - ), - 'simpletest_result_summary' => array( - 'render element' => 'form', - 'file' => 'simpletest.pages.inc', - ), - ); -} - -/** - * Implements 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'), - '@fail' => format_plural(isset($summary['#fail']) ? $summary['#fail'] : 0, '1 fail', '@count fails'), - '@exception' => format_plural(isset($summary['#exception']) ? $summary['#exception'] : 0, '1 exception', '@count exceptions'), - ); - if (!$summary['#debug']) { - return t('@pass, @fail, and @exception', $args); - } - $args['@debug'] = format_plural(isset($summary['#debug']) ? $summary['#debug'] : 0, '1 debug message', '@count debug messages'); - return t('@pass, @fail, @exception, and @debug', $args); -} - -/** - * Actually runs tests. - * - * @param $test_list - * List of tests to run. - * @param $reporter - * Which reporter to use. Allowed values are: text, xml, html and drupal, - * drupal being the default. - */ -function simpletest_run_tests($test_list, $reporter = 'drupal') { - $test_id = db_insert('simpletest_test_id') - ->useDefaults(array('test_id')) - ->execute(); - - // Clear out the previous verbose files. - file_unmanaged_delete_recursive('public://simpletest/verbose'); - - // Get the info for the first test being run. - $first_test = array_shift($test_list); - $first_instance = new $first_test(); - array_unshift($test_list, $first_test); - $info = $first_instance->getInfo(); - - $batch = array( - 'title' => t('Running tests'), - 'operations' => array( - array('_simpletest_batch_operation', array($test_list, $test_id)), - ), - 'finished' => '_simpletest_batch_finished', - 'progress_message' => '', - 'css' => array(drupal_get_path('module', 'simpletest') . '/simpletest.css'), - 'init_message' => t('Processing test @num of @max - %test.', array('%test' => $info['name'], '@num' => '1', '@max' => count($test_list))), - ); - batch_set($batch); - - module_invoke_all('test_group_started'); - - return $test_id; -} - -/** - * Batch operation callback. - */ -function _simpletest_batch_operation($test_list_init, $test_id, &$context) { - // Get working values. - if (!isset($context['sandbox']['max'])) { - // First iteration: initialize working values. - $test_list = $test_list_init; - $context['sandbox']['max'] = count($test_list); - $test_results = array('#pass' => 0, '#fail' => 0, '#exception' => 0, '#debug' => 0); - } - else { - // Nth iteration: get the current values where we last stored them. - $test_list = $context['sandbox']['tests']; - $test_results = $context['sandbox']['test_results']; - } - $max = $context['sandbox']['max']; - - // Perform the next test. - $test_class = array_shift($test_list); - $test = new $test_class($test_id); - $test->run(); - $size = count($test_list); - $info = $test->getInfo(); - - module_invoke_all('test_finished', $test->results); - - // Gather results and compose the report. - $test_results[$test_class] = $test->results; - foreach ($test_results[$test_class] as $key => $value) { - $test_results[$key] += $value; - } - $test_results[$test_class]['#name'] = $info['name']; - $items = array(); - foreach (element_children($test_results) as $class) { - array_unshift($items, '<div class="simpletest-' . ($test_results[$class]['#fail'] + $test_results[$class]['#exception'] ? 'fail' : 'pass') . '">' . t('@name: @summary', array('@name' => $test_results[$class]['#name'], '@summary' => _simpletest_format_summary_line($test_results[$class]))) . '</div>'); - } - $context['message'] = t('Processed test @num of @max - %test.', array('%test' => $info['name'], '@num' => $max - $size, '@max' => $max)); - $context['message'] .= '<div class="simpletest-' . ($test_results['#fail'] + $test_results['#exception'] ? 'fail' : 'pass') . '">Overall results: ' . _simpletest_format_summary_line($test_results) . '</div>'; - $context['message'] .= theme('item_list', array('items' => $items)); - - // Save working values for the next iteration. - $context['sandbox']['tests'] = $test_list; - $context['sandbox']['test_results'] = $test_results; - // The test_id is the only thing we need to save for the report page. - $context['results']['test_id'] = $test_id; - - // Multistep processing: report progress. - $context['finished'] = 1 - $size / $max; -} - -function _simpletest_batch_finished($success, $results, $operations, $elapsed) { - if ($success) { - drupal_set_message(t('The test run finished in @elapsed.', array('@elapsed' => $elapsed))); - } - else { - // Use the test_id passed as a parameter to _simpletest_batch_operation(). - $test_id = $operations[0][1][1]; - - // Retrieve the last database prefix used for testing and the last test - // class that was run from. Use the information to read the lgo file - // in case any fatal errors caused the test to crash. - list($last_prefix, $last_test_class) = simpletest_last_test_get($test_id); - 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('Use the <em>Clean environment</em> button to clean-up temporary files and tables.'), 'warning'); - } - module_invoke_all('test_group_finished'); -} - -/** - * Get information about the last test that ran given a test ID. - * - * @param $test_id - * The test ID to get the last test from. - * @return - * Array containing the last database prefix used and the last test class - * that ran. - */ -function simpletest_last_test_get($test_id) { - $last_prefix = db_query_range('SELECT last_prefix FROM {simpletest_test_id} WHERE test_id = :test_id', 0, 1, array(':test_id' => $test_id))->fetchField(); - $last_test_class = db_query_range('SELECT test_class FROM {simpletest} WHERE test_id = :test_id ORDER BY message_id DESC', 0, 1, array(':test_id' => $test_id))->fetchField(); - return array($last_prefix, $last_test_class); -} - -/** - * Read the error log and report any errors as assertion failures. - * - * The errors in the log should only be fatal errors since any other errors - * will have been recorded by the error handler. - * - * @param $test_id - * The test ID to which the log relates. - * @param $prefix - * The database prefix to which the log relates. - * @param $test_class - * The test class to which the log relates. - * @param $during_test - * Indicates that the current file directory path is a temporary file - * file directory used during testing. - * @return - * Found any entries in log. - */ -function simpletest_log_read($test_id, $prefix, $test_class, $during_test = FALSE) { - $log = 'public://' . ($during_test ? '' : '/simpletest/' . substr($prefix, 10)) . '/error.log'; - $found = FALSE; - if (file_exists($log)) { - foreach (file($log) as $line) { - if (preg_match('/\[.*?\] (.*?): (.*?) in (.*) on line (\d+)/', $line, $match)) { - // Parse PHP fatal errors for example: PHP Fatal error: Call to - // undefined function break_me() in /path/to/file.php on line 17 - $caller = array( - 'line' => $match[4], - 'file' => $match[3], - ); - DrupalTestCase::insertAssert($test_id, $test_class, FALSE, $match[2], $match[1], $caller); - } - else { - // Unknown format, place the entire message in the log. - DrupalTestCase::insertAssert($test_id, $test_class, FALSE, $line, 'Fatal error'); - } - $found = TRUE; - } - } - return $found; -} - -/** - * Get a list of all of the tests provided by the system. - * - * The list of test classes is loaded from the registry where it looks for - * files ending in ".test". Once loaded the test list is cached and stored in - * a static variable. In order to list tests provided by disabled modules - * hook_registry_files_alter() is used to forcefully add them to the registry. - * - * @return - * An array of tests keyed with the groups specified in each of the tests - * getInfo() method and then keyed by the test class. An example of the array - * structure is provided below. - * - * @code - * $groups['Block'] => array( - * 'BlockTestCase' => array( - * 'name' => 'Block functionality', - * 'description' => 'Add, edit and delete custom block...', - * 'group' => 'Block', - * ), - * ); - * @endcode - * @see simpletest_registry_files_alter() - */ -function simpletest_test_get_all() { - $groups = &drupal_static(__FUNCTION__); - - if (!$groups) { - // Load test information from cache if available, otherwise retrieve the - // information from each tests getInfo() method. - if ($cache = cache()->get('simpletest')) { - $groups = $cache->data; - } - else { - // Select all clases in files ending with .test. - $classes = db_query("SELECT name FROM {registry} WHERE type = :type AND filename LIKE :name", array(':type' => 'class', ':name' => '%.test'))->fetchCol(); - - // Check that each class has a getInfo() method and store the information - // in an array keyed with the group specified in the test information. - $groups = array(); - foreach ($classes as $class) { - // Test classes need to implement getInfo() to be valid. - if (class_exists($class) && method_exists($class, 'getInfo')) { - $info = call_user_func(array($class, 'getInfo')); - - // 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; -} - -/** - * Implements 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 symmetrical file. - - // Create filename. - file_put_contents('public://' . $filename . '.txt', $text); - return $filename; -} - -/** - * Remove all temporary database tables and directories. - */ -function simpletest_clean_environment() { - simpletest_clean_database(); - simpletest_clean_temporary_directories(); - if (variable_get('simpletest_clear_results', TRUE)) { - $count = simpletest_clean_results_table(); - drupal_set_message(format_plural($count, 'Removed 1 test result.', 'Removed @count test results.')); - } - 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. - registry_rebuild(); - cache()->delete('simpletest'); -} - -/** - * 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}') . '%'); - $schema = drupal_get_schema_unprocessed('simpletest'); - $count = 0; - 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($matches[0]); - $count++; - } - } - - 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.')); - } -} - -/** - * Find all leftover temporary directories and remove them. - */ -function simpletest_clean_temporary_directories() { - $count = 0; - if (is_dir('public://simpletest')) { - $files = scandir('public://simpletest'); - foreach ($files as $file) { - $path = 'public://simpletest/' . $file; - if (is_dir($path) && is_numeric($file)) { - file_unmanaged_delete_recursive($path); - $count++; - } - } - } - - if ($count > 0) { - drupal_set_message(format_plural($count, 'Removed 1 temporary directory.', 'Removed @count temporary directories.')); - } - else { - drupal_set_message(t('No temporary directories to remove.')); - } -} - -/** - * Clear the test result tables. - * - * @param $test_id - * Test ID to remove results for, or NULL to remove all results. - * @return - * The number of results removed. - */ -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(); - - db_delete('simpletest') - ->condition('test_id', $test_id) - ->execute(); - db_delete('simpletest_test_id') - ->condition('test_id', $test_id) - ->execute(); - } - else { - $count = db_query('SELECT COUNT(test_id) FROM {simpletest_test_id}')->fetchField(); - - // Clear test results. - db_delete('simpletest')->execute(); - db_delete('simpletest_test_id')->execute(); - } - - return $count; - } - return 0; -} diff --git a/modules/simpletest/simpletest.pages.inc b/modules/simpletest/simpletest.pages.inc deleted file mode 100644 index 696d1471887..00000000000 --- a/modules/simpletest/simpletest.pages.inc +++ /dev/null @@ -1,510 +0,0 @@ -<?php - -/** - * @file - * Page callbacks for simpletest module. - */ - -/** - * List tests arranged in groups that can be selected and run. - */ -function simpletest_test_form($form) { - $form['tests'] = array( - '#type' => 'fieldset', - '#title' => t('Tests'), - '#description' => t('Select the test(s) or test group(s) you would like to run, and click <em>Run tests</em>.'), - ); - - $form['tests']['table'] = array( - '#theme' => 'simpletest_test_table', - ); - - // Generate the list of tests arranged by group. - $groups = simpletest_test_get_all(); - foreach ($groups as $group => $tests) { - $form['tests']['table'][$group] = array( - '#collapsed' => TRUE, - ); - - foreach ($tests as $class => $info) { - $form['tests']['table'][$group][$class] = array( - '#type' => 'checkbox', - '#title' => $info['name'], - '#description' => $info['description'], - ); - } - } - - // 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; -} - -/** - * Returns HTML for a test list generated by simpletest_test_form() into a table. - * - * @param $variables - * An associative array containing: - * - table: A render element representing the table. - * - * @ingroup themeable - */ -function theme_simpletest_test_table($variables) { - $table = $variables['table']; - - drupal_add_css(drupal_get_path('module', 'simpletest') . '/simpletest.css'); - drupal_add_js(drupal_get_path('module', 'simpletest') . '/simpletest.js'); - drupal_add_js('misc/tableselect.js'); - - // Create header for test selection table. - $header = array( - array('class' => array('select-all')), - array('data' => t('Test'), 'class' => array('simpletest_test')), - array('data' => t('Description'), 'class' => array('simpletest_description')), - ); - - // Define the images used to expand/collapse the test groups. - $js = array( - 'images' => array( - theme('image', array('path' => 'misc/menu-collapsed.png', 'width' => 7, 'height' => 7, 'alt' => t('Expand'), 'title' => t('Expand'))) . ' <a href="#" class="simpletest-collapse">(' . t('Expand') . ')</a>', - theme('image', array('path' => 'misc/menu-expanded.png', 'width' => 7, 'height' => 7, 'alt' => t('Collapse'), 'title' => t('Collapse'))) . ' <a href="#" class="simpletest-collapse">(' . t('Collapse') . ')</a>', - ), - ); - - // Cycle through each test group and create a row. - $rows = array(); - foreach (element_children($table) as $key) { - $element = &$table[$key]; - $row = array(); - - // Make the class name safe for output on the page by replacing all - // non-word/decimal characters with a dash (-). - $test_class = strtolower(trim(preg_replace("/[^\w\d]/", "-", $key))); - - // Select the right "expand"/"collapse" image, depending on whether the - // category is expanded (at least one test selected) or not. - $collapsed = !empty($element['#collapsed']); - $image_index = $collapsed ? 0 : 1; - - // Place-holder for checkboxes to select group of tests. - $row[] = array('id' => $test_class, 'class' => array('simpletest-select-all')); - - // Expand/collapse image and group title. - $row[] = array( - 'data' => '<div class="simpletest-image" id="simpletest-test-group-' . $test_class . '"></div>' . - '<label for="' . $test_class . '-select-all" class="simpletest-group-label">' . $key . '</label>', - 'class' => array('simpletest-group-label'), - ); - - $row[] = array( - 'data' => ' ', - 'class' => array('simpletest-group-description'), - ); - - $rows[] = array('data' => $row, 'class' => array('simpletest-group')); - - // Add individual tests to group. - $current_js = array( - 'testClass' => $test_class . '-test', - 'testNames' => array(), - 'imageDirection' => $image_index, - 'clickActive' => FALSE, - ); - - // Sorting $element by children's #title attribute instead of by class name. - uasort($element, 'element_sort_by_title'); - - // Cycle through each test within the current group. - foreach (element_children($element) as $test_name) { - $test = $element[$test_name]; - $row = array(); - - $current_js['testNames'][] = $test['#id']; - - // Store test title and description so that checkbox won't render them. - $title = $test['#title']; - $description = $test['#description']; - - $test['#title_display'] = 'invisible'; - unset($test['#description']); - - // Test name is used to determine what tests to run. - $test['#name'] = $test_name; - - $row[] = array( - 'data' => drupal_render($test), - 'class' => array('simpletest-test-select'), - ); - $row[] = array( - 'data' => '<label for="' . $test['#id'] . '">' . $title . '</label>', - 'class' => array('simpletest-test-label'), - ); - $row[] = array( - 'data' => '<div class="description">' . $description . '</div>', - 'class' => array('simpletest-test-description'), - ); - - $rows[] = array('data' => $row, 'class' => array($test_class . '-test', ($collapsed ? 'js-hide' : ''))); - } - $js['simpletest-test-group-' . $test_class] = $current_js; - unset($table[$key]); - } - - // Add js array of settings. - drupal_add_js(array('simpleTest' => $js), 'setting'); - - if (empty($rows)) { - return '<strong>' . t('No tests to display.') . '</strong>'; - } - else { - return theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'simpletest-form-table'))); - } -} - -/** - * Run selected tests. - */ -function simpletest_test_form_submit($form, &$form_state) { - // Get list of tests. - $tests_list = array(); - foreach ($form_state['values'] as $class_name => $value) { - // 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 ) { - $test_id = simpletest_run_tests($tests_list, 'drupal'); - $form_state['redirect'] = 'admin/config/development/testing/results/' . $test_id; - } - else { - drupal_set_message(t('No test(s) selected.'), 'error'); - } -} - -/** - * Test results form for $test_id. - */ -function simpletest_result_form($form, &$form_state, $test_id) { - // 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'); - return $form; - } - - // Load all classes and include CSS. - drupal_add_css(drupal_get_path('module', 'simpletest') . '/simpletest.css'); - - // Keep track of which test cases passed or failed. - $filter = array( - 'pass' => array(), - 'fail' => array(), - ); - - // Summary result fieldset. - $form['result'] = array( - '#type' => 'fieldset', - '#title' => t('Results'), - ); - $form['result']['summary'] = $summary = array( - '#theme' => 'simpletest_result_summary', - '#pass' => 0, - '#fail' => 0, - '#exception' => 0, - '#debug' => 0, - ); - - // Cycle through each test group. - $header = array(t('Message'), t('Group'), t('Filename'), t('Line'), t('Function'), array('colspan' => 2, 'data' => t('Status'))); - $form['result']['results'] = array(); - foreach ($results as $group => $assertions) { - // Create group fieldset with summary information. - $info = call_user_func(array($group, 'getInfo')); - $form['result']['results'][$group] = array( - '#type' => 'fieldset', - '#title' => $info['name'], - '#description' => $info['description'], - '#collapsible' => TRUE, - ); - $form['result']['results'][$group]['summary'] = $summary; - $group_summary = &$form['result']['results'][$group]['summary']; - - // Create table of assertions for the group. - $rows = array(); - foreach ($assertions as $assertion) { - $row = array(); - $row[] = $assertion->message; - $row[] = $assertion->message_group; - $row[] = basename($assertion->file); - $row[] = $assertion->line; - $row[] = $assertion->function; - $row[] = simpletest_result_status_image($assertion->status); - - $class = 'simpletest-' . $assertion->status; - if ($assertion->message_group == 'Debug') { - $class = 'simpletest-debug'; - } - $rows[] = array('data' => $row, 'class' => array($class)); - - $group_summary['#' . $assertion->status]++; - $form['result']['summary']['#' . $assertion->status]++; - } - $form['result']['results'][$group]['table'] = array( - '#theme' => 'table', - '#header' => $header, - '#rows' => $rows, - ); - - // Set summary information. - $group_summary['#ok'] = $group_summary['#fail'] + $group_summary['#exception'] == 0; - $form['result']['results'][$group]['#collapsed'] = $group_summary['#ok']; - - // Store test group (class) as for use in filter. - $filter[$group_summary['#ok'] ? 'pass' : 'fail'][] = $group; - } - - // Overal summary status. - $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'] = array( - '#type' => 'fieldset', - '#title' => t('Actions'), - '#attributes' => array('class' => array('container-inline')), - '#weight' => -11, - ); - - $form['action']['filter'] = array( - '#type' => 'select', - '#title' => 'Filter', - '#options' => array( - 'all' => t('All (@count)', array('@count' => count($filter['pass']) + count($filter['fail']))), - 'pass' => t('Pass (@count)', array('@count' => count($filter['pass']))), - 'fail' => t('Fail (@count)', array('@count' => count($filter['fail']))), - ), - ); - $form['action']['filter']['#default_value'] = ($filter['fail'] ? 'fail' : 'all'); - - // Categorized test classes for to be used with selected filter value. - $form['action']['filter_pass'] = array( - '#type' => 'hidden', - '#default_value' => implode(',', $filter['pass']), - ); - $form['action']['filter_fail'] = array( - '#type' => 'hidden', - '#default_value' => implode(',', $filter['fail']), - ); - - $form['action']['op'] = array( - '#type' => 'submit', - '#value' => t('Run tests'), - ); - - $form['action']['return'] = array( - '#type' => 'link', - '#title' => t('Return to list'), - '#href' => 'admin/config/development/testing', - ); - - if (is_numeric($test_id)) { - simpletest_clean_results_table($test_id); - } - - return $form; -} - -/** - * Re-run the tests that match the filter. - */ -function simpletest_result_form_submit($form, &$form_state) { - $pass = $form_state['values']['filter_pass'] ? explode(',', $form_state['values']['filter_pass']) : array(); - $fail = $form_state['values']['filter_fail'] ? explode(',', $form_state['values']['filter_fail']) : array(); - - if ($form_state['values']['filter'] == 'all') { - $classes = array_merge($pass, $fail); - } - elseif ($form_state['values']['filter'] == 'pass') { - $classes = $pass; - } - else { - $classes = $fail; - } - - if (!$classes) { - $form_state['redirect'] = 'admin/config/development/testing'; - return; - } - - $form_state_execute = array('values' => array()); - foreach ($classes as $class) { - $form_state_execute['values'][$class] = 1; - } - - simpletest_test_form_submit(array(), $form_state_execute); - $form_state['redirect'] = $form_state_execute['redirect']; -} - -/** - * Returns HTML for the summary status of a simpletest result. - * - * @param $variables - * An associative array containing: - * - form: A render element representing the form. - * - * @ingroup themeable - */ -function theme_simpletest_result_summary($variables) { - $form = $variables['form']; - return '<div class="simpletest-' . ($form['#ok'] ? 'pass' : 'fail') . '">' . _simpletest_format_summary_line($form) . '</div>'; -} - -/** - * Get test results for $test_id. - * - * @param $test_id The test_id to retrieve results of. - * @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(); - - $test_results = array(); - foreach ($results as $result) { - if (!isset($test_results[$result->test_class])) { - $test_results[$result->test_class] = array(); - } - $test_results[$result->test_class][] = $result; - } - return $test_results; -} - -/** - * Get the appropriate image for the status. - * - * @param $status Status string, either: pass, fail, exception. - * @return HTML image or false. - */ -function simpletest_result_status_image($status) { - // $map does not use drupal_static() as its value never changes. - static $map; - - if (!isset($map)) { - $map = array( - 'pass' => theme('image', array('path' => 'misc/watchdog-ok.png', 'width' => 18, 'height' => 18, 'alt' => t('Pass'))), - 'fail' => theme('image', array('path' => 'misc/watchdog-error.png', 'width' => 18, 'height' => 18, 'alt' => t('Fail'))), - 'exception' => theme('image', array('path' => 'misc/watchdog-warning.png', 'width' => 18, 'height' => 18, 'alt' => t('Exception'))), - 'debug' => theme('image', array('path' => 'misc/watchdog-warning.png', 'width' => 18, 'height' => 18, 'alt' => t('Debug'))), - ); - } - if (isset($map[$status])) { - return $map[$status]; - } - return FALSE; -} - -/** - * Provides settings form for SimpleTest variables. - * - * @ingroup forms - * @see simpletest_settings_form_validate() - */ -function simpletest_settings_form($form, &$form_state) { - $form['general'] = array( - '#type' => 'fieldset', - '#title' => t('General'), - ); - $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 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', TRUE), - ); - - $form['httpauth'] = array( - '#type' => 'fieldset', - '#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), - ); - $username = variable_get('simpletest_httpauth_username'); - $password = variable_get('simpletest_httpauth_password'); - $form['httpauth']['simpletest_httpauth_username'] = array( - '#type' => 'textfield', - '#title' => t('Username'), - '#default_value' => $username, - ); - if ($username && $password) { - $form['httpauth']['simpletest_httpauth_username']['#description'] = t('Leave this blank to delete both the existing username and password.'); - } - $form['httpauth']['simpletest_httpauth_password'] = array( - '#type' => 'password', - '#title' => t('Password'), - ); - if ($password) { - $form['httpauth']['simpletest_httpauth_password']['#description'] = t('To change the password, enter the new password here.'); - } - - return system_settings_form($form); -} - -/** - * Validation handler for simpletest_settings_form(). - */ -function simpletest_settings_form_validate($form, &$form_state) { - // If a username was provided but a password wasn't, preserve the existing - // password. - if (!empty($form_state['values']['simpletest_httpauth_username']) && empty($form_state['values']['simpletest_httpauth_password'])) { - $form_state['values']['simpletest_httpauth_password'] = variable_get('simpletest_httpauth_password', ''); - } - - // If a password was provided but a username wasn't, the credentials are - // incorrect, so throw an error. - if (empty($form_state['values']['simpletest_httpauth_username']) && !empty($form_state['values']['simpletest_httpauth_password'])) { - form_set_error('simpletest_httpauth_username', t('HTTP authentication credentials must include a username in addition to a password.')); - } -} - diff --git a/modules/simpletest/simpletest.test b/modules/simpletest/simpletest.test deleted file mode 100644 index e5b6042ac4b..00000000000 --- a/modules/simpletest/simpletest.test +++ /dev/null @@ -1,505 +0,0 @@ -<?php - -/** - * @file - * Tests for simpletest.module. - */ - -class SimpleTestFunctionalTest extends DrupalWebTestCase { - /** - * The results array that has been parsed by getTestResults(). - */ - protected $childTestResults; - - /** - * Store the test ID from each test run for comparison, to ensure they are - * incrementing. - */ - protected $test_ids = array(); - - public static function getInfo() { - return array( - 'name' => 'SimpleTest functionality', - 'description' => 'Test SimpleTest\'s web interface: check that the intended tests were - run and ensure that test reports display the intended results. Also - test SimpleTest\'s internal browser and API\'s both explicitly and - implicitly.', - 'group' => 'SimpleTest' - ); - } - - function setUp() { - if (!$this->inCURL()) { - parent::setUp('simpletest'); - - // Create and login user - $admin_user = $this->drupalCreateUser(array('administer unit tests')); - $this->drupalLogin($admin_user); - } - else { - parent::setUp('non_existent_module'); - } - } - - /** - * Test the internal browsers functionality. - */ - function testInternalBrowser() { - global $conf; - 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->assertNoTitle('Foo', t('Site title does not match.')); - // Make sure that we are locked out of the installer when prefixing - // using the user-agent header. This is an important security check. - global $base_url; - - $this->drupalGet($base_url . '/install.php', array('external' => TRUE)); - $this->assertResponse(403, 'Cannot access install.php with a "simpletest" user-agent header.'); - - $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.')); - $this->assertFalse(empty($headers[0]['location']), t('Intermediate request contained a Location header.')); - $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?destination=user/logout', $edit, t('Log in')); - $headers = $this->drupalGetHeaders(TRUE); - $this->assertEqual(count($headers), 2, t('Simpletest stopped following redirects after the first one.')); - } - } - - /** - * Test validation of the User-Agent header we use to perform test requests. - */ - function testUserAgentValidation() { - if (!$this->inCURL()) { - global $base_url; - $simpletest_path = $base_url . '/' . drupal_get_path('module', 'simpletest'); - $HTTP_path = $simpletest_path .'/tests/http.php?q=node'; - $https_path = $simpletest_path .'/tests/https.php?q=node'; - // Generate a valid simpletest User-Agent to pass validation. - $this->assertTrue(preg_match('/simpletest\d+/', $this->databasePrefix, $matches), t('Database prefix contains simpletest prefix.')); - $test_ua = drupal_generate_test_ua($matches[0]); - $this->additionalCurlOptions = array(CURLOPT_USERAGENT => $test_ua); - - // Test pages only available for testing. - $this->drupalGet($HTTP_path); - $this->assertResponse(200, t('Requesting http.php with a legitimate simpletest User-Agent returns OK.')); - $this->drupalGet($https_path); - $this->assertResponse(200, t('Requesting https.php with a legitimate simpletest User-Agent returns OK.')); - - // Now slightly modify the HMAC on the header, which should not validate. - $this->additionalCurlOptions = array(CURLOPT_USERAGENT => $test_ua . 'X'); - $this->drupalGet($HTTP_path); - $this->assertResponse(403, t('Requesting http.php with a bad simpletest User-Agent fails.')); - $this->drupalGet($https_path); - $this->assertResponse(403, t('Requesting https.php with a bad simpletest User-Agent fails.')); - - // Use a real User-Agent and verify that the special files http.php and - // https.php can't be accessed. - $this->additionalCurlOptions = array(CURLOPT_USERAGENT => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12'); - $this->drupalGet($HTTP_path); - $this->assertResponse(403, t('Requesting http.php with a normal User-Agent fails.')); - $this->drupalGet($https_path); - $this->assertResponse(403, t('Requesting https.php with a normal User-Agent fails.')); - } - } - - /** - * Make sure that tests selected through the web interface are run and - * that the results are displayed correctly. - */ - function testWebTestRunner() { - $this->pass = t('SimpleTest pass.'); - $this->fail = t('SimpleTest fail.'); - $this->valid_permission = 'access content'; - $this->invalid_permission = 'invalid permission'; - - if ($this->inCURL()) { - // Only run following code if this test is running itself through a CURL request. - $this->stubTest(); - } - else { - - // 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'); - - $edit = array(); - $edit['SimpleTestFunctionalTest'] = TRUE; - $this->drupalPost(NULL, $edit, t('Run tests')); - - // Parse results and confirm that they are correct. - $this->getTestResults(); - $this->confirmStubTestResults(); - } - - // Regression test for #290316. - // Check that test_id is incrementing. - $this->assertTrue($this->test_ids[0] != $this->test_ids[1], t('Test ID is incrementing.')); - } - } - - /** - * Test to be run and the results confirmed. - */ - function stubTest() { - $this->pass($this->pass); - $this->fail($this->fail); - - $this->drupalCreateUser(array($this->valid_permission)); - $this->drupalCreateUser(array($this->invalid_permission)); - - $this->pass(t('Test ID is @id.', array('@id' => $this->testId))); - - // Generates a warning. - $i = 1 / 0; - - // Call an assert function specific to that class. - $this->assertNothing(); - - // Generates a warning inside a PHP function. - array_key_exists(NULL, NULL); - - debug('Foo', 'Debug'); - } - - /** - * Assert nothing. - */ - function assertNothing() { - $this->pass("This is nothing."); - } - - /** - * Confirm that the stub test produced the desired results. - */ - function confirmStubTestResults() { - $this->assertAssertion(t('Enabled modules: %modules', array('%modules' => 'non_existent_module')), 'Other', 'Fail', 'simpletest.test', 'SimpleTestFunctionalTest->setUp()'); - - $this->assertAssertion($this->pass, 'Other', 'Pass', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()'); - $this->assertAssertion($this->fail, 'Other', 'Fail', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()'); - - $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 caught by simpletest. - $this->assertAssertion('Division by zero', 'Warning', 'Fail', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()'); - - // Check that the backtracing code works for specific assert function. - $this->assertAssertion('This is nothing.', 'Other', 'Pass', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()'); - - // Check that errors that occur inside PHP internal functions are correctly reported. - // The exact error message differs between PHP versions so we check only - // the function name 'array_key_exists'. - $this->assertAssertion('array_key_exists', 'Warning', 'Fail', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()'); - - $this->assertAssertion("Debug: 'Foo'", 'Debug', 'Fail', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()'); - - $this->assertEqual('6 passes, 5 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.')); - } - - /** - * Fetch the test id from the test results. - */ - function getTestIdFromResults() { - foreach ($this->childTestResults['assertions'] as $assertion) { - if (preg_match('@^Test ID is ([0-9]*)\.$@', $assertion['message'], $matches)) { - return $matches[1]; - } - } - return NULL; - } - - /** - * Assert that an assertion with the specified values is displayed - * in the test results. - * - * @param string $message Assertion message. - * @param string $type Assertion type. - * @param string $status Assertion status. - * @param string $file File where the assertion originated. - * @param string $functuion Function where the assertion originated. - * @return Assertion result. - */ - function assertAssertion($message, $type, $status, $file, $function) { - $message = trim(strip_tags($message)); - $found = FALSE; - foreach ($this->childTestResults['assertions'] as $assertion) { - if ((strpos($assertion['message'], $message) !== FALSE) && - $assertion['type'] == $type && - $assertion['status'] == $status && - $assertion['file'] == $file && - $assertion['function'] == $function) { - $found = TRUE; - break; - } - } - return $this->assertTrue($found, t('Found assertion {"@message", "@type", "@status", "@file", "@function"}.', array('@message' => $message, '@type' => $type, '@status' => $status, "@file" => $file, "@function" => $function))); - } - - /** - * Get the results from a test and store them in the class array $results. - */ - function getTestResults() { - $results = array(); - if ($this->parse()) { - if ($fieldset = $this->getResultFieldSet()) { - // Code assumes this is the only test in group. - $results['summary'] = $this->asText($fieldset->div->div[1]); - $results['name'] = $this->asText($fieldset->legend); - - $results['assertions'] = array(); - $tbody = $fieldset->div->table->tbody; - foreach ($tbody->tr as $row) { - $assertion = array(); - $assertion['message'] = $this->asText($row->td[0]); - $assertion['type'] = $this->asText($row->td[1]); - $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'); - $assertion['status'] = ($row->td[5]->img['src'] == $ok_url) ? 'Pass' : 'Fail'; - $results['assertions'][] = $assertion; - } - } - } - $this->childTestResults = $results; - } - - /** - * Get the fieldset containing the results for group this test is in. - */ - function getResultFieldSet() { - $fieldsets = $this->xpath('//fieldset'); - $info = $this->getInfo(); - foreach ($fieldsets as $fieldset) { - if ($this->asText($fieldset->legend) == $info['name']) { - return $fieldset; - } - } - return FALSE; - } - - /** - * Extract the text contained by the element. - * - * @param $element - * Element to extract text from. - * @return - * Extracted text. - */ - function asText(SimpleXMLElement $element) { - if (!is_object($element)) { - return $this->fail('The element is not an element.'); - } - return trim(html_entity_decode(strip_tags($element->asXML()))); - } - - /** - * Check if the test is being run from inside a CURL request. - */ - function inCURL() { - return (bool) drupal_valid_test_ua(); - } -} - -/** - * Test internal testing framework browser. - */ -class SimpleTestBrowserTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'SimpleTest browser', - 'description' => 'Test the internal browser of the testing framework.', - 'group' => 'SimpleTest', - ); - } - - function setUp() { - parent::setUp(); - variable_set('user_register', USER_REGISTER_VISITORS); - } - - /** - * Test DrupalWebTestCase::getAbsoluteUrl(). - */ - function testGetAbsoluteUrl() { - // Testbed runs with Clean URLs disabled, so disable it here. - variable_set('clean_url', 0); - $url = 'user/login'; - - $this->drupalGet($url); - $absolute = url($url, array('absolute' => TRUE)); - $this->assertEqual($absolute, $this->url, t('Passed and requested URL are equal.')); - $this->assertEqual($this->url, $this->getAbsoluteUrl($this->url), t('Requested and returned absolute URL are equal.')); - - $this->drupalPost(NULL, array(), t('Log in')); - $this->assertEqual($absolute, $this->url, t('Passed and requested URL are equal.')); - $this->assertEqual($this->url, $this->getAbsoluteUrl($this->url), t('Requested and returned absolute URL are equal.')); - - $this->clickLink('Create new account'); - $url = 'user/register'; - $absolute = url($url, array('absolute' => TRUE)); - $this->assertEqual($absolute, $this->url, t('Passed and requested URL are equal.')); - $this->assertEqual($this->url, $this->getAbsoluteUrl($this->url), t('Requested and returned absolute URL are equal.')); - } - - /** - * Tests XPath escaping. - */ - function testXPathEscaping() { - $testpage = <<< EOF -<html> -<body> -<a href="link1">A "weird" link, just to bother the dumb "XPath 1.0"</a> -<a href="link2">A second "even more weird" link, in memory of George O'Malley</a> -</body> -</html> -EOF; - $this->drupalSetContent($testpage); - - // Matches the first link. - $urls = $this->xpath('//a[text()=:text]', array(':text' => 'A "weird" link, just to bother the dumb "XPath 1.0"')); - $this->assertEqual($urls[0]['href'], 'link1', 'Match with quotes.'); - - $urls = $this->xpath('//a[text()=:text]', array(':text' => 'A second "even more weird" link, in memory of George O\'Malley')); - $this->assertEqual($urls[0]['href'], 'link2', 'Match with mixed single and double quotes.'); - } -} - -class SimpleTestMailCaptureTestCase extends DrupalWebTestCase { - /** - * Implement getInfo(). - */ - public static function getInfo() { - return array( - 'name' => 'SimpleTest e-mail capturing', - 'description' => 'Test the SimpleTest e-mail capturing logic, the assertMail assertion and the drupalGetMails function.', - 'group' => 'SimpleTest', - ); - } - - /** - * Test to see if the wrapper function is executed correctly. - */ - function testMailSend() { - // Create an e-mail. - $subject = $this->randomString(64); - $body = $this->randomString(128); - $message = array( - 'id' => 'drupal_mail_test', - 'headers' => array('Content-type'=> 'text/html'), - 'subject' => $subject, - 'to' => 'foobar@example.com', - 'body' => $body, - ); - - // Before we send the e-mail, drupalGetMails should return an empty array. - $captured_emails = $this->drupalGetMails(); - $this->assertEqual(count($captured_emails), 0, t('The captured e-mails queue is empty.'), t('E-mail')); - - // Send the e-mail. - $response = drupal_mail_system('simpletest', 'drupal_mail_test')->mail($message); - - // Ensure that there is one e-mail in the captured e-mails array. - $captured_emails = $this->drupalGetMails(); - $this->assertEqual(count($captured_emails), 1, t('One e-mail was captured.'), t('E-mail')); - - // Assert that the e-mail was sent by iterating over the message properties - // and ensuring that they are captured intact. - foreach ($message as $field => $value) { - $this->assertMail($field, $value, t('The e-mail was sent and the value for property @field is intact.', array('@field' => $field)), t('E-mail')); - } - - // Send additional e-mails so more than one e-mail is captured. - for ($index = 0; $index < 5; $index++) { - $message = array( - 'id' => 'drupal_mail_test_' . $index, - 'headers' => array('Content-type'=> 'text/html'), - 'subject' => $this->randomString(64), - 'to' => $this->randomName(32) . '@example.com', - 'body' => $this->randomString(512), - ); - drupal_mail_system('drupal_mail_test', $index)->mail($message); - } - - // There should now be 6 e-mails captured. - $captured_emails = $this->drupalGetMails(); - $this->assertEqual(count($captured_emails), 6, t('All e-mails were captured.'), t('E-mail')); - - // Test different ways of getting filtered e-mails via drupalGetMails(). - $captured_emails = $this->drupalGetMails(array('id' => 'drupal_mail_test')); - $this->assertEqual(count($captured_emails), 1, t('Only one e-mail is returned when filtering by id.'), t('E-mail')); - $captured_emails = $this->drupalGetMails(array('id' => 'drupal_mail_test', 'subject' => $subject)); - $this->assertEqual(count($captured_emails), 1, t('Only one e-mail is returned when filtering by id and subject.'), t('E-mail')); - $captured_emails = $this->drupalGetMails(array('id' => 'drupal_mail_test', 'subject' => $subject, 'from' => 'this_was_not_used@example.com')); - $this->assertEqual(count($captured_emails), 0, t('No e-mails are returned when querying with an unused from address.'), t('E-mail')); - - // Send the last e-mail again, so we can confirm that the drupalGetMails-filter - // correctly returns all e-mails with a given property/value. - drupal_mail_system('drupal_mail_test', $index)->mail($message); - $captured_emails = $this->drupalGetMails(array('id' => 'drupal_mail_test_4')); - $this->assertEqual(count($captured_emails), 2, t('All e-mails with the same id are returned when filtering by id.'), t('E-mail')); - } -} - -/** - * 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. Image module is used for test.", - 'group' => 'SimpleTest', - ); - } - - function setUp() { - return parent::setUp('image'); - } - - function testFolderSetup() { - $directory = file_default_scheme() . '://styles'; - $this->assertTrue(file_prepare_directory($directory, FALSE), "Directory 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.')); - } -} diff --git a/modules/simpletest/tests/actions.test b/modules/simpletest/tests/actions.test deleted file mode 100644 index 23587f0c54d..00000000000 --- a/modules/simpletest/tests/actions.test +++ /dev/null @@ -1,126 +0,0 @@ -<?php - -class ActionsConfigurationTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Actions configuration', - 'description' => 'Tests complex actions configuration by adding, editing, and deleting a complex action.', - 'group' => 'Actions', - ); - } - - /** - * Test the configuration of advanced actions through the administration - * interface. - */ - function testActionConfiguration() { - // Create a user with permission to view the actions administration pages. - $user = $this->drupalCreateUser(array('administer actions')); - $this->drupalLogin($user); - - // Make a POST request to admin/config/system/actions/manage. - $edit = array(); - $edit['action'] = drupal_hash_base64('system_goto_action'); - $this->drupalPost('admin/config/system/actions/manage', $edit, t('Create')); - - // Make a POST request to the individual action configuration page. - $edit = array(); - $action_label = $this->randomName(); - $edit['actions_label'] = $action_label; - $edit['url'] = 'admin'; - $this->drupalPost('admin/config/system/actions/configure/' . drupal_hash_base64('system_goto_action'), $edit, t('Save')); - - // Make sure that the new complex action was saved properly. - $this->assertText(t('The action has been successfully saved.'), t("Make sure we get a confirmation that we've successfully saved the complex action.")); - $this->assertText($action_label, t("Make sure the action label appears on the configuration page after we've saved the complex action.")); - - // Make another POST request to the action edit page. - $this->clickLink(t('configure')); - preg_match('|admin/config/system/actions/configure/(\d+)|', $this->getUrl(), $matches); - $aid = $matches[1]; - $edit = array(); - $new_action_label = $this->randomName(); - $edit['actions_label'] = $new_action_label; - $edit['url'] = 'admin'; - $this->drupalPost(NULL, $edit, t('Save')); - - // Make sure that the action updated properly. - $this->assertText(t('The action has been successfully saved.'), t("Make sure we get a confirmation that we've successfully updated the complex action.")); - $this->assertNoText($action_label, t("Make sure the old action label does NOT appear on the configuration page after we've updated the complex action.")); - $this->assertText($new_action_label, t("Make sure the action label appears on the configuration page after we've updated the complex action.")); - - // Make sure that deletions work properly. - $this->clickLink(t('delete')); - $edit = array(); - $this->drupalPost("admin/config/system/actions/delete/$aid", $edit, t('Delete')); - - // Make sure that the action was actually deleted. - $this->assertRaw(t('Action %action was deleted', array('%action' => $new_action_label)), t('Make sure that we get a delete confirmation message.')); - $this->drupalGet('admin/config/system/actions/manage'); - $this->assertNoText($new_action_label, t("Make sure the action label does not appear on the overview page after we've deleted the action.")); - $exists = db_query('SELECT aid FROM {actions} WHERE callback = :callback', array(':callback' => 'drupal_goto_action'))->fetchField(); - $this->assertFalse($exists, t('Make sure the action is gone from the database after being deleted.')); - } -} - -/** - * Test actions executing in a potential loop, and make sure they abort properly. - */ -class ActionLoopTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Actions executing in a potentially infinite loop', - 'description' => 'Tests actions executing in a loop, and makes sure they abort properly.', - 'group' => 'Actions', - ); - } - - function setUp() { - parent::setUp('dblog', 'trigger', 'actions_loop_test'); - } - - /** - * Set up a loop with 3 - 12 recursions, and see if it aborts properly. - */ - function testActionLoop() { - $user = $this->drupalCreateUser(array('administer actions')); - $this->drupalLogin($user); - - $hash = drupal_hash_base64('actions_loop_test_log'); - $edit = array('aid' => $hash); - $this->drupalPost('admin/structure/trigger/actions_loop_test', $edit, t('Assign')); - - // Delete any existing watchdog messages to clear the plethora of - // "Action added" messages from when Drupal was installed. - db_delete('watchdog')->execute(); - // To prevent this test from failing when xdebug is enabled, the maximum - // recursion level should be kept low enough to prevent the xdebug - // infinite recursion protection mechanism from aborting the request. - // See http://drupal.org/node/587634. - variable_set('actions_max_stack', mt_rand(3, 12)); - $this->triggerActions(); - } - - /** - * Create an infinite loop by causing a watchdog message to be set, - * which causes the actions to be triggered again, up to actions_max_stack - * times. - */ - protected function triggerActions() { - $this->drupalGet('<front>', array('query' => array('trigger_actions_on_watchdog' => TRUE))); - $expected = array(); - $expected[] = 'Triggering action loop'; - for ($i = 1; $i <= variable_get('actions_max_stack', 35); $i++) { - $expected[] = "Test log #$i"; - } - $expected[] = 'Stack overflow: too many calls to actions_do(). Aborting to prevent infinite recursion.'; - - $result = db_query("SELECT message FROM {watchdog} WHERE type = 'actions_loop_test' OR type = 'actions' ORDER BY wid"); - $loop_started = FALSE; - foreach ($result as $row) { - $expected_message = array_shift($expected); - $this->assertEqual($row->message, $expected_message, t('Expected message %expected, got %message.', array('%expected' => $expected_message, '%message' => $row->message))); - } - $this->assertTrue(empty($expected), t('All expected messages found.')); - } -} diff --git a/modules/simpletest/tests/actions_loop_test.info b/modules/simpletest/tests/actions_loop_test.info deleted file mode 100644 index 35075114c4b..00000000000 --- a/modules/simpletest/tests/actions_loop_test.info +++ /dev/null @@ -1,6 +0,0 @@ -name = Actions loop test -description = Support module for action loop testing. -package = Testing -version = VERSION -core = 8.x -hidden = TRUE diff --git a/modules/simpletest/tests/actions_loop_test.install b/modules/simpletest/tests/actions_loop_test.install deleted file mode 100644 index b22fd85ab9d..00000000000 --- a/modules/simpletest/tests/actions_loop_test.install +++ /dev/null @@ -1,11 +0,0 @@ -<?php - -/** - * Implements hook_install(). - */ -function actions_loop_test_install() { - db_update('system') - ->fields(array('weight' => 1)) - ->condition('name', 'actions_loop_test') - ->execute(); -} diff --git a/modules/simpletest/tests/actions_loop_test.module b/modules/simpletest/tests/actions_loop_test.module deleted file mode 100644 index 77764907b9e..00000000000 --- a/modules/simpletest/tests/actions_loop_test.module +++ /dev/null @@ -1,94 +0,0 @@ -<?php - -/** - * Implements hook_trigger_info(). - */ -function actions_loop_test_trigger_info() { - return array( - 'actions_loop_test' => array( - 'watchdog' => array( - 'label' => t('When a message is logged'), - ), - ), - ); -} - -/** - * Implements hook_watchdog(). - */ -function actions_loop_test_watchdog(array $log_entry) { - // If the triggering actions are not explicitly enabled, abort. - if (empty($_GET['trigger_actions_on_watchdog'])) { - return; - } - // Get all the action ids assigned to the trigger on the watchdog hook's - // "run" event. - $aids = trigger_get_assigned_actions('watchdog'); - // We can pass in any applicable information in $context. There isn't much in - // this case, but we'll pass in the hook name as the bare minimum. - $context = array( - 'hook' => 'watchdog', - ); - // Fire the actions on the associated object ($log_entry) and the context - // variable. - actions_do(array_keys($aids), $log_entry, $context); -} - -/** - * Implements hook_init(). - */ -function actions_loop_test_init() { - if (!empty($_GET['trigger_actions_on_watchdog'])) { - watchdog_skip_semaphore('actions_loop_test', 'Triggering action loop'); - } -} - -/** - * Implements hook_action_info(). - */ -function actions_loop_test_action_info() { - return array( - 'actions_loop_test_log' => array( - 'label' => t('Write a message to the log.'), - 'type' => 'system', - 'configurable' => FALSE, - 'triggers' => array('any'), - ), - ); -} - -/** - * Write a message to the log. - */ -function actions_loop_test_log() { - $count = &drupal_static(__FUNCTION__, 0); - $count++; - watchdog_skip_semaphore('actions_loop_test', "Test log #$count"); -} - -/** - * Replacement of the watchdog() function that eliminates the use of semaphores - * so that we can test the abortion of an action loop. - */ -function watchdog_skip_semaphore($type, $message, $variables = array(), $severity = WATCHDOG_NOTICE, $link = NULL) { - global $user, $base_root; - - // Prepare the fields to be logged - $log_entry = array( - 'type' => $type, - 'message' => $message, - 'variables' => $variables, - 'severity' => $severity, - 'link' => $link, - 'user' => $user, - 'request_uri' => $base_root . request_uri(), - 'referer' => $_SERVER['HTTP_REFERER'], - 'ip' => ip_address(), - 'timestamp' => REQUEST_TIME, - ); - - // Call the logging hooks to log/process the message - foreach (module_implements('watchdog') as $module) { - module_invoke($module, 'watchdog', $log_entry); - } -} diff --git a/modules/simpletest/tests/ajax.test b/modules/simpletest/tests/ajax.test deleted file mode 100644 index 9a76b9692a9..00000000000 --- a/modules/simpletest/tests/ajax.test +++ /dev/null @@ -1,488 +0,0 @@ -<?php - -class AJAXTestCase extends DrupalWebTestCase { - function setUp() { - $modules = func_get_args(); - if (isset($modules[0]) && is_array($modules[0])) { - $modules = $modules[0]; - } - parent::setUp(array_unique(array_merge(array('ajax_test', 'ajax_forms_test'), $modules))); - } - - /** - * Assert that a command with the required properties exists within the array of Ajax commands returned by the server. - * - * The Ajax framework, via the ajax_deliver() and ajax_render() functions, - * returns an array of commands. This array sometimes includes commands - * automatically provided by the framework in addition to commands returned by - * a particular page callback. During testing, we're usually interested that a - * particular command is present, and don't care whether other commands - * precede or follow the one we're interested in. Additionally, the command - * we're interested in may include additional data that we're not interested - * in. Therefore, this function simply asserts that one of the commands in - * $haystack contains all of the keys and values in $needle. Furthermore, if - * $needle contains a 'settings' key with an array value, we simply assert - * that all keys and values within that array are present in the command we're - * checking, and do not consider it a failure if the actual command contains - * additional settings that aren't part of $needle. - * - * @param $haystack - * An array of Ajax commands returned by the server. - * @param $needle - * Array of info we're expecting in one of those commands. - * @param $message - * An assertion message. - */ - protected function assertCommand($haystack, $needle, $message) { - $found = FALSE; - foreach ($haystack as $command) { - // If the command has additional settings that we're not testing for, do - // not consider that a failure. - if (isset($command['settings']) && is_array($command['settings']) && isset($needle['settings']) && is_array($needle['settings'])) { - $command['settings'] = array_intersect_key($command['settings'], $needle['settings']); - } - // If the command has additional data that we're not testing for, do not - // consider that a failure. Also, == instead of ===, because we don't - // require the key/value pairs to be in any particular order - // (http://www.php.net/manual/en/language.operators.array.php). - if (array_intersect_key($command, $needle) == $needle) { - $found = TRUE; - break; - } - } - $this->assertTrue($found, $message); - } -} - -/** - * Tests primary Ajax framework functions. - */ -class AJAXFrameworkTestCase extends AJAXTestCase { - protected $profile = 'testing'; - - public static function getInfo() { - return array( - 'name' => 'AJAX framework', - 'description' => 'Performs tests on AJAX framework functions.', - 'group' => 'AJAX', - ); - } - - /** - * Test that ajax_render() returns JavaScript settings generated during the page request. - * - * @todo Add tests to ensure that ajax_render() returns commands for new CSS - * and JavaScript files to be loaded by the page. See - * http://drupal.org/node/561858. - */ - function testAJAXRender() { - $commands = $this->drupalGetAJAX('ajax-test/render'); - - // Verify that there is a command to load settings added with - // drupal_add_js(). - $expected = array( - 'command' => 'settings', - 'settings' => array('basePath' => base_path(), 'ajax' => 'test'), - ); - $this->assertCommand($commands, $expected, t('ajax_render() loads settings added with drupal_add_js().')); - - // Verify that Ajax settings are loaded for #type 'link'. - $this->drupalGet('ajax-test/link'); - $settings = $this->drupalGetSettings(); - $this->assertEqual($settings['ajax']['ajax-link']['url'], url('filter/tips')); - $this->assertEqual($settings['ajax']['ajax-link']['wrapper'], 'block-system-main'); - } - - /** - * Test behavior of ajax_render_error(). - */ - function testAJAXRenderError() { - // Verify default error message. - $commands = $this->drupalGetAJAX('ajax-test/render-error'); - $expected = array( - 'command' => 'alert', - 'text' => t('An error occurred while handling the request: The server received invalid input.'), - ); - $this->assertCommand($commands, $expected, t('ajax_render_error() invokes alert command.')); - - // Verify custom error message. - $edit = array( - 'message' => 'Custom error message.', - ); - $commands = $this->drupalGetAJAX('ajax-test/render-error', array('query' => $edit)); - $expected = array( - 'command' => 'alert', - 'text' => $edit['message'], - ); - $this->assertCommand($commands, $expected, t('Custom error message is output.')); - } - - /** - * Test that new JavaScript and CSS files added during an AJAX request are returned. - */ - function testLazyLoad() { - $expected = array( - 'setting_name' => 'ajax_forms_test_lazy_load_form_submit', - 'setting_value' => 'executed', - 'css' => drupal_get_path('module', 'system') . '/system.admin.css', - 'js' => drupal_get_path('module', 'system') . '/system.js', - ); - - // Get the base page. - $this->drupalGet('ajax_forms_test_lazy_load_form'); - $original_settings = $this->drupalGetSettings(); - $original_css = $original_settings['ajaxPageState']['css']; - $original_js = $original_settings['ajaxPageState']['js']; - - // Verify that the base page doesn't have the settings and files that are to - // be lazy loaded as part of the next request. - $this->assertTrue(!isset($original_settings[$expected['setting_name']]), t('Page originally lacks the %setting, as expected.', array('%setting' => $expected['setting_name']))); - $this->assertTrue(!isset($original_settings[$expected['css']]), t('Page originally lacks the %css file, as expected.', array('%css' => $expected['css']))); - $this->assertTrue(!isset($original_settings[$expected['js']]), t('Page originally lacks the %js file, as expected.', array('%js' => $expected['js']))); - - // Submit the AJAX request. - $commands = $this->drupalPostAJAX(NULL, array(), array('op' => t('Submit'))); - $new_settings = $this->drupalGetSettings(); - $new_css = $new_settings['ajaxPageState']['css']; - $new_js = $new_settings['ajaxPageState']['js']; - - // Verify the expected setting was added. - $this->assertIdentical($new_settings[$expected['setting_name']], $expected['setting_value'], t('Page now has the %setting.', array('%setting' => $expected['setting_name']))); - - // Verify the expected CSS file was added, both to Drupal.settings, and as - // an AJAX command for inclusion into the HTML. - // @todo A drupal_css_defaults() function in Drupal 8 would be nice. - $expected_css_html = drupal_get_css(array($expected['css'] => array( - 'type' => 'file', - 'group' => CSS_DEFAULT, - 'weight' => 0, - 'every_page' => FALSE, - 'media' => 'all', - 'preprocess' => TRUE, - 'data' => $expected['css'], - 'browsers' => array('IE' => TRUE, '!IE' => TRUE), - )), TRUE); - $this->assertEqual($new_css, $original_css + array($expected['css'] => 1), t('Page state now has the %css file.', array('%css' => $expected['css']))); - $this->assertCommand($commands, array('data' => $expected_css_html), t('Page now has the %css file.', array('%css' => $expected['css']))); - - // Verify the expected JS file was added, both to Drupal.settings, and as - // an AJAX command for inclusion into the HTML. By testing for an exact HTML - // string containing the SCRIPT tag, we also ensure that unexpected - // JavaScript code, such as a jQuery.extend() that would potentially clobber - // rather than properly merge settings, didn't accidentally get added. - $expected_js_html = drupal_get_js('header', array($expected['js'] => drupal_js_defaults($expected['js'])), TRUE); - $this->assertEqual($new_js, $original_js + array($expected['js'] => 1), t('Page state now has the %js file.', array('%js' => $expected['js']))); - $this->assertCommand($commands, array('data' => $expected_js_html), t('Page now has the %js file.', array('%js' => $expected['js']))); - } -} - -/** - * Tests Ajax framework commands. - */ -class AJAXCommandsTestCase extends AJAXTestCase { - public static function getInfo() { - return array( - 'name' => 'AJAX commands', - 'description' => 'Performs tests on AJAX framework commands.', - 'group' => 'AJAX', - ); - } - - /** - * Test the various Ajax Commands. - */ - function testAJAXCommands() { - $form_path = 'ajax_forms_test_ajax_commands_form'; - $web_user = $this->drupalCreateUser(array('access content')); - $this->drupalLogin($web_user); - - $edit = array(); - - // Tests the 'after' command. - $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'After': Click to put something after the div"))); - $expected = array( - 'command' => 'insert', - 'method' => 'after', - 'data' => 'This will be placed after', - ); - $this->assertCommand($commands, $expected, "'after' AJAX command issued with correct data"); - - // Tests the 'alert' command. - $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'Alert': Click to alert"))); - $expected = array( - 'command' => 'alert', - 'text' => 'Alert', - ); - $this->assertCommand($commands, $expected, "'alert' AJAX Command issued with correct text"); - - // Tests the 'append' command. - $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'Append': Click to append something"))); - $expected = array( - 'command' => 'insert', - 'method' => 'append', - 'data' => 'Appended text', - ); - $this->assertCommand($commands, $expected, "'append' AJAX command issued with correct data"); - - // Tests the 'before' command. - $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'before': Click to put something before the div"))); - $expected = array( - 'command' => 'insert', - 'method' => 'before', - 'data' => 'Before text', - ); - $this->assertCommand($commands, $expected, "'before' AJAX command issued with correct data"); - - // Tests the 'changed' command. - $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX changed: Click to mark div changed."))); - $expected = array( - 'command' => 'changed', - 'selector' => '#changed_div', - ); - $this->assertCommand($commands, $expected, "'changed' AJAX command issued with correct selector"); - - // Tests the 'changed' command using the second argument. - $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX changed: Click to mark div changed with asterisk."))); - $expected = array( - 'command' => 'changed', - 'selector' => '#changed_div', - 'asterisk' => '#changed_div_mark_this', - ); - $this->assertCommand($commands, $expected, "'changed' AJAX command (with asterisk) issued with correct selector"); - - // Tests the 'css' command. - $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("Set the the '#box' div to be blue."))); - $expected = array( - 'command' => 'css', - 'selector' => '#css_div', - 'argument' => array('background-color' => 'blue'), - ); - $this->assertCommand($commands, $expected, "'css' AJAX command issued with correct selector"); - - // Tests the 'data' command. - $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX data command: Issue command."))); - $expected = array( - 'command' => 'data', - 'name' => 'testkey', - 'value' => 'testvalue', - ); - $this->assertCommand($commands, $expected, "'data' AJAX command issued with correct key and value"); - - // Tests the 'invoke' command. - $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX invoke command: Invoke addClass() method."))); - $expected = array( - 'command' => 'invoke', - 'method' => 'addClass', - 'arguments' => array('error'), - ); - $this->assertCommand($commands, $expected, "'invoke' AJAX command issued with correct method and argument"); - - // Tests the 'html' command. - $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX html: Replace the HTML in a selector."))); - $expected = array( - 'command' => 'insert', - 'method' => 'html', - 'data' => 'replacement text', - ); - $this->assertCommand($commands, $expected, "'html' AJAX command issued with correct data"); - - // Tests the 'insert' command. - $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX insert: Let client insert based on #ajax['method']."))); - $expected = array( - 'command' => 'insert', - 'data' => 'insert replacement text', - ); - $this->assertCommand($commands, $expected, "'insert' AJAX command issued with correct data"); - - // Tests the 'prepend' command. - $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'prepend': Click to prepend something"))); - $expected = array( - 'command' => 'insert', - 'method' => 'prepend', - 'data' => 'prepended text', - ); - $this->assertCommand($commands, $expected, "'prepend' AJAX command issued with correct data"); - - // Tests the 'remove' command. - $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'remove': Click to remove text"))); - $expected = array( - 'command' => 'remove', - 'selector' => '#remove_text', - ); - $this->assertCommand($commands, $expected, "'remove' AJAX command issued with correct command and selector"); - - // Tests the 'restripe' command. - $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'restripe' command"))); - $expected = array( - 'command' => 'restripe', - 'selector' => '#restripe_table', - ); - $this->assertCommand($commands, $expected, "'restripe' AJAX command issued with correct selector"); - - // Tests the 'settings' command. - $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'settings' command"))); - $expected = array( - 'command' => 'settings', - 'settings' => array('ajax_forms_test' => array('foo' => 42)), - ); - $this->assertCommand($commands, $expected, "'settings' AJAX command issued with correct data"); - } -} - -/** - * Test that $form_state['values'] is properly delivered to $ajax['callback']. - */ -class AJAXFormValuesTestCase extends AJAXTestCase { - public static function getInfo() { - return array( - 'name' => 'AJAX command form values', - 'description' => 'Tests that form values are properly delivered to AJAX callbacks.', - 'group' => 'AJAX', - ); - } - - function setUp() { - parent::setUp(); - - $this->web_user = $this->drupalCreateUser(array('access content')); - $this->drupalLogin($this->web_user); - } - - /** - * Create a simple form, then POST to system/ajax to change to it. - */ - function testSimpleAJAXFormValue() { - // Verify form values of a select element. - foreach (array('red', 'green', 'blue') as $item) { - $edit = array( - 'select' => $item, - ); - $commands = $this->drupalPostAJAX('ajax_forms_test_get_form', $edit, 'select'); - $expected = array( - 'command' => 'data', - 'value' => $item, - ); - $this->assertCommand($commands, $expected, "verification of AJAX form values from a selectbox issued with a correct value"); - } - - // Verify form values of a checkbox element. - foreach (array(FALSE, TRUE) as $item) { - $edit = array( - 'checkbox' => $item, - ); - $commands = $this->drupalPostAJAX('ajax_forms_test_get_form', $edit, 'checkbox'); - $expected = array( - 'command' => 'data', - 'value' => (int) $item, - ); - $this->assertCommand($commands, $expected, "verification of AJAX form values from a checkbox issued with a correct value"); - } - } -} - -/** - * Tests that Ajax-enabled forms work when multiple instances of the same form are on a page. - */ -class AJAXMultiFormTestCase extends AJAXTestCase { - public static function getInfo() { - return array( - 'name' => 'AJAX multi form', - 'description' => 'Tests that AJAX-enabled forms work when multiple instances of the same form are on a page.', - 'group' => 'AJAX', - ); - } - - function setUp() { - parent::setUp(array('form_test')); - - // Create a multi-valued field for 'page' nodes to use for Ajax testing. - $field_name = 'field_ajax_test'; - $field = array( - 'field_name' => $field_name, - 'type' => 'text', - 'cardinality' => FIELD_CARDINALITY_UNLIMITED, - ); - field_create_field($field); - $instance = array( - 'field_name' => $field_name, - 'entity_type' => 'node', - 'bundle' => 'page', - ); - field_create_instance($instance); - - // Login a user who can create 'page' nodes. - $this->web_user = $this->drupalCreateUser(array('create page content')); - $this->drupalLogin($this->web_user); - } - - /** - * Test that a page with the 'page_node_form' included twice works correctly. - */ - function testMultiForm() { - // HTML IDs for elements within the field are potentially modified with - // each Ajax submission, but these variables are stable and help target the - // desired elements. - $field_name = 'field_ajax_test'; - $field_xpaths = array( - 'page-node-form' => '//form[@id="page-node-form"]//div[contains(@class, "field-name-field-ajax-test")]', - 'page-node-form--2' => '//form[@id="page-node-form--2"]//div[contains(@class, "field-name-field-ajax-test")]', - ); - $button_name = $field_name . '_add_more'; - $button_value = t('Add another item'); - $button_xpath_suffix = '//input[@name="' . $button_name . '"]'; - $field_items_xpath_suffix = '//input[@type="text"]'; - - // Ensure the initial page contains both node forms and the correct number - // of field items and "add more" button for the multi-valued field within - // each form. - $this->drupalGet('form-test/two-instances-of-same-form'); - foreach ($field_xpaths as $form_html_id => $field_xpath) { - $this->assert(count($this->xpath($field_xpath . $field_items_xpath_suffix)) == 1, t('Found the correct number of field items on the initial page.')); - $this->assertFieldByXPath($field_xpath . $button_xpath_suffix, NULL, t('Found the "add more" button on the initial page.')); - } - $this->assertNoDuplicateIds(t('Initial page contains unique IDs'), 'Other'); - - // Submit the "add more" button of each form twice. After each corresponding - // page update, ensure the same as above. - foreach ($field_xpaths as $form_html_id => $field_xpath) { - for ($i = 0; $i < 2; $i++) { - $this->drupalPostAJAX(NULL, array(), array($button_name => $button_value), 'system/ajax', array(), array(), $form_html_id); - $this->assert(count($this->xpath($field_xpath . $field_items_xpath_suffix)) == $i+2, t('Found the correct number of field items after an AJAX submission.')); - $this->assertFieldByXPath($field_xpath . $button_xpath_suffix, NULL, t('Found the "add more" button after an AJAX submission.')); - $this->assertNoDuplicateIds(t('Updated page contains unique IDs'), 'Other'); - } - } - } -} - -/** - * Miscellaneous Ajax tests using ajax_test module. - */ -class AJAXElementValidation extends AJAXTestCase { - public static function getInfo() { - return array( - 'name' => 'Miscellaneous AJAX tests', - 'description' => 'Various tests of AJAX behavior', - 'group' => 'AJAX', - ); - } - - /** - * Try to post an Ajax change to a form that has a validated element. - * - * The drivertext field is Ajax-enabled. An additional field is not, but - * is set to be a required field. In this test the required field is not - * filled in, and we want to see if the activation of the "drivertext" - * Ajax-enabled field fails due to the required field being empty. - */ - function testAJAXElementValidation() { - $web_user = $this->drupalCreateUser(); - $edit = array('drivertext' => t('some dumb text')); - - // Post with 'drivertext' as the triggering element. - $post_result = $this->drupalPostAJAX('ajax_validation_test', $edit, 'drivertext'); - // Look for a validation failure in the resultant JSON. - $this->assertNoText(t('Error message'), t("No error message in resultant JSON")); - $this->assertText('ajax_forms_test_validation_form_callback invoked', t('The correct callback was invoked')); - } -} diff --git a/modules/simpletest/tests/ajax_forms_test.info b/modules/simpletest/tests/ajax_forms_test.info deleted file mode 100644 index 987ee25afcd..00000000000 --- a/modules/simpletest/tests/ajax_forms_test.info +++ /dev/null @@ -1,6 +0,0 @@ -name = "AJAX form test mock module" -description = "Test for AJAX form calls." -core = 8.x -package = Testing -version = VERSION -hidden = TRUE diff --git a/modules/simpletest/tests/ajax_forms_test.module b/modules/simpletest/tests/ajax_forms_test.module deleted file mode 100644 index 075b005eaee..00000000000 --- a/modules/simpletest/tests/ajax_forms_test.module +++ /dev/null @@ -1,500 +0,0 @@ -<?php - -/** - * @file - * Simpletest mock module for Ajax forms testing. - */ - -/** - * Implements hook_menu(). - * @return unknown_type - */ -function ajax_forms_test_menu() { - $items = array(); - $items['ajax_forms_test_get_form'] = array( - 'title' => 'AJAX forms simple form test', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('ajax_forms_test_simple_form'), - 'access callback' => TRUE, - ); - $items['ajax_forms_test_ajax_commands_form'] = array( - 'title' => 'AJAX forms AJAX commands test', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('ajax_forms_test_ajax_commands_form'), - 'access callback' => TRUE, - ); - $items['ajax_validation_test'] = array( - 'title' => 'AJAX Validation Test', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('ajax_forms_test_validation_form'), - 'access callback' => TRUE, - ); - $items['ajax_forms_test_lazy_load_form'] = array( - 'title' => 'AJAX forms lazy load test', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('ajax_forms_test_lazy_load_form'), - 'access callback' => TRUE, - ); - return $items; -} - - -/** - * A basic form used to test form_state['values'] during callback. - */ -function ajax_forms_test_simple_form($form, &$form_state) { - $form = array(); - $form['select'] = array( - '#type' => 'select', - '#options' => array( - 'red' => 'red', - 'green' => 'green', - 'blue' => 'blue'), - '#ajax' => array( - 'callback' => 'ajax_forms_test_simple_form_select_callback', - ), - '#suffix' => '<div id="ajax_selected_color">No color yet selected</div>', - ); - - $form['checkbox'] = array( - '#type' => 'checkbox', - '#title' => t('Test checkbox'), - '#ajax' => array( - 'callback' => 'ajax_forms_test_simple_form_checkbox_callback', - ), - '#suffix' => '<div id="ajax_checkbox_value">No action yet</div>', - ); - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('submit'), - ); - return $form; -} - -/** - * Ajax callback triggered by select. - */ -function ajax_forms_test_simple_form_select_callback($form, $form_state) { - $commands = array(); - $commands[] = ajax_command_html('#ajax_selected_color', $form_state['values']['select']); - $commands[] = ajax_command_data('#ajax_selected_color', 'form_state_value_select', $form_state['values']['select']); - return array('#type' => 'ajax', '#commands' => $commands); -} - -/** - * Ajax callback triggered by checkbox. - */ -function ajax_forms_test_simple_form_checkbox_callback($form, $form_state) { - $commands = array(); - $commands[] = ajax_command_html('#ajax_checkbox_value', (int) $form_state['values']['checkbox']); - $commands[] = ajax_command_data('#ajax_checkbox_value', 'form_state_value_select', (int) $form_state['values']['checkbox']); - return array('#type' => 'ajax', '#commands' => $commands); -} - - -/** - * Form to display the Ajax Commands. - * @param $form - * @param $form_state - * @return unknown_type - */ -function ajax_forms_test_ajax_commands_form($form, &$form_state) { - $form = array(); - - // Shows the 'after' command with a callback generating commands. - $form['after_command_example'] = array( - '#value' => t("AJAX 'After': Click to put something after the div"), - '#type' => 'submit', - '#ajax' => array( - 'callback' => 'ajax_forms_test_advanced_commands_after_callback', - ), - '#suffix' => '<div id="after_div">Something can be inserted after this</div>', - ); - - // Shows the 'alert' command. - $form['alert_command_example'] = array( - '#value' => t("AJAX 'Alert': Click to alert"), - '#type' => 'submit', - '#ajax' => array( - 'callback' => 'ajax_forms_test_advanced_commands_alert_callback', - ), - ); - - // Shows the 'append' command. - $form['append_command_example'] = array( - '#value' => t("AJAX 'Append': Click to append something"), - '#type' => 'submit', - '#ajax' => array( - 'callback' => 'ajax_forms_test_advanced_commands_append_callback', - ), - '#suffix' => '<div id="append_div">Append inside this div</div>', - ); - - - // Shows the 'before' command. - $form['before_command_example'] = array( - '#value' => t("AJAX 'before': Click to put something before the div"), - '#type' => 'submit', - '#ajax' => array( - 'callback' => 'ajax_forms_test_advanced_commands_before_callback', - ), - '#suffix' => '<div id="before_div">Insert something before this.</div>', - ); - - // Shows the 'changed' command without asterisk. - $form['changed_command_example'] = array( - '#value' => t("AJAX changed: Click to mark div changed."), - '#type' => 'submit', - '#ajax' => array( - 'callback' => 'ajax_forms_test_advanced_commands_changed_callback', - ), - '#suffix' => '<div id="changed_div"> <div id="changed_div_mark_this">This div can be marked as changed or not.</div></div>', - ); - // Shows the 'changed' command adding the asterisk. - $form['changed_command_asterisk_example'] = array( - '#value' => t("AJAX changed: Click to mark div changed with asterisk."), - '#type' => 'submit', - '#ajax' => array( - 'callback' => 'ajax_forms_test_advanced_commands_changed_asterisk_callback', - ), - ); - - // Shows the Ajax 'css' command. - $form['css_command_example'] = array( - '#value' => t("Set the the '#box' div to be blue."), - '#type' => 'submit', - '#ajax' => array( - 'callback' => 'ajax_forms_test_advanced_commands_css_callback', - ), - '#suffix' => '<div id="css_div" style="height: 50px; width: 50px; border: 1px solid black"> box</div>', - ); - - - // Shows the Ajax 'data' command. But there is no use of this information, - // as this would require a javascript client to use the data. - $form['data_command_example'] = array( - '#value' => t("AJAX data command: Issue command."), - '#type' => 'submit', - '#ajax' => array( - 'callback' => 'ajax_forms_test_advanced_commands_data_callback', - ), - '#suffix' => '<div id="data_div">Data attached to this div.</div>', - ); - - // Shows the Ajax 'invoke' command. - $form['invoke_command_example'] = array( - '#value' => t("AJAX invoke command: Invoke addClass() method."), - '#type' => 'submit', - '#ajax' => array( - 'callback' => 'ajax_forms_test_advanced_commands_invoke_callback', - ), - '#suffix' => '<div id="invoke_div">Original contents</div>', - ); - - // Shows the Ajax 'html' command. - $form['html_command_example'] = array( - '#value' => t("AJAX html: Replace the HTML in a selector."), - '#type' => 'submit', - '#ajax' => array( - 'callback' => 'ajax_forms_test_advanced_commands_html_callback', - ), - '#suffix' => '<div id="html_div">Original contents</div>', - ); - - // Shows the Ajax 'insert' command. - $form['insert_command_example'] = array( - '#value' => t("AJAX insert: Let client insert based on #ajax['method']."), - '#type' => 'submit', - '#ajax' => array( - 'callback' => 'ajax_forms_test_advanced_commands_insert_callback', - 'method' => 'prepend', - ), - '#suffix' => '<div id="insert_div">Original contents</div>', - ); - - // Shows the Ajax 'prepend' command. - $form['prepend_command_example'] = array( - '#value' => t("AJAX 'prepend': Click to prepend something"), - '#type' => 'submit', - '#ajax' => array( - 'callback' => 'ajax_forms_test_advanced_commands_prepend_callback', - ), - '#suffix' => '<div id="prepend_div">Something will be prepended to this div. </div>', - ); - - // Shows the Ajax 'remove' command. - $form['remove_command_example'] = array( - '#value' => t("AJAX 'remove': Click to remove text"), - '#type' => 'submit', - '#ajax' => array( - 'callback' => 'ajax_forms_test_advanced_commands_remove_callback', - ), - '#suffix' => '<div id="remove_div"><div id="remove_text">text to be removed</div></div>', - ); - - // Shows the Ajax 'restripe' command. - $form['restripe_command_example'] = array( - '#type' => 'submit', - '#value' => t("AJAX 'restripe' command"), - '#ajax' => array( - 'callback' => 'ajax_forms_test_advanced_commands_restripe_callback', - ), - '#suffix' => '<div id="restripe_div"> - <table id="restripe_table" style="border: 1px solid black" > - <tr id="table-first"><td>first row</td></tr> - <tr ><td>second row</td></tr> - </table> - </div>', - ); - - // Demonstrates the Ajax 'settings' command. The 'settings' command has - // nothing visual to "show", but it can be tested via SimpleTest and via - // Firebug. - $form['settings_command_example'] = array( - '#type' => 'submit', - '#value' => t("AJAX 'settings' command"), - '#ajax' => array( - 'callback' => 'ajax_forms_test_advanced_commands_settings_callback', - ), - ); - - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Submit'), - ); - - return $form; -} - -/** - * Ajax callback for 'after'. - */ -function ajax_forms_test_advanced_commands_after_callback($form, $form_state) { - $selector = '#after_div'; - - $commands = array(); - $commands[] = ajax_command_after($selector, "This will be placed after"); - return array('#type' => 'ajax', '#commands' => $commands); -} - -/** - * Ajax callback for 'alert'. - */ -function ajax_forms_test_advanced_commands_alert_callback($form, $form_state) { - $commands = array(); - $commands[] = ajax_command_alert("Alert"); - return array('#type' => 'ajax', '#commands' => $commands); -} - -/** - * Ajax callback for 'append'. - */ -function ajax_forms_test_advanced_commands_append_callback($form, $form_state) { - $selector = '#append_div'; - $commands = array(); - $commands[] = ajax_command_append($selector, "Appended text"); - return array('#type' => 'ajax', '#commands' => $commands); -} - -/** - * Ajax callback for 'before'. - */ -function ajax_forms_test_advanced_commands_before_callback($form, $form_state) { - $selector = '#before_div'; - - $commands = array(); - $commands[] = ajax_command_before($selector, "Before text"); - return array('#type' => 'ajax', '#commands' => $commands); -} - -/** - * Ajax callback for 'changed'. - */ -function ajax_forms_test_advanced_commands_changed_callback($form, $form_state) { - $commands[] = ajax_command_changed('#changed_div'); - return array('#type' => 'ajax', '#commands' => $commands); -} -/** - * Ajax callback for 'changed' with asterisk marking inner div. - */ -function ajax_forms_test_advanced_commands_changed_asterisk_callback($form, $form_state) { - $commands = array(); - $commands[] = ajax_command_changed('#changed_div', '#changed_div_mark_this'); - return array('#type' => 'ajax', '#commands' => $commands); -} - -/** - * Ajax callback for 'css'. - */ -function ajax_forms_test_advanced_commands_css_callback($form, $form_state) { - $selector = '#css_div'; - $color = 'blue'; - - $commands = array(); - $commands[] = ajax_command_css($selector, array('background-color' => $color)); - return array('#type' => 'ajax', '#commands' => $commands); -} - -/** - * Ajax callback for 'data'. - */ -function ajax_forms_test_advanced_commands_data_callback($form, $form_state) { - $selector = '#data_div'; - - $commands = array(); - $commands[] = ajax_command_data($selector, 'testkey', 'testvalue'); - return array('#type' => 'ajax', '#commands' => $commands); -} - -/** - * Ajax callback for 'invoke'. - */ -function ajax_forms_test_advanced_commands_invoke_callback($form, $form_state) { - $commands = array(); - $commands[] = ajax_command_invoke('#invoke_div', 'addClass', array('error')); - return array('#type' => 'ajax', '#commands' => $commands); -} - -/** - * Ajax callback for 'html'. - */ -function ajax_forms_test_advanced_commands_html_callback($form, $form_state) { - $commands = array(); - $commands[] = ajax_command_html('#html_div', 'replacement text'); - return array('#type' => 'ajax', '#commands' => $commands); -} - -/** - * Ajax callback for 'insert'. - */ -function ajax_forms_test_advanced_commands_insert_callback($form, $form_state) { - $commands = array(); - $commands[] = ajax_command_insert('#insert_div', 'insert replacement text'); - return array('#type' => 'ajax', '#commands' => $commands); -} - -/** - * Ajax callback for 'prepend'. - */ -function ajax_forms_test_advanced_commands_prepend_callback($form, $form_state) { - $commands = array(); - $commands[] = ajax_command_prepend('#prepend_div', "prepended text"); - return array('#type' => 'ajax', '#commands' => $commands); -} - -/** - * Ajax callback for 'remove'. - */ -function ajax_forms_test_advanced_commands_remove_callback($form, $form_state) { - $commands = array(); - $commands[] = ajax_command_remove('#remove_text'); - return array('#type' => 'ajax', '#commands' => $commands); -} - -/** - * Ajax callback for 'restripe'. - */ -function ajax_forms_test_advanced_commands_restripe_callback($form, $form_state) { - $commands = array(); - $commands[] = ajax_command_restripe('#restripe_table'); - return array('#type' => 'ajax', '#commands' => $commands); -} - -/** - * Ajax callback for 'settings'. - */ -function ajax_forms_test_advanced_commands_settings_callback($form, $form_state) { - $commands = array(); - $setting['ajax_forms_test']['foo'] = 42; - $commands[] = ajax_command_settings($setting); - return array('#type' => 'ajax', '#commands' => $commands); -} - -/** - * This form and its related submit and callback functions demonstrate - * not validating another form element when a single Ajax element is triggered. - * - * The "drivertext" element is an Ajax-enabled textfield, free-form. - * The "required_field" element is a textfield marked required. - * - * The correct behavior is that the Ajax-enabled drivertext element should - * be able to trigger without causing validation of the "required_field". - */ -function ajax_forms_test_validation_form($form, &$form_state) { - - $form['drivertext'] = array( - '#title' => t('AJAX-enabled textfield.'), - '#description' => t("When this one AJAX-triggers and the spare required field is empty, you should not get an error."), - '#type' => 'textfield', - '#default_value' => !empty($form_state['values']['drivertext']) ? $form_state['values']['drivertext'] : "", - '#ajax' => array( - 'callback' => 'ajax_forms_test_validation_form_callback', - 'wrapper' => 'message_area', - 'method' => 'replace', - ), - '#suffix' => '<div id="message_area"></div>', - ); - - $form['spare_required_field'] = array( - '#title' => t("Spare Required Field"), - '#type' => 'textfield', - '#required' => TRUE, - ); - - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Submit'), - ); - - return $form; -} -/** - * Submit handler for the validation form. - */ -function ajax_forms_test_validation_form_submit($form, $form_state) { - drupal_set_message(t("Validation form submitted")); -} - -/** - * Ajax callback for the 'drivertext' element of the validation form. - */ -function ajax_forms_test_validation_form_callback($form, $form_state) { - drupal_set_message("ajax_forms_test_validation_form_callback invoked"); - drupal_set_message(t("Callback: drivertext=%drivertext, spare_required_field=%spare_required_field", array('%drivertext' => $form_state['values']['drivertext'], '%spare_required_field' => $form_state['values']['spare_required_field']))); - return '<div id="message_area">ajax_forms_test_validation_form_callback at ' . date('c') . '</div>'; -} - -/** - * Form builder: Builds a form that triggers a simple AJAX callback. - */ -function ajax_forms_test_lazy_load_form($form, &$form_state) { - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Submit'), - '#ajax' => array( - 'callback' => 'ajax_forms_test_lazy_load_form_ajax', - ), - ); - return $form; -} - -/** - * Form submit handler: Adds JavaScript and CSS that wasn't on the original form. - */ -function ajax_forms_test_lazy_load_form_submit($form, &$form_state) { - drupal_add_js(array('ajax_forms_test_lazy_load_form_submit' => 'executed'), 'setting'); - drupal_add_css(drupal_get_path('module', 'system') . '/system.admin.css'); - drupal_add_js(drupal_get_path('module', 'system') . '/system.js'); - $form_state['rebuild'] = TRUE; -} - -/** - * AJAX callback for the ajax_forms_test_lazy_load_form() form. - * - * This function returns nothing, because all we're interested in testing is - * ajax_render() adding commands for JavaScript and CSS added during the page - * request, such as the ones added in ajax_forms_test_lazy_load_form_submit(). - */ -function ajax_forms_test_lazy_load_form_ajax($form, &$form_state) { - return NULL; -} diff --git a/modules/simpletest/tests/ajax_test.info b/modules/simpletest/tests/ajax_test.info deleted file mode 100644 index dda7f55e0d2..00000000000 --- a/modules/simpletest/tests/ajax_test.info +++ /dev/null @@ -1,6 +0,0 @@ -name = AJAX Test -description = Support module for AJAX framework tests. -package = Testing -version = VERSION -core = 8.x -hidden = TRUE diff --git a/modules/simpletest/tests/ajax_test.module b/modules/simpletest/tests/ajax_test.module deleted file mode 100644 index 4148a0839fb..00000000000 --- a/modules/simpletest/tests/ajax_test.module +++ /dev/null @@ -1,71 +0,0 @@ -<?php - -/** - * @file - * Helper module for Ajax framework tests. - */ - -/** - * Implements hook_menu(). - */ -function ajax_test_menu() { - $items['ajax-test/render'] = array( - 'title' => 'ajax_render', - 'page callback' => 'ajax_test_render', - 'delivery callback' => 'ajax_deliver', - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); - $items['ajax-test/render-error'] = array( - 'title' => 'ajax_render_error', - 'page callback' => 'ajax_test_error', - 'delivery callback' => 'ajax_deliver', - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); - $items['ajax-test/link'] = array( - 'title' => 'AJAX Link', - 'page callback' => 'ajax_test_link', - 'access callback' => TRUE, - ); - return $items; -} - -/** - * Menu callback; Return an element suitable for use by ajax_deliver(). - * - * Additionally ensures that ajax_render() incorporates JavaScript settings - * generated during the page request by invoking drupal_add_js() with a dummy - * setting. - */ -function ajax_test_render() { - drupal_add_js(array('ajax' => 'test'), 'setting'); - return array('#type' => 'ajax', '#commands' => array()); -} - -/** - * Menu callback; Returns Ajax element with #error property set. - */ -function ajax_test_error() { - $message = ''; - if (!empty($_GET['message'])) { - $message = $_GET['message']; - } - return array('#type' => 'ajax', '#error' => $message); -} - -/** - * Menu callback; Renders a #type link with #ajax. - */ -function ajax_test_link() { - $build['link'] = array( - '#type' => 'link', - '#title' => 'Show help', - '#href' => 'filter/tips', - '#ajax' => array( - 'wrapper' => 'block-system-main', - ), - ); - return $build; -} - diff --git a/modules/simpletest/tests/batch.test b/modules/simpletest/tests/batch.test deleted file mode 100644 index f668e522805..00000000000 --- a/modules/simpletest/tests/batch.test +++ /dev/null @@ -1,405 +0,0 @@ -<?php - -/** - * @file - * Tests for the Batch API. - */ - -/** - * Tests for the Batch API. - */ -class BatchProcessingTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Batch processing', - 'description' => 'Test batch processing in form and non-form workflow.', - 'group' => 'Batch API', - ); - } - - function setUp() { - parent::setUp('batch_test'); - } - - /** - * Test batches triggered outside of form submission. - */ - function testBatchNoForm() { - // Displaying the page triggers batch 1. - $this->drupalGet('batch-test/no-form'); - $this->assertBatchMessages($this->_resultMessages(1), t('Batch for step 2 performed successfully.')); - $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), t('Execution order was correct.')); - $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.')); - } - - /** - * Test batches defined in a form submit handler. - */ - function testBatchForm() { - // Batch 0: no operation. - $edit = array('batch' => 'batch_0'); - $this->drupalPost('batch-test/simple', $edit, 'Submit'); - $this->assertBatchMessages($this->_resultMessages('batch_0'), t('Batch with no operation performed successfully.')); - $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.')); - - // Batch 1: several simple operations. - $edit = array('batch' => 'batch_1'); - $this->drupalPost('batch-test/simple', $edit, 'Submit'); - $this->assertBatchMessages($this->_resultMessages('batch_1'), t('Batch with simple operations performed successfully.')); - $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), t('Execution order was correct.')); - $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.')); - - // Batch 2: one multistep operation. - $edit = array('batch' => 'batch_2'); - $this->drupalPost('batch-test/simple', $edit, 'Submit'); - $this->assertBatchMessages($this->_resultMessages('batch_2'), t('Batch with multistep operation performed successfully.')); - $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_2'), t('Execution order was correct.')); - $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.')); - - // Batch 3: simple + multistep combined. - $edit = array('batch' => 'batch_3'); - $this->drupalPost('batch-test/simple', $edit, 'Submit'); - $this->assertBatchMessages($this->_resultMessages('batch_3'), t('Batch with simple and multistep operations performed successfully.')); - $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_3'), t('Execution order was correct.')); - $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.')); - - // Batch 4: nested batch. - $edit = array('batch' => 'batch_4'); - $this->drupalPost('batch-test/simple', $edit, 'Submit'); - $this->assertBatchMessages($this->_resultMessages('batch_4'), t('Nested batch performed successfully.')); - $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_4'), t('Execution order was correct.')); - $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.')); - } - - /** - * Test batches defined in a multistep form. - */ - function testBatchFormMultistep() { - $this->drupalGet('batch-test/multistep'); - $this->assertText('step 1', t('Form is displayed in step 1.')); - - // First step triggers batch 1. - $this->drupalPost(NULL, array(), 'Submit'); - $this->assertBatchMessages($this->_resultMessages('batch_1'), t('Batch for step 1 performed successfully.')); - $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), t('Execution order was correct.')); - $this->assertText('step 2', t('Form is displayed in step 2.')); - - // Second step triggers batch 2. - $this->drupalPost(NULL, array(), 'Submit'); - $this->assertBatchMessages($this->_resultMessages('batch_2'), t('Batch for step 2 performed successfully.')); - $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_2'), t('Execution order was correct.')); - $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.')); - } - - /** - * Test batches defined in different submit handlers on the same form. - */ - function testBatchFormMultipleBatches() { - // Batches 1, 2 and 3 are triggered in sequence by different submit - // handlers. Each submit handler modify the submitted 'value'. - $value = rand(0, 255); - $edit = array('value' => $value); - $this->drupalPost('batch-test/chained', $edit, 'Submit'); - // Check that result messages are present and in the correct order. - $this->assertBatchMessages($this->_resultMessages('chained'), t('Batches defined in separate submit handlers performed successfully.')); - // The stack contains execution order of batch callbacks and submit - // hanlders and logging of corresponding $form_state[{values']. - $this->assertEqual(batch_test_stack(), $this->_resultStack('chained', $value), t('Execution order was correct, and $form_state is correctly persisted.')); - $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.')); - } - - /** - * Test batches defined in a programmatically submitted form. - * - * Same as above, but the form is submitted through drupal_form_execute(). - */ - function testBatchFormProgrammatic() { - // Batches 1, 2 and 3 are triggered in sequence by different submit - // handlers. Each submit handler modify the submitted 'value'. - $value = rand(0, 255); - $this->drupalGet('batch-test/programmatic/' . $value); - // Check that result messages are present and in the correct order. - $this->assertBatchMessages($this->_resultMessages('chained'), t('Batches defined in separate submit handlers performed successfully.')); - // The stack contains execution order of batch callbacks and submit - // hanlders and logging of corresponding $form_state[{values']. - $this->assertEqual(batch_test_stack(), $this->_resultStack('chained', $value), t('Execution order was correct, and $form_state is correctly persisted.')); - $this->assertText('Got out of a programmatic batched form.', t('Page execution continues normally.')); - } - - /** - * Test that drupal_form_submit() can run within a batch operation. - */ - function testDrupalFormSubmitInBatch() { - // Displaying the page triggers a batch that programmatically submits a - // form. - $value = rand(0, 255); - $this->drupalGet('batch-test/nested-programmatic/' . $value); - $this->assertEqual(batch_test_stack(), array('mock form submitted with value = ' . $value), t('drupal_form_submit() ran successfully within a batch operation.')); - } - - /** - * Test batches that return $context['finished'] > 1 do in fact complete. - * See http://drupal.org/node/600836 - */ - function testBatchLargePercentage() { - // Displaying the page triggers batch 5. - $this->drupalGet('batch-test/large-percentage'); - $this->assertBatchMessages($this->_resultMessages(1), t('Batch for step 2 performed successfully.')); - $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_5'), t('Execution order was correct.')); - $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.')); - } - - - /** - * Will trigger a pass if the texts were found in order in the raw content. - * - * @param $texts - * Array of raw strings to look for . - * @param $message - * Message to display. - * @return - * TRUE on pass, FALSE on fail. - */ - function assertBatchMessages($texts, $message) { - $pattern = '|' . implode('.*', $texts) .'|s'; - return $this->assertPattern($pattern, $message); - } - - /** - * Helper function: return expected execution stacks for the test batches. - */ - function _resultStack($id, $value = 0) { - $stack = array(); - switch ($id) { - case 'batch_1': - for ($i = 1; $i <= 10; $i++) { - $stack[] = "op 1 id $i"; - } - break; - - case 'batch_2': - for ($i = 1; $i <= 10; $i++) { - $stack[] = "op 2 id $i"; - } - break; - - case 'batch_3': - for ($i = 1; $i <= 5; $i++) { - $stack[] = "op 1 id $i"; - } - for ($i = 1; $i <= 5; $i++) { - $stack[] = "op 2 id $i"; - } - for ($i = 6; $i <= 10; $i++) { - $stack[] = "op 1 id $i"; - } - for ($i = 6; $i <= 10; $i++) { - $stack[] = "op 2 id $i"; - } - break; - - case 'batch_4': - for ($i = 1; $i <= 5; $i++) { - $stack[] = "op 1 id $i"; - } - $stack[] = 'setting up batch 2'; - for ($i = 6; $i <= 10; $i++) { - $stack[] = "op 1 id $i"; - } - $stack = array_merge($stack, $this->_resultStack('batch_2')); - break; - - case 'batch_5': - for ($i = 1; $i <= 10; $i++) { - $stack[] = "op 5 id $i"; - } - break; - - case 'chained': - $stack[] = 'submit handler 1'; - $stack[] = 'value = ' . $value; - $stack = array_merge($stack, $this->_resultStack('batch_1')); - $stack[] = 'submit handler 2'; - $stack[] = 'value = ' . ($value + 1); - $stack = array_merge($stack, $this->_resultStack('batch_2')); - $stack[] = 'submit handler 3'; - $stack[] = 'value = ' . ($value + 2); - $stack[] = 'submit handler 4'; - $stack[] = 'value = ' . ($value + 3); - $stack = array_merge($stack, $this->_resultStack('batch_3')); - break; - } - return $stack; - } - - /** - * Helper function: return expected result messages for the test batches. - */ - function _resultMessages($id) { - $messages = array(); - - switch ($id) { - case 'batch_0': - $messages[] = 'results for batch 0<br />none'; - break; - - case 'batch_1': - $messages[] = 'results for batch 1<br />op 1: processed 10 elements'; - break; - - case 'batch_2': - $messages[] = 'results for batch 2<br />op 2: processed 10 elements'; - break; - - case 'batch_3': - $messages[] = 'results for batch 3<br />op 1: processed 10 elements<br />op 2: processed 10 elements'; - break; - - case 'batch_4': - $messages[] = 'results for batch 4<br />op 1: processed 10 elements'; - $messages = array_merge($messages, $this->_resultMessages('batch_2')); - break; - - case 'batch_5': - $messages[] = 'results for batch 5<br />op 1: processed 10 elements. $context[\'finished\'] > 1 returned from batch process, with success.'; - break; - - case 'chained': - $messages = array_merge($messages, $this->_resultMessages('batch_1')); - $messages = array_merge($messages, $this->_resultMessages('batch_2')); - $messages = array_merge($messages, $this->_resultMessages('batch_3')); - break; - } - return $messages; - } -} - -/** - * Tests for the Batch API Progress page. - */ -class BatchPageTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Batch progress page', - 'description' => 'Test the content of the progress page.', - 'group' => 'Batch API', - ); - } - - function setUp() { - parent::setUp('batch_test'); - } - - /** - * Tests that the batch API progress page uses the correct theme. - */ - function testBatchProgressPageTheme() { - // Make sure that the page which starts the batch (an administrative page) - // is using a different theme than would normally be used by the batch API. - variable_set('theme_default', 'bartik'); - variable_set('admin_theme', 'seven'); - // Log in as an administrator who can see the administrative theme. - $admin_user = $this->drupalCreateUser(array('view the administration theme')); - $this->drupalLogin($admin_user); - // Visit an administrative page that runs a test batch, and check that the - // theme that was used during batch execution (which the batch callback - // function saved as a variable) matches the theme used on the - // administrative page. - $this->drupalGet('admin/batch-test/test-theme'); - // The stack should contain the name of the theme used on the progress - // page. - $this->assertEqual(batch_test_stack(), array('seven'), t('A progressive batch correctly uses the theme of the page that started the batch.')); - } -} - -/** - * Tests the function _batch_api_percentage() to make sure that the rounding - * works properly in all cases. - */ -class BatchPercentagesUnitTestCase extends DrupalUnitTestCase { - protected $testCases = array(); - - public static function getInfo() { - return array( - 'name' => 'Batch percentages', - 'description' => 'Unit tests of progress percentage rounding.', - 'group' => 'Batch API', - ); - } - - function setUp() { - // Set up an array of test cases, where the expected values are the keys, - // and the values are arrays with the keys 'total' and 'current', - // corresponding with the function parameters of _batch_api_percentage(). - $this->testCases = array( - // 1/2 is 50%. - '50' => array('total' => 2, 'current' => 1), - // Though we should never encounter a case where the current set is set - // 0, if we did, we should get 0%. - '0' => array('total' => 3, 'current' => 0), - // 1/3 is closer to 33% than to 34%. - '33' => array('total' => 3, 'current' => 1), - // 2/3 is closer to 67% than to 66%. - '67' => array('total' => 3, 'current' => 2), - // A full 3/3 should equal 100%. - '100' => array('total' => 3, 'current' => 3), - // 1/199 should round up to 1%. - '1' => array('total' => 199, 'current' => 1), - // 198/199 should round down to 99%. - '99' => array('total' => 199, 'current' => 198), - // 199/200 would have rounded up to 100%, which would give the false - // impression of being finished, so we add another digit and should get - // 99.5%. - '99.5' => array('total' => 200, 'current' => 199), - // The same logic holds for 1/200: we should get 0.5%. - '0.5' => array('total' => 200, 'current' => 1), - // Numbers that come out evenly, such as 50/200, should be forced to have - // extra digits for consistancy. - '25.0' => array('total' => 200, 'current' => 50), - // Regardless of number of digits we're using, 100% should always just be - // 100%. - '100' => array('total' => 200, 'current' => 200), - // 1998/1999 should similarly round down to 99.9%. - '99.9' => array('total' => 1999, 'current' => 1998), - // 1999/2000 should add another digit and go to 99.95%. - '99.95' => array('total' => 2000, 'current' => 1999), - // 19999/20000 should add yet another digit and go to 99.995%. - '99.995' => array('total' => 20000, 'current' => 19999), - // The next five test cases simulate a batch with a single operation - // ('total' equals 1) that takes several steps to complete. Within the - // operation, we imagine that there are 501 items to process, and 100 are - // completed during each step. The percentages we get back should be - // rounded the usual way for the first few passes (i.e., 20%, 40%, etc.), - // but for the last pass through, when 500 out of 501 items have been - // processed, we do not want to round up to 100%, since that would - // erroneously indicate that the processing is complete. - '20' => array('total' => 1, 'current' => 100/501), - '40' => array('total' => 1, 'current' => 200/501), - '60' => array('total' => 1, 'current' => 300/501), - '80' => array('total' => 1, 'current' => 400/501), - '99.8' => array('total' => 1, 'current' => 500/501), - ); - require_once DRUPAL_ROOT . '/includes/batch.inc'; - parent::setUp(); - } - - /** - * Test the _batch_api_percentage() function. - */ - function testBatchPercentages() { - foreach ($this->testCases as $expected_result => $arguments) { - // PHP sometimes casts numeric strings that are array keys to integers, - // cast them back here. - $expected_result = (string) $expected_result; - $total = $arguments['total']; - $current = $arguments['current']; - $actual_result = _batch_api_percentage($total, $current); - if ($actual_result === $expected_result) { - $this->pass(t('Expected the batch api percentage at the state @numerator/@denominator to be @expected%, and got @actual%.', array('@numerator' => $current, '@denominator' => $total, '@expected' => $expected_result, '@actual' => $actual_result))); - } - else { - $this->fail(t('Expected the batch api percentage at the state @numerator/@denominator to be @expected%, but got @actual%.', array('@numerator' => $current, '@denominator' => $total, '@expected' => $expected_result, '@actual' => $actual_result))); - } - } - } -} diff --git a/modules/simpletest/tests/batch_test.callbacks.inc b/modules/simpletest/tests/batch_test.callbacks.inc deleted file mode 100644 index 75e665533e9..00000000000 --- a/modules/simpletest/tests/batch_test.callbacks.inc +++ /dev/null @@ -1,141 +0,0 @@ -<?php - - -/** - * @file - * Batch callbacks for the Batch API tests. - */ - -/** - * Simple batch operation. - */ -function _batch_test_callback_1($id, $sleep, &$context) { - // No-op, but ensure the batch take a couple iterations. - // Batch needs time to run for the test, so sleep a bit. - usleep($sleep); - // Track execution, and store some result for post-processing in the - // 'finished' callback. - batch_test_stack("op 1 id $id"); - $context['results'][1][] = $id; -} - -/** - * Multistep batch operation. - */ -function _batch_test_callback_2($start, $total, $sleep, &$context) { - // Initialize context with progress information. - if (!isset($context['sandbox']['current'])) { - $context['sandbox']['current'] = $start; - $context['sandbox']['count'] = 0; - } - - // Process by groups of 5 (arbitrary value). - $limit = 5; - for ($i = 0; $i < $limit && $context['sandbox']['count'] < $total; $i++) { - // No-op, but ensure the batch take a couple iterations. - // Batch needs time to run for the test, so sleep a bit. - usleep($sleep); - // Track execution, and store some result for post-processing in the - // 'finished' callback. - $id = $context['sandbox']['current'] + $i; - batch_test_stack("op 2 id $id"); - $context['results'][2][] = $id; - - // Update progress information. - $context['sandbox']['count']++; - } - $context['sandbox']['current'] += $i; - - // Inform batch engine about progress. - if ($context['sandbox']['count'] != $total) { - $context['finished'] = $context['sandbox']['count'] / $total; - } -} - -/** - * Simple batch operation. - */ -function _batch_test_callback_5($id, $sleep, &$context) { - // No-op, but ensure the batch take a couple iterations. - // Batch needs time to run for the test, so sleep a bit. - usleep($sleep); - // Track execution, and store some result for post-processing in the - // 'finished' callback. - batch_test_stack("op 5 id $id"); - $context['results'][5][] = $id; - // This test is to test finished > 1 - $context['finished'] = 3.14; -} - -/** - * Batch operation setting up its own batch. - */ -function _batch_test_nested_batch_callback() { - batch_test_stack('setting up batch 2'); - batch_set(_batch_test_batch_2()); -} - -/** - * Common 'finished' callbacks for batches 1 to 4. - */ -function _batch_test_finished_helper($batch_id, $success, $results, $operations) { - $messages = array("results for batch $batch_id"); - if ($results) { - foreach ($results as $op => $op_results) { - $messages[] = 'op '. $op . ': processed ' . count($op_results) . ' elements'; - } - } - else { - $messages[] = 'none'; - } - - if (!$success) { - // A fatal error occurred during the processing. - $error_operation = reset($operations); - $messages[] = t('An error occurred while processing @op with arguments:<br/>@args', array('@op' => $error_operation[0], '@args' => print_r($error_operation[1], TRUE))); - } - - drupal_set_message(implode('<br />', $messages)); -} - -/** - * 'finished' callback for batch 0. - */ -function _batch_test_finished_0($success, $results, $operations) { - _batch_test_finished_helper(0, $success, $results, $operations); -} - -/** - * 'finished' callback for batch 1. - */ -function _batch_test_finished_1($success, $results, $operations) { - _batch_test_finished_helper(1, $success, $results, $operations); -} - -/** - * 'finished' callback for batch 2. - */ -function _batch_test_finished_2($success, $results, $operations) { - _batch_test_finished_helper(2, $success, $results, $operations); -} - -/** - * 'finished' callback for batch 3. - */ -function _batch_test_finished_3($success, $results, $operations) { - _batch_test_finished_helper(3, $success, $results, $operations); -} - -/** - * 'finished' callback for batch 4. - */ -function _batch_test_finished_4($success, $results, $operations) { - _batch_test_finished_helper(4, $success, $results, $operations); -} - -/** - * 'finished' callback for batch 5. - */ -function _batch_test_finished_5($success, $results, $operations) { - _batch_test_finished_helper(5, $success, $results, $operations); -} diff --git a/modules/simpletest/tests/batch_test.info b/modules/simpletest/tests/batch_test.info deleted file mode 100644 index cf2cc3081ee..00000000000 --- a/modules/simpletest/tests/batch_test.info +++ /dev/null @@ -1,6 +0,0 @@ -name = "Batch API test" -description = "Support module for Batch API tests." -package = Testing -version = VERSION -core = 8.x -hidden = TRUE diff --git a/modules/simpletest/tests/batch_test.module b/modules/simpletest/tests/batch_test.module deleted file mode 100644 index 1200e767dd7..00000000000 --- a/modules/simpletest/tests/batch_test.module +++ /dev/null @@ -1,513 +0,0 @@ -<?php - -/** - * @file - * Helper module for the Batch API tests. - */ - -/** - * Implement hook_menu(). - */ -function batch_test_menu() { - $items = array(); - - $items['batch-test'] = array( - 'title' => 'Batch test', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('batch_test_simple_form'), - 'access callback' => TRUE, - ); - // Simple form: one submit handler, setting a batch. - $items['batch-test/simple'] = array( - 'title' => 'Simple', - 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => 0, - ); - // Multistep form: two steps, each setting a batch. - $items['batch-test/multistep'] = array( - 'title' => 'Multistep', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('batch_test_multistep_form'), - 'access callback' => TRUE, - 'type' => MENU_LOCAL_TASK, - 'weight' => 1, - ); - // Chained form: four submit handlers, several of which set a batch. - $items['batch-test/chained'] = array( - 'title' => 'Chained', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('batch_test_chained_form'), - 'access callback' => TRUE, - 'type' => MENU_LOCAL_TASK, - 'weight' => 2, - ); - // Programmatic form: the page submits the 'Chained' form through - // drupal_form_submit(). - $items['batch-test/programmatic'] = array( - 'title' => 'Programmatic', - 'page callback' => 'batch_test_programmatic', - 'access callback' => TRUE, - 'type' => MENU_LOCAL_TASK, - 'weight' => 3, - ); - // No form: fire a batch simply by accessing a page. - $items['batch-test/no-form'] = array( - 'title' => 'Simple page', - 'page callback' => 'batch_test_no_form', - 'access callback' => TRUE, - 'type' => MENU_LOCAL_TASK, - 'weight' => 4, - ); - // No form: fire a batch; return > 100% complete - $items['batch-test/large-percentage'] = array( - 'title' => 'Simple page with batch over 100% complete', - 'page callback' => 'batch_test_large_percentage', - 'access callback' => TRUE, - 'type' => MENU_LOCAL_TASK, - 'weight' => 5, - ); - // Tests programmatic form submission within a batch operation. - $items['batch-test/nested-programmatic'] = array( - 'title' => 'Nested programmatic', - 'page callback' => 'batch_test_nested_drupal_form_submit', - 'access callback' => TRUE, - 'type' => MENU_LOCAL_TASK, - 'weight' => 6, - ); - // Landing page to test redirects. - $items['batch-test/redirect'] = array( - 'title' => 'Redirect', - 'page callback' => 'batch_test_redirect_page', - 'access callback' => TRUE, - 'type' => MENU_LOCAL_TASK, - 'weight' => 7, - ); - // This item lives under 'admin' so that the page uses the admin theme. - $items['admin/batch-test/test-theme'] = array( - 'page callback' => 'batch_test_theme_batch', - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); - - return $items; -} - -/** - * Simple form. - */ -function batch_test_simple_form() { - $form['batch'] = array( - '#type' => 'select', - '#title' => 'Choose batch', - '#options' => array( - 'batch_0' => 'batch 0', - 'batch_1' => 'batch 1', - 'batch_2' => 'batch 2', - 'batch_3' => 'batch 3', - 'batch_4' => 'batch 4', - ), - ); - $form['submit'] = array( - '#type' => 'submit', - '#value' => 'Submit', - ); - - return $form; -} - -/** - * Submit handler for the simple form. - */ -function batch_test_simple_form_submit($form, &$form_state) { - batch_test_stack(NULL, TRUE); - - $function = '_batch_test_' . $form_state['values']['batch']; - batch_set($function()); - - $form_state['redirect'] = 'batch-test/redirect'; -} - - -/** - * Multistep form. - */ -function batch_test_multistep_form($form, &$form_state) { - if (empty($form_state['storage']['step'])) { - $form_state['storage']['step'] = 1; - } - - $form['step_display'] = array( - '#markup' => 'step ' . $form_state['storage']['step'] . '<br/>', - ); - $form['submit'] = array( - '#type' => 'submit', - '#value' => 'Submit', - ); - - return $form; -} - -/** - * Submit handler for the multistep form. - */ -function batch_test_multistep_form_submit($form, &$form_state) { - batch_test_stack(NULL, TRUE); - - switch ($form_state['storage']['step']) { - case 1: - batch_set(_batch_test_batch_1()); - break; - case 2: - batch_set(_batch_test_batch_2()); - break; - } - - if ($form_state['storage']['step'] < 2) { - $form_state['storage']['step']++; - $form_state['rebuild'] = TRUE; - } - - // This will only be effective on the last step. - $form_state['redirect'] = 'batch-test/redirect'; -} - -/** - * Form with chained submit callbacks. - */ -function batch_test_chained_form() { - // This value is used to test that $form_state persists through batched - // submit handlers. - $form['value'] = array( - '#type' => 'textfield', - '#title' => 'Value', - '#default_value' => 1, - ); - $form['submit'] = array( - '#type' => 'submit', - '#value' => 'Submit', - ); - $form['#submit'] = array( - 'batch_test_chained_form_submit_1', - 'batch_test_chained_form_submit_2', - 'batch_test_chained_form_submit_3', - 'batch_test_chained_form_submit_4', - ); - - return $form; -} - -/** - * Submit handler #1 for the chained form. - */ -function batch_test_chained_form_submit_1($form, &$form_state) { - batch_test_stack(NULL, TRUE); - - batch_test_stack('submit handler 1'); - batch_test_stack('value = ' . $form_state['values']['value']); - - $form_state['values']['value']++; - batch_set(_batch_test_batch_1()); - - // This redirect should not be taken into account. - $form_state['redirect'] = 'should/be/discarded'; -} - -/** - * Submit handler #2 for the chained form. - */ -function batch_test_chained_form_submit_2($form, &$form_state) { - batch_test_stack('submit handler 2'); - batch_test_stack('value = ' . $form_state['values']['value']); - - $form_state['values']['value']++; - batch_set(_batch_test_batch_2()); - - // This redirect should not be taken into account. - $form_state['redirect'] = 'should/be/discarded'; -} - -/** - * Submit handler #3 for the chained form. - */ -function batch_test_chained_form_submit_3($form, &$form_state) { - batch_test_stack('submit handler 3'); - batch_test_stack('value = ' . $form_state['values']['value']); - - $form_state['values']['value']++; - - // This redirect should not be taken into account. - $form_state['redirect'] = 'should/be/discarded'; -} - -/** - * Submit handler #4 for the chained form. - */ -function batch_test_chained_form_submit_4($form, &$form_state) { - batch_test_stack('submit handler 4'); - batch_test_stack('value = ' . $form_state['values']['value']); - - $form_state['values']['value']++; - batch_set(_batch_test_batch_3()); - - // This is the redirect that should prevail. - $form_state['redirect'] = 'batch-test/redirect'; -} - -/** - * Menu callback: programmatically submits the 'Chained' form. - */ -function batch_test_programmatic($value = 1) { - $form_state = array( - 'values' => array('value' => $value) - ); - drupal_form_submit('batch_test_chained_form', $form_state); - return 'Got out of a programmatic batched form.'; -} - -/** - * Menu callback: programmatically submits a form within a batch. - */ -function batch_test_nested_drupal_form_submit($value = 1) { - // Set the batch and process it. - $batch['operations'] = array( - array('_batch_test_nested_drupal_form_submit_callback', array($value)), - ); - batch_set($batch); - batch_process('batch-test/redirect'); -} - -/** - * Batch operation: submits form_test_mock_form using drupal_form_submit(). - */ -function _batch_test_nested_drupal_form_submit_callback($value) { - $state['values']['test_value'] = $value; - drupal_form_submit('batch_test_mock_form', $state); -} - -/** - * A simple form with a textfield and submit button. - */ -function batch_test_mock_form($form, $form_state) { - $form['test_value'] = array( - '#type' => 'textfield', - ); - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Submit'), - ); - - return $form; -} - -/** - * Submit handler for the batch_test_mock form. - */ -function batch_test_mock_form_submit($form, &$form_state) { - batch_test_stack('mock form submitted with value = ' . $form_state['values']['test_value']); -} - -/** - * Menu callback: fire a batch process without a form submission. - */ -function batch_test_no_form() { - batch_test_stack(NULL, TRUE); - - batch_set(_batch_test_batch_1()); - batch_process('batch-test/redirect'); -} - -/** - * Menu callback: fire a batch process without a form submission. - */ -function batch_test_large_percentage() { - batch_test_stack(NULL, TRUE); - - batch_set(_batch_test_batch_5()); - batch_process('batch-test/redirect'); -} - -/** - * Menu callback: successful redirection. - */ -function batch_test_redirect_page() { - return 'Redirection successful.'; -} - -/** - * Batch 0: no operation. - */ -function _batch_test_batch_0() { - $batch = array( - 'operations' => array(), - 'finished' => '_batch_test_finished_0', - 'file' => drupal_get_path('module', 'batch_test'). '/batch_test.callbacks.inc', - ); - return $batch; -} - -/** - * Batch 1: repeats a simple operation. - * - * Operations: op 1 from 1 to 10. - */ -function _batch_test_batch_1() { - // Ensure the batch takes at least two iterations. - $total = 10; - $sleep = (1000000 / $total) * 2; - - $operations = array(); - for ($i = 1; $i <= $total; $i++) { - $operations[] = array('_batch_test_callback_1', array($i, $sleep)); - } - $batch = array( - 'operations' => $operations, - 'finished' => '_batch_test_finished_1', - 'file' => drupal_get_path('module', 'batch_test'). '/batch_test.callbacks.inc', - ); - return $batch; -} - -/** - * Batch 2: single multistep operation. - * - * Operations: op 2 from 1 to 10. - */ -function _batch_test_batch_2() { - // Ensure the batch takes at least two iterations. - $total = 10; - $sleep = (1000000 / $total) * 2; - - $operations = array( - array('_batch_test_callback_2', array(1, $total, $sleep)), - ); - $batch = array( - 'operations' => $operations, - 'finished' => '_batch_test_finished_2', - 'file' => drupal_get_path('module', 'batch_test') . '/batch_test.callbacks.inc', - ); - return $batch; -} - -/** - * Batch 3: both single and multistep operations. - * - * Operations: - * - op 1 from 1 to 5, - * - op 2 from 1 to 5, - * - op 1 from 6 to 10, - * - op 2 from 6 to 10. - */ -function _batch_test_batch_3() { - // Ensure the batch takes at least two iterations. - $total = 10; - $sleep = (1000000 / $total) * 2; - - $operations = array(); - for ($i = 1; $i <= round($total / 2); $i++) { - $operations[] = array('_batch_test_callback_1', array($i, $sleep)); - } - $operations[] = array('_batch_test_callback_2', array(1, $total / 2, $sleep)); - for ($i = round($total / 2) + 1; $i <= $total; $i++) { - $operations[] = array('_batch_test_callback_1', array($i, $sleep)); - } - $operations[] = array('_batch_test_callback_2', array(6, $total / 2, $sleep)); - $batch = array( - 'operations' => $operations, - 'finished' => '_batch_test_finished_3', - 'file' => drupal_get_path('module', 'batch_test') . '/batch_test.callbacks.inc', - ); - return $batch; -} - -/** - * Batch 4: batch within a batch. - * - * Operations: - * - op 1 from 1 to 5, - * - set batch 2 (op 2 from 1 to 10, should run at the end) - * - op 1 from 6 to 10, - */ -function _batch_test_batch_4() { - // Ensure the batch takes at least two iterations. - $total = 10; - $sleep = (1000000 / $total) * 2; - - $operations = array(); - for ($i = 1; $i <= round($total / 2); $i++) { - $operations[] = array('_batch_test_callback_1', array($i, $sleep)); - } - $operations[] = array('_batch_test_nested_batch_callback', array()); - for ($i = round($total / 2) + 1; $i <= $total; $i++) { - $operations[] = array('_batch_test_callback_1', array($i, $sleep)); - } - $batch = array( - 'operations' => $operations, - 'finished' => '_batch_test_finished_4', - 'file' => drupal_get_path('module', 'batch_test') . '/batch_test.callbacks.inc', - ); - return $batch; -} - -/** - * Batch 5: repeats a simple operation. - * - * Operations: op 1 from 1 to 10. - */ -function _batch_test_batch_5() { - // Ensure the batch takes at least two iterations. - $total = 10; - $sleep = (1000000 / $total) * 2; - - $operations = array(); - for ($i = 1; $i <= $total; $i++) { - $operations[] = array('_batch_test_callback_5', array($i, $sleep)); - } - $batch = array( - 'operations' => $operations, - 'finished' => '_batch_test_finished_5', - 'file' => drupal_get_path('module', 'batch_test'). '/batch_test.callbacks.inc', - ); - return $batch; -} - -/** - * Menu callback: run a batch for testing theme used on the progress page. - */ -function batch_test_theme_batch() { - batch_test_stack(NULL, TRUE); - $batch = array( - 'operations' => array( - array('_batch_test_theme_callback', array()), - ), - ); - batch_set($batch); - batch_process('batch-test/redirect'); -} - -/** - * Batch callback function for testing the theme used on the progress page. - */ -function _batch_test_theme_callback() { - // Because drupalGet() steps through the full progressive batch before - // returning control to the test function, we cannot test that the correct - // theme is being used on the batch processing page by viewing that page - // directly. Instead, we save the theme being used in a variable here, so - // that it can be loaded and inspected in the thread running the test. - global $theme; - batch_test_stack($theme); -} - -/** - * Helper function: store or retrieve traced execution data. - */ -function batch_test_stack($data = NULL, $reset = FALSE) { - if ($reset) { - variable_del('batch_test_stack'); - } - if (!isset($data)) { - return variable_get('batch_test_stack', array()); - } - $stack = variable_get('batch_test_stack', array()); - $stack[] = $data; - variable_set('batch_test_stack', $stack); -} diff --git a/modules/simpletest/tests/bootstrap.test b/modules/simpletest/tests/bootstrap.test deleted file mode 100644 index fcf52b97d82..00000000000 --- a/modules/simpletest/tests/bootstrap.test +++ /dev/null @@ -1,499 +0,0 @@ -<?php - -class BootstrapIPAddressTestCase extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'IP address and HTTP_HOST test', - 'description' => 'Get the IP address from the current visitor from the server variables, check hostname validation.', - 'group' => 'Bootstrap' - ); - } - - function setUp() { - $this->oldserver = $_SERVER; - - $this->remote_ip = '127.0.0.1'; - $this->proxy_ip = '127.0.0.2'; - $this->proxy2_ip = '127.0.0.3'; - $this->forwarded_ip = '127.0.0.4'; - $this->cluster_ip = '127.0.0.5'; - $this->untrusted_ip = '0.0.0.0'; - - drupal_static_reset('ip_address'); - - $_SERVER['REMOTE_ADDR'] = $this->remote_ip; - unset($_SERVER['HTTP_X_FORWARDED_FOR']); - unset($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']); - - parent::setUp(); - } - - function tearDown() { - $_SERVER = $this->oldserver; - drupal_static_reset('ip_address'); - parent::tearDown(); - } - - /** - * test IP Address and hostname - */ - function testIPAddressHost() { - // Test the normal IP address. - $this->assertTrue( - ip_address() == $this->remote_ip, - t('Got remote IP address.') - ); - - // Proxy forwarding on but no proxy addresses defined. - variable_set('reverse_proxy', 1); - $this->assertTrue( - ip_address() == $this->remote_ip, - t('Proxy forwarding without trusted proxies got remote IP address.') - ); - - // Proxy forwarding on and proxy address not trusted. - variable_set('reverse_proxy_addresses', array($this->proxy_ip, $this->proxy2_ip)); - drupal_static_reset('ip_address'); - $_SERVER['REMOTE_ADDR'] = $this->untrusted_ip; - $this->assertTrue( - ip_address() == $this->untrusted_ip, - t('Proxy forwarding with untrusted proxy got remote IP address.') - ); - - // Proxy forwarding on and proxy address trusted. - $_SERVER['REMOTE_ADDR'] = $this->proxy_ip; - $_SERVER['HTTP_X_FORWARDED_FOR'] = $this->forwarded_ip; - drupal_static_reset('ip_address'); - $this->assertTrue( - ip_address() == $this->forwarded_ip, - t('Proxy forwarding with trusted proxy got forwarded IP address.') - ); - - // Multi-tier architecture with comma separated values in header. - $_SERVER['REMOTE_ADDR'] = $this->proxy_ip; - $_SERVER['HTTP_X_FORWARDED_FOR'] = implode(', ', array($this->untrusted_ip, $this->forwarded_ip, $this->proxy2_ip)); - drupal_static_reset('ip_address'); - $this->assertTrue( - ip_address() == $this->forwarded_ip, - t('Proxy forwarding with trusted 2-tier proxy got forwarded IP address.') - ); - - // Custom client-IP header. - variable_set('reverse_proxy_header', 'HTTP_X_CLUSTER_CLIENT_IP'); - $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'] = $this->cluster_ip; - drupal_static_reset('ip_address'); - $this->assertTrue( - ip_address() == $this->cluster_ip, - t('Cluster environment got cluster client IP.') - ); - - // Verifies that drupal_valid_http_host() prevents invalid characters. - $this->assertFalse(drupal_valid_http_host('security/.drupal.org:80'), t('HTTP_HOST with / is invalid')); - $this->assertFalse(drupal_valid_http_host('security\\.drupal.org:80'), t('HTTP_HOST with \\ is invalid')); - $this->assertFalse(drupal_valid_http_host('security<.drupal.org:80'), t('HTTP_HOST with < is invalid')); - $this->assertFalse(drupal_valid_http_host('security..drupal.org:80'), t('HTTP_HOST with .. is invalid')); - // IPv6 loopback address - $this->assertTrue(drupal_valid_http_host('[::1]:80'), t('HTTP_HOST containing IPv6 loopback is valid')); - } -} - -class BootstrapPageCacheTestCase extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Page cache test', - 'description' => 'Enable the page cache and test it with various HTTP requests.', - 'group' => 'Bootstrap' - ); - } - - function setUp() { - parent::setUp('system_test'); - } - - /** - * Test support for requests containing If-Modified-Since and If-None-Match headers. - */ - function testConditionalRequests() { - variable_set('cache', 1); - - // Fill the cache. - $this->drupalGet(''); - - $this->drupalHead(''); - $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.')); - $etag = $this->drupalGetHeader('ETag'); - $last_modified = $this->drupalGetHeader('Last-Modified'); - - $this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified, 'If-None-Match: ' . $etag)); - $this->assertResponse(304, t('Conditional request returned 304 Not Modified.')); - - $this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC822, strtotime($last_modified)), 'If-None-Match: ' . $etag)); - $this->assertResponse(304, t('Conditional request with obsolete If-Modified-Since date returned 304 Not Modified.')); - - $this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC850, strtotime($last_modified)), 'If-None-Match: ' . $etag)); - $this->assertResponse(304, t('Conditional request with obsolete If-Modified-Since date returned 304 Not Modified.')); - - $this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified)); - $this->assertResponse(200, t('Conditional request without If-None-Match returned 200 OK.')); - $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.')); - - $this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC1123, strtotime($last_modified) + 1), 'If-None-Match: ' . $etag)); - $this->assertResponse(200, t('Conditional request with new a If-Modified-Since date newer than Last-Modified returned 200 OK.')); - $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.')); - - $user = $this->drupalCreateUser(); - $this->drupalLogin($user); - $this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified, 'If-None-Match: ' . $etag)); - $this->assertResponse(200, t('Conditional request returned 200 OK for authenticated user.')); - $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), t('Absense of Page was not cached.')); - } - - /** - * Test cache headers. - */ - function testPageCache() { - variable_set('cache', 1); - - // Fill the cache. - $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar'))); - $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', t('Page was not cached.')); - $this->assertEqual($this->drupalGetHeader('Vary'), 'Cookie,Accept-Encoding', t('Vary header was sent.')); - $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'public, max-age=0', t('Cache-Control header was sent.')); - $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', t('Expires header was sent.')); - $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', t('Custom header was sent.')); - - // Check cache. - $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar'))); - $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.')); - $this->assertEqual($this->drupalGetHeader('Vary'), 'Cookie,Accept-Encoding', t('Vary: Cookie header was sent.')); - $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'public, max-age=0', t('Cache-Control header was sent.')); - $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', t('Expires header was sent.')); - $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', t('Custom header was sent.')); - - // Check replacing default headers. - $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Expires', 'value' => 'Fri, 19 Nov 2008 05:00:00 GMT'))); - $this->assertEqual($this->drupalGetHeader('Expires'), 'Fri, 19 Nov 2008 05:00:00 GMT', t('Default header was replaced.')); - $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Vary', 'value' => 'User-Agent'))); - $this->assertEqual($this->drupalGetHeader('Vary'), 'User-Agent,Accept-Encoding', t('Default header was replaced.')); - - // Check that authenticated users bypass the cache. - $user = $this->drupalCreateUser(); - $this->drupalLogin($user); - $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar'))); - $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), t('Caching was bypassed.')); - $this->assertTrue(strpos($this->drupalGetHeader('Vary'), 'Cookie') === FALSE, t('Vary: Cookie header was not sent.')); - $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'no-cache, must-revalidate, post-check=0, pre-check=0', t('Cache-Control header was sent.')); - $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', t('Expires header was sent.')); - $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', t('Custom header was sent.')); - - } - - /** - * Test page compression. - * - * The test should pass even if zlib.output_compression is enabled in php.ini, - * .htaccess or similar, or if compression is done outside PHP, e.g. by the - * mod_deflate Apache module. - */ - function testPageCompression() { - variable_set('cache', 1); - - // Fill the cache and verify that output is compressed. - $this->drupalGet('', array(), array('Accept-Encoding: gzip,deflate')); - $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', t('Page was not cached.')); - $this->drupalSetContent(gzinflate(substr($this->drupalGetContent(), 10, -8))); - $this->assertRaw('</html>', t('Page was gzip compressed.')); - - // Verify that cached output is compressed. - $this->drupalGet('', array(), array('Accept-Encoding: gzip,deflate')); - $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.')); - $this->assertEqual($this->drupalGetHeader('Content-Encoding'), 'gzip', t('A Content-Encoding header was sent.')); - $this->drupalSetContent(gzinflate(substr($this->drupalGetContent(), 10, -8))); - $this->assertRaw('</html>', t('Page was gzip compressed.')); - - // Verify that a client without compression support gets an uncompressed page. - $this->drupalGet(''); - $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.')); - $this->assertFalse($this->drupalGetHeader('Content-Encoding'), t('A Content-Encoding header was not sent.')); - $this->assertTitle(t('Welcome to @site-name | @site-name', array('@site-name' => variable_get('site_name', 'Drupal'))), t('Site title matches.')); - $this->assertRaw('</html>', t('Page was not compressed.')); - } -} - -class BootstrapVariableTestCase extends DrupalWebTestCase { - - function setUp() { - parent::setUp('system_test'); - } - - public static function getInfo() { - return array( - 'name' => 'Variable test', - 'description' => 'Make sure the variable system functions correctly.', - 'group' => 'Bootstrap' - ); - } - - /** - * testVariable - */ - function testVariable() { - // Setting and retrieving values. - $variable = $this->randomName(); - variable_set('simpletest_bootstrap_variable_test', $variable); - $this->assertIdentical($variable, variable_get('simpletest_bootstrap_variable_test'), t('Setting and retrieving values')); - - // Make sure the variable persists across multiple requests. - $this->drupalGet('system-test/variable-get'); - $this->assertText($variable, t('Variable persists across multiple requests')); - - // Deleting variables. - $default_value = $this->randomName(); - variable_del('simpletest_bootstrap_variable_test'); - $variable = variable_get('simpletest_bootstrap_variable_test', $default_value); - $this->assertIdentical($variable, $default_value, t('Deleting variables')); - } - - /** - * Makes sure that the default variable parameter is passed through okay. - */ - function testVariableDefaults() { - // Tests passing nothing through to the default. - $this->assertIdentical(NULL, variable_get('simpletest_bootstrap_variable_test'), t('Variables are correctly defaulting to NULL.')); - - // Tests passing 5 to the default parameter. - $this->assertIdentical(5, variable_get('simpletest_bootstrap_variable_test', 5), t('The default variable parameter is passed through correctly.')); - } - -} - -/** - * Test hook_boot() and hook_exit(). - */ -class HookBootExitTestCase extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Boot and exit hook invocation', - 'description' => 'Test that hook_boot() and hook_exit() are called correctly.', - 'group' => 'Bootstrap', - ); - } - - function setUp() { - parent::setUp('system_test', 'dblog'); - } - - /** - * Test calling of hook_boot() and hook_exit(). - */ - function testHookBootExit() { - // Test with cache disabled. Boot and exit should always fire. - variable_set('cache', 0); - $this->drupalGet(''); - $calls = 1; - $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot called with disabled cache.')); - $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit called with disabled cache.')); - - // Test with normal cache. Boot and exit should be called. - variable_set('cache', 1); - $this->drupalGet(''); - $calls++; - $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot called with normal cache.')); - $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit called with normal cache.')); - - // Boot and exit should not fire since the page is cached. - variable_set('page_cache_invoke_hooks', FALSE); - $this->assertTrue(cache('page')->get(url('', array('absolute' => TRUE))), t('Page has been cached.')); - $this->drupalGet(''); - $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot not called with aggressive cache and a cached page.')); - $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit not called with aggressive cache and a cached page.')); - - // Test with page cache cleared, boot and exit should be called. - $this->assertTrue(db_delete('cache_page')->execute(), t('Page cache cleared.')); - $this->drupalGet(''); - $calls++; - $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot called with aggressive cache and no cached page.')); - $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit called with aggressive cache and no cached page.')); - } -} - -/** - * Test drupal_get_filename()'s availability. - */ -class BootstrapGetFilenameTestCase extends DrupalUnitTestCase { - - public static function getInfo() { - return array( - 'name' => 'Get filename test', - 'description' => 'Test that drupal_get_filename() works correctly when the file is not found in the database.', - 'group' => 'Bootstrap', - ); - } - - /** - * Test that drupal_get_filename() works correctly when the file is not found in the database. - */ - function testDrupalGetFilename() { - // Reset the static cache so we can test the "db is not active" code of - // drupal_get_filename(). - drupal_static_reset('drupal_get_filename'); - - // Retrieving the location of a module. - $this->assertIdentical(drupal_get_filename('module', 'php'), 'modules/php/php.module', t('Retrieve module location.')); - - // Retrieving the location of a theme. - $this->assertIdentical(drupal_get_filename('theme', 'stark'), 'themes/stark/stark.info', t('Retrieve theme location.')); - - // Retrieving the location of a theme engine. - $this->assertIdentical(drupal_get_filename('theme_engine', 'phptemplate'), 'themes/engines/phptemplate/phptemplate.engine', t('Retrieve theme engine location.')); - - // Retrieving a file that is definitely not stored in the database. - $this->assertIdentical(drupal_get_filename('profile', 'standard'), 'profiles/standard/standard.profile', t('Retrieve install profile location.')); - } -} - -class BootstrapTimerTestCase extends DrupalUnitTestCase { - - public static function getInfo() { - return array( - 'name' => 'Timer test', - 'description' => 'Test that timer_read() works both when a timer is running and when a timer is stopped.', - 'group' => 'Bootstrap', - ); - } - - /** - * Test timer_read() to ensure it properly accumulates time when the timer - * started and stopped multiple times. - * @return - */ - function testTimer() { - timer_start('test'); - sleep(1); - $this->assertTrue(timer_read('test') >= 1000, t('Timer measured 1 second of sleeping while running.')); - sleep(1); - timer_stop('test'); - $this->assertTrue(timer_read('test') >= 2000, t('Timer measured 2 seconds of sleeping after being stopped.')); - timer_start('test'); - sleep(1); - $this->assertTrue(timer_read('test') >= 3000, t('Timer measured 3 seconds of sleeping after being restarted.')); - sleep(1); - $timer = timer_stop('test'); - $this->assertTrue(timer_read('test') >= 4000, t('Timer measured 4 seconds of sleeping after being stopped for a second time.')); - $this->assertEqual($timer['count'], 2, t('Timer counted 2 instances of being started.')); - } -} - -/** - * Test that resetting static variables works. - */ -class BootstrapResettableStaticTestCase extends DrupalUnitTestCase { - - public static function getInfo() { - return array( - 'name' => 'Resettable static variables test', - 'description' => 'Test that drupal_static() and drupal_static_reset() work.', - 'group' => 'Bootstrap', - ); - } - - /** - * Test that a variable reference returned by drupal_static() gets reset when - * drupal_static_reset() is called. - */ - function testDrupalStatic() { - $name = __CLASS__ . '_' . __METHOD__; - $var = &drupal_static($name, 'foo'); - $this->assertEqual($var, 'foo', t('Variable returned by drupal_static() was set to its default.')); - - // Call the specific reset and the global reset each twice to ensure that - // multiple resets can be issued without odd side effects. - $var = 'bar'; - drupal_static_reset($name); - $this->assertEqual($var, 'foo', t('Variable was reset after first invocation of name-specific reset.')); - $var = 'bar'; - drupal_static_reset($name); - $this->assertEqual($var, 'foo', t('Variable was reset after second invocation of name-specific reset.')); - $var = 'bar'; - drupal_static_reset(); - $this->assertEqual($var, 'foo', t('Variable was reset after first invocation of global reset.')); - $var = 'bar'; - drupal_static_reset(); - $this->assertEqual($var, 'foo', t('Variable was reset after second invocation of global reset.')); - } -} - -/** - * Test miscellaneous functions in bootstrap.inc. - */ -class BootstrapMiscTestCase extends DrupalUnitTestCase { - - public static function getInfo() { - return array( - 'name' => 'Miscellaneous bootstrap unit tests', - 'description' => 'Test miscellaneous functions in bootstrap.inc.', - 'group' => 'Bootstrap', - ); - } - - /** - * Test miscellaneous functions in bootstrap.inc. - */ - function testMisc() { - // Test drupal_array_merge_deep(). - $link_options_1 = array('fragment' => 'x', 'attributes' => array('title' => 'X', 'class' => array('a', 'b')), 'language' => 'en'); - $link_options_2 = array('fragment' => 'y', 'attributes' => array('title' => 'Y', 'class' => array('c', 'd')), 'html' => TRUE); - $expected = array('fragment' => 'y', 'attributes' => array('title' => 'Y', 'class' => array('a', 'b', 'c', 'd')), 'language' => 'en', 'html' => TRUE); - $this->assertIdentical(drupal_array_merge_deep($link_options_1, $link_options_2), $expected, t('drupal_array_merge_deep() returned a properly merged array.')); - } -} - -/** - * Tests for overriding server variables via the API. - */ -class BootstrapOverrideServerVariablesTestCase extends DrupalUnitTestCase { - public static function getInfo() { - return array( - 'name' => 'Overriding server variables', - 'description' => 'Test that drupal_override_server_variables() works correctly.', - 'group' => 'Bootstrap', - ); - } - - /** - * Test providing a direct URL to to drupal_override_server_variables(). - */ - function testDrupalOverrideServerVariablesProvidedURL() { - $tests = array( - 'http://example.com' => array( - 'HTTP_HOST' => 'example.com', - 'SCRIPT_NAME' => isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : NULL, - ), - 'http://example.com/index.php' => array( - 'HTTP_HOST' => 'example.com', - 'SCRIPT_NAME' => '/index.php', - ), - 'http://example.com/subdirectory/index.php' => array( - 'HTTP_HOST' => 'example.com', - 'SCRIPT_NAME' => '/subdirectory/index.php', - ), - ); - foreach ($tests as $url => $expected_server_values) { - // Remember the original value of $_SERVER, since the function call below - // will modify it. - $original_server = $_SERVER; - // Call drupal_override_server_variables() and ensure that all expected - // $_SERVER variables were modified correctly. - drupal_override_server_variables(array('url' => $url)); - foreach ($expected_server_values as $key => $value) { - $this->assertIdentical($_SERVER[$key], $value); - } - // Restore the original value of $_SERVER. - $_SERVER = $original_server; - } - } -} - diff --git a/modules/simpletest/tests/cache.test b/modules/simpletest/tests/cache.test deleted file mode 100644 index 664247b8ab5..00000000000 --- a/modules/simpletest/tests/cache.test +++ /dev/null @@ -1,375 +0,0 @@ -<?php - -class CacheTestCase extends DrupalWebTestCase { - protected $default_bin = 'cache_page'; - protected $default_cid = 'test_temporary'; - protected $default_value = 'CacheTest'; - - /** - * Check whether or not a cache entry exists. - * - * @param $cid - * The cache id. - * @param $var - * The variable the cache should contain. - * @param $bin - * The bin the cache item was stored in. - * @return - * TRUE on pass, FALSE on fail. - */ - protected function checkCacheExists($cid, $var, $bin = NULL) { - if ($bin == NULL) { - $bin = $this->default_bin; - } - - $cached = cache($bin)->get($cid); - - return isset($cached->data) && $cached->data == $var; - } - - /** - * Assert or a cache entry exists. - * - * @param $message - * Message to display. - * @param $var - * The variable the cache should contain. - * @param $cid - * The cache id. - * @param $bin - * The bin the cache item was stored in. - */ - protected function assertCacheExists($message, $var = NULL, $cid = NULL, $bin = NULL) { - if ($bin == NULL) { - $bin = $this->default_bin; - } - if ($cid == NULL) { - $cid = $this->default_cid; - } - if ($var == NULL) { - $var = $this->default_value; - } - - $this->assertTrue($this->checkCacheExists($cid, $var, $bin), $message); - } - - /** - * Assert or a cache entry has been removed. - * - * @param $message - * Message to display. - * @param $cid - * The cache id. - * @param $bin - * The bin the cache item was stored in. - */ - function assertCacheRemoved($message, $cid = NULL, $bin = NULL) { - if ($bin == NULL) { - $bin = $this->default_bin; - } - if ($cid == NULL) { - $cid = $this->default_cid; - } - - $cached = cache($bin)->get($cid); - $this->assertFalse($cached, $message); - } - - /** - * Perform the general wipe. - * @param $bin - * The bin to perform the wipe on. - */ - protected function generalWipe($bin = NULL) { - if ($bin == NULL) { - $bin = $this->default_bin; - } - - cache($bin)->expire(); - } - - /** - * Setup the lifetime settings for caching. - * - * @param $time - * The time in seconds the cache should minimal live. - */ - protected function setupLifetime($time) { - variable_set('cache_lifetime', $time); - variable_set('cache_flush', 0); - } -} - -class CacheSavingCase extends CacheTestCase { - public static function getInfo() { - return array( - 'name' => 'Cache saving test', - 'description' => 'Check our variables are saved and restored the right way.', - 'group' => 'Cache' - ); - } - - /** - * Test the saving and restoring of a string. - */ - function testString() { - $this->checkVariable($this->randomName(100)); - } - - /** - * Test the saving and restoring of an integer. - */ - function testInteger() { - $this->checkVariable(100); - } - - /** - * Test the saving and restoring of a double. - */ - function testDouble() { - $this->checkVariable(1.29); - } - - /** - * Test the saving and restoring of an array. - */ - function testArray() { - $this->checkVariable(array('drupal1', 'drupal2' => 'drupal3', 'drupal4' => array('drupal5', 'drupal6'))); - } - - /** - * Test the saving and restoring of an object. - */ - function testObject() { - $test_object = new stdClass(); - $test_object->test1 = $this->randomName(100); - $test_object->test2 = 100; - $test_object->test3 = array('drupal1', 'drupal2' => 'drupal3', 'drupal4' => array('drupal5', 'drupal6')); - - - cache()->set('test_object', $test_object); - $cached = cache()->get('test_object'); - $this->assertTrue(isset($cached->data) && $cached->data == $test_object, t('Object is saved and restored properly.')); - } - - /** - * Check or a variable is stored and restored properly. - */ - function checkVariable($var) { - cache()->set('test_var', $var); - $cached = cache()->get('test_var'); - $this->assertTrue(isset($cached->data) && $cached->data === $var, t('@type is saved and restored properly.', array('@type' => ucfirst(gettype($var))))); - } - - /** - * Test no empty cids are written in cache table. - */ - function testNoEmptyCids() { - $this->drupalGet('user/register'); - $this->assertFalse(cache_get(''), t('No cache entry is written with an empty cid.')); - } -} - -/** - * Test getMultiple(). - */ -class CacheGetMultipleUnitTest extends CacheTestCase { - - public static function getInfo() { - return array( - 'name' => 'Fetching multiple cache items', - 'description' => 'Confirm that multiple records are fetched correctly.', - 'group' => 'Cache', - ); - } - - function setUp() { - $this->default_bin = 'page'; - parent::setUp(); - } - - /** - * Test getMultiple(). - */ - function testCacheMultiple() { - $item1 = $this->randomName(10); - $item2 = $this->randomName(10); - $cache = cache($this->default_bin); - $cache->set('item1', $item1); - $cache->set('item2', $item2); - $this->assertTrue($this->checkCacheExists('item1', $item1), t('Item 1 is cached.')); - $this->assertTrue($this->checkCacheExists('item2', $item2), t('Item 2 is cached.')); - - // Fetch both records from the database with getMultiple(). - $item_ids = array('item1', 'item2'); - $items = $cache->getMultiple($item_ids); - $this->assertEqual($items['item1']->data, $item1, t('Item was returned from cache successfully.')); - $this->assertEqual($items['item2']->data, $item2, t('Item was returned from cache successfully.')); - - // Remove one item from the cache. - $cache->delete('item2'); - - // Confirm that only one item is returned by getMultiple(). - $item_ids = array('item1', 'item2'); - $items = $cache->getMultiple($item_ids); - $this->assertEqual($items['item1']->data, $item1, t('Item was returned from cache successfully.')); - $this->assertFalse(isset($items['item2']), t('Item was not returned from the cache.')); - $this->assertTrue(count($items) == 1, t('Only valid cache entries returned.')); - } -} - -/** - * Test cache clearing methods. - */ -class CacheClearCase extends CacheTestCase { - public static function getInfo() { - return array( - 'name' => 'Cache clear test', - 'description' => 'Check our clearing is done the proper way.', - 'group' => 'Cache' - ); - } - - function setUp() { - $this->default_bin = 'page'; - $this->default_value = $this->randomName(10); - - parent::setUp(); - } - - /** - * Test clearing using a cid. - */ - function testClearCid() { - $cache = cache($this->default_bin); - $cache->set('test_cid_clear', $this->default_value); - - $this->assertCacheExists(t('Cache was set for clearing cid.'), $this->default_value, 'test_cid_clear'); - $cache->delete('test_cid_clear'); - - $this->assertCacheRemoved(t('Cache was removed after clearing cid.'), 'test_cid_clear'); - } - - /** - * Test clearing using wildcard. - */ - function testClearWildcard() { - $cache = cache($this->default_bin); - $cache->set('test_cid_clear1', $this->default_value); - $cache->set('test_cid_clear2', $this->default_value); - $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value) - && $this->checkCacheExists('test_cid_clear2', $this->default_value), - t('Two caches were created for checking cid "*" with wildcard true.')); - $cache->flush(); - $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value) - || $this->checkCacheExists('test_cid_clear2', $this->default_value), - t('Two caches removed after clearing cid "*" with wildcard true.')); - - $cache->set('test_cid_clear1', $this->default_value); - $cache->set('test_cid_clear2', $this->default_value); - $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value) - && $this->checkCacheExists('test_cid_clear2', $this->default_value), - t('Two caches were created for checking cid substring with wildcard true.')); - $cache->deletePrefix('test_'); - $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value) - || $this->checkCacheExists('test_cid_clear2', $this->default_value), - t('Two caches removed after clearing cid substring with wildcard true.')); - } - - /** - * Test clearing using an array. - */ - function testClearArray() { - // Create three cache entries. - $cache = cache($this->default_bin); - $cache->set('test_cid_clear1', $this->default_value); - $cache->set('test_cid_clear2', $this->default_value); - $cache->set('test_cid_clear3', $this->default_value); - $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value) - && $this->checkCacheExists('test_cid_clear2', $this->default_value) - && $this->checkCacheExists('test_cid_clear3', $this->default_value), - t('Three cache entries were created.')); - - // Clear two entries using an array. - $cache->deleteMultiple(array('test_cid_clear1', 'test_cid_clear2')); - $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value) - || $this->checkCacheExists('test_cid_clear2', $this->default_value), - t('Two cache entries removed after clearing with an array.')); - - $this->assertTrue($this->checkCacheExists('test_cid_clear3', $this->default_value), - t('Entry was not cleared from the cache')); - - // Set the cache clear threshold to 2 to confirm that the full bin is cleared - // when the threshold is exceeded. - variable_set('cache_clear_threshold', 2); - $cache->set('test_cid_clear1', $this->default_value); - $cache->set('test_cid_clear2', $this->default_value); - $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value) - && $this->checkCacheExists('test_cid_clear2', $this->default_value), - t('Two cache entries were created.')); - $cache->deleteMultiple(array('test_cid_clear1', 'test_cid_clear2', 'test_cid_clear3')); - $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value) - || $this->checkCacheExists('test_cid_clear2', $this->default_value) - || $this->checkCacheExists('test_cid_clear3', $this->default_value), - t('All cache entries removed when the array exceeded the cache clear threshold.')); - } - - /** - * Test drupal_flush_all_caches(). - */ - function testFlushAllCaches() { - // Create cache entries for each flushed cache bin. - $bins = array('cache', 'filter', 'page', 'bootstrap', 'path'); - $bins = array_merge(module_invoke_all('flush_caches'), $bins); - foreach ($bins as $id => $bin) { - $cid = 'test_cid_clear' . $id; - cache($bin)->set($cid, $this->default_value); - } - - // Remove all caches then make sure that they are cleared. - drupal_flush_all_caches(); - - foreach ($bins as $id => $bin) { - $cid = 'test_cid_clear' . $id; - $this->assertFalse($this->checkCacheExists($cid, $this->default_value, $bin), t('All cache entries removed from @bin.', array('@bin' => $bin))); - } - } -} - -/** - * Test isEmpty() method. - */ -class CacheIsEmptyCase extends CacheTestCase { - public static function getInfo() { - return array( - 'name' => 'Cache emptiness test', - 'description' => 'Check if a cache bin is empty after performing clear operations.', - 'group' => 'Cache' - ); - } - - function setUp() { - $this->default_bin = 'page'; - $this->default_value = $this->randomName(10); - - parent::setUp(); - } - - /** - * Test clearing using a cid. - */ - function testIsEmpty() { - // Clear the cache bin. - $cache = cache($this->default_bin); - $cache->flush(); - $this->assertTrue($cache->isEmpty(), t('The cache bin is empty')); - // Add some data to the cache bin. - $cache->set($this->default_cid, $this->default_value); - $this->assertCacheExists(t('Cache was set.'), $this->default_value, $this->default_cid); - $this->assertFalse($cache->isEmpty(), t('The cache bin is not empty')); - // Remove the cached data. - $cache->delete($this->default_cid); - $this->assertCacheRemoved(t('Cache was removed.'), $this->default_cid); - $this->assertTrue($cache->isEmpty(), t('The cache bin is empty')); - } -} diff --git a/modules/simpletest/tests/common.test b/modules/simpletest/tests/common.test deleted file mode 100644 index 5154e0ccda3..00000000000 --- a/modules/simpletest/tests/common.test +++ /dev/null @@ -1,2470 +0,0 @@ -<?php - -/** - * @file - * Tests for common.inc functionality. - */ - -/** - * Tests for URL generation functions. - */ -class DrupalAlterTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'drupal_alter() tests', - 'description' => 'Confirm that alteration of arguments passed to drupal_alter() works correctly.', - 'group' => 'System', - ); - } - - function setUp() { - parent::setUp('common_test'); - } - - function testDrupalAlter() { - // This test depends on Bartik, so make sure that it is always the current - // active theme. - global $theme, $base_theme_info; - $theme = 'bartik'; - $base_theme_info = array(); - - $array = array('foo' => 'bar'); - $entity = new stdClass(); - $entity->foo = 'bar'; - - // Verify alteration of a single argument. - $array_copy = $array; - $array_expected = array('foo' => 'Drupal theme'); - drupal_alter('drupal_alter', $array_copy); - $this->assertEqual($array_copy, $array_expected, t('Single array was altered.')); - - $entity_copy = clone $entity; - $entity_expected = clone $entity; - $entity_expected->foo = 'Drupal theme'; - drupal_alter('drupal_alter', $entity_copy); - $this->assertEqual($entity_copy, $entity_expected, t('Single object was altered.')); - - // Verify alteration of multiple arguments. - $array_copy = $array; - $array_expected = array('foo' => 'Drupal theme'); - $entity_copy = clone $entity; - $entity_expected = clone $entity; - $entity_expected->foo = 'Drupal theme'; - $array2_copy = $array; - $array2_expected = array('foo' => 'Drupal theme'); - drupal_alter('drupal_alter', $array_copy, $entity_copy, $array2_copy); - $this->assertEqual($array_copy, $array_expected, t('First argument to drupal_alter() was altered.')); - $this->assertEqual($entity_copy, $entity_expected, t('Second argument to drupal_alter() was altered.')); - $this->assertEqual($array2_copy, $array2_expected, t('Third argument to drupal_alter() was altered.')); - } -} - -/** - * Tests for URL generation functions. - * - * url() calls module_implements(), which may issue a db query, which requires - * inheriting from a web test case rather than a unit test case. - */ -class CommonURLUnitTest extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'URL generation tests', - 'description' => 'Confirm that url(), drupal_get_query_parameters(), drupal_http_build_query(), and l() work correctly with various input.', - 'group' => 'System', - ); - } - - /** - * Confirm that invalid text given as $path is filtered. - */ - function testLXSS() { - $text = $this->randomName(); - $path = "<SCRIPT>alert('XSS')</SCRIPT>"; - $link = l($text, $path); - $sanitized_path = check_url(url($path)); - $this->assertTrue(strpos($link, $sanitized_path) !== FALSE, t('XSS attack @path was filtered', array('@path' => $path))); - } - - /* - * Tests for active class in l() function. - */ - function testLActiveClass() { - $link = l($this->randomName(), $_GET['q']); - $this->assertTrue($this->hasClass($link, 'active'), t('Class @class is present on link to the current page', array('@class' => 'active'))); - } - - /** - * Tests for custom class in l() function. - */ - function testLCustomClass() { - $class = $this->randomName(); - $link = l($this->randomName(), $_GET['q'], array('attributes' => array('class' => array($class)))); - $this->assertTrue($this->hasClass($link, $class), t('Custom class @class is present on link when requested', array('@class' => $class))); - $this->assertTrue($this->hasClass($link, 'active'), t('Class @class is present on link to the current page', array('@class' => 'active'))); - } - - private function hasClass($link, $class) { - return preg_match('|class="([^\"\s]+\s+)*' . $class . '|', $link); - } - - /** - * Test drupal_get_query_parameters(). - */ - function testDrupalGetQueryParameters() { - $original = array( - 'a' => 1, - 'b' => array( - 'd' => 4, - 'e' => array( - 'f' => 5, - ), - ), - 'c' => 3, - 'q' => 'foo/bar', - ); - - // Default arguments. - $result = $_GET; - unset($result['q']); - $this->assertEqual(drupal_get_query_parameters(), $result, t("\$_GET['q'] was removed.")); - - // Default exclusion. - $result = $original; - unset($result['q']); - $this->assertEqual(drupal_get_query_parameters($original), $result, t("'q' was removed.")); - - // First-level exclusion. - $result = $original; - unset($result['b']); - $this->assertEqual(drupal_get_query_parameters($original, array('b')), $result, t("'b' was removed.")); - - // Second-level exclusion. - $result = $original; - unset($result['b']['d']); - $this->assertEqual(drupal_get_query_parameters($original, array('b[d]')), $result, t("'b[d]' was removed.")); - - // Third-level exclusion. - $result = $original; - unset($result['b']['e']['f']); - $this->assertEqual(drupal_get_query_parameters($original, array('b[e][f]')), $result, t("'b[e][f]' was removed.")); - - // Multiple exclusions. - $result = $original; - unset($result['a'], $result['b']['e'], $result['c']); - $this->assertEqual(drupal_get_query_parameters($original, array('a', 'b[e]', 'c')), $result, t("'a', 'b[e]', 'c' were removed.")); - } - - /** - * Test drupal_http_build_query(). - */ - function testDrupalHttpBuildQuery() { - $this->assertEqual(drupal_http_build_query(array('a' => ' &#//+%20@۞')), 'a=%20%26%23//%2B%2520%40%DB%9E', t('Value was properly encoded.')); - $this->assertEqual(drupal_http_build_query(array(' &#//+%20@۞' => 'a')), '%20%26%23%2F%2F%2B%2520%40%DB%9E=a', t('Key was properly encoded.')); - $this->assertEqual(drupal_http_build_query(array('a' => '1', 'b' => '2', 'c' => '3')), 'a=1&b=2&c=3', t('Multiple values were properly concatenated.')); - $this->assertEqual(drupal_http_build_query(array('a' => array('b' => '2', 'c' => '3'), 'd' => 'foo')), 'a[b]=2&a[c]=3&d=foo', t('Nested array was properly encoded.')); - } - - /** - * Test drupal_parse_url(). - */ - function testDrupalParseUrl() { - // Relative URL. - $url = 'foo/bar?foo=bar&bar=baz&baz#foo'; - $result = array( - 'path' => 'foo/bar', - 'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''), - 'fragment' => 'foo', - ); - $this->assertEqual(drupal_parse_url($url), $result, t('Relative URL parsed correctly.')); - - // Relative URL that is known to confuse parse_url(). - $url = 'foo/bar:1'; - $result = array( - 'path' => 'foo/bar:1', - 'query' => array(), - 'fragment' => '', - ); - $this->assertEqual(drupal_parse_url($url), $result, t('Relative URL parsed correctly.')); - - // Absolute URL. - $url = '/foo/bar?foo=bar&bar=baz&baz#foo'; - $result = array( - 'path' => '/foo/bar', - 'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''), - 'fragment' => 'foo', - ); - $this->assertEqual(drupal_parse_url($url), $result, t('Absolute URL parsed correctly.')); - - // External URL testing. - $url = 'http://drupal.org/foo/bar?foo=bar&bar=baz&baz#foo'; - - // Test that drupal can recognize an absolute URL. Used to prevent attack vectors. - $this->assertTrue(url_is_external($url), t('Correctly identified an external URL.')); - - // Test the parsing of absolute URLs. - $result = array( - 'path' => 'http://drupal.org/foo/bar', - 'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''), - 'fragment' => 'foo', - ); - $this->assertEqual(drupal_parse_url($url), $result, t('External URL parsed correctly.')); - - // Verify proper parsing of URLs when clean URLs are disabled. - $result = array( - 'path' => 'foo/bar', - 'query' => array('bar' => 'baz'), - 'fragment' => 'foo', - ); - // Non-clean URLs #1: Absolute URL generated by url(). - $url = $GLOBALS['base_url'] . '/?q=foo/bar&bar=baz#foo'; - $this->assertEqual(drupal_parse_url($url), $result, t('Absolute URL with clean URLs disabled parsed correctly.')); - - // Non-clean URLs #2: Relative URL generated by url(). - $url = '?q=foo/bar&bar=baz#foo'; - $this->assertEqual(drupal_parse_url($url), $result, t('Relative URL with clean URLs disabled parsed correctly.')); - - // Non-clean URLs #3: URL generated by url() on non-Apache webserver. - $url = 'index.php?q=foo/bar&bar=baz#foo'; - $this->assertEqual(drupal_parse_url($url), $result, t('Relative URL on non-Apache webserver with clean URLs disabled parsed correctly.')); - - // Test that drupal_parse_url() does not allow spoofing a URL to force a malicious redirect. - $parts = drupal_parse_url('forged:http://cwe.mitre.org/data/definitions/601.html'); - $this->assertFalse(valid_url($parts['path'], TRUE), t('drupal_parse_url() correctly parsed a forged URL.')); - } - - /** - * Test url() with/without query, with/without fragment, absolute on/off and - * assert all that works when clean URLs are on and off. - */ - function testUrl() { - global $base_url; - - foreach (array(FALSE, TRUE) as $absolute) { - // Get the expected start of the path string. - $base = $absolute ? $base_url . '/' : base_path(); - $absolute_string = $absolute ? 'absolute' : NULL; - - // Disable Clean URLs. - $GLOBALS['conf']['clean_url'] = 0; - - $url = $base . '?q=node/123'; - $result = url('node/123', array('absolute' => $absolute)); - $this->assertEqual($url, $result, "$url == $result"); - - $url = $base . '?q=node/123#foo'; - $result = url('node/123', array('fragment' => 'foo', 'absolute' => $absolute)); - $this->assertEqual($url, $result, "$url == $result"); - - $url = $base . '?q=node/123&foo'; - $result = url('node/123', array('query' => array('foo' => NULL), 'absolute' => $absolute)); - $this->assertEqual($url, $result, "$url == $result"); - - $url = $base . '?q=node/123&foo=bar&bar=baz'; - $result = url('node/123', array('query' => array('foo' => 'bar', 'bar' => 'baz'), 'absolute' => $absolute)); - $this->assertEqual($url, $result, "$url == $result"); - - $url = $base . '?q=node/123&foo#bar'; - $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute)); - $this->assertEqual($url, $result, "$url == $result"); - - $url = $base . '?q=node/123&foo#0'; - $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => '0', 'absolute' => $absolute)); - $this->assertEqual($url, $result, "$url == $result"); - - $url = $base . '?q=node/123&foo'; - $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => '', 'absolute' => $absolute)); - $this->assertEqual($url, $result, "$url == $result"); - - $url = $base; - $result = url('<front>', array('absolute' => $absolute)); - $this->assertEqual($url, $result, "$url == $result"); - - // Enable Clean URLs. - $GLOBALS['conf']['clean_url'] = 1; - - $url = $base . 'node/123'; - $result = url('node/123', array('absolute' => $absolute)); - $this->assertEqual($url, $result, "$url == $result"); - - $url = $base . 'node/123#foo'; - $result = url('node/123', array('fragment' => 'foo', 'absolute' => $absolute)); - $this->assertEqual($url, $result, "$url == $result"); - - $url = $base . 'node/123?foo'; - $result = url('node/123', array('query' => array('foo' => NULL), 'absolute' => $absolute)); - $this->assertEqual($url, $result, "$url == $result"); - - $url = $base . 'node/123?foo=bar&bar=baz'; - $result = url('node/123', array('query' => array('foo' => 'bar', 'bar' => 'baz'), 'absolute' => $absolute)); - $this->assertEqual($url, $result, "$url == $result"); - - $url = $base . 'node/123?foo#bar'; - $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute)); - $this->assertEqual($url, $result, "$url == $result"); - - $url = $base; - $result = url('<front>', array('absolute' => $absolute)); - $this->assertEqual($url, $result, "$url == $result"); - } - } - - /** - * Test external URL handling. - */ - function testExternalUrls() { - $test_url = 'http://drupal.org/'; - - // Verify external URL can contain a fragment. - $url = $test_url . '#drupal'; - $result = url($url); - $this->assertEqual($url, $result, t('External URL with fragment works without a fragment in $options.')); - - // Verify fragment can be overidden in an external URL. - $url = $test_url . '#drupal'; - $fragment = $this->randomName(10); - $result = url($url, array('fragment' => $fragment)); - $this->assertEqual($test_url . '#' . $fragment, $result, t('External URL fragment is overidden with a custom fragment in $options.')); - - // Verify external URL can contain a query string. - $url = $test_url . '?drupal=awesome'; - $result = url($url); - $this->assertEqual($url, $result, t('External URL with query string works without a query string in $options.')); - - // Verify external URL can be extended with a query string. - $url = $test_url; - $query = array($this->randomName(5) => $this->randomName(5)); - $result = url($url, array('query' => $query)); - $this->assertEqual($url . '?' . http_build_query($query, '', '&'), $result, t('External URL can be extended with a query string in $options.')); - - // Verify query string can be extended in an external URL. - $url = $test_url . '?drupal=awesome'; - $query = array($this->randomName(5) => $this->randomName(5)); - $result = url($url, array('query' => $query)); - $this->assertEqual($url . '&' . http_build_query($query, '', '&'), $result, t('External URL query string can be extended with a custom query string in $options.')); - } -} - -/** - * Tests for the check_plain(), filter_xss() and format_string() functions. - */ -class CommonXssUnitTest extends DrupalUnitTestCase { - - public static function getInfo() { - return array( - 'name' => 'String filtering tests', - 'description' => 'Confirm that check_plain(), filter_xss(), format_string() and check_url() work correctly, including invalid multi-byte sequences.', - 'group' => 'System', - ); - } - - /** - * Check that invalid multi-byte sequences are rejected. - */ - function testInvalidMultiByte() { - // Ignore PHP 5.3+ invalid multibyte sequence warning. - $text = @check_plain("Foo\xC0barbaz"); - $this->assertEqual($text, '', 'check_plain() rejects invalid sequence "Foo\xC0barbaz"'); - // Ignore PHP 5.3+ invalid multibyte sequence warning. - $text = @check_plain("\xc2\""); - $this->assertEqual($text, '', 'check_plain() rejects invalid sequence "\xc2\""'); - $text = check_plain("Fooÿñ"); - $this->assertEqual($text, "Fooÿñ", 'check_plain() accepts valid sequence "Fooÿñ"'); - $text = filter_xss("Foo\xC0barbaz"); - $this->assertEqual($text, '', 'filter_xss() rejects invalid sequence "Foo\xC0barbaz"'); - $text = filter_xss("Fooÿñ"); - $this->assertEqual($text, "Fooÿñ", 'filter_xss() accepts valid sequence Fooÿñ'); - } - - /** - * Check that special characters are escaped. - */ - function testEscaping() { - $text = check_plain("<script>"); - $this->assertEqual($text, '<script>', 'check_plain() escapes <script>'); - $text = check_plain('<>&"\''); - $this->assertEqual($text, '<>&"'', 'check_plain() escapes reserved HTML characters.'); - } - - /** - * Test t() and format_string() replacement functionality. - */ - function testFormatStringAndT() { - foreach (array('format_string', 't') as $function) { - $text = $function('Simple text'); - $this->assertEqual($text, 'Simple text', $function . ' leaves simple text alone.'); - $text = $function('Escaped text: @value', array('@value' => '<script>')); - $this->assertEqual($text, 'Escaped text: <script>', $function . ' replaces and escapes string.'); - $text = $function('Placeholder text: %value', array('%value' => '<script>')); - $this->assertEqual($text, 'Placeholder text: <em class="placeholder"><script></em>', $function . ' replaces, escapes and themes string.'); - $text = $function('Verbatim text: !value', array('!value' => '<script>')); - $this->assertEqual($text, 'Verbatim text: <script>', $function . ' replaces verbatim string as-is.'); - } - } - - /** - * Check that harmful protocols are stripped. - */ - function testBadProtocolStripping() { - // Ensure that check_url() strips out harmful protocols, and encodes for - // HTML. Ensure drupal_strip_dangerous_protocols() can be used to return a - // plain-text string stripped of harmful protocols. - $url = 'javascript:http://www.example.com/?x=1&y=2'; - $expected_plain = 'http://www.example.com/?x=1&y=2'; - $expected_html = 'http://www.example.com/?x=1&y=2'; - $this->assertIdentical(check_url($url), $expected_html, t('check_url() filters a URL and encodes it for HTML.')); - $this->assertIdentical(drupal_strip_dangerous_protocols($url), $expected_plain, t('drupal_strip_dangerous_protocols() filters a URL and returns plain text.')); - } -} - -class CommonSizeTestCase extends DrupalUnitTestCase { - protected $exact_test_cases; - protected $rounded_test_cases; - - public static function getInfo() { - return array( - 'name' => 'Size parsing test', - 'description' => 'Parse a predefined amount of bytes and compare the output with the expected value.', - 'group' => 'System' - ); - } - - function setUp() { - $kb = DRUPAL_KILOBYTE; - $this->exact_test_cases = array( - '1 byte' => 1, - '1 KB' => $kb, - '1 MB' => $kb * $kb, - '1 GB' => $kb * $kb * $kb, - '1 TB' => $kb * $kb * $kb * $kb, - '1 PB' => $kb * $kb * $kb * $kb * $kb, - '1 EB' => $kb * $kb * $kb * $kb * $kb * $kb, - '1 ZB' => $kb * $kb * $kb * $kb * $kb * $kb * $kb, - '1 YB' => $kb * $kb * $kb * $kb * $kb * $kb * $kb * $kb, - ); - $this->rounded_test_cases = array( - '2 bytes' => 2, - '1 MB' => ($kb * $kb) - 1, // rounded to 1 MB (not 1000 or 1024 kilobyte!) - round(3623651 / ($this->exact_test_cases['1 MB']), 2) . ' MB' => 3623651, // megabytes - round(67234178751368124 / ($this->exact_test_cases['1 PB']), 2) . ' PB' => 67234178751368124, // petabytes - round(235346823821125814962843827 / ($this->exact_test_cases['1 YB']), 2) . ' YB' => 235346823821125814962843827, // yottabytes - ); - parent::setUp(); - } - - /** - * Check that format_size() returns the expected string. - */ - function testCommonFormatSize() { - foreach (array($this->exact_test_cases, $this->rounded_test_cases) as $test_cases) { - foreach ($test_cases as $expected => $input) { - $this->assertEqual( - ($result = format_size($input, NULL)), - $expected, - $expected . ' == ' . $result . ' (' . $input . ' bytes)' - ); - } - } - } - - /** - * Check that parse_size() returns the proper byte sizes. - */ - function testCommonParseSize() { - foreach ($this->exact_test_cases as $string => $size) { - $this->assertEqual( - $parsed_size = parse_size($string), - $size, - $size . ' == ' . $parsed_size . ' (' . $string . ')' - ); - } - - // Some custom parsing tests - $string = '23476892 bytes'; - $this->assertEqual( - ($parsed_size = parse_size($string)), - $size = 23476892, - $string . ' == ' . $parsed_size . ' bytes' - ); - $string = '76MRandomStringThatShouldBeIgnoredByParseSize.'; // 76 MB - $this->assertEqual( - $parsed_size = parse_size($string), - $size = 79691776, - $string . ' == ' . $parsed_size . ' bytes' - ); - $string = '76.24 Giggabyte'; // Misspeld text -> 76.24 GB - $this->assertEqual( - $parsed_size = parse_size($string), - $size = 81862076662, - $string . ' == ' . $parsed_size . ' bytes' - ); - } - - /** - * Cross-test parse_size() and format_size(). - */ - function testCommonParseSizeFormatSize() { - foreach ($this->exact_test_cases as $size) { - $this->assertEqual( - $size, - ($parsed_size = parse_size($string = format_size($size, NULL))), - $size . ' == ' . $parsed_size . ' (' . $string . ')' - ); - } - } -} - -/** - * Test drupal_explode_tags() and drupal_implode_tags(). - */ -class DrupalTagsHandlingTestCase extends DrupalWebTestCase { - var $validTags = array( - 'Drupal' => 'Drupal', - 'Drupal with some spaces' => 'Drupal with some spaces', - '"Legendary Drupal mascot of doom: ""Druplicon"""' => 'Legendary Drupal mascot of doom: "Druplicon"', - '"Drupal, although it rhymes with sloopal, is as awesome as a troopal!"' => 'Drupal, although it rhymes with sloopal, is as awesome as a troopal!', - ); - - public static function getInfo() { - return array( - 'name' => 'Drupal tags handling', - 'description' => "Performs tests on Drupal's handling of tags, both explosion and implosion tactics used.", - 'group' => 'System' - ); - } - - /** - * Explode a series of tags. - */ - function testDrupalExplodeTags() { - $string = implode(', ', array_keys($this->validTags)); - $tags = drupal_explode_tags($string); - $this->assertTags($tags); - } - - /** - * Implode a series of tags. - */ - function testDrupalImplodeTags() { - $tags = array_values($this->validTags); - // Let's explode and implode to our heart's content. - for ($i = 0; $i < 10; $i++) { - $string = drupal_implode_tags($tags); - $tags = drupal_explode_tags($string); - } - $this->assertTags($tags); - } - - /** - * Helper function: asserts that the ending array of tags is what we wanted. - */ - function assertTags($tags) { - $original = $this->validTags; - foreach ($tags as $tag) { - $key = array_search($tag, $original); - $this->assertTrue($key, t('Make sure tag %tag shows up in the final tags array (originally %original)', array('%tag' => $tag, '%original' => $key))); - unset($original[$key]); - } - foreach ($original as $leftover) { - $this->fail(t('Leftover tag %leftover was left over.', array('%leftover' => $leftover))); - } - } -} - -/** - * Test the Drupal CSS system. - */ -class CascadingStylesheetsTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Cascading stylesheets', - 'description' => 'Tests adding various cascading stylesheets to the page.', - 'group' => 'System', - ); - } - - function setUp() { - parent::setUp('php', 'locale', 'common_test'); - // Reset drupal_add_css() before each test. - drupal_static_reset('drupal_add_css'); - } - - /** - * Check default stylesheets as empty. - */ - function testDefault() { - $this->assertEqual(array(), drupal_add_css(), t('Default CSS is empty.')); - } - - /** - * Test that stylesheets in module .info files are loaded. - */ - function testModuleInfo() { - $this->drupalGet(''); - - // Verify common_test.css in a STYLE media="all" tag. - $elements = $this->xpath('//style[@media=:media and contains(text(), :filename)]', array( - ':media' => 'all', - ':filename' => 'tests/common_test.css', - )); - $this->assertTrue(count($elements), "Stylesheet with media 'all' in module .info file found."); - - // Verify common_test.print.css in a STYLE media="print" tag. - $elements = $this->xpath('//style[@media=:media and contains(text(), :filename)]', array( - ':media' => 'print', - ':filename' => 'tests/common_test.print.css', - )); - $this->assertTrue(count($elements), "Stylesheet with media 'print' in module .info file found."); - } - - /** - * Tests adding a file stylesheet. - */ - function testAddFile() { - $path = drupal_get_path('module', 'simpletest') . '/simpletest.css'; - $css = drupal_add_css($path); - $this->assertEqual($css[$path]['data'], $path, t('Adding a CSS file caches it properly.')); - } - - /** - * Tests adding an external stylesheet. - */ - function testAddExternal() { - $path = 'http://example.com/style.css'; - $css = drupal_add_css($path, 'external'); - $this->assertEqual($css[$path]['type'], 'external', t('Adding an external CSS file caches it properly.')); - } - - /** - * Makes sure that reseting the CSS empties the cache. - */ - function testReset() { - drupal_static_reset('drupal_add_css'); - $this->assertEqual(array(), drupal_add_css(), t('Resetting the CSS empties the cache.')); - } - - /** - * Tests rendering the stylesheets. - */ - function testRenderFile() { - $css = drupal_get_path('module', 'simpletest') . '/simpletest.css'; - drupal_add_css($css); - $styles = drupal_get_css(); - $this->assertTrue(strpos($styles, $css) > 0, t('Rendered CSS includes the added stylesheet.')); - } - - /** - * Tests rendering an external stylesheet. - */ - function testRenderExternal() { - $css = 'http://example.com/style.css'; - drupal_add_css($css, 'external'); - $styles = drupal_get_css(); - // Stylesheet URL may be the href of a LINK tag or in an @import statement - // of a STYLE tag. - $this->assertTrue(strpos($styles, 'href="' . $css) > 0 || strpos($styles, '@import url("' . $css . '")') > 0, t('Rendering an external CSS file.')); - } - - /** - * Tests rendering inline stylesheets with preprocessing on. - */ - function testRenderInlinePreprocess() { - $css = 'body { padding: 0px; }'; - $css_preprocessed = '<style type="text/css" media="all">' . "\n<!--/*--><![CDATA[/*><!--*/\n" . drupal_load_stylesheet_content($css, TRUE) . "\n/*]]>*/-->\n" . '</style>'; - drupal_add_css($css, array('type' => 'inline')); - $styles = drupal_get_css(); - $this->assertEqual(trim($styles), $css_preprocessed, t('Rendering preprocessed inline CSS adds it to the page.')); - } - - /** - * Tests rendering inline stylesheets with preprocessing off. - */ - function testRenderInlineNoPreprocess() { - $css = 'body { padding: 0px; }'; - drupal_add_css($css, array('type' => 'inline', 'preprocess' => FALSE)); - $styles = drupal_get_css(); - $this->assertTrue(strpos($styles, $css) > 0, t('Rendering non-preprocessed inline CSS adds it to the page.')); - } - - /** - * Tests rendering inline stylesheets through a full page request. - */ - function testRenderInlineFullPage() { - $css = 'body { font-size: 254px; }'; - // Inline CSS is minified unless 'preprocess' => FALSE is passed as a - // drupal_add_css() option. - $expected = 'body{font-size:254px;}'; - - // Create a node, using the PHP filter that tests drupal_add_css(). - $php_format_id = 'php_code'; - $settings = array( - 'type' => 'page', - 'body' => array( - LANGUAGE_NONE => array( - array( - 'value' => t('This tests the inline CSS!') . "<?php drupal_add_css('$css', 'inline'); ?>", - 'format' => $php_format_id, - ), - ), - ), - 'promote' => 1, - ); - $node = $this->drupalCreateNode($settings); - - // Fetch the page. - $this->drupalGet('node/' . $node->nid); - $this->assertRaw($expected, t('Inline stylesheets appear in the full page rendering.')); - } - - /** - * Test CSS ordering. - */ - function testRenderOrder() { - // A module CSS file. - drupal_add_css(drupal_get_path('module', 'simpletest') . '/simpletest.css'); - // A few system CSS files, ordered in a strange way. - $system_path = drupal_get_path('module', 'system'); - drupal_add_css($system_path . '/system.menus.css', array('group' => CSS_SYSTEM)); - drupal_add_css($system_path . '/system.base.css', array('group' => CSS_SYSTEM, 'weight' => -10)); - drupal_add_css($system_path . '/system.theme.css', array('group' => CSS_SYSTEM)); - - $expected = array( - $system_path . '/system.base.css', - $system_path . '/system.menus.css', - $system_path . '/system.theme.css', - drupal_get_path('module', 'simpletest') . '/simpletest.css', - ); - - - $styles = drupal_get_css(); - // Stylesheet URL may be the href of a LINK tag or in an @import statement - // of a STYLE tag. - if (preg_match_all('/(href="|url\(")' . preg_quote($GLOBALS['base_url'] . '/', '/') . '([^?]+)\?/', $styles, $matches)) { - $result = $matches[2]; - } - else { - $result = array(); - } - - $this->assertIdentical($result, $expected, t('The CSS files are in the expected order.')); - } - - /** - * Test CSS override. - */ - function testRenderOverride() { - $system = drupal_get_path('module', 'system'); - $simpletest = drupal_get_path('module', 'simpletest'); - - drupal_add_css($system . '/system.base.css'); - drupal_add_css($simpletest . '/tests/system.base.css'); - - // The dummy stylesheet should be the only one included. - $styles = drupal_get_css(); - $this->assert(strpos($styles, $simpletest . '/tests/system.base.css') !== FALSE, t('The overriding CSS file is output.')); - $this->assert(strpos($styles, $system . '/system.base.css') === FALSE, t('The overridden CSS file is not output.')); - - drupal_add_css($simpletest . '/tests/system.base.css'); - drupal_add_css($system . '/system.base.css'); - - // The standard stylesheet should be the only one included. - $styles = drupal_get_css(); - $this->assert(strpos($styles, $system . '/system.base.css') !== FALSE, t('The overriding CSS file is output.')); - $this->assert(strpos($styles, $simpletest . '/tests/system.base.css') === FALSE, t('The overridden CSS file is not output.')); - } - - /** - * Tests Locale module's CSS Alter to include RTL overrides. - */ - function testAlter() { - // Switch the language to a right to left language and add system.base.css. - global $language; - $language->direction = LANGUAGE_RTL; - $path = drupal_get_path('module', 'system'); - drupal_add_css($path . '/system.base.css'); - - // Check to see if system.base-rtl.css was also added. - $styles = drupal_get_css(); - $this->assert(strpos($styles, $path . '/system.base-rtl.css') !== FALSE, t('CSS is alterable as right to left overrides are added.')); - - // Change the language back to left to right. - $language->direction = LANGUAGE_LTR; - } - - /** - * Tests that the query string remains intact when adding CSS files that have - * query string parameters. - */ - function testAddCssFileWithQueryString() { - $this->drupalGet('common-test/query-string'); - $query_string = variable_get('css_js_query_string', '0'); - $this->assertRaw(drupal_get_path('module', 'node') . '/node.css?' . $query_string, t('Query string was appended correctly to css.')); - $this->assertRaw(drupal_get_path('module', 'node') . '/node-fake.css?arg1=value1&arg2=value2', t('Query string not escaped on a URI.')); - } -} - -/** - * Test for cleaning HTML identifiers. - */ -class DrupalHTMLIdentifierTestCase extends DrupalUnitTestCase { - public static function getInfo() { - return array( - 'name' => 'HTML identifiers', - 'description' => 'Test the functions drupal_html_class(), drupal_html_id() and drupal_clean_css_identifier() for expected behavior', - 'group' => 'System', - ); - } - - /** - * Tests that drupal_clean_css_identifier() cleans the identifier properly. - */ - function testDrupalCleanCSSIdentifier() { - // Verify that no valid ASCII characters are stripped from the identifier. - $identifier = 'abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789'; - $this->assertIdentical(drupal_clean_css_identifier($identifier, array()), $identifier, t('Verify valid ASCII characters pass through.')); - - // Verify that valid UTF-8 characters are not stripped from the identifier. - $identifier = '¡¢£¤¥'; - $this->assertIdentical(drupal_clean_css_identifier($identifier, array()), $identifier, t('Verify valid UTF-8 characters pass through.')); - - // Verify that invalid characters (including non-breaking space) are stripped from the identifier. - $this->assertIdentical(drupal_clean_css_identifier('invalid !"#$%&\'()*+,./:;<=>?@[\\]^`{|}~ identifier', array()), 'invalididentifier', t('Strip invalid characters.')); - } - - /** - * Tests that drupal_html_class() cleans the class name properly. - */ - function testDrupalHTMLClass() { - // Verify Drupal coding standards are enforced. - $this->assertIdentical(drupal_html_class('CLASS NAME_[Ü]'), 'class-name--ü', t('Enforce Drupal coding standards.')); - } - - /** - * Tests that drupal_html_id() cleans the ID properly. - */ - function testDrupalHTMLId() { - // Verify that letters, digits, and hyphens are not stripped from the ID. - $id = 'abcdefghijklmnopqrstuvwxyz-0123456789'; - $this->assertIdentical(drupal_html_id($id), $id, t('Verify valid characters pass through.')); - - // Verify that invalid characters are stripped from the ID. - $this->assertIdentical(drupal_html_id('invalid,./:@\\^`{Üidentifier'), 'invalididentifier', t('Strip invalid characters.')); - - // Verify Drupal coding standards are enforced. - $this->assertIdentical(drupal_html_id('ID NAME_[1]'), 'id-name-1', t('Enforce Drupal coding standards.')); - - // Reset the static cache so we can ensure the unique id count is at zero. - drupal_static_reset('drupal_html_id'); - - // Clean up IDs with invalid starting characters. - $this->assertIdentical(drupal_html_id('test-unique-id'), 'test-unique-id', t('Test the uniqueness of IDs #1.')); - $this->assertIdentical(drupal_html_id('test-unique-id'), 'test-unique-id--2', t('Test the uniqueness of IDs #2.')); - $this->assertIdentical(drupal_html_id('test-unique-id'), 'test-unique-id--3', t('Test the uniqueness of IDs #3.')); - } -} - -/** - * CSS Unit Tests. - */ -class CascadingStylesheetsUnitTest extends DrupalUnitTestCase { - public static function getInfo() { - return array( - 'name' => 'CSS Unit Tests', - 'description' => 'Unit tests on CSS functions like aggregation.', - 'group' => 'System', - ); - } - - /** - * Tests basic CSS loading with and without optimization via drupal_load_stylesheet(). - * - * Known tests: - * - Retain white-space in selectors. (http://drupal.org/node/472820) - * - Proper URLs in imported files. (http://drupal.org/node/265719) - * - Retain pseudo-selectors. (http://drupal.org/node/460448) - */ - function testLoadCssBasic() { - // Array of files to test living in 'simpletest/files/css_test_files/'. - // - Original: name.css - // - Unoptimized expected content: name.css.unoptimized.css - // - Optimized expected content: name.css.optimized.css - $testfiles = array( - 'css_input_without_import.css', - 'css_input_with_import.css', - 'comment_hacks.css' - ); - $path = drupal_get_path('module', 'simpletest') . '/files/css_test_files'; - foreach ($testfiles as $file) { - $expected = file_get_contents("$path/$file.unoptimized.css"); - $unoptimized_output = drupal_load_stylesheet("$path/$file.unoptimized.css", FALSE); - $this->assertEqual($unoptimized_output, $expected, t('Unoptimized CSS file has expected contents (@file)', array('@file' => $file))); - - $expected = file_get_contents("$path/$file.optimized.css"); - $optimized_output = drupal_load_stylesheet("$path/$file", TRUE); - $this->assertEqual($optimized_output, $expected, t('Optimized CSS file has expected contents (@file)', array('@file' => $file))); - - // Repeat the tests by accessing the stylesheets by URL. - $expected = file_get_contents("$path/$file.unoptimized.css"); - $unoptimized_output_url = drupal_load_stylesheet($GLOBALS['base_url'] . "/$path/$file.unoptimized.css", FALSE); - $this->assertEqual($unoptimized_output, $expected, t('Unoptimized CSS file (loaded from an URL) has expected contents (@file)', array('@file' => $file))); - - $expected = file_get_contents("$path/$file.optimized.css"); - $optimized_output = drupal_load_stylesheet($GLOBALS['base_url'] . "/$path/$file", TRUE); - $this->assertEqual($optimized_output, $expected, t('Optimized CSS file (loaded from an URL) has expected contents (@file)', array('@file' => $file))); - } - } -} - -/** - * Test drupal_http_request(). - */ -class DrupalHTTPRequestTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Drupal HTTP request', - 'description' => "Performs tests on Drupal's HTTP request mechanism.", - 'group' => 'System' - ); - } - - function setUp() { - parent::setUp('system_test'); - } - - function testDrupalHTTPRequest() { - global $is_https; - - // Parse URL schema. - $missing_scheme = drupal_http_request('example.com/path'); - $this->assertEqual($missing_scheme->code, -1002, t('Returned with "-1002" error code.')); - $this->assertEqual($missing_scheme->error, 'missing schema', t('Returned with "missing schema" error message.')); - - $unable_to_parse = drupal_http_request('http:///path'); - $this->assertEqual($unable_to_parse->code, -1001, t('Returned with "-1001" error code.')); - $this->assertEqual($unable_to_parse->error, 'unable to parse URL', t('Returned with "unable to parse URL" error message.')); - - // Fetch page. - $result = drupal_http_request(url('node', array('absolute' => TRUE))); - $this->assertEqual($result->code, 200, t('Fetched page successfully.')); - $this->drupalSetContent($result->data); - $this->assertTitle(t('Welcome to @site-name | @site-name', array('@site-name' => variable_get('site_name', 'Drupal'))), t('Site title matches.')); - - // Test that code and status message is returned. - $result = drupal_http_request(url('pagedoesnotexist', array('absolute' => TRUE))); - $this->assertTrue(!empty($result->protocol), t('Result protocol is returned.')); - $this->assertEqual($result->code, '404', t('Result code is 404')); - $this->assertEqual($result->status_message, 'Not Found', t('Result status message is "Not Found"')); - - // Skip the timeout tests when the testing environment is HTTPS because - // stream_set_timeout() does not work for SSL connections. - // @link http://bugs.php.net/bug.php?id=47929 - if (!$is_https) { - // Test that timeout is respected. The test machine is expected to be able - // to make the connection (i.e. complete the fsockopen()) in 2 seconds and - // return within a total of 5 seconds. If the test machine is extremely - // slow, the test will fail. fsockopen() has been seen to time out in - // slightly less than the specified timeout, so allow a little slack on - // the minimum expected time (i.e. 1.8 instead of 2). - timer_start(__METHOD__); - $result = drupal_http_request(url('system-test/sleep/10', array('absolute' => TRUE)), array('timeout' => 2)); - $time = timer_read(__METHOD__) / 1000; - $this->assertTrue(1.8 < $time && $time < 5, t('Request timed out (%time seconds).', array('%time' => $time))); - $this->assertTrue($result->error, t('An error message was returned.')); - $this->assertEqual($result->code, HTTP_REQUEST_TIMEOUT, t('Proper error code was returned.')); - } - } - - function testDrupalHTTPRequestBasicAuth() { - $username = $this->randomName(); - $password = $this->randomName(); - $url = url('system-test/auth', array('absolute' => TRUE)); - - $auth = str_replace('://', '://' . $username . ':' . $password . '@', $url); - $result = drupal_http_request($auth); - - $this->drupalSetContent($result->data); - $this->assertRaw($username, t('$_SERVER["PHP_AUTH_USER"] is passed correctly.')); - $this->assertRaw($password, t('$_SERVER["PHP_AUTH_PW"] is passed correctly.')); - } - - function testDrupalHTTPRequestRedirect() { - $redirect_301 = drupal_http_request(url('system-test/redirect/301', array('absolute' => TRUE)), array('max_redirects' => 1)); - $this->assertEqual($redirect_301->redirect_code, 301, t('drupal_http_request follows the 301 redirect.')); - - $redirect_301 = drupal_http_request(url('system-test/redirect/301', array('absolute' => TRUE)), array('max_redirects' => 0)); - $this->assertFalse(isset($redirect_301->redirect_code), t('drupal_http_request does not follow 301 redirect if max_redirects = 0.')); - - $redirect_invalid = drupal_http_request(url('system-test/redirect-noscheme', array('absolute' => TRUE)), array('max_redirects' => 1)); - $this->assertEqual($redirect_invalid->code, -1002, t('301 redirect to invalid URL returned with error code !error.', array('!error' => $redirect_invalid->error))); - $this->assertEqual($redirect_invalid->error, 'missing schema', t('301 redirect to invalid URL returned with error message "!error".', array('!error' => $redirect_invalid->error))); - - $redirect_invalid = drupal_http_request(url('system-test/redirect-noparse', array('absolute' => TRUE)), array('max_redirects' => 1)); - $this->assertEqual($redirect_invalid->code, -1001, t('301 redirect to invalid URL returned with error message code "!error".', array('!error' => $redirect_invalid->error))); - $this->assertEqual($redirect_invalid->error, 'unable to parse URL', t('301 redirect to invalid URL returned with error message "!error".', array('!error' => $redirect_invalid->error))); - - $redirect_invalid = drupal_http_request(url('system-test/redirect-invalid-scheme', array('absolute' => TRUE)), array('max_redirects' => 1)); - $this->assertEqual($redirect_invalid->code, -1003, t('301 redirect to invalid URL returned with error code !error.', array('!error' => $redirect_invalid->error))); - $this->assertEqual($redirect_invalid->error, 'invalid schema ftp', t('301 redirect to invalid URL returned with error message "!error".', array('!error' => $redirect_invalid->error))); - - $redirect_302 = drupal_http_request(url('system-test/redirect/302', array('absolute' => TRUE)), array('max_redirects' => 1)); - $this->assertEqual($redirect_302->redirect_code, 302, t('drupal_http_request follows the 302 redirect.')); - - $redirect_302 = drupal_http_request(url('system-test/redirect/302', array('absolute' => TRUE)), array('max_redirects' => 0)); - $this->assertFalse(isset($redirect_302->redirect_code), t('drupal_http_request does not follow 302 redirect if $retry = 0.')); - - $redirect_307 = drupal_http_request(url('system-test/redirect/307', array('absolute' => TRUE)), array('max_redirects' => 1)); - $this->assertEqual($redirect_307->redirect_code, 307, t('drupal_http_request follows the 307 redirect.')); - - $redirect_307 = drupal_http_request(url('system-test/redirect/307', array('absolute' => TRUE)), array('max_redirects' => 0)); - $this->assertFalse(isset($redirect_307->redirect_code), t('drupal_http_request does not follow 307 redirect if max_redirects = 0.')); - - $multiple_redirect_final_url = url('system-test/multiple-redirects/0', array('absolute' => TRUE)); - $multiple_redirect_1 = drupal_http_request(url('system-test/multiple-redirects/1', array('absolute' => TRUE)), array('max_redirects' => 1)); - $this->assertEqual($multiple_redirect_1->redirect_url, $multiple_redirect_final_url, t('redirect_url contains the final redirection location after 1 redirect.')); - - $multiple_redirect_3 = drupal_http_request(url('system-test/multiple-redirects/3', array('absolute' => TRUE)), array('max_redirects' => 3)); - $this->assertEqual($multiple_redirect_3->redirect_url, $multiple_redirect_final_url, t('redirect_url contains the final redirection location after 3 redirects.')); - } -} - -/** - * Testing drupal_add_region_content and drupal_get_region_content. - */ -class DrupalSetContentTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Drupal set/get regions', - 'description' => 'Performs tests on setting and retrieiving content from theme regions.', - 'group' => 'System' - ); - } - - - /** - * Test setting and retrieving content for theme regions. - */ - function testRegions() { - global $theme_key; - - $block_regions = array_keys(system_region_list($theme_key)); - $delimiter = $this->randomName(32); - $values = array(); - // Set some random content for each region available. - foreach ($block_regions as $region) { - $first_chunk = $this->randomName(32); - drupal_add_region_content($region, $first_chunk); - $second_chunk = $this->randomName(32); - drupal_add_region_content($region, $second_chunk); - // Store the expected result for a drupal_get_region_content call for this region. - $values[$region] = $first_chunk . $delimiter . $second_chunk; - } - - // Ensure drupal_get_region_content returns expected results when fetching all regions. - $content = drupal_get_region_content(NULL, $delimiter); - foreach ($content as $region => $region_content) { - $this->assertEqual($region_content, $values[$region], t('@region region text verified when fetching all regions', array('@region' => $region))); - } - - // Ensure drupal_get_region_content returns expected results when fetching a single region. - foreach ($block_regions as $region) { - $region_content = drupal_get_region_content($region, $delimiter); - $this->assertEqual($region_content, $values[$region], t('@region region text verified when fetching single region.', array('@region' => $region))); - } - } -} - -/** - * Testing drupal_goto and hook_drupal_goto_alter(). - */ -class DrupalGotoTest extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Drupal goto', - 'description' => 'Performs tests on the drupal_goto function and hook_drupal_goto_alter', - 'group' => 'System' - ); - } - - function setUp() { - parent::setUp('common_test'); - } - - /** - * Test drupal_goto(). - */ - function testDrupalGoto() { - $this->drupalGet('common-test/drupal_goto/redirect'); - $headers = $this->drupalGetHeaders(TRUE); - list(, $status) = explode(' ', $headers[0][':status'], 3); - $this->assertEqual($status, 302, t('Expected response code was sent.')); - $this->assertText('drupal_goto', t('Drupal goto redirect succeeded.')); - $this->assertEqual($this->getUrl(), url('common-test/drupal_goto', array('absolute' => TRUE)), t('Drupal goto redirected to expected URL.')); - - $this->drupalGet('common-test/drupal_goto/redirect_advanced'); - $headers = $this->drupalGetHeaders(TRUE); - list(, $status) = explode(' ', $headers[0][':status'], 3); - $this->assertEqual($status, 301, t('Expected response code was sent.')); - $this->assertText('drupal_goto', t('Drupal goto redirect succeeded.')); - $this->assertEqual($this->getUrl(), url('common-test/drupal_goto', array('query' => array('foo' => '123'), 'absolute' => TRUE)), t('Drupal goto redirected to expected URL.')); - - // Test that drupal_goto() respects ?destination=xxx. Use an complicated URL - // to test that the path is encoded and decoded properly. - $destination = 'common-test/drupal_goto/destination?foo=%2525&bar=123'; - $this->drupalGet('common-test/drupal_goto/redirect', array('query' => array('destination' => $destination))); - $this->assertText('drupal_goto', t('Drupal goto redirect with destination succeeded.')); - $this->assertEqual($this->getUrl(), url('common-test/drupal_goto/destination', array('query' => array('foo' => '%25', 'bar' => '123'), 'absolute' => TRUE)), t('Drupal goto redirected to given query string destination.')); - } - - /** - * Test hook_drupal_goto_alter(). - */ - function testDrupalGotoAlter() { - $this->drupalGet('common-test/drupal_goto/redirect_fail'); - - $this->assertNoText(t("Drupal goto failed to stop program"), t("Drupal goto stopped program.")); - $this->assertNoText('drupal_goto_fail', t("Drupal goto redirect failed.")); - } - - /** - * Test drupal_get_destination(). - */ - function testDrupalGetDestination() { - $query = $this->randomName(10); - - // Verify that a 'destination' query string is used as destination. - $this->drupalGet('common-test/destination', array('query' => array('destination' => $query))); - $this->assertText('The destination: ' . $query, t('The given query string destination is determined as destination.')); - - // Verify that the current path is used as destination. - $this->drupalGet('common-test/destination', array('query' => array($query => NULL))); - $url = 'common-test/destination?' . $query; - $this->assertText('The destination: ' . $url, t('The current path is determined as destination.')); - } -} - -/** - * Tests for the JavaScript system. - */ -class JavaScriptTestCase extends DrupalWebTestCase { - /** - * Store configured value for JavaScript preprocessing. - */ - protected $preprocess_js = NULL; - - public static function getInfo() { - return array( - 'name' => 'JavaScript', - 'description' => 'Tests the JavaScript system.', - 'group' => 'System' - ); - } - - function setUp() { - // Enable Locale and SimpleTest in the test environment. - parent::setUp('locale', 'simpletest', 'common_test'); - - // Disable preprocessing - $this->preprocess_js = variable_get('preprocess_js', 0); - variable_set('preprocess_js', 0); - - // Reset drupal_add_js() and drupal_add_library() statics before each test. - drupal_static_reset('drupal_add_js'); - drupal_static_reset('drupal_add_library'); - } - - function tearDown() { - // Restore configured value for JavaScript preprocessing. - variable_set('preprocess_js', $this->preprocess_js); - parent::tearDown(); - } - - /** - * Test default JavaScript is empty. - */ - function testDefault() { - $this->assertEqual(array(), drupal_add_js(), t('Default JavaScript is empty.')); - } - - /** - * Test adding a JavaScript file. - */ - function testAddFile() { - $javascript = drupal_add_js('misc/collapse.js'); - $this->assertTrue(array_key_exists('misc/jquery.js', $javascript), t('jQuery is added when a file is added.')); - $this->assertTrue(array_key_exists('misc/drupal.js', $javascript), t('Drupal.js is added when file is added.')); - $this->assertTrue(array_key_exists('misc/collapse.js', $javascript), t('JavaScript files are correctly added.')); - $this->assertEqual(base_path(), $javascript['settings']['data'][0]['basePath'], t('Base path JavaScript setting is correctly set.')); - url('', array('prefix' => &$prefix)); - $this->assertEqual(empty($prefix) ? '' : $prefix, $javascript['settings']['data'][1]['pathPrefix'], t('Path prefix JavaScript setting is correctly set.')); - } - - /** - * Test adding settings. - */ - function testAddSetting() { - $javascript = drupal_add_js(array('drupal' => 'rocks', 'dries' => 280342800), 'setting'); - $this->assertEqual(280342800, $javascript['settings']['data'][2]['dries'], t('JavaScript setting is set correctly.')); - $this->assertEqual('rocks', $javascript['settings']['data'][2]['drupal'], t('The other JavaScript setting is set correctly.')); - } - - /** - * Tests adding an external JavaScript File. - */ - function testAddExternal() { - $path = 'http://example.com/script.js'; - $javascript = drupal_add_js($path, 'external'); - $this->assertTrue(array_key_exists('http://example.com/script.js', $javascript), t('Added an external JavaScript file.')); - } - - /** - * Test drupal_get_js() for JavaScript settings. - */ - function testHeaderSetting() { - // Only the second of these two entries should appear in Drupal.settings. - drupal_add_js(array('commonTest' => 'commonTestShouldNotAppear'), 'setting'); - drupal_add_js(array('commonTest' => 'commonTestShouldAppear'), 'setting'); - // All three of these entries should appear in Drupal.settings. - drupal_add_js(array('commonTestArray' => array('commonTestValue0')), 'setting'); - drupal_add_js(array('commonTestArray' => array('commonTestValue1')), 'setting'); - drupal_add_js(array('commonTestArray' => array('commonTestValue2')), 'setting'); - // Only the second of these two entries should appear in Drupal.settings. - drupal_add_js(array('commonTestArray' => array('key' => 'commonTestOldValue')), 'setting'); - drupal_add_js(array('commonTestArray' => array('key' => 'commonTestNewValue')), 'setting'); - - $javascript = drupal_get_js('header'); - $this->assertTrue(strpos($javascript, 'basePath') > 0, t('Rendered JavaScript header returns basePath setting.')); - $this->assertTrue(strpos($javascript, 'misc/jquery.js') > 0, t('Rendered JavaScript header includes jQuery.')); - $this->assertTrue(strpos($javascript, 'pathPrefix') > 0, t('Rendered JavaScript header returns pathPrefix setting.')); - - // Test whether drupal_add_js can be used to override a previous setting. - $this->assertTrue(strpos($javascript, 'commonTestShouldAppear') > 0, t('Rendered JavaScript header returns custom setting.')); - $this->assertTrue(strpos($javascript, 'commonTestShouldNotAppear') === FALSE, t('drupal_add_js() correctly overrides a custom setting.')); - - // Test whether drupal_add_js can be used to add numerically indexed values - // to an array. - $array_values_appear = strpos($javascript, 'commonTestValue0') > 0 && strpos($javascript, 'commonTestValue1') > 0 && strpos($javascript, 'commonTestValue2') > 0; - $this->assertTrue($array_values_appear, t('drupal_add_js() correctly adds settings to the end of an indexed array.')); - - // Test whether drupal_add_js can be used to override the entry for an - // existing key in an associative array. - $associative_array_override = strpos($javascript, 'commonTestNewValue') > 0 && strpos($javascript, 'commonTestOldValue') === FALSE; - $this->assertTrue($associative_array_override, t('drupal_add_js() correctly overrides settings within an associative array.')); - } - - /** - * Test to see if resetting the JavaScript empties the cache. - */ - function testReset() { - drupal_add_js('misc/collapse.js'); - drupal_static_reset('drupal_add_js'); - $this->assertEqual(array(), drupal_add_js(), t('Resetting the JavaScript correctly empties the cache.')); - } - - /** - * Test adding inline scripts. - */ - function testAddInline() { - $inline = 'jQuery(function () { });'; - $javascript = drupal_add_js($inline, array('type' => 'inline', 'scope' => 'footer')); - $this->assertTrue(array_key_exists('misc/jquery.js', $javascript), t('jQuery is added when inline scripts are added.')); - $data = end($javascript); - $this->assertEqual($inline, $data['data'], t('Inline JavaScript is correctly added to the footer.')); - } - - /** - * Test rendering an external JavaScript file. - */ - function testRenderExternal() { - $external = 'http://example.com/example.js'; - drupal_add_js($external, 'external'); - $javascript = drupal_get_js(); - // Local files have a base_path() prefix, external files should not. - $this->assertTrue(strpos($javascript, 'src="' . $external) > 0, t('Rendering an external JavaScript file.')); - } - - /** - * Test drupal_get_js() with a footer scope. - */ - function testFooterHTML() { - $inline = 'jQuery(function () { });'; - drupal_add_js($inline, array('type' => 'inline', 'scope' => 'footer')); - $javascript = drupal_get_js('footer'); - $this->assertTrue(strpos($javascript, $inline) > 0, t('Rendered JavaScript footer returns the inline code.')); - } - - /** - * Test drupal_add_js() sets preproccess to false when cache is set to false. - */ - function testNoCache() { - $javascript = drupal_add_js('misc/collapse.js', array('cache' => FALSE)); - $this->assertFalse($javascript['misc/collapse.js']['preprocess'], t('Setting cache to FALSE sets proprocess to FALSE when adding JavaScript.')); - } - - /** - * Test adding a JavaScript file with a different group. - */ - function testDifferentGroup() { - $javascript = drupal_add_js('misc/collapse.js', array('group' => JS_THEME)); - $this->assertEqual($javascript['misc/collapse.js']['group'], JS_THEME, t('Adding a JavaScript file with a different group caches the given group.')); - } - - /** - * Test adding a JavaScript file with a different weight. - */ - function testDifferentWeight() { - $javascript = drupal_add_js('misc/collapse.js', array('weight' => 2)); - $this->assertEqual($javascript['misc/collapse.js']['weight'], 2, t('Adding a JavaScript file with a different weight caches the given weight.')); - } - - /** - * Test JavaScript ordering. - */ - function testRenderOrder() { - // Add a bunch of JavaScript in strange ordering. - drupal_add_js('(function($){alert("Weight 5 #1");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => 5)); - drupal_add_js('(function($){alert("Weight 0 #1");})(jQuery);', array('type' => 'inline', 'scope' => 'footer')); - drupal_add_js('(function($){alert("Weight 0 #2");})(jQuery);', array('type' => 'inline', 'scope' => 'footer')); - drupal_add_js('(function($){alert("Weight -8 #1");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => -8)); - drupal_add_js('(function($){alert("Weight -8 #2");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => -8)); - drupal_add_js('(function($){alert("Weight -8 #3");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => -8)); - drupal_add_js('http://example.com/example.js?Weight -5 #1', array('type' => 'external', 'scope' => 'footer', 'weight' => -5)); - drupal_add_js('(function($){alert("Weight -8 #4");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => -8)); - drupal_add_js('(function($){alert("Weight 5 #2");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => 5)); - drupal_add_js('(function($){alert("Weight 0 #3");})(jQuery);', array('type' => 'inline', 'scope' => 'footer')); - - // Construct the expected result from the regex. - $expected = array( - "-8 #1", - "-8 #2", - "-8 #3", - "-8 #4", - "-5 #1", // The external script. - "0 #1", - "0 #2", - "0 #3", - "5 #1", - "5 #2", - ); - - // Retrieve the rendered JavaScript and test against the regex. - $js = drupal_get_js('footer'); - $matches = array(); - if (preg_match_all('/Weight\s([-0-9]+\s[#0-9]+)/', $js, $matches)) { - $result = $matches[1]; - } - else { - $result = array(); - } - $this->assertIdentical($result, $expected, t('JavaScript is added in the expected weight order.')); - } - - /** - * Test rendering the JavaScript with a file's weight above jQuery's. - */ - function testRenderDifferentWeight() { - // JavaScript files are sorted first by group, then by the 'every_page' - // flag, then by weight (see drupal_sort_css_js()), so to test the effect of - // weight, we need the other two options to be the same. - drupal_add_js('misc/collapse.js', array('group' => JS_LIBRARY, 'every_page' => TRUE, 'weight' => -21)); - $javascript = drupal_get_js(); - $this->assertTrue(strpos($javascript, 'misc/collapse.js') < strpos($javascript, 'misc/jquery.js'), t('Rendering a JavaScript file above jQuery.')); - } - - /** - * Test altering a JavaScript's weight via hook_js_alter(). - * - * @see simpletest_js_alter() - */ - function testAlter() { - // Add both tableselect.js and simpletest.js, with a larger weight on SimpleTest. - drupal_add_js('misc/tableselect.js'); - drupal_add_js(drupal_get_path('module', 'simpletest') . '/simpletest.js', array('weight' => 9999)); - - // Render the JavaScript, testing if simpletest.js was altered to be before - // tableselect.js. See simpletest_js_alter() to see where this alteration - // takes place. - $javascript = drupal_get_js(); - $this->assertTrue(strpos($javascript, 'simpletest.js') < strpos($javascript, 'misc/tableselect.js'), t('Altering JavaScript weight through the alter hook.')); - } - - /** - * Adds a library to the page and tests for both its JavaScript and its CSS. - */ - function testLibraryRender() { - $result = drupal_add_library('system', 'farbtastic'); - $this->assertTrue($result !== FALSE, t('Library was added without errors.')); - $scripts = drupal_get_js(); - $styles = drupal_get_css(); - $this->assertTrue(strpos($scripts, 'misc/farbtastic/farbtastic.js'), t('JavaScript of library was added to the page.')); - $this->assertTrue(strpos($styles, 'misc/farbtastic/farbtastic.css'), t('Stylesheet of library was added to the page.')); - } - - /** - * Adds a JavaScript library to the page and alters it. - * - * @see common_test_library_info_alter() - */ - function testLibraryAlter() { - // Verify that common_test altered the title of Farbtastic. - $library = drupal_get_library('system', 'farbtastic'); - $this->assertEqual($library['title'], 'Farbtastic: Altered Library', t('Registered libraries were altered.')); - - // common_test_library_info_alter() also added a dependency on jQuery Form. - drupal_add_library('system', 'farbtastic'); - $scripts = drupal_get_js(); - $this->assertTrue(strpos($scripts, 'misc/jquery.form.js'), t('Altered library dependencies are added to the page.')); - } - - /** - * Tests that multiple modules can implement the same library. - * - * @see common_test_library_info() - */ - function testLibraryNameConflicts() { - $farbtastic = drupal_get_library('common_test', 'farbtastic'); - $this->assertEqual($farbtastic['title'], 'Custom Farbtastic Library', t('Alternative libraries can be added to the page.')); - } - - /** - * Tests non-existing libraries. - */ - function testLibraryUnknown() { - $result = drupal_get_library('unknown', 'unknown'); - $this->assertFalse($result, t('Unknown library returned FALSE.')); - drupal_static_reset('drupal_get_library'); - - $result = drupal_add_library('unknown', 'unknown'); - $this->assertFalse($result, t('Unknown library returned FALSE.')); - $scripts = drupal_get_js(); - $this->assertTrue(strpos($scripts, 'unknown') === FALSE, t('Unknown library was not added to the page.')); - } - - /** - * Tests the addition of libraries through the #attached['library'] property. - */ - function testAttachedLibrary() { - $element['#attached']['library'][] = array('system', 'farbtastic'); - drupal_render($element); - $scripts = drupal_get_js(); - $this->assertTrue(strpos($scripts, 'misc/farbtastic/farbtastic.js'), t('The attached_library property adds the additional libraries.')); - } - - /** - * Tests retrieval of libraries via drupal_get_library(). - */ - function testGetLibrary() { - // Retrieve all libraries registered by a module. - $libraries = drupal_get_library('common_test'); - $this->assertTrue(isset($libraries['farbtastic']), t('Retrieved all module libraries.')); - // Retrieve all libraries for a module not implementing hook_library_info(). - // Note: This test installs Locale module. - $libraries = drupal_get_library('locale'); - $this->assertEqual($libraries, array(), t('Retrieving libraries from a module not implementing hook_library_info() returns an emtpy array.')); - - // Retrieve a specific library by module and name. - $farbtastic = drupal_get_library('common_test', 'farbtastic'); - $this->assertEqual($farbtastic['version'], '5.3', t('Retrieved a single library.')); - // Retrieve a non-existing library by module and name. - $farbtastic = drupal_get_library('common_test', 'foo'); - $this->assertIdentical($farbtastic, FALSE, t('Retrieving a non-existing library returns FALSE.')); - } - - /** - * Tests that the query string remains intact when adding JavaScript files - * that have query string parameters. - */ - function testAddJsFileWithQueryString() { - $this->drupalGet('common-test/query-string'); - $query_string = variable_get('css_js_query_string', '0'); - $this->assertRaw(drupal_get_path('module', 'node') . '/node.js?' . $query_string, t('Query string was appended correctly to js.')); - } -} - -/** - * Tests for drupal_render(). - */ -class DrupalRenderTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'drupal_render()', - 'description' => 'Performs functional tests on drupal_render().', - 'group' => 'System', - ); - } - - function setUp() { - parent::setUp('common_test'); - } - - /** - * Test sorting by weight. - */ - function testDrupalRenderSorting() { - $first = $this->randomName(); - $second = $this->randomName(); - // Build an array with '#weight' set for each element. - $elements = array( - 'second' => array( - '#weight' => 10, - '#markup' => $second, - ), - 'first' => array( - '#weight' => 0, - '#markup' => $first, - ), - ); - $output = drupal_render($elements); - - // The lowest weight element should appear last in $output. - $this->assertTrue(strpos($output, $second) > strpos($output, $first), t('Elements were sorted correctly by weight.')); - - // Confirm that the $elements array has '#sorted' set to TRUE. - $this->assertTrue($elements['#sorted'], t("'#sorted' => TRUE was added to the array")); - - // Pass $elements through element_children() and ensure it remains - // sorted in the correct order. drupal_render() will return an empty string - // if used on the same array in the same request. - $children = element_children($elements); - $this->assertTrue(array_shift($children) == 'first', t('Child found in the correct order.')); - $this->assertTrue(array_shift($children) == 'second', t('Child found in the correct order.')); - - - // The same array structure again, but with #sorted set to TRUE. - $elements = array( - 'second' => array( - '#weight' => 10, - '#markup' => $second, - ), - 'first' => array( - '#weight' => 0, - '#markup' => $first, - ), - '#sorted' => TRUE, - ); - $output = drupal_render($elements); - - // The elements should appear in output in the same order as the array. - $this->assertTrue(strpos($output, $second) < strpos($output, $first), t('Elements were not sorted.')); - } - - /** - * Test #attached functionality in children elements. - */ - function testDrupalRenderChildrenAttached() { - // The cache system is turned off for POST requests. - $request_method = $_SERVER['REQUEST_METHOD']; - $_SERVER['REQUEST_METHOD'] = 'GET'; - - // Create an element with a child and subchild. Each element loads a - // different JavaScript file using #attached. - $parent_js = drupal_get_path('module', 'user') . '/user.js'; - $child_js = drupal_get_path('module', 'forum') . '/forum.js'; - $subchild_js = drupal_get_path('module', 'book') . '/book.js'; - $element = array( - '#type' => 'fieldset', - '#cache' => array( - 'keys' => array('simpletest', 'drupal_render', 'children_attached'), - ), - '#attached' => array('js' => array($parent_js)), - '#title' => 'Parent', - ); - $element['child'] = array( - '#type' => 'fieldset', - '#attached' => array('js' => array($child_js)), - '#title' => 'Child', - ); - $element['child']['subchild'] = array( - '#attached' => array('js' => array($subchild_js)), - '#markup' => 'Subchild', - ); - - // Render the element and verify the presence of #attached JavaScript. - drupal_render($element); - $scripts = drupal_get_js(); - $this->assertTrue(strpos($scripts, $parent_js), t('The element #attached JavaScript was included.')); - $this->assertTrue(strpos($scripts, $child_js), t('The child #attached JavaScript was included.')); - $this->assertTrue(strpos($scripts, $subchild_js), t('The subchild #attached JavaScript was included.')); - - // Load the element from cache and verify the presence of the #attached - // JavaScript. - drupal_static_reset('drupal_add_js'); - $this->assertTrue(drupal_render_cache_get($element), t('The element was retrieved from cache.')); - $scripts = drupal_get_js(); - $this->assertTrue(strpos($scripts, $parent_js), t('The element #attached JavaScript was included when loading from cache.')); - $this->assertTrue(strpos($scripts, $child_js), t('The child #attached JavaScript was included when loading from cache.')); - $this->assertTrue(strpos($scripts, $subchild_js), t('The subchild #attached JavaScript was included when loading from cache.')); - - $_SERVER['REQUEST_METHOD'] = $request_method; - } - - /** - * Test passing arguments to the theme function. - */ - function testDrupalRenderThemeArguments() { - $element = array( - '#theme' => 'common_test_foo', - ); - // Test that defaults work. - $this->assertEqual(drupal_render($element), 'foobar', 'Defaults work'); - $element = array( - '#theme' => 'common_test_foo', - '#foo' => $this->randomName(), - '#bar' => $this->randomName(), - ); - // Test that passing arguments to the theme function works. - $this->assertEqual(drupal_render($element), $element['#foo'] . $element['#bar'], 'Passing arguments to theme functions works'); - } - - /** - * Test rendering form elements without passing through form_builder(). - */ - function testDrupalRenderFormElements() { - // Define a series of form elements. - $element = array( - '#type' => 'button', - '#value' => $this->randomName(), - ); - $this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'submit')); - - $element = array( - '#type' => 'textfield', - '#title' => $this->randomName(), - '#value' => $this->randomName(), - ); - $this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'text')); - - $element = array( - '#type' => 'password', - '#title' => $this->randomName(), - ); - $this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'password')); - - $element = array( - '#type' => 'textarea', - '#title' => $this->randomName(), - '#value' => $this->randomName(), - ); - $this->assertRenderedElement($element, '//textarea'); - - $element = array( - '#type' => 'radio', - '#title' => $this->randomName(), - '#value' => FALSE, - ); - $this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'radio')); - - $element = array( - '#type' => 'checkbox', - '#title' => $this->randomName(), - ); - $this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'checkbox')); - - $element = array( - '#type' => 'select', - '#title' => $this->randomName(), - '#options' => array( - 0 => $this->randomName(), - 1 => $this->randomName(), - ), - ); - $this->assertRenderedElement($element, '//select'); - - $element = array( - '#type' => 'file', - '#title' => $this->randomName(), - ); - $this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'file')); - - $element = array( - '#type' => 'item', - '#title' => $this->randomName(), - '#markup' => $this->randomName(), - ); - $this->assertRenderedElement($element, '//div[contains(@class, :class) and contains(., :markup)]/label[contains(., :label)]', array( - ':class' => 'form-type-item', - ':markup' => $element['#markup'], - ':label' => $element['#title'], - )); - - $element = array( - '#type' => 'hidden', - '#title' => $this->randomName(), - '#value' => $this->randomName(), - ); - $this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'hidden')); - - $element = array( - '#type' => 'link', - '#title' => $this->randomName(), - '#href' => $this->randomName(), - '#options' => array( - 'absolute' => TRUE, - ), - ); - $this->assertRenderedElement($element, '//a[@href=:href and contains(., :title)]', array( - ':href' => url($element['#href'], array('absolute' => TRUE)), - ':title' => $element['#title'], - )); - - $element = array( - '#type' => 'fieldset', - '#title' => $this->randomName(), - ); - $this->assertRenderedElement($element, '//fieldset/legend[contains(., :title)]', array( - ':title' => $element['#title'], - )); - - $element['item'] = array( - '#type' => 'item', - '#title' => $this->randomName(), - '#markup' => $this->randomName(), - ); - $this->assertRenderedElement($element, '//fieldset/div/div[contains(@class, :class) and contains(., :markup)]', array( - ':class' => 'form-type-item', - ':markup' => $element['item']['#markup'], - )); - } - - /** - * Test rendering elements with invalid keys. - */ - function testDrupalRenderInvalidKeys() { - $error = array( - '%type' => 'User error', - '!message' => '"child" is an invalid render array key', - '%function' => 'element_children()', - ); - $message = t('%type: !message in %function (line ', $error); - - variable_set('error_level', ERROR_REPORTING_DISPLAY_ALL); - $this->drupalGet('common-test/drupal-render-invalid-keys'); - $this->assertResponse(200, t('Received expected HTTP status code.')); - $this->assertRaw($message, t('Found error message: !message.', array('!message' => $message))); - } - - protected function assertRenderedElement(array $element, $xpath, array $xpath_args = array()) { - $original_element = $element; - $this->drupalSetContent(drupal_render($element)); - $this->verbose('<pre>' . check_plain(var_export($original_element, TRUE)) . '</pre>' - . '<pre>' . check_plain(var_export($element, TRUE)) . '</pre>' - . '<hr />' . $this->drupalGetContent() - ); - - // @see DrupalWebTestCase::xpath() - $xpath = $this->buildXPathQuery($xpath, $xpath_args); - $element += array('#value' => NULL); - $this->assertFieldByXPath($xpath, $element['#value'], t('#type @type was properly rendered.', array( - '@type' => var_export($element['#type'], TRUE), - ))); - } -} - -/** - * Test for valid_url(). - */ -class ValidUrlTestCase extends DrupalUnitTestCase { - public static function getInfo() { - return array( - 'name' => 'Valid Url', - 'description' => "Performs tests on Drupal's valid url function.", - 'group' => 'System' - ); - } - - /** - * Test valid absolute urls. - */ - function testValidAbsolute() { - $url_schemes = array('http', 'https', 'ftp'); - $valid_absolute_urls = array( - 'example.com', - 'www.example.com', - 'ex-ample.com', - '3xampl3.com', - 'example.com/paren(the)sis', - 'example.com/index.html#pagetop', - 'example.com:8080', - 'subdomain.example.com', - 'example.com/index.php?q=node', - 'example.com/index.php?q=node¶m=false', - 'user@www.example.com', - 'user:pass@www.example.com:8080/login.php?do=login&style=%23#pagetop', - '127.0.0.1', - 'example.org?', - 'john%20doe:secret:foo@example.org/', - 'example.org/~,$\'*;', - 'caf%C3%A9.example.org', - '[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html', - ); - - foreach ($url_schemes as $scheme) { - foreach ($valid_absolute_urls as $url) { - $test_url = $scheme . '://' . $url; - $valid_url = valid_url($test_url, TRUE); - $this->assertTrue($valid_url, t('@url is a valid url.', array('@url' => $test_url))); - } - } - } - - /** - * Test invalid absolute urls. - */ - function testInvalidAbsolute() { - $url_schemes = array('http', 'https', 'ftp'); - $invalid_ablosule_urls = array( - '', - 'ex!ample.com', - 'ex%ample.com', - ); - - foreach ($url_schemes as $scheme) { - foreach ($invalid_ablosule_urls as $url) { - $test_url = $scheme . '://' . $url; - $valid_url = valid_url($test_url, TRUE); - $this->assertFalse($valid_url, t('@url is NOT a valid url.', array('@url' => $test_url))); - } - } - } - - /** - * Test valid relative urls. - */ - function testValidRelative() { - $valid_relative_urls = array( - 'paren(the)sis', - 'index.html#pagetop', - 'index.php?q=node', - 'index.php?q=node¶m=false', - 'login.php?do=login&style=%23#pagetop', - ); - - foreach (array('', '/') as $front) { - foreach ($valid_relative_urls as $url) { - $test_url = $front . $url; - $valid_url = valid_url($test_url); - $this->assertTrue($valid_url, t('@url is a valid url.', array('@url' => $test_url))); - } - } - } - - /** - * Test invalid relative urls. - */ - function testInvalidRelative() { - $invalid_relative_urls = array( - 'ex^mple', - 'example<>', - 'ex%ample', - ); - - foreach (array('', '/') as $front) { - foreach ($invalid_relative_urls as $url) { - $test_url = $front . $url; - $valid_url = valid_url($test_url); - $this->assertFALSE($valid_url, t('@url is NOT a valid url.', array('@url' => $test_url))); - } - } - } -} - -/** - * Tests for CRUD API functions. - */ -class DrupalDataApiTest extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Data API functions', - 'description' => 'Tests the performance of CRUD APIs.', - 'group' => 'System', - ); - } - - function setUp() { - parent::setUp('database_test'); - } - - /** - * Test the drupal_write_record() API function. - */ - function testDrupalWriteRecord() { - // Insert a record - no columns allow NULL values. - $person = new stdClass(); - $person->name = 'John'; - $person->unknown_column = 123; - $insert_result = drupal_write_record('test', $person); - $this->assertTrue($insert_result == SAVED_NEW, t('Correct value returned when a record is inserted with drupal_write_record() for a table with a single-field primary key.')); - $this->assertTrue(isset($person->id), t('Primary key is set on record created with drupal_write_record().')); - $this->assertIdentical($person->age, 0, t('Age field set to default value.')); - $this->assertIdentical($person->job, 'Undefined', t('Job field set to default value.')); - - // Verify that the record was inserted. - $result = db_query("SELECT * FROM {test} WHERE id = :id", array(':id' => $person->id))->fetchObject(); - $this->assertIdentical($result->name, 'John', t('Name field set.')); - $this->assertIdentical($result->age, '0', t('Age field set to default value.')); - $this->assertIdentical($result->job, 'Undefined', t('Job field set to default value.')); - $this->assertFalse(isset($result->unknown_column), t('Unknown column was ignored.')); - - // Update the newly created record. - $person->name = 'Peter'; - $person->age = 27; - $person->job = NULL; - $update_result = drupal_write_record('test', $person, array('id')); - $this->assertTrue($update_result == SAVED_UPDATED, t('Correct value returned when a record updated with drupal_write_record() for table with single-field primary key.')); - - // Verify that the record was updated. - $result = db_query("SELECT * FROM {test} WHERE id = :id", array(':id' => $person->id))->fetchObject(); - $this->assertIdentical($result->name, 'Peter', t('Name field set.')); - $this->assertIdentical($result->age, '27', t('Age field set.')); - $this->assertIdentical($result->job, '', t('Job field set and cast to string.')); - - // Try to insert NULL in columns that does not allow this. - $person = new stdClass(); - $person->name = 'Ringo'; - $person->age = NULL; - $person->job = NULL; - $insert_result = drupal_write_record('test', $person); - $this->assertTrue(isset($person->id), t('Primary key is set on record created with drupal_write_record().')); - $result = db_query("SELECT * FROM {test} WHERE id = :id", array(':id' => $person->id))->fetchObject(); - $this->assertIdentical($result->name, 'Ringo', t('Name field set.')); - $this->assertIdentical($result->age, '0', t('Age field set.')); - $this->assertIdentical($result->job, '', t('Job field set.')); - - // Insert a record - the "age" column allows NULL. - $person = new stdClass(); - $person->name = 'Paul'; - $person->age = NULL; - $insert_result = drupal_write_record('test_null', $person); - $this->assertTrue(isset($person->id), t('Primary key is set on record created with drupal_write_record().')); - $result = db_query("SELECT * FROM {test_null} WHERE id = :id", array(':id' => $person->id))->fetchObject(); - $this->assertIdentical($result->name, 'Paul', t('Name field set.')); - $this->assertIdentical($result->age, NULL, t('Age field set.')); - - // Insert a record - do not specify the value of a column that allows NULL. - $person = new stdClass(); - $person->name = 'Meredith'; - $insert_result = drupal_write_record('test_null', $person); - $this->assertTrue(isset($person->id), t('Primary key is set on record created with drupal_write_record().')); - $this->assertIdentical($person->age, 0, t('Age field set to default value.')); - $result = db_query("SELECT * FROM {test_null} WHERE id = :id", array(':id' => $person->id))->fetchObject(); - $this->assertIdentical($result->name, 'Meredith', t('Name field set.')); - $this->assertIdentical($result->age, '0', t('Age field set to default value.')); - - // Update the newly created record. - $person->name = 'Mary'; - $person->age = NULL; - $update_result = drupal_write_record('test_null', $person, array('id')); - $result = db_query("SELECT * FROM {test_null} WHERE id = :id", array(':id' => $person->id))->fetchObject(); - $this->assertIdentical($result->name, 'Mary', t('Name field set.')); - $this->assertIdentical($result->age, NULL, t('Age field set.')); - - // Insert a record - the "data" column should be serialized. - $person = new stdClass(); - $person->name = 'Dave'; - $update_result = drupal_write_record('test_serialized', $person); - $result = db_query("SELECT * FROM {test_serialized} WHERE id = :id", array(':id' => $person->id))->fetchObject(); - $this->assertIdentical($result->name, 'Dave', t('Name field set.')); - $this->assertIdentical($result->info, NULL, t('Info field set.')); - - $person->info = array(); - $update_result = drupal_write_record('test_serialized', $person, array('id')); - $result = db_query("SELECT * FROM {test_serialized} WHERE id = :id", array(':id' => $person->id))->fetchObject(); - $this->assertIdentical(unserialize($result->info), array(), t('Info field updated.')); - - // Update the serialized record. - $data = array('foo' => 'bar', 1 => 2, 'empty' => '', 'null' => NULL); - $person->info = $data; - $update_result = drupal_write_record('test_serialized', $person, array('id')); - $result = db_query("SELECT * FROM {test_serialized} WHERE id = :id", array(':id' => $person->id))->fetchObject(); - $this->assertIdentical(unserialize($result->info), $data, t('Info field updated.')); - - // Run an update query where no field values are changed. The database - // layer should return zero for number of affected rows, but - // db_write_record() should still return SAVED_UPDATED. - $update_result = drupal_write_record('test_null', $person, array('id')); - $this->assertTrue($update_result == SAVED_UPDATED, t('Correct value returned when a valid update is run without changing any values.')); - - // Insert an object record for a table with a multi-field primary key. - $node_access = new stdClass(); - $node_access->nid = mt_rand(); - $node_access->gid = mt_rand(); - $node_access->realm = $this->randomName(); - $insert_result = drupal_write_record('node_access', $node_access); - $this->assertTrue($insert_result == SAVED_NEW, t('Correct value returned when a record is inserted with drupal_write_record() for a table with a multi-field primary key.')); - - // Update the record. - $update_result = drupal_write_record('node_access', $node_access, array('nid', 'gid', 'realm')); - $this->assertTrue($update_result == SAVED_UPDATED, t('Correct value returned when a record is updated with drupal_write_record() for a table with a multi-field primary key.')); - } - -} - -/** - * Tests Simpletest error and exception collector. - */ -class DrupalErrorCollectionUnitTest extends DrupalWebTestCase { - - /** - * Errors triggered during the test. - * - * Errors are intercepted by the overriden implementation - * of DrupalWebTestCase::error below. - * - * @var Array - */ - protected $collectedErrors = array(); - - public static function getInfo() { - return array( - 'name' => 'SimpleTest error collector', - 'description' => 'Performs tests on the Simpletest error and exception collector.', - 'group' => 'SimpleTest', - ); - } - - function setUp() { - parent::setUp('system_test', 'error_test'); - } - - /** - * Test that simpletest collects errors from the tested site. - */ - function testErrorCollect() { - $this->collectedErrors = array(); - $this->drupalGet('error-test/generate-warnings-with-report'); - $this->assertEqual(count($this->collectedErrors), 3, t('Three errors were collected')); - - if (count($this->collectedErrors) == 3) { - $this->assertError($this->collectedErrors[0], 'Notice', 'error_test_generate_warnings()', 'error_test.module', 'Undefined variable: bananas'); - $this->assertError($this->collectedErrors[1], 'Warning', 'error_test_generate_warnings()', 'error_test.module', 'Division by zero'); - $this->assertError($this->collectedErrors[2], 'User warning', 'error_test_generate_warnings()', 'error_test.module', 'Drupal is awesome'); - } - else { - // Give back the errors to the log report. - foreach ($this->collectedErrors as $error) { - parent::error($error['message'], $error['group'], $error['caller']); - } - } - } - - /** - * Error handler that collects errors in an array. - * - * This test class is trying to verify that simpletest correctly sees errors - * and warnings. However, it can't generate errors and warnings that - * propagate up to the testing framework itself, or these tests would always - * fail. So, this special copy of error() doesn't propagate the errors up - * the class hierarchy. It just stuffs them into a protected collectedErrors - * array for various assertions to inspect. - */ - protected function error($message = '', $group = 'Other', array $caller = NULL) { - // Due to a WTF elsewhere, simpletest treats debug() and verbose() - // messages as if they were an 'error'. But, we don't want to collect - // those here. This function just wants to collect the real errors (PHP - // notices, PHP fatal errors, etc.), and let all the 'errors' from the - // 'User notice' group bubble up to the parent classes to be handled (and - // eventually displayed) as normal. - if ($group == 'User notice') { - parent::error($message, $group, $caller); - } - // Everything else should be collected but not propagated. - else { - $this->collectedErrors[] = array( - 'message' => $message, - 'group' => $group, - 'caller' => $caller - ); - } - } - - /** - * Assert that a collected error matches what we are expecting. - */ - function assertError($error, $group, $function, $file, $message = NULL) { - $this->assertEqual($error['group'], $group, t("Group was %group", array('%group' => $group))); - $this->assertEqual($error['caller']['function'], $function, t("Function was %function", array('%function' => $function))); - $this->assertEqual(basename($error['caller']['file']), $file, t("File was %file", array('%file' => $file))); - if (isset($message)) { - $this->assertEqual($error['message'], $message, t("Message was %message", array('%message' => $message))); - } - } -} - -/** - * Test the drupal_parse_info_file() API function. - */ -class ParseInfoFilesTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Parsing .info files', - 'description' => 'Tests parsing .info files.', - 'group' => 'System', - ); - } - - /** - * Parse an example .info file an verify the results. - */ - function testParseInfoFile() { - $info_values = drupal_parse_info_file(drupal_get_path('module', 'simpletest') . '/tests/common_test_info.txt'); - $this->assertEqual($info_values['simple_string'], 'A simple string', t('Simple string value was parsed correctly.'), t('System')); - $this->assertEqual($info_values['simple_constant'], WATCHDOG_INFO, t('Constant value was parsed correctly.'), t('System')); - $this->assertEqual($info_values['double_colon'], 'dummyClassName::', t('Value containing double-colon was parsed correctly.'), t('System')); - } -} - -/** - * Tests for the drupal_system_listing() function. - */ -class DrupalSystemListingTestCase extends DrupalWebTestCase { - /** - * Use the testing profile; this is needed for testDirectoryPrecedence(). - */ - protected $profile = 'testing'; - - public static function getInfo() { - return array( - 'name' => 'Drupal system listing', - 'description' => 'Tests the mechanism for scanning system directories in drupal_system_listing().', - 'group' => 'System', - ); - } - - /** - * Test that files in different directories take precedence as expected. - */ - function testDirectoryPrecedence() { - // Define the module files we will search for, and the directory precedence - // we expect. - $expected_directories = array( - // When the copy of the module in the profile directory is incompatible - // with Drupal core, the copy in the core modules directory takes - // precedence. - 'drupal_system_listing_incompatible_test' => array( - 'modules/simpletest/tests', - 'profiles/testing/modules', - ), - // When both copies of the module are compatible with Drupal core, the - // copy in the profile directory takes precedence. - 'drupal_system_listing_compatible_test' => array( - 'profiles/testing/modules', - 'modules/simpletest/tests', - ), - ); - - // This test relies on two versions of the same module existing in - // different places in the filesystem. Without that, the test has no - // meaning, so assert their presence first. - foreach ($expected_directories as $module => $directories) { - foreach ($directories as $directory) { - $filename = "$directory/$module/$module.module"; - $this->assertTrue(file_exists(DRUPAL_ROOT . '/' . $filename), t('@filename exists.', array('@filename' => $filename))); - } - } - - // Now scan the directories and check that the files take precedence as - // expected. - $files = drupal_system_listing('/\.module$/', 'modules', 'name', 1); - foreach ($expected_directories as $module => $directories) { - $expected_directory = array_shift($directories); - $expected_filename = "$expected_directory/$module/$module.module"; - $this->assertEqual($files[$module]->uri, $expected_filename, t('Module @module was found at @filename.', array('@module' => $module, '@filename' => $expected_filename))); - } - } -} - -/** - * Tests for the format_date() function. - */ -class FormatDateUnitTest extends DrupalWebTestCase { - - /** - * Arbitrary langcode for a custom language. - */ - const LANGCODE = 'xx'; - - public static function getInfo() { - return array( - 'name' => 'Format date', - 'description' => 'Test the format_date() function.', - 'group' => 'System', - ); - } - - function setUp() { - parent::setUp('locale'); - variable_set('configurable_timezones', 1); - variable_set('date_format_long', 'l, j. F Y - G:i'); - variable_set('date_format_medium', 'j. F Y - G:i'); - variable_set('date_format_short', 'Y M j - g:ia'); - variable_set('locale_custom_strings_' . self::LANGCODE, array( - '' => array('Sunday' => 'domingo'), - 'Long month name' => array('March' => 'marzo'), - )); - $this->refreshVariables(); - } - - /** - * Test admin-defined formats in format_date(). - */ - function testAdminDefinedFormatDate() { - // Create an admin user. - $this->admin_user = $this->drupalCreateUser(array('administer site configuration')); - $this->drupalLogin($this->admin_user); - - // Add new date format. - $admin_date_format = 'j M y'; - $edit = array('date_format' => $admin_date_format); - $this->drupalPost('admin/config/regional/date-time/formats/add', $edit, t('Add format')); - - // Add new date type. - $edit = array( - 'date_type' => 'Example Style', - 'machine_name' => 'example_style', - 'date_format' => $admin_date_format, - ); - $this->drupalPost('admin/config/regional/date-time/types/add', $edit, t('Add date type')); - - $timestamp = strtotime('2007-03-10T00:00:00+00:00'); - $this->assertIdentical(format_date($timestamp, 'example_style', '', 'America/Los_Angeles'), '9 Mar 07', t('Test format_date() using an admin-defined date type.')); - $this->assertIdentical(format_date($timestamp, 'undefined_style'), format_date($timestamp, 'medium'), t('Test format_date() defaulting to medium when $type not found.')); - } - - /** - * Tests for the format_date() function. - */ - function testFormatDate() { - global $user, $language; - - $timestamp = strtotime('2007-03-26T00:00:00+00:00'); - $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'America/Los_Angeles', 'en'), 'Sunday, 25-Mar-07 17:00:00 PDT', t('Test all parameters.')); - $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), 'domingo, 25-Mar-07 17:00:00 PDT', t('Test translated format.')); - $this->assertIdentical(format_date($timestamp, 'custom', '\\l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), 'l, 25-Mar-07 17:00:00 PDT', t('Test an escaped format string.')); - $this->assertIdentical(format_date($timestamp, 'custom', '\\\\l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), '\\domingo, 25-Mar-07 17:00:00 PDT', t('Test format containing backslash character.')); - $this->assertIdentical(format_date($timestamp, 'custom', '\\\\\\l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), '\\l, 25-Mar-07 17:00:00 PDT', t('Test format containing backslash followed by escaped format string.')); - $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'Europe/London', 'en'), 'Monday, 26-Mar-07 01:00:00 BST', t('Test a different time zone.')); - - // Create an admin user and add Spanish language. - $admin_user = $this->drupalCreateUser(array('administer languages')); - $this->drupalLogin($admin_user); - $edit = array( - 'predefined_langcode' => 'custom', - 'langcode' => self::LANGCODE, - 'name' => self::LANGCODE, - 'direction' => LANGUAGE_LTR, - ); - $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language')); - - // Set language prefix. - $edit = array('prefix[' . self::LANGCODE . ']' => self::LANGCODE); - $this->drupalPost('admin/config/regional/language/configure/url', $edit, t('Save configuration')); - - // Create a test user to carry out the tests. - $test_user = $this->drupalCreateUser(); - $this->drupalLogin($test_user); - $edit = array('language' => self::LANGCODE, 'mail' => $test_user->mail, 'timezone' => 'America/Los_Angeles'); - $this->drupalPost('user/' . $test_user->uid . '/edit', $edit, t('Save')); - - // Disable session saving as we are about to modify the global $user. - drupal_save_session(FALSE); - // Save the original user and language and then replace it with the test user and language. - $real_user = $user; - $user = user_load($test_user->uid, TRUE); - $real_language = $language->language; - $language->language = $user->language; - // Simulate a Drupal bootstrap with the logged-in user. - date_default_timezone_set(drupal_get_user_timezone()); - - $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'America/Los_Angeles', 'en'), 'Sunday, 25-Mar-07 17:00:00 PDT', t('Test a different language.')); - $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'Europe/London'), 'Monday, 26-Mar-07 01:00:00 BST', t('Test a different time zone.')); - $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T'), 'domingo, 25-Mar-07 17:00:00 PDT', t('Test custom date format.')); - $this->assertIdentical(format_date($timestamp, 'long'), 'domingo, 25. marzo 2007 - 17:00', t('Test long date format.')); - $this->assertIdentical(format_date($timestamp, 'medium'), '25. marzo 2007 - 17:00', t('Test medium date format.')); - $this->assertIdentical(format_date($timestamp, 'short'), '2007 Mar 25 - 5:00pm', t('Test short date format.')); - $this->assertIdentical(format_date($timestamp), '25. marzo 2007 - 17:00', t('Test default date format.')); - - // Restore the original user and language, and enable session saving. - $user = $real_user; - $language->language = $real_language; - // Restore default time zone. - date_default_timezone_set(drupal_get_user_timezone()); - drupal_save_session(TRUE); - } -} - -/** - * Tests for the format_date() function. - */ -class DrupalAttributesUnitTest extends DrupalUnitTestCase { - public static function getInfo() { - return array( - 'name' => 'HTML Attributes', - 'description' => 'Perform unit tests on the drupal_attributes() function.', - 'group' => 'System', - ); - } - - /** - * Tests that drupal_html_class() cleans the class name properly. - */ - function testDrupalAttributes() { - // Verify that special characters are HTML encoded. - $this->assertIdentical(drupal_attributes(array('title' => '&"\'<>')), ' title="&"'<>"', t('HTML encode attribute values.')); - - // Verify multi-value attributes are concatenated with spaces. - $attributes = array('class' => array('first', 'last')); - $this->assertIdentical(drupal_attributes(array('class' => array('first', 'last'))), ' class="first last"', t('Concatenate multi-value attributes.')); - - // Verify empty attribute values are rendered. - $this->assertIdentical(drupal_attributes(array('alt' => '')), ' alt=""', t('Empty attribute value #1.')); - $this->assertIdentical(drupal_attributes(array('alt' => NULL)), ' alt=""', t('Empty attribute value #2.')); - - // Verify multiple attributes are rendered. - $attributes = array( - 'id' => 'id-test', - 'class' => array('first', 'last'), - 'alt' => 'Alternate', - ); - $this->assertIdentical(drupal_attributes($attributes), ' id="id-test" class="first last" alt="Alternate"', t('Multiple attributes.')); - - // Verify empty attributes array is rendered. - $this->assertIdentical(drupal_attributes(array()), '', t('Empty attributes array.')); - } -} - -/** - * Tests converting PHP variables to JSON strings and back. - */ -class DrupalJSONTest extends DrupalUnitTestCase { - public static function getInfo() { - return array( - 'name' => 'JSON', - 'description' => 'Perform unit tests on the drupal_json_encode() and drupal_json_decode() functions.', - 'group' => 'System', - ); - } - - /** - * Tests converting PHP variables to JSON strings and back. - */ - function testJSON() { - // Setup a string with the full ASCII table. - // @todo: Add tests for non-ASCII characters and Unicode. - $str = ''; - for ($i=0; $i < 128; $i++) { - $str .= chr($i); - } - // Characters that must be escaped. - $html_unsafe = array('<', '>', '&'); - $html_unsafe_escaped = array('\u003c', '\u003e', '\u0026'); - - // Verify there aren't character encoding problems with the source string. - $this->assertIdentical(strlen($str), 128, t('A string with the full ASCII table has the correct length.')); - foreach ($html_unsafe as $char) { - $this->assertTrue(strpos($str, $char) > 0, t('A string with the full ASCII table includes @s.', array('@s' => $char))); - } - - // Verify that JSON encoding produces a string with all of the characters. - $json = drupal_json_encode($str); - $this->assertTrue(strlen($json) > strlen($str), t('A JSON encoded string is larger than the source string.')); - - // Verify that encoding/decoding is reversible. - $json_decoded = drupal_json_decode($json); - $this->assertIdentical($str, $json_decoded, t('Encoding a string to JSON and decoding back results in the original string.')); - - // Verify reversibility for structured data. Also verify that necessary - // characters are escaped. - $source = array(TRUE, FALSE, 0, 1, '0', '1', $str, array('key1' => $str, 'key2' => array('nested' => TRUE))); - $json = drupal_json_encode($source); - foreach ($html_unsafe as $char) { - $this->assertTrue(strpos($json, $char) === FALSE, t('A JSON encoded string does not contain @s.', array('@s' => $char))); - } - // Verify that JSON encoding escapes the HTML unsafe characters - foreach ($html_unsafe_escaped as $char) { - $this->assertTrue(strpos($json, $char) > 0, t('A JSON encoded string contains @s.', array('@s' => $char))); - } - $json_decoded = drupal_json_decode($json); - $this->assertNotIdentical($source, $json, t('An array encoded in JSON is not identical to the source.')); - $this->assertIdentical($source, $json_decoded, t('Encoding structured data to JSON and decoding back results in the original data.')); - } -} - -/** - * Basic tests for drupal_add_feed(). - */ -class DrupalAddFeedTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'drupal_add_feed() tests', - 'description' => 'Make sure that drupal_add_feed() works correctly with various constructs.', - 'group' => 'System', - ); - } - - /** - * Test drupal_add_feed() with paths, URLs, and titles. - */ - function testBasicFeedAddNoTitle() { - $path = $this->randomName(12); - $external_url = 'http://' . $this->randomName(12) . '/' . $this->randomName(12); - $fully_qualified_local_url = url($this->randomName(12), array('absolute' => TRUE)); - - $path_for_title = $this->randomName(12); - $external_for_title = 'http://' . $this->randomName(12) . '/' . $this->randomName(12); - $fully_qualified_for_title = url($this->randomName(12), array('absolute' => TRUE)); - - // Possible permutations of drupal_add_feed() to test. - // - 'input_url': the path passed to drupal_add_feed(), - // - 'output_url': the expected URL to be found in the header. - // - 'title' == the title of the feed as passed into drupal_add_feed(). - $urls = array( - 'path without title' => array( - 'input_url' => $path, - 'output_url' => url($path, array('absolute' => TRUE)), - 'title' => '', - ), - 'external url without title' => array( - 'input_url' => $external_url, - 'output_url' => $external_url, - 'title' => '', - ), - 'local url without title' => array( - 'input_url' => $fully_qualified_local_url, - 'output_url' => $fully_qualified_local_url, - 'title' => '', - ), - 'path with title' => array( - 'input_url' => $path_for_title, - 'output_url' => url($path_for_title, array('absolute' => TRUE)), - 'title' => $this->randomName(12), - ), - 'external url with title' => array( - 'input_url' => $external_for_title, - 'output_url' => $external_for_title, - 'title' => $this->randomName(12), - ), - 'local url with title' => array( - 'input_url' => $fully_qualified_for_title, - 'output_url' => $fully_qualified_for_title, - 'title' => $this->randomName(12), - ), - ); - - foreach ($urls as $description => $feed_info) { - drupal_add_feed($feed_info['input_url'], $feed_info['title']); - } - - $this->drupalSetContent(drupal_get_html_head()); - foreach ($urls as $description => $feed_info) { - $this->assertPattern($this->urlToRSSLinkPattern($feed_info['output_url'], $feed_info['title']), t('Found correct feed header for %description', array('%description' => $description))); - } - } - - /** - * Create a pattern representing the RSS feed in the page. - */ - function urlToRSSLinkPattern($url, $title = '') { - // Escape any regular expression characters in the url ('?' is the worst). - $url = preg_replace('/([+?.*])/', '[$0]', $url); - $generated_pattern = '%<link +rel="alternate" +type="application/rss.xml" +title="' . $title . '" +href="' . $url . '" */>%'; - return $generated_pattern; - } -} diff --git a/modules/simpletest/tests/common_test.css b/modules/simpletest/tests/common_test.css deleted file mode 100644 index b86ceadb7b9..00000000000 --- a/modules/simpletest/tests/common_test.css +++ /dev/null @@ -1,2 +0,0 @@ - -/* This file is for testing CSS file inclusion, no contents are necessary. */ diff --git a/modules/simpletest/tests/common_test.info b/modules/simpletest/tests/common_test.info deleted file mode 100644 index 9e6d24f9a24..00000000000 --- a/modules/simpletest/tests/common_test.info +++ /dev/null @@ -1,8 +0,0 @@ -name = "Common Test" -description = "Support module for Common tests." -package = Testing -version = VERSION -core = 8.x -stylesheets[all][] = common_test.css -stylesheets[print][] = common_test.print.css -hidden = TRUE diff --git a/modules/simpletest/tests/common_test.module b/modules/simpletest/tests/common_test.module deleted file mode 100644 index a3bb9b0fade..00000000000 --- a/modules/simpletest/tests/common_test.module +++ /dev/null @@ -1,262 +0,0 @@ -<?php - -/** - * @file - * Helper module for the Common tests. - */ - -/** - * Implements hook_menu(). - */ -function common_test_menu() { - $items['common-test/drupal_goto'] = array( - 'title' => 'Drupal Goto', - 'page callback' => 'common_test_drupal_goto_land', - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - $items['common-test/drupal_goto/fail'] = array( - 'title' => 'Drupal Goto', - 'page callback' => 'common_test_drupal_goto_land_fail', - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - $items['common-test/drupal_goto/redirect'] = array( - 'title' => 'Drupal Goto', - 'page callback' => 'common_test_drupal_goto_redirect', - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - $items['common-test/drupal_goto/redirect_advanced'] = array( - 'title' => 'Drupal Goto', - 'page callback' => 'common_test_drupal_goto_redirect_advanced', - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - $items['common-test/drupal_goto/redirect_fail'] = array( - 'title' => 'Drupal Goto Failure', - 'page callback' => 'drupal_goto', - 'page arguments' => array('common-test/drupal_goto/fail'), - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - $items['common-test/destination'] = array( - 'title' => 'Drupal Get Destination', - 'page callback' => 'common_test_destination', - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - $items['common-test/query-string'] = array( - 'title' => 'Test querystring', - 'page callback' => 'common_test_js_and_css_querystring', - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - $items['common-test/drupal-render-invalid-keys'] = array( - 'title' => 'Drupal Render', - 'page callback' => 'common_test_drupal_render_invalid_keys', - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - return $items; -} - -/** - * Redirect using drupal_goto(). - */ -function common_test_drupal_goto_redirect() { - drupal_goto('common-test/drupal_goto'); -} - -/** - * Redirect using drupal_goto(). - */ -function common_test_drupal_goto_redirect_advanced() { - drupal_goto('common-test/drupal_goto', array('query' => array('foo' => '123')), 301); -} - -/** - * Landing page for drupal_goto(). - */ -function common_test_drupal_goto_land() { - print "drupal_goto"; -} - -/** - * Fail landing page for drupal_goto(). - */ -function common_test_drupal_goto_land_fail() { - print "drupal_goto_fail"; -} - -/** - * Implements hook_drupal_goto_alter(). - */ -function common_test_drupal_goto_alter(&$path, &$options, &$http_response_code) { - if ($path == 'common-test/drupal_goto/fail') { - $path = 'common-test/drupal_goto/redirect'; - } -} - -/** - * Print destination query parameter. - */ -function common_test_destination() { - $destination = drupal_get_destination(); - print "The destination: " . check_plain($destination['destination']); -} - -/** - * Render an element with an invalid render array key. - */ -function common_test_drupal_render_invalid_keys() { - define('SIMPLETEST_COLLECT_ERRORS', FALSE); - - // Keys that begin with # may contain a value of any type, otherwise they must - // contain arrays. - $key = 'child'; - $value = 'This should be an array.'; - $element = array( - $key => $value, - ); - return drupal_render($element); -} - -/** - * Implements hook_TYPE_alter(). - */ -function common_test_drupal_alter_alter(&$data, &$arg2 = NULL, &$arg3 = NULL) { - // Alter first argument. - if (is_array($data)) { - $data['foo'] = 'Drupal'; - } - elseif (is_object($data)) { - $data->foo = 'Drupal'; - } - // Alter second argument, if present. - if (isset($arg2)) { - if (is_array($arg2)) { - $arg2['foo'] = 'Drupal'; - } - elseif (is_object($arg2)) { - $arg2->foo = 'Drupal'; - } - } - // Try to alter third argument, if present. - if (isset($arg3)) { - if (is_array($arg3)) { - $arg3['foo'] = 'Drupal'; - } - elseif (is_object($arg3)) { - $arg3->foo = 'Drupal'; - } - } -} - -/** - * Implements hook_TYPE_alter() on behalf of Bartik theme. - * - * Same as common_test_drupal_alter_alter(), but here, we verify that themes - * can also alter and come last. - */ -function bartik_drupal_alter_alter(&$data, &$arg2 = NULL, &$arg3 = NULL) { - // Alter first argument. - if (is_array($data)) { - $data['foo'] .= ' theme'; - } - elseif (is_object($data)) { - $data->foo .= ' theme'; - } - // Alter second argument, if present. - if (isset($arg2)) { - if (is_array($arg2)) { - $arg2['foo'] .= ' theme'; - } - elseif (is_object($arg2)) { - $arg2->foo .= ' theme'; - } - } - // Try to alter third argument, if present. - if (isset($arg3)) { - if (is_array($arg3)) { - $arg3['foo'] .= ' theme'; - } - elseif (is_object($arg3)) { - $arg3->foo .= ' theme'; - } - } -} - -/** - * Implements hook_theme(). - */ -function common_test_theme() { - return array( - 'common_test_foo' => array( - 'variables' => array('foo' => 'foo', 'bar' => 'bar'), - ), - ); -} - -/** - * Theme function for testing drupal_render() theming. - */ -function theme_common_test_foo($variables) { - return $variables['foo'] . $variables['bar']; -} - -/** - * Implements hook_library_info_alter(). - */ -function common_test_library_info_alter(&$libraries, $module) { - if ($module == 'system' && isset($libraries['farbtastic'])) { - // Change the title of Farbtastic to "Farbtastic: Altered Library". - $libraries['farbtastic']['title'] = 'Farbtastic: Altered Library'; - // Make Farbtastic depend on jQuery Form to test library dependencies. - $libraries['farbtastic']['dependencies'][] = array('system', 'form'); - } -} - -/** - * Implements hook_library_info(). - * - * Adds Farbtastic in a different version. - */ -function common_test_library_info() { - $libraries['farbtastic'] = array( - 'title' => 'Custom Farbtastic Library', - 'website' => 'http://code.google.com/p/farbtastic/', - 'version' => '5.3', - 'js' => array( - 'misc/farbtastic/farbtastic.js' => array(), - ), - 'css' => array( - 'misc/farbtastic/farbtastic.css' => array(), - ), - ); - return $libraries; -} - -/** - * Adds a JavaScript file and a CSS file with a query string appended. - */ -function common_test_js_and_css_querystring() { - drupal_add_js(drupal_get_path('module', 'node') . '/node.js'); - drupal_add_css(drupal_get_path('module', 'node') . '/node.css'); - // A relative URI may have a query string. - drupal_add_css('/' . drupal_get_path('module', 'node') . '/node-fake.css?arg1=value1&arg2=value2'); - return ''; -} - -/** - * Implements hook_cron(). - * - * System module should handle if a module does not catch an exception and keep - * cron going. - * - * @see common_test_cron_helper() - * - */ -function common_test_cron() { - throw new Exception(t('Uncaught exception')); -} diff --git a/modules/simpletest/tests/common_test.print.css b/modules/simpletest/tests/common_test.print.css deleted file mode 100644 index b86ceadb7b9..00000000000 --- a/modules/simpletest/tests/common_test.print.css +++ /dev/null @@ -1,2 +0,0 @@ - -/* This file is for testing CSS file inclusion, no contents are necessary. */ diff --git a/modules/simpletest/tests/common_test_cron_helper.info b/modules/simpletest/tests/common_test_cron_helper.info deleted file mode 100644 index ce1a6326fb3..00000000000 --- a/modules/simpletest/tests/common_test_cron_helper.info +++ /dev/null @@ -1,6 +0,0 @@ -name = "Common Test Cron Helper" -description = "Helper module for CronRunTestCase::testCronExceptions()." -package = Testing -version = VERSION -core = 7.x -hidden = TRUE diff --git a/modules/simpletest/tests/common_test_cron_helper.module b/modules/simpletest/tests/common_test_cron_helper.module deleted file mode 100644 index 94a2b2c4382..00000000000 --- a/modules/simpletest/tests/common_test_cron_helper.module +++ /dev/null @@ -1,17 +0,0 @@ -<?php -/** - * @file - * Helper module for the testCronExceptions in addition to common_test module. - */ - -/** - * Implements hook_cron(). - * - * common_test_cron() throws an exception, but the execution should reach this - * function as well. - * - * @see common_test_cron() - */ -function common_test_cron_helper_cron() { - variable_set('common_test_cron', 'success'); -} diff --git a/modules/simpletest/tests/common_test_info.txt b/modules/simpletest/tests/common_test_info.txt deleted file mode 100644 index ae217b91703..00000000000 --- a/modules/simpletest/tests/common_test_info.txt +++ /dev/null @@ -1,9 +0,0 @@ -; Test parsing with a simple string. -simple_string = A simple string - -; Test that constants can be used as values. -simple_constant = WATCHDOG_INFO - -; After parsing the .info file, 'double_colon' should hold the literal value. -; Parsing should not throw a fatal error or try to access a class constant. -double_colon = dummyClassName:: diff --git a/modules/simpletest/tests/database_test.info b/modules/simpletest/tests/database_test.info deleted file mode 100644 index 7f386617833..00000000000 --- a/modules/simpletest/tests/database_test.info +++ /dev/null @@ -1,6 +0,0 @@ -name = "Database Test" -description = "Support module for Database layer tests." -core = 8.x -package = Testing -version = VERSION -hidden = TRUE diff --git a/modules/simpletest/tests/database_test.install b/modules/simpletest/tests/database_test.install deleted file mode 100644 index 4dce2b19af8..00000000000 --- a/modules/simpletest/tests/database_test.install +++ /dev/null @@ -1,217 +0,0 @@ -<?php - -/** - * @file - * Install, update and uninstall functions for the database_test module. - */ - -/** - * Implements hook_schema(). - * - * The database tests use the database API which depends on schema - * information for certain operations on certain databases. - * Therefore, the schema must actually be declared in a normal module - * like any other, not directly in the test file. - */ -function database_test_schema() { - $schema['test'] = array( - 'description' => 'Basic test table for the database unit tests.', - 'fields' => array( - 'id' => array( - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'name' => array( - 'description' => "A person's name", - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'age' => array( - 'description' => "The person's age", - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'job' => array( - 'description' => "The person's job", - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => 'Undefined', - ), - ), - 'primary key' => array('id'), - 'unique keys' => array( - 'name' => array('name') - ), - 'indexes' => array( - 'ages' => array('age'), - ), - ); - - // This is an alternate version of the same table that is structured the same - // but has a non-serial Primary Key. - $schema['test_people'] = array( - 'description' => 'A duplicate version of the test table, used for additional tests.', - 'fields' => array( - 'name' => array( - 'description' => "A person's name", - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'age' => array( - 'description' => "The person's age", - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'job' => array( - 'description' => "The person's job", - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - ), - 'primary key' => array('job'), - 'indexes' => array( - 'ages' => array('age'), - ), - ); - - $schema['test_one_blob'] = array( - 'description' => 'A simple table including a BLOB field for testing BLOB behavior.', - 'fields' => array( - 'id' => array( - 'description' => 'Simple unique ID.', - 'type' => 'serial', - 'not null' => TRUE, - ), - 'blob1' => array( - 'description' => 'A BLOB field.', - 'type' => 'blob', - ), - ), - 'primary key' => array('id'), - ); - - $schema['test_two_blobs'] = array( - 'description' => 'A simple test table with two BLOB fields.', - 'fields' => array( - 'id' => array( - 'description' => 'Simple unique ID.', - 'type' => 'serial', - 'not null' => TRUE, - ), - 'blob1' => array( - 'description' => 'A dummy BLOB field.', - 'type' => 'blob', - ), - 'blob2' => array( - 'description' => 'A second BLOB field.', - 'type' => 'blob' - ), - ), - 'primary key' => array('id'), - ); - - $schema['test_task'] = array( - 'description' => 'A task list for people in the test table.', - 'fields' => array( - 'tid' => array( - 'description' => 'Task ID, primary key.', - 'type' => 'serial', - 'not null' => TRUE, - ), - 'pid' => array( - 'description' => 'The {test_people}.pid, foreign key for the test table.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'task' => array( - 'description' => 'The task to be completed.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'priority' => array( - 'description' => 'The priority of the task.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - ), - 'primary key' => array('tid'), - ); - - $schema['test_null'] = array( - 'description' => 'Basic test table for NULL value handling.', - 'fields' => array( - 'id' => array( - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'name' => array( - 'description' => "A person's name.", - 'type' => 'varchar', - 'length' => 255, - 'not null' => FALSE, - 'default' => '', - ), - 'age' => array( - 'description' => "The person's age.", - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => FALSE, - 'default' => 0), - ), - 'primary key' => array('id'), - 'unique keys' => array( - 'name' => array('name') - ), - 'indexes' => array( - 'ages' => array('age'), - ), - ); - - $schema['test_serialized'] = array( - 'description' => 'Basic test table for NULL value handling.', - 'fields' => array( - 'id' => array( - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'name' => array( - 'description' => "A person's name.", - 'type' => 'varchar', - 'length' => 255, - 'not null' => FALSE, - 'default' => '', - ), - 'info' => array( - 'description' => "The person's data in serialized form.", - 'type' => 'blob', - 'serialize' => TRUE, - ), - ), - 'primary key' => array('id'), - 'unique keys' => array( - 'name' => array('name') - ), - ); - - return $schema; -} diff --git a/modules/simpletest/tests/database_test.module b/modules/simpletest/tests/database_test.module deleted file mode 100644 index 6fac31919c7..00000000000 --- a/modules/simpletest/tests/database_test.module +++ /dev/null @@ -1,241 +0,0 @@ -<?php - -/** - * Implements hook_query_alter(). - */ -function database_test_query_alter(QueryAlterableInterface $query) { - - if ($query->hasTag('database_test_alter_add_range')) { - $query->range(0, 2); - } - - if ($query->hasTag('database_test_alter_add_join')) { - $people_alias = $query->join('test', 'people', "test_task.pid = %alias.id"); - $name_field = $query->addField($people_alias, 'name', 'name'); - $query->condition($people_alias . '.id', 2); - } - - if ($query->hasTag('database_test_alter_change_conditional')) { - $conditions =& $query->conditions(); - $conditions[0]['value'] = 2; - } - - if ($query->hasTag('database_test_alter_change_fields')) { - $fields =& $query->getFields(); - unset($fields['age']); - } - - if ($query->hasTag('database_test_alter_change_expressions')) { - $expressions =& $query->getExpressions(); - $expressions['double_age']['expression'] = 'age*3'; - } -} - - -/** - * Implements hook_query_TAG_alter(). - * - * Called by DatabaseTestCase::testAlterRemoveRange. - */ -function database_test_query_database_test_alter_remove_range_alter(QueryAlterableInterface $query) { - $query->range(); -} - -/** - * Implements hook_menu(). - */ -function database_test_menu() { - $items['database_test/db_query_temporary'] = array( - 'access callback' => TRUE, - 'page callback' => 'database_test_db_query_temporary', - ); - $items['database_test/pager_query_even'] = array( - 'access callback' => TRUE, - 'page callback' => 'database_test_even_pager_query', - ); - $items['database_test/pager_query_odd'] = array( - 'access callback' => TRUE, - 'page callback' => 'database_test_odd_pager_query', - ); - $items['database_test/tablesort'] = array( - 'access callback' => TRUE, - 'page callback' => 'database_test_tablesort', - ); - $items['database_test/tablesort_first'] = array( - 'access callback' => TRUE, - 'page callback' => 'database_test_tablesort_first', - ); - $items['database_test/tablesort_default_sort'] = array( - 'access callback' => TRUE, - 'page callback' => 'drupal_get_form', - 'page arguments' => array('database_test_theme_tablesort'), - ); - return $items; -} - -/** - * Run a db_query_temporary and output the table name and its number of rows. - * - * We need to test that the table created is temporary, so we run it here, in a - * separate menu callback request; After this request is done, the temporary - * table should automatically dropped. - */ -function database_test_db_query_temporary() { - $table_name = db_query_temporary('SELECT status FROM {system}', array()); - drupal_json_output(array( - 'table_name' => $table_name, - 'row_count' => db_select($table_name)->countQuery()->execute()->fetchField(), - )); - exit; -} - -/** - * Run a pager query and return the results. - * - * This function does care about the page GET parameter, as set by the - * simpletest HTTP call. - */ -function database_test_even_pager_query($limit) { - - $query = db_select('test', 't'); - $query - ->fields('t', array('name')) - ->orderBy('age'); - - // This should result in 2 pages of results. - $query = $query->extend('PagerDefault')->limit($limit); - - $names = $query->execute()->fetchCol(); - - drupal_json_output(array( - 'names' => $names, - )); - exit; -} - -/** - * Run a pager query and return the results. - * - * This function does care about the page GET parameter, as set by the - * simpletest HTTP call. - */ -function database_test_odd_pager_query($limit) { - - $query = db_select('test_task', 't'); - $query - ->fields('t', array('task')) - ->orderBy('pid'); - - // This should result in 4 pages of results. - $query = $query->extend('PagerDefault')->limit($limit); - - $names = $query->execute()->fetchCol(); - - drupal_json_output(array( - 'names' => $names, - )); - exit; -} - -/** - * Run a tablesort query and return the results. - * - * This function does care about the page GET parameter, as set by the - * simpletest HTTP call. - */ -function database_test_tablesort() { - $header = array( - 'tid' => array('data' => t('Task ID'), 'field' => 'tid', 'sort' => 'desc'), - 'pid' => array('data' => t('Person ID'), 'field' => 'pid'), - 'task' => array('data' => t('Task'), 'field' => 'task'), - 'priority' => array('data' => t('Priority'), 'field' => 'priority', ), - ); - - $query = db_select('test_task', 't'); - $query - ->fields('t', array('tid', 'pid', 'task', 'priority')); - - $query = $query->extend('TableSort')->orderByHeader($header); - - // We need all the results at once to check the sort. - $tasks = $query->execute()->fetchAll(); - - drupal_json_output(array( - 'tasks' => $tasks, - )); - exit; -} - -/** - * Run a tablesort query with a second order_by after and return the results. - * - * This function does care about the page GET parameter, as set by the - * simpletest HTTP call. - */ -function database_test_tablesort_first() { - $header = array( - 'tid' => array('data' => t('Task ID'), 'field' => 'tid', 'sort' => 'desc'), - 'pid' => array('data' => t('Person ID'), 'field' => 'pid'), - 'task' => array('data' => t('Task'), 'field' => 'task'), - 'priority' => array('data' => t('Priority'), 'field' => 'priority', ), - ); - - $query = db_select('test_task', 't'); - $query - ->fields('t', array('tid', 'pid', 'task', 'priority')); - - $query = $query->extend('TableSort')->orderByHeader($header)->orderBy('priority'); - - // We need all the results at once to check the sort. - $tasks = $query->execute()->fetchAll(); - - drupal_json_output(array( - 'tasks' => $tasks, - )); - exit; -} - -/** - * Output a form without setting a header sort. - */ -function database_test_theme_tablesort($form, &$form_state) { - $header = array( - 'username' => array('data' => t('Username'), 'field' => 'u.name'), - 'status' => array('data' => t('Status'), 'field' => 'u.status'), - ); - - $query = db_select('users', 'u'); - $query->condition('u.uid', 0, '<>'); - user_build_filter_query($query); - - $count_query = clone $query; - $count_query->addExpression('COUNT(u.uid)'); - - $query = $query->extend('PagerDefault')->extend('TableSort'); - $query - ->fields('u', array('uid', 'name', 'status', 'created', 'access')) - ->limit(50) - ->orderByHeader($header) - ->setCountQuery($count_query); - $result = $query->execute(); - - $options = array(); - - $status = array(t('blocked'), t('active')); - $accounts = array(); - foreach ($result as $account) { - $options[$account->uid] = array( - 'username' => check_plain($account->name), - 'status' => $status[$account->status], - ); - } - - $form['accounts'] = array( - '#type' => 'tableselect', - '#header' => $header, - '#options' => $options, - '#empty' => t('No people available.'), - ); - - return $form; -} diff --git a/modules/simpletest/tests/database_test.test b/modules/simpletest/tests/database_test.test deleted file mode 100644 index 87d386aa7e5..00000000000 --- a/modules/simpletest/tests/database_test.test +++ /dev/null @@ -1,3691 +0,0 @@ -<?php - -/** - * Dummy class for fetching into a class. - * - * PDO supports using a new instance of an arbitrary class for records - * rather than just a stdClass or array. This class is for testing that - * functionality. (See testQueryFetchClass() below) - */ -class FakeRecord { } - -/** - * Base test class for databases. - * - * Because all database tests share the same test data, we can centralize that - * here. - */ -class DatabaseTestCase extends DrupalWebTestCase { - protected $profile = 'testing'; - - function setUp() { - parent::setUp('database_test'); - - $schema['test'] = drupal_get_schema('test'); - $schema['test_people'] = drupal_get_schema('test_people'); - $schema['test_one_blob'] = drupal_get_schema('test_one_blob'); - $schema['test_two_blobs'] = drupal_get_schema('test_two_blobs'); - $schema['test_task'] = drupal_get_schema('test_task'); - - $this->installTables($schema); - - $this->addSampleData(); - } - - /** - * Set up several tables needed by a certain test. - * - * @param $schema - * An array of table definitions to install. - */ - function installTables($schema) { - // This ends up being a test for table drop and create, too, which is nice. - foreach ($schema as $name => $data) { - if (db_table_exists($name)) { - db_drop_table($name); - } - db_create_table($name, $data); - } - - foreach ($schema as $name => $data) { - $this->assertTrue(db_table_exists($name), t('Table @name created successfully.', array('@name' => $name))); - } - } - - /** - * Set up tables for NULL handling. - */ - function ensureSampleDataNull() { - $schema['test_null'] = drupal_get_schema('test_null'); - $this->installTables($schema); - - db_insert('test_null') - ->fields(array('name', 'age')) - ->values(array( - 'name' => 'Kermit', - 'age' => 25, - )) - ->values(array( - 'name' => 'Fozzie', - 'age' => NULL, - )) - ->values(array( - 'name' => 'Gonzo', - 'age' => 27, - )) - ->execute(); - } - - /** - * Setup our sample data. - * - * These are added using db_query(), since we're not trying to test the - * INSERT operations here, just populate. - */ - function addSampleData() { - // We need the IDs, so we can't use a multi-insert here. - $john = db_insert('test') - ->fields(array( - 'name' => 'John', - 'age' => 25, - 'job' => 'Singer', - )) - ->execute(); - - $george = db_insert('test') - ->fields(array( - 'name' => 'George', - 'age' => 27, - 'job' => 'Singer', - )) - ->execute(); - - $ringo = db_insert('test') - ->fields(array( - 'name' => 'Ringo', - 'age' => 28, - 'job' => 'Drummer', - )) - ->execute(); - - $paul = db_insert('test') - ->fields(array( - 'name' => 'Paul', - 'age' => 26, - 'job' => 'Songwriter', - )) - ->execute(); - - db_insert('test_people') - ->fields(array( - 'name' => 'Meredith', - 'age' => 30, - 'job' => 'Speaker', - )) - ->execute(); - - db_insert('test_task') - ->fields(array('pid', 'task', 'priority')) - ->values(array( - 'pid' => $john, - 'task' => 'eat', - 'priority' => 3, - )) - ->values(array( - 'pid' => $john, - 'task' => 'sleep', - 'priority' => 4, - )) - ->values(array( - 'pid' => $john, - 'task' => 'code', - 'priority' => 1, - )) - ->values(array( - 'pid' => $george, - 'task' => 'sing', - 'priority' => 2, - )) - ->values(array( - 'pid' => $george, - 'task' => 'sleep', - 'priority' => 2, - )) - ->values(array( - 'pid' => $paul, - 'task' => 'found new band', - 'priority' => 1, - )) - ->values(array( - 'pid' => $paul, - 'task' => 'perform at superbowl', - 'priority' => 3, - )) - ->execute(); - } -} - -/** - * Test connection management. - */ -class DatabaseConnectionTestCase extends DatabaseTestCase { - - public static function getInfo() { - return array( - 'name' => 'Connection tests', - 'description' => 'Tests of the core database system.', - 'group' => 'Database', - ); - } - - /** - * Test that connections return appropriate connection objects. - */ - function testConnectionRouting() { - // Clone the master credentials to a slave connection. - // Note this will result in two independent connection objects that happen - // to point to the same place. - $connection_info = Database::getConnectionInfo('default'); - Database::addConnectionInfo('default', 'slave', $connection_info['default']); - - $db1 = Database::getConnection('default', 'default'); - $db2 = Database::getConnection('slave', 'default'); - - $this->assertNotNull($db1, t('default connection is a real connection object.')); - $this->assertNotNull($db2, t('slave connection is a real connection object.')); - $this->assertNotIdentical($db1, $db2, t('Each target refers to a different connection.')); - - // Try to open those targets another time, that should return the same objects. - $db1b = Database::getConnection('default', 'default'); - $db2b = Database::getConnection('slave', 'default'); - $this->assertIdentical($db1, $db1b, t('A second call to getConnection() returns the same object.')); - $this->assertIdentical($db2, $db2b, t('A second call to getConnection() returns the same object.')); - - // Try to open an unknown target. - $unknown_target = $this->randomName(); - $db3 = Database::getConnection($unknown_target, 'default'); - $this->assertNotNull($db3, t('Opening an unknown target returns a real connection object.')); - $this->assertIdentical($db1, $db3, t('An unknown target opens the default connection.')); - - // Try to open that unknown target another time, that should return the same object. - $db3b = Database::getConnection($unknown_target, 'default'); - $this->assertIdentical($db3, $db3b, t('A second call to getConnection() returns the same object.')); - } - - /** - * Test that connections return appropriate connection objects. - */ - function testConnectionRoutingOverride() { - // Clone the master credentials to a slave connection. - // Note this will result in two independent connection objects that happen - // to point to the same place. - $connection_info = Database::getConnectionInfo('default'); - Database::addConnectionInfo('default', 'slave', $connection_info['default']); - - Database::ignoreTarget('default', 'slave'); - - $db1 = Database::getConnection('default', 'default'); - $db2 = Database::getConnection('slave', 'default'); - - $this->assertIdentical($db1, $db2, t('Both targets refer to the same connection.')); - } - - /** - * Tests the closing of a database connection. - */ - function testConnectionClosing() { - // Open the default target so we have an object to compare. - $db1 = Database::getConnection('default', 'default'); - - // Try to close the the default connection, then open a new one. - Database::closeConnection('default', 'default'); - $db2 = Database::getConnection('default', 'default'); - - // Opening a connection after closing it should yield an object different than the original. - $this->assertNotIdentical($db1, $db2, t('Opening the default connection after it is closed returns a new object.')); - } - - /** - * Tests the connection options of the active database. - */ - function testConnectionOptions() { - $connection_info = Database::getConnectionInfo('default'); - - // Be sure we're connected to the default database. - $db = Database::getConnection('default', 'default'); - $connectionOptions = $db->getConnectionOptions(); - - // In the MySQL driver, the port can be different, so check individual - // options. - $this->assertEqual($connection_info['default']['driver'], $connectionOptions['driver'], t('The default connection info driver matches the current connection options driver.')); - $this->assertEqual($connection_info['default']['database'], $connectionOptions['database'], t('The default connection info database matches the current connection options database.')); - - // Set up identical slave and confirm connection options are identical. - Database::addConnectionInfo('default', 'slave', $connection_info['default']); - $db2 = Database::getConnection('slave', 'default'); - $connectionOptions2 = $db2->getConnectionOptions(); - - // Get a fresh copy of the default connection options. - $connectionOptions = $db->getConnectionOptions(); - $this->assertIdentical($connectionOptions, $connectionOptions2, t('The default and slave connection options are identical.')); - - // Set up a new connection with different connection info. - $test = $connection_info['default']; - $test['database'] .= 'test'; - Database::addConnectionInfo('test', 'default', $test); - $connection_info = Database::getConnectionInfo('test'); - - // Get a fresh copy of the default connection options. - $connectionOptions = $db->getConnectionOptions(); - $this->assertNotEqual($connection_info['default']['database'], $connectionOptions['database'], t('The test connection info database does not match the current connection options database.')); - } -} - -/** - * Test fetch actions, part 1. - * - * We get timeout errors if we try to run too many tests at once. - */ -class DatabaseFetchTestCase extends DatabaseTestCase { - - public static function getInfo() { - return array( - 'name' => 'Fetch tests', - 'description' => 'Test the Database system\'s various fetch capabilities.', - 'group' => 'Database', - ); - } - - /** - * Confirm that we can fetch a record properly in default object mode. - */ - function testQueryFetchDefault() { - $records = array(); - $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25)); - $this->assertTrue($result instanceof DatabaseStatementInterface, t('Result set is a Drupal statement object.')); - foreach ($result as $record) { - $records[] = $record; - $this->assertTrue(is_object($record), t('Record is an object.')); - $this->assertIdentical($record->name, 'John', t('25 year old is John.')); - } - - $this->assertIdentical(count($records), 1, t('There is only one record.')); - } - - /** - * Confirm that we can fetch a record to an object explicitly. - */ - function testQueryFetchObject() { - $records = array(); - $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_OBJ)); - foreach ($result as $record) { - $records[] = $record; - $this->assertTrue(is_object($record), t('Record is an object.')); - $this->assertIdentical($record->name, 'John', t('25 year old is John.')); - } - - $this->assertIdentical(count($records), 1, t('There is only one record.')); - } - - /** - * Confirm that we can fetch a record to an array associative explicitly. - */ - function testQueryFetchArray() { - $records = array(); - $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_ASSOC)); - foreach ($result as $record) { - $records[] = $record; - if ($this->assertTrue(is_array($record), t('Record is an array.'))) { - $this->assertIdentical($record['name'], 'John', t('Record can be accessed associatively.')); - } - } - - $this->assertIdentical(count($records), 1, t('There is only one record.')); - } - - /** - * Confirm that we can fetch a record into a new instance of a custom class. - * - * @see FakeRecord - */ - function testQueryFetchClass() { - $records = array(); - $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => 'FakeRecord')); - foreach ($result as $record) { - $records[] = $record; - if ($this->assertTrue($record instanceof FakeRecord, t('Record is an object of class FakeRecord.'))) { - $this->assertIdentical($record->name, 'John', t('25 year old is John.')); - } - } - - $this->assertIdentical(count($records), 1, t('There is only one record.')); - } -} - -/** - * Test fetch actions, part 2. - * - * We get timeout errors if we try to run too many tests at once. - */ -class DatabaseFetch2TestCase extends DatabaseTestCase { - - public static function getInfo() { - return array( - 'name' => 'Fetch tests, part 2', - 'description' => 'Test the Database system\'s various fetch capabilities.', - 'group' => 'Database', - ); - } - - function setUp() { - parent::setUp(); - } - - // Confirm that we can fetch a record into an indexed array explicitly. - function testQueryFetchNum() { - $records = array(); - $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_NUM)); - foreach ($result as $record) { - $records[] = $record; - if ($this->assertTrue(is_array($record), t('Record is an array.'))) { - $this->assertIdentical($record[0], 'John', t('Record can be accessed numerically.')); - } - } - - $this->assertIdentical(count($records), 1, 'There is only one record'); - } - - /** - * Confirm that we can fetch a record into a doubly-keyed array explicitly. - */ - function testQueryFetchBoth() { - $records = array(); - $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_BOTH)); - foreach ($result as $record) { - $records[] = $record; - if ($this->assertTrue(is_array($record), t('Record is an array.'))) { - $this->assertIdentical($record[0], 'John', t('Record can be accessed numerically.')); - $this->assertIdentical($record['name'], 'John', t('Record can be accessed associatively.')); - } - } - - $this->assertIdentical(count($records), 1, t('There is only one record.')); - } - - /** - * Confirm that we can fetch an entire column of a result set at once. - */ - function testQueryFetchCol() { - $records = array(); - $result = db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25)); - $column = $result->fetchCol(); - $this->assertIdentical(count($column), 3, t('fetchCol() returns the right number of records.')); - - $result = db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25)); - $i = 0; - foreach ($result as $record) { - $this->assertIdentical($record->name, $column[$i++], t('Column matches direct accesss.')); - } - } -} - -/** - * Test the insert builder. - */ -class DatabaseInsertTestCase extends DatabaseTestCase { - - public static function getInfo() { - return array( - 'name' => 'Insert tests', - 'description' => 'Test the Insert query builder.', - 'group' => 'Database', - ); - } - - /** - * Test the very basic insert functionality. - */ - function testSimpleInsert() { - $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField(); - - $query = db_insert('test'); - $query->fields(array( - 'name' => 'Yoko', - 'age' => '29', - )); - $query->execute(); - - $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField(); - $this->assertIdentical($num_records_before + 1, (int) $num_records_after, t('Record inserts correctly.')); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Yoko'))->fetchField(); - $this->assertIdentical($saved_age, '29', t('Can retrieve after inserting.')); - } - - /** - * Test that we can insert multiple records in one query object. - */ - function testMultiInsert() { - $num_records_before = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField(); - - $query = db_insert('test'); - $query->fields(array( - 'name' => 'Larry', - 'age' => '30', - )); - - // We should be able to specify values in any order if named. - $query->values(array( - 'age' => '31', - 'name' => 'Curly', - )); - - // We should be able to say "use the field order". - // This is not the recommended mechanism for most cases, but it should work. - $query->values(array('Moe', '32')); - $query->execute(); - - $num_records_after = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField(); - $this->assertIdentical($num_records_before + 3, $num_records_after, t('Record inserts correctly.')); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField(); - $this->assertIdentical($saved_age, '30', t('Can retrieve after inserting.')); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField(); - $this->assertIdentical($saved_age, '31', t('Can retrieve after inserting.')); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField(); - $this->assertIdentical($saved_age, '32', t('Can retrieve after inserting.')); - } - - /** - * Test that an insert object can be reused with new data after it executes. - */ - function testRepeatedInsert() { - $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField(); - - $query = db_insert('test'); - - $query->fields(array( - 'name' => 'Larry', - 'age' => '30', - )); - $query->execute(); // This should run the insert, but leave the fields intact. - - // We should be able to specify values in any order if named. - $query->values(array( - 'age' => '31', - 'name' => 'Curly', - )); - $query->execute(); - - // We should be able to say "use the field order". - $query->values(array('Moe', '32')); - $query->execute(); - - $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField(); - $this->assertIdentical((int) $num_records_before + 3, (int) $num_records_after, t('Record inserts correctly.')); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField(); - $this->assertIdentical($saved_age, '30', t('Can retrieve after inserting.')); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField(); - $this->assertIdentical($saved_age, '31', t('Can retrieve after inserting.')); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField(); - $this->assertIdentical($saved_age, '32', t('Can retrieve after inserting.')); - } - - /** - * Test that we can specify fields without values and specify values later. - */ - function testInsertFieldOnlyDefinintion() { - // This is useful for importers, when we want to create a query and define - // its fields once, then loop over a multi-insert execution. - db_insert('test') - ->fields(array('name', 'age')) - ->values(array('Larry', '30')) - ->values(array('Curly', '31')) - ->values(array('Moe', '32')) - ->execute(); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField(); - $this->assertIdentical($saved_age, '30', t('Can retrieve after inserting.')); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField(); - $this->assertIdentical($saved_age, '31', t('Can retrieve after inserting.')); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField(); - $this->assertIdentical($saved_age, '32', t('Can retrieve after inserting.')); - } - - /** - * Test that inserts return the proper auto-increment ID. - */ - function testInsertLastInsertID() { - $id = db_insert('test') - ->fields(array( - 'name' => 'Larry', - 'age' => '30', - )) - ->execute(); - - $this->assertIdentical($id, '5', t('Auto-increment ID returned successfully.')); - } - - /** - * Test that the INSERT INTO ... SELECT ... syntax works. - */ - function testInsertSelect() { - $query = db_select('test_people', 'tp'); - // The query builder will always append expressions after fields. - // Add the expression first to test that the insert fields are correctly - // re-ordered. - $query->addExpression('tp.age', 'age'); - $query - ->fields('tp', array('name','job')) - ->condition('tp.name', 'Meredith'); - - // The resulting query should be equivalent to: - // INSERT INTO test (age, name, job) - // SELECT tp.age AS age, tp.name AS name, tp.job AS job - // FROM test_people tp - // WHERE tp.name = 'Meredith' - db_insert('test') - ->from($query) - ->execute(); - - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Meredith'))->fetchField(); - $this->assertIdentical($saved_age, '30', t('Can retrieve after inserting.')); - } -} - -/** - * Insert tests using LOB fields, which are weird on some databases. - */ -class DatabaseInsertLOBTestCase extends DatabaseTestCase { - - public static function getInfo() { - return array( - 'name' => 'Insert tests, LOB fields', - 'description' => 'Test the Insert query builder with LOB fields.', - 'group' => 'Database', - ); - } - - /** - * Test that we can insert a single blob field successfully. - */ - function testInsertOneBlob() { - $data = "This is\000a test."; - $this->assertTrue(strlen($data) === 15, t('Test data contains a NULL.')); - $id = db_insert('test_one_blob') - ->fields(array('blob1' => $data)) - ->execute(); - $r = db_query('SELECT * FROM {test_one_blob} WHERE id = :id', array(':id' => $id))->fetchAssoc(); - $this->assertTrue($r['blob1'] === $data, t('Can insert a blob: id @id, @data.', array('@id' => $id, '@data' => serialize($r)))); - } - - /** - * Test that we can insert multiple blob fields in the same query. - */ - function testInsertMultipleBlob() { - $id = db_insert('test_two_blobs') - ->fields(array( - 'blob1' => 'This is', - 'blob2' => 'a test', - )) - ->execute(); - $r = db_query('SELECT * FROM {test_two_blobs} WHERE id = :id', array(':id' => $id))->fetchAssoc(); - $this->assertTrue($r['blob1'] === 'This is' && $r['blob2'] === 'a test', t('Can insert multiple blobs per row.')); - } -} - -/** - * Insert tests for "database default" values. - */ -class DatabaseInsertDefaultsTestCase extends DatabaseTestCase { - - public static function getInfo() { - return array( - 'name' => 'Insert tests, default fields', - 'description' => 'Test the Insert query builder with default values.', - 'group' => 'Database', - ); - } - - /** - * Test that we can run a query that is "default values for everything". - */ - function testDefaultInsert() { - $query = db_insert('test')->useDefaults(array('job')); - $id = $query->execute(); - - $schema = drupal_get_schema('test'); - - $job = db_query('SELECT job FROM {test} WHERE id = :id', array(':id' => $id))->fetchField(); - $this->assertEqual($job, $schema['fields']['job']['default'], t('Default field value is set.')); - } - - /** - * Test that no action will be preformed if no fields are specified. - */ - function testDefaultEmptyInsert() { - $num_records_before = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField(); - - try { - $result = db_insert('test')->execute(); - // This is only executed if no exception has been thrown. - $this->fail(t('Expected exception NoFieldsException has not been thrown.')); - } catch (NoFieldsException $e) { - $this->pass(t('Expected exception NoFieldsException has been thrown.')); - } - - $num_records_after = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField(); - $this->assertIdentical($num_records_before, $num_records_after, t('Do nothing as no fields are specified.')); - } - - /** - * Test that we can insert fields with values and defaults in the same query. - */ - function testDefaultInsertWithFields() { - $query = db_insert('test') - ->fields(array('name' => 'Bob')) - ->useDefaults(array('job')); - $id = $query->execute(); - - $schema = drupal_get_schema('test'); - - $job = db_query('SELECT job FROM {test} WHERE id = :id', array(':id' => $id))->fetchField(); - $this->assertEqual($job, $schema['fields']['job']['default'], t('Default field value is set.')); - } -} - -/** - * Update builder tests. - */ -class DatabaseUpdateTestCase extends DatabaseTestCase { - - public static function getInfo() { - return array( - 'name' => 'Update tests', - 'description' => 'Test the Update query builder.', - 'group' => 'Database', - ); - } - - /** - * Confirm that we can update a single record successfully. - */ - function testSimpleUpdate() { - $num_updated = db_update('test') - ->fields(array('name' => 'Tiffany')) - ->condition('id', 1) - ->execute(); - $this->assertIdentical($num_updated, 1, t('Updated 1 record.')); - - $saved_name = db_query('SELECT name FROM {test} WHERE id = :id', array(':id' => 1))->fetchField(); - $this->assertIdentical($saved_name, 'Tiffany', t('Updated name successfully.')); - } - - /** - * Confirm updating to NULL. - */ - function testSimpleNullUpdate() { - $this->ensureSampleDataNull(); - $num_updated = db_update('test_null') - ->fields(array('age' => NULL)) - ->condition('name', 'Kermit') - ->execute(); - $this->assertIdentical($num_updated, 1, t('Updated 1 record.')); - - $saved_age = db_query('SELECT age FROM {test_null} WHERE name = :name', array(':name' => 'Kermit'))->fetchField(); - $this->assertNull($saved_age, t('Updated name successfully.')); - } - - /** - * Confirm that we can update a multiple records successfully. - */ - function testMultiUpdate() { - $num_updated = db_update('test') - ->fields(array('job' => 'Musician')) - ->condition('job', 'Singer') - ->execute(); - $this->assertIdentical($num_updated, 2, t('Updated 2 records.')); - - $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); - $this->assertIdentical($num_matches, '2', t('Updated fields successfully.')); - } - - /** - * Confirm that we can update a multiple records with a non-equality condition. - */ - function testMultiGTUpdate() { - $num_updated = db_update('test') - ->fields(array('job' => 'Musician')) - ->condition('age', 26, '>') - ->execute(); - $this->assertIdentical($num_updated, 2, t('Updated 2 records.')); - - $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); - $this->assertIdentical($num_matches, '2', t('Updated fields successfully.')); - } - - /** - * Confirm that we can update a multiple records with a where call. - */ - function testWhereUpdate() { - $num_updated = db_update('test') - ->fields(array('job' => 'Musician')) - ->where('age > :age', array(':age' => 26)) - ->execute(); - $this->assertIdentical($num_updated, 2, t('Updated 2 records.')); - - $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); - $this->assertIdentical($num_matches, '2', t('Updated fields successfully.')); - } - - /** - * Confirm that we can stack condition and where calls. - */ - function testWhereAndConditionUpdate() { - $update = db_update('test') - ->fields(array('job' => 'Musician')) - ->where('age > :age', array(':age' => 26)) - ->condition('name', 'Ringo'); - $num_updated = $update->execute(); - $this->assertIdentical($num_updated, 1, t('Updated 1 record.')); - - $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); - $this->assertIdentical($num_matches, '1', t('Updated fields successfully.')); - } - - /** - * Test updating with expressions. - */ - function testExpressionUpdate() { - // Set age = 1 for a single row for this test to work. - db_update('test') - ->condition('id', 1) - ->fields(array('age' => 1)) - ->execute(); - - // Ensure that expressions are handled properly. This should set every - // record's age to a square of itself, which will change only three of the - // four records in the table since 1*1 = 1. That means only three records - // are modified, so we should get back 3, not 4, from execute(). - $num_rows = db_update('test') - ->expression('age', 'age * age') - ->execute(); - $this->assertIdentical($num_rows, 3, t('Number of affected rows are returned.')); - } -} - -/** - * Tests for more complex update statements. - */ -class DatabaseUpdateComplexTestCase extends DatabaseTestCase { - - public static function getInfo() { - return array( - 'name' => 'Update tests, Complex', - 'description' => 'Test the Update query builder, complex queries.', - 'group' => 'Database', - ); - } - - /** - * Test updates with OR conditionals. - */ - function testOrConditionUpdate() { - $update = db_update('test') - ->fields(array('job' => 'Musician')) - ->condition(db_or() - ->condition('name', 'John') - ->condition('name', 'Paul') - ); - $num_updated = $update->execute(); - $this->assertIdentical($num_updated, 2, t('Updated 2 records.')); - - $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); - $this->assertIdentical($num_matches, '2', t('Updated fields successfully.')); - } - - /** - * Test WHERE IN clauses. - */ - function testInConditionUpdate() { - $num_updated = db_update('test') - ->fields(array('job' => 'Musician')) - ->condition('name', array('John', 'Paul'), 'IN') - ->execute(); - $this->assertIdentical($num_updated, 2, t('Updated 2 records.')); - - $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); - $this->assertIdentical($num_matches, '2', t('Updated fields successfully.')); - } - - /** - * Test WHERE NOT IN clauses. - */ - function testNotInConditionUpdate() { - // The o is lowercase in the 'NoT IN' operator, to make sure the operators - // work in mixed case. - $num_updated = db_update('test') - ->fields(array('job' => 'Musician')) - ->condition('name', array('John', 'Paul', 'George'), 'NoT IN') - ->execute(); - $this->assertIdentical($num_updated, 1, t('Updated 1 record.')); - - $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); - $this->assertIdentical($num_matches, '1', t('Updated fields successfully.')); - } - - /** - * Test BETWEEN conditional clauses. - */ - function testBetweenConditionUpdate() { - $num_updated = db_update('test') - ->fields(array('job' => 'Musician')) - ->condition('age', array(25, 26), 'BETWEEN') - ->execute(); - $this->assertIdentical($num_updated, 2, t('Updated 2 records.')); - - $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); - $this->assertIdentical($num_matches, '2', t('Updated fields successfully.')); - } - - /** - * Test LIKE conditionals. - */ - function testLikeConditionUpdate() { - $num_updated = db_update('test') - ->fields(array('job' => 'Musician')) - ->condition('name', '%ge%', 'LIKE') - ->execute(); - $this->assertIdentical($num_updated, 1, t('Updated 1 record.')); - - $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); - $this->assertIdentical($num_matches, '1', t('Updated fields successfully.')); - } - - /** - * Test update with expression values. - */ - function testUpdateExpression() { - $before_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField(); - $GLOBALS['larry_test'] = 1; - $num_updated = db_update('test') - ->condition('name', 'Ringo') - ->fields(array('job' => 'Musician')) - ->expression('age', 'age + :age', array(':age' => 4)) - ->execute(); - $this->assertIdentical($num_updated, 1, t('Updated 1 record.')); - - $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); - $this->assertIdentical($num_matches, '1', t('Updated fields successfully.')); - - $person = db_query('SELECT * FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetch(); - $this->assertEqual($person->name, 'Ringo', t('Name set correctly.')); - $this->assertEqual($person->age, $before_age + 4, t('Age set correctly.')); - $this->assertEqual($person->job, 'Musician', t('Job set correctly.')); - $GLOBALS['larry_test'] = 0; - } - - /** - * Test update with only expression values. - */ - function testUpdateOnlyExpression() { - $before_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField(); - $num_updated = db_update('test') - ->condition('name', 'Ringo') - ->expression('age', 'age + :age', array(':age' => 4)) - ->execute(); - $this->assertIdentical($num_updated, 1, t('Updated 1 record.')); - - $after_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField(); - $this->assertEqual($before_age + 4, $after_age, t('Age updated correctly')); - } -} - -/** - * Test update queries involving LOB values. - */ -class DatabaseUpdateLOBTestCase extends DatabaseTestCase { - - public static function getInfo() { - return array( - 'name' => 'Update tests, LOB', - 'description' => 'Test the Update query builder with LOB fields.', - 'group' => 'Database', - ); - } - - /** - * Confirm that we can update a blob column. - */ - function testUpdateOneBlob() { - $data = "This is\000a test."; - $this->assertTrue(strlen($data) === 15, t('Test data contains a NULL.')); - $id = db_insert('test_one_blob') - ->fields(array('blob1' => $data)) - ->execute(); - - $data .= $data; - db_update('test_one_blob') - ->condition('id', $id) - ->fields(array('blob1' => $data)) - ->execute(); - - $r = db_query('SELECT * FROM {test_one_blob} WHERE id = :id', array(':id' => $id))->fetchAssoc(); - $this->assertTrue($r['blob1'] === $data, t('Can update a blob: id @id, @data.', array('@id' => $id, '@data' => serialize($r)))); - } - - /** - * Confirm that we can update two blob columns in the same table. - */ - function testUpdateMultipleBlob() { - $id = db_insert('test_two_blobs') - ->fields(array( - 'blob1' => 'This is', - 'blob2' => 'a test', - )) - ->execute(); - - db_update('test_two_blobs') - ->condition('id', $id) - ->fields(array('blob1' => 'and so', 'blob2' => 'is this')) - ->execute(); - - $r = db_query('SELECT * FROM {test_two_blobs} WHERE id = :id', array(':id' => $id))->fetchAssoc(); - $this->assertTrue($r['blob1'] === 'and so' && $r['blob2'] === 'is this', t('Can update multiple blobs per row.')); - } -} - -/** - * Delete/Truncate tests. - * - * The DELETE tests are not as extensive, as all of the interesting code for - * DELETE queries is in the conditional which is identical to the UPDATE and - * SELECT conditional handling. - * - * The TRUNCATE tests are not extensive either, because the behavior of - * TRUNCATE queries is not consistent across database engines. We only test - * that a TRUNCATE query actually deletes all rows from the target table. - */ -class DatabaseDeleteTruncateTestCase extends DatabaseTestCase { - - public static function getInfo() { - return array( - 'name' => 'Delete/Truncate tests', - 'description' => 'Test the Delete and Truncate query builders.', - 'group' => 'Database', - ); - } - - /** - * Confirm that we can use a subselect in a delete successfully. - */ - function testSubselectDelete() { - $num_records_before = db_query('SELECT COUNT(*) FROM {test_task}')->fetchField(); - $pid_to_delete = db_query("SELECT * FROM {test_task} WHERE task = 'sleep'")->fetchField(); - - $subquery = db_select('test', 't') - ->fields('t', array('id')) - ->condition('t.id', array($pid_to_delete), 'IN'); - $delete = db_delete('test_task') - ->condition('task', 'sleep') - ->condition('pid', $subquery, 'IN'); - - $num_deleted = $delete->execute(); - $this->assertEqual($num_deleted, 1, t("Deleted 1 record.")); - - $num_records_after = db_query('SELECT COUNT(*) FROM {test_task}')->fetchField(); - $this->assertEqual($num_records_before, $num_records_after + $num_deleted, t('Deletion adds up.')); - } - - /** - * Confirm that we can delete a single record successfully. - */ - function testSimpleDelete() { - $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField(); - - $num_deleted = db_delete('test') - ->condition('id', 1) - ->execute(); - $this->assertIdentical($num_deleted, 1, t('Deleted 1 record.')); - - $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField(); - $this->assertEqual($num_records_before, $num_records_after + $num_deleted, t('Deletion adds up.')); - } - - /** - * Confirm that we can truncate a whole table successfully. - */ - function testTruncate() { - $num_records_before = db_query("SELECT COUNT(*) FROM {test}")->fetchField(); - - db_truncate('test')->execute(); - - $num_records_after = db_query("SELECT COUNT(*) FROM {test}")->fetchField(); - $this->assertEqual(0, $num_records_after, t('Truncate really deletes everything.')); - } -} - -/** - * Test the MERGE query builder. - */ -class DatabaseMergeTestCase extends DatabaseTestCase { - - public static function getInfo() { - return array( - 'name' => 'Merge tests', - 'description' => 'Test the Merge query builder.', - 'group' => 'Database', - ); - } - - /** - * Confirm that we can merge-insert a record successfully. - */ - function testMergeInsert() { - $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - - $result = db_merge('test_people') - ->key(array('job' => 'Presenter')) - ->fields(array( - 'age' => 31, - 'name' => 'Tiffany', - )) - ->execute(); - - $this->assertEqual($result, MergeQuery::STATUS_INSERT, t('Insert status returned.')); - - $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - $this->assertEqual($num_records_before + 1, $num_records_after, t('Merge inserted properly.')); - - $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Presenter'))->fetch(); - $this->assertEqual($person->name, 'Tiffany', t('Name set correctly.')); - $this->assertEqual($person->age, 31, t('Age set correctly.')); - $this->assertEqual($person->job, 'Presenter', t('Job set correctly.')); - } - - /** - * Confirm that we can merge-update a record successfully. - */ - function testMergeUpdate() { - $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - - $result = db_merge('test_people') - ->key(array('job' => 'Speaker')) - ->fields(array( - 'age' => 31, - 'name' => 'Tiffany', - )) - ->execute(); - - $this->assertEqual($result, MergeQuery::STATUS_UPDATE, t('Update status returned.')); - - $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - $this->assertEqual($num_records_before, $num_records_after, t('Merge updated properly.')); - - $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch(); - $this->assertEqual($person->name, 'Tiffany', t('Name set correctly.')); - $this->assertEqual($person->age, 31, t('Age set correctly.')); - $this->assertEqual($person->job, 'Speaker', t('Job set correctly.')); - } - - /** - * Confirm that we can merge-update a record successfully, with different insert and update. - */ - function testMergeUpdateExcept() { - $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - - db_merge('test_people') - ->key(array('job' => 'Speaker')) - ->insertFields(array('age' => 31)) - ->updateFields(array('name' => 'Tiffany')) - ->execute(); - - $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - $this->assertEqual($num_records_before, $num_records_after, t('Merge updated properly.')); - - $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch(); - $this->assertEqual($person->name, 'Tiffany', t('Name set correctly.')); - $this->assertEqual($person->age, 30, t('Age skipped correctly.')); - $this->assertEqual($person->job, 'Speaker', t('Job set correctly.')); - } - - /** - * Confirm that we can merge-update a record successfully, with alternate replacement. - */ - function testMergeUpdateExplicit() { - $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - - db_merge('test_people') - ->key(array('job' => 'Speaker')) - ->insertFields(array( - 'age' => 31, - 'name' => 'Tiffany', - )) - ->updateFields(array( - 'name' => 'Joe', - )) - ->execute(); - - $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - $this->assertEqual($num_records_before, $num_records_after, t('Merge updated properly.')); - - $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch(); - $this->assertEqual($person->name, 'Joe', t('Name set correctly.')); - $this->assertEqual($person->age, 30, t('Age skipped correctly.')); - $this->assertEqual($person->job, 'Speaker', t('Job set correctly.')); - } - - /** - * Confirm that we can merge-update a record successfully, with expressions. - */ - function testMergeUpdateExpression() { - $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - - $age_before = db_query('SELECT age FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetchField(); - - // This is a very contrived example, as I have no idea why you'd want to - // change age this way, but that's beside the point. - // Note that we are also double-setting age here, once as a literal and - // once as an expression. This test will only pass if the expression wins, - // which is what is supposed to happen. - db_merge('test_people') - ->key(array('job' => 'Speaker')) - ->fields(array('name' => 'Tiffany')) - ->insertFields(array('age' => 31)) - ->expression('age', 'age + :age', array(':age' => 4)) - ->execute(); - - $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - $this->assertEqual($num_records_before, $num_records_after, t('Merge updated properly.')); - - $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch(); - $this->assertEqual($person->name, 'Tiffany', t('Name set correctly.')); - $this->assertEqual($person->age, $age_before + 4, t('Age updated correctly.')); - $this->assertEqual($person->job, 'Speaker', t('Job set correctly.')); - } - - /** - * Test that we can merge-insert without any update fields. - */ - function testMergeInsertWithoutUpdate() { - $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - - db_merge('test_people') - ->key(array('job' => 'Presenter')) - ->execute(); - - $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - $this->assertEqual($num_records_before + 1, $num_records_after, t('Merge inserted properly.')); - - $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Presenter'))->fetch(); - $this->assertEqual($person->name, '', t('Name set correctly.')); - $this->assertEqual($person->age, 0, t('Age set correctly.')); - $this->assertEqual($person->job, 'Presenter', t('Job set correctly.')); - } - - /** - * Confirm that we can merge-update without any update fields. - */ - function testMergeUpdateWithoutUpdate() { - $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - - db_merge('test_people') - ->key(array('job' => 'Speaker')) - ->execute(); - - $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - $this->assertEqual($num_records_before, $num_records_after, t('Merge skipped properly.')); - - $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch(); - $this->assertEqual($person->name, 'Meredith', t('Name skipped correctly.')); - $this->assertEqual($person->age, 30, t('Age skipped correctly.')); - $this->assertEqual($person->job, 'Speaker', t('Job skipped correctly.')); - - db_merge('test_people') - ->key(array('job' => 'Speaker')) - ->insertFields(array('age' => 31)) - ->execute(); - - $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - $this->assertEqual($num_records_before, $num_records_after, t('Merge skipped properly.')); - - $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch(); - $this->assertEqual($person->name, 'Meredith', t('Name skipped correctly.')); - $this->assertEqual($person->age, 30, t('Age skipped correctly.')); - $this->assertEqual($person->job, 'Speaker', t('Job skipped correctly.')); - } - - /** - * Test that an invalid merge query throws an exception like it is supposed to. - */ - function testInvalidMerge() { - try { - // This query should die because there is no key field specified. - db_merge('test_people') - ->fields(array( - 'age' => 31, - 'name' => 'Tiffany', - )) - ->execute(); - } - catch (InvalidMergeQueryException $e) { - $this->pass(t('InvalidMergeQueryException thrown for invalid query.')); - return; - } - $this->fail(t('No InvalidMergeQueryException thrown')); - } -} - -/** - * Test the SELECT builder. - */ -class DatabaseSelectTestCase extends DatabaseTestCase { - - public static function getInfo() { - return array( - 'name' => 'Select tests', - 'description' => 'Test the Select query builder.', - 'group' => 'Database', - ); - } - - /** - * Test rudimentary SELECT statements. - */ - function testSimpleSelect() { - $query = db_select('test'); - $name_field = $query->addField('test', 'name'); - $age_field = $query->addField('test', 'age', 'age'); - $result = $query->execute(); - - $num_records = 0; - foreach ($result as $record) { - $num_records++; - } - - $this->assertEqual($num_records, 4, t('Returned the correct number of rows.')); - } - - /** - * Test rudimentary SELECT statement with a COMMENT. - */ - function testSimpleComment() { - $query = db_select('test')->comment('Testing query comments'); - $name_field = $query->addField('test', 'name'); - $age_field = $query->addField('test', 'age', 'age'); - $result = $query->execute(); - - $num_records = 0; - foreach ($result as $record) { - $num_records++; - } - - $query = (string)$query; - $expected = "/* Testing query comments */ SELECT test.name AS name, test.age AS age\nFROM \n{test} test"; - - $this->assertEqual($num_records, 4, t('Returned the correct number of rows.')); - $this->assertEqual($query, $expected, t('The flattened query contains the comment string.')); - } - - /** - * Test query COMMENT system against vulnerabilities. - */ - function testVulnerableComment() { - $query = db_select('test')->comment('Testing query comments */ SELECT nid FROM {node}; --'); - $name_field = $query->addField('test', 'name'); - $age_field = $query->addField('test', 'age', 'age'); - $result = $query->execute(); - - $num_records = 0; - foreach ($result as $record) { - $num_records++; - } - - $query = (string)$query; - $expected = "/* Testing query comments SELECT nid FROM {node}; -- */ SELECT test.name AS name, test.age AS age\nFROM \n{test} test"; - - $this->assertEqual($num_records, 4, t('Returned the correct number of rows.')); - $this->assertEqual($query, $expected, t('The flattened query contains the sanitised comment string.')); - } - - /** - * Test basic conditionals on SELECT statements. - */ - function testSimpleSelectConditional() { - $query = db_select('test'); - $name_field = $query->addField('test', 'name'); - $age_field = $query->addField('test', 'age', 'age'); - $query->condition('age', 27); - $result = $query->execute(); - - // Check that the aliases are being created the way we want. - $this->assertEqual($name_field, 'name', t('Name field alias is correct.')); - $this->assertEqual($age_field, 'age', t('Age field alias is correct.')); - - // Ensure that we got the right record. - $record = $result->fetch(); - $this->assertEqual($record->$name_field, 'George', t('Fetched name is correct.')); - $this->assertEqual($record->$age_field, 27, t('Fetched age is correct.')); - } - - /** - * Test SELECT statements with expressions. - */ - function testSimpleSelectExpression() { - $query = db_select('test'); - $name_field = $query->addField('test', 'name'); - $age_field = $query->addExpression("age*2", 'double_age'); - $query->condition('age', 27); - $result = $query->execute(); - - // Check that the aliases are being created the way we want. - $this->assertEqual($name_field, 'name', t('Name field alias is correct.')); - $this->assertEqual($age_field, 'double_age', t('Age field alias is correct.')); - - // Ensure that we got the right record. - $record = $result->fetch(); - $this->assertEqual($record->$name_field, 'George', t('Fetched name is correct.')); - $this->assertEqual($record->$age_field, 27*2, t('Fetched age expression is correct.')); - } - - /** - * Test SELECT statements with multiple expressions. - */ - function testSimpleSelectExpressionMultiple() { - $query = db_select('test'); - $name_field = $query->addField('test', 'name'); - $age_double_field = $query->addExpression("age*2"); - $age_triple_field = $query->addExpression("age*3"); - $query->condition('age', 27); - $result = $query->execute(); - - // Check that the aliases are being created the way we want. - $this->assertEqual($age_double_field, 'expression', t('Double age field alias is correct.')); - $this->assertEqual($age_triple_field, 'expression_2', t('Triple age field alias is correct.')); - - // Ensure that we got the right record. - $record = $result->fetch(); - $this->assertEqual($record->$name_field, 'George', t('Fetched name is correct.')); - $this->assertEqual($record->$age_double_field, 27*2, t('Fetched double age expression is correct.')); - $this->assertEqual($record->$age_triple_field, 27*3, t('Fetched triple age expression is correct.')); - } - - /** - * Test adding multiple fields to a select statement at the same time. - */ - function testSimpleSelectMultipleFields() { - $record = db_select('test') - ->fields('test', array('id', 'name', 'age', 'job')) - ->condition('age', 27) - ->execute()->fetchObject(); - - // Check that all fields we asked for are present. - $this->assertNotNull($record->id, t('ID field is present.')); - $this->assertNotNull($record->name, t('Name field is present.')); - $this->assertNotNull($record->age, t('Age field is present.')); - $this->assertNotNull($record->job, t('Job field is present.')); - - // Ensure that we got the right record. - // Check that all fields we asked for are present. - $this->assertEqual($record->id, 2, t('ID field has the correct value.')); - $this->assertEqual($record->name, 'George', t('Name field has the correct value.')); - $this->assertEqual($record->age, 27, t('Age field has the correct value.')); - $this->assertEqual($record->job, 'Singer', t('Job field has the correct value.')); - } - - /** - * Test adding all fields from a given table to a select statement. - */ - function testSimpleSelectAllFields() { - $record = db_select('test') - ->fields('test') - ->condition('age', 27) - ->execute()->fetchObject(); - - // Check that all fields we asked for are present. - $this->assertNotNull($record->id, t('ID field is present.')); - $this->assertNotNull($record->name, t('Name field is present.')); - $this->assertNotNull($record->age, t('Age field is present.')); - $this->assertNotNull($record->job, t('Job field is present.')); - - // Ensure that we got the right record. - // Check that all fields we asked for are present. - $this->assertEqual($record->id, 2, t('ID field has the correct value.')); - $this->assertEqual($record->name, 'George', t('Name field has the correct value.')); - $this->assertEqual($record->age, 27, t('Age field has the correct value.')); - $this->assertEqual($record->job, 'Singer', t('Job field has the correct value.')); - } - - /** - * Test that we can find a record with a NULL value. - */ - function testNullCondition() { - $this->ensureSampleDataNull(); - - $names = db_select('test_null', 'tn') - ->fields('tn', array('name')) - ->isNull('age') - ->execute()->fetchCol(); - - $this->assertEqual(count($names), 1, t('Correct number of records found with NULL age.')); - $this->assertEqual($names[0], 'Fozzie', t('Correct record returned for NULL age.')); - } - - /** - * Test that we can find a record without a NULL value. - */ - function testNotNullCondition() { - $this->ensureSampleDataNull(); - - $names = db_select('test_null', 'tn') - ->fields('tn', array('name')) - ->isNotNull('tn.age') - ->orderBy('name') - ->execute()->fetchCol(); - - $this->assertEqual(count($names), 2, t('Correct number of records found withNOT NULL age.')); - $this->assertEqual($names[0], 'Gonzo', t('Correct record returned for NOT NULL age.')); - $this->assertEqual($names[1], 'Kermit', t('Correct record returned for NOT NULL age.')); - } - - /** - * Test that we can UNION multiple Select queries together. This is - * semantically equal to UNION DISTINCT, so we don't explicity test that. - */ - function testUnion() { - $query_1 = db_select('test', 't') - ->fields('t', array('name')) - ->condition('age', array(27, 28), 'IN'); - - $query_2 = db_select('test', 't') - ->fields('t', array('name')) - ->condition('age', 28); - - $query_1->union($query_2); - - $names = $query_1->execute()->fetchCol(); - - // Ensure we only get 2 records. - $this->assertEqual(count($names), 2, t('UNION correctly discarded duplicates.')); - - $this->assertEqual($names[0], 'George', t('First query returned correct name.')); - $this->assertEqual($names[1], 'Ringo', t('Second query returned correct name.')); - } - - /** - * Test that we can UNION ALL multiple Select queries together. - */ - function testUnionAll() { - $query_1 = db_select('test', 't') - ->fields('t', array('name')) - ->condition('age', array(27, 28), 'IN'); - - $query_2 = db_select('test', 't') - ->fields('t', array('name')) - ->condition('age', 28); - - $query_1->union($query_2, 'ALL'); - - $names = $query_1->execute()->fetchCol(); - - // Ensure we get all 3 records. - $this->assertEqual(count($names), 3, t('UNION ALL correctly preserved duplicates.')); - - $this->assertEqual($names[0], 'George', t('First query returned correct first name.')); - $this->assertEqual($names[1], 'Ringo', t('Second query returned correct second name.')); - $this->assertEqual($names[2], 'Ringo', t('Third query returned correct name.')); - } - - /** - * Test that random ordering of queries works. - * - * We take the approach of testing the Drupal layer only, rather than trying - * to test that the database's random number generator actually produces - * random queries (which is very difficult to do without an unacceptable risk - * of the test failing by accident). - * - * Therefore, in this test we simply run the same query twice and assert that - * the two results are reordered versions of each other (as well as of the - * same query without the random ordering). It is reasonable to assume that - * if we run the same select query twice and the results are in a different - * order each time, the only way this could happen is if we have successfully - * triggered the database's random ordering functionality. - */ - function testRandomOrder() { - // Use 52 items, so the chance that this test fails by accident will be the - // same as the chance that a deck of cards will come out in the same order - // after shuffling it (in other words, nearly impossible). - $number_of_items = 52; - while (db_query("SELECT MAX(id) FROM {test}")->fetchField() < $number_of_items) { - db_insert('test')->fields(array('name' => $this->randomName()))->execute(); - } - - // First select the items in order and make sure we get an ordered list. - $expected_ids = range(1, $number_of_items); - $ordered_ids = db_select('test', 't') - ->fields('t', array('id')) - ->range(0, $number_of_items) - ->orderBy('id') - ->execute() - ->fetchCol(); - $this->assertEqual($ordered_ids, $expected_ids, t('A query without random ordering returns IDs in the correct order.')); - - // Now perform the same query, but instead choose a random ordering. We - // expect this to contain a differently ordered version of the original - // result. - $randomized_ids = db_select('test', 't') - ->fields('t', array('id')) - ->range(0, $number_of_items) - ->orderRandom() - ->execute() - ->fetchCol(); - $this->assertNotEqual($randomized_ids, $ordered_ids, t('A query with random ordering returns an unordered set of IDs.')); - $sorted_ids = $randomized_ids; - sort($sorted_ids); - $this->assertEqual($sorted_ids, $ordered_ids, t('After sorting the random list, the result matches the original query.')); - - // Now perform the exact same query again, and make sure the order is - // different. - $randomized_ids_second_set = db_select('test', 't') - ->fields('t', array('id')) - ->range(0, $number_of_items) - ->orderRandom() - ->execute() - ->fetchCol(); - $this->assertNotEqual($randomized_ids_second_set, $randomized_ids, t('Performing the query with random ordering a second time returns IDs in a different order.')); - $sorted_ids_second_set = $randomized_ids_second_set; - sort($sorted_ids_second_set); - $this->assertEqual($sorted_ids_second_set, $sorted_ids, t('After sorting the second random list, the result matches the sorted version of the first random list.')); - } - - /** - * Test that aliases are renamed when duplicates. - */ - function testSelectDuplicateAlias() { - $query = db_select('test', 't'); - $alias1 = $query->addField('t', 'name', 'the_alias'); - $alias2 = $query->addField('t', 'age', 'the_alias'); - $this->assertNotIdentical($alias1, $alias2, 'Duplicate aliases are renamed.'); - } -} - -/** - * Test case for subselects in a dynamic SELECT query. - */ -class DatabaseSelectSubqueryTestCase extends DatabaseTestCase { - - public static function getInfo() { - return array( - 'name' => 'Select tests, subqueries', - 'description' => 'Test the Select query builder.', - 'group' => 'Database', - ); - } - - /** - * Test that we can use a subquery in a FROM clause. - */ - function testFromSubquerySelect() { - // Create a subquery, which is just a normal query object. - $subquery = db_select('test_task', 'tt'); - $subquery->addField('tt', 'pid', 'pid'); - $subquery->addField('tt', 'task', 'task'); - $subquery->condition('priority', 1); - - for ($i = 0; $i < 2; $i++) { - // Create another query that joins against the virtual table resulting - // from the subquery. - $select = db_select($subquery, 'tt2'); - $select->join('test', 't', 't.id=tt2.pid'); - $select->addField('t', 'name'); - if ($i) { - // Use a different number of conditions here to confuse the subquery - // placeholder counter, testing http://drupal.org/node/1112854. - $select->condition('name', 'John'); - } - $select->condition('task', 'code'); - - // The resulting query should be equivalent to: - // SELECT t.name - // FROM (SELECT tt.pid AS pid, tt.task AS task FROM test_task tt WHERE priority=1) tt - // INNER JOIN test t ON t.id=tt.pid - // WHERE tt.task = 'code' - $people = $select->execute()->fetchCol(); - - $this->assertEqual(count($people), 1, t('Returned the correct number of rows.')); - } - } - - /** - * Test that we can use a subquery in a FROM clause with a limit. - */ - function testFromSubquerySelectWithLimit() { - // Create a subquery, which is just a normal query object. - $subquery = db_select('test_task', 'tt'); - $subquery->addField('tt', 'pid', 'pid'); - $subquery->addField('tt', 'task', 'task'); - $subquery->orderBy('priority', 'DESC'); - $subquery->range(0, 1); - - // Create another query that joins against the virtual table resulting - // from the subquery. - $select = db_select($subquery, 'tt2'); - $select->join('test', 't', 't.id=tt2.pid'); - $select->addField('t', 'name'); - - // The resulting query should be equivalent to: - // SELECT t.name - // FROM (SELECT tt.pid AS pid, tt.task AS task FROM test_task tt ORDER BY priority DESC LIMIT 1 OFFSET 0) tt - // INNER JOIN test t ON t.id=tt.pid - $people = $select->execute()->fetchCol(); - - $this->assertEqual(count($people), 1, t('Returned the correct number of rows.')); - } - - /** - * Test that we can use a subquery in a WHERE clause. - */ - function testConditionSubquerySelect() { - // Create a subquery, which is just a normal query object. - $subquery = db_select('test_task', 'tt'); - $subquery->addField('tt', 'pid', 'pid'); - $subquery->condition('tt.priority', 1); - - // Create another query that joins against the virtual table resulting - // from the subquery. - $select = db_select('test_task', 'tt2'); - $select->addField('tt2', 'task'); - $select->condition('tt2.pid', $subquery, 'IN'); - - // The resulting query should be equivalent to: - // SELECT tt2.name - // FROM test tt2 - // WHERE tt2.pid IN (SELECT tt.pid AS pid FROM test_task tt WHERE tt.priority=1) - $people = $select->execute()->fetchCol(); - $this->assertEqual(count($people), 5, t('Returned the correct number of rows.')); - } - - /** - * Test that we can use a subquery in a JOIN clause. - */ - function testJoinSubquerySelect() { - // Create a subquery, which is just a normal query object. - $subquery = db_select('test_task', 'tt'); - $subquery->addField('tt', 'pid', 'pid'); - $subquery->condition('priority', 1); - - // Create another query that joins against the virtual table resulting - // from the subquery. - $select = db_select('test', 't'); - $select->join($subquery, 'tt', 't.id=tt.pid'); - $select->addField('t', 'name'); - - // The resulting query should be equivalent to: - // SELECT t.name - // FROM test t - // INNER JOIN (SELECT tt.pid AS pid FROM test_task tt WHERE priority=1) tt ON t.id=tt.pid - $people = $select->execute()->fetchCol(); - - $this->assertEqual(count($people), 2, t('Returned the correct number of rows.')); - } - - /** - * Test EXISTS subquery conditionals on SELECT statements. - * - * We essentially select all rows from the {test} table that have matching - * rows in the {test_people} table based on the shared name column. - */ - function testExistsSubquerySelect() { - // Put George into {test_people}. - db_insert('test_people') - ->fields(array( - 'name' => 'George', - 'age' => 27, - 'job' => 'Singer', - )) - ->execute(); - // Base query to {test}. - $query = db_select('test', 't') - ->fields('t', array('name')); - // Subquery to {test_people}. - $subquery = db_select('test_people', 'tp') - ->fields('tp', array('name')) - ->where('tp.name = t.name'); - $query->exists($subquery); - $result = $query->execute(); - - // Ensure that we got the right record. - $record = $result->fetch(); - $this->assertEqual($record->name, 'George', t('Fetched name is correct using EXISTS query.')); - } - - /** - * Test NOT EXISTS subquery conditionals on SELECT statements. - * - * We essentially select all rows from the {test} table that don't have - * matching rows in the {test_people} table based on the shared name column. - */ - function testNotExistsSubquerySelect() { - // Put George into {test_people}. - db_insert('test_people') - ->fields(array( - 'name' => 'George', - 'age' => 27, - 'job' => 'Singer', - )) - ->execute(); - - // Base query to {test}. - $query = db_select('test', 't') - ->fields('t', array('name')); - // Subquery to {test_people}. - $subquery = db_select('test_people', 'tp') - ->fields('tp', array('name')) - ->where('tp.name = t.name'); - $query->notExists($subquery); - - // Ensure that we got the right number of records. - $people = $query->execute()->fetchCol(); - $this->assertEqual(count($people), 3, t('NOT EXISTS query returned the correct results.')); - } -} - -/** - * Test select with order by clauses. - */ -class DatabaseSelectOrderedTestCase extends DatabaseTestCase { - - public static function getInfo() { - return array( - 'name' => 'Select tests, ordered', - 'description' => 'Test the Select query builder.', - 'group' => 'Database', - ); - } - - /** - * Test basic order by. - */ - function testSimpleSelectOrdered() { - $query = db_select('test'); - $name_field = $query->addField('test', 'name'); - $age_field = $query->addField('test', 'age', 'age'); - $query->orderBy($age_field); - $result = $query->execute(); - - $num_records = 0; - $last_age = 0; - foreach ($result as $record) { - $num_records++; - $this->assertTrue($record->age >= $last_age, t('Results returned in correct order.')); - $last_age = $record->age; - } - - $this->assertEqual($num_records, 4, t('Returned the correct number of rows.')); - } - - /** - * Test multiple order by. - */ - function testSimpleSelectMultiOrdered() { - $query = db_select('test'); - $name_field = $query->addField('test', 'name'); - $age_field = $query->addField('test', 'age', 'age'); - $job_field = $query->addField('test', 'job'); - $query->orderBy($job_field); - $query->orderBy($age_field); - $result = $query->execute(); - - $num_records = 0; - $expected = array( - array('Ringo', 28, 'Drummer'), - array('John', 25, 'Singer'), - array('George', 27, 'Singer'), - array('Paul', 26, 'Songwriter'), - ); - $results = $result->fetchAll(PDO::FETCH_NUM); - foreach ($expected as $k => $record) { - $num_records++; - foreach ($record as $kk => $col) { - if ($expected[$k][$kk] != $results[$k][$kk]) { - $this->assertTrue(FALSE, t('Results returned in correct order.')); - } - } - } - $this->assertEqual($num_records, 4, t('Returned the correct number of rows.')); - } - - /** - * Test order by descending. - */ - function testSimpleSelectOrderedDesc() { - $query = db_select('test'); - $name_field = $query->addField('test', 'name'); - $age_field = $query->addField('test', 'age', 'age'); - $query->orderBy($age_field, 'DESC'); - $result = $query->execute(); - - $num_records = 0; - $last_age = 100000000; - foreach ($result as $record) { - $num_records++; - $this->assertTrue($record->age <= $last_age, t('Results returned in correct order.')); - $last_age = $record->age; - } - - $this->assertEqual($num_records, 4, t('Returned the correct number of rows.')); - } -} - -/** - * Test more complex select statements. - */ -class DatabaseSelectComplexTestCase extends DatabaseTestCase { - - public static function getInfo() { - return array( - 'name' => 'Select tests, complex', - 'description' => 'Test the Select query builder with more complex queries.', - 'group' => 'Database', - ); - } - - /** - * Test simple JOIN statements. - */ - function testDefaultJoin() { - $query = db_select('test_task', 't'); - $people_alias = $query->join('test', 'p', 't.pid = p.id'); - $name_field = $query->addField($people_alias, 'name', 'name'); - $task_field = $query->addField('t', 'task', 'task'); - $priority_field = $query->addField('t', 'priority', 'priority'); - - $query->orderBy($priority_field); - $result = $query->execute(); - - $num_records = 0; - $last_priority = 0; - foreach ($result as $record) { - $num_records++; - $this->assertTrue($record->$priority_field >= $last_priority, t('Results returned in correct order.')); - $this->assertNotEqual($record->$name_field, 'Ringo', t('Taskless person not selected.')); - $last_priority = $record->$priority_field; - } - - $this->assertEqual($num_records, 7, t('Returned the correct number of rows.')); - } - - /** - * Test LEFT OUTER joins. - */ - function testLeftOuterJoin() { - $query = db_select('test', 'p'); - $people_alias = $query->leftJoin('test_task', 't', 't.pid = p.id'); - $name_field = $query->addField('p', 'name', 'name'); - $task_field = $query->addField($people_alias, 'task', 'task'); - $priority_field = $query->addField($people_alias, 'priority', 'priority'); - - $query->orderBy($name_field); - $result = $query->execute(); - - $num_records = 0; - $last_name = 0; - - foreach ($result as $record) { - $num_records++; - $this->assertTrue(strcmp($record->$name_field, $last_name) >= 0, t('Results returned in correct order.')); - $last_priority = $record->$name_field; - } - - $this->assertEqual($num_records, 8, t('Returned the correct number of rows.')); - } - - /** - * Test GROUP BY clauses. - */ - function testGroupBy() { - $query = db_select('test_task', 't'); - $count_field = $query->addExpression('COUNT(task)', 'num'); - $task_field = $query->addField('t', 'task'); - $query->orderBy($count_field); - $query->groupBy($task_field); - $result = $query->execute(); - - $num_records = 0; - $last_count = 0; - $records = array(); - foreach ($result as $record) { - $num_records++; - $this->assertTrue($record->$count_field >= $last_count, t('Results returned in correct order.')); - $last_count = $record->$count_field; - $records[$record->$task_field] = $record->$count_field; - } - - $correct_results = array( - 'eat' => 1, - 'sleep' => 2, - 'code' => 1, - 'found new band' => 1, - 'perform at superbowl' => 1, - ); - - foreach ($correct_results as $task => $count) { - $this->assertEqual($records[$task], $count, t("Correct number of '@task' records found.", array('@task' => $task))); - } - - $this->assertEqual($num_records, 6, t('Returned the correct number of total rows.')); - } - - /** - * Test GROUP BY and HAVING clauses together. - */ - function testGroupByAndHaving() { - $query = db_select('test_task', 't'); - $count_field = $query->addExpression('COUNT(task)', 'num'); - $task_field = $query->addField('t', 'task'); - $query->orderBy($count_field); - $query->groupBy($task_field); - $query->having('COUNT(task) >= 2'); - $result = $query->execute(); - - $num_records = 0; - $last_count = 0; - $records = array(); - foreach ($result as $record) { - $num_records++; - $this->assertTrue($record->$count_field >= 2, t('Record has the minimum count.')); - $this->assertTrue($record->$count_field >= $last_count, t('Results returned in correct order.')); - $last_count = $record->$count_field; - $records[$record->$task_field] = $record->$count_field; - } - - $correct_results = array( - 'sleep' => 2, - ); - - foreach ($correct_results as $task => $count) { - $this->assertEqual($records[$task], $count, t("Correct number of '@task' records found.", array('@task' => $task))); - } - - $this->assertEqual($num_records, 1, t('Returned the correct number of total rows.')); - } - - /** - * Test range queries. The SQL clause varies with the database. - */ - function testRange() { - $query = db_select('test'); - $name_field = $query->addField('test', 'name'); - $age_field = $query->addField('test', 'age', 'age'); - $query->range(0, 2); - $result = $query->execute(); - - $num_records = 0; - foreach ($result as $record) { - $num_records++; - } - - $this->assertEqual($num_records, 2, t('Returned the correct number of rows.')); - } - - /** - * Test distinct queries. - */ - function testDistinct() { - $query = db_select('test_task'); - $task_field = $query->addField('test_task', 'task'); - $query->distinct(); - $result = $query->execute(); - - $num_records = 0; - foreach ($result as $record) { - $num_records++; - } - - $this->assertEqual($num_records, 6, t('Returned the correct number of rows.')); - } - - /** - * Test that we can generate a count query from a built query. - */ - function testCountQuery() { - $query = db_select('test'); - $name_field = $query->addField('test', 'name'); - $age_field = $query->addField('test', 'age', 'age'); - $query->orderBy('name'); - - $count = $query->countQuery()->execute()->fetchField(); - - $this->assertEqual($count, 4, t('Counted the correct number of records.')); - - // Now make sure we didn't break the original query! We should still have - // all of the fields we asked for. - $record = $query->execute()->fetch(); - $this->assertEqual($record->$name_field, 'George', t('Correct data retrieved.')); - $this->assertEqual($record->$age_field, 27, t('Correct data retrieved.')); - } - - /** - * Test that countQuery properly removes 'all_fields' statements and - * ordering clauses. - */ - function testCountQueryRemovals() { - $query = db_select('test'); - $query->fields('test'); - $query->orderBy('name'); - $count = $query->countQuery(); - - // Check that the 'all_fields' statement is handled properly. - $tables = $query->getTables(); - $this->assertEqual($tables['test']['all_fields'], 1, t('Query correctly sets \'all_fields\' statement.')); - $tables = $count->getTables(); - $this->assertFalse(isset($tables['test']['all_fields']), t('Count query correctly unsets \'all_fields\' statement.')); - - // Check that the ordering clause is handled properly. - $orderby = $query->getOrderBy(); - $this->assertEqual($orderby['name'], 'ASC', t('Query correctly sets ordering clause.')); - $orderby = $count->getOrderBy(); - $this->assertFalse(isset($orderby['name']), t('Count query correctly unsets ordering caluse.')); - - // Make sure that the count query works. - $count = $count->execute()->fetchField(); - - $this->assertEqual($count, 4, t('Counted the correct number of records.')); - } - - - /** - * Test that countQuery properly removes fields and expressions. - */ - function testCountQueryFieldRemovals() { - // countQuery should remove all fields and expressions, so this can be - // tested by adding a non-existent field and expression: if it ends - // up in the query, an error will be thrown. If not, it will return the - // number of records, which in this case happens to be 4 (there are four - // records in the {test} table). - $query = db_select('test'); - $query->fields('test', array('fail')); - $this->assertEqual(4, $query->countQuery()->execute()->fetchField(), t('Count Query removed fields')); - - $query = db_select('test'); - $query->addExpression('fail'); - $this->assertEqual(4, $query->countQuery()->execute()->fetchField(), t('Count Query removed expressions')); - } - - /** - * Test that we can generate a count query from a query with distinct. - */ - function testCountQueryDistinct() { - $query = db_select('test_task'); - $task_field = $query->addField('test_task', 'task'); - $query->distinct(); - - $count = $query->countQuery()->execute()->fetchField(); - - $this->assertEqual($count, 6, t('Counted the correct number of records.')); - } - - /** - * Test that we can generate a count query from a query with GROUP BY. - */ - function testCountQueryGroupBy() { - $query = db_select('test_task'); - $pid_field = $query->addField('test_task', 'pid'); - $query->groupBy('pid'); - - $count = $query->countQuery()->execute()->fetchField(); - - $this->assertEqual($count, 3, t('Counted the correct number of records.')); - - // Use a column alias as, without one, the query can succeed for the wrong - // reason. - $query = db_select('test_task'); - $pid_field = $query->addField('test_task', 'pid', 'pid_alias'); - $query->addExpression('COUNT(test_task.task)', 'count'); - $query->groupBy('pid_alias'); - $query->orderBy('pid_alias', 'asc'); - - $count = $query->countQuery()->execute()->fetchField(); - - $this->assertEqual($count, 3, t('Counted the correct number of records.')); - } - - /** - * Confirm that we can properly nest conditional clauses. - */ - function testNestedConditions() { - // This query should translate to: - // "SELECT job FROM {test} WHERE name = 'Paul' AND (age = 26 OR age = 27)" - // That should find only one record. Yes it's a non-optimal way of writing - // that query but that's not the point! - $query = db_select('test'); - $query->addField('test', 'job'); - $query->condition('name', 'Paul'); - $query->condition(db_or()->condition('age', 26)->condition('age', 27)); - - $job = $query->execute()->fetchField(); - $this->assertEqual($job, 'Songwriter', t('Correct data retrieved.')); - } - - /** - * Confirm we can join on a single table twice with a dynamic alias. - */ - function testJoinTwice() { - $query = db_select('test')->fields('test'); - $alias = $query->join('test', 'test', 'test.job = %alias.job'); - $query->addField($alias, 'name', 'othername'); - $query->addField($alias, 'job', 'otherjob'); - $query->where("$alias.name <> test.name"); - $crowded_job = $query->execute()->fetch(); - $this->assertEqual($crowded_job->job, $crowded_job->otherjob, t('Correctly joined same table twice.')); - $this->assertNotEqual($crowded_job->name, $crowded_job->othername, t('Correctly joined same table twice.')); - } - -} - -/** - * Test more complex select statements, part 2. - */ -class DatabaseSelectComplexTestCase2 extends DatabaseTestCase { - - public static function getInfo() { - return array( - 'name' => 'Select tests, complex 2', - 'description' => 'Test the Select query builder with even more complex queries.', - 'group' => 'Database', - ); - } - - function setUp() { - DrupalWebTestCase::setUp('database_test', 'node_access_test'); - - $schema['test'] = drupal_get_schema('test'); - $schema['test_people'] = drupal_get_schema('test_people'); - $schema['test_one_blob'] = drupal_get_schema('test_one_blob'); - $schema['test_two_blobs'] = drupal_get_schema('test_two_blobs'); - $schema['test_task'] = drupal_get_schema('test_task'); - - $this->installTables($schema); - - $this->addSampleData(); - } - - /** - * Test that we can join on a query. - */ - function testJoinSubquery() { - $acct = $this->drupalCreateUser(array('access content')); - $this->drupalLogin($acct); - - $query = db_select('test_task', 'tt', array('target' => 'slave')); - $query->addExpression('tt.pid + 1', 'abc'); - $query->condition('priority', 1, '>'); - $query->condition('priority', 100, '<'); - - $subquery = db_select('test', 'tp'); - $subquery->join('test_one_blob', 'tpb', 'tp.id = tpb.id'); - $subquery->join('node', 'n', 'tp.id = n.nid'); - $subquery->addTag('node_access'); - $subquery->addMetaData('account', $acct); - $subquery->addField('tp', 'id'); - $subquery->condition('age', 5, '>'); - $subquery->condition('age', 500, '<'); - - $query->leftJoin($subquery, 'sq', 'tt.pid = sq.id'); - $query->join('test_one_blob', 'tb3', 'tt.pid = tb3.id'); - - // Construct the query string. - // This is the same sequence that SelectQuery::execute() goes through. - $query->preExecute(); - $query->getArguments(); - $str = (string) $query; - - // Verify that the string only has one copy of condition placeholder 0. - $pos = strpos($str, 'db_condition_placeholder_0', 0); - $pos2 = strpos($str, 'db_condition_placeholder_0', $pos + 1); - $this->assertFalse($pos2, "Condition placeholder is not repeated"); - } -} - -class DatabaseSelectPagerDefaultTestCase extends DatabaseTestCase { - - public static function getInfo() { - return array( - 'name' => 'Pager query tests', - 'description' => 'Test the pager query extender.', - 'group' => 'Database', - ); - } - - /** - * Confirm that a pager query returns the correct results. - * - * Note that we have to make an HTTP request to a test page handler - * because the pager depends on GET parameters. - */ - function testEvenPagerQuery() { - // To keep the test from being too brittle, we determine up front - // what the page count should be dynamically, and pass the control - // information forward to the actual query on the other side of the - // HTTP request. - $limit = 2; - $count = db_query('SELECT COUNT(*) FROM {test}')->fetchField(); - - $correct_number = $limit; - $num_pages = floor($count / $limit); - - // If there is no remainder from rounding, subtract 1 since we index from 0. - if (!($num_pages * $limit < $count)) { - $num_pages--; - } - - for ($page = 0; $page <= $num_pages; ++$page) { - $this->drupalGet('database_test/pager_query_even/' . $limit, array('query' => array('page' => $page))); - $data = json_decode($this->drupalGetContent()); - - if ($page == $num_pages) { - $correct_number = $count - ($limit * $page); - } - - $this->assertEqual(count($data->names), $correct_number, t('Correct number of records returned by pager: @number', array('@number' => $correct_number))); - } - } - - /** - * Confirm that a pager query returns the correct results. - * - * Note that we have to make an HTTP request to a test page handler - * because the pager depends on GET parameters. - */ - function testOddPagerQuery() { - // To keep the test from being too brittle, we determine up front - // what the page count should be dynamically, and pass the control - // information forward to the actual query on the other side of the - // HTTP request. - $limit = 2; - $count = db_query('SELECT COUNT(*) FROM {test_task}')->fetchField(); - - $correct_number = $limit; - $num_pages = floor($count / $limit); - - // If there is no remainder from rounding, subtract 1 since we index from 0. - if (!($num_pages * $limit < $count)) { - $num_pages--; - } - - for ($page = 0; $page <= $num_pages; ++$page) { - $this->drupalGet('database_test/pager_query_odd/' . $limit, array('query' => array('page' => $page))); - $data = json_decode($this->drupalGetContent()); - - if ($page == $num_pages) { - $correct_number = $count - ($limit * $page); - } - - $this->assertEqual(count($data->names), $correct_number, t('Correct number of records returned by pager: @number', array('@number' => $correct_number))); - } - } - - /** - * Confirm that a pager query with inner pager query returns valid results. - * - * This is a regression test for #467984. - */ - function testInnerPagerQuery() { - $query = db_select('test', 't')->extend('PagerDefault'); - $query - ->fields('t', array('age')) - ->orderBy('age') - ->limit(5); - - $outer_query = db_select($query); - $outer_query->addField('subquery', 'age'); - - $ages = $outer_query - ->execute() - ->fetchCol(); - $this->assertEqual($ages, array(25, 26, 27, 28), t('Inner pager query returned the correct ages.')); - } - - /** - * Confirm that a paging query with a having expression returns valid results. - * - * This is a regression test for #467984. - */ - function testHavingPagerQuery() { - $query = db_select('test', 't')->extend('PagerDefault'); - $query - ->fields('t', array('name')) - ->orderBy('name') - ->groupBy('name') - ->having('MAX(age) > :count', array(':count' => 26)) - ->limit(5); - - $ages = $query - ->execute() - ->fetchCol(); - $this->assertEqual($ages, array('George', 'Ringo'), t('Pager query with having expression returned the correct ages.')); - } - - /** - * Confirm that every pager gets a valid non-overlaping element ID. - */ - function testElementNumbers() { - $_GET['page'] = '3, 2, 1, 0'; - - $name = db_select('test', 't')->extend('PagerDefault') - ->element(2) - ->fields('t', array('name')) - ->orderBy('age') - ->limit(1) - ->execute() - ->fetchField(); - $this->assertEqual($name, 'Paul', t('Pager query #1 with a specified element ID returned the correct results.')); - - // Setting an element smaller than the previous one - // should not overwrite the pager $maxElement with a smaller value. - $name = db_select('test', 't')->extend('PagerDefault') - ->element(1) - ->fields('t', array('name')) - ->orderBy('age') - ->limit(1) - ->execute() - ->fetchField(); - $this->assertEqual($name, 'George', t('Pager query #2 with a specified element ID returned the correct results.')); - - $name = db_select('test', 't')->extend('PagerDefault') - ->fields('t', array('name')) - ->orderBy('age') - ->limit(1) - ->execute() - ->fetchField(); - $this->assertEqual($name, 'John', t('Pager query #3 with a generated element ID returned the correct results.')); - - unset($_GET['page']); - } -} - - -class DatabaseSelectTableSortDefaultTestCase extends DatabaseTestCase { - - public static function getInfo() { - return array( - 'name' => 'Tablesort query tests', - 'description' => 'Test the tablesort query extender.', - 'group' => 'Database', - ); - } - - /** - * Confirm that a tablesort query returns the correct results. - * - * Note that we have to make an HTTP request to a test page handler - * because the pager depends on GET parameters. - */ - function testTableSortQuery() { - $sorts = array( - array('field' => t('Task ID'), 'sort' => 'desc', 'first' => 'perform at superbowl', 'last' => 'eat'), - array('field' => t('Task ID'), 'sort' => 'asc', 'first' => 'eat', 'last' => 'perform at superbowl'), - array('field' => t('Task'), 'sort' => 'asc', 'first' => 'code', 'last' => 'sleep'), - array('field' => t('Task'), 'sort' => 'desc', 'first' => 'sleep', 'last' => 'code'), - // more elements here - - ); - - foreach ($sorts as $sort) { - $this->drupalGet('database_test/tablesort/', array('query' => array('order' => $sort['field'], 'sort' => $sort['sort']))); - $data = json_decode($this->drupalGetContent()); - - $first = array_shift($data->tasks); - $last = array_pop($data->tasks); - - $this->assertEqual($first->task, $sort['first'], t('Items appear in the correct order.')); - $this->assertEqual($last->task, $sort['last'], t('Items appear in the correct order.')); - } - } - - /** - * Confirm that if a tablesort's orderByHeader is called before another orderBy, that the header happens first. - * - */ - function testTableSortQueryFirst() { - $sorts = array( - array('field' => t('Task ID'), 'sort' => 'desc', 'first' => 'perform at superbowl', 'last' => 'eat'), - array('field' => t('Task ID'), 'sort' => 'asc', 'first' => 'eat', 'last' => 'perform at superbowl'), - array('field' => t('Task'), 'sort' => 'asc', 'first' => 'code', 'last' => 'sleep'), - array('field' => t('Task'), 'sort' => 'desc', 'first' => 'sleep', 'last' => 'code'), - // more elements here - - ); - - foreach ($sorts as $sort) { - $this->drupalGet('database_test/tablesort_first/', array('query' => array('order' => $sort['field'], 'sort' => $sort['sort']))); - $data = json_decode($this->drupalGetContent()); - - $first = array_shift($data->tasks); - $last = array_pop($data->tasks); - - $this->assertEqual($first->task, $sort['first'], t('Items appear in the correct order sorting by @field @sort.', array('@field' => $sort['field'], '@sort' => $sort['sort']))); - $this->assertEqual($last->task, $sort['last'], t('Items appear in the correct order sorting by @field @sort.', array('@field' => $sort['field'], '@sort' => $sort['sort']))); - } - } - - /** - * Confirm that if a sort is not set in a tableselect form there is no error thrown when using the default. - */ - function testTableSortDefaultSort() { - $this->drupalGet('database_test/tablesort_default_sort'); - // Any PHP errors or notices thrown would trigger a simpletest exception, so - // no additional assertions are needed. - } -} - -/** - * Select tagging tests. - * - * Tags are a way to flag queries for alter hooks so they know - * what type of query it is, such as "node_access". - */ -class DatabaseTaggingTestCase extends DatabaseTestCase { - - public static function getInfo() { - return array( - 'name' => 'Query tagging tests', - 'description' => 'Test the tagging capabilities of the Select builder.', - 'group' => 'Database', - ); - } - - /** - * Confirm that a query has a "tag" added to it. - */ - function testHasTag() { - $query = db_select('test'); - $query->addField('test', 'name'); - $query->addField('test', 'age', 'age'); - - $query->addTag('test'); - - $this->assertTrue($query->hasTag('test'), t('hasTag() returned true.')); - $this->assertFalse($query->hasTag('other'), t('hasTag() returned false.')); - } - - /** - * Test query tagging "has all of these tags" functionality. - */ - function testHasAllTags() { - $query = db_select('test'); - $query->addField('test', 'name'); - $query->addField('test', 'age', 'age'); - - $query->addTag('test'); - $query->addTag('other'); - - $this->assertTrue($query->hasAllTags('test', 'other'), t('hasAllTags() returned true.')); - $this->assertFalse($query->hasAllTags('test', 'stuff'), t('hasAllTags() returned false.')); - } - - /** - * Test query tagging "has at least one of these tags" functionality. - */ - function testHasAnyTag() { - $query = db_select('test'); - $query->addField('test', 'name'); - $query->addField('test', 'age', 'age'); - - $query->addTag('test'); - - $this->assertTrue($query->hasAnyTag('test', 'other'), t('hasAnyTag() returned true.')); - $this->assertFalse($query->hasAnyTag('other', 'stuff'), t('hasAnyTag() returned false.')); - } - - /** - * Test that we can attach meta data to a query object. - * - * This is how we pass additional context to alter hooks. - */ - function testMetaData() { - $query = db_select('test'); - $query->addField('test', 'name'); - $query->addField('test', 'age', 'age'); - - $data = array( - 'a' => 'A', - 'b' => 'B', - ); - - $query->addMetaData('test', $data); - - $return = $query->getMetaData('test'); - $this->assertEqual($data, $return, t('Corect metadata returned.')); - - $return = $query->getMetaData('nothere'); - $this->assertNull($return, t('Non-existent key returned NULL.')); - } -} - -/** - * Select alter tests. - * - * @see database_test_query_alter() - */ -class DatabaseAlterTestCase extends DatabaseTestCase { - - public static function getInfo() { - return array( - 'name' => 'Query altering tests', - 'description' => 'Test the hook_query_alter capabilities of the Select builder.', - 'group' => 'Database', - ); - } - - /** - * Test that we can do basic alters. - */ - function testSimpleAlter() { - $query = db_select('test'); - $query->addField('test', 'name'); - $query->addField('test', 'age', 'age'); - $query->addTag('database_test_alter_add_range'); - - $result = $query->execute(); - - $num_records = 0; - foreach ($result as $record) { - $num_records++; - } - - $this->assertEqual($num_records, 2, t('Returned the correct number of rows.')); - } - - /** - * Test that we can alter the joins on a query. - */ - function testAlterWithJoin() { - $query = db_select('test_task'); - $tid_field = $query->addField('test_task', 'tid'); - $task_field = $query->addField('test_task', 'task'); - $query->orderBy($task_field); - $query->addTag('database_test_alter_add_join'); - - $result = $query->execute(); - - $records = $result->fetchAll(); - - $this->assertEqual(count($records), 2, t('Returned the correct number of rows.')); - - $this->assertEqual($records[0]->name, 'George', t('Correct data retrieved.')); - $this->assertEqual($records[0]->$tid_field, 4, t('Correct data retrieved.')); - $this->assertEqual($records[0]->$task_field, 'sing', t('Correct data retrieved.')); - $this->assertEqual($records[1]->name, 'George', t('Correct data retrieved.')); - $this->assertEqual($records[1]->$tid_field, 5, t('Correct data retrieved.')); - $this->assertEqual($records[1]->$task_field, 'sleep', t('Correct data retrieved.')); - } - - /** - * Test that we can alter a query's conditionals. - */ - function testAlterChangeConditional() { - $query = db_select('test_task'); - $tid_field = $query->addField('test_task', 'tid'); - $pid_field = $query->addField('test_task', 'pid'); - $task_field = $query->addField('test_task', 'task'); - $people_alias = $query->join('test', 'people', "test_task.pid = people.id"); - $name_field = $query->addField($people_alias, 'name', 'name'); - $query->condition('test_task.tid', '1'); - $query->orderBy($tid_field); - $query->addTag('database_test_alter_change_conditional'); - - $result = $query->execute(); - - $records = $result->fetchAll(); - - $this->assertEqual(count($records), 1, t('Returned the correct number of rows.')); - $this->assertEqual($records[0]->$name_field, 'John', t('Correct data retrieved.')); - $this->assertEqual($records[0]->$tid_field, 2, t('Correct data retrieved.')); - $this->assertEqual($records[0]->$pid_field, 1, t('Correct data retrieved.')); - $this->assertEqual($records[0]->$task_field, 'sleep', t('Correct data retrieved.')); - } - - /** - * Test that we can alter the fields of a query. - */ - function testAlterChangeFields() { - $query = db_select('test'); - $name_field = $query->addField('test', 'name'); - $age_field = $query->addField('test', 'age', 'age'); - $query->orderBy('name'); - $query->addTag('database_test_alter_change_fields'); - - $record = $query->execute()->fetch(); - $this->assertEqual($record->$name_field, 'George', t('Correct data retrieved.')); - $this->assertFalse(isset($record->$age_field), t('Age field not found, as intended.')); - } - - /** - * Test that we can alter expressions in the query. - */ - function testAlterExpression() { - $query = db_select('test'); - $name_field = $query->addField('test', 'name'); - $age_field = $query->addExpression("age*2", 'double_age'); - $query->condition('age', 27); - $query->addTag('database_test_alter_change_expressions'); - $result = $query->execute(); - - // Ensure that we got the right record. - $record = $result->fetch(); - - $this->assertEqual($record->$name_field, 'George', t('Fetched name is correct.')); - $this->assertEqual($record->$age_field, 27*3, t('Fetched age expression is correct.')); - } - - /** - * Test that we can remove a range() value from a query. This also tests hook_query_TAG_alter(). - */ - function testAlterRemoveRange() { - $query = db_select('test'); - $query->addField('test', 'name'); - $query->addField('test', 'age', 'age'); - $query->range(0, 2); - $query->addTag('database_test_alter_remove_range'); - - $num_records = count($query->execute()->fetchAll()); - - $this->assertEqual($num_records, 4, t('Returned the correct number of rows.')); - } - - /** - * Test that we can do basic alters on subqueries. - */ - function testSimpleAlterSubquery() { - // Create a sub-query with an alter tag. - $subquery = db_select('test', 'p'); - $subquery->addField('p', 'name'); - $subquery->addField('p', 'id'); - // Pick out George. - $subquery->condition('age', 27); - $subquery->addExpression("age*2", 'double_age'); - // This query alter should change it to age * 3. - $subquery->addTag('database_test_alter_change_expressions'); - - // Create a main query and join to sub-query. - $query = db_select('test_task', 'tt'); - $query->join($subquery, 'pq', 'pq.id = tt.pid'); - $age_field = $query->addField('pq', 'double_age'); - $name_field = $query->addField('pq', 'name'); - - $record = $query->execute()->fetch(); - $this->assertEqual($record->$name_field, 'George', t('Fetched name is correct.')); - $this->assertEqual($record->$age_field, 27*3, t('Fetched age expression is correct.')); - } -} - -/** - * Regression tests. - */ -class DatabaseRegressionTestCase extends DatabaseTestCase { - - public static function getInfo() { - return array( - 'name' => 'Regression tests', - 'description' => 'Regression tests cases for the database layer.', - 'group' => 'Database', - ); - } - - /** - * Regression test for #310447. - * - * Tries to insert non-ascii UTF-8 data in a database column and checks - * if its stored properly. - */ - function testRegression_310447() { - // That's a 255 character UTF-8 string. - $name = str_repeat("é", 255); - db_insert('test') - ->fields(array( - 'name' => $name, - 'age' => 20, - 'job' => 'Dancer', - ))->execute(); - - $from_database = db_query('SELECT name FROM {test} WHERE name = :name', array(':name' => $name))->fetchField(); - $this->assertIdentical($name, $from_database, t("The database handles UTF-8 characters cleanly.")); - } - - /** - * Test the db_table_exists() function. - */ - function testDBTableExists() { - $this->assertIdentical(TRUE, db_table_exists('node'), t('Returns true for existent table.')); - $this->assertIdentical(FALSE, db_table_exists('nosuchtable'), t('Returns false for nonexistent table.')); - } - - /** - * Test the db_field_exists() function. - */ - function testDBFieldExists() { - $this->assertIdentical(TRUE, db_field_exists('node', 'nid'), t('Returns true for existent column.')); - $this->assertIdentical(FALSE, db_field_exists('node', 'nosuchcolumn'), t('Returns false for nonexistent column.')); - } - - /** - * Test the db_index_exists() function. - */ - function testDBIndexExists() { - $this->assertIdentical(TRUE, db_index_exists('node', 'node_created'), t('Returns true for existent index.')); - $this->assertIdentical(FALSE, db_index_exists('node', 'nosuchindex'), t('Returns false for nonexistent index.')); - } -} - -/** - * Query logging tests. - */ -class DatabaseLoggingTestCase extends DatabaseTestCase { - - public static function getInfo() { - return array( - 'name' => 'Query logging', - 'description' => 'Test the query logging facility.', - 'group' => 'Database', - ); - } - - /** - * Test that we can log the existence of a query. - */ - function testEnableLogging() { - Database::startLog('testing'); - - db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol(); - db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchCol(); - - $queries = Database::getLog('testing', 'default'); - - $this->assertEqual(count($queries), 2, t('Correct number of queries recorded.')); - - foreach ($queries as $query) { - $this->assertEqual($query['caller']['function'], __FUNCTION__, t('Correct function in query log.')); - } - } - - /** - * Test that we can run two logs in parallel. - */ - function testEnableMultiLogging() { - Database::startLog('testing1'); - - db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol(); - - Database::startLog('testing2'); - - db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchCol(); - - $queries1 = Database::getLog('testing1'); - $queries2 = Database::getLog('testing2'); - - $this->assertEqual(count($queries1), 2, t('Correct number of queries recorded for log 1.')); - $this->assertEqual(count($queries2), 1, t('Correct number of queries recorded for log 2.')); - } - - /** - * Test that we can log queries against multiple targets on the same connection. - */ - function testEnableTargetLogging() { - // Clone the master credentials to a slave connection and to another fake - // connection. - $connection_info = Database::getConnectionInfo('default'); - Database::addConnectionInfo('default', 'slave', $connection_info['default']); - - Database::startLog('testing1'); - - db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol(); - - db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'slave'));//->fetchCol(); - - $queries1 = Database::getLog('testing1'); - - $this->assertEqual(count($queries1), 2, t('Recorded queries from all targets.')); - $this->assertEqual($queries1[0]['target'], 'default', t('First query used default target.')); - $this->assertEqual($queries1[1]['target'], 'slave', t('Second query used slave target.')); - } - - /** - * Test that logs to separate targets collapse to the same connection properly. - * - * This test is identical to the one above, except that it doesn't create - * a fake target so the query should fall back to running on the default - * target. - */ - function testEnableTargetLoggingNoTarget() { - Database::startLog('testing1'); - - db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol(); - - // We use "fake" here as a target because any non-existent target will do. - // However, because all of the tests in this class share a single page - // request there is likely to be a target of "slave" from one of the other - // unit tests, so we use a target here that we know with absolute certainty - // does not exist. - db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'fake'))->fetchCol(); - - $queries1 = Database::getLog('testing1'); - - $this->assertEqual(count($queries1), 2, t('Recorded queries from all targets.')); - $this->assertEqual($queries1[0]['target'], 'default', t('First query used default target.')); - $this->assertEqual($queries1[1]['target'], 'default', t('Second query used default target as fallback.')); - } - - /** - * Test that we can log queries separately on different connections. - */ - function testEnableMultiConnectionLogging() { - // Clone the master credentials to a fake connection. - // That both connections point to the same physical database is irrelevant. - $connection_info = Database::getConnectionInfo('default'); - Database::addConnectionInfo('test2', 'default', $connection_info['default']); - - Database::startLog('testing1'); - Database::startLog('testing1', 'test2'); - - db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol(); - - $old_key = db_set_active('test2'); - - db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'slave'))->fetchCol(); - - db_set_active($old_key); - - $queries1 = Database::getLog('testing1'); - $queries2 = Database::getLog('testing1', 'test2'); - - $this->assertEqual(count($queries1), 1, t('Correct number of queries recorded for first connection.')); - $this->assertEqual(count($queries2), 1, t('Correct number of queries recorded for second connection.')); - } -} - -/** - * Query serialization tests. - */ -class DatabaseSerializeQueryTestCase extends DatabaseTestCase { - public static function getInfo() { - return array( - 'name' => 'Serialize query', - 'description' => 'Test serializing and unserializing a query.', - 'group' => 'Database', - ); - } - - /** - * Confirm that a query can be serialized and unserialized. - */ - function testSerializeQuery() { - $query = db_select('test'); - $query->addField('test', 'age'); - $query->condition('name', 'Ringo'); - // If this doesn't work, it will throw an exception, so no need for an - // assertion. - $query = unserialize(serialize($query)); - $results = $query->execute()->fetchCol(); - $this->assertEqual($results[0], 28, t('Query properly executed after unserialization.')); - } -} - -/** - * Range query tests. - */ -class DatabaseRangeQueryTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Range query test', - 'description' => 'Test the Range query functionality.', - 'group' => 'Database', - ); - } - - function setUp() { - parent::setUp('database_test'); - } - - /** - * Confirm that range query work and return correct result. - */ - function testRangeQuery() { - // Test if return correct number of rows. - $range_rows = db_query_range("SELECT name FROM {system} ORDER BY name", 2, 3)->fetchAll(); - $this->assertEqual(count($range_rows), 3, t('Range query work and return correct number of rows.')); - - // Test if return target data. - $raw_rows = db_query('SELECT name FROM {system} ORDER BY name')->fetchAll(); - $raw_rows = array_slice($raw_rows, 2, 3); - $this->assertEqual($range_rows, $raw_rows, t('Range query work and return target data.')); - } -} - -/** - * Temporary query tests. - */ -class DatabaseTemporaryQueryTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Temporary query test', - 'description' => 'Test the temporary query functionality.', - 'group' => 'Database', - ); - } - - function setUp() { - parent::setUp('database_test'); - } - - /** - * Return the number of rows of a table. - */ - function countTableRows($table_name) { - return db_select($table_name)->countQuery()->execute()->fetchField(); - } - - /** - * Confirm that temporary tables work and are limited to one request. - */ - function testTemporaryQuery() { - $this->drupalGet('database_test/db_query_temporary'); - $data = json_decode($this->drupalGetContent()); - if ($data) { - $this->assertEqual($this->countTableRows("system"), $data->row_count, t('The temporary table contains the correct amount of rows.')); - $this->assertFalse(db_table_exists($data->table_name), t('The temporary table is, indeed, temporary.')); - } - else { - $this->fail(t("The creation of the temporary table failed.")); - } - - // Now try to run two db_query_temporary() in the same request. - $table_name_system = db_query_temporary('SELECT status FROM {system}', array()); - $table_name_users = db_query_temporary('SELECT uid FROM {users}', array()); - - $this->assertEqual($this->countTableRows($table_name_system), $this->countTableRows("system"), t('A temporary table was created successfully in this request.')); - $this->assertEqual($this->countTableRows($table_name_users), $this->countTableRows("users"), t('A second temporary table was created successfully in this request.')); - } -} - -/** - * Test how the current database driver interprets the SQL syntax. - * - * In order to ensure consistent SQL handling throughout Drupal - * across multiple kinds of database systems, we test that the - * database system interprets SQL syntax in an expected fashion. - */ -class DatabaseBasicSyntaxTestCase extends DatabaseTestCase { - public static function getInfo() { - return array( - 'name' => 'Basic SQL syntax tests', - 'description' => 'Test SQL syntax interpretation.', - 'group' => 'Database', - ); - } - - function setUp() { - parent::setUp('database_test'); - } - - /** - * Test for string concatenation. - */ - function testBasicConcat() { - $result = db_query('SELECT CONCAT(:a1, CONCAT(:a2, CONCAT(:a3, CONCAT(:a4, :a5))))', array( - ':a1' => 'This', - ':a2' => ' ', - ':a3' => 'is', - ':a4' => ' a ', - ':a5' => 'test.', - )); - $this->assertIdentical($result->fetchField(), 'This is a test.', t('Basic CONCAT works.')); - } - - /** - * Test for string concatenation with field values. - */ - function testFieldConcat() { - $result = db_query('SELECT CONCAT(:a1, CONCAT(name, CONCAT(:a2, CONCAT(age, :a3)))) FROM {test} WHERE age = :age', array( - ':a1' => 'The age of ', - ':a2' => ' is ', - ':a3' => '.', - ':age' => 25, - )); - $this->assertIdentical($result->fetchField(), 'The age of John is 25.', t('Field CONCAT works.')); - } - - /** - * Test escaping of LIKE wildcards. - */ - function testLikeEscape() { - db_insert('test') - ->fields(array( - 'name' => 'Ring_', - )) - ->execute(); - - // Match both "Ringo" and "Ring_". - $num_matches = db_select('test', 't') - ->condition('name', 'Ring_', 'LIKE') - ->countQuery() - ->execute() - ->fetchField(); - $this->assertIdentical($num_matches, '2', t('Found 2 records.')); - // Match only "Ring_" using a LIKE expression with no wildcards. - $num_matches = db_select('test', 't') - ->condition('name', db_like('Ring_'), 'LIKE') - ->countQuery() - ->execute() - ->fetchField(); - $this->assertIdentical($num_matches, '1', t('Found 1 record.')); - } - - /** - * Test LIKE query containing a backslash. - */ - function testLikeBackslash() { - db_insert('test') - ->fields(array('name')) - ->values(array( - 'name' => 'abcde\f', - )) - ->values(array( - 'name' => 'abc%\_', - )) - ->execute(); - - // Match both rows using a LIKE expression with two wildcards and a verbatim - // backslash. - $num_matches = db_select('test', 't') - ->condition('name', 'abc%\\\\_', 'LIKE') - ->countQuery() - ->execute() - ->fetchField(); - $this->assertIdentical($num_matches, '2', t('Found 2 records.')); - // Match only the former using a LIKE expression with no wildcards. - $num_matches = db_select('test', 't') - ->condition('name', db_like('abc%\_'), 'LIKE') - ->countQuery() - ->execute() - ->fetchField(); - $this->assertIdentical($num_matches, '1', t('Found 1 record.')); - } -} - -/** - * Test invalid data handling. - */ -class DatabaseInvalidDataTestCase extends DatabaseTestCase { - public static function getInfo() { - return array( - 'name' => 'Invalid data', - 'description' => 'Test handling of some invalid data.', - 'group' => 'Database', - ); - } - - function setUp() { - parent::setUp('database_test'); - } - - /** - * Traditional SQL database systems abort inserts when invalid data is encountered. - */ - function testInsertDuplicateData() { - // Try to insert multiple records where at least one has bad data. - try { - db_insert('test') - ->fields(array('name', 'age', 'job')) - ->values(array( - 'name' => 'Elvis', - 'age' => 63, - 'job' => 'Singer', - ))->values(array( - 'name' => 'John', // <-- Duplicate value on unique field. - 'age' => 17, - 'job' => 'Consultant', - )) - ->values(array( - 'name' => 'Frank', - 'age' => 75, - 'job' => 'Singer', - )) - ->execute(); - $this->fail(t('Insert succeedded when it should not have.')); - } - catch (Exception $e) { - // Check if the first record was inserted. - $name = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 63))->fetchField(); - - if ($name == 'Elvis') { - if (!Database::getConnection()->supportsTransactions()) { - // This is an expected fail. - // Database engines that don't support transactions can leave partial - // inserts in place when an error occurs. This is the case for MySQL - // when running on a MyISAM table. - $this->pass(t("The whole transaction has not been rolled-back when a duplicate key insert occurs, this is expected because the database doesn't support transactions")); - } - else { - $this->fail(t('The whole transaction is rolled back when a duplicate key insert occurs.')); - } - } - else { - $this->pass(t('The whole transaction is rolled back when a duplicate key insert occurs.')); - } - - // Ensure the other values were not inserted. - $record = db_select('test') - ->fields('test', array('name', 'age')) - ->condition('age', array(17, 75), 'IN') - ->execute()->fetchObject(); - - $this->assertFalse($record, t('The rest of the insert aborted as expected.')); - } - } - -} - -/** - * Drupal-specific SQL syntax tests. - */ -class DatabaseQueryTestCase extends DatabaseTestCase { - public static function getInfo() { - return array( - 'name' => 'Custom query syntax tests', - 'description' => 'Test Drupal\'s extended prepared statement syntax..', - 'group' => 'Database', - ); - } - - function setUp() { - parent::setUp('database_test'); - } - - /** - * Test that we can specify an array of values in the query by simply passing in an array. - */ - function testArraySubstitution() { - $names = db_query('SELECT name FROM {test} WHERE age IN (:ages) ORDER BY age', array(':ages' => array(25, 26, 27)))->fetchAll(); - - $this->assertEqual(count($names), 3, t('Correct number of names returned')); - } -} - -/** - * Test transaction support, particularly nesting. - * - * We test nesting by having two transaction layers, an outer and inner. The - * outer layer encapsulates the inner layer. Our transaction nesting abstraction - * should allow the outer layer function to call any function it wants, - * especially the inner layer that starts its own transaction, and be - * confident that, when the function it calls returns, its own transaction - * is still "alive." - * - * Call structure: - * transactionOuterLayer() - * Start transaction - * transactionInnerLayer() - * Start transaction (does nothing in database) - * [Maybe decide to roll back] - * Do more stuff - * Should still be in transaction A - * - */ -class DatabaseTransactionTestCase extends DatabaseTestCase { - - public static function getInfo() { - return array( - 'name' => 'Transaction tests', - 'description' => 'Test the transaction abstraction system.', - 'group' => 'Database', - ); - } - - /** - * Helper method for transaction unit test. This "outer layer" transaction - * starts and then encapsulates the "inner layer" transaction. This nesting - * is used to evaluate whether the the database transaction API properly - * supports nesting. By "properly supports," we mean the outer transaction - * continues to exist regardless of what functions are called and whether - * those functions start their own transactions. - * - * In contrast, a typical database would commit the outer transaction, start - * a new transaction for the inner layer, commit the inner layer transaction, - * and then be confused when the outer layer transaction tries to commit its - * transaction (which was already committed when the inner transaction - * started). - * - * @param $suffix - * Suffix to add to field values to differentiate tests. - * @param $rollback - * Whether or not to try rolling back the transaction when we're done. - * @param $ddl_statement - * Whether to execute a DDL statement during the inner transaction. - */ - protected function transactionOuterLayer($suffix, $rollback = FALSE, $ddl_statement = FALSE) { - $connection = Database::getConnection(); - $depth = $connection->transactionDepth(); - $txn = db_transaction(); - - // Insert a single row into the testing table. - db_insert('test') - ->fields(array( - 'name' => 'David' . $suffix, - 'age' => '24', - )) - ->execute(); - - $this->assertTrue($connection->inTransaction(), t('In transaction before calling nested transaction.')); - - // We're already in a transaction, but we call ->transactionInnerLayer - // to nest another transaction inside the current one. - $this->transactionInnerLayer($suffix, $rollback, $ddl_statement); - - $this->assertTrue($connection->inTransaction(), t('In transaction after calling nested transaction.')); - - if ($rollback) { - // Roll back the transaction, if requested. - // This rollback should propagate to the last savepoint. - $txn->rollback(); - $this->assertTrue(($connection->transactionDepth() == $depth), t('Transaction has rolled back to the last savepoint after calling rollback().')); - } - } - - /** - * Helper method for transaction unit tests. This "inner layer" transaction - * is either used alone or nested inside of the "outer layer" transaction. - * - * @param $suffix - * Suffix to add to field values to differentiate tests. - * @param $rollback - * Whether or not to try rolling back the transaction when we're done. - * @param $ddl_statement - * Whether to execute a DDL statement during the transaction. - */ - protected function transactionInnerLayer($suffix, $rollback = FALSE, $ddl_statement = FALSE) { - $connection = Database::getConnection(); - - $depth = $connection->transactionDepth(); - // Start a transaction. If we're being called from ->transactionOuterLayer, - // then we're already in a transaction. Normally, that would make starting - // a transaction here dangerous, but the database API handles this problem - // for us by tracking the nesting and avoiding the danger. - $txn = db_transaction(); - - $depth2 = $connection->transactionDepth(); - $this->assertTrue($depth < $depth2, t('Transaction depth is has increased with new transaction.')); - - // Insert a single row into the testing table. - db_insert('test') - ->fields(array( - 'name' => 'Daniel' . $suffix, - 'age' => '19', - )) - ->execute(); - - $this->assertTrue($connection->inTransaction(), t('In transaction inside nested transaction.')); - - if ($ddl_statement) { - $table = array( - 'fields' => array( - 'id' => array( - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - ), - 'primary key' => array('id'), - ); - db_create_table('database_test_1', $table); - - $this->assertTrue($connection->inTransaction(), t('In transaction inside nested transaction.')); - } - - if ($rollback) { - // Roll back the transaction, if requested. - // This rollback should propagate to the last savepoint. - $txn->rollback(); - $this->assertTrue(($connection->transactionDepth() == $depth), t('Transaction has rolled back to the last savepoint after calling rollback().')); - } - } - - /** - * Test transaction rollback on a database that supports transactions. - * - * If the active connection does not support transactions, this test does nothing. - */ - function testTransactionRollBackSupported() { - // This test won't work right if transactions are not supported. - if (!Database::getConnection()->supportsTransactions()) { - return; - } - try { - // Create two nested transactions. Roll back from the inner one. - $this->transactionOuterLayer('B', TRUE); - - // Neither of the rows we inserted in the two transaction layers - // should be present in the tables post-rollback. - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidB'))->fetchField(); - $this->assertNotIdentical($saved_age, '24', t('Cannot retrieve DavidB row after commit.')); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielB'))->fetchField(); - $this->assertNotIdentical($saved_age, '19', t('Cannot retrieve DanielB row after commit.')); - } - catch (Exception $e) { - $this->fail($e->getMessage()); - } - } - - /** - * Test transaction rollback on a database that does not support transactions. - * - * If the active driver supports transactions, this test does nothing. - */ - function testTransactionRollBackNotSupported() { - // This test won't work right if transactions are supported. - if (Database::getConnection()->supportsTransactions()) { - return; - } - try { - // Create two nested transactions. Attempt to roll back from the inner one. - $this->transactionOuterLayer('B', TRUE); - - // Because our current database claims to not support transactions, - // the inserted rows should be present despite the attempt to roll back. - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidB'))->fetchField(); - $this->assertIdentical($saved_age, '24', t('DavidB not rolled back, since transactions are not supported.')); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielB'))->fetchField(); - $this->assertIdentical($saved_age, '19', t('DanielB not rolled back, since transactions are not supported.')); - } - catch (Exception $e) { - $this->fail($e->getMessage()); - } - } - - /** - * Test committed transaction. - * - * The behavior of this test should be identical for connections that support - * transactions and those that do not. - */ - function testCommittedTransaction() { - try { - // Create two nested transactions. The changes should be committed. - $this->transactionOuterLayer('A'); - - // Because we committed, both of the inserted rows should be present. - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidA'))->fetchField(); - $this->assertIdentical($saved_age, '24', t('Can retrieve DavidA row after commit.')); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielA'))->fetchField(); - $this->assertIdentical($saved_age, '19', t('Can retrieve DanielA row after commit.')); - } - catch (Exception $e) { - $this->fail($e->getMessage()); - } - } - - /** - * Test the compatibility of transactions with DDL statements. - */ - function testTransactionWithDdlStatement() { - // First, test that a commit works normally, even with DDL statements. - try { - $this->transactionOuterLayer('D', FALSE, TRUE); - - // Because we committed, the inserted rows should both be present. - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidD'))->fetchField(); - $this->assertIdentical($saved_age, '24', t('Can retrieve DavidD row after commit.')); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielD'))->fetchField(); - $this->assertIdentical($saved_age, '19', t('Can retrieve DanielD row after commit.')); - // The created table should also exist. - $count = db_query('SELECT COUNT(id) FROM {database_test_1}')->fetchField(); - $this->assertIdentical($count, '0', t('Table was successfully created inside a transaction.')); - } - catch (Exception $e) { - $this->fail((string) $e); - } - - // If we rollback the transaction, an exception might be thrown. - try { - $this->transactionOuterLayer('E', TRUE, TRUE); - - // Because we rolled back, the inserted rows shouldn't be present. - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidE'))->fetchField(); - $this->assertNotIdentical($saved_age, '24', t('Cannot retrieve DavidE row after rollback.')); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielE'))->fetchField(); - $this->assertNotIdentical($saved_age, '19', t('Cannot retrieve DanielE row after rollback.')); - } - catch (Exception $e) { - // An exception also lets the test pass. - $this->assertTrue(true, t('Exception thrown on rollback after a DDL statement was executed.')); - } - } - - /** - * Insert a single row into the testing table. - */ - protected function insertRow($name) { - db_insert('test') - ->fields(array( - 'name' => $name, - )) - ->execute(); - } - - /** - * Start over for a new test. - */ - protected function cleanUp() { - db_truncate('test') - ->execute(); - } - - /** - * Assert that a given row is present in the test table. - * - * @param $name - * The name of the row. - * @param $message - * The message to log for the assertion. - */ - function assertRowPresent($name, $message = NULL) { - if (!isset($message)) { - $message = t('Row %name is present.', array('%name' => $name)); - } - $present = (boolean) db_query('SELECT 1 FROM {test} WHERE name = :name', array(':name' => $name))->fetchField(); - return $this->assertTrue($present, $message); - } - - /** - * Assert that a given row is absent from the test table. - * - * @param $name - * The name of the row. - * @param $message - * The message to log for the assertion. - */ - function assertRowAbsent($name, $message = NULL) { - if (!isset($message)) { - $message = t('Row %name is absent.', array('%name' => $name)); - } - $present = (boolean) db_query('SELECT 1 FROM {test} WHERE name = :name', array(':name' => $name))->fetchField(); - return $this->assertFalse($present, $message); - } - - /** - * Test transaction stacking and commit / rollback. - */ - function testTransactionStacking() { - // This test won't work right if transactions are supported. - if (Database::getConnection()->supportsTransactions()) { - return; - } - - $database = Database::getConnection(); - - // Standard case: pop the inner transaction before the outer transaction. - $transaction = db_transaction(); - $this->insertRow('outer'); - $transaction2 = db_transaction(); - $this->insertRow('inner'); - // Pop the inner transaction. - unset($transaction2); - $this->assertTrue($database->inTransaction(), t('Still in a transaction after popping the inner transaction')); - // Pop the outer transaction. - unset($transaction); - $this->assertFalse($database->inTransaction(), t('Transaction closed after popping the outer transaction')); - $this->assertRowPresent('outer'); - $this->assertRowPresent('inner'); - - // Pop the transaction in a different order they have been pushed. - $this->cleanUp(); - $transaction = db_transaction(); - $this->insertRow('outer'); - $transaction2 = db_transaction(); - $this->insertRow('inner'); - // Pop the outer transaction, nothing should happen. - unset($transaction); - $this->insertRow('inner-after-outer-commit'); - $this->assertTrue($database->inTransaction(), t('Still in a transaction after popping the outer transaction')); - // Pop the inner transaction, the whole transaction should commit. - unset($transaction2); - $this->assertFalse($database->inTransaction(), t('Transaction closed after popping the inner transaction')); - $this->assertRowPresent('outer'); - $this->assertRowPresent('inner'); - $this->assertRowPresent('inner-after-outer-commit'); - - // Rollback the inner transaction. - $this->cleanUp(); - $transaction = db_transaction(); - $this->insertRow('outer'); - $transaction2 = db_transaction(); - $this->insertRow('inner'); - // Now rollback the inner transaction. - $transaction2->rollback(); - unset($transaction2); - $this->assertTrue($database->inTransaction(), t('Still in a transaction after popping the outer transaction')); - // Pop the outer transaction, it should commit. - $this->insertRow('outer-after-inner-rollback'); - unset($transaction); - $this->assertFalse($database->inTransaction(), t('Transaction closed after popping the inner transaction')); - $this->assertRowPresent('outer'); - $this->assertRowAbsent('inner'); - $this->assertRowPresent('outer-after-inner-rollback'); - - // Rollback the inner transaction after committing the outer one. - $this->cleanUp(); - $transaction = db_transaction(); - $this->insertRow('outer'); - $transaction2 = db_transaction(); - $this->insertRow('inner'); - // Pop the outer transaction, nothing should happen. - unset($transaction); - $this->assertTrue($database->inTransaction(), t('Still in a transaction after popping the outer transaction')); - // Now rollback the inner transaction, it should rollback. - $transaction2->rollback(); - unset($transaction2); - $this->assertFalse($database->inTransaction(), t('Transaction closed after popping the inner transaction')); - $this->assertRowPresent('outer'); - $this->assertRowAbsent('inner'); - - // Rollback the outer transaction while the inner transaction is active. - // In that case, an exception will be triggered because we cannot - // ensure that the final result will have any meaning. - $this->cleanUp(); - $transaction = db_transaction(); - $this->insertRow('outer'); - $transaction2 = db_transaction(); - $this->insertRow('inner'); - // Rollback the outer transaction. - try { - $transaction->rollback(); - unset($transaction); - $this->fail(t('Rolling back the outer transaction while the inner transaction is active resulted in an exception.')); - } - catch (Exception $e) { - $this->pass(t('Rolling back the outer transaction while the inner transaction is active resulted in an exception.')); - } - $this->assertFalse($database->inTransaction(), t('No more in a transaction after rolling back the outer transaction')); - // Try to commit the inner transaction. - try { - unset($transaction2); - $this->fail(t('Trying to commit the inner transaction resulted in an exception.')); - } - catch (Exception $e) { - $this->pass(t('Trying to commit the inner transaction resulted in an exception.')); - } - $this->assertRowAbsent('outer'); - $this->assertRowAbsent('inner'); - } -} - - -/** - * Check the sequences API. - */ -class DatabaseNextIdCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => t('Sequences API'), - 'description' => t('Test the secondary sequences API.'), - 'group' => t('Database'), - ); - } - - /** - * Test that the sequences API work. - */ - function testDbNextId() { - $first = db_next_id(); - $second = db_next_id(); - // We can test for exact increase in here because we know there is no - // other process operating on these tables -- normally we could only - // expect $second > $first. - $this->assertEqual($first + 1, $second, t('The second call from a sequence provides a number increased by one.')); - $result = db_next_id(1000); - $this->assertEqual($result, 1001, t('Sequence provides a larger number than the existing ID.')); - } -} - -/** - * Tests the empty pseudo-statement class. - */ -class DatabaseEmptyStatementTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => t('Empty statement'), - 'description' => t('Test the empty pseudo-statement class.'), - 'group' => t('Database'), - ); - } - - /** - * Test that the empty result set behaves as empty. - */ - function testEmpty() { - $result = new DatabaseStatementEmpty(); - - $this->assertTrue($result instanceof DatabaseStatementInterface, t('Class implements expected interface')); - $this->assertNull($result->fetchObject(), t('Null result returned.')); - } - - /** - * Test that the empty result set iterates safely. - */ - function testEmptyIteration() { - $result = new DatabaseStatementEmpty(); - - foreach ($result as $record) { - $this->fail(t('Iterating empty result set should not iterate.')); - return; - } - - $this->pass(t('Iterating empty result set skipped iteration.')); - } - - /** - * Test that the empty result set mass-fetches in an expected way. - */ - function testEmptyFetchAll() { - $result = new DatabaseStatementEmpty(); - - $this->assertEqual($result->fetchAll(), array(), t('Empty array returned from empty result set.')); - } -} diff --git a/modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info b/modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info deleted file mode 100644 index 53515e769e3..00000000000 --- a/modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info +++ /dev/null @@ -1,6 +0,0 @@ -name = "Drupal system listing compatible test" -description = "Support module for testing the drupal_system_listing function." -package = Testing -version = VERSION -core = 8.x -hidden = TRUE diff --git a/modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.module b/modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.module deleted file mode 100644 index b3d9bbc7f37..00000000000 --- a/modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.module +++ /dev/null @@ -1 +0,0 @@ -<?php diff --git a/modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info b/modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info deleted file mode 100644 index 8753edaab9f..00000000000 --- a/modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info +++ /dev/null @@ -1,6 +0,0 @@ -name = "Drupal system listing incompatible test" -description = "Support module for testing the drupal_system_listing function." -package = Testing -version = VERSION -core = 8.x -hidden = TRUE diff --git a/modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.module b/modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.module deleted file mode 100644 index b3d9bbc7f37..00000000000 --- a/modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.module +++ /dev/null @@ -1 +0,0 @@ -<?php diff --git a/modules/simpletest/tests/error.test b/modules/simpletest/tests/error.test deleted file mode 100644 index b1ec4b3f5ca..00000000000 --- a/modules/simpletest/tests/error.test +++ /dev/null @@ -1,116 +0,0 @@ -<?php - -/** - * Tests Drupal error and exception handlers. - */ -class DrupalErrorHandlerUnitTest extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Drupal error handlers', - 'description' => 'Performs tests on the Drupal error and exception handler.', - 'group' => 'System', - ); - } - - function setUp() { - parent::setUp('error_test'); - } - - /** - * Test the error handler. - */ - function testErrorHandler() { - $error_notice = array( - '%type' => 'Notice', - '!message' => 'Undefined variable: bananas', - '%function' => 'error_test_generate_warnings()', - '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'), - ); - $error_warning = array( - '%type' => 'Warning', - '!message' => 'Division by zero', - '%function' => 'error_test_generate_warnings()', - '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'), - ); - $error_user_notice = array( - '%type' => 'User warning', - '!message' => 'Drupal is awesome', - '%function' => 'error_test_generate_warnings()', - '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'), - ); - - // Set error reporting to collect notices. - variable_set('error_level', ERROR_REPORTING_DISPLAY_ALL); - $this->drupalGet('error-test/generate-warnings'); - $this->assertResponse(200, t('Received expected HTTP status code.')); - $this->assertErrorMessage($error_notice); - $this->assertErrorMessage($error_warning); - $this->assertErrorMessage($error_user_notice); - - // Set error reporting to not collect notices. - variable_set('error_level', ERROR_REPORTING_DISPLAY_SOME); - $this->drupalGet('error-test/generate-warnings'); - $this->assertResponse(200, t('Received expected HTTP status code.')); - $this->assertNoErrorMessage($error_notice); - $this->assertErrorMessage($error_warning); - $this->assertErrorMessage($error_user_notice); - - // Set error reporting to not show any errors. - variable_set('error_level', ERROR_REPORTING_HIDE); - $this->drupalGet('error-test/generate-warnings'); - $this->assertResponse(200, t('Received expected HTTP status code.')); - $this->assertNoErrorMessage($error_notice); - $this->assertNoErrorMessage($error_warning); - $this->assertNoErrorMessage($error_user_notice); - } - - /** - * Test the exception handler. - */ - function testExceptionHandler() { - $error_exception = array( - '%type' => 'Exception', - '!message' => 'Drupal is awesome', - '%function' => 'error_test_trigger_exception()', - '%line' => 57, - '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'), - ); - $error_pdo_exception = array( - '%type' => 'PDOException', - '!message' => 'SELECT * FROM bananas_are_awesome', - '%function' => 'error_test_trigger_pdo_exception()', - '%line' => 65, - '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'), - ); - - $this->drupalGet('error-test/trigger-exception'); - $this->assertTrue(strpos($this->drupalGetHeader(':status'), '500 Service unavailable (with message)'), t('Received expected HTTP status line.')); - $this->assertErrorMessage($error_exception); - - $this->drupalGet('error-test/trigger-pdo-exception'); - $this->assertTrue(strpos($this->drupalGetHeader(':status'), '500 Service unavailable (with message)'), t('Received expected HTTP status line.')); - // We cannot use assertErrorMessage() since the extact error reported - // varies from database to database. Check that the SQL string is displayed. - $this->assertText($error_pdo_exception['%type'], t('Found %type in error page.', $error_pdo_exception)); - $this->assertText($error_pdo_exception['!message'], t('Found !message in error page.', $error_pdo_exception)); - $error_details = t('in %function (line ', $error_pdo_exception); - $this->assertRaw($error_details, t("Found '!message' in error page.", array('!message' => $error_details))); - } - - /** - * Helper function: assert that the error message is found. - */ - function assertErrorMessage(array $error) { - $message = t('%type: !message in %function (line ', $error); - $this->assertRaw($message, t('Found error message: !message.', array('!message' => $message))); - } - - /** - * Helper function: assert that the error message is not found. - */ - function assertNoErrorMessage(array $error) { - $message = t('%type: !message in %function (line ', $error); - $this->assertNoRaw($message, t('Did not find error message: !message.', array('!message' => $message))); - } -} - diff --git a/modules/simpletest/tests/error_test.info b/modules/simpletest/tests/error_test.info deleted file mode 100644 index d5db3ee392f..00000000000 --- a/modules/simpletest/tests/error_test.info +++ /dev/null @@ -1,6 +0,0 @@ -name = "Error test" -description = "Support module for error and exception testing." -package = Testing -version = VERSION -core = 8.x -hidden = TRUE diff --git a/modules/simpletest/tests/error_test.module b/modules/simpletest/tests/error_test.module deleted file mode 100644 index d062cb067c4..00000000000 --- a/modules/simpletest/tests/error_test.module +++ /dev/null @@ -1,65 +0,0 @@ -<?php - -/** - * Implements hook_menu(). - */ -function error_test_menu() { - $items['error-test/generate-warnings'] = array( - 'title' => 'Generate warnings', - 'page callback' => 'error_test_generate_warnings', - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); - $items['error-test/generate-warnings-with-report'] = array( - 'title' => 'Generate warnings with Simpletest reporting', - 'page callback' => 'error_test_generate_warnings', - 'page arguments' => array(TRUE), - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); - $items['error-test/trigger-exception'] = array( - 'title' => 'Trigger an exception', - 'page callback' => 'error_test_trigger_exception', - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); - $items['error-test/trigger-pdo-exception'] = array( - 'title' => 'Trigger a PDO exception', - 'page callback' => 'error_test_trigger_pdo_exception', - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); - - return $items; -} - -/** - * Menu callback; generate warnings to test the error handler. - */ -function error_test_generate_warnings($collect_errors = FALSE) { - // Tell Drupal error reporter to send errors to Simpletest or not. - define('SIMPLETEST_COLLECT_ERRORS', $collect_errors); - // This will generate a notice. - $monkey_love = $bananas; - // This will generate a warning. - $awesomely_big = 1/0; - // This will generate a user error. - trigger_error("Drupal is awesome", E_USER_WARNING); - return ""; -} - -/** - * Menu callback; trigger an exception to test the exception handler. - */ -function error_test_trigger_exception() { - define('SIMPLETEST_COLLECT_ERRORS', FALSE); - throw new Exception("Drupal is awesome"); -} - -/** - * Menu callback; trigger an exception to test the exception handler. - */ -function error_test_trigger_pdo_exception() { - define('SIMPLETEST_COLLECT_ERRORS', FALSE); - db_query('SELECT * FROM bananas_are_awesome'); -} diff --git a/modules/simpletest/tests/file.test b/modules/simpletest/tests/file.test deleted file mode 100644 index 55e3b0aa33b..00000000000 --- a/modules/simpletest/tests/file.test +++ /dev/null @@ -1,2748 +0,0 @@ -<?php - -/** - * @file - * This provides SimpleTests for the core file handling functionality. - * These include FileValidateTest and FileSaveTest. - */ - -/** - * Helper validator that returns the $errors parameter. - */ -function file_test_validator($file, $errors) { - return $errors; -} - -/** - * Helper function for testing file_scan_directory(). - * - * Each time the function is called the file is stored in a static variable. - * When the function is called with no $filepath parameter, the results are - * returned. - * - * @param $filepath - * File path - * @return - * If $filepath is NULL, an array of all previous $filepath parameters - */ -function file_test_file_scan_callback($filepath = NULL) { - $files = &drupal_static(__FUNCTION__, array()); - if (isset($filepath)) { - $files[] = $filepath; - } - else { - return $files; - } -} - -/** - * Reset static variables used by file_test_file_scan_callback(). - */ -function file_test_file_scan_callback_reset() { - drupal_static_reset('file_test_file_scan_callback'); -} - -/** - * Base class for file tests that adds some additional file specific - * assertions and helper functions. - */ -class FileTestCase extends DrupalWebTestCase { - /** - * Check that two files have the same values for all fields other than the - * timestamp. - * - * @param $before - * File object to compare. - * @param $after - * File object to compare. - */ - function assertFileUnchanged($before, $after) { - $this->assertEqual($before->fid, $after->fid, t('File id is the same: %file1 == %file2.', array('%file1' => $before->fid, '%file2' => $after->fid)), 'File unchanged'); - $this->assertEqual($before->uid, $after->uid, t('File owner is the same: %file1 == %file2.', array('%file1' => $before->uid, '%file2' => $after->uid)), 'File unchanged'); - $this->assertEqual($before->filename, $after->filename, t('File name is the same: %file1 == %file2.', array('%file1' => $before->filename, '%file2' => $after->filename)), 'File unchanged'); - $this->assertEqual($before->uri, $after->uri, t('File path is the same: %file1 == %file2.', array('%file1' => $before->uri, '%file2' => $after->uri)), 'File unchanged'); - $this->assertEqual($before->filemime, $after->filemime, t('File MIME type is the same: %file1 == %file2.', array('%file1' => $before->filemime, '%file2' => $after->filemime)), 'File unchanged'); - $this->assertEqual($before->filesize, $after->filesize, t('File size is the same: %file1 == %file2.', array('%file1' => $before->filesize, '%file2' => $after->filesize)), 'File unchanged'); - $this->assertEqual($before->status, $after->status, t('File status is the same: %file1 == %file2.', array('%file1' => $before->status, '%file2' => $after->status)), 'File unchanged'); - } - - /** - * Check that two files are not the same by comparing the fid and filepath. - * - * @param $file1 - * File object to compare. - * @param $file2 - * File object to compare. - */ - function assertDifferentFile($file1, $file2) { - $this->assertNotEqual($file1->fid, $file2->fid, t('Files have different ids: %file1 != %file2.', array('%file1' => $file1->fid, '%file2' => $file2->fid)), 'Different file'); - $this->assertNotEqual($file1->uri, $file2->uri, t('Files have different paths: %file1 != %file2.', array('%file1' => $file1->uri, '%file2' => $file2->uri)), 'Different file'); - } - - /** - * Check that two files are the same by comparing the fid and filepath. - * - * @param $file1 - * File object to compare. - * @param $file2 - * File object to compare. - */ - function assertSameFile($file1, $file2) { - $this->assertEqual($file1->fid, $file2->fid, t('Files have the same ids: %file1 == %file2.', array('%file1' => $file1->fid, '%file2-fid' => $file2->fid)), 'Same file'); - $this->assertEqual($file1->uri, $file2->uri, t('Files have the same path: %file1 == %file2.', array('%file1' => $file1->uri, '%file2' => $file2->uri)), 'Same file'); - } - - /** - * Helper function to test the permissions of a file. - * - * @param $filepath - * String file path. - * @param $expected_mode - * Octal integer like 0664 or 0777. - * @param $message - * Optional message. - */ - function assertFilePermissions($filepath, $expected_mode, $message = NULL) { - // Clear out PHP's file stat cache to be sure we see the current value. - clearstatcache(); - - // Mask out all but the last three octets. - $actual_mode = fileperms($filepath) & 0777; - - // PHP on Windows has limited support for file permissions. Usually each of - // "user", "group" and "other" use one octal digit (3 bits) to represent the - // read/write/execute bits. On Windows, chmod() ignores the "group" and - // "other" bits, and fileperms() returns the "user" bits in all three - // positions. $expected_mode is updated to reflect this. - if (substr(PHP_OS, 0, 3) == 'WIN') { - // Reset the "group" and "other" bits. - $expected_mode = $expected_mode & 0700; - // Shift the "user" bits to the "group" and "other" positions also. - $expected_mode = $expected_mode | $expected_mode >> 3 | $expected_mode >> 6; - } - - if (!isset($message)) { - $message = t('Expected file permission to be %expected, actually were %actual.', array('%actual' => decoct($actual_mode), '%expected' => decoct($expected_mode))); - } - $this->assertEqual($actual_mode, $expected_mode, $message); - } - - /** - * Helper function to test the permissions of a directory. - * - * @param $directory - * String directory path. - * @param $expected_mode - * Octal integer like 0664 or 0777. - * @param $message - * Optional message. - */ - function assertDirectoryPermissions($directory, $expected_mode, $message = NULL) { - // Clear out PHP's file stat cache to be sure we see the current value. - clearstatcache(); - - // Mask out all but the last three octets. - $actual_mode = fileperms($directory) & 0777; - - // PHP on Windows has limited support for file permissions. Usually each of - // "user", "group" and "other" use one octal digit (3 bits) to represent the - // read/write/execute bits. On Windows, chmod() ignores the "group" and - // "other" bits, and fileperms() returns the "user" bits in all three - // positions. $expected_mode is updated to reflect this. - if (substr(PHP_OS, 0, 3) == 'WIN') { - // Reset the "group" and "other" bits. - $expected_mode = $expected_mode & 0700; - // Shift the "user" bits to the "group" and "other" positions also. - $expected_mode = $expected_mode | $expected_mode >> 3 | $expected_mode >> 6; - } - - if (!isset($message)) { - $message = t('Expected directory permission to be %expected, actually were %actual.', array('%actual' => decoct($actual_mode), '%expected' => decoct($expected_mode))); - } - $this->assertEqual($actual_mode, $expected_mode, $message); - } - - /** - * Create a directory and assert it exists. - * - * @param $path - * Optional string with a directory path. If none is provided, a random - * name in the site's files directory will be used. - * @return - * The path to the directory. - */ - function createDirectory($path = NULL) { - // A directory to operate on. - if (!isset($path)) { - $path = file_default_scheme() . '://' . $this->randomName(); - } - $this->assertTrue(drupal_mkdir($path) && is_dir($path), t('Directory was created successfully.')); - return $path; - } - - /** - * Create a file and save it to the files table and assert that it occurs - * correctly. - * - * @param $filepath - * Optional string specifying the file path. If none is provided then a - * randomly named file will be created in the site's files directory. - * @param $contents - * Optional contents to save into the file. If a NULL value is provided an - * arbitrary string will be used. - * @param $scheme - * Optional string indicating the stream scheme to use. Drupal core includes - * public, private, and temporary. The public wrapper is the default. - * @return - * File object. - */ - function createFile($filepath = NULL, $contents = NULL, $scheme = NULL) { - if (!isset($filepath)) { - $filepath = $this->randomName(); - } - if (!isset($scheme)) { - $scheme = file_default_scheme(); - } - $filepath = $scheme . '://' . $filepath; - - if (!isset($contents)) { - $contents = "file_put_contents() doesn't seem to appreciate empty strings so let's put in some data."; - } - - file_put_contents($filepath, $contents); - $this->assertTrue(is_file($filepath), t('The test file exists on the disk.'), 'Create test file'); - - $file = new stdClass(); - $file->uri = $filepath; - $file->filename = basename($file->uri); - $file->filemime = 'text/plain'; - $file->uid = 1; - $file->timestamp = REQUEST_TIME; - $file->filesize = filesize($file->uri); - $file->status = 0; - // Write the record directly rather than calling file_save() so we don't - // invoke the hooks. - $this->assertNotIdentical(drupal_write_record('file_managed', $file), FALSE, t('The file was added to the database.'), 'Create test file'); - - return $file; - } -} - -/** - * Base class for file tests that use the file_test module to test uploads and - * hooks. - */ -class FileHookTestCase extends FileTestCase { - function setUp() { - // Install file_test module - parent::setUp('file_test'); - // Clear out any hook calls. - file_test_reset(); - } - - /** - * Assert that all of the specified hook_file_* hooks were called once, other - * values result in failure. - * - * @param $expected - * Array with string containing with the hook name, e.g. 'load', 'save', - * 'insert', etc. - */ - function assertFileHooksCalled($expected) { - // Determine which hooks were called. - $actual = array_keys(array_filter(file_test_get_all_calls())); - - // Determine if there were any expected that were not called. - $uncalled = array_diff($expected, $actual); - if (count($uncalled)) { - $this->assertTrue(FALSE, t('Expected hooks %expected to be called but %uncalled was not called.', array('%expected' => implode(', ', $expected), '%uncalled' => implode(', ', $uncalled)))); - } - else { - $this->assertTrue(TRUE, t('All the expected hooks were called: %expected', array('%expected' => empty($expected) ? t('(none)') : implode(', ', $expected)))); - } - - // Determine if there were any unexpected calls. - $unexpected = array_diff($actual, $expected); - if (count($unexpected)) { - $this->assertTrue(FALSE, t('Unexpected hooks were called: %unexpected.', array('%unexpected' => empty($unexpected) ? t('(none)') : implode(', ', $unexpected)))); - } - else { - $this->assertTrue(TRUE, t('No unexpected hooks were called.')); - } - } - - /** - * Assert that a hook_file_* hook was called a certain number of times. - * - * @param $hook - * String with the hook name, e.g. 'load', 'save', 'insert', etc. - * @param $expected_count - * Optional integer count. - * @param $message - * Optional translated string message. - */ - function assertFileHookCalled($hook, $expected_count = 1, $message = NULL) { - $actual_count = count(file_test_get_calls($hook)); - - if (!isset($message)) { - if ($actual_count == $expected_count) { - $message = t('hook_file_@name was called correctly.', array('@name' => $hook)); - } - elseif ($expected_count == 0) { - $message = format_plural($actual_count, 'hook_file_@name was not expected to be called but was actually called once.', 'hook_file_@name was not expected to be called but was actually called @count times.', array('@name' => $hook, '@count' => $actual_count)); - } - else { - $message = t('hook_file_@name was expected to be called %expected times but was called %actual times.', array('@name' => $hook, '%expected' => $expected_count, '%actual' => $actual_count)); - } - } - $this->assertEqual($actual_count, $expected_count, $message); - } -} - - -/** - * This will run tests against the file_space_used() function. - */ -class FileSpaceUsedTest extends FileTestCase { - public static function getInfo() { - return array( - 'name' => 'File space used tests', - 'description' => 'Tests the file_space_used() function.', - 'group' => 'File API', - ); - } - - function setUp() { - parent::setUp(); - - // Create records for a couple of users with different sizes. - $file = array('uid' => 2, 'uri' => 'public://example1.txt', 'filesize' => 50, 'status' => FILE_STATUS_PERMANENT); - drupal_write_record('file_managed', $file); - $file = array('uid' => 2, 'uri' => 'public://example2.txt', 'filesize' => 20, 'status' => FILE_STATUS_PERMANENT); - drupal_write_record('file_managed', $file); - $file = array('uid' => 3, 'uri' => 'public://example3.txt', 'filesize' => 100, 'status' => FILE_STATUS_PERMANENT); - drupal_write_record('file_managed', $file); - $file = array('uid' => 3, 'uri' => 'public://example4.txt', 'filesize' => 200, 'status' => FILE_STATUS_PERMANENT); - drupal_write_record('file_managed', $file); - - // Now create some non-permanent files. - $file = array('uid' => 2, 'uri' => 'public://example5.txt', 'filesize' => 1, 'status' => 0); - drupal_write_record('file_managed', $file); - $file = array('uid' => 3, 'uri' => 'public://example6.txt', 'filesize' => 3, 'status' => 0); - drupal_write_record('file_managed', $file); - } - - /** - * Test different users with the default status. - */ - function testFileSpaceUsed() { - // Test different users with default status. - $this->assertEqual(file_space_used(2), 70); - $this->assertEqual(file_space_used(3), 300); - $this->assertEqual(file_space_used(), 370); - - // Test the status fields - $this->assertEqual(file_space_used(NULL, 0), 4); - $this->assertEqual(file_space_used(NULL, FILE_STATUS_PERMANENT), 370); - - // Test both the user and status. - $this->assertEqual(file_space_used(1, 0), 0); - $this->assertEqual(file_space_used(1, FILE_STATUS_PERMANENT), 0); - $this->assertEqual(file_space_used(2, 0), 1); - $this->assertEqual(file_space_used(2, FILE_STATUS_PERMANENT), 70); - $this->assertEqual(file_space_used(3, 0), 3); - $this->assertEqual(file_space_used(3, FILE_STATUS_PERMANENT), 300); - } -} - -/** - * This will run tests against the file validation functions (file_validate_*). - */ -class FileValidatorTest extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'File validator tests', - 'description' => 'Tests the functions used to validate uploaded files.', - 'group' => 'File API', - ); - } - - function setUp() { - parent::setUp(); - - $this->image = new stdClass(); - $this->image->uri = 'misc/druplicon.png'; - $this->image->filename = basename($this->image->uri); - - $this->non_image = new stdClass(); - $this->non_image->uri = 'misc/jquery.js'; - $this->non_image->filename = basename($this->non_image->uri); - } - - /** - * Test the file_validate_extensions() function. - */ - function testFileValidateExtensions() { - $file = new stdClass(); - $file->filename = 'asdf.txt'; - $errors = file_validate_extensions($file, 'asdf txt pork'); - $this->assertEqual(count($errors), 0, t('Valid extension accepted.'), 'File'); - - $file->filename = 'asdf.txt'; - $errors = file_validate_extensions($file, 'exe png'); - $this->assertEqual(count($errors), 1, t('Invalid extension blocked.'), 'File'); - } - - /** - * This ensures a specific file is actually an image. - */ - function testFileValidateIsImage() { - $this->assertTrue(file_exists($this->image->uri), t('The image being tested exists.'), 'File'); - $errors = file_validate_is_image($this->image); - $this->assertEqual(count($errors), 0, t('No error reported for our image file.'), 'File'); - - $this->assertTrue(file_exists($this->non_image->uri), t('The non-image being tested exists.'), 'File'); - $errors = file_validate_is_image($this->non_image); - $this->assertEqual(count($errors), 1, t('An error reported for our non-image file.'), 'File'); - } - - /** - * This ensures the resolution of a specific file is within bounds. - * The image will be resized if it's too large. - */ - function testFileValidateImageResolution() { - // Non-images. - $errors = file_validate_image_resolution($this->non_image); - $this->assertEqual(count($errors), 0, t("Shouldn't get any errors for a non-image file."), 'File'); - $errors = file_validate_image_resolution($this->non_image, '50x50', '100x100'); - $this->assertEqual(count($errors), 0, t("Don't check the resolution on non files."), 'File'); - - // Minimum size. - $errors = file_validate_image_resolution($this->image); - $this->assertEqual(count($errors), 0, t('No errors for an image when there is no minimum or maximum resolution.'), 'File'); - $errors = file_validate_image_resolution($this->image, 0, '200x1'); - $this->assertEqual(count($errors), 1, t("Got an error for an image that wasn't wide enough."), 'File'); - $errors = file_validate_image_resolution($this->image, 0, '1x200'); - $this->assertEqual(count($errors), 1, t("Got an error for an image that wasn't tall enough."), 'File'); - $errors = file_validate_image_resolution($this->image, 0, '200x200'); - $this->assertEqual(count($errors), 1, t('Small images report an error.'), 'File'); - - // Maximum size. - if (image_get_toolkit()) { - // Copy the image so that the original doesn't get resized. - copy('misc/druplicon.png', 'temporary://druplicon.png'); - $this->image->uri = 'temporary://druplicon.png'; - - $errors = file_validate_image_resolution($this->image, '10x5'); - $this->assertEqual(count($errors), 0, t('No errors should be reported when an oversized image can be scaled down.'), 'File'); - - $info = image_get_info($this->image->uri); - $this->assertTrue($info['width'] <= 10, t('Image scaled to correct width.'), 'File'); - $this->assertTrue($info['height'] <= 5, t('Image scaled to correct height.'), 'File'); - - drupal_unlink('temporary://druplicon.png'); - } - else { - // TODO: should check that the error is returned if no toolkit is available. - $errors = file_validate_image_resolution($this->image, '5x10'); - $this->assertEqual(count($errors), 1, t("Oversize images that can't be scaled get an error."), 'File'); - } - } - - /** - * This will ensure the filename length is valid. - */ - function testFileValidateNameLength() { - // Create a new file object. - $file = new stdClass(); - - // Add a filename with an allowed length and test it. - $file->filename = str_repeat('x', 240); - $this->assertEqual(strlen($file->filename), 240); - $errors = file_validate_name_length($file); - $this->assertEqual(count($errors), 0, t('No errors reported for 240 length filename.'), 'File'); - - // Add a filename with a length too long and test it. - $file->filename = str_repeat('x', 241); - $errors = file_validate_name_length($file); - $this->assertEqual(count($errors), 1, t('An error reported for 241 length filename.'), 'File'); - - // Add a filename with an empty string and test it. - $file->filename = ''; - $errors = file_validate_name_length($file); - $this->assertEqual(count($errors), 1, t('An error reported for 0 length filename.'), 'File'); - } - - - /** - * Test file_validate_size(). - */ - function testFileValidateSize() { - global $user; - $original_user = $user; - drupal_save_session(FALSE); - - // Run these test as uid = 1. - $user = user_load(1); - - $file = new stdClass(); - $file->filesize = 999999; - $errors = file_validate_size($file, 1, 1); - $this->assertEqual(count($errors), 0, t('No size limits enforced on uid=1.'), 'File'); - - // Run these tests as a regular user. - $user = $this->drupalCreateUser(); - - // Create a file with a size of 1000 bytes, and quotas of only 1 byte. - $file = new stdClass(); - $file->filesize = 1000; - $errors = file_validate_size($file, 0, 0); - $this->assertEqual(count($errors), 0, t('No limits means no errors.'), 'File'); - $errors = file_validate_size($file, 1, 0); - $this->assertEqual(count($errors), 1, t('Error for the file being over the limit.'), 'File'); - $errors = file_validate_size($file, 0, 1); - $this->assertEqual(count($errors), 1, t('Error for the user being over their limit.'), 'File'); - $errors = file_validate_size($file, 1, 1); - $this->assertEqual(count($errors), 2, t('Errors for both the file and their limit.'), 'File'); - - $user = $original_user; - drupal_save_session(TRUE); - } -} - - - -/** - * Tests the file_unmanaged_save_data() function. - */ -class FileUnmanagedSaveDataTest extends FileTestCase { - public static function getInfo() { - return array( - 'name' => 'Unmanaged file save data', - 'description' => 'Tests the unmanaged file save data function.', - 'group' => 'File API', - ); - } - - /** - * Test the file_unmanaged_save_data() function. - */ - function testFileSaveData() { - $contents = $this->randomName(8); - - // No filename. - $filepath = file_unmanaged_save_data($contents); - $this->assertTrue($filepath, t('Unnamed file saved correctly.')); - $this->assertEqual(file_uri_scheme($filepath), file_default_scheme(), t("File was placed in Drupal's files directory.")); - $this->assertEqual($contents, file_get_contents($filepath), t('Contents of the file are correct.')); - - // Provide a filename. - $filepath = file_unmanaged_save_data($contents, 'public://asdf.txt', FILE_EXISTS_REPLACE); - $this->assertTrue($filepath, t('Unnamed file saved correctly.')); - $this->assertEqual('asdf.txt', basename($filepath), t('File was named correctly.')); - $this->assertEqual($contents, file_get_contents($filepath), t('Contents of the file are correct.')); - $this->assertFilePermissions($filepath, variable_get('file_chmod_file', 0664)); - } -} - -/** - * Tests the file_unmanaged_save_data() function on remote filesystems. - */ -class RemoteFileUnmanagedSaveDataTest extends FileUnmanagedSaveDataTest { - public static function getInfo() { - $info = parent::getInfo(); - $info['group'] = 'File API (remote)'; - return $info; - } - - function setUp() { - parent::setUp('file_test'); - variable_set('file_default_scheme', 'dummy-remote'); - } -} - -/** - * Test the file_save_upload() function. - */ -class FileSaveUploadTest extends FileHookTestCase { - /** - * An image file path for uploading. - */ - protected $image; - - /** - * A PHP file path for upload security testing. - */ - protected $phpfile; - - /** - * The largest file id when the test starts. - */ - protected $maxFidBefore; - - public static function getInfo() { - return array( - 'name' => 'File uploading', - 'description' => 'Tests the file uploading functions.', - 'group' => 'File API', - ); - } - - function setUp() { - parent::setUp(); - $account = $this->drupalCreateUser(array('access content')); - $this->drupalLogin($account); - - $image_files = $this->drupalGetTestFiles('image'); - $this->image = current($image_files); - - list(, $this->image_extension) = explode('.', $this->image->filename); - $this->assertTrue(is_file($this->image->uri), t("The image file we're going to upload exists.")); - - $this->phpfile = current($this->drupalGetTestFiles('php')); - $this->assertTrue(is_file($this->phpfile->uri), t("The PHP file we're going to upload exists.")); - - $this->maxFidBefore = db_query('SELECT MAX(fid) AS fid FROM {file_managed}')->fetchField(); - - // Upload with replace to guarantee there's something there. - $edit = array( - 'file_test_replace' => FILE_EXISTS_REPLACE, - 'files[file_test_upload]' => drupal_realpath($this->image->uri), - ); - $this->drupalPost('file-test/upload', $edit, t('Submit')); - $this->assertResponse(200, t('Received a 200 response for posted test file.')); - $this->assertRaw(t('You WIN!'), t('Found the success message.')); - - // Check that the correct hooks were called then clean out the hook - // counters. - $this->assertFileHooksCalled(array('validate', 'insert')); - file_test_reset(); - } - - /** - * Test the file_save_upload() function. - */ - function testNormal() { - $max_fid_after = db_query('SELECT MAX(fid) AS fid FROM {file_managed}')->fetchField(); - $this->assertTrue($max_fid_after > $this->maxFidBefore, t('A new file was created.')); - $file1 = file_load($max_fid_after); - $this->assertTrue($file1, t('Loaded the file.')); - // MIME type of the uploaded image may be either image/jpeg or image/png. - $this->assertEqual(substr($file1->filemime, 0, 5), 'image', 'A MIME type was set.'); - - // Reset the hook counters to get rid of the 'load' we just called. - file_test_reset(); - - // Upload a second file. - $max_fid_before = db_query('SELECT MAX(fid) AS fid FROM {file_managed}')->fetchField(); - $image2 = current($this->drupalGetTestFiles('image')); - $edit = array('files[file_test_upload]' => drupal_realpath($image2->uri)); - $this->drupalPost('file-test/upload', $edit, t('Submit')); - $this->assertResponse(200, t('Received a 200 response for posted test file.')); - $this->assertRaw(t('You WIN!')); - $max_fid_after = db_query('SELECT MAX(fid) AS fid FROM {file_managed}')->fetchField(); - - // Check that the correct hooks were called. - $this->assertFileHooksCalled(array('validate', 'insert')); - - $file2 = file_load($max_fid_after); - $this->assertTrue($file2); - // MIME type of the uploaded image may be either image/jpeg or image/png. - $this->assertEqual(substr($file2->filemime, 0, 5), 'image', 'A MIME type was set.'); - - // Load both files using file_load_multiple(). - $files = file_load_multiple(array($file1->fid, $file2->fid)); - $this->assertTrue(isset($files[$file1->fid]), t('File was loaded successfully')); - $this->assertTrue(isset($files[$file2->fid]), t('File was loaded successfully')); - - // Upload a third file to a subdirectory. - $image3 = current($this->drupalGetTestFiles('image')); - $image3_realpath = drupal_realpath($image3->uri); - $dir = $this->randomName(); - $edit = array( - 'files[file_test_upload]' => $image3_realpath, - 'file_subdir' => $dir, - ); - $this->drupalPost('file-test/upload', $edit, t('Submit')); - $this->assertResponse(200, t('Received a 200 response for posted test file.')); - $this->assertRaw(t('You WIN!')); - $this->assertTrue(is_file('temporary://' . $dir . '/' . trim(basename($image3_realpath)))); - - // Check that file_load_multiple() with no arguments returns FALSE. - $this->assertFalse(file_load_multiple(), t('No files were loaded.')); - } - - /** - * Test extension handling. - */ - function testHandleExtension() { - // The file being tested is a .gif which is in the default safe list - // of extensions to allow when the extension validator isn't used. This is - // implicitly tested at the testNormal() test. Here we tell - // file_save_upload() to only allow ".foo". - $extensions = 'foo'; - $edit = array( - 'file_test_replace' => FILE_EXISTS_REPLACE, - 'files[file_test_upload]' => drupal_realpath($this->image->uri), - 'extensions' => $extensions, - ); - - $this->drupalPost('file-test/upload', $edit, t('Submit')); - $this->assertResponse(200, t('Received a 200 response for posted test file.')); - $message = t('Only files with the following extensions are allowed:') . ' <em class="placeholder">' . $extensions . '</em>'; - $this->assertRaw($message, t('Can\'t upload a disallowed extension')); - $this->assertRaw(t('Epic upload FAIL!'), t('Found the failure message.')); - - // Check that the correct hooks were called. - $this->assertFileHooksCalled(array('validate')); - - // Reset the hook counters. - file_test_reset(); - - $extensions = 'foo ' . $this->image_extension; - // Now tell file_save_upload() to allow the extension of our test image. - $edit = array( - 'file_test_replace' => FILE_EXISTS_REPLACE, - 'files[file_test_upload]' => drupal_realpath($this->image->uri), - 'extensions' => $extensions, - ); - - $this->drupalPost('file-test/upload', $edit, t('Submit')); - $this->assertResponse(200, t('Received a 200 response for posted test file.')); - $this->assertNoRaw(t('Only files with the following extensions are allowed:'), t('Can upload an allowed extension.')); - $this->assertRaw(t('You WIN!'), t('Found the success message.')); - - // Check that the correct hooks were called. - $this->assertFileHooksCalled(array('validate', 'load', 'update')); - - // Reset the hook counters. - file_test_reset(); - - // Now tell file_save_upload() to allow any extension. - $edit = array( - 'file_test_replace' => FILE_EXISTS_REPLACE, - 'files[file_test_upload]' => drupal_realpath($this->image->uri), - 'allow_all_extensions' => TRUE, - ); - $this->drupalPost('file-test/upload', $edit, t('Submit')); - $this->assertResponse(200, t('Received a 200 response for posted test file.')); - $this->assertNoRaw(t('Only files with the following extensions are allowed:'), t('Can upload any extension.')); - $this->assertRaw(t('You WIN!'), t('Found the success message.')); - - // Check that the correct hooks were called. - $this->assertFileHooksCalled(array('validate', 'load', 'update')); - } - - /** - * Test dangerous file handling. - */ - function testHandleDangerousFile() { - // Allow the .php extension and make sure it gets renamed to .txt for - // safety. Also check to make sure its MIME type was changed. - $edit = array( - 'file_test_replace' => FILE_EXISTS_REPLACE, - 'files[file_test_upload]' => drupal_realpath($this->phpfile->uri), - 'is_image_file' => FALSE, - 'extensions' => 'php', - ); - - $this->drupalPost('file-test/upload', $edit, t('Submit')); - $this->assertResponse(200, t('Received a 200 response for posted test file.')); - $message = t('For security reasons, your upload has been renamed to') . ' <em class="placeholder">' . $this->phpfile->filename . '.txt' . '</em>'; - $this->assertRaw($message, t('Dangerous file was renamed.')); - $this->assertRaw(t('File MIME type is text/plain.'), t('Dangerous file\'s MIME type was changed.')); - $this->assertRaw(t('You WIN!'), t('Found the success message.')); - - // Check that the correct hooks were called. - $this->assertFileHooksCalled(array('validate', 'insert')); - - // Ensure dangerous files are not renamed when insecure uploads is TRUE. - // Turn on insecure uploads. - variable_set('allow_insecure_uploads', 1); - // Reset the hook counters. - file_test_reset(); - - $this->drupalPost('file-test/upload', $edit, t('Submit')); - $this->assertResponse(200, t('Received a 200 response for posted test file.')); - $this->assertNoRaw(t('For security reasons, your upload has been renamed'), t('Found no security message.')); - $this->assertRaw(t('File name is !filename', array('!filename' => $this->phpfile->filename)), t('Dangerous file was not renamed when insecure uploads is TRUE.')); - $this->assertRaw(t('You WIN!'), t('Found the success message.')); - - // Check that the correct hooks were called. - $this->assertFileHooksCalled(array('validate', 'insert')); - - // Turn off insecure uploads. - variable_set('allow_insecure_uploads', 0); - } - - /** - * Test file munge handling. - */ - function testHandleFileMunge() { - // Ensure insecure uploads are disabled for this test. - variable_set('allow_insecure_uploads', 0); - $this->image = file_move($this->image, $this->image->uri . '.foo.' . $this->image_extension); - - // Reset the hook counters to get rid of the 'move' we just called. - file_test_reset(); - - $extensions = $this->image_extension; - $edit = array( - 'files[file_test_upload]' => drupal_realpath($this->image->uri), - 'extensions' => $extensions, - ); - - $munged_filename = $this->image->filename; - $munged_filename = substr($munged_filename, 0, strrpos($munged_filename, '.')); - $munged_filename .= '_.' . $this->image_extension; - - $this->drupalPost('file-test/upload', $edit, t('Submit')); - $this->assertResponse(200, t('Received a 200 response for posted test file.')); - $this->assertRaw(t('For security reasons, your upload has been renamed'), t('Found security message.')); - $this->assertRaw(t('File name is !filename', array('!filename' => $munged_filename)), t('File was successfully munged.')); - $this->assertRaw(t('You WIN!'), t('Found the success message.')); - - // Check that the correct hooks were called. - $this->assertFileHooksCalled(array('validate', 'insert')); - - // Ensure we don't munge files if we're allowing any extension. - // Reset the hook counters. - file_test_reset(); - - $edit = array( - 'files[file_test_upload]' => drupal_realpath($this->image->uri), - 'allow_all_extensions' => TRUE, - ); - - $this->drupalPost('file-test/upload', $edit, t('Submit')); - $this->assertResponse(200, t('Received a 200 response for posted test file.')); - $this->assertNoRaw(t('For security reasons, your upload has been renamed'), t('Found no security message.')); - $this->assertRaw(t('File name is !filename', array('!filename' => $this->image->filename)), t('File was not munged when allowing any extension.')); - $this->assertRaw(t('You WIN!'), t('Found the success message.')); - - // Check that the correct hooks were called. - $this->assertFileHooksCalled(array('validate', 'insert')); - } - - /** - * Test renaming when uploading over a file that already exists. - */ - function testExistingRename() { - $edit = array( - 'file_test_replace' => FILE_EXISTS_RENAME, - 'files[file_test_upload]' => drupal_realpath($this->image->uri) - ); - $this->drupalPost('file-test/upload', $edit, t('Submit')); - $this->assertResponse(200, t('Received a 200 response for posted test file.')); - $this->assertRaw(t('You WIN!'), t('Found the success message.')); - - // Check that the correct hooks were called. - $this->assertFileHooksCalled(array('validate', 'insert')); - } - - /** - * Test replacement when uploading over a file that already exists. - */ - function testExistingReplace() { - $edit = array( - 'file_test_replace' => FILE_EXISTS_REPLACE, - 'files[file_test_upload]' => drupal_realpath($this->image->uri) - ); - $this->drupalPost('file-test/upload', $edit, t('Submit')); - $this->assertResponse(200, t('Received a 200 response for posted test file.')); - $this->assertRaw(t('You WIN!'), t('Found the success message.')); - - // Check that the correct hooks were called. - $this->assertFileHooksCalled(array('validate', 'load', 'update')); - } - - /** - * Test for failure when uploading over a file that already exists. - */ - function testExistingError() { - $edit = array( - 'file_test_replace' => FILE_EXISTS_ERROR, - 'files[file_test_upload]' => drupal_realpath($this->image->uri) - ); - $this->drupalPost('file-test/upload', $edit, t('Submit')); - $this->assertResponse(200, t('Received a 200 response for posted test file.')); - $this->assertRaw(t('Epic upload FAIL!'), t('Found the failure message.')); - - // Check that the no hooks were called while failing. - $this->assertFileHooksCalled(array()); - } - - /** - * Test for no failures when not uploading a file. - */ - function testNoUpload() { - $this->drupalPost('file-test/upload', array(), t('Submit')); - $this->assertNoRaw(t('Epic upload FAIL!'), t('Failure message not found.')); - } -} - -/** - * Test the file_save_upload() function on remote filesystems. - */ -class RemoteFileSaveUploadTest extends FileSaveUploadTest { - public static function getInfo() { - $info = parent::getInfo(); - $info['group'] = 'File API (remote)'; - return $info; - } - - function setUp() { - parent::setUp('file_test'); - variable_set('file_default_scheme', 'dummy-remote'); - } -} - -/** - * Directory related tests. - */ -class FileDirectoryTest extends FileTestCase { - public static function getInfo() { - return array( - 'name' => 'File paths and directories', - 'description' => 'Tests operations dealing with directories.', - 'group' => 'File API', - ); - } - - /** - * Test directory handling functions. - */ - function testFileCheckDirectoryHandling() { - // A directory to operate on. - $directory = file_default_scheme() . '://' . $this->randomName() . '/' . $this->randomName(); - $this->assertFalse(is_dir($directory), t('Directory does not exist prior to testing.')); - - // Non-existent directory. - $this->assertFalse(file_prepare_directory($directory, 0), t('Error reported for non-existing directory.'), 'File'); - - // Make a directory. - $this->assertTrue(file_prepare_directory($directory, FILE_CREATE_DIRECTORY), t('No error reported when creating a new directory.'), 'File'); - - // Make sure directory actually exists. - $this->assertTrue(is_dir($directory), t('Directory actually exists.'), 'File'); - - if (substr(PHP_OS, 0, 3) != 'WIN') { - // PHP on Windows doesn't support any kind of useful read-only mode for - // directories. When executing a chmod() on a directory, PHP only sets the - // read-only flag, which doesn't prevent files to actually be written - // in the directory on any recent version of Windows. - - // Make directory read only. - @drupal_chmod($directory, 0444); - $this->assertFalse(file_prepare_directory($directory, 0), t('Error reported for a non-writeable directory.'), 'File'); - - // Test directory permission modification. - $this->assertTrue(file_prepare_directory($directory, FILE_MODIFY_PERMISSIONS), t('No error reported when making directory writeable.'), 'File'); - } - - // Test that the directory has the correct permissions. - $this->assertDirectoryPermissions($directory, variable_get('file_chmod_directory', 0775)); - - // Remove .htaccess file to then test that it gets re-created. - @drupal_unlink(file_default_scheme() . '://.htaccess'); - $this->assertFalse(is_file(file_default_scheme() . '://.htaccess'), t('Successfully removed the .htaccess file in the files directory.'), 'File'); - file_ensure_htaccess(); - $this->assertTrue(is_file(file_default_scheme() . '://.htaccess'), t('Successfully re-created the .htaccess file in the files directory.'), 'File'); - // Verify contents of .htaccess file. - $file = file_get_contents(file_default_scheme() . '://.htaccess'); - $this->assertEqual($file, "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006\nOptions None\nOptions +FollowSymLinks", t('The .htaccess file contains the proper content.'), 'File'); - } - - /** - * This will take a directory and path, and find a valid filepath that is not - * taken by another file. - */ - function testFileCreateNewFilepath() { - // First we test against an imaginary file that does not exist in a - // directory. - $basename = 'xyz.txt'; - $directory = 'misc'; - $original = $directory . '/' . $basename; - $path = file_create_filename($basename, $directory); - $this->assertEqual($path, $original, t('New filepath %new equals %original.', array('%new' => $path, '%original' => $original)), 'File'); - - // Then we test against a file that already exists within that directory. - $basename = 'druplicon.png'; - $original = $directory . '/' . $basename; - $expected = $directory . '/druplicon_0.png'; - $path = file_create_filename($basename, $directory); - $this->assertEqual($path, $expected, t('Creating a new filepath from %original equals %new.', array('%new' => $path, '%original' => $original)), 'File'); - - // @TODO: Finally we copy a file into a directory several times, to ensure a properly iterating filename suffix. - } - - /** - * This will test the filepath for a destination based on passed flags and - * whether or not the file exists. - * - * If a file exists, file_destination($destination, $replace) will either - * return: - * - the existing filepath, if $replace is FILE_EXISTS_REPLACE - * - a new filepath if FILE_EXISTS_RENAME - * - an error (returning FALSE) if FILE_EXISTS_ERROR. - * If the file doesn't currently exist, then it will simply return the - * filepath. - */ - function testFileDestination() { - // First test for non-existent file. - $destination = 'misc/xyz.txt'; - $path = file_destination($destination, FILE_EXISTS_REPLACE); - $this->assertEqual($path, $destination, t('Non-existing filepath destination is correct with FILE_EXISTS_REPLACE.'), 'File'); - $path = file_destination($destination, FILE_EXISTS_RENAME); - $this->assertEqual($path, $destination, t('Non-existing filepath destination is correct with FILE_EXISTS_RENAME.'), 'File'); - $path = file_destination($destination, FILE_EXISTS_ERROR); - $this->assertEqual($path, $destination, t('Non-existing filepath destination is correct with FILE_EXISTS_ERROR.'), 'File'); - - $destination = 'misc/druplicon.png'; - $path = file_destination($destination, FILE_EXISTS_REPLACE); - $this->assertEqual($path, $destination, t('Existing filepath destination remains the same with FILE_EXISTS_REPLACE.'), 'File'); - $path = file_destination($destination, FILE_EXISTS_RENAME); - $this->assertNotEqual($path, $destination, t('A new filepath destination is created when filepath destination already exists with FILE_EXISTS_RENAME.'), 'File'); - $path = file_destination($destination, FILE_EXISTS_ERROR); - $this->assertEqual($path, FALSE, t('An error is returned when filepath destination already exists with FILE_EXISTS_ERROR.'), 'File'); - } - - /** - * Ensure that the file_directory_temp() function always returns a value. - */ - function testFileDirectoryTemp() { - // Start with an empty variable to ensure we have a clean slate. - variable_set('file_temporary_path', ''); - $tmp_directory = file_directory_temp(); - $this->assertEqual(empty($tmp_directory), FALSE, t('file_directory_temp() returned a non-empty value.')); - $setting = variable_get('file_temporary_path', ''); - $this->assertEqual($setting, $tmp_directory, t("The 'file_temporary_path' variable has the same value that file_directory_temp() returned.")); - } -} - -/** - * Directory related tests. - */ -class RemoteFileDirectoryTest extends FileDirectoryTest { - public static function getInfo() { - $info = parent::getInfo(); - $info['group'] = 'File API (remote)'; - return $info; - } - - function setUp() { - parent::setUp('file_test'); - variable_set('file_default_scheme', 'dummy-remote'); - } -} - -/** - * Tests the file_scan_directory() function. - */ -class FileScanDirectoryTest extends FileTestCase { - public static function getInfo() { - return array( - 'name' => 'File scan directory', - 'description' => 'Tests the file_scan_directory() function.', - 'group' => 'File API', - ); - } - - function setUp() { - parent::setUp(); - $this->path = drupal_get_path('module', 'simpletest') . '/files'; - } - - /** - * Check the format of the returned values. - */ - function testReturn() { - // Grab a listing of all the JavaSscript files and check that they're - // passed to the callback. - $all_files = file_scan_directory($this->path, '/^javascript-/'); - ksort($all_files); - $this->assertEqual(2, count($all_files), t('Found two, expected javascript files.')); - - // Check the first file. - $file = reset($all_files); - $this->assertEqual(key($all_files), $file->uri, t('Correct array key was used for the first returned file.')); - $this->assertEqual($file->uri, $this->path . '/javascript-1.txt', t('First file name was set correctly.')); - $this->assertEqual($file->filename, 'javascript-1.txt', t('First basename was set correctly')); - $this->assertEqual($file->name, 'javascript-1', t('First name was set correctly.')); - - // Check the second file. - $file = next($all_files); - $this->assertEqual(key($all_files), $file->uri, t('Correct array key was used for the second returned file.')); - $this->assertEqual($file->uri, $this->path . '/javascript-2.script', t('Second file name was set correctly.')); - $this->assertEqual($file->filename, 'javascript-2.script', t('Second basename was set correctly')); - $this->assertEqual($file->name, 'javascript-2', t('Second name was set correctly.')); - } - - /** - * Check that the callback function is called correctly. - */ - function testOptionCallback() { - // When nothing is matched nothing should be passed to the callback. - $all_files = file_scan_directory($this->path, '/^NONEXISTINGFILENAME/', array('callback' => 'file_test_file_scan_callback')); - $this->assertEqual(0, count($all_files), t('No files were found.')); - $results = file_test_file_scan_callback(); - file_test_file_scan_callback_reset(); - $this->assertEqual(0, count($results), t('No files were passed to the callback.')); - - // Grab a listing of all the JavaSscript files and check that they're - // passed to the callback. - $all_files = file_scan_directory($this->path, '/^javascript-/', array('callback' => 'file_test_file_scan_callback')); - $this->assertEqual(2, count($all_files), t('Found two, expected javascript files.')); - $results = file_test_file_scan_callback(); - file_test_file_scan_callback_reset(); - $this->assertEqual(2, count($results), t('Files were passed to the callback.')); - } - - /** - * Check that the no-mask parameter is honored. - */ - function testOptionNoMask() { - // Grab a listing of all the JavaSscript files. - $all_files = file_scan_directory($this->path, '/^javascript-/'); - $this->assertEqual(2, count($all_files), t('Found two, expected javascript files.')); - - // Now use the nomast parameter to filter out the .script file. - $filtered_files = file_scan_directory($this->path, '/^javascript-/', array('nomask' => '/.script$/')); - $this->assertEqual(1, count($filtered_files), t('Filtered correctly.')); - } - - /** - * Check that key parameter sets the return value's key. - */ - function testOptionKey() { - // "filename", for the path starting with $dir. - $expected = array($this->path . '/javascript-1.txt', $this->path . '/javascript-2.script'); - $actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'filepath'))); - sort($actual); - $this->assertEqual($expected, $actual, t('Returned the correct values for the filename key.')); - - // "basename", for the basename of the file. - $expected = array('javascript-1.txt', 'javascript-2.script'); - $actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'filename'))); - sort($actual); - $this->assertEqual($expected, $actual, t('Returned the correct values for the basename key.')); - - // "name" for the name of the file without an extension. - $expected = array('javascript-1', 'javascript-2'); - $actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'name'))); - sort($actual); - $this->assertEqual($expected, $actual, t('Returned the correct values for the name key.')); - - // Invalid option that should default back to "filename". - $expected = array($this->path . '/javascript-1.txt', $this->path . '/javascript-2.script'); - $actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'INVALID'))); - sort($actual); - $this->assertEqual($expected, $actual, t('An invalid key defaulted back to the default.')); - } - - /** - * Check that the recurse option decends into subdirectories. - */ - function testOptionRecurse() { - $files = file_scan_directory(drupal_get_path('module', 'simpletest'), '/^javascript-/', array('recurse' => FALSE)); - $this->assertTrue(empty($files), t("Without recursion couldn't find javascript files.")); - - $files = file_scan_directory(drupal_get_path('module', 'simpletest'), '/^javascript-/', array('recurse' => TRUE)); - $this->assertEqual(2, count($files), t('With recursion we found the expected javascript files.')); - } - - - /** - * Check that the min_depth options lets us ignore files in the starting - * directory. - */ - function testOptionMinDepth() { - $files = file_scan_directory($this->path, '/^javascript-/', array('min_depth' => 0)); - $this->assertEqual(2, count($files), t('No minimum-depth gets files in current directory.')); - - $files = file_scan_directory($this->path, '/^javascript-/', array('min_depth' => 1)); - $this->assertTrue(empty($files), t("Minimum-depth of 1 successfully excludes files from current directory.")); - } -} - -/** - * Tests the file_scan_directory() function on remote filesystems. - */ -class RemoteFileScanDirectoryTest extends FileScanDirectoryTest { - public static function getInfo() { - $info = parent::getInfo(); - $info['group'] = 'File API (remote)'; - return $info; - } - - function setUp() { - parent::setUp('file_test'); - variable_set('file_default_scheme', 'dummy-remote'); - } -} - -/** - * Deletion related tests. - */ -class FileUnmanagedDeleteTest extends FileTestCase { - public static function getInfo() { - return array( - 'name' => 'Unmanaged file delete', - 'description' => 'Tests the unmanaged file delete function.', - 'group' => 'File API', - ); - } - - /** - * Delete a normal file. - */ - function testNormal() { - // Create a file for testing - $file = $this->createFile(); - - // Delete a regular file - $this->assertTrue(file_unmanaged_delete($file->uri), t('Deleted worked.')); - $this->assertFalse(file_exists($file->uri), t('Test file has actually been deleted.')); - } - - /** - * Try deleting a missing file. - */ - function testMissing() { - // Try to delete a non-existing file - $this->assertTrue(file_unmanaged_delete(file_default_scheme() . '/' . $this->randomName()), t('Returns true when deleting a non-existent file.')); - } - - /** - * Try deleting a directory. - */ - function testDirectory() { - // A directory to operate on. - $directory = $this->createDirectory(); - - // Try to delete a directory - $this->assertFalse(file_unmanaged_delete($directory), t('Could not delete the delete directory.')); - $this->assertTrue(file_exists($directory), t('Directory has not been deleted.')); - } -} - -/** - * Deletion related tests on remote filesystems. - */ -class RemoteFileUnmanagedDeleteTest extends FileUnmanagedDeleteTest { - public static function getInfo() { - $info = parent::getInfo(); - $info['group'] = 'File API (remote)'; - return $info; - } - - function setUp() { - parent::setUp('file_test'); - variable_set('file_default_scheme', 'dummy-remote'); - } -} - -/** - * Deletion related tests. - */ -class FileUnmanagedDeleteRecursiveTest extends FileTestCase { - public static function getInfo() { - return array( - 'name' => 'Unmanaged recursive file delete', - 'description' => 'Tests the unmanaged file delete recursive function.', - 'group' => 'File API', - ); - } - - /** - * Delete a normal file. - */ - function testSingleFile() { - // Create a file for testing - $filepath = file_default_scheme() . '://' . $this->randomName(); - file_put_contents($filepath, ''); - - // Delete the file. - $this->assertTrue(file_unmanaged_delete_recursive($filepath), t('Function reported success.')); - $this->assertFalse(file_exists($filepath), t('Test file has been deleted.')); - } - - /** - * Try deleting an empty directory. - */ - function testEmptyDirectory() { - // A directory to operate on. - $directory = $this->createDirectory(); - - // Delete the directory. - $this->assertTrue(file_unmanaged_delete_recursive($directory), t('Function reported success.')); - $this->assertFalse(file_exists($directory), t('Directory has been deleted.')); - } - - /** - * Try deleting a directory with some files. - */ - function testDirectory() { - // A directory to operate on. - $directory = $this->createDirectory(); - $filepathA = $directory . '/A'; - $filepathB = $directory . '/B'; - file_put_contents($filepathA, ''); - file_put_contents($filepathB, ''); - - // Delete the directory. - $this->assertTrue(file_unmanaged_delete_recursive($directory), t('Function reported success.')); - $this->assertFalse(file_exists($filepathA), t('Test file A has been deleted.')); - $this->assertFalse(file_exists($filepathB), t('Test file B has been deleted.')); - $this->assertFalse(file_exists($directory), t('Directory has been deleted.')); - } - - /** - * Try deleting subdirectories with some files. - */ - function testSubDirectory() { - // A directory to operate on. - $directory = $this->createDirectory(); - $subdirectory = $this->createDirectory($directory . '/sub'); - $filepathA = $directory . '/A'; - $filepathB = $subdirectory . '/B'; - file_put_contents($filepathA, ''); - file_put_contents($filepathB, ''); - - // Delete the directory. - $this->assertTrue(file_unmanaged_delete_recursive($directory), t('Function reported success.')); - $this->assertFalse(file_exists($filepathA), t('Test file A has been deleted.')); - $this->assertFalse(file_exists($filepathB), t('Test file B has been deleted.')); - $this->assertFalse(file_exists($subdirectory), t('Subdirectory has been deleted.')); - $this->assertFalse(file_exists($directory), t('Directory has been deleted.')); - } -} - -/** - * Deletion related tests on remote filesystems. - */ -class RemoteFileUnmanagedDeleteRecursiveTest extends FileUnmanagedDeleteRecursiveTest { - public static function getInfo() { - $info = parent::getInfo(); - $info['group'] = 'File API (remote)'; - return $info; - } - - function setUp() { - parent::setUp('file_test'); - variable_set('file_default_scheme', 'dummy-remote'); - } -} - -/** - * Unmanaged move related tests. - */ -class FileUnmanagedMoveTest extends FileTestCase { - public static function getInfo() { - return array( - 'name' => 'Unmanaged file moving', - 'description' => 'Tests the unmanaged file move function.', - 'group' => 'File API', - ); - } - - /** - * Move a normal file. - */ - function testNormal() { - // Create a file for testing - $file = $this->createFile(); - - // Moving to a new name. - $desired_filepath = 'public://' . $this->randomName(); - $new_filepath = file_unmanaged_move($file->uri, $desired_filepath, FILE_EXISTS_ERROR); - $this->assertTrue($new_filepath, t('Move was successful.')); - $this->assertEqual($new_filepath, $desired_filepath, t('Returned expected filepath.')); - $this->assertTrue(file_exists($new_filepath), t('File exists at the new location.')); - $this->assertFalse(file_exists($file->uri), t('No file remains at the old location.')); - $this->assertFilePermissions($new_filepath, variable_get('file_chmod_file', 0664)); - - // Moving with rename. - $desired_filepath = 'public://' . $this->randomName(); - $this->assertTrue(file_exists($new_filepath), t('File exists before moving.')); - $this->assertTrue(file_put_contents($desired_filepath, ' '), t('Created a file so a rename will have to happen.')); - $newer_filepath = file_unmanaged_move($new_filepath, $desired_filepath, FILE_EXISTS_RENAME); - $this->assertTrue($newer_filepath, t('Move was successful.')); - $this->assertNotEqual($newer_filepath, $desired_filepath, t('Returned expected filepath.')); - $this->assertTrue(file_exists($newer_filepath), t('File exists at the new location.')); - $this->assertFalse(file_exists($new_filepath), t('No file remains at the old location.')); - $this->assertFilePermissions($newer_filepath, variable_get('file_chmod_file', 0664)); - - // TODO: test moving to a directory (rather than full directory/file path) - // TODO: test creating and moving normal files (rather than streams) - } - - /** - * Try to move a missing file. - */ - function testMissing() { - // Move non-existent file. - $new_filepath = file_unmanaged_move($this->randomName(), $this->randomName()); - $this->assertFalse($new_filepath, t('Moving a missing file fails.')); - } - - /** - * Try to move a file onto itself. - */ - function testOverwriteSelf() { - // Create a file for testing. - $file = $this->createFile(); - - // Move the file onto itself without renaming shouldn't make changes. - $new_filepath = file_unmanaged_move($file->uri, $file->uri, FILE_EXISTS_REPLACE); - $this->assertFalse($new_filepath, t('Moving onto itself without renaming fails.')); - $this->assertTrue(file_exists($file->uri), t('File exists after moving onto itself.')); - - // Move the file onto itself with renaming will result in a new filename. - $new_filepath = file_unmanaged_move($file->uri, $file->uri, FILE_EXISTS_RENAME); - $this->assertTrue($new_filepath, t('Moving onto itself with renaming works.')); - $this->assertFalse(file_exists($file->uri), t('Original file has been removed.')); - $this->assertTrue(file_exists($new_filepath), t('File exists after moving onto itself.')); - } -} - -/** - * Unmanaged move related tests on remote filesystems. - */ -class RemoteFileUnmanagedMoveTest extends FileUnmanagedMoveTest { - public static function getInfo() { - $info = parent::getInfo(); - $info['group'] = 'File API (remote)'; - return $info; - } - - function setUp() { - parent::setUp('file_test'); - variable_set('file_default_scheme', 'dummy-remote'); - } -} - -/** - * Unmanaged copy related tests. - */ -class FileUnmanagedCopyTest extends FileTestCase { - public static function getInfo() { - return array( - 'name' => 'Unmanaged file copying', - 'description' => 'Tests the unmanaged file copy function.', - 'group' => 'File API', - ); - } - - /** - * Copy a normal file. - */ - function testNormal() { - // Create a file for testing - $file = $this->createFile(); - - // Copying to a new name. - $desired_filepath = 'public://' . $this->randomName(); - $new_filepath = file_unmanaged_copy($file->uri, $desired_filepath, FILE_EXISTS_ERROR); - $this->assertTrue($new_filepath, t('Copy was successful.')); - $this->assertEqual($new_filepath, $desired_filepath, t('Returned expected filepath.')); - $this->assertTrue(file_exists($file->uri), t('Original file remains.')); - $this->assertTrue(file_exists($new_filepath), t('New file exists.')); - $this->assertFilePermissions($new_filepath, variable_get('file_chmod_file', 0664)); - - // Copying with rename. - $desired_filepath = 'public://' . $this->randomName(); - $this->assertTrue(file_put_contents($desired_filepath, ' '), t('Created a file so a rename will have to happen.')); - $newer_filepath = file_unmanaged_copy($file->uri, $desired_filepath, FILE_EXISTS_RENAME); - $this->assertTrue($newer_filepath, t('Copy was successful.')); - $this->assertNotEqual($newer_filepath, $desired_filepath, t('Returned expected filepath.')); - $this->assertTrue(file_exists($file->uri), t('Original file remains.')); - $this->assertTrue(file_exists($newer_filepath), t('New file exists.')); - $this->assertFilePermissions($newer_filepath, variable_get('file_chmod_file', 0664)); - - // TODO: test copying to a directory (rather than full directory/file path) - // TODO: test copying normal files using normal paths (rather than only streams) - } - - /** - * Copy a non-existent file. - */ - function testNonExistent() { - // Copy non-existent file - $desired_filepath = $this->randomName(); - $this->assertFalse(file_exists($desired_filepath), t("Randomly named file doesn't exists.")); - $new_filepath = file_unmanaged_copy($desired_filepath, $this->randomName()); - $this->assertFalse($new_filepath, t('Copying a missing file fails.')); - } - - /** - * Copy a file onto itself. - */ - function testOverwriteSelf() { - // Create a file for testing - $file = $this->createFile(); - - // Copy the file onto itself with renaming works. - $new_filepath = file_unmanaged_copy($file->uri, $file->uri, FILE_EXISTS_RENAME); - $this->assertTrue($new_filepath, t('Copying onto itself with renaming works.')); - $this->assertNotEqual($new_filepath, $file->uri, t('Copied file has a new name.')); - $this->assertTrue(file_exists($file->uri), t('Original file exists after copying onto itself.')); - $this->assertTrue(file_exists($new_filepath), t('Copied file exists after copying onto itself.')); - $this->assertFilePermissions($new_filepath, variable_get('file_chmod_file', 0664)); - - // Copy the file onto itself without renaming fails. - $new_filepath = file_unmanaged_copy($file->uri, $file->uri, FILE_EXISTS_ERROR); - $this->assertFalse($new_filepath, t('Copying onto itself without renaming fails.')); - $this->assertTrue(file_exists($file->uri), t('File exists after copying onto itself.')); - - // Copy the file into same directory without renaming fails. - $new_filepath = file_unmanaged_copy($file->uri, drupal_dirname($file->uri), FILE_EXISTS_ERROR); - $this->assertFalse($new_filepath, t('Copying onto itself fails.')); - $this->assertTrue(file_exists($file->uri), t('File exists after copying onto itself.')); - - // Copy the file into same directory with renaming works. - $new_filepath = file_unmanaged_copy($file->uri, drupal_dirname($file->uri), FILE_EXISTS_RENAME); - $this->assertTrue($new_filepath, t('Copying into same directory works.')); - $this->assertNotEqual($new_filepath, $file->uri, t('Copied file has a new name.')); - $this->assertTrue(file_exists($file->uri), t('Original file exists after copying onto itself.')); - $this->assertTrue(file_exists($new_filepath), t('Copied file exists after copying onto itself.')); - $this->assertFilePermissions($new_filepath, variable_get('file_chmod_file', 0664)); - } -} - -/** - * Unmanaged copy related tests on remote filesystems. - */ -class RemoteFileUnmanagedCopyTest extends FileUnmanagedCopyTest { - public static function getInfo() { - $info = parent::getInfo(); - $info['group'] = 'File API (remote)'; - return $info; - } - - function setUp() { - parent::setUp('file_test'); - variable_set('file_default_scheme', 'dummy-remote'); - } -} - -/** - * Deletion related tests. - */ -class FileDeleteTest extends FileHookTestCase { - public static function getInfo() { - return array( - 'name' => 'File delete', - 'description' => 'Tests the file delete function.', - 'group' => 'File API', - ); - } - - /** - * Tries deleting a normal file (as opposed to a directory, symlink, etc). - */ - function testUnused() { - $file = $this->createFile(); - - // Check that deletion removes the file and database record. - $this->assertTrue(is_file($file->uri), t('File exists.')); - $this->assertIdentical(file_delete($file), TRUE, t('Delete worked.')); - $this->assertFileHooksCalled(array('delete')); - $this->assertFalse(file_exists($file->uri), t('Test file has actually been deleted.')); - $this->assertFalse(file_load($file->fid), t('File was removed from the database.')); - } - - /** - * Tries deleting a file that is in use. - */ - function testInUse() { - $file = $this->createFile(); - file_usage_add($file, 'testing', 'test', 1); - file_usage_add($file, 'testing', 'test', 1); - - file_usage_delete($file, 'testing', 'test', 1); - file_delete($file); - $usage = file_usage_list($file); - $this->assertEqual($usage['testing']['test'], array(1 => 1), t('Test file is still in use.')); - $this->assertTrue(file_exists($file->uri), t('File still exists on the disk.')); - $this->assertTrue(file_load($file->fid), t('File still exists in the database.')); - - // Clear out the call to hook_file_load(). - file_test_reset(); - - file_usage_delete($file, 'testing', 'test', 1); - file_delete($file); - $usage = file_usage_list($file); - $this->assertFileHooksCalled(array('delete')); - $this->assertTrue(empty($usage), t('File usage data was removed.')); - $this->assertFalse(file_exists($file->uri), t('File has been deleted after its last usage was removed.')); - $this->assertFalse(file_load($file->fid), t('File was removed from the database.')); - } -} - - -/** - * Move related tests - */ -class FileMoveTest extends FileHookTestCase { - public static function getInfo() { - return array( - 'name' => 'File moving', - 'description' => 'Tests the file move function.', - 'group' => 'File API', - ); - } - - /** - * Move a normal file. - */ - function testNormal() { - $contents = $this->randomName(10); - $source = $this->createFile(NULL, $contents); - $desired_filepath = 'public://' . $this->randomName(); - - // Clone the object so we don't have to worry about the function changing - // our reference copy. - $result = file_move(clone $source, $desired_filepath, FILE_EXISTS_ERROR); - - // Check the return status and that the contents changed. - $this->assertTrue($result, t('File moved successfully.')); - $this->assertFalse(file_exists($source->uri)); - $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file correctly written.')); - - // Check that the correct hooks were called. - $this->assertFileHooksCalled(array('move', 'load', 'update')); - - // Make sure we got the same file back. - $this->assertEqual($source->fid, $result->fid, t("Source file id's' %fid is unchanged after move.", array('%fid' => $source->fid))); - - // Reload the file from the database and check that the changes were - // actually saved. - $loaded_file = file_load($result->fid, TRUE); - $this->assertTrue($loaded_file, t('File can be loaded from the database.')); - $this->assertFileUnchanged($result, $loaded_file); - } - - /** - * Test renaming when moving onto a file that already exists. - */ - function testExistingRename() { - // Setup a file to overwrite. - $contents = $this->randomName(10); - $source = $this->createFile(NULL, $contents); - $target = $this->createFile(); - $this->assertDifferentFile($source, $target); - - // Clone the object so we don't have to worry about the function changing - // our reference copy. - $result = file_move(clone $source, $target->uri, FILE_EXISTS_RENAME); - - // Check the return status and that the contents changed. - $this->assertTrue($result, t('File moved successfully.')); - $this->assertFalse(file_exists($source->uri)); - $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file correctly written.')); - - // Check that the correct hooks were called. - $this->assertFileHooksCalled(array('move', 'load', 'update')); - - // Compare the returned value to what made it into the database. - $this->assertFileUnchanged($result, file_load($result->fid, TRUE)); - // The target file should not have been altered. - $this->assertFileUnchanged($target, file_load($target->fid, TRUE)); - // Make sure we end up with two distinct files afterwards. - $this->assertDifferentFile($target, $result); - - // Compare the source and results. - $loaded_source = file_load($source->fid, TRUE); - $this->assertEqual($loaded_source->fid, $result->fid, t("Returned file's id matches the source.")); - $this->assertNotEqual($loaded_source->uri, $source->uri, t("Returned file path has changed from the original.")); - } - - /** - * Test replacement when moving onto a file that already exists. - */ - function testExistingReplace() { - // Setup a file to overwrite. - $contents = $this->randomName(10); - $source = $this->createFile(NULL, $contents); - $target = $this->createFile(); - $this->assertDifferentFile($source, $target); - - // Clone the object so we don't have to worry about the function changing - // our reference copy. - $result = file_move(clone $source, $target->uri, FILE_EXISTS_REPLACE); - - // Look at the results. - $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file were overwritten.')); - $this->assertFalse(file_exists($source->uri)); - $this->assertTrue($result, t('File moved successfully.')); - - // Check that the correct hooks were called. - $this->assertFileHooksCalled(array('move', 'update', 'delete', 'load')); - - // Reload the file from the database and check that the changes were - // actually saved. - $loaded_result = file_load($result->fid, TRUE); - $this->assertFileUnchanged($result, $loaded_result); - // Check that target was re-used. - $this->assertSameFile($target, $loaded_result); - // Source and result should be totally different. - $this->assertDifferentFile($source, $loaded_result); - } - - /** - * Test replacement when moving onto itself. - */ - function testExistingReplaceSelf() { - // Setup a file to overwrite. - $contents = $this->randomName(10); - $source = $this->createFile(NULL, $contents); - - // Copy the file over itself. Clone the object so we don't have to worry - // about the function changing our reference copy. - $result = file_move(clone $source, $source->uri, FILE_EXISTS_REPLACE); - $this->assertFalse($result, t('File move failed.')); - $this->assertEqual($contents, file_get_contents($source->uri), t('Contents of file were not altered.')); - - // Check that no hooks were called while failing. - $this->assertFileHooksCalled(array()); - - // Load the file from the database and make sure it is identical to what - // was returned. - $this->assertFileUnchanged($source, file_load($source->fid, TRUE)); - } - - /** - * Test that moving onto an existing file fails when FILE_EXISTS_ERROR is - * specified. - */ - function testExistingError() { - $contents = $this->randomName(10); - $source = $this->createFile(); - $target = $this->createFile(NULL, $contents); - $this->assertDifferentFile($source, $target); - - // Clone the object so we don't have to worry about the function changing - // our reference copy. - $result = file_move(clone $source, $target->uri, FILE_EXISTS_ERROR); - - // Check the return status and that the contents did not change. - $this->assertFalse($result, t('File move failed.')); - $this->assertTrue(file_exists($source->uri)); - $this->assertEqual($contents, file_get_contents($target->uri), t('Contents of file were not altered.')); - - // Check that no hooks were called while failing. - $this->assertFileHooksCalled(array()); - - // Load the file from the database and make sure it is identical to what - // was returned. - $this->assertFileUnchanged($source, file_load($source->fid, TRUE)); - $this->assertFileUnchanged($target, file_load($target->fid, TRUE)); - } -} - - -/** - * Copy related tests. - */ -class FileCopyTest extends FileHookTestCase { - public static function getInfo() { - return array( - 'name' => 'File copying', - 'description' => 'Tests the file copy function.', - 'group' => 'File API', - ); - } - - /** - * Test file copying in the normal, base case. - */ - function testNormal() { - $contents = $this->randomName(10); - $source = $this->createFile(NULL, $contents); - $desired_uri = 'public://' . $this->randomName(); - - // Clone the object so we don't have to worry about the function changing - // our reference copy. - $result = file_copy(clone $source, $desired_uri, FILE_EXISTS_ERROR); - - // Check the return status and that the contents changed. - $this->assertTrue($result, t('File copied successfully.')); - $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file were copied correctly.')); - - // Check that the correct hooks were called. - $this->assertFileHooksCalled(array('copy', 'insert')); - - $this->assertDifferentFile($source, $result); - $this->assertEqual($result->uri, $desired_uri, t('The copied file object has the desired filepath.')); - $this->assertTrue(file_exists($source->uri), t('The original file still exists.')); - $this->assertTrue(file_exists($result->uri), t('The copied file exists.')); - - // Reload the file from the database and check that the changes were - // actually saved. - $this->assertFileUnchanged($result, file_load($result->fid, TRUE)); - } - - /** - * Test renaming when copying over a file that already exists. - */ - function testExistingRename() { - // Setup a file to overwrite. - $contents = $this->randomName(10); - $source = $this->createFile(NULL, $contents); - $target = $this->createFile(); - $this->assertDifferentFile($source, $target); - - // Clone the object so we don't have to worry about the function changing - // our reference copy. - $result = file_copy(clone $source, $target->uri, FILE_EXISTS_RENAME); - - // Check the return status and that the contents changed. - $this->assertTrue($result, t('File copied successfully.')); - $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file were copied correctly.')); - $this->assertNotEqual($result->uri, $source->uri, t('Returned file path has changed from the original.')); - - // Check that the correct hooks were called. - $this->assertFileHooksCalled(array('copy', 'insert')); - - // Load all the affected files to check the changes that actually made it - // to the database. - $loaded_source = file_load($source->fid, TRUE); - $loaded_target = file_load($target->fid, TRUE); - $loaded_result = file_load($result->fid, TRUE); - - // Verify that the source file wasn't changed. - $this->assertFileUnchanged($source, $loaded_source); - - // Verify that what was returned is what's in the database. - $this->assertFileUnchanged($result, $loaded_result); - - // Make sure we end up with three distinct files afterwards. - $this->assertDifferentFile($loaded_source, $loaded_target); - $this->assertDifferentFile($loaded_target, $loaded_result); - $this->assertDifferentFile($loaded_source, $loaded_result); - } - - /** - * Test replacement when copying over a file that already exists. - */ - function testExistingReplace() { - // Setup a file to overwrite. - $contents = $this->randomName(10); - $source = $this->createFile(NULL, $contents); - $target = $this->createFile(); - $this->assertDifferentFile($source, $target); - - // Clone the object so we don't have to worry about the function changing - // our reference copy. - $result = file_copy(clone $source, $target->uri, FILE_EXISTS_REPLACE); - - // Check the return status and that the contents changed. - $this->assertTrue($result, t('File copied successfully.')); - $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file were overwritten.')); - $this->assertDifferentFile($source, $result); - - // Check that the correct hooks were called. - $this->assertFileHooksCalled(array('load', 'copy', 'update')); - - // Load all the affected files to check the changes that actually made it - // to the database. - $loaded_source = file_load($source->fid, TRUE); - $loaded_target = file_load($target->fid, TRUE); - $loaded_result = file_load($result->fid, TRUE); - - // Verify that the source file wasn't changed. - $this->assertFileUnchanged($source, $loaded_source); - - // Verify that what was returned is what's in the database. - $this->assertFileUnchanged($result, $loaded_result); - - // Target file was reused for the result. - $this->assertFileUnchanged($loaded_target, $loaded_result); - } - - /** - * Test that copying over an existing file fails when FILE_EXISTS_ERROR is - * specified. - */ - function testExistingError() { - $contents = $this->randomName(10); - $source = $this->createFile(); - $target = $this->createFile(NULL, $contents); - $this->assertDifferentFile($source, $target); - - // Clone the object so we don't have to worry about the function changing - // our reference copy. - $result = file_copy(clone $source, $target->uri, FILE_EXISTS_ERROR); - - // Check the return status and that the contents were not changed. - $this->assertFalse($result, t('File copy failed.')); - $this->assertEqual($contents, file_get_contents($target->uri), t('Contents of file were not altered.')); - - // Check that the correct hooks were called. - $this->assertFileHooksCalled(array()); - - $this->assertFileUnchanged($source, file_load($source->fid, TRUE)); - $this->assertFileUnchanged($target, file_load($target->fid, TRUE)); - } -} - - -/** - * Tests the file_load() function. - */ -class FileLoadTest extends FileHookTestCase { - public static function getInfo() { - return array( - 'name' => 'File loading', - 'description' => 'Tests the file_load() function.', - 'group' => 'File API', - ); - } - - /** - * Try to load a non-existent file by fid. - */ - function testLoadMissingFid() { - $this->assertFalse(file_load(-1), t("Try to load an invalid fid fails.")); - $this->assertFileHooksCalled(array()); - } - - /** - * Try to load a non-existent file by URI. - */ - function testLoadMissingFilepath() { - $files = file_load_multiple(array(), array('uri' => 'foobar://misc/druplicon.png')); - $this->assertFalse(reset($files), t("Try to load a file that doesn't exist in the database fails.")); - $this->assertFileHooksCalled(array()); - } - - /** - * Try to load a non-existent file by status. - */ - function testLoadInvalidStatus() { - $files = file_load_multiple(array(), array('status' => -99)); - $this->assertFalse(reset($files), t("Trying to load a file with an invalid status fails.")); - $this->assertFileHooksCalled(array()); - } - - /** - * Load a single file and ensure that the correct values are returned. - */ - function testSingleValues() { - // Create a new file object from scratch so we know the values. - $file = $this->createFile('druplicon.txt', NULL, 'public'); - - $by_fid_file = file_load($file->fid); - $this->assertFileHookCalled('load'); - $this->assertTrue(is_object($by_fid_file), t('file_load() returned an object.')); - $this->assertEqual($by_fid_file->fid, $file->fid, t("Loading by fid got the same fid."), 'File'); - $this->assertEqual($by_fid_file->uri, $file->uri, t("Loading by fid got the correct filepath."), 'File'); - $this->assertEqual($by_fid_file->filename, $file->filename, t("Loading by fid got the correct filename."), 'File'); - $this->assertEqual($by_fid_file->filemime, $file->filemime, t("Loading by fid got the correct MIME type."), 'File'); - $this->assertEqual($by_fid_file->status, $file->status, t("Loading by fid got the correct status."), 'File'); - $this->assertTrue($by_fid_file->file_test['loaded'], t('file_test_file_load() was able to modify the file during load.')); - } - - /** - * This will test loading file data from the database. - */ - function testMultiple() { - // Create a new file object. - $file = $this->createFile('druplicon.txt', NULL, 'public'); - - // Load by path. - file_test_reset(); - $by_path_files = file_load_multiple(array(), array('uri' => $file->uri)); - $this->assertFileHookCalled('load'); - $this->assertEqual(1, count($by_path_files), t('file_load_multiple() returned an array of the correct size.')); - $by_path_file = reset($by_path_files); - $this->assertTrue($by_path_file->file_test['loaded'], t('file_test_file_load() was able to modify the file during load.')); - $this->assertEqual($by_path_file->fid, $file->fid, t("Loading by filepath got the correct fid."), 'File'); - - // Load by fid. - file_test_reset(); - $by_fid_files = file_load_multiple(array($file->fid), array()); - $this->assertFileHookCalled('load'); - $this->assertEqual(1, count($by_fid_files), t('file_load_multiple() returned an array of the correct size.')); - $by_fid_file = reset($by_fid_files); - $this->assertTrue($by_fid_file->file_test['loaded'], t('file_test_file_load() was able to modify the file during load.')); - $this->assertEqual($by_fid_file->uri, $file->uri, t("Loading by fid got the correct filepath."), 'File'); - } -} - -/** - * Tests the file_save() function. - */ -class FileSaveTest extends FileHookTestCase { - public static function getInfo() { - return array( - 'name' => 'File saving', - 'description' => 'Tests the file_save() function.', - 'group' => 'File API', - ); - } - - function testFileSave() { - // Create a new file object. - $file = array( - 'uid' => 1, - 'filename' => 'druplicon.txt', - 'uri' => 'public://druplicon.txt', - 'filemime' => 'text/plain', - 'timestamp' => 1, - 'status' => FILE_STATUS_PERMANENT, - ); - $file = (object) $file; - file_put_contents($file->uri, 'hello world'); - - // Save it, inserting a new record. - $saved_file = file_save($file); - - // Check that the correct hooks were called. - $this->assertFileHooksCalled(array('insert')); - - $this->assertNotNull($saved_file, t("Saving the file should give us back a file object."), 'File'); - $this->assertTrue($saved_file->fid > 0, t("A new file ID is set when saving a new file to the database."), 'File'); - $loaded_file = db_query('SELECT * FROM {file_managed} f WHERE f.fid = :fid', array(':fid' => $saved_file->fid))->fetch(PDO::FETCH_OBJ); - $this->assertNotNull($loaded_file, t("Record exists in the database.")); - $this->assertEqual($loaded_file->status, $file->status, t("Status was saved correctly.")); - $this->assertEqual($saved_file->filesize, filesize($file->uri), t("File size was set correctly."), 'File'); - $this->assertTrue($saved_file->timestamp > 1, t("File size was set correctly."), 'File'); - - - // Resave the file, updating the existing record. - file_test_reset(); - $saved_file->status = 7; - $resaved_file = file_save($saved_file); - - // Check that the correct hooks were called. - $this->assertFileHooksCalled(array('load', 'update')); - - $this->assertEqual($resaved_file->fid, $saved_file->fid, t("The file ID of an existing file is not changed when updating the database."), 'File'); - $this->assertTrue($resaved_file->timestamp >= $saved_file->timestamp, t("Timestamp didn't go backwards."), 'File'); - $loaded_file = db_query('SELECT * FROM {file_managed} f WHERE f.fid = :fid', array(':fid' => $saved_file->fid))->fetch(PDO::FETCH_OBJ); - $this->assertNotNull($loaded_file, t("Record still exists in the database."), 'File'); - $this->assertEqual($loaded_file->status, $saved_file->status, t("Status was saved correctly.")); - } -} - -/** - * Tests file usage functions. - */ -class FileUsageTest extends FileTestCase { - public static function getInfo() { - return array( - 'name' => 'File usage', - 'description' => 'Tests the file usage functions.', - 'group' => 'File', - ); - } - - /** - * Tests file_usage_list(). - */ - function testGetUsage() { - $file = $this->createFile(); - db_insert('file_usage') - ->fields(array( - 'fid' => $file->fid, - 'module' => 'testing', - 'type' => 'foo', - 'id' => 1, - 'count' => 1 - )) - ->execute(); - db_insert('file_usage') - ->fields(array( - 'fid' => $file->fid, - 'module' => 'testing', - 'type' => 'bar', - 'id' => 2, - 'count' => 2 - )) - ->execute(); - - $usage = file_usage_list($file); - - $this->assertEqual(count($usage['testing']), 2, t('Returned the correct number of items.')); - $this->assertTrue(isset($usage['testing']['foo'][1]), t('Returned the correct id.')); - $this->assertTrue(isset($usage['testing']['bar'][2]), t('Returned the correct id.')); - $this->assertEqual($usage['testing']['foo'][1], 1, t('Returned the correct count.')); - $this->assertEqual($usage['testing']['bar'][2], 2, t('Returned the correct count.')); - } - - /** - * Tests file_usage_add(). - */ - function testAddUsage() { - $file = $this->createFile(); - file_usage_add($file, 'testing', 'foo', 1); - // Add the file twice to ensure that the count is incremented rather than - // creating additional records. - file_usage_add($file, 'testing', 'bar', 2); - file_usage_add($file, 'testing', 'bar', 2); - - $usage = db_select('file_usage', 'f') - ->fields('f') - ->condition('f.fid', $file->fid) - ->execute() - ->fetchAllAssoc('id'); - $this->assertEqual(count($usage), 2, t('Created two records')); - $this->assertEqual($usage[1]->module, 'testing', t('Correct module')); - $this->assertEqual($usage[2]->module, 'testing', t('Correct module')); - $this->assertEqual($usage[1]->type, 'foo', t('Correct type')); - $this->assertEqual($usage[2]->type, 'bar', t('Correct type')); - $this->assertEqual($usage[1]->count, 1, t('Correct count')); - $this->assertEqual($usage[2]->count, 2, t('Correct count')); - } - - /** - * Tests file_usage_delete(). - */ - function testRemoveUsage() { - $file = $this->createFile(); - db_insert('file_usage') - ->fields(array( - 'fid' => $file->fid, - 'module' => 'testing', - 'type' => 'bar', - 'id' => 2, - 'count' => 3, - )) - ->execute(); - - // Normal decrement. - file_usage_delete($file, 'testing', 'bar', 2); - $count = db_select('file_usage', 'f') - ->fields('f', array('count')) - ->condition('f.fid', $file->fid) - ->execute() - ->fetchField(); - $this->assertEqual(2, $count, t('The count was decremented correctly.')); - - // Multiple decrement and removal. - file_usage_delete($file, 'testing', 'bar', 2, 2); - $count = db_select('file_usage', 'f') - ->fields('f', array('count')) - ->condition('f.fid', $file->fid) - ->execute() - ->fetchField(); - $this->assertIdentical(FALSE, $count, t('The count was removed entirely when empty.')); - - // Non-existent decrement. - file_usage_delete($file, 'testing', 'bar', 2); - $count = db_select('file_usage', 'f') - ->fields('f', array('count')) - ->condition('f.fid', $file->fid) - ->execute() - ->fetchField(); - $this->assertIdentical(FALSE, $count, t('Decrementing non-exist record complete.')); - } -} - -/** - * Tests the file_validate() function.. - */ -class FileValidateTest extends FileHookTestCase { - public static function getInfo() { - return array( - 'name' => 'File validate', - 'description' => 'Tests the file_validate() function.', - 'group' => 'File API', - ); - } - - /** - * Test that the validators passed into are checked. - */ - function testCallerValidation() { - $file = $this->createFile(); - - // Empty validators. - $this->assertEqual(file_validate($file, array()), array(), t('Validating an empty array works successfully.')); - $this->assertFileHooksCalled(array('validate')); - - // Use the file_test.module's test validator to ensure that passing tests - // return correctly. - file_test_reset(); - file_test_set_return('validate', array()); - $passing = array('file_test_validator' => array(array())); - $this->assertEqual(file_validate($file, $passing), array(), t('Validating passes.')); - $this->assertFileHooksCalled(array('validate')); - - // Now test for failures in validators passed in and by hook_validate. - file_test_reset(); - file_test_set_return('validate', array('Epic fail')); - $failing = array('file_test_validator' => array(array('Failed', 'Badly'))); - $this->assertEqual(file_validate($file, $failing), array('Failed', 'Badly', 'Epic fail'), t('Validating returns errors.')); - $this->assertFileHooksCalled(array('validate')); - } -} - -/** - * Tests the file_save_data() function. - */ -class FileSaveDataTest extends FileHookTestCase { - public static function getInfo() { - return array( - 'name' => 'File save data', - 'description' => 'Tests the file save data function.', - 'group' => 'File API', - ); - } - - /** - * Test the file_save_data() function when no filename is provided. - */ - function testWithoutFilename() { - $contents = $this->randomName(8); - - $result = file_save_data($contents); - $this->assertTrue($result, t('Unnamed file saved correctly.')); - - $this->assertEqual(file_default_scheme(), file_uri_scheme($result->uri), t("File was placed in Drupal's files directory.")); - $this->assertEqual($result->filename, basename($result->uri), t("Filename was set to the file's basename.")); - $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of the file are correct.')); - $this->assertEqual($result->filemime, 'application/octet-stream', t('A MIME type was set.')); - $this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent.")); - - // Check that the correct hooks were called. - $this->assertFileHooksCalled(array('insert')); - - // Verify that what was returned is what's in the database. - $this->assertFileUnchanged($result, file_load($result->fid, TRUE)); - } - - /** - * Test the file_save_data() function when a filename is provided. - */ - function testWithFilename() { - $contents = $this->randomName(8); - - $result = file_save_data($contents, 'public://' . 'asdf.txt'); - $this->assertTrue($result, t('Unnamed file saved correctly.')); - - $this->assertEqual('public', file_uri_scheme($result->uri), t("File was placed in Drupal's files directory.")); - $this->assertEqual('asdf.txt', basename($result->uri), t('File was named correctly.')); - $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of the file are correct.')); - $this->assertEqual($result->filemime, 'text/plain', t('A MIME type was set.')); - $this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent.")); - - // Check that the correct hooks were called. - $this->assertFileHooksCalled(array('insert')); - - // Verify that what was returned is what's in the database. - $this->assertFileUnchanged($result, file_load($result->fid, TRUE)); - } - - /** - * Test file_save_data() when renaming around an existing file. - */ - function testExistingRename() { - // Setup a file to overwrite. - $existing = $this->createFile(); - $contents = $this->randomName(8); - - $result = file_save_data($contents, $existing->uri, FILE_EXISTS_RENAME); - $this->assertTrue($result, t("File saved successfully.")); - - $this->assertEqual('public', file_uri_scheme($result->uri), t("File was placed in Drupal's files directory.")); - $this->assertEqual($result->filename, $existing->filename, t("Filename was set to the basename of the source, rather than that of the renamed file.")); - $this->assertEqual($contents, file_get_contents($result->uri), t("Contents of the file are correct.")); - $this->assertEqual($result->filemime, 'application/octet-stream', t("A MIME type was set.")); - $this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent.")); - - // Check that the correct hooks were called. - $this->assertFileHooksCalled(array('insert')); - - // Ensure that the existing file wasn't overwritten. - $this->assertDifferentFile($existing, $result); - $this->assertFileUnchanged($existing, file_load($existing->fid, TRUE)); - - // Verify that was returned is what's in the database. - $this->assertFileUnchanged($result, file_load($result->fid, TRUE)); - } - - /** - * Test file_save_data() when replacing an existing file. - */ - function testExistingReplace() { - // Setup a file to overwrite. - $existing = $this->createFile(); - $contents = $this->randomName(8); - - $result = file_save_data($contents, $existing->uri, FILE_EXISTS_REPLACE); - $this->assertTrue($result, t('File saved successfully.')); - - $this->assertEqual('public', file_uri_scheme($result->uri), t("File was placed in Drupal's files directory.")); - $this->assertEqual($result->filename, $existing->filename, t('Filename was set to the basename of the existing file, rather than preserving the original name.')); - $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of the file are correct.')); - $this->assertEqual($result->filemime, 'application/octet-stream', t('A MIME type was set.')); - $this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent.")); - - // Check that the correct hooks were called. - $this->assertFileHooksCalled(array('load', 'update')); - - // Verify that the existing file was re-used. - $this->assertSameFile($existing, $result); - - // Verify that what was returned is what's in the database. - $this->assertFileUnchanged($result, file_load($result->fid, TRUE)); - } - - /** - * Test that file_save_data() fails overwriting an existing file. - */ - function testExistingError() { - $contents = $this->randomName(8); - $existing = $this->createFile(NULL, $contents); - - // Check the overwrite error. - $result = file_save_data('asdf', $existing->uri, FILE_EXISTS_ERROR); - $this->assertFalse($result, t('Overwriting a file fails when FILE_EXISTS_ERROR is specified.')); - $this->assertEqual($contents, file_get_contents($existing->uri), t('Contents of existing file were unchanged.')); - - // Check that no hooks were called while failing. - $this->assertFileHooksCalled(array()); - - // Ensure that the existing file wasn't overwritten. - $this->assertFileUnchanged($existing, file_load($existing->fid, TRUE)); - } -} - -/** - * Tests for download/file transfer functions. - */ -class FileDownloadTest extends FileTestCase { - public static function getInfo() { - return array( - 'name' => 'File download', - 'description' => 'Tests for file download/transfer functions.', - 'group' => 'File API', - ); - } - - function setUp() { - parent::setUp('file_test'); - // Clear out any hook calls. - file_test_reset(); - } - - /** - * Test the public file transfer system. - */ - function testPublicFileTransfer() { - // Test generating an URL to a created file. - $file = $this->createFile(); - $url = file_create_url($file->uri); - $this->assertEqual($GLOBALS['base_url'] . '/' . file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath() . '/' . $file->filename, $url, t('Correctly generated a URL for a created file.')); - $this->drupalHead($url); - $this->assertResponse(200, t('Confirmed that the generated URL is correct by downloading the created file.')); - - // Test generating an URL to a shipped file (i.e. a file that is part of - // Drupal core, a module or a theme, for example a JavaScript file). - $filepath = 'misc/jquery.js'; - $url = file_create_url($filepath); - $this->assertEqual($GLOBALS['base_url'] . '/' . $filepath, $url, t('Correctly generated a URL for a shipped file.')); - $this->drupalHead($url); - $this->assertResponse(200, t('Confirmed that the generated URL is correct by downloading the shipped file.')); - } - - /** - * Test the private file transfer system. - */ - function testPrivateFileTransfer() { - // Set file downloads to private so handler functions get called. - - // Create a file. - $file = $this->createFile(NULL, NULL, 'private'); - $url = file_create_url($file->uri); - - // Set file_test access header to allow the download. - file_test_set_return('download', array('x-foo' => 'Bar')); - $this->drupalHead($url); - $headers = $this->drupalGetHeaders(); - $this->assertEqual($headers['x-foo'] , 'Bar', t('Found header set by file_test module on private download.')); - $this->assertResponse(200, t('Correctly allowed access to a file when file_test provides headers.')); - - // Deny access to all downloads via a -1 header. - file_test_set_return('download', -1); - $this->drupalHead($url); - $this->assertResponse(403, t('Correctly denied access to a file when file_test sets the header to -1.')); - - // Try non-existent file. - $url = file_create_url('private://' . $this->randomName()); - $this->drupalHead($url); - $this->assertResponse(404, t('Correctly returned 404 response for a non-existent file.')); - } - - /** - * Test file_create_url(). - */ - function testFileCreateUrl() { - global $base_url; - - // Tilde (~) is excluded from this test because it is encoded by - // rawurlencode() in PHP 5.2 but not in PHP 5.3, as per RFC 3986. - // @see http://www.php.net/manual/en/function.rawurlencode.php#86506 - $basename = " -._!$'\"()*@[]?&+%#,;=:\n\x00" . // "Special" ASCII characters. - "%23%25%26%2B%2F%3F" . // Characters that look like a percent-escaped string. - "éøïвβ中國書۞"; // Characters from various non-ASCII alphabets. - $basename_encoded = '%20-._%21%24%27%22%28%29%2A%40%5B%5D%3F%26%2B%25%23%2C%3B%3D%3A__' . - '%2523%2525%2526%252B%252F%253F' . - '%C3%A9%C3%B8%C3%AF%D0%B2%CE%B2%E4%B8%AD%E5%9C%8B%E6%9B%B8%DB%9E'; - - $this->checkUrl('public', '', $basename, $base_url . '/' . file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath() . '/' . $basename_encoded); - $this->checkUrl('private', '', $basename, $base_url . '/system/files/' . $basename_encoded); - $this->checkUrl('private', '', $basename, $base_url . '/?q=system/files/' . $basename_encoded, '0'); - } - - /** - * Download a file from the URL generated by file_create_url(). - * - * Create a file with the specified scheme, directory and filename; check that - * the URL generated by file_create_url() for the specified file equals the - * specified URL; fetch the URL and then compare the contents to the file. - * - * @param $scheme - * A scheme, e.g. "public" - * @param $directory - * A directory, possibly "" - * @param $filename - * A filename - * @param $expected_url - * The expected URL - * @param $clean_url - * The value of the clean_url setting - */ - private function checkUrl($scheme, $directory, $filename, $expected_url, $clean_url = '1') { - variable_set('clean_url', $clean_url); - - // Convert $filename to a valid filename, i.e. strip characters not - // supported by the filesystem, and create the file in the specified - // directory. - $filepath = file_create_filename($filename, $directory); - $directory_uri = $scheme . '://' . dirname($filepath); - file_prepare_directory($directory_uri, FILE_CREATE_DIRECTORY); - $file = $this->createFile($filepath, NULL, $scheme); - - $url = file_create_url($file->uri); - $this->assertEqual($url, $expected_url, t('Generated URL matches expected URL.')); - - if ($scheme == 'private') { - // Tell the implementation of hook_file_download() in file_test.module - // that this file may be downloaded. - file_test_set_return('download', array('x-foo' => 'Bar')); - } - - $this->drupalGet($url); - if ($this->assertResponse(200) == 'pass') { - $this->assertRaw(file_get_contents($file->uri), t('Contents of the file are correct.')); - } - - file_delete($file); - } -} - -/** - * Tests for file URL rewriting. - */ -class FileURLRewritingTest extends FileTestCase { - public static function getInfo() { - return array( - 'name' => 'File URL rewriting', - 'description' => 'Tests for file URL rewriting.', - 'group' => 'File', - ); - } - - function setUp() { - parent::setUp('file_test'); - } - - /** - * Test the generating of rewritten shipped file URLs. - */ - function testShippedFileURL() { - // Test generating an URL to a shipped file (i.e. a file that is part of - // Drupal core, a module or a theme, for example a JavaScript file). - - // Test alteration of file URLs to use a CDN. - variable_set('file_test_hook_file_url_alter', 'cdn'); - $filepath = 'misc/jquery.js'; - $url = file_create_url($filepath); - $this->assertEqual(FILE_URL_TEST_CDN_1 . '/' . $filepath, $url, t('Correctly generated a CDN URL for a shipped file.')); - $filepath = 'misc/favicon.ico'; - $url = file_create_url($filepath); - $this->assertEqual(FILE_URL_TEST_CDN_2 . '/' . $filepath, $url, t('Correctly generated a CDN URL for a shipped file.')); - - // Test alteration of file URLs to use root-relative URLs. - variable_set('file_test_hook_file_url_alter', 'root-relative'); - $filepath = 'misc/jquery.js'; - $url = file_create_url($filepath); - $this->assertEqual(base_path() . '/' . $filepath, $url, t('Correctly generated a root-relative URL for a shipped file.')); - $filepath = 'misc/favicon.ico'; - $url = file_create_url($filepath); - $this->assertEqual(base_path() . '/' . $filepath, $url, t('Correctly generated a root-relative URL for a shipped file.')); - - // Test alteration of file URLs to use protocol-relative URLs. - variable_set('file_test_hook_file_url_alter', 'protocol-relative'); - $filepath = 'misc/jquery.js'; - $url = file_create_url($filepath); - $this->assertEqual('/' . base_path() . '/' . $filepath, $url, t('Correctly generated a protocol-relative URL for a shipped file.')); - $filepath = 'misc/favicon.ico'; - $url = file_create_url($filepath); - $this->assertEqual('/' . base_path() . '/' . $filepath, $url, t('Correctly generated a protocol-relative URL for a shipped file.')); - } - - /** - * Test the generating of rewritten public created file URLs. - */ - function testPublicCreatedFileURL() { - // Test generating an URL to a created file. - - // Test alteration of file URLs to use a CDN. - variable_set('file_test_hook_file_url_alter', 'cdn'); - $file = $this->createFile(); - $url = file_create_url($file->uri); - $public_directory_path = file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath(); - $this->assertEqual(FILE_URL_TEST_CDN_2 . '/' . $public_directory_path . '/' . $file->filename, $url, t('Correctly generated a CDN URL for a created file.')); - - // Test alteration of file URLs to use root-relative URLs. - variable_set('file_test_hook_file_url_alter', 'root-relative'); - $file = $this->createFile(); - $url = file_create_url($file->uri); - $this->assertEqual(base_path() . '/' . $public_directory_path . '/' . $file->filename, $url, t('Correctly generated a root-relative URL for a created file.')); - - // Test alteration of file URLs to use a protocol-relative URLs. - variable_set('file_test_hook_file_url_alter', 'protocol-relative'); - $file = $this->createFile(); - $url = file_create_url($file->uri); - $this->assertEqual('/' . base_path() . '/' . $public_directory_path . '/' . $file->filename, $url, t('Correctly generated a protocol-relative URL for a created file.')); - } -} - -/** - * Tests for file_munge_filename() and file_unmunge_filename(). - */ -class FileNameMungingTest extends FileTestCase { - public static function getInfo() { - return array( - 'name' => 'File naming', - 'description' => 'Test filename munging and unmunging.', - 'group' => 'File API', - ); - } - - function setUp() { - parent::setUp(); - $this->bad_extension = 'php'; - $this->name = $this->randomName() . '.' . $this->bad_extension . '.txt'; - } - - /** - * Create a file and munge/unmunge the name. - */ - function testMunging() { - // Disable insecure uploads. - variable_set('allow_insecure_uploads', 0); - $munged_name = file_munge_filename($this->name, '', TRUE); - $messages = drupal_get_messages(); - $this->assertTrue(in_array(t('For security reasons, your upload has been renamed to %filename.', array('%filename' => $munged_name)), $messages['status']), t('Alert properly set when a file is renamed.')); - $this->assertNotEqual($munged_name, $this->name, t('The new filename (%munged) has been modified from the original (%original)', array('%munged' => $munged_name, '%original' => $this->name))); - } - - /** - * If the allow_insecure_uploads variable evaluates to true, the file should - * come out untouched, no matter how evil the filename. - */ - function testMungeIgnoreInsecure() { - variable_set('allow_insecure_uploads', 1); - $munged_name = file_munge_filename($this->name, ''); - $this->assertIdentical($munged_name, $this->name, t('The original filename (%original) matches the munged filename (%munged) when insecure uploads are enabled.', array('%munged' => $munged_name, '%original' => $this->name))); - } - - /** - * White listed extensions are ignored by file_munge_filename(). - */ - function testMungeIgnoreWhitelisted() { - // Declare our extension as whitelisted. - $munged_name = file_munge_filename($this->name, $this->bad_extension); - $this->assertIdentical($munged_name, $this->name, t('The new filename (%munged) matches the original (%original) once the extension has been whitelisted.', array('%munged' => $munged_name, '%original' => $this->name))); - } - - /** - * Ensure that unmunge gets your name back. - */ - function testUnMunge() { - $munged_name = file_munge_filename($this->name, '', FALSE); - $unmunged_name = file_unmunge_filename($munged_name); - $this->assertIdentical($unmunged_name, $this->name, t('The unmunged (%unmunged) filename matches the original (%original)', array('%unmunged' => $unmunged_name, '%original' => $this->name))); - } -} - -/** - * Tests for file_get_mimetype(). - */ -class FileMimeTypeTest extends DrupalWebTestCase { - function setUp() { - parent::setUp('file_test'); - } - - public static function getInfo() { - return array( - 'name' => 'File mimetypes', - 'description' => 'Test filename mimetype detection.', - 'group' => 'File API', - ); - } - - /** - * Test mapping of mimetypes from filenames. - */ - public function testFileMimeTypeDetection() { - $prefix = 'simpletest://'; - - $test_case = array( - 'test.jar' => 'application/java-archive', - 'test.jpeg' => 'image/jpeg', - 'test.JPEG' => 'image/jpeg', - 'test.jpg' => 'image/jpeg', - 'test.jar.jpg' => 'image/jpeg', - 'test.jpg.jar' => 'application/java-archive', - 'test.pcf.Z' => 'application/x-font', - 'pcf.z' => 'application/octet-stream', - 'jar' => 'application/octet-stream', - 'some.junk' => 'application/octet-stream', - 'foo.file_test_1' => 'madeup/file_test_1', - 'foo.file_test_2' => 'madeup/file_test_2', - 'foo.doc' => 'madeup/doc', - 'test.ogg' => 'audio/ogg', - ); - - // Test using default mappings. - foreach ($test_case as $input => $expected) { - // Test stream [URI]. - $output = file_get_mimetype($prefix . $input); - $this->assertIdentical($output, $expected, t('Mimetype for %input is %output (expected: %expected).', array('%input' => $input, '%output' => $output, '%expected' => $expected))); - - // Test normal path equivalent - $output = file_get_mimetype($input); - $this->assertIdentical($output, $expected, t('Mimetype (using default mappings) for %input is %output (expected: %expected).', array('%input' => $input, '%output' => $output, '%expected' => $expected))); - } - - // Now test passing in the map. - $mapping = array( - 'mimetypes' => array( - 0 => 'application/java-archive', - 1 => 'image/jpeg', - ), - 'extensions' => array( - 'jar' => 0, - 'jpg' => 1, - ) - ); - - $test_case = array( - 'test.jar' => 'application/java-archive', - 'test.jpeg' => 'application/octet-stream', - 'test.jpg' => 'image/jpeg', - 'test.jar.jpg' => 'image/jpeg', - 'test.jpg.jar' => 'application/java-archive', - 'test.pcf.z' => 'application/octet-stream', - 'pcf.z' => 'application/octet-stream', - 'jar' => 'application/octet-stream', - 'some.junk' => 'application/octet-stream', - 'foo.file_test_1' => 'application/octet-stream', - 'foo.file_test_2' => 'application/octet-stream', - 'foo.doc' => 'application/octet-stream', - 'test.ogg' => 'application/octet-stream', - ); - - foreach ($test_case as $input => $expected) { - $output = file_get_mimetype($input, $mapping); - $this->assertIdentical($output, $expected, t('Mimetype (using passed-in mappings) for %input is %output (expected: %expected).', array('%input' => $input, '%output' => $output, '%expected' => $expected))); - } - } -} - -/** - * Tests stream wrapper functions. - */ -class StreamWrapperTest extends DrupalWebTestCase { - - protected $scheme = 'dummy'; - protected $classname = 'DrupalDummyStreamWrapper'; - - public static function getInfo() { - return array( - 'name' => 'Stream wrappers', - 'description' => 'Tests stream wrapper functions.', - 'group' => 'File API', - ); - } - - function setUp() { - parent::setUp('file_test'); - drupal_static_reset('file_get_stream_wrappers'); - } - - function tearDown() { - parent::tearDown(); - stream_wrapper_unregister($this->scheme); - } - - /** - * Test the getClassName() function. - */ - function testGetClassName() { - // Check the dummy scheme. - $this->assertEqual($this->classname, file_stream_wrapper_get_class($this->scheme), t('Got correct class name for dummy scheme.')); - // Check core's scheme. - $this->assertEqual('DrupalPublicStreamWrapper', file_stream_wrapper_get_class('public'), t('Got correct class name for public scheme.')); - } - - /** - * Test the file_stream_wrapper_get_instance_by_scheme() function. - */ - function testGetInstanceByScheme() { - $instance = file_stream_wrapper_get_instance_by_scheme($this->scheme); - $this->assertEqual($this->classname, get_class($instance), t('Got correct class type for dummy scheme.')); - - $instance = file_stream_wrapper_get_instance_by_scheme('public'); - $this->assertEqual('DrupalPublicStreamWrapper', get_class($instance), t('Got correct class type for public scheme.')); - } - - /** - * Test the URI and target functions. - */ - function testUriFunctions() { - $instance = file_stream_wrapper_get_instance_by_uri($this->scheme . '://foo'); - $this->assertEqual($this->classname, get_class($instance), t('Got correct class type for dummy URI.')); - - $instance = file_stream_wrapper_get_instance_by_uri('public://foo'); - $this->assertEqual('DrupalPublicStreamWrapper', get_class($instance), t('Got correct class type for public URI.')); - - // Test file_uri_target(). - $this->assertEqual(file_uri_target('public://foo/bar.txt'), 'foo/bar.txt', t('Got a valid stream target from public://foo/bar.txt.')); - $this->assertFalse(file_uri_target('foo/bar.txt'), t('foo/bar.txt is not a valid stream.')); - - // Test file_build_uri() and DrupalLocalStreamWrapper::getDirectoryPath(). - $this->assertEqual(file_build_uri('foo/bar.txt'), 'public://foo/bar.txt', t('Expected scheme was added.')); - $this->assertEqual(file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath(), variable_get('file_public_path'), t('Expected default directory path was returned.')); - $this->assertEqual(file_stream_wrapper_get_instance_by_scheme('temporary')->getDirectoryPath(), variable_get('file_temporary_path'), t('Expected temporary directory path was returned.')); - - variable_set('file_default_scheme', 'private'); - $this->assertEqual(file_build_uri('foo/bar.txt'), 'private://foo/bar.txt', t('Got a valid URI from foo/bar.txt.')); - } - - /** - * Test the scheme functions. - */ - function testGetValidStreamScheme() { - $this->assertEqual('foo', file_uri_scheme('foo://pork//chops'), t('Got the correct scheme from foo://asdf')); - $this->assertTrue(file_stream_wrapper_valid_scheme(file_uri_scheme('public://asdf')), t('Got a valid stream scheme from public://asdf')); - $this->assertFalse(file_stream_wrapper_valid_scheme(file_uri_scheme('foo://asdf')), t('Did not get a valid stream scheme from foo://asdf')); - } -} diff --git a/modules/simpletest/tests/file_test.info b/modules/simpletest/tests/file_test.info deleted file mode 100644 index b71f2a028a9..00000000000 --- a/modules/simpletest/tests/file_test.info +++ /dev/null @@ -1,7 +0,0 @@ -name = "File test" -description = "Support module for file handling tests." -package = Testing -version = VERSION -core = 8.x -files[] = file_test.module -hidden = TRUE diff --git a/modules/simpletest/tests/file_test.module b/modules/simpletest/tests/file_test.module deleted file mode 100644 index b3c43e071bb..00000000000 --- a/modules/simpletest/tests/file_test.module +++ /dev/null @@ -1,461 +0,0 @@ -<?php - -/** - * @file - * Helper module for the file tests. - * - * The caller is must call file_test_reset() to initializing this module before - * calling file_test_get_calls() or file_test_set_return(). - */ - - -define('FILE_URL_TEST_CDN_1', 'http://cdn1.example.com'); -define('FILE_URL_TEST_CDN_2', 'http://cdn2.example.com'); - - -/** - * Implements hook_menu(). - */ -function file_test_menu() { - $items['file-test/upload'] = array( - 'title' => 'Upload test', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('_file_test_form'), - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - return $items; -} - -/** - * Implements hook_stream_wrappers(). - */ -function file_test_stream_wrappers() { - return array( - 'dummy' => array( - 'name' => t('Dummy files'), - 'class' => 'DrupalDummyStreamWrapper', - 'description' => t('Dummy wrapper for simpletest.'), - ), - 'dummy-remote' => array( - 'name' => t('Dummy files (remote)'), - 'class' => 'DrupalDummyRemoteStreamWrapper', - 'description' => t('Dummy wrapper for simpletest (remote).'), - ), - ); -} - -/** - * Form to test file uploads. - */ -function _file_test_form($form, &$form_state) { - $form['file_test_upload'] = array( - '#type' => 'file', - '#title' => t('Upload a file'), - ); - $form['file_test_replace'] = array( - '#type' => 'select', - '#title' => t('Replace existing image'), - '#options' => array( - FILE_EXISTS_RENAME => t('Appends number until name is unique'), - FILE_EXISTS_REPLACE => t('Replace the existing file'), - FILE_EXISTS_ERROR => t('Fail with an error'), - ), - '#default_value' => FILE_EXISTS_RENAME, - ); - $form['file_subdir'] = array( - '#type' => 'textfield', - '#title' => t('Subdirectory for test file'), - '#default_value' => '', - ); - - $form['extensions'] = array( - '#type' => 'textfield', - '#title' => t('Allowed extensions.'), - '#default_value' => '', - ); - - $form['allow_all_extensions'] = array( - '#type' => 'checkbox', - '#title' => t('Allow all extensions?'), - '#default_value' => FALSE, - ); - - $form['is_image_file'] = array( - '#type' => 'checkbox', - '#title' => t('Is this an image file?'), - '#default_value' => TRUE, - ); - - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Submit'), - ); - return $form; -} - -/** - * Process the upload. - */ -function _file_test_form_submit(&$form, &$form_state) { - // Process the upload and perform validation. Note: we're using the - // form value for the $replace parameter. - if (!empty($form_state['values']['file_subdir'])) { - $destination = 'temporary://' . $form_state['values']['file_subdir']; - file_prepare_directory($destination, FILE_CREATE_DIRECTORY); - } - else { - $destination = FALSE; - } - - // Setup validators. - $validators = array(); - if ($form_state['values']['is_image_file']) { - $validators['file_validate_is_image'] = array(); - } - - if ($form_state['values']['allow_all_extensions']) { - $validators['file_validate_extensions'] = array(); - } - elseif (!empty($form_state['values']['extensions'])) { - $validators['file_validate_extensions'] = array($form_state['values']['extensions']); - } - - $file = file_save_upload('file_test_upload', $validators, $destination, $form_state['values']['file_test_replace']); - if ($file) { - $form_state['values']['file_test_upload'] = $file; - drupal_set_message(t('File @filepath was uploaded.', array('@filepath' => $file->uri))); - drupal_set_message(t('File name is @filename.', array('@filename' => $file->filename))); - drupal_set_message(t('File MIME type is @mimetype.', array('@mimetype' => $file->filemime))); - drupal_set_message(t('You WIN!')); - } - elseif ($file === FALSE) { - drupal_set_message(t('Epic upload FAIL!'), 'error'); - } -} - - -/** - * Reset/initialize the history of calls to the file_* hooks. - * - * @see file_test_get_calls() - * @see file_test_reset() - */ -function file_test_reset() { - // Keep track of calls to these hooks - $results = array( - 'load' => array(), - 'validate' => array(), - 'download' => array(), - 'insert' => array(), - 'update' => array(), - 'copy' => array(), - 'move' => array(), - 'delete' => array(), - ); - variable_set('file_test_results', $results); - - // These hooks will return these values, see file_test_set_return(). - $return = array( - 'validate' => array(), - 'download' => NULL, - ); - variable_set('file_test_return', $return); -} - -/** - * Get the arguments passed to invocation of a given hook since - * file_test_reset() was last called. - * - * @param $op - * One of the hook_file_* operations: 'load', 'validate', 'download', - * 'insert', 'update', 'copy', 'move', 'delete'. - * - * @return - * Array of the parameters passed to each call. - * - * @see _file_test_log_call() - * @see file_test_reset() - */ -function file_test_get_calls($op) { - $results = variable_get('file_test_results', array()); - return $results[$op]; -} - -/** - * Get an array with the calls for all hooks. - * - * @return - * An array keyed by hook name ('load', 'validate', 'download', 'insert', - * 'update', 'copy', 'move', 'delete') with values being arrays of parameters - * passed to each call. - */ -function file_test_get_all_calls() { - return variable_get('file_test_results', array()); -} - -/** - * Store the values passed to a hook invocation. - * - * @param $op - * One of the hook_file_* operations: 'load', 'validate', 'download', - * 'insert', 'update', 'copy', 'move', 'delete'. - * @param $args - * Values passed to hook. - * - * @see file_test_get_calls() - * @see file_test_reset() - */ -function _file_test_log_call($op, $args) { - $results = variable_get('file_test_results', array()); - $results[$op][] = $args; - variable_set('file_test_results', $results); -} - -/** - * Load the appropriate return value. - * - * @param $op - * One of the hook_file_[validate,download] operations. - * - * @return - * Value set by file_test_set_return(). - * - * @see file_test_set_return() - * @see file_test_reset() - */ -function _file_test_get_return($op) { - $return = variable_get('file_test_return', array($op => NULL)); - return $return[$op]; -} - -/** - * Assign a return value for a given operation. - * - * @param $op - * One of the hook_file_[validate,download] operations. - * @param $value - * Value for the hook to return. - * - * @see _file_test_get_return() - * @see file_test_reset() - */ -function file_test_set_return($op, $value) { - $return = variable_get('file_test_return', array()); - $return[$op] = $value; - variable_set('file_test_return', $return); -} - -/** - * Implements hook_file_load(). - */ -function file_test_file_load($files) { - foreach ($files as $file) { - _file_test_log_call('load', array($file)); - // Assign a value on the object so that we can test that the $file is passed - // by reference. - $file->file_test['loaded'] = TRUE; - } -} - -/** - * Implements hook_file_validate(). - */ -function file_test_file_validate($file) { - _file_test_log_call('validate', array($file)); - return _file_test_get_return('validate'); -} - -/** - * Implements hook_file_download(). - */ -function file_test_file_download($uri) { - _file_test_log_call('download', array($uri)); - return _file_test_get_return('download'); -} - -/** - * Implements hook_file_insert(). - */ -function file_test_file_insert($file) { - _file_test_log_call('insert', array($file)); -} - -/** - * Implements hook_file_update(). - */ -function file_test_file_update($file) { - _file_test_log_call('update', array($file)); -} - -/** - * Implements hook_file_copy(). - */ -function file_test_file_copy($file, $source) { - _file_test_log_call('copy', array($file, $source)); -} - -/** - * Implements hook_file_move(). - */ -function file_test_file_move($file, $source) { - _file_test_log_call('move', array($file, $source)); -} - -/** - * Implements hook_file_delete(). - */ -function file_test_file_delete($file) { - _file_test_log_call('delete', array($file)); -} - -/** - * Implements hook_file_url_alter(). - */ -function file_test_file_url_alter(&$uri) { - // Only run this hook when this variable is set. Otherwise, we'd have to add - // another hidden test module just for this hook. - $alter_mode = variable_get('file_test_hook_file_url_alter', FALSE); - if (!$alter_mode) { - return; - } - // Test alteration of file URLs to use a CDN. - elseif ($alter_mode == 'cdn') { - $cdn_extensions = array('css', 'js', 'gif', 'jpg', 'jpeg', 'png'); - - // Most CDNs don't support private file transfers without a lot of hassle, - // so don't support this in the common case. - $schemes = array('public'); - - $scheme = file_uri_scheme($uri); - - // Only serve shipped files and public created files from the CDN. - if (!$scheme || in_array($scheme, $schemes)) { - // Shipped files. - if (!$scheme) { - $path = $uri; - } - // Public created files. - else { - $wrapper = file_stream_wrapper_get_instance_by_scheme($scheme); - $path = $wrapper->getDirectoryPath() . '/' . file_uri_target($uri); - } - - // Clean up Windows paths. - $path = str_replace('\\', '/', $path); - - // Serve files with one of the CDN extensions from CDN 1, all others from - // CDN 2. - $pathinfo = pathinfo($path); - if (array_key_exists('extension', $pathinfo) && in_array($pathinfo['extension'], $cdn_extensions)) { - $uri = FILE_URL_TEST_CDN_1 . '/' . $path; - } - else { - $uri = FILE_URL_TEST_CDN_2 . '/' . $path; - } - } - } - // Test alteration of file URLs to use root-relative URLs. - elseif ($alter_mode == 'root-relative') { - // Only serve shipped files and public created files with root-relative - // URLs. - $scheme = file_uri_scheme($uri); - if (!$scheme || $scheme == 'public') { - // Shipped files. - if (!$scheme) { - $path = $uri; - } - // Public created files. - else { - $wrapper = file_stream_wrapper_get_instance_by_scheme($scheme); - $path = $wrapper->getDirectoryPath() . '/' . file_uri_target($uri); - } - - // Clean up Windows paths. - $path = str_replace('\\', '/', $path); - - // Generate a root-relative URL. - $uri = base_path() . '/' . $path; - } - } - // Test alteration of file URLs to use protocol-relative URLs. - elseif ($alter_mode == 'protocol-relative') { - // Only serve shipped files and public created files with protocol-relative - // URLs. - $scheme = file_uri_scheme($uri); - if (!$scheme || $scheme == 'public') { - // Shipped files. - if (!$scheme) { - $path = $uri; - } - // Public created files. - else { - $wrapper = file_stream_wrapper_get_instance_by_scheme($scheme); - $path = $wrapper->getDirectoryPath() . '/' . file_uri_target($uri); - } - - // Clean up Windows paths. - $path = str_replace('\\', '/', $path); - - // Generate a protocol-relative URL. - $uri = '/' . base_path() . '/' . $path; - } - } -} - -/** - * Implements hook_file_mimetype_mapping_alter(). - */ -function file_test_file_mimetype_mapping_alter(&$mapping) { - // Add new mappings. - $mapping['mimetypes']['file_test_mimetype_1'] = 'madeup/file_test_1'; - $mapping['mimetypes']['file_test_mimetype_2'] = 'madeup/file_test_2'; - $mapping['mimetypes']['file_test_mimetype_3'] = 'madeup/doc'; - $mapping['extensions']['file_test_1'] = 'file_test_mimetype_1'; - $mapping['extensions']['file_test_2'] = 'file_test_mimetype_2'; - $mapping['extensions']['file_test_3'] = 'file_test_mimetype_2'; - // Override existing mapping. - $mapping['extensions']['doc'] = 'file_test_mimetype_3'; -} - -/** - * Helper class for testing the stream wrapper registry. - * - * Dummy stream wrapper implementation (dummy://). - */ -class DrupalDummyStreamWrapper extends DrupalLocalStreamWrapper { - function getDirectoryPath() { - return variable_get('stream_public_path', 'sites/default/files'); - } - - /** - * Override getInternalUri(). - * - * Return a dummy path for testing. - */ - function getInternalUri() { - return '/dummy/example.txt'; - } - - /** - * Override getExternalUrl(). - * - * Return the HTML URI of a public file. - */ - function getExternalUrl() { - return '/dummy/example.txt'; - } -} - -/** - * Helper class for testing the stream wrapper registry. - * - * Dummy remote stream wrapper implementation (dummy-remote://). - * - * Basically just the public scheme but not returning a local file for realpath. - */ -class DrupalDummyRemoteStreamWrapper extends DrupalPublicStreamWrapper { - function realpath() { - return FALSE; - } -} diff --git a/modules/simpletest/tests/filetransfer.test b/modules/simpletest/tests/filetransfer.test deleted file mode 100644 index 905d23cab7f..00000000000 --- a/modules/simpletest/tests/filetransfer.test +++ /dev/null @@ -1,168 +0,0 @@ -<?php - - -class FileTranferTest extends DrupalWebTestCase { - protected $hostname = 'localhost'; - protected $username = 'drupal'; - protected $password = 'password'; - protected $port = '42'; - - public static function getInfo() { - return array( - 'name' => 'FileTransfer unit tests', - 'description' => 'Test that the jail is respected and that protocols using recursive file move operations work.', - 'group' => 'System' - ); - } - - function setUp() { - parent::setUp(); - $this->testConnection = TestFileTransfer::factory(DRUPAL_ROOT, array('hostname' => $this->hostname, 'username' => $this->username, 'password' => $this->password, 'port' => $this->port)); - } - - function _getFakeModuleFiles() { - $files = array( - 'fake.module', - 'fake.info', - 'theme' => array( - 'fake.tpl.php' - ), - 'inc' => array( - 'fake.inc' - ) - ); - return $files; - } - - function _buildFakeModule() { - $location = 'temporary://fake'; - if (is_dir($location)) { - $ret = 0; - $output = array(); - exec('rm -Rf ' . escapeshellarg($location), $output, $ret); - if ($ret != 0) { - throw new Exception('Error removing fake module directory.'); - } - } - - $files = $this->_getFakeModuleFiles(); - $this->_writeDirectory($location, $files); - return $location; - } - - function _writeDirectory($base, $files = array()) { - mkdir($base); - foreach ($files as $key => $file) { - if (is_array($file)) { - $this->_writeDirectory($base . DIRECTORY_SEPARATOR . $key, $file); - } - else { - //just write the filename into the file - file_put_contents($base . DIRECTORY_SEPARATOR . $file, $file); - } - } - } - - function testJail() { - $source = $this->_buildFakeModule(); - - // This convoluted piece of code is here because our testing framework does - // not support expecting exceptions. - $gotit = FALSE; - try { - $this->testConnection->copyDirectory($source, '/tmp'); - } - catch (FileTransferException $e) { - $gotit = TRUE; - } - $this->assertTrue($gotit, 'Was not able to copy a directory outside of the jailed area.'); - - $gotit = TRUE; - try { - $this->testConnection->copyDirectory($source, DRUPAL_ROOT . '/'. variable_get('file_public_path', conf_path() . '/files')); - } - catch (FileTransferException $e) { - $gotit = FALSE; - } - $this->assertTrue($gotit, 'Was able to copy a directory inside of the jailed area'); - } -} - -/** - * Mock FileTransfer object for test case. - */ -class TestFileTransfer extends FileTransfer { - protected $host = NULL; - protected $username = NULL; - protected $password = NULL; - protected $port = NULL; - - /** - * This is for testing the CopyRecursive logic. - */ - public $shouldIsDirectoryReturnTrue = FALSE; - - function __construct($jail, $username, $password, $hostname = 'localhost', $port = 9999) { - parent::__construct($jail, $username, $password, $hostname, $port); - } - - static function factory($jail, $settings) { - return new TestFileTransfer($jail, $settings['username'], $settings['password'], $settings['hostname'], $settings['port']); - } - - function connect() { - $parts = explode(':', $this->hostname); - $port = (count($parts) == 2) ? $parts[1] : $this->port; - $this->connection = new MockTestConnection(); - $this->connection->connectionString = 'test://' . urlencode($this->username) . ':' . urlencode($this->password) . "@$this->host:$this->port/"; - } - - function copyFileJailed($source, $destination) { - $this->connection->run("copyFile $source $destination"); - } - - protected function removeDirectoryJailed($directory) { - $this->connection->run("rmdir $directory"); - } - - function createDirectoryJailed($directory) { - $this->connection->run("mkdir $directory"); - } - - function removeFileJailed($destination) { - if (!ftp_delete($this->connection, $item)) { - throw new FileTransferException('Unable to remove to file @file.', NULL, array('@file' => $item)); - } - } - - function isDirectory($path) { - return $this->shouldIsDirectoryReturnTrue; - } - - function isFile($path) { - return FALSE; - } - - function chmodJailed($path, $mode, $recursive) { - return; - } -} - -/** - * Mock connection object for test case. - */ -class MockTestConnection { - - var $commandsRun = array(); - var $connectionString; - - function run($cmd) { - $this->commandsRun[] = $cmd; - } - - function flushCommands() { - $out = $this->commandsRun; - $this->commandsRun = array(); - return $out; - } -} diff --git a/modules/simpletest/tests/filter_test.info b/modules/simpletest/tests/filter_test.info deleted file mode 100644 index ee27c29d13d..00000000000 --- a/modules/simpletest/tests/filter_test.info +++ /dev/null @@ -1,6 +0,0 @@ -name = Filter test module -description = Tests filter hooks and functions. -package = Testing -version = VERSION -core = 8.x -hidden = TRUE diff --git a/modules/simpletest/tests/filter_test.module b/modules/simpletest/tests/filter_test.module deleted file mode 100644 index 2cebc7085d0..00000000000 --- a/modules/simpletest/tests/filter_test.module +++ /dev/null @@ -1,62 +0,0 @@ -<?php - -/** - * @file - * Test module for Filter module hooks and functions not used in core. - */ - -/** - * Implements hook_filter_format_insert(). - */ -function filter_test_filter_format_insert($format) { - drupal_set_message('hook_filter_format_insert invoked.'); -} - -/** - * Implements hook_filter_format_update(). - */ -function filter_test_filter_format_update($format) { - drupal_set_message('hook_filter_format_update invoked.'); -} - -/** - * Implements hook_filter_format_disable(). - */ -function filter_test_filter_format_disable($format) { - drupal_set_message('hook_filter_format_disable invoked.'); -} - -/** - * Implements hook_filter_info(). - */ -function filter_test_filter_info() { - $filters['filter_test_uncacheable'] = array( - 'title' => 'Uncacheable filter', - 'description' => 'Does nothing, but makes a text format uncacheable.', - 'cache' => FALSE, - ); - $filters['filter_test_replace'] = array( - 'title' => 'Testing filter', - 'description' => 'Replaces all content with filter and text format information.', - 'process callback' => 'filter_test_replace', - ); - return $filters; -} - -/** - * Process handler for filter_test_replace filter. - * - * Replaces all text with filter and text format information. - */ -function filter_test_replace($text, $filter, $format, $langcode, $cache, $cache_id) { - $text = array(); - $text[] = 'Filter: ' . $filter->title . ' (' . $filter->name . ')'; - $text[] = 'Format: ' . $format->name . ' (' . $format->format . ')'; - $text[] = 'Language: ' . $langcode; - $text[] = 'Cache: ' . ($cache ? 'Enabled' : 'Disabled'); - if ($cache_id) { - $text[] = 'Cache ID: ' . $cache_id; - } - return implode("<br />\n", $text); -} - diff --git a/modules/simpletest/tests/form.test b/modules/simpletest/tests/form.test deleted file mode 100644 index 386880e78a1..00000000000 --- a/modules/simpletest/tests/form.test +++ /dev/null @@ -1,1574 +0,0 @@ -<?php - -/** - * @file - * Unit tests for the Drupal Form API. - */ - -class FormsTestCase extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Form element validation', - 'description' => 'Tests various form element validation mechanisms.', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - } - - /** - * Check several empty values for required forms elements. - * - * Carriage returns, tabs, spaces, and unchecked checkbox elements are not - * valid content for a required field. - * - * If the form field is found in form_get_errors() then the test pass. - */ - function testRequiredFields() { - // Originates from http://drupal.org/node/117748 - // Sets of empty strings and arrays. - $empty_strings = array('""' => "", '"\n"' => "\n", '" "' => " ", '"\t"' => "\t", '" \n\t "' => " \n\t ", '"\n\n\n\n\n"' => "\n\n\n\n\n"); - $empty_arrays = array('array()' => array()); - $empty_checkbox = array(NULL); - - $elements['textfield']['element'] = array('#title' => $this->randomName(), '#type' => 'textfield'); - $elements['textfield']['empty_values'] = $empty_strings; - - $elements['password']['element'] = array('#title' => $this->randomName(), '#type' => 'password'); - $elements['password']['empty_values'] = $empty_strings; - - $elements['password_confirm']['element'] = array('#title' => $this->randomName(), '#type' => 'password_confirm'); - // Provide empty values for both password fields. - foreach ($empty_strings as $key => $value) { - $elements['password_confirm']['empty_values'][$key] = array('pass1' => $value, 'pass2' => $value); - } - - $elements['textarea']['element'] = array('#title' => $this->randomName(), '#type' => 'textarea'); - $elements['textarea']['empty_values'] = $empty_strings; - - $elements['radios']['element'] = array('#title' => $this->randomName(), '#type' => 'radios', '#options' => array('' => t('None'), $this->randomName(), $this->randomName(), $this->randomName())); - $elements['radios']['empty_values'] = $empty_arrays; - - $elements['checkbox']['element'] = array('#title' => $this->randomName(), '#type' => 'checkbox', '#required' => TRUE, '#title' => $this->randomName()); - $elements['checkbox']['empty_values'] = $empty_checkbox; - - $elements['checkboxes']['element'] = array('#title' => $this->randomName(), '#type' => 'checkboxes', '#options' => array($this->randomName(), $this->randomName(), $this->randomName())); - $elements['checkboxes']['empty_values'] = $empty_arrays; - - $elements['select']['element'] = array('#title' => $this->randomName(), '#type' => 'select', '#options' => array('' => t('None'), $this->randomName(), $this->randomName(), $this->randomName())); - $elements['select']['empty_values'] = $empty_strings; - - $elements['file']['element'] = array('#title' => $this->randomName(), '#type' => 'file'); - $elements['file']['empty_values'] = $empty_strings; - - // Regular expression to find the expected marker on required elements. - $required_marker_preg = '@<label.*<abbr class="form-required" title="This field is required\.">\*</abbr></label>@'; - - // Go through all the elements and all the empty values for them. - foreach ($elements as $type => $data) { - foreach ($data['empty_values'] as $key => $empty) { - foreach (array(TRUE, FALSE) as $required) { - $form_id = $this->randomName(); - $form = array(); - $form_state = form_state_defaults(); - form_clear_error(); - $form['op'] = array('#type' => 'submit', '#value' => t('Submit')); - $element = $data['element']['#title']; - $form[$element] = $data['element']; - $form[$element]['#required'] = $required; - $form_state['input'][$element] = $empty; - $form_state['input']['form_id'] = $form_id; - $form_state['method'] = 'post'; - drupal_prepare_form($form_id, $form, $form_state); - drupal_process_form($form_id, $form, $form_state); - $errors = form_get_errors(); - // Form elements of type 'radios' throw all sorts of PHP notices - // when you try to render them like this, so we ignore those for - // testing the required marker. - // @todo Fix this work-around (http://drupal.org/node/588438). - $form_output = ($type == 'radios') ? '' : drupal_render($form); - if ($required) { - // Make sure we have a form error for this element. - $this->assertTrue(isset($errors[$element]), "Check empty($key) '$type' field '$element'"); - if (!empty($form_output)) { - // Make sure the form element is marked as required. - $this->assertTrue(preg_match($required_marker_preg, $form_output), "Required '$type' field is marked as required"); - } - } - else { - if (!empty($form_output)) { - // Make sure the form element is *not* marked as required. - $this->assertFalse(preg_match($required_marker_preg, $form_output), "Optional '$type' field is not marked as required"); - } - if ($type == 'select') { - // Select elements are going to have validation errors with empty - // input, since those are illegal choices. Just make sure the - // error is not "field is required". - $this->assertTrue((empty($errors[$element]) || strpos('field is required', $errors[$element]) === FALSE), "Optional '$type' field '$element' is not treated as a required element"); - } - else { - // Make sure there is *no* form error for this element. - $this->assertTrue(empty($errors[$element]), "Optional '$type' field '$element' has no errors with empty input"); - } - } - } - } - } - // Clear the expected form error messages so they don't appear as exceptions. - drupal_get_messages(); - } - - /** - * Test default value handling for checkboxes. - * - * @see _form_test_checkbox() - */ - function testCheckboxProcessing() { - // First, try to submit without the required checkbox. - $edit = array(); - $this->drupalPost('form-test/checkbox', $edit, t('Submit')); - $this->assertRaw(t('!name field is required.', array('!name' => 'required_checkbox')), t('A required checkbox is actually mandatory')); - - // Now try to submit the form correctly. - $values = drupal_json_decode($this->drupalPost(NULL, array('required_checkbox' => 1), t('Submit'))); - $expected_values = array( - 'disabled_checkbox_on' => 'disabled_checkbox_on', - 'disabled_checkbox_off' => '', - 'checkbox_on' => 'checkbox_on', - 'checkbox_off' => '', - 'zero_checkbox_on' => '0', - 'zero_checkbox_off' => '', - ); - foreach ($expected_values as $widget => $expected_value) { - $this->assertEqual($values[$widget], $expected_value, t('Checkbox %widget returns expected value (expected: %expected, got: %value)', array( - '%widget' => var_export($widget, TRUE), - '%expected' => var_export($expected_value, TRUE), - '%value' => var_export($values[$widget], TRUE), - ))); - } - } - - /** - * Tests validation of #type 'select' elements. - */ - function testSelect() { - $form = $form_state = array(); - $form = form_test_select($form, $form_state); - $error = '!name field is required.'; - $this->drupalGet('form-test/select'); - - // Posting without any values should throw validation errors. - $this->drupalPost(NULL, array(), 'Submit'); - $this->assertNoText(t($error, array('!name' => $form['select']['#title']))); - $this->assertNoText(t($error, array('!name' => $form['select_required']['#title']))); - $this->assertNoText(t($error, array('!name' => $form['select_optional']['#title']))); - $this->assertNoText(t($error, array('!name' => $form['empty_value']['#title']))); - $this->assertNoText(t($error, array('!name' => $form['empty_value_one']['#title']))); - $this->assertText(t($error, array('!name' => $form['no_default']['#title']))); - $this->assertNoText(t($error, array('!name' => $form['no_default_optional']['#title']))); - $this->assertText(t($error, array('!name' => $form['no_default_empty_option']['#title']))); - $this->assertNoText(t($error, array('!name' => $form['no_default_empty_option_optional']['#title']))); - $this->assertText(t($error, array('!name' => $form['no_default_empty_value']['#title']))); - $this->assertText(t($error, array('!name' => $form['no_default_empty_value_one']['#title']))); - $this->assertNoText(t($error, array('!name' => $form['no_default_empty_value_optional']['#title']))); - $this->assertNoText(t($error, array('!name' => $form['multiple']['#title']))); - $this->assertNoText(t($error, array('!name' => $form['multiple_no_default']['#title']))); - $this->assertText(t($error, array('!name' => $form['multiple_no_default_required']['#title']))); - - // Post values for required fields. - $edit = array( - 'no_default' => 'three', - 'no_default_empty_option' => 'three', - 'no_default_empty_value' => 'three', - 'no_default_empty_value_one' => 'three', - 'multiple_no_default_required[]' => 'three', - ); - $this->drupalPost(NULL, $edit, 'Submit'); - $values = drupal_json_decode($this->drupalGetContent()); - - // Verify expected values. - $expected = array( - 'select' => 'one', - 'empty_value' => 'one', - 'empty_value_one' => 'one', - 'no_default' => 'three', - 'no_default_optional' => 'one', - 'no_default_optional_empty_value' => '', - 'no_default_empty_option' => 'three', - 'no_default_empty_option_optional' => '', - 'no_default_empty_value' => 'three', - 'no_default_empty_value_one' => 'three', - 'no_default_empty_value_optional' => 0, - 'multiple' => array('two' => 'two'), - 'multiple_no_default' => array(), - 'multiple_no_default_required' => array('three' => 'three'), - ); - foreach ($expected as $key => $value) { - $this->assertIdentical($values[$key], $value, t('@name: @actual is equal to @expected.', array( - '@name' => $key, - '@actual' => var_export($values[$key], TRUE), - '@expected' => var_export($value, TRUE), - ))); - } - } - - /** - * Test handling of disabled elements. - * - * @see _form_test_disabled_elements() - */ - function testDisabledElements() { - // Get the raw form in its original state. - $form_state = array(); - $form = _form_test_disabled_elements(array(), $form_state); - - // Build a submission that tries to hijack the form by submitting input for - // elements that are disabled. - $edit = array(); - foreach (element_children($form) as $key) { - if (isset($form[$key]['#test_hijack_value'])) { - if (is_array($form[$key]['#test_hijack_value'])) { - foreach ($form[$key]['#test_hijack_value'] as $subkey => $value) { - $edit[$key . '[' . $subkey . ']'] = $value; - } - } - else { - $edit[$key] = $form[$key]['#test_hijack_value']; - } - } - } - - // Submit the form with no input, as the browser does for disabled elements, - // and fetch the $form_state['values'] that is passed to the submit handler. - $this->drupalPost('form-test/disabled-elements', array(), t('Submit')); - $returned_values['normal'] = drupal_json_decode($this->content); - - // Do the same with input, as could happen if JavaScript un-disables an - // element. drupalPost() emulates a browser by not submitting input for - // disabled elements, so we need to un-disable those elements first. - $this->drupalGet('form-test/disabled-elements'); - $disabled_elements = array(); - foreach ($this->xpath('//*[@disabled]') as $element) { - $disabled_elements[] = (string) $element['name']; - unset($element['disabled']); - } - - // All the elements should be marked as disabled, including the ones below - // the disabled container. - $this->assertEqual(count($disabled_elements), 32, t('The correct elements have the disabled property in the HTML code.')); - - $this->drupalPost(NULL, $edit, t('Submit')); - $returned_values['hijacked'] = drupal_json_decode($this->content); - - // Ensure that the returned values match the form's default values in both - // cases. - foreach ($returned_values as $type => $values) { - $this->assertFormValuesDefault($values, $form); - } - } - - /** - * Assert that the values submitted to a form matches the default values of the elements. - */ - function assertFormValuesDefault($values, $form) { - foreach (element_children($form) as $key) { - if (isset($form[$key]['#default_value'])) { - if (isset($form[$key]['#expected_value'])) { - $expected_value = $form[$key]['#expected_value']; - } - else { - $expected_value = $form[$key]['#default_value']; - } - - if ($key == 'checkboxes_multiple') { - // Checkboxes values are not filtered out. - $values[$key] = array_filter($values[$key]); - } - $this->assertIdentical($expected_value, $values[$key], t('Default value for %type: expected %expected, returned %returned.', array('%type' => $key, '%expected' => var_export($expected_value, TRUE), '%returned' => var_export($values[$key], TRUE)))); - } - - // Recurse children. - $this->assertFormValuesDefault($values, $form[$key]); - } - } - - /** - * Verify markup for disabled form elements. - * - * @see _form_test_disabled_elements() - */ - function testDisabledMarkup() { - $this->drupalGet('form-test/disabled-elements'); - $form_state = array(); - $form = _form_test_disabled_elements(array(), $form_state); - $type_map = array( - 'textarea' => 'textarea', - 'select' => 'select', - 'weight' => 'select', - 'date' => 'select', - ); - - foreach ($form as $name => $item) { - // Skip special #types. - if (!isset($item['#type']) || in_array($item['#type'], array('hidden', 'text_format'))) { - continue; - } - // Setup XPath and CSS class depending on #type. - if (in_array($item['#type'], array('image_button', 'button', 'submit'))) { - $path = "//!type[contains(@class, :div-class) and @value=:value]"; - $class = 'form-button-disabled'; - } - else { - // starts-with() required for checkboxes. - $path = "//div[contains(@class, :div-class)]/descendant::!type[starts-with(@name, :name)]"; - $class = 'form-disabled'; - } - // Replace DOM element name in $path according to #type. - $type = 'input'; - if (isset($type_map[$item['#type']])) { - $type = $type_map[$item['#type']]; - } - $path = strtr($path, array('!type' => $type)); - // Verify that the element exists. - $element = $this->xpath($path, array( - ':name' => check_plain($name), - ':div-class' => $class, - ':value' => isset($item['#value']) ? $item['#value'] : '', - )); - $this->assertTrue(isset($element[0]), t('Disabled form element class found for #type %type.', array('%type' => $item['#type']))); - } - - // Verify special element #type text-format. - $element = $this->xpath('//div[contains(@class, :div-class)]/descendant::textarea[@name=:name]', array( - ':name' => 'text_format[value]', - ':div-class' => 'form-disabled', - )); - $this->assertTrue(isset($element[0]), t('Disabled form element class found for #type %type.', array('%type' => 'text_format[value]'))); - $element = $this->xpath('//div[contains(@class, :div-class)]/descendant::select[@name=:name]', array( - ':name' => 'text_format[format]', - ':div-class' => 'form-disabled', - )); - $this->assertTrue(isset($element[0]), t('Disabled form element class found for #type %type.', array('%type' => 'text_format[format]'))); - } - - /** - * Test Form API protections against input forgery. - * - * @see _form_test_input_forgery() - */ - function testInputForgery() { - $this->drupalGet('form-test/input-forgery'); - $checkbox = $this->xpath('//input[@name="checkboxes[two]"]'); - $checkbox[0]['value'] = 'FORGERY'; - $this->drupalPost(NULL, array('checkboxes[one]' => TRUE, 'checkboxes[two]' => TRUE), t('Submit')); - $this->assertText('An illegal choice has been detected.', t('Input forgery was detected.')); - } -} - -/** - * Tests building and processing of core form elements. - */ -class FormElementTestCase extends DrupalWebTestCase { - protected $profile = 'testing'; - - public static function getInfo() { - return array( - 'name' => 'Element processing', - 'description' => 'Tests building and processing of core form elements.', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp(array('form_test')); - } - - /** - * Tests placeholder text for textfield, password, and textarea. - */ - function testPlaceHolderText() { - $this->drupalGet('form-test/placeholder-text'); - $expected = 'placeholder-text'; - // Test to make sure textfields and passwords have the proper placeholder - // text. - foreach (array('textfield', 'password') as $type) { - $element = $this->xpath('//input[@id=:id and @placeholder=:expected]', array( - ':id' => 'edit-' . $type, - ':expected' => $expected, - )); - $this->assertTrue(!empty($element), t('Placeholder text placed in @type.', array('@type' => $type))); - } - - // Test to make sure textarea has the proper placeholder text. - $element = $this->xpath('//textarea[@id=:id and @placeholder=:expected]', array( - ':id' => 'edit-textarea', - ':expected' => $expected, - )); - $this->assertTrue(!empty($element), t('Placeholder text placed in textarea.')); - } - - /** - * Tests expansion of #options for #type checkboxes and radios. - */ - function testOptions() { - $this->drupalGet('form-test/checkboxes-radios'); - - // Verify that all options appear in their defined order. - foreach (array('checkbox', 'radio') as $type) { - $elements = $this->xpath('//input[@type=:type]', array(':type' => $type)); - $expected_values = array('0', 'foo', '1', 'bar', '>'); - foreach ($elements as $element) { - $expected = array_shift($expected_values); - $this->assertIdentical((string) $element['value'], $expected); - } - } - - // Enable customized option sub-elements. - $this->drupalGet('form-test/checkboxes-radios/customize'); - - // Verify that all options appear in their defined order, taking a custom - // #weight into account. - foreach (array('checkbox', 'radio') as $type) { - $elements = $this->xpath('//input[@type=:type]', array(':type' => $type)); - $expected_values = array('0', 'foo', 'bar', '>', '1'); - foreach ($elements as $element) { - $expected = array_shift($expected_values); - $this->assertIdentical((string) $element['value'], $expected); - } - } - // Verify that custom #description properties are output. - foreach (array('checkboxes', 'radios') as $type) { - $elements = $this->xpath('//input[@id=:id]/following-sibling::div[@class=:class]', array( - ':id' => 'edit-' . $type . '-foo', - ':class' => 'description', - )); - $this->assertTrue(count($elements), t('Custom %type option description found.', array( - '%type' => $type, - ))); - } - } -} - -/** - * Test form alter hooks. - */ -class FormAlterTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Form alter hooks', - 'description' => 'Tests hook_form_alter() and hook_form_FORM_ID_alter().', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - } - - /** - * Tests execution order of hook_form_alter() and hook_form_FORM_ID_alter(). - */ - function testExecutionOrder() { - $this->drupalGet('form-test/alter'); - // Ensure that the order is first by module, then for a given module, the - // id-specific one after the generic one. - $expected = array( - 'block_form_form_test_alter_form_alter() executed.', - 'form_test_form_alter() executed.', - 'form_test_form_form_test_alter_form_alter() executed.', - 'system_form_form_test_alter_form_alter() executed.', - ); - $content = preg_replace('/\s+/', ' ', filter_xss($this->content, array())); - $this->assert(strpos($content, implode(' ', $expected)) !== FALSE, t('Form alter hooks executed in the expected order.')); - } -} - -/** - * Test form validation handlers. - */ -class FormValidationTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Form validation handlers', - 'description' => 'Tests form processing and alteration via form validation handlers.', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - } - - /** - * Tests form alterations by #element_validate, #validate, and form_set_value(). - */ - function testValidate() { - $this->drupalGet('form-test/validate'); - // Verify that #element_validate handlers can alter the form and submitted - // form values. - $edit = array( - 'name' => 'element_validate', - ); - $this->drupalPost(NULL, $edit, 'Save'); - $this->assertFieldByName('name', '#value changed by #element_validate', t('Form element #value was altered.')); - $this->assertText('Name value: value changed by form_set_value() in #element_validate', t('Form element value in $form_state was altered.')); - - // Verify that #validate handlers can alter the form and submitted - // form values. - $edit = array( - 'name' => 'validate', - ); - $this->drupalPost(NULL, $edit, 'Save'); - $this->assertFieldByName('name', '#value changed by #validate', t('Form element #value was altered.')); - $this->assertText('Name value: value changed by form_set_value() in #validate', t('Form element value in $form_state was altered.')); - - // Verify that #element_validate handlers can make form elements - // inaccessible, but values persist. - $edit = array( - 'name' => 'element_validate_access', - ); - $this->drupalPost(NULL, $edit, 'Save'); - $this->assertNoFieldByName('name', t('Form element was hidden.')); - $this->assertText('Name value: element_validate_access', t('Value for inaccessible form element exists.')); - - // Verify that value for inaccessible form element persists. - $this->drupalPost(NULL, array(), 'Save'); - $this->assertNoFieldByName('name', t('Form element was hidden.')); - $this->assertText('Name value: element_validate_access', t('Value for inaccessible form element exists.')); - } - - /** - * Tests partial form validation through #limit_validation_errors. - */ - function testValidateLimitErrors() { - $edit = array( - 'test' => 'invalid', - 'test_numeric_index[0]' => 'invalid', - 'test_substring[foo]' => 'invalid', - ); - $path = 'form-test/limit-validation-errors'; - - // Submit the form by pressing the 'Partial validate' button (uses - // #limit_validation_errors) and ensure that the title field is not - // validated, but the #element_validate handler for the 'test' field - // is triggered. - $this->drupalPost($path, $edit, t('Partial validate')); - $this->assertNoText(t('!name field is required.', array('!name' => 'Title'))); - $this->assertText('Test element is invalid'); - - // Edge case of #limit_validation_errors containing numeric indexes: same - // thing with the 'Partial validate (numeric index)' button and the - // 'test_numeric_index' field. - $this->drupalPost($path, $edit, t('Partial validate (numeric index)')); - $this->assertNoText(t('!name field is required.', array('!name' => 'Title'))); - $this->assertText('Test (numeric index) element is invalid'); - - // Ensure something like 'foobar' isn't considered "inside" 'foo'. - $this->drupalPost($path, $edit, t('Partial validate (substring)')); - $this->assertNoText(t('!name field is required.', array('!name' => 'Title'))); - $this->assertText('Test (substring) foo element is invalid'); - - // Ensure not validated values are not available to submit handlers. - $this->drupalPost($path, array('title' => '', 'test' => 'valid'), t('Partial validate')); - $this->assertText('Only validated values appear in the form values.'); - - // Now test full form validation and ensure that the #element_validate - // handler is still triggered. - $this->drupalPost($path, $edit, t('Full validate')); - $this->assertText(t('!name field is required.', array('!name' => 'Title'))); - $this->assertText('Test element is invalid'); - } -} - -/** - * Test form element labels, required markers and associated output. - */ -class FormsElementsLabelsTestCase extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Form element and label output test', - 'description' => 'Test form element labels, required markers and associated output.', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - } - - /** - * Test form elements, labels, title attibutes and required marks output - * correctly and have the correct label option class if needed. - */ - function testFormLabels() { - $this->drupalGet('form_test/form-labels'); - - // Check that the checkbox/radio processing is not interfering with - // basic placement. - $elements = $this->xpath('//input[@id="edit-form-checkboxes-test-third-checkbox"]/following-sibling::label[@for="edit-form-checkboxes-test-third-checkbox" and @class="option"]'); - $this->assertTrue(isset($elements[0]), t("Label follows field and label option class correct for regular checkboxes.")); - - $elements = $this->xpath('//input[@id="edit-form-radios-test-second-radio"]/following-sibling::label[@for="edit-form-radios-test-second-radio" and @class="option"]'); - $this->assertTrue(isset($elements[0]), t("Label follows field and label option class correct for regular radios.")); - - // Exercise various defaults for checkboxes and modifications to ensure - // appropriate override and correct behaviour. - $elements = $this->xpath('//input[@id="edit-form-checkbox-test"]/following-sibling::label[@for="edit-form-checkbox-test" and @class="option"]'); - $this->assertTrue(isset($elements[0]), t("Label follows field and label option class correct for a checkbox by default.")); - - // Exercise various defaults for textboxes and modifications to ensure - // appropriate override and correct behaviour. - $elements = $this->xpath('//label[@for="edit-form-textfield-test-title-and-required"]/child::abbr[@class="form-required"]/parent::*/following-sibling::input[@id="edit-form-textfield-test-title-and-required"]'); - $this->assertTrue(isset($elements[0]), t("Label precedes textfield, with required marker inside label.")); - - $elements = $this->xpath('//input[@id="edit-form-textfield-test-no-title-required"]/preceding-sibling::label[@for="edit-form-textfield-test-no-title-required"]/abbr[@class="form-required"]'); - $this->assertTrue(isset($elements[0]), t("Label tag with required marker precedes required textfield with no title.")); - - $elements = $this->xpath('//input[@id="edit-form-textfield-test-title-invisible"]/preceding-sibling::label[@for="edit-form-textfield-test-title-invisible" and @class="element-invisible"]'); - $this->assertTrue(isset($elements[0]), t("Label preceding field and label class is element-invisible.")); - - $elements = $this->xpath('//input[@id="edit-form-textfield-test-title"]/preceding-sibling::abbr[@class="form-required"]'); - $this->assertFalse(isset($elements[0]), t("No required marker on non-required field.")); - - $elements = $this->xpath('//input[@id="edit-form-textfield-test-title-after"]/following-sibling::label[@for="edit-form-textfield-test-title-after" and @class="option"]'); - $this->assertTrue(isset($elements[0]), t("Label after field and label option class correct for text field.")); - - $elements = $this->xpath('//label[@for="edit-form-textfield-test-title-no-show"]'); - $this->assertFalse(isset($elements[0]), t("No label tag when title set not to display.")); - - // Check #field_prefix and #field_suffix placement. - $elements = $this->xpath('//span[@class="field-prefix"]/following-sibling::div[@id="edit-form-radios-test"]'); - $this->assertTrue(isset($elements[0]), t("Properly placed the #field_prefix element after the label and before the field.")); - - $elements = $this->xpath('//span[@class="field-suffix"]/preceding-sibling::div[@id="edit-form-radios-test"]'); - $this->assertTrue(isset($elements[0]), t("Properly places the #field_suffix element immediately after the form field.")); - - // Check #prefix and #suffix placement. - $elements = $this->xpath('//div[@id="form-test-textfield-title-prefix"]/following-sibling::div[contains(@class, \'form-item-form-textfield-test-title\')]'); - $this->assertTrue(isset($elements[0]), t("Properly places the #prefix element before the form item.")); - - $elements = $this->xpath('//div[@id="form-test-textfield-title-suffix"]/preceding-sibling::div[contains(@class, \'form-item-form-textfield-test-title\')]'); - $this->assertTrue(isset($elements[0]), t("Properly places the #suffix element before the form item.")); - } -} - -/** - * Test the tableselect form element for expected behavior. - */ -class FormsElementsTableSelectFunctionalTest extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Tableselect form element type test', - 'description' => 'Test the tableselect element for expected behavior', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - } - - - /** - * Test the display of checkboxes when #multiple is TRUE. - */ - function testMultipleTrue() { - - $this->drupalGet('form_test/tableselect/multiple-true'); - - $this->assertNoText(t('Empty text.'), t('Empty text should not be displayed.')); - - // Test for the presence of the Select all rows tableheader. - $this->assertFieldByXPath('//th[@class="select-all"]', NULL, t('Presence of the "Select all" checkbox.')); - - $rows = array('row1', 'row2', 'row3'); - foreach ($rows as $row) { - $this->assertFieldByXPath('//input[@type="checkbox"]', $row, t('Checkbox for value @row.', array('@row' => $row))); - } - } - - /** - * Test the display of radios when #multiple is FALSE. - */ - function testMultipleFalse() { - $this->drupalGet('form_test/tableselect/multiple-false'); - - $this->assertNoText(t('Empty text.'), t('Empty text should not be displayed.')); - - // Test for the absence of the Select all rows tableheader. - $this->assertNoFieldByXPath('//th[@class="select-all"]', '', t('Absence of the "Select all" checkbox.')); - - $rows = array('row1', 'row2', 'row3'); - foreach ($rows as $row) { - $this->assertFieldByXPath('//input[@type="radio"]', $row, t('Radio button for value @row.', array('@row' => $row))); - } - } - - /** - * Test the display of the #empty text when #options is an empty array. - */ - function testEmptyText() { - $this->drupalGet('form_test/tableselect/empty-text'); - $this->assertText(t('Empty text.'), t('Empty text should be displayed.')); - } - - /** - * Test the submission of single and multiple values when #multiple is TRUE. - */ - function testMultipleTrueSubmit() { - - // Test a submission with one checkbox checked. - $edit = array(); - $edit['tableselect[row1]'] = TRUE; - $this->drupalPost('form_test/tableselect/multiple-true', $edit, 'Submit'); - - $this->assertText(t('Submitted: row1 = row1'), t('Checked checkbox row1')); - $this->assertText(t('Submitted: row2 = 0'), t('Unchecked checkbox row2.')); - $this->assertText(t('Submitted: row3 = 0'), t('Unchecked checkbox row3.')); - - // Test a submission with multiple checkboxes checked. - $edit['tableselect[row1]'] = TRUE; - $edit['tableselect[row3]'] = TRUE; - $this->drupalPost('form_test/tableselect/multiple-true', $edit, 'Submit'); - - $this->assertText(t('Submitted: row1 = row1'), t('Checked checkbox row1.')); - $this->assertText(t('Submitted: row2 = 0'), t('Unchecked checkbox row2.')); - $this->assertText(t('Submitted: row3 = row3'), t('Checked checkbox row3.')); - - } - - /** - * Test submission of values when #multiple is FALSE. - */ - function testMultipleFalseSubmit() { - $edit['tableselect'] = 'row1'; - $this->drupalPost('form_test/tableselect/multiple-false', $edit, 'Submit'); - $this->assertText(t('Submitted: row1'), t('Selected radio button')); - } - - /** - * Test the #js_select property. - */ - function testAdvancedSelect() { - // When #multiple = TRUE a Select all checkbox should be displayed by default. - $this->drupalGet('form_test/tableselect/advanced-select/multiple-true-default'); - $this->assertFieldByXPath('//th[@class="select-all"]', NULL, t('Display a "Select all" checkbox by default when #multiple is TRUE.')); - - // When #js_select is set to FALSE, a "Select all" checkbox should not be displayed. - $this->drupalGet('form_test/tableselect/advanced-select/multiple-true-no-advanced-select'); - $this->assertNoFieldByXPath('//th[@class="select-all"]', NULL, t('Do not display a "Select all" checkbox when #js_select is FALSE.')); - - // A "Select all" checkbox never makes sense when #multiple = FALSE, regardless of the value of #js_select. - $this->drupalGet('form_test/tableselect/advanced-select/multiple-false-default'); - $this->assertNoFieldByXPath('//th[@class="select-all"]', NULL, t('Do not display a "Select all" checkbox when #multiple is FALSE.')); - - $this->drupalGet('form_test/tableselect/advanced-select/multiple-false-advanced-select'); - $this->assertNoFieldByXPath('//th[@class="select-all"]', NULL, t('Do not display a "Select all" checkbox when #multiple is FALSE, even when #js_select is TRUE.')); - } - - - /** - * Test the whether the option checker gives an error on invalid tableselect values for checkboxes. - */ - function testMultipleTrueOptionchecker() { - - list($header, $options) = _form_test_tableselect_get_data(); - - $form['tableselect'] = array( - '#type' => 'tableselect', - '#header' => $header, - '#options' => $options, - ); - - // Test with a valid value. - list($processed_form, $form_state, $errors) = $this->formSubmitHelper($form, array('tableselect' => array('row1' => 'row1'))); - $this->assertFalse(isset($errors['tableselect']), t('Option checker allows valid values for checkboxes.')); - - // Test with an invalid value. - list($processed_form, $form_state, $errors) = $this->formSubmitHelper($form, array('tableselect' => array('non_existing_value' => 'non_existing_value'))); - $this->assertTrue(isset($errors['tableselect']), t('Option checker disallows invalid values for checkboxes.')); - - } - - - /** - * Test the whether the option checker gives an error on invalid tableselect values for radios. - */ - function testMultipleFalseOptionchecker() { - - list($header, $options) = _form_test_tableselect_get_data(); - - $form['tableselect'] = array( - '#type' => 'tableselect', - '#header' => $header, - '#options' => $options, - '#multiple' => FALSE, - ); - - // Test with a valid value. - list($processed_form, $form_state, $errors) = $this->formSubmitHelper($form, array('tableselect' => 'row1')); - $this->assertFalse(isset($errors['tableselect']), t('Option checker allows valid values for radio buttons.')); - - // Test with an invalid value. - list($processed_form, $form_state, $errors) = $this->formSubmitHelper($form, array('tableselect' => 'non_existing_value')); - $this->assertTrue(isset($errors['tableselect']), t('Option checker disallows invalid values for radio buttons.')); - } - - - /** - * Helper function for the option check test to submit a form while collecting errors. - * - * @param $form_element - * A form element to test. - * @param $edit - * An array containing post data. - * - * @return - * An array containing the processed form, the form_state and any errors. - */ - private function formSubmitHelper($form, $edit) { - $form_id = $this->randomName(); - $form_state = form_state_defaults(); - - $form['op'] = array('#type' => 'submit', '#value' => t('Submit')); - - $form_state['input'] = $edit; - $form_state['input']['form_id'] = $form_id; - - drupal_prepare_form($form_id, $form, $form_state); - - drupal_process_form($form_id, $form, $form_state); - - $errors = form_get_errors(); - - // Clear errors and messages. - drupal_get_messages(); - form_clear_error(); - - // Return the processed form together with form_state and errors - // to allow the caller lowlevel access to the form. - return array($form, $form_state, $errors); - } - -} - -/** - * Test the vertical_tabs form element for expected behavior. - */ -class FormsElementsVerticalTabsFunctionalTest extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Vertical tabs form element type test', - 'description' => 'Test the vertical_tabs element for expected behavior', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - } - - /** - * Ensures that vertical-tabs.js is included before collapse.js. - * - * Otherwise, collapse.js adds "SHOW" or "HIDE" labels to the tabs. - */ - function testJavaScriptOrdering() { - $this->drupalGet('form_test/vertical-tabs'); - $position1 = strpos($this->content, 'misc/vertical-tabs.js'); - $position2 = strpos($this->content, 'misc/collapse.js'); - $this->assertTrue($position1 !== FALSE && $position2 !== FALSE && $position1 < $position2, t('vertical-tabs.js is included before collapse.js')); - } -} - -/** - * Test the form storage on a multistep form. - * - * The tested form puts data into the storage during the initial form - * construction. These tests verify that there are no duplicate form - * constructions, with and without manual form caching activiated. Furthermore - * when a validation error occurs, it makes sure that changed form element - * values aren't lost due to a wrong form rebuild. - */ -class FormsFormStorageTestCase extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Multistep form using form storage', - 'description' => 'Tests a multistep form using form storage and makes sure validation and caching works right.', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - - $this->web_user = $this->drupalCreateUser(array('access content')); - $this->drupalLogin($this->web_user); - } - - /** - * Tests using the form in a usual way. - */ - function testForm() { - $this->drupalGet('form_test/form-storage'); - $this->assertText('Form constructions: 1'); - - $edit = array('title' => 'new', 'value' => 'value_is_set'); - - // Use form rebuilding triggered by a submit button. - $this->drupalPost(NULL, $edit, 'Continue submit'); - $this->assertText('Form constructions: 2'); - $this->assertText('Form constructions: 3'); - - // Reset the form to the values of the storage, using a form rebuild - // triggered by button of type button. - $this->drupalPost(NULL, array('title' => 'changed'), 'Reset'); - $this->assertFieldByName('title', 'new', 'Values have been resetted.'); - // After rebuilding, the form has been cached. - $this->assertText('Form constructions: 4'); - - $this->drupalPost(NULL, $edit, 'Save'); - $this->assertText('Form constructions: 4'); - $this->assertText('Title: new', t('The form storage has stored the values.')); - } - - /** - * Tests using the form with an activated $form_state['cache'] property. - */ - function testFormCached() { - $this->drupalGet('form_test/form-storage', array('query' => array('cache' => 1))); - $this->assertText('Form constructions: 1'); - - $edit = array('title' => 'new', 'value' => 'value_is_set'); - - // Use form rebuilding triggered by a submit button. - $this->drupalPost(NULL, $edit, 'Continue submit'); - $this->assertText('Form constructions: 2'); - - // Reset the form to the values of the storage, using a form rebuild - // triggered by button of type button. - $this->drupalPost(NULL, array('title' => 'changed'), 'Reset'); - $this->assertFieldByName('title', 'new', 'Values have been resetted.'); - $this->assertText('Form constructions: 3'); - - $this->drupalPost(NULL, $edit, 'Save'); - $this->assertText('Form constructions: 3'); - $this->assertText('Title: new', t('The form storage has stored the values.')); - } - - /** - * Tests validation when form storage is used. - */ - function testValidation() { - $this->drupalPost('form_test/form-storage', array('title' => '', 'value' => 'value_is_set'), 'Continue submit'); - $this->assertPattern('/value_is_set/', t('The input values have been kept.')); - } - - /** - * Tests updating cached form storage during form validation. - * - * If form caching is enabled and a form stores data in the form storage, then - * the form storage also has to be updated in case of a validation error in - * the form. This test re-uses the existing form for multi-step tests, but - * triggers a special #element_validate handler to update the form storage - * during form validation, while another, required element in the form - * triggers a form validation error. - */ - function testCachedFormStorageValidation() { - // Request the form with 'cache' query parameter to enable form caching. - $this->drupalGet('form_test/form-storage', array('query' => array('cache' => 1))); - - // Skip step 1 of the multi-step form, since the first step copies over - // 'title' into form storage, but we want to verify that changes in the form - // storage are updated in the cache during form validation. - $edit = array('title' => 'foo'); - $this->drupalPost(NULL, $edit, 'Continue submit'); - - // In step 2, trigger a validation error for the required 'title' field, and - // post the special 'change_title' value for the 'value' field, which - // conditionally invokes the #element_validate handler to update the form - // storage. - $edit = array('title' => '', 'value' => 'change_title'); - $this->drupalPost(NULL, $edit, 'Save'); - - // At this point, the form storage should contain updated values, but we do - // not see them, because the form has not been rebuilt yet due to the - // validation error. Post again and verify that the rebuilt form contains - // the values of the updated form storage. - $this->drupalPost(NULL, array('title' => 'foo', 'value' => 'bar'), 'Save'); - $this->assertText("The thing has been changed.", 'The altered form storage value was updated in cache and taken over.'); - } - - /** - * Tests a form using form state without using 'storage' to pass data from the - * constructor to a submit handler. The data has to persist even when caching - * gets activated, what may happen when a modules alter the form and adds - * #ajax properties. - */ - function testFormStatePersist() { - // Test the form one time with caching activated and one time without. - $run_options = array( - array(), - array('query' => array('cache' => 1)), - ); - foreach ($run_options as $options) { - $this->drupalPost('form-test/state-persist', array(), t('Submit'), $options); - // The submit handler outputs the value in $form_state, assert it's there. - $this->assertText('State persisted.'); - - // Test it again, but first trigger a validation error, then test. - $this->drupalPost('form-test/state-persist', array('title' => ''), t('Submit'), $options); - $this->assertText(t('!name field is required.', array('!name' => 'title'))); - // Submit the form again triggering no validation error. - $this->drupalPost(NULL, array('title' => 'foo'), t('Submit'), $options); - $this->assertText('State persisted.'); - - // Now post to the rebuilt form and verify it's still there afterwards. - $this->drupalPost(NULL, array('title' => 'bar'), t('Submit'), $options); - $this->assertText('State persisted.'); - } - } -} - -/** - * Test wrapper form callbacks. - */ -class FormsFormWrapperTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Form wrapper callback', - 'description' => 'Tests form wrapper callbacks to pass a prebuilt form to form builder functions.', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - } - - /** - * Tests using the form in a usual way. - */ - function testWrapperCallback() { - $this->drupalGet('form_test/wrapper-callback'); - $this->assertText('Form wrapper callback element output.', t('The form contains form wrapper elements.')); - $this->assertText('Form builder element output.', t('The form contains form builder elements.')); - } -} - -/** - * Test $form_state clearance. - */ -class FormStateValuesCleanTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Form state values clearance', - 'description' => 'Test proper removal of submitted form values using form_state_values_clean().', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - } - - /** - * Tests form_state_values_clean(). - */ - function testFormStateValuesClean() { - $values = drupal_json_decode($this->drupalPost('form_test/form-state-values-clean', array(), t('Submit'))); - - // Setup the expected result. - $result = array( - 'beer' => 1000, - 'baz' => array('beer' => 2000), - ); - - // Verify that all internal Form API elements were removed. - $this->assertFalse(isset($values['form_id']), t('%element was removed.', array('%element' => 'form_id'))); - $this->assertFalse(isset($values['form_token']), t('%element was removed.', array('%element' => 'form_token'))); - $this->assertFalse(isset($values['form_build_id']), t('%element was removed.', array('%element' => 'form_build_id'))); - $this->assertFalse(isset($values['op']), t('%element was removed.', array('%element' => 'op'))); - - // Verify that all buttons were removed. - $this->assertFalse(isset($values['foo']), t('%element was removed.', array('%element' => 'foo'))); - $this->assertFalse(isset($values['bar']), t('%element was removed.', array('%element' => 'bar'))); - $this->assertFalse(isset($values['baz']['foo']), t('%element was removed.', array('%element' => 'foo'))); - $this->assertFalse(isset($values['baz']['baz']), t('%element was removed.', array('%element' => 'baz'))); - - // Verify that nested form value still exists. - $this->assertTrue(isset($values['baz']['beer']), t('Nested form value still exists.')); - - // Verify that actual form values equal resulting form values. - $this->assertEqual($values, $result, t('Expected form values equal actual form values.')); - } -} - -/** - * Tests form rebuilding. - * - * @todo Add tests for other aspects of form rebuilding. - */ -class FormsRebuildTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Form rebuilding', - 'description' => 'Tests functionality of drupal_rebuild_form().', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - - $this->web_user = $this->drupalCreateUser(array('access content')); - $this->drupalLogin($this->web_user); - } - - /** - * Tests preservation of values. - */ - function testRebuildPreservesValues() { - $edit = array( - 'checkbox_1_default_off' => TRUE, - 'checkbox_1_default_on' => FALSE, - 'text_1' => 'foo', - ); - $this->drupalPost('form-test/form-rebuild-preserve-values', $edit, 'Add more'); - - // Verify that initial elements retained their submitted values. - $this->assertFieldChecked('edit-checkbox-1-default-off', t('A submitted checked checkbox retained its checked state during a rebuild.')); - $this->assertNoFieldChecked('edit-checkbox-1-default-on', t('A submitted unchecked checkbox retained its unchecked state during a rebuild.')); - $this->assertFieldById('edit-text-1', 'foo', t('A textfield retained its submitted value during a rebuild.')); - - // Verify that newly added elements were initialized with their default values. - $this->assertFieldChecked('edit-checkbox-2-default-on', t('A newly added checkbox was initialized with a default checked state.')); - $this->assertNoFieldChecked('edit-checkbox-2-default-off', t('A newly added checkbox was initialized with a default unchecked state.')); - $this->assertFieldById('edit-text-2', 'DEFAULT 2', t('A newly added textfield was initialized with its default value.')); - } - - /** - * Tests that a form's action is retained after an Ajax submission. - * - * The 'action' attribute of a form should not change after an Ajax submission - * followed by a non-Ajax submission, which triggers a validation error. - */ - function testPreserveFormActionAfterAJAX() { - // Create a multi-valued field for 'page' nodes to use for Ajax testing. - $field_name = 'field_ajax_test'; - $field = array( - 'field_name' => $field_name, - 'type' => 'text', - 'cardinality' => FIELD_CARDINALITY_UNLIMITED, - ); - field_create_field($field); - $instance = array( - 'field_name' => $field_name, - 'entity_type' => 'node', - 'bundle' => 'page', - ); - field_create_instance($instance); - - // Log in a user who can create 'page' nodes. - $this->web_user = $this->drupalCreateUser(array('create page content')); - $this->drupalLogin($this->web_user); - - // Get the form for adding a 'page' node. Submit an "add another item" Ajax - // submission and verify it worked by ensuring the updated page has two text - // field items in the field for which we just added an item. - $this->drupalGet('node/add/page'); - $this->drupalPostAJAX(NULL, array(), array('field_ajax_test_add_more' => t('Add another item')), 'system/ajax', array(), array(), 'page-node-form'); - $this->assert(count($this->xpath('//div[contains(@class, "field-name-field-ajax-test")]//input[@type="text"]')) == 2, t('AJAX submission succeeded.')); - - // Submit the form with the non-Ajax "Save" button, leaving the title field - // blank to trigger a validation error, and ensure that a validation error - // occurred, because this test is for testing what happens when a form is - // re-rendered without being re-built, which is what happens when there's - // a validation error. - $this->drupalPost(NULL, array(), t('Save')); - $this->assertText('Title field is required.', t('Non-AJAX submission correctly triggered a validation error.')); - - // Ensure that the form contains two items in the multi-valued field, so we - // know we're testing a form that was correctly retrieved from cache. - $this->assert(count($this->xpath('//form[contains(@id, "page-node-form")]//div[contains(@class, "form-item-field-ajax-test")]//input[@type="text"]')) == 2, t('Form retained its state from cache.')); - - // Ensure that the form's action is correct. - $forms = $this->xpath('//form[contains(@class, "node-page-form")]'); - $this->assert(count($forms) == 1 && $forms[0]['action'] == url('node/add/page'), t('Re-rendered form contains the correct action value.')); - } -} - -/** - * Test the programmatic form submission behavior. - */ -class FormsProgrammaticTestCase extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Programmatic form submissions', - 'description' => 'Test the programmatic form submission behavior.', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - } - - /** - * Test the programmatic form submission workflow. - */ - function testSubmissionWorkflow() { - // Backup the current batch status and reset it to avoid conflicts while - // processing the dummy form submit handler. - $current_batch = $batch =& batch_get(); - $batch = array(); - - // Test that a programmatic form submission is rejected when a required - // textfield is omitted and correctly processed when it is provided. - $this->submitForm(array(), FALSE); - $this->submitForm(array('textfield' => 'test 1'), TRUE); - $this->submitForm(array(), FALSE); - $this->submitForm(array('textfield' => 'test 2'), TRUE); - - // Test that a programmatic form submission can turn on and off checkboxes - // which are, by default, checked. - $this->submitForm(array('textfield' => 'dummy value', 'checkboxes' => array(1 => 1, 2 => 2)), TRUE); - $this->submitForm(array('textfield' => 'dummy value', 'checkboxes' => array(1 => 1, 2 => NULL)), TRUE); - $this->submitForm(array('textfield' => 'dummy value', 'checkboxes' => array(1 => NULL, 2 => 2)), TRUE); - $this->submitForm(array('textfield' => 'dummy value', 'checkboxes' => array(1 => NULL, 2 => NULL)), TRUE); - - // Test that a programmatic form submission can correctly click a button - // that limits validation errors based on user input. Since we do not - // submit any values for "textfield" here and the textfield is required, we - // only expect form validation to pass when validation is limited to a - // different field. - $this->submitForm(array('op' => 'Submit with limited validation', 'field_to_validate' => 'all'), FALSE); - $this->submitForm(array('op' => 'Submit with limited validation', 'field_to_validate' => 'textfield'), FALSE); - $this->submitForm(array('op' => 'Submit with limited validation', 'field_to_validate' => 'field_to_validate'), TRUE); - - // Restore the current batch status. - $batch = $current_batch; - } - - /** - * Helper function used to programmatically submit the form defined in - * form_test.module with the given values. - * - * @param $values - * An array of field values to be submitted. - * @param $valid_input - * A boolean indicating whether or not the form submission is expected to - * be valid. - */ - private function submitForm($values, $valid_input) { - // Programmatically submit the given values. - $form_state = array('values' => $values); - drupal_form_submit('form_test_programmatic_form', $form_state); - - // Check that the form returns an error when expected, and vice versa. - $errors = form_get_errors(); - $valid_form = empty($errors); - $args = array( - '%values' => print_r($values, TRUE), - '%errors' => $valid_form ? t('None') : implode(' ', $errors), - ); - $this->assertTrue($valid_input == $valid_form, t('Input values: %values<br/>Validation handler errors: %errors', $args)); - - // We check submitted values only if we have a valid input. - if ($valid_input) { - // By fetching the values from $form_state['storage'] we ensure that the - // submission handler was properly executed. - $stored_values = $form_state['storage']['programmatic_form_submit']; - foreach ($values as $key => $value) { - $this->assertTrue(isset($stored_values[$key]) && $stored_values[$key] == $value, t('Submission handler correctly executed: %stored_key is %stored_value', array('%stored_key' => $key, '%stored_value' => print_r($value, TRUE)))); - } - } - } -} - -/** - * Test that FAPI correctly determines $form_state['triggering_element']. - */ -class FormsTriggeringElementTestCase extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Form triggering element determination', - 'description' => 'Test the determination of $form_state[\'triggering_element\'].', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - } - - /** - * Test the determination of $form_state['triggering_element'] when no button - * information is included in the POST data, as is sometimes the case when - * the ENTER key is pressed in a textfield in Internet Explorer. - */ - function testNoButtonInfoInPost() { - $path = 'form-test/clicked-button'; - $edit = array(); - $form_html_id = 'form-test-clicked-button'; - - // Ensure submitting a form with no buttons results in no - // $form_state['triggering_element'] and the form submit handler not - // running. - $this->drupalPost($path, $edit, NULL, array(), array(), $form_html_id); - $this->assertText('There is no clicked button.', t('$form_state[\'triggering_element\'] set to NULL.')); - $this->assertNoText('Submit handler for form_test_clicked_button executed.', t('Form submit handler did not execute.')); - - // Ensure submitting a form with one or more submit buttons results in - // $form_state['triggering_element'] being set to the first one the user has - // access to. An argument with 'r' in it indicates a restricted - // (#access=FALSE) button. - $this->drupalPost($path . '/s', $edit, NULL, array(), array(), $form_html_id); - $this->assertText('The clicked button is button1.', t('$form_state[\'triggering_element\'] set to only button.')); - $this->assertText('Submit handler for form_test_clicked_button executed.', t('Form submit handler executed.')); - - $this->drupalPost($path . '/s/s', $edit, NULL, array(), array(), $form_html_id); - $this->assertText('The clicked button is button1.', t('$form_state[\'triggering_element\'] set to first button.')); - $this->assertText('Submit handler for form_test_clicked_button executed.', t('Form submit handler executed.')); - - $this->drupalPost($path . '/rs/s', $edit, NULL, array(), array(), $form_html_id); - $this->assertText('The clicked button is button2.', t('$form_state[\'triggering_element\'] set to first available button.')); - $this->assertText('Submit handler for form_test_clicked_button executed.', t('Form submit handler executed.')); - - // Ensure submitting a form with buttons of different types results in - // $form_state['triggering_element'] being set to the first button, - // regardless of type. For the FAPI 'button' type, this should result in the - // submit handler not executing. The types are 's'(ubmit), 'b'(utton), and - // 'i'(mage_button). - $this->drupalPost($path . '/s/b/i', $edit, NULL, array(), array(), $form_html_id); - $this->assertText('The clicked button is button1.', t('$form_state[\'triggering_element\'] set to first button.')); - $this->assertText('Submit handler for form_test_clicked_button executed.', t('Form submit handler executed.')); - - $this->drupalPost($path . '/b/s/i', $edit, NULL, array(), array(), $form_html_id); - $this->assertText('The clicked button is button1.', t('$form_state[\'triggering_element\'] set to first button.')); - $this->assertNoText('Submit handler for form_test_clicked_button executed.', t('Form submit handler did not execute.')); - - $this->drupalPost($path . '/i/s/b', $edit, NULL, array(), array(), $form_html_id); - $this->assertText('The clicked button is button1.', t('$form_state[\'triggering_element\'] set to first button.')); - $this->assertText('Submit handler for form_test_clicked_button executed.', t('Form submit handler executed.')); - } - - /** - * Test that $form_state['triggering_element'] does not get set to a button - * with #access=FALSE. - */ - function testAttemptAccessControlBypass() { - $path = 'form-test/clicked-button'; - $form_html_id = 'form-test-clicked-button'; - - // Retrieve a form where 'button1' has #access=FALSE and 'button2' doesn't. - $this->drupalGet($path . '/rs/s'); - - // Submit the form with 'button1=button1' in the POST data, which someone - // trying to get around security safeguards could easily do. We have to do - // a little trickery here, to work around the safeguards in drupalPost(): by - // renaming the text field that is in the form to 'button1', we can get the - // data we want into $_POST. - $elements = $this->xpath('//form[@id="' . $form_html_id . '"]//input[@name="text"]'); - $elements[0]['name'] = 'button1'; - $this->drupalPost(NULL, array('button1' => 'button1'), NULL, array(), array(), $form_html_id); - - // Ensure that $form_state['triggering_element'] was not set to the - // restricted button. Do this with both a negative and positive assertion, - // because negative assertions alone can be brittle. See - // testNoButtonInfoInPost() for why the triggering element gets set to - // 'button2'. - $this->assertNoText('The clicked button is button1.', t('$form_state[\'triggering_element\'] not set to a restricted button.')); - $this->assertText('The clicked button is button2.', t('$form_state[\'triggering_element\'] not set to a restricted button.')); - } -} - -/** - * Tests rebuilding of arbitrary forms by altering them. - */ -class FormsArbitraryRebuildTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Rebuild arbitrary forms', - 'description' => 'Tests altering forms to be rebuilt so there are multiple steps.', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - // Auto-create a field for testing. - $field = array( - 'field_name' => 'test_multiple', - 'type' => 'text', - 'cardinality' => -1, - 'translatable' => FALSE, - ); - field_create_field($field); - - $instance = array( - 'entity_type' => 'node', - 'field_name' => 'test_multiple', - 'bundle' => 'page', - 'label' => 'Test a multiple valued field', - 'widget' => array( - 'type' => 'text_textfield', - 'weight' => 0, - ), - ); - field_create_instance($instance); - variable_set('user_register', USER_REGISTER_VISITORS); - } - - /** - * Tests a basic rebuild with the user registration form. - */ - function testUserRegistrationRebuild() { - $edit = array( - 'name' => 'foo', - 'mail' => 'bar@example.com', - ); - $this->drupalPost('user/register', $edit, 'Rebuild'); - $this->assertText('Form rebuilt.'); - $this->assertFieldByName('name', 'foo', 'Entered user name has been kept.'); - $this->assertFieldByName('mail', 'bar@example.com', 'Entered mail address has been kept.'); - } - - /** - * Tests a rebuild caused by a multiple value field. - */ - function testUserRegistrationMultipleField() { - $edit = array( - 'name' => 'foo', - 'mail' => 'bar@example.com', - ); - $this->drupalPost('user/register', $edit, t('Add another item'), array('query' => array('field' => TRUE))); - $this->assertText('Test a multiple valued field', 'Form has been rebuilt.'); - $this->assertFieldByName('name', 'foo', 'Entered user name has been kept.'); - $this->assertFieldByName('mail', 'bar@example.com', 'Entered mail address has been kept.'); - } -} - -/** - * Tests form API file inclusion. - */ -class FormsFileInclusionTestCase extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Form API file inclusion', - 'description' => 'Tests form API file inclusion.', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - } - - /** - * Tests loading an include specified in hook_menu(). - */ - function testLoadMenuInclude() { - $this->drupalPostAJAX('form-test/load-include-menu', array(), array('op' => t('Save')), 'system/ajax', array(), array(), 'form-test-load-include-menu'); - $this->assertText('Submit callback called.'); - } - - /** - * Tests loading a custom specified inlcude. - */ - function testLoadCustomInclude() { - $this->drupalPost('form-test/load-include-custom', array(), t('Save')); - $this->assertText('Submit callback called.'); - } -} - -/** - * Tests checkbox element. - */ -class FormCheckboxTestCase extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Form API checkbox', - 'description' => 'Tests form API checkbox handling of various combinations of #default_value and #return_value.', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - } - - function testFormCheckbox() { - // Ensure that the checked state is determined and rendered correctly for - // tricky combinations of default and return values. - foreach (array(FALSE, NULL, TRUE, 0, '0', '', 1, '1', 'foobar', '1foobar') as $default_value) { - // Only values that can be used for array indeces are supported for - // #return_value, with the exception of integer 0, which is not supported. - // @see form_process_checkbox(). - foreach (array('0', '', 1, '1', 'foobar', '1foobar') as $return_value) { - $form_array = drupal_get_form('form_test_checkbox_type_juggling', $default_value, $return_value); - $form = drupal_render($form_array); - if ($default_value === TRUE) { - $checked = TRUE; - } - elseif ($return_value === '0') { - $checked = ($default_value === '0'); - } - elseif ($return_value === '') { - $checked = ($default_value === ''); - } - elseif ($return_value === 1 || $return_value === '1') { - $checked = ($default_value === 1 || $default_value === '1'); - } - elseif ($return_value === 'foobar') { - $checked = ($default_value === 'foobar'); - } - elseif ($return_value === '1foobar') { - $checked = ($default_value === '1foobar'); - } - $checked_in_html = strpos($form, 'checked') !== FALSE; - $message = t('#default_value is %default_value #return_value is %return_value.', array('%default_value' => var_export($default_value, TRUE), '%return_value' => var_export($return_value, TRUE))); - $this->assertIdentical($checked, $checked_in_html, $message); - } - } - - // Ensure that $form_state['values'] is populated correctly for a checkboxes - // group that includes a 0-indexed array of options. - $results = json_decode($this->drupalPost('form-test/checkboxes-zero', array(), 'Save')); - $this->assertIdentical($results->checkbox_off, array(0, 0, 0), t('All three in checkbox_off are zeroes: off.')); - $this->assertIdentical($results->checkbox_zero_default, array('0', 0, 0), t('The first choice is on in checkbox_zero_default')); - $this->assertIdentical($results->checkbox_string_zero_default, array('0', 0, 0), t('The first choice is on in checkbox_string_zero_default')); - $edit = array('checkbox_off[0]' => '0'); - $results = json_decode($this->drupalPost('form-test/checkboxes-zero', $edit, 'Save')); - $this->assertIdentical($results->checkbox_off, array('0', 0, 0), t('The first choice is on in checkbox_off but the rest is not')); - - // Ensure that each checkbox is rendered correctly for a checkboxes group - // that includes a 0-indexed array of options. - $this->drupalPost('form-test/checkboxes-zero/0', array(), 'Save'); - $checkboxes = $this->xpath('//input[@type="checkbox"]'); - foreach ($checkboxes as $checkbox) { - $checked = isset($checkbox['checked']); - $name = (string) $checkbox['name']; - $this->assertIdentical($checked, $name == 'checkbox_zero_default[0]' || $name == 'checkbox_string_zero_default[0]', t('Checkbox %name correctly checked', array('%name' => $name))); - } - $edit = array('checkbox_off[0]' => '0'); - $this->drupalPost('form-test/checkboxes-zero/0', $edit, 'Save'); - $checkboxes = $this->xpath('//input[@type="checkbox"]'); - foreach ($checkboxes as $checkbox) { - $checked = isset($checkbox['checked']); - $name = (string) $checkbox['name']; - $this->assertIdentical($checked, $name == 'checkbox_off[0]' || $name == 'checkbox_zero_default[0]' || $name == 'checkbox_string_zero_default[0]', t('Checkbox %name correctly checked', array('%name' => $name))); - } - } -} diff --git a/modules/simpletest/tests/form_test.file.inc b/modules/simpletest/tests/form_test.file.inc deleted file mode 100644 index f9197ead2a5..00000000000 --- a/modules/simpletest/tests/form_test.file.inc +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -/** - * @file - * An include file to test loading it with the form API. - */ - -/** - * Form constructor for testing FAPI file inclusion of the file specified in - * hook_menu(). - */ -function form_test_load_include_menu($form, &$form_state) { - // Submit the form via Ajax. That way the FAPI has to care about including - // the file specified in hook_menu(). - $ajax_wrapper_id = drupal_html_id('form-test-load-include-menu-ajax-wrapper'); - $form['ajax_wrapper'] = array( - '#markup' => '<div id="' . $ajax_wrapper_id . '"></div>', - ); - $form['button'] = array( - '#type' => 'submit', - '#value' => t('Save'), - '#submit' => array('form_test_load_include_submit'), - '#ajax' => array( - 'wrapper' => $ajax_wrapper_id, - 'method' => 'append', - 'callback' => 'form_test_load_include_menu_ajax', - ), - ); - return $form; -} - -/** - * Submit callback for the form API file inclusion test forms. - */ -function form_test_load_include_submit($form, $form_state) { - drupal_set_message('Submit callback called.'); -} - -/** - * Ajax callback for the file inclusion via menu test. - */ -function form_test_load_include_menu_ajax($form) { - // We don't need to return anything, since #ajax['method'] is 'append', which - // does not remove the original #ajax['wrapper'] element, and status messages - // are automatically added by the Ajax framework as long as there's a wrapper - // element to add them to. - return ''; -} diff --git a/modules/simpletest/tests/form_test.info b/modules/simpletest/tests/form_test.info deleted file mode 100644 index 5354350daf0..00000000000 --- a/modules/simpletest/tests/form_test.info +++ /dev/null @@ -1,6 +0,0 @@ -name = "FormAPI Test" -description = "Support module for Form API tests." -package = Testing -version = VERSION -core = 8.x -hidden = TRUE diff --git a/modules/simpletest/tests/form_test.module b/modules/simpletest/tests/form_test.module deleted file mode 100644 index 36a66484511..00000000000 --- a/modules/simpletest/tests/form_test.module +++ /dev/null @@ -1,1648 +0,0 @@ -<?php - -/** - * @file - * Helper module for the form API tests. - */ - -/** - * Implements hook_menu(). - */ -function form_test_menu() { - $items['form-test/alter'] = array( - 'title' => 'Form altering test', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('form_test_alter_form'), - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - $items['form-test/validate'] = array( - 'title' => 'Form validation handlers test', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('form_test_validate_form'), - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - $items['form-test/limit-validation-errors'] = array( - 'title' => 'Form validation with some error suppression', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('form_test_limit_validation_errors_form'), - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - - $items['form_test/tableselect/multiple-true'] = array( - 'title' => 'Tableselect checkboxes test', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('_form_test_tableselect_multiple_true_form'), - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - $items['form_test/tableselect/multiple-false'] = array( - 'title' => 'Tableselect radio button test', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('_form_test_tableselect_multiple_false_form'), - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - $items['form_test/tableselect/empty-text'] = array( - 'title' => 'Tableselect empty text test', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('_form_test_tableselect_empty_form'), - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - $items['form_test/tableselect/advanced-select'] = array( - 'title' => 'Tableselect js_select tests', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('_form_test_tableselect_js_select_form'), - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - - $items['form_test/vertical-tabs'] = array( - 'title' => 'Vertical tabs tests', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('_form_test_vertical_tabs_form'), - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - - $items['form_test/form-storage'] = array( - 'title' => 'Form storage test', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('form_test_storage_form'), - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - - $items['form_test/wrapper-callback'] = array( - 'title' => 'Form wrapper callback test', - 'page callback' => 'form_test_wrapper_callback', - 'page arguments' => array('form_test_wrapper_callback_form'), - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - - $items['form_test/form-state-values-clean'] = array( - 'title' => 'Form state values clearance test', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('form_test_form_state_values_clean_form'), - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - - $items['form-test/checkbox'] = array( - 'title' => t('Form test'), - 'page callback' => 'drupal_get_form', - 'page arguments' => array('_form_test_checkbox'), - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); - $items['form-test/select'] = array( - 'title' => t('Select'), - 'page callback' => 'drupal_get_form', - 'page arguments' => array('form_test_select'), - 'access callback' => TRUE, - ); - $items['form-test/placeholder-text'] = array( - 'title' => 'Placeholder', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('form_test_placeholder_test'), - 'access callback' => TRUE, - ); - $items['form-test/checkboxes-radios'] = array( - 'title' => t('Checkboxes, Radios'), - 'page callback' => 'drupal_get_form', - 'page arguments' => array('form_test_checkboxes_radios'), - 'access callback' => TRUE, - ); - - $items['form-test/disabled-elements'] = array( - 'title' => t('Form test'), - 'page callback' => 'drupal_get_form', - 'page arguments' => array('_form_test_disabled_elements'), - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); - - $items['form-test/input-forgery'] = array( - 'title' => t('Form test'), - 'page callback' => 'drupal_get_form', - 'page arguments' => array('_form_test_input_forgery'), - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); - - $items['form-test/form-rebuild-preserve-values'] = array( - 'title' => 'Form values preservation during rebuild test', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('form_test_form_rebuild_preserve_values_form'), - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - - $items['form_test/form-labels'] = array( - 'title' => 'Form label test', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('form_label_test_form'), - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - - $items['form-test/state-persist'] = array( - 'title' => 'Form state persistence without storage', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('form_test_state_persist'), - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); - - $items['form-test/clicked-button'] = array( - 'title' => 'Clicked button test', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('form_test_clicked_button'), - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); - - if (module_exists('node')) { - $items['form-test/two-instances-of-same-form'] = array( - 'title' => 'AJAX test with two form instances', - 'page callback' => 'form_test_two_instances', - 'access callback' => 'node_access', - 'access arguments' => array('create', 'page'), - 'file path' => drupal_get_path('module', 'node'), - 'file' => 'node.pages.inc', - 'type' => MENU_CALLBACK, - ); - } - - $items['form-test/load-include-menu'] = array( - 'title' => 'FAPI test loading includes', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('form_test_load_include_menu'), - 'access callback' => TRUE, - 'file' => 'form_test.file.inc', - 'type' => MENU_CALLBACK, - ); - - $items['form-test/load-include-custom'] = array( - 'title' => 'FAPI test loading includes', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('form_test_load_include_custom'), - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); - $items['form-test/checkboxes-zero'] = array( - 'title' => 'FAPI test involving checkboxes and zero', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('form_test_checkboxes_zero'), - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); - - return $items; -} - -/** - * Form submit handler to return form values as JSON. - */ -function _form_test_submit_values_json($form, &$form_state) { - drupal_json_output($form_state['values']); - drupal_exit(); -} - -/** - * Form builder for testing hook_form_alter() and hook_form_FORM_ID_alter(). - */ -function form_test_alter_form($form, &$form_state) { - // Elements can be added as needed for future testing needs, but for now, - // we're only testing alter hooks that do not require any elements added by - // this function. - return $form; -} - -/** - * Implements hook_form_FORM_ID_alter() on behalf of block.module. - */ -function block_form_form_test_alter_form_alter(&$form, &$form_state) { - drupal_set_message('block_form_form_test_alter_form_alter() executed.'); -} - -/** - * Implements hook_form_alter(). - */ -function form_test_form_alter(&$form, &$form_state, $form_id) { - if ($form_id == 'form_test_alter_form') { - drupal_set_message('form_test_form_alter() executed.'); - } -} - -/** - * Implements hook_form_FORM_ID_alter(). - */ -function form_test_form_form_test_alter_form_alter(&$form, &$form_state) { - drupal_set_message('form_test_form_form_test_alter_form_alter() executed.'); -} - -/** - * Implements hook_form_FORM_ID_alter() on behalf of system.module. - */ -function system_form_form_test_alter_form_alter(&$form, &$form_state) { - drupal_set_message('system_form_form_test_alter_form_alter() executed.'); -} - -/** - * Form builder for testing drupal_validate_form(). - * - * Serves for testing form processing and alterations by form validation - * handlers, especially for the case of a validation error: - * - form_set_value() should be able to alter submitted values in - * $form_state['values'] without affecting the form element. - * - #element_validate handlers should be able to alter the $element in the form - * structure and the alterations should be contained in the rebuilt form. - * - #validate handlers should be able to alter the $form and the alterations - * should be contained in the rebuilt form. - */ -function form_test_validate_form($form, &$form_state) { - $form['name'] = array( - '#type' => 'textfield', - '#title' => 'Name', - '#default_value' => '', - '#element_validate' => array('form_test_element_validate_name'), - ); - $form['submit'] = array( - '#type' => 'submit', - '#value' => 'Save', - ); - - // To simplify this test, enable form caching and use form storage to - // remember our alteration. - $form_state['cache'] = TRUE; - - return $form; -} - -/** - * Form element validation handler for 'name' in form_test_validate_form(). - */ -function form_test_element_validate_name(&$element, &$form_state) { - $triggered = FALSE; - if ($form_state['values']['name'] == 'element_validate') { - // Alter the form element. - $element['#value'] = '#value changed by #element_validate'; - // Alter the submitted value in $form_state. - form_set_value($element, 'value changed by form_set_value() in #element_validate', $form_state); - - $triggered = TRUE; - } - if ($form_state['values']['name'] == 'element_validate_access') { - $form_state['storage']['form_test_name'] = $form_state['values']['name']; - // Alter the form element. - $element['#access'] = FALSE; - - $triggered = TRUE; - } - elseif (!empty($form_state['storage']['form_test_name'])) { - // To simplify this test, just take over the element's value into $form_state. - form_set_value($element, $form_state['storage']['form_test_name'], $form_state); - - $triggered = TRUE; - } - - if ($triggered) { - // Output the element's value from $form_state. - drupal_set_message(t('@label value: @value', array('@label' => $element['#title'], '@value' => $form_state['values']['name']))); - - // Trigger a form validation error to see our changes. - form_set_error(''); - } -} - -/** - * Form validation handler for form_test_validate_form(). - */ -function form_test_validate_form_validate(&$form, &$form_state) { - if ($form_state['values']['name'] == 'validate') { - // Alter the form element. - $form['name']['#value'] = '#value changed by #validate'; - // Alter the submitted value in $form_state. - form_set_value($form['name'], 'value changed by form_set_value() in #validate', $form_state); - // Output the element's value from $form_state. - drupal_set_message(t('@label value: @value', array('@label' => $form['name']['#title'], '@value' => $form_state['values']['name']))); - - // Trigger a form validation error to see our changes. - form_set_error(''); - } -} - -/** - * Builds a simple form with a button triggering partial validation. - */ -function form_test_limit_validation_errors_form($form, &$form_state) { - $form['title'] = array( - '#type' => 'textfield', - '#title' => 'Title', - '#required' => TRUE, - ); - - $form['test'] = array( - '#title' => 'Test', - '#type' => 'textfield', - '#element_validate' => array('form_test_limit_validation_errors_element_validate_test'), - ); - $form['test_numeric_index'] = array( - '#tree' => TRUE, - ); - $form['test_numeric_index'][0] = array( - '#title' => 'Test (numeric index)', - '#type' => 'textfield', - '#element_validate' => array('form_test_limit_validation_errors_element_validate_test'), - ); - - $form['test_substring'] = array( - '#tree' => TRUE, - ); - $form['test_substring']['foo'] = array( - '#title' => 'Test (substring) foo', - '#type' => 'textfield', - '#element_validate' => array('form_test_limit_validation_errors_element_validate_test'), - ); - $form['test_substring']['foobar'] = array( - '#title' => 'Test (substring) foobar', - '#type' => 'textfield', - '#element_validate' => array('form_test_limit_validation_errors_element_validate_test'), - ); - - $form['actions']['partial'] = array( - '#type' => 'submit', - '#limit_validation_errors' => array(array('test')), - '#submit' => array('form_test_limit_validation_errors_form_partial_submit'), - '#value' => t('Partial validate'), - ); - $form['actions']['partial_numeric_index'] = array( - '#type' => 'submit', - '#limit_validation_errors' => array(array('test_numeric_index', 0)), - '#submit' => array('form_test_limit_validation_errors_form_partial_submit'), - '#value' => t('Partial validate (numeric index)'), - ); - $form['actions']['substring'] = array( - '#type' => 'submit', - '#limit_validation_errors' => array(array('test_substring', 'foo')), - '#submit' => array('form_test_limit_validation_errors_form_partial_submit'), - '#value' => t('Partial validate (substring)'), - ); - $form['actions']['full'] = array( - '#type' => 'submit', - '#value' => t('Full validate'), - ); - return $form; -} - -/** - * Form element validation handler for the 'test' element. - */ -function form_test_limit_validation_errors_element_validate_test(&$element, &$form_state) { - if ($element['#value'] == 'invalid') { - form_error($element, t('@label element is invalid', array('@label' => $element['#title']))); - } -} - -/** - * Form submit handler for the partial validation submit button. - */ -function form_test_limit_validation_errors_form_partial_submit($form, $form_state) { - // The title has not been validated, thus its value - in case of the test case - // an empty string - may not be set. - if (!isset($form_state['values']['title']) && isset($form_state['values']['test'])) { - drupal_set_message('Only validated values appear in the form values.'); - } -} - -/** - * Create a header and options array. Helper function for callbacks. - */ -function _form_test_tableselect_get_data() { - $header = array( - 'one' => t('One'), - 'two' => t('Two'), - 'three' => t('Three'), - 'four' => t('Four'), - ); - - $options['row1'] = array( - 'one' => 'row1col1', - 'two' => t('row1col2'), - 'three' => t('row1col3'), - 'four' => t('row1col4'), - ); - - $options['row2'] = array( - 'one' => 'row2col1', - 'two' => t('row2col2'), - 'three' => t('row2col3'), - 'four' => t('row2col4'), - ); - - $options['row3'] = array( - 'one' => 'row3col1', - 'two' => t('row3col2'), - 'three' => t('row3col3'), - 'four' => t('row3col4'), - ); - - return array($header, $options); -} - -/** - * Build a form to test the tableselect element. - * - * @param $form_state - * The form_state - * @param $element_properties - * An array of element properties for the tableselect element. - * - * @return - * A form with a tableselect element and a submit button. - */ -function _form_test_tableselect_form_builder($form, $form_state, $element_properties) { - list($header, $options) = _form_test_tableselect_get_data(); - - $form['tableselect'] = $element_properties; - - $form['tableselect'] += array( - '#type' => 'tableselect', - '#header' => $header, - '#options' => $options, - '#multiple' => FALSE, - '#empty' => t('Empty text.'), - ); - - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Submit'), - ); - - return $form; -} - -/** - * Test the tableselect #multiple = TRUE functionality. - */ -function _form_test_tableselect_multiple_true_form($form, $form_state) { - return _form_test_tableselect_form_builder($form, $form_state, array('#multiple' => TRUE)); -} - -/** - * Process the tableselect #multiple = TRUE submitted values. - */ -function _form_test_tableselect_multiple_true_form_submit($form, &$form_state) { - $selected = $form_state['values']['tableselect']; - foreach ($selected as $key => $value) { - drupal_set_message(t('Submitted: @key = @value', array('@key' => $key, '@value' => $value))); - } -} - -/** - * Test the tableselect #multiple = FALSE functionality. - */ -function _form_test_tableselect_multiple_false_form($form, $form_state) { - return _form_test_tableselect_form_builder($form, $form_state, array('#multiple' => FALSE)); -} - -/** - * Process the tableselect #multiple = FALSE submitted values. - */ -function _form_test_tableselect_multiple_false_form_submit($form, &$form_state) { - drupal_set_message(t('Submitted: @value', array('@value' => $form_state['values']['tableselect']))); -} - -/** - * Test functionality of the tableselect #empty property. - */ -function _form_test_tableselect_empty_form($form, $form_state) { - return _form_test_tableselect_form_builder($form, $form_state, array('#options' => array())); -} - -/** - * Test functionality of the tableselect #js_select property. - */ -function _form_test_tableselect_js_select_form($form, $form_state, $action) { - switch ($action) { - case 'multiple-true-default': - $options = array('#multiple' => TRUE); - break; - - case 'multiple-false-default': - $options = array('#multiple' => FALSE); - break; - - case 'multiple-true-no-advanced-select': - $options = array('#multiple' => TRUE, '#js_select' => FALSE); - break; - - case 'multiple-false-advanced-select': - $options = array('#multiple' => FALSE, '#js_select' => TRUE); - break; - } - - return _form_test_tableselect_form_builder($form, $form_state, $options); -} - -/** - * Tests functionality of vertical tabs. - */ -function _form_test_vertical_tabs_form($form, &$form_state) { - $form['vertical_tabs'] = array( - '#type' => 'vertical_tabs', - ); - $form['tab1'] = array( - '#type' => 'fieldset', - '#title' => t('Tab 1'), - '#collapsible' => TRUE, - '#group' => 'vertical_tabs', - ); - $form['tab1']['field1'] = array( - '#title' => t('Field 1'), - '#type' => 'textfield', - ); - $form['tab2'] = array( - '#type' => 'fieldset', - '#title' => t('Tab 2'), - '#collapsible' => TRUE, - '#group' => 'vertical_tabs', - ); - $form['tab2']['field2'] = array( - '#title' => t('Field 2'), - '#type' => 'textfield', - ); - return $form; -} - -/** - * A multistep form for testing the form storage. - * - * It uses two steps for editing a virtual "thing". Any changes to it are saved - * in the form storage and have to be present during any step. By setting the - * request parameter "cache" the form can be tested with caching enabled, as - * it would be the case, if the form would contain some #ajax callbacks. - * - * @see form_test_storage_form_submit() - */ -function form_test_storage_form($form, &$form_state) { - if ($form_state['rebuild']) { - $form_state['input'] = array(); - } - // Initialize - if (empty($form_state['storage'])) { - if (empty($form_state['input'])) { - $_SESSION['constructions'] = 0; - } - // Put the initial thing into the storage - $form_state['storage'] = array( - 'thing' => array( - 'title' => 'none', - 'value' => '', - ), - ); - } - // Count how often the form is constructed. - $_SESSION['constructions']++; - drupal_set_message("Form constructions: " . $_SESSION['constructions']); - - $form['title'] = array( - '#type' => 'textfield', - '#title' => 'Title', - '#default_value' => $form_state['storage']['thing']['title'], - '#required' => TRUE, - ); - $form['value'] = array( - '#type' => 'textfield', - '#title' => 'Value', - '#default_value' => $form_state['storage']['thing']['value'], - '#element_validate' => array('form_test_storage_element_validate_value_cached'), - ); - $form['continue_button'] = array( - '#type' => 'button', - '#value' => 'Reset', - // Rebuilds the form without keeping the values. - ); - $form['continue_submit'] = array( - '#type' => 'submit', - '#value' => 'Continue submit', - '#submit' => array('form_storage_test_form_continue_submit'), - ); - $form['submit'] = array( - '#type' => 'submit', - '#value' => 'Save', - ); - - if (isset($_REQUEST['cache'])) { - // Manually activate caching, so we can test that the storage keeps working - // when it's enabled. - $form_state['cache'] = TRUE; - } - - return $form; -} - -/** - * Form element validation handler for 'value' element in form_test_storage_form(). - * - * Tests updating of cached form storage during validation. - */ -function form_test_storage_element_validate_value_cached($element, &$form_state) { - // If caching is enabled and we receive a certain value, change the storage. - // This presumes that another submitted form value triggers a validation error - // elsewhere in the form. Form API should still update the cached form storage - // though. - if (isset($_REQUEST['cache']) && $form_state['values']['value'] == 'change_title') { - $form_state['storage']['thing']['changed'] = TRUE; - } -} - -/** - * Form submit handler to continue multi-step form. - */ -function form_storage_test_form_continue_submit($form, &$form_state) { - $form_state['storage']['thing']['title'] = $form_state['values']['title']; - $form_state['storage']['thing']['value'] = $form_state['values']['value']; - $form_state['rebuild'] = TRUE; -} - -/** - * Form submit handler to finish multi-step form. - */ -function form_test_storage_form_submit($form, &$form_state) { - drupal_set_message("Title: " . check_plain($form_state['values']['title'])); - drupal_set_message("Form constructions: " . $_SESSION['constructions']); - if (isset($form_state['storage']['thing']['changed'])) { - drupal_set_message("The thing has been changed."); - } - $form_state['redirect'] = 'node'; -} - -/** - * A form for testing form labels and required marks. - */ -function form_label_test_form() { - $form['form_checkboxes_test'] = array( - '#type' => 'checkboxes', - '#title' => t('Checkboxes test'), - '#options' => array( - 'first-checkbox' => t('First checkbox'), - 'second-checkbox' => t('Second checkbox'), - 'third-checkbox' => t('Third checkbox'), - ), - ); - $form['form_radios_test'] = array( - '#type' => 'radios', - '#title' => t('Radios test'), - '#options' => array( - 'first-radio' => t('First radio'), - 'second-radio' => t('Second radio'), - 'third-radio' => t('Third radio'), - ), - // Test #field_prefix and #field_suffix placement. - '#field_prefix' => '<span id="form-test-radios-field-prefix">' . t('Radios #field_prefix element') . '</span>', - '#field_suffix' => '<span id="form-test-radios-field-suffix">' . t('Radios #field_suffix element') . '</span>', - ); - $form['form_checkbox_test'] = array( - '#type' => 'checkbox', - '#title' => t('Checkbox test'), - ); - $form['form_textfield_test_title_and_required'] = array( - '#type' => 'textfield', - '#title' => t('Textfield test for required with title'), - '#required' => TRUE, - ); - $form['form_textfield_test_no_title_required'] = array( - '#type' => 'textfield', - // We use an empty title, since not setting #title suppresses the label - // and required marker. - '#title' => '', - '#required' => TRUE, - ); - $form['form_textfield_test_title'] = array( - '#type' => 'textfield', - '#title' => t('Textfield test for title only'), - // Not required. - // Test #prefix and #suffix placement. - '#prefix' => '<div id="form-test-textfield-title-prefix">' . t('Textfield #prefix element') . '</div>', - '#suffix' => '<div id="form-test-textfield-title-suffix">' . t('Textfield #suffix element') . '</div>', - ); - $form['form_textfield_test_title_after'] = array( - '#type' => 'textfield', - '#title' => t('Textfield test for title after element'), - '#title_display' => 'after', - ); - $form['form_textfield_test_title_invisible'] = array( - '#type' => 'textfield', - '#title' => t('Textfield test for invisible title'), - '#title_display' => 'invisible', - ); - // Textfield test for title set not to display - $form['form_textfield_test_title_no_show'] = array( - '#type' => 'textfield', - ); - - return $form; -} - -/** - * Menu callback; Invokes a form builder function with a wrapper callback. - */ -function form_test_wrapper_callback($form_id) { - $form_state = array( - 'build_info' => array('args' => array()), - 'wrapper_callback' => 'form_test_wrapper_callback_wrapper', - ); - return drupal_build_form($form_id, $form_state); -} - -/** - * Form wrapper for form_test_wrapper_callback_form(). - */ -function form_test_wrapper_callback_wrapper($form, &$form_state) { - $form['wrapper'] = array('#markup' => 'Form wrapper callback element output.'); - return $form; -} - -/** - * Form builder for form wrapper callback test. - */ -function form_test_wrapper_callback_form($form, &$form_state) { - $form['builder'] = array('#markup' => 'Form builder element output.'); - return $form; -} - -/** - * Form builder for form_state_values_clean() test. - */ -function form_test_form_state_values_clean_form($form, &$form_state) { - // Build an example form containing multiple submit and button elements; not - // only on the top-level. - $form = array('#tree' => TRUE); - $form['foo'] = array('#type' => 'submit', '#value' => t('Submit')); - $form['bar'] = array('#type' => 'submit', '#value' => t('Submit')); - $form['beer'] = array('#type' => 'value', '#value' => 1000); - $form['baz']['foo'] = array('#type' => 'button', '#value' => t('Submit')); - $form['baz']['baz'] = array('#type' => 'submit', '#value' => t('Submit')); - $form['baz']['beer'] = array('#type' => 'value', '#value' => 2000); - return $form; -} - -/** - * Form submit handler for form_state_values_clean() test form. - */ -function form_test_form_state_values_clean_form_submit($form, &$form_state) { - form_state_values_clean($form_state); - drupal_json_output($form_state['values']); - exit; -} - -/** - * Build a form to test a checkbox. - */ -function _form_test_checkbox($form, &$form_state) { - // A required checkbox. - $form['required_checkbox'] = array( - '#type' => 'checkbox', - '#required' => TRUE, - '#title' => 'required_checkbox', - ); - - // A disabled checkbox should get its default value back. - $form['disabled_checkbox_on'] = array( - '#type' => 'checkbox', - '#disabled' => TRUE, - '#return_value' => 'disabled_checkbox_on', - '#default_value' => 'disabled_checkbox_on', - '#title' => 'disabled_checkbox_on', - ); - $form['disabled_checkbox_off'] = array( - '#type' => 'checkbox', - '#disabled' => TRUE, - '#return_value' => 'disabled_checkbox_off', - '#default_value' => NULL, - '#title' => 'disabled_checkbox_off', - ); - - // A checkbox is active when #default_value == #return_value. - $form['checkbox_on'] = array( - '#type' => 'checkbox', - '#return_value' => 'checkbox_on', - '#default_value' => 'checkbox_on', - '#title' => 'checkbox_on', - ); - - // But inactive in any other case. - $form['checkbox_off'] = array( - '#type' => 'checkbox', - '#return_value' => 'checkbox_off', - '#default_value' => 'checkbox_on', - '#title' => 'checkbox_off', - ); - - // Checkboxes with a #return_value of '0' are supported. - $form['zero_checkbox_on'] = array( - '#type' => 'checkbox', - '#return_value' => '0', - '#default_value' => '0', - '#title' => 'zero_checkbox_on', - ); - - // In that case, passing a #default_value != '0' means that the checkbox is off. - $form['zero_checkbox_off'] = array( - '#type' => 'checkbox', - '#return_value' => '0', - '#default_value' => '1', - '#title' => 'zero_checkbox_off', - ); - - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Submit') - ); - - return $form; -} - -/** - * Return the form values via JSON. - */ -function _form_test_checkbox_submit($form, &$form_state) { - drupal_json_output($form_state['values']); - exit(); -} - -/** - * Builds a form to test #type 'select' validation. - */ -function form_test_select($form, &$form_state) { - $base = array( - '#type' => 'select', - '#options' => drupal_map_assoc(array('one', 'two', 'three')), - ); - - $form['select'] = $base + array( - '#title' => '#default_value one', - '#default_value' => 'one', - ); - $form['select_required'] = $base + array( - '#title' => '#default_value one, #required', - '#required' => TRUE, - '#default_value' => 'one', - ); - $form['select_optional'] = $base + array( - '#title' => '#default_value one, not #required', - '#required' => FALSE, - '#default_value' => 'one', - ); - $form['empty_value'] = $base + array( - '#title' => '#default_value one, #required, #empty_value 0', - '#required' => TRUE, - '#default_value' => 'one', - '#empty_value' => 0, - ); - $form['empty_value_one'] = $base + array( - '#title' => '#default_value = #empty_value, #required', - '#required' => TRUE, - '#default_value' => 'one', - '#empty_value' => 'one', - ); - - $form['no_default'] = $base + array( - '#title' => 'No #default_value, #required', - '#required' => TRUE, - ); - $form['no_default_optional'] = $base + array( - '#title' => 'No #default_value, not #required', - '#required' => FALSE, - '#description' => 'Should result in "one" because it is not required and there is no #empty_value requested, so default browser behavior of preselecting first option is in effect.', - ); - $form['no_default_optional_empty_value'] = $base + array( - '#title' => 'No #default_value, not #required, #empty_value empty string', - '#empty_value' => '', - '#required' => FALSE, - '#description' => 'Should result in an empty string (due to #empty_value) because it is optional.', - ); - - $form['no_default_empty_option'] = $base + array( - '#title' => 'No #default_value, #required, #empty_option', - '#required' => TRUE, - '#empty_option' => '- Choose -', - ); - $form['no_default_empty_option_optional'] = $base + array( - '#title' => 'No #default_value, not #required, #empty_option', - '#empty_option' => '- Dismiss -', - '#description' => 'Should result in an empty string (default of #empty_value) because it is optional.', - ); - - $form['no_default_empty_value'] = $base + array( - '#title' => 'No #default_value, #required, #empty_value 0', - '#required' => TRUE, - '#empty_value' => 0, - '#description' => 'Should never result in 0.', - ); - $form['no_default_empty_value_one'] = $base + array( - '#title' => 'No #default_value, #required, #empty_value one', - '#required' => TRUE, - '#empty_value' => 'one', - '#description' => 'A mistakenly assigned #empty_value contained in #options should not be valid.', - ); - $form['no_default_empty_value_optional'] = $base + array( - '#title' => 'No #default_value, not #required, #empty_value 0', - '#required' => FALSE, - '#empty_value' => 0, - '#description' => 'Should result in 0 because it is optional.', - ); - - $form['multiple'] = $base + array( - '#title' => '#multiple, #default_value two', - '#default_value' => array('two'), - '#multiple' => TRUE, - ); - $form['multiple_no_default'] = $base + array( - '#title' => '#multiple, no #default_value', - '#multiple' => TRUE, - ); - $form['multiple_no_default_required'] = $base + array( - '#title' => '#multiple, #required, no #default_value', - '#required' => TRUE, - '#multiple' => TRUE, - ); - - $form['submit'] = array('#type' => 'submit', '#value' => 'Submit'); - return $form; -} - -/** - * Form submit handler for form_test_select(). - */ -function form_test_select_submit($form, &$form_state) { - drupal_json_output($form_state['values']); - exit(); -} - -/** - * Builds a form to test the placeholder attribute. - */ -function form_test_placeholder_test($form, &$form_state) { - foreach (array('textfield', 'textarea', 'password') as $type) { - $form[$type] = array( - '#type' => $type, - '#title' => $type, - '#placeholder' => 'placeholder-text', - ); - } - - return $form; -} - -/** - * Form constructor to test expansion of #type checkboxes and radios. - */ -function form_test_checkboxes_radios($form, &$form_state, $customize = FALSE) { - $form['#submit'] = array('_form_test_submit_values_json'); - - // Expand #type checkboxes, setting custom element properties for some but not - // all options. - $form['checkboxes'] = array( - '#type' => 'checkboxes', - '#title' => 'Checkboxes', - '#options' => array( - 0 => 'Zero', - 'foo' => 'Foo', - 1 => 'One', - 'bar' => 'Bar', - '>' => 'Special Char', - ), - ); - if ($customize) { - $form['checkboxes'] += array( - 'foo' => array( - '#description' => 'Enable to foo.', - ), - 1 => array( - '#weight' => 10, - ), - ); - } - - // Expand #type radios, setting custom element properties for some but not - // all options. - $form['radios'] = array( - '#type' => 'radios', - '#title' => 'Radios', - '#options' => array( - 0 => 'Zero', - 'foo' => 'Foo', - 1 => 'One', - 'bar' => 'Bar', - '>' => 'Special Char', - ), - ); - if ($customize) { - $form['radios'] += array( - 'foo' => array( - '#description' => 'Enable to foo.', - ), - 1 => array( - '#weight' => 10, - ), - ); - } - - $form['submit'] = array('#type' => 'submit', '#value' => 'Submit'); - - return $form; -} - -/** - * Build a form to test disabled elements. - */ -function _form_test_disabled_elements($form, &$form_state) { - // Elements that take a simple default value. - foreach (array('textfield', 'textarea', 'hidden') as $type) { - $form[$type] = array( - '#type' => $type, - '#title' => $type, - '#default_value' => $type, - '#test_hijack_value' => 'HIJACK', - '#disabled' => TRUE, - ); - } - - // Multiple values option elements. - foreach (array('checkboxes', 'select') as $type) { - $form[$type . '_multiple'] = array( - '#type' => $type, - '#title' => $type . ' (multiple)', - '#options' => array( - 'test_1' => 'Test 1', - 'test_2' => 'Test 2', - ), - '#multiple' => TRUE, - '#default_value' => array('test_2' => 'test_2'), - // The keys of #test_hijack_value need to match the #name of the control. - // @see FormsTestCase::testDisabledElements() - '#test_hijack_value' => $type == 'select' ? array('' => 'test_1') : array('test_1' => 'test_1'), - '#disabled' => TRUE, - ); - } - - // Single values option elements. - foreach (array('radios', 'select') as $type) { - $form[$type . '_single'] = array( - '#type' => $type, - '#title' => $type . ' (single)', - '#options' => array( - 'test_1' => 'Test 1', - 'test_2' => 'Test 2', - ), - '#multiple' => FALSE, - '#default_value' => 'test_2', - '#test_hijack_value' => 'test_1', - '#disabled' => TRUE, - ); - } - - // Checkbox and radio. - foreach (array('checkbox', 'radio') as $type) { - $form[$type . '_unchecked'] = array( - '#type' => $type, - '#title' => $type . ' (unchecked)', - '#return_value' => 1, - '#default_value' => 0, - '#test_hijack_value' => 1, - '#disabled' => TRUE, - ); - $form[$type . '_checked'] = array( - '#type' => $type, - '#title' => $type . ' (checked)', - '#return_value' => 1, - '#default_value' => 1, - '#test_hijack_value' => NULL, - '#disabled' => TRUE, - ); - } - - // Weight. - $form['weight'] = array( - '#type' => 'weight', - '#title' => 'weight', - '#default_value' => 10, - '#test_hijack_value' => 5, - '#disabled' => TRUE, - ); - - // Date. - $form['date'] = array( - '#type' => 'date', - '#title' => 'date', - '#disabled' => TRUE, - '#default_value' => array( - 'day' => 19, - 'month' => 11, - 'year' => 1978, - ), - '#test_hijack_value' => array( - 'day' => 20, - 'month' => 12, - 'year' => 1979, - ), - ); - - // The #disabled state should propagate to children. - $form['disabled_container'] = array( - '#disabled' => TRUE, - ); - foreach (array('textfield', 'textarea', 'hidden') as $type) { - $form['disabled_container']['disabled_container_' . $type] = array( - '#type' => $type, - '#title' => $type, - '#default_value' => $type, - '#test_hijack_value' => 'HIJACK', - ); - } - - // Text format. - $form['text_format'] = array( - '#type' => 'text_format', - '#title' => 'Text format', - '#disabled' => TRUE, - '#default_value' => 'Text value', - '#format' => 'plain_text', - '#expected_value' => array( - 'value' => 'Text value', - 'format' => 'plain_text', - ), - '#test_hijack_value' => array( - 'value' => 'HIJACK', - 'format' => 'filtered_html', - ), - ); - - // Password fields. - $form['password'] = array( - '#type' => 'password', - '#title' => 'Password', - '#disabled' => TRUE, - ); - $form['password_confirm'] = array( - '#type' => 'password_confirm', - '#title' => 'Password confirm', - '#disabled' => TRUE, - ); - - // Files. - $form['file'] = array( - '#type' => 'file', - '#title' => 'File', - '#disabled' => TRUE, - ); - $form['managed_file'] = array( - '#type' => 'managed_file', - '#title' => 'Managed file', - '#disabled' => TRUE, - ); - - // Buttons. - $form['image_button'] = array( - '#type' => 'image_button', - '#value' => 'Image button', - '#disabled' => TRUE, - ); - $form['button'] = array( - '#type' => 'button', - '#value' => 'Button', - '#disabled' => TRUE, - ); - $form['submit_disabled'] = array( - '#type' => 'submit', - '#value' => 'Submit', - '#disabled' => TRUE, - ); - - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Submit'), - ); - - return $form; -} - -/** - * Return the form values via JSON. - */ -function _form_test_disabled_elements_submit($form, &$form_state) { - drupal_json_output($form_state['values']); - exit(); -} - -/** - * Build a form to test input forgery of enabled elements. - */ -function _form_test_input_forgery($form, &$form_state) { - // For testing that a user can't submit a value not matching one of the - // allowed options. - $form['checkboxes'] = array( - '#type' => 'checkboxes', - '#options' => array( - 'one' => 'One', - 'two' => 'Two', - ), - ); - - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Submit'), - ); - return $form; -} - -/** - * Return the form values via JSON. - */ -function _form_test_input_forgery_submit($form, &$form_state) { - drupal_json_output($form_state['values']); - exit(); -} - -/** - * Form builder for testing preservation of values during a rebuild. - */ -function form_test_form_rebuild_preserve_values_form($form, &$form_state) { - // Start the form with two checkboxes, to test different defaults, and a - // textfield, to test more than one element type. - $form = array( - 'checkbox_1_default_off' => array( - '#type' => 'checkbox', - '#title' => t('This checkbox defaults to unchecked.'), - '#default_value' => FALSE, - ), - 'checkbox_1_default_on' => array( - '#type' => 'checkbox', - '#title' => t('This checkbox defaults to checked.'), - '#default_value' => TRUE, - ), - 'text_1' => array( - '#type' => 'textfield', - '#title' => t('This textfield has a non-empty default value.'), - '#default_value' => 'DEFAULT 1', - ), - ); - // Provide an 'add more' button that rebuilds the form with an additional two - // checkboxes and a textfield. The test is to make sure that the rebuild - // triggered by this button preserves the user input values for the initial - // elements and initializes the new elements with the correct default values. - if (empty($form_state['storage']['add_more'])) { - $form['add_more'] = array( - '#type' => 'submit', - '#value' => 'Add more', - '#submit' => array('form_test_form_rebuild_preserve_values_form_add_more'), - ); - } - else { - $form += array( - 'checkbox_2_default_off' => array( - '#type' => 'checkbox', - '#title' => t('This checkbox defaults to unchecked.'), - '#default_value' => FALSE, - ), - 'checkbox_2_default_on' => array( - '#type' => 'checkbox', - '#title' => t('This checkbox defaults to checked.'), - '#default_value' => TRUE, - ), - 'text_2' => array( - '#type' => 'textfield', - '#title' => t('This textfield has a non-empty default value.'), - '#default_value' => 'DEFAULT 2', - ), - ); - } - // A submit button that finishes the form workflow (does not rebuild). - $form['submit'] = array( - '#type' => 'submit', - '#value' => 'Submit', - ); - return $form; -} - -/** - * Button submit handler for form_test_form_rebuild_preserve_values_form(). - */ -function form_test_form_rebuild_preserve_values_form_add_more($form, &$form_state) { - // Rebuild, to test preservation of input values. - $form_state['storage']['add_more'] = TRUE; - $form_state['rebuild'] = TRUE; -} - -/** - * Form submit handler for form_test_form_rebuild_preserve_values_form(). - */ -function form_test_form_rebuild_preserve_values_form_submit($form, &$form_state) { - // Finish the workflow. Do not rebuild. - drupal_set_message(t('Form values: %values', array('%values' => var_export($form_state['values'], TRUE)))); -} - -/** - * Form constructor for testing form state persistence. - */ -function form_test_state_persist($form, &$form_state) { - $form['title'] = array( - '#type' => 'textfield', - '#title' => 'title', - '#default_value' => 'DEFAULT', - '#required' => TRUE, - ); - $form_state['value'] = 'State persisted.'; - - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Submit'), - ); - return $form; -} - -/** - * Submit handler. - * - * @see form_test_state_persist() - */ -function form_test_state_persist_submit($form, &$form_state) { - drupal_set_message($form_state['value']); - $form_state['rebuild'] = TRUE; -} - -/** - * Implements hook_form_FORM_ID_alter(). - * - * @see form_test_state_persist() - */ -function form_test_form_form_test_state_persist_alter(&$form, &$form_state) { - // Simulate a form alter implementation inserting form elements that enable - // caching of the form, e.g. elements having #ajax. - if (!empty($_REQUEST['cache'])) { - $form_state['cache'] = TRUE; - } -} - -/** - * Form builder to test programmatic form submissions. - */ -function form_test_programmatic_form($form, &$form_state) { - $form['textfield'] = array( - '#title' => 'Textfield', - '#type' => 'textfield', - ); - - $form['checkboxes'] = array( - '#type' => 'checkboxes', - '#options' => array( - 1 => 'First checkbox', - 2 => 'Second checkbox', - ), - // Both checkboxes are selected by default so that we can test the ability - // of programmatic form submissions to uncheck them. - '#default_value' => array(1, 2), - ); - - $form['field_to_validate'] = array( - '#type' => 'radios', - '#title' => 'Field to validate (in the case of limited validation)', - '#description' => 'If the form is submitted by clicking the "Submit with limited validation" button, then validation can be limited based on the value of this radio button.', - '#options' => array( - 'all' => 'Validate all fields', - 'textfield' => 'Validate the "Textfield" field', - 'field_to_validate' => 'Validate the "Field to validate" field', - ), - '#default_value' => 'all', - ); - - // The main submit button for the form. - $form['submit'] = array( - '#type' => 'submit', - '#value' => 'Submit', - ); - // A secondary submit button that allows validation to be limited based on - // the value of the above radio selector. - $form['submit_limit_validation'] = array( - '#type' => 'submit', - '#value' => 'Submit with limited validation', - // Use the same submit handler for this button as for the form itself. - // (This must be set explicitly or otherwise the form API will ignore the - // #limit_validation_errors property.) - '#submit' => array('form_test_programmatic_form_submit'), - ); - if (!empty($form_state['input']['field_to_validate']) && $form_state['input']['field_to_validate'] != 'all') { - $form['submit_limit_validation']['#limit_validation_errors'] = array( - array($form_state['input']['field_to_validate']), - ); - } - - return $form; -} - -/** - * Form validation handler for programmatic form submissions. - * - * To test that the validation handler is correctly executed, the field value is - * explicitly required here. - */ -function form_test_programmatic_form_validate($form, &$form_state) { - if (empty($form_state['values']['textfield'])) { - form_set_error('textfield', t('Textfield is required.')); - } -} - -/** - * Form submit handler for programmatic form submissions. - * - * To test that the submission handler is correctly executed, we store the - * submitted values in a place we can access from the caller context. - */ -function form_test_programmatic_form_submit($form, &$form_state) { - $form_state['storage']['programmatic_form_submit'] = $form_state['values']; -} - -/** - * Form builder to test button click detection. - */ -function form_test_clicked_button($form, &$form_state) { - // A single text field. In IE, when a form has only one non-button input field - // and the ENTER key is pressed while that field has focus, the form is - // submitted without any information identifying the button responsible for - // the submission. In other browsers, the form is submitted as though the - // first button were clicked. - $form['text'] = array( - '#title' => 'Text', - '#type' => 'textfield', - ); - - // Loop through each path argument, addding buttons based on the information - // in the argument. For example, if the path is - // form-test/clicked-button/s/i/rb, then 3 buttons are added: a 'submit', an - // 'image_button', and a 'button' with #access=FALSE. This enables form.test - // to test a variety of combinations. - $i=0; - $args = array_slice(arg(), 2); - foreach ($args as $arg) { - $name = 'button' . ++$i; - // 's', 'b', or 'i' in the argument define the button type wanted. - if (strpos($arg, 's') !== FALSE) { - $type = 'submit'; - } - elseif (strpos($arg, 'b') !== FALSE) { - $type = 'button'; - } - elseif (strpos($arg, 'i') !== FALSE) { - $type = 'image_button'; - } - else { - $type = NULL; - } - if (isset($type)) { - $form[$name] = array( - '#type' => $type, - '#name' => $name, - ); - // Image buttons need a #src; the others need a #value. - if ($type == 'image_button') { - $form[$name]['#src'] = 'misc/druplicon.png'; - } - else { - $form[$name]['#value'] = $name; - } - // 'r' for restricted, so we can test that button click detection code - // correctly takes #access security into account. - if (strpos($arg, 'r') !== FALSE) { - $form[$name]['#access'] = FALSE; - } - } - } - - return $form; -} - -/** - * Form validation handler for the form_test_clicked_button() form. - */ -function form_test_clicked_button_validate($form, &$form_state) { - if (isset($form_state['triggering_element'])) { - drupal_set_message(t('The clicked button is %name.', array('%name' => $form_state['triggering_element']['#name']))); - } - else { - drupal_set_message('There is no clicked button.'); - } -} - -/** - * Form submit handler for the form_test_clicked_button() form. - */ -function form_test_clicked_button_submit($form, &$form_state) { - drupal_set_message('Submit handler for form_test_clicked_button executed.'); -} - -/** - * Implements hook_form_FORM_ID_alter() for the registration form. - */ -function form_test_form_user_register_form_alter(&$form, &$form_state) { - $form['test_rebuild'] = array( - '#type' => 'submit', - '#value' => t('Rebuild'), - '#submit' => array('form_test_user_register_form_rebuild'), - ); - // If requested, add the test field by attaching the node page form. - if (!empty($_REQUEST['field'])) { - $node = (object)array('type' => 'page'); - field_attach_form('node', $node, $form, $form_state); - } -} - -/** - * Submit callback that just lets the form rebuild. - */ -function form_test_user_register_form_rebuild($form, &$form_state) { - drupal_set_message('Form rebuilt.'); - $form_state['rebuild'] = TRUE; -} - -/** - * Menu callback that returns two instances of the node form. - */ -function form_test_two_instances() { - global $user; - $node1 = (object) array( - 'uid' => $user->uid, - 'name' => (isset($user->name) ? $user->name : ''), - 'type' => 'page', - 'language' => LANGUAGE_NONE, - ); - $node2 = clone($node1); - $return['node_form_1'] = drupal_get_form('page_node_form', $node1); - $return['node_form_2'] = drupal_get_form('page_node_form', $node2); - return $return; -} - -/** - * Menu callback for testing custom form includes. - */ -function form_test_load_include_custom($form, &$form_state) { - $form['button'] = array( - '#type' => 'submit', - '#value' => t('Save'), - '#submit' => array('form_test_load_include_submit'), - ); - // Specify the include file and enable form caching. That way the form is - // cached when it is submitted, but needs to find the specified submit handler - // in the include. - // Filename is a bit weird here: modules/simpletest/tests/form_test.file.inc - form_load_include($form_state, 'inc', 'form_test', 'form_test.file'); - $form_state['cache'] = TRUE; - return $form; -} - -function form_test_checkbox_type_juggling($form, $form_state, $default_value, $return_value) { - $form['checkbox'] = array( - '#type' => 'checkbox', - '#return_value' => $return_value, - '#default_value' => $default_value, - ); - return $form; -} - -function form_test_checkboxes_zero($form, &$form_state, $json = TRUE) { - $form['checkbox_off'] = array( - '#type' => 'checkboxes', - '#options' => array('foo', 'bar', 'baz'), - ); - $form['checkbox_zero_default'] = array( - '#type' => 'checkboxes', - '#options' => array('foo', 'bar', 'baz'), - '#default_value' => array(0), - ); - $form['checkbox_string_zero_default'] = array( - '#type' => 'checkboxes', - '#options' => array('foo', 'bar', 'baz'), - '#default_value' => array('0'), - ); - $form['submit'] = array( - '#type' => 'submit', - '#value' => 'Save', - ); - if ($json) { - $form['#submit'][] = '_form_test_checkbox_submit'; - } - else { - $form['#submit'][] = '_form_test_checkboxes_zero_no_redirect'; - } - return $form; -} - -function _form_test_checkboxes_zero_no_redirect($form, &$form_state) { - $form_state['redirect'] = FALSE; -} diff --git a/modules/simpletest/tests/graph.test b/modules/simpletest/tests/graph.test deleted file mode 100644 index c190161b4fb..00000000000 --- a/modules/simpletest/tests/graph.test +++ /dev/null @@ -1,195 +0,0 @@ -<?php - -/** - * @file - * Provides unit tests for graph.inc. - */ - -/** - * Unit tests for the graph handling features. - */ -class GraphUnitTest extends DrupalUnitTestCase { - public static function getInfo() { - return array( - 'name' => 'Graph', - 'description' => 'Graph handling unit tests.', - 'group' => 'System', - ); - } - - function setUp() { - require_once DRUPAL_ROOT . '/includes/graph.inc'; - parent::setUp(); - } - - /** - * Test depth-first-search features. - */ - function testDepthFirstSearch() { - // The sample graph used is: - // 1 --> 2 --> 3 5 ---> 6 - // | ^ ^ - // | | | - // | | | - // +---> 4 <-- 7 8 ---> 9 - $graph = $this->normalizeGraph(array( - 1 => array(2), - 2 => array(3, 4), - 3 => array(), - 4 => array(3), - 5 => array(6), - 7 => array(4, 5), - 8 => array(9), - 9 => array(), - )); - drupal_depth_first_search($graph); - - $expected_paths = array( - 1 => array(2, 3, 4), - 2 => array(3, 4), - 3 => array(), - 4 => array(3), - 5 => array(6), - 7 => array(4, 3, 5, 6), - 8 => array(9), - 9 => array(), - ); - $this->assertPaths($graph, $expected_paths); - - $expected_reverse_paths = array( - 1 => array(), - 2 => array(1), - 3 => array(2, 1, 4, 7), - 4 => array(2, 1, 7), - 5 => array(7), - 7 => array(), - 8 => array(), - 9 => array(8), - ); - $this->assertReversePaths($graph, $expected_reverse_paths); - - // Assert that DFS didn't created "missing" vertexes automatically. - $this->assertFALSE(isset($graph[6]), t('Vertex 6 has not been created')); - - $expected_components = array( - array(1, 2, 3, 4, 5, 7), - array(8, 9), - ); - $this->assertComponents($graph, $expected_components); - - $expected_weights = array( - array(1, 2, 3), - array(2, 4, 3), - array(7, 4, 3), - array(7, 5), - array(8, 9), - ); - $this->assertWeights($graph, $expected_weights); - } - - /** - * Return a normalized version of a graph. - */ - function normalizeGraph($graph) { - $normalized_graph = array(); - foreach ($graph as $vertex => $edges) { - // Create vertex even if it hasn't any edges. - $normalized_graph[$vertex] = array(); - foreach ($edges as $edge) { - $normalized_graph[$vertex]['edges'][$edge] = TRUE; - } - } - return $normalized_graph; - } - - /** - * Verify expected paths in a graph. - * - * @param $graph - * A graph array processed by drupal_depth_first_search(). - * @param $expected_paths - * An associative array containing vertices with their expected paths. - */ - function assertPaths($graph, $expected_paths) { - foreach ($expected_paths as $vertex => $paths) { - // Build an array with keys = $paths and values = TRUE. - $expected = array_fill_keys($paths, TRUE); - $result = isset($graph[$vertex]['paths']) ? $graph[$vertex]['paths'] : array(); - $this->assertEqual($expected, $result, t('Expected paths for vertex @vertex: @expected-paths, got @paths', array('@vertex' => $vertex, '@expected-paths' => $this->displayArray($expected, TRUE), '@paths' => $this->displayArray($result, TRUE)))); - } - } - - /** - * Verify expected reverse paths in a graph. - * - * @param $graph - * A graph array processed by drupal_depth_first_search(). - * @param $expected_reverse_paths - * An associative array containing vertices with their expected reverse - * paths. - */ - function assertReversePaths($graph, $expected_reverse_paths) { - foreach ($expected_reverse_paths as $vertex => $paths) { - // Build an array with keys = $paths and values = TRUE. - $expected = array_fill_keys($paths, TRUE); - $result = isset($graph[$vertex]['reverse_paths']) ? $graph[$vertex]['reverse_paths'] : array(); - $this->assertEqual($expected, $result, t('Expected reverse paths for vertex @vertex: @expected-paths, got @paths', array('@vertex' => $vertex, '@expected-paths' => $this->displayArray($expected, TRUE), '@paths' => $this->displayArray($result, TRUE)))); - } - } - - /** - * Verify expected components in a graph. - * - * @param $graph - * A graph array processed by drupal_depth_first_search(). - * @param $expected_components - * An array containing of components defined as a list of their vertices. - */ - function assertComponents($graph, $expected_components) { - $unassigned_vertices = array_fill_keys(array_keys($graph), TRUE); - foreach ($expected_components as $component) { - $result_components = array(); - foreach ($component as $vertex) { - $result_components[] = $graph[$vertex]['component']; - unset($unassigned_vertices[$vertex]); - } - $this->assertEqual(1, count(array_unique($result_components)), t('Expected one unique component for vertices @vertices, got @components', array('@vertices' => $this->displayArray($component), '@components' => $this->displayArray($result_components)))); - } - $this->assertEqual(array(), $unassigned_vertices, t('Vertices not assigned to a component: @vertices', array('@vertices' => $this->displayArray($unassigned_vertices, TRUE)))); - } - - /** - * Verify expected order in a graph. - * - * @param $graph - * A graph array processed by drupal_depth_first_search(). - * @param $expected_orders - * An array containing lists of vertices in their expected order. - */ - function assertWeights($graph, $expected_orders) { - foreach ($expected_orders as $order) { - $previous_vertex = array_shift($order); - foreach ($order as $vertex) { - $this->assertTrue($graph[$previous_vertex]['weight'] < $graph[$vertex]['weight'], t('Weights of @previous-vertex and @vertex are correct relative to each other', array('@previous-vertex' => $previous_vertex, '@vertex' => $vertex))); - } - } - } - - /** - * Helper function to output vertices as comma-separated list. - * - * @param $paths - * An array containing a list of vertices. - * @param $keys - * (optional) Whether to output the keys of $paths instead of the values. - */ - function displayArray($paths, $keys = FALSE) { - if (!empty($paths)) { - return implode(', ', $keys ? array_keys($paths) : $paths); - } - else { - return '(empty)'; - } - } -} - diff --git a/modules/simpletest/tests/http.php b/modules/simpletest/tests/http.php deleted file mode 100644 index a22938d9bea..00000000000 --- a/modules/simpletest/tests/http.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php - -/** - * @file - * Fake an HTTP request, for use during testing. - */ - -// Set a global variable to indicate a mock HTTP request. -$is_http_mock = !empty($_SERVER['HTTPS']); - -// Change to HTTP. -$_SERVER['HTTPS'] = NULL; -ini_set('session.cookie_secure', FALSE); -foreach ($_SERVER as $key => $value) { - $_SERVER[$key] = str_replace('modules/simpletest/tests/http.php', 'index.php', $value); - $_SERVER[$key] = str_replace('https://', 'http://', $_SERVER[$key]); -} - -// Change current directory to the Drupal root. -chdir('../../..'); -define('DRUPAL_ROOT', getcwd()); -require_once DRUPAL_ROOT . '/includes/bootstrap.inc'; - -// Make sure this file can only be used by simpletest. -drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); -if (!drupal_valid_test_ua()) { - header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden'); - exit; -} - -drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); -menu_execute_active_handler(); diff --git a/modules/simpletest/tests/https.php b/modules/simpletest/tests/https.php deleted file mode 100644 index b5ffab69345..00000000000 --- a/modules/simpletest/tests/https.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php - -/** - * @file - * Fake an https request, for use during testing. - */ - -// Set a global variable to indicate a mock HTTPS request. -$is_https_mock = empty($_SERVER['HTTPS']); - -// Change to https. -$_SERVER['HTTPS'] = 'on'; -foreach ($_SERVER as $key => $value) { - $_SERVER[$key] = str_replace('modules/simpletest/tests/https.php', 'index.php', $value); - $_SERVER[$key] = str_replace('http://', 'https://', $_SERVER[$key]); -} - -// Change current directory to the Drupal root. -chdir('../../..'); -define('DRUPAL_ROOT', getcwd()); -require_once DRUPAL_ROOT . '/includes/bootstrap.inc'; - -// Make sure this file can only be used by simpletest. -drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); -if (!drupal_valid_test_ua()) { - header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden'); - exit; -} - -drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); -menu_execute_active_handler(); diff --git a/modules/simpletest/tests/image.test b/modules/simpletest/tests/image.test deleted file mode 100644 index 60599bee529..00000000000 --- a/modules/simpletest/tests/image.test +++ /dev/null @@ -1,460 +0,0 @@ -<?php - -/** - * @file - * Tests for core image handling API. - */ - -/** - * Base class for image manipulation testing. - */ -class ImageToolkitTestCase extends DrupalWebTestCase { - protected $toolkit; - protected $file; - protected $image; - - function setUp() { - parent::setUp('image_test'); - - // Use the image_test.module's test toolkit. - $this->toolkit = 'test'; - - // Pick a file for testing. - $file = current($this->drupalGetTestFiles('image')); - $this->file = $file->uri; - - // Setup a dummy image to work with, this replicate image_load() so we - // can avoid calling it. - $this->image = new stdClass(); - $this->image->source = $this->file; - $this->image->info = image_get_info($this->file); - $this->image->toolkit = $this->toolkit; - - // Clear out any hook calls. - image_test_reset(); - } - - /** - * Assert that all of the specified image toolkit operations were called - * exactly once once, other values result in failure. - * - * @param $expected - * Array with string containing with the operation name, e.g. 'load', - * 'save', 'crop', etc. - */ - function assertToolkitOperationsCalled(array $expected) { - // Determine which operations were called. - $actual = array_keys(array_filter(image_test_get_all_calls())); - - // Determine if there were any expected that were not called. - $uncalled = array_diff($expected, $actual); - if (count($uncalled)) { - $this->assertTrue(FALSE, t('Expected operations %expected to be called but %uncalled was not called.', array('%expected' => implode(', ', $expected), '%uncalled' => implode(', ', $uncalled)))); - } - else { - $this->assertTrue(TRUE, t('All the expected operations were called: %expected', array('%expected' => implode(', ', $expected)))); - } - - // Determine if there were any unexpected calls. - $unexpected = array_diff($actual, $expected); - if (count($unexpected)) { - $this->assertTrue(FALSE, t('Unexpected operations were called: %unexpected.', array('%unexpected' => implode(', ', $unexpected)))); - } - else { - $this->assertTrue(TRUE, t('No unexpected operations were called.')); - } - } -} - -/** - * Test that the functions in image.inc correctly pass data to the toolkit. - */ -class ImageToolkitUnitTest extends ImageToolkitTestCase { - public static function getInfo() { - return array( - 'name' => 'Image toolkit tests', - 'description' => 'Check image toolkit functions.', - 'group' => 'Image', - ); - } - - /** - * Check that hook_image_toolkits() is called and only available toolkits are - * returned. - */ - function testGetAvailableToolkits() { - $toolkits = image_get_available_toolkits(); - $this->assertTrue(isset($toolkits['test']), t('The working toolkit was returned.')); - $this->assertFalse(isset($toolkits['broken']), t('The toolkit marked unavailable was not returned')); - $this->assertToolkitOperationsCalled(array()); - } - - /** - * Test the image_load() function. - */ - function testLoad() { - $image = image_load($this->file, $this->toolkit); - $this->assertTrue(is_object($image), t('Returned an object.')); - $this->assertEqual($this->toolkit, $image->toolkit, t('Image had toolkit set.')); - $this->assertToolkitOperationsCalled(array('load', 'get_info')); - } - - /** - * Test the image_save() function. - */ - function testSave() { - $this->assertFalse(image_save($this->image), t('Function returned the expected value.')); - $this->assertToolkitOperationsCalled(array('save')); - } - - /** - * Test the image_resize() function. - */ - function testResize() { - $this->assertTrue(image_resize($this->image, 1, 2), t('Function returned the expected value.')); - $this->assertToolkitOperationsCalled(array('resize')); - - // Check the parameters. - $calls = image_test_get_all_calls(); - $this->assertEqual($calls['resize'][0][1], 1, t('Width was passed correctly')); - $this->assertEqual($calls['resize'][0][2], 2, t('Height was passed correctly')); - } - - /** - * Test the image_scale() function. - */ - function testScale() { -// TODO: need to test upscaling - $this->assertTrue(image_scale($this->image, 10, 10), t('Function returned the expected value.')); - $this->assertToolkitOperationsCalled(array('resize')); - - // Check the parameters. - $calls = image_test_get_all_calls(); - $this->assertEqual($calls['resize'][0][1], 10, t('Width was passed correctly')); - $this->assertEqual($calls['resize'][0][2], 5, t('Height was based off aspect ratio and passed correctly')); - } - - /** - * Test the image_scale_and_crop() function. - */ - function testScaleAndCrop() { - $this->assertTrue(image_scale_and_crop($this->image, 5, 10), t('Function returned the expected value.')); - $this->assertToolkitOperationsCalled(array('resize', 'crop')); - - // Check the parameters. - $calls = image_test_get_all_calls(); - - $this->assertEqual($calls['crop'][0][1], 7.5, t('X was computed and passed correctly')); - $this->assertEqual($calls['crop'][0][2], 0, t('Y was computed and passed correctly')); - $this->assertEqual($calls['crop'][0][3], 5, t('Width was computed and passed correctly')); - $this->assertEqual($calls['crop'][0][4], 10, t('Height was computed and passed correctly')); - } - - /** - * Test the image_rotate() function. - */ - function testRotate() { - $this->assertTrue(image_rotate($this->image, 90, 1), t('Function returned the expected value.')); - $this->assertToolkitOperationsCalled(array('rotate')); - - // Check the parameters. - $calls = image_test_get_all_calls(); - $this->assertEqual($calls['rotate'][0][1], 90, t('Degrees were passed correctly')); - $this->assertEqual($calls['rotate'][0][2], 1, t('Background color was passed correctly')); - } - - /** - * Test the image_crop() function. - */ - function testCrop() { - $this->assertTrue(image_crop($this->image, 1, 2, 3, 4), t('Function returned the expected value.')); - $this->assertToolkitOperationsCalled(array('crop')); - - // Check the parameters. - $calls = image_test_get_all_calls(); - $this->assertEqual($calls['crop'][0][1], 1, t('X was passed correctly')); - $this->assertEqual($calls['crop'][0][2], 2, t('Y was passed correctly')); - $this->assertEqual($calls['crop'][0][3], 3, t('Width was passed correctly')); - $this->assertEqual($calls['crop'][0][4], 4, t('Height was passed correctly')); - } - - /** - * Test the image_desaturate() function. - */ - function testDesaturate() { - $this->assertTrue(image_desaturate($this->image), t('Function returned the expected value.')); - $this->assertToolkitOperationsCalled(array('desaturate')); - - // Check the parameters. - $calls = image_test_get_all_calls(); - $this->assertEqual(count($calls['desaturate'][0]), 1, t('Only the image was passed.')); - } -} - -/** - * Test the core GD image manipulation functions. - */ -class ImageToolkitGdTestCase extends DrupalWebTestCase { - // Colors that are used in testing. - protected $black = array(0, 0, 0, 0); - protected $red = array(255, 0, 0, 0); - protected $green = array(0, 255, 0, 0); - protected $blue = array(0, 0, 255, 0); - protected $yellow = array(255, 255, 0, 0); - protected $fuchsia = array(255, 0, 255, 0); // Used as background colors. - protected $transparent = array(0, 0, 0, 127); - protected $white = array(255, 255, 255, 0); - - protected $width = 40; - protected $height = 20; - - public static function getInfo() { - return array( - 'name' => 'Image GD manipulation tests', - 'description' => 'Check that core image manipulations work properly: scale, resize, rotate, crop, scale and crop, and desaturate.', - 'group' => 'Image', - ); - } - - /** - * Function to compare two colors by RGBa. - */ - function colorsAreEqual($color_a, $color_b) { - // Fully transparent pixels are equal, regardless of RGB. - if ($color_a[3] == 127 && $color_b[3] == 127) { - return TRUE; - } - - foreach ($color_a as $key => $value) { - if ($color_b[$key] != $value) { - return FALSE; - } - } - - return TRUE; - } - - /** - * Function for finding a pixel's RGBa values. - */ - function getPixelColor($image, $x, $y) { - $color_index = imagecolorat($image->resource, $x, $y); - - $transparent_index = imagecolortransparent($image->resource); - if ($color_index == $transparent_index) { - return array(0, 0, 0, 127); - } - - return array_values(imagecolorsforindex($image->resource, $color_index)); - } - - /** - * Since PHP can't visually check that our images have been manipulated - * properly, build a list of expected color values for each of the corners and - * the expected height and widths for the final images. - */ - function testManipulations() { - // If GD isn't available don't bother testing this. - if (!function_exists('image_gd_check_settings') || !image_gd_check_settings()) { - $this->pass(t('Image manipulations for the GD toolkit were skipped because the GD toolkit is not available.')); - return; - } - - // Typically the corner colors will be unchanged. These colors are in the - // order of top-left, top-right, bottom-right, bottom-left. - $default_corners = array($this->red, $this->green, $this->blue, $this->transparent); - - // A list of files that will be tested. - $files = array( - 'image-test.png', - 'image-test.gif', - 'image-test.jpg', - ); - - // Setup a list of tests to perform on each type. - $operations = array( - 'resize' => array( - 'function' => 'resize', - 'arguments' => array(20, 10), - 'width' => 20, - 'height' => 10, - 'corners' => $default_corners, - ), - 'scale_x' => array( - 'function' => 'scale', - 'arguments' => array(20, NULL), - 'width' => 20, - 'height' => 10, - 'corners' => $default_corners, - ), - 'scale_y' => array( - 'function' => 'scale', - 'arguments' => array(NULL, 10), - 'width' => 20, - 'height' => 10, - 'corners' => $default_corners, - ), - 'upscale_x' => array( - 'function' => 'scale', - 'arguments' => array(80, NULL, TRUE), - 'width' => 80, - 'height' => 40, - 'corners' => $default_corners, - ), - 'upscale_y' => array( - 'function' => 'scale', - 'arguments' => array(NULL, 40, TRUE), - 'width' => 80, - 'height' => 40, - 'corners' => $default_corners, - ), - 'crop' => array( - 'function' => 'crop', - 'arguments' => array(12, 4, 16, 12), - 'width' => 16, - 'height' => 12, - 'corners' => array_fill(0, 4, $this->white), - ), - 'scale_and_crop' => array( - 'function' => 'scale_and_crop', - 'arguments' => array(10, 8), - 'width' => 10, - 'height' => 8, - 'corners' => array_fill(0, 4, $this->black), - ), - ); - - // Systems using non-bundled GD2 don't have imagerotate. Test if available. - if (function_exists('imagerotate')) { - $operations += array( - 'rotate_5' => array( - 'function' => 'rotate', - 'arguments' => array(5, 0xFF00FF), // Fuchsia background. - 'width' => 42, - 'height' => 24, - 'corners' => array_fill(0, 4, $this->fuchsia), - ), - 'rotate_90' => array( - 'function' => 'rotate', - 'arguments' => array(90, 0xFF00FF), // Fuchsia background. - 'width' => 20, - 'height' => 40, - 'corners' => array($this->fuchsia, $this->red, $this->green, $this->blue), - ), - 'rotate_transparent_5' => array( - 'function' => 'rotate', - 'arguments' => array(5), - 'width' => 42, - 'height' => 24, - 'corners' => array_fill(0, 4, $this->transparent), - ), - 'rotate_transparent_90' => array( - 'function' => 'rotate', - 'arguments' => array(90), - 'width' => 20, - 'height' => 40, - 'corners' => array($this->transparent, $this->red, $this->green, $this->blue), - ), - ); - } - - // Systems using non-bundled GD2 don't have imagefilter. Test if available. - if (function_exists('imagefilter')) { - $operations += array( - 'desaturate' => array( - 'function' => 'desaturate', - 'arguments' => array(), - 'height' => 20, - 'width' => 40, - // Grayscale corners are a bit funky. Each of the corners are a shade of - // gray. The values of these were determined simply by looking at the - // final image to see what desaturated colors end up being. - 'corners' => array( - array_fill(0, 3, 76) + array(3 => 0), - array_fill(0, 3, 149) + array(3 => 0), - array_fill(0, 3, 29) + array(3 => 0), - array_fill(0, 3, 0) + array(3 => 127) - ), - ), - ); - } - - foreach ($files as $file) { - foreach ($operations as $op => $values) { - // Load up a fresh image. - $image = image_load(drupal_get_path('module', 'simpletest') . '/files/' . $file, 'gd'); - if (!$image) { - $this->fail(t('Could not load image %file.', array('%file' => $file))); - continue 2; - } - - // Transparent GIFs and the imagefilter function don't work together. - // There is a todo in image.gd.inc to correct this. - if ($image->info['extension'] == 'gif') { - if ($op == 'desaturate') { - $values['corners'][3] = $this->white; - } - } - - // Perform our operation. - $function = 'image_' . $values['function']; - $arguments = array(); - $arguments[] = &$image; - $arguments = array_merge($arguments, $values['arguments']); - call_user_func_array($function, $arguments); - - // To keep from flooding the test with assert values, make a general - // value for whether each group of values fail. - $correct_dimensions_real = TRUE; - $correct_dimensions_object = TRUE; - $correct_colors = TRUE; - - // Check the real dimensions of the image first. - if (imagesy($image->resource) != $values['height'] || imagesx($image->resource) != $values['width']) { - $correct_dimensions_real = FALSE; - } - - // Check that the image object has an accurate record of the dimensions. - if ($image->info['width'] != $values['width'] || $image->info['height'] != $values['height']) { - $correct_dimensions_object = FALSE; - } - // Now check each of the corners to ensure color correctness. - foreach ($values['corners'] as $key => $corner) { - // Get the location of the corner. - switch ($key) { - case 0: - $x = 0; - $y = 0; - break; - case 1: - $x = $values['width'] - 1; - $y = 0; - break; - case 2: - $x = $values['width'] - 1; - $y = $values['height'] - 1; - break; - case 3: - $x = 0; - $y = $values['height'] - 1; - break; - } - $color = $this->getPixelColor($image, $x, $y); - $correct_colors = $this->colorsAreEqual($color, $corner); - } - - $directory = file_default_scheme() . '://imagetests'; - file_prepare_directory($directory, FILE_CREATE_DIRECTORY); - image_save($image, $directory . '/' . $op . '.' . $image->info['extension']); - - $this->assertTrue($correct_dimensions_real, t('Image %file after %action action has proper dimensions.', array('%file' => $file, '%action' => $op))); - $this->assertTrue($correct_dimensions_object, t('Image %file object after %action action is reporting the proper height and width values.', array('%file' => $file, '%action' => $op))); - // JPEG colors will always be messed up due to compression. - if ($image->info['extension'] != 'jpg') { - $this->assertTrue($correct_colors, t('Image %file object after %action action has the correct color placement.', array('%file' => $file, '%action' => $op))); - } - } - } - - } -} diff --git a/modules/simpletest/tests/image_test.info b/modules/simpletest/tests/image_test.info deleted file mode 100644 index becc207718d..00000000000 --- a/modules/simpletest/tests/image_test.info +++ /dev/null @@ -1,6 +0,0 @@ -name = "Image test" -description = "Support module for image toolkit tests." -package = Testing -version = VERSION -core = 8.x -hidden = TRUE diff --git a/modules/simpletest/tests/image_test.module b/modules/simpletest/tests/image_test.module deleted file mode 100644 index de640f0ba24..00000000000 --- a/modules/simpletest/tests/image_test.module +++ /dev/null @@ -1,138 +0,0 @@ -<?php - -/** - * @file - * Helper module for the image tests. - */ - -/** - * Implements hook_image_toolkits(). - */ -function image_test_image_toolkits() { - return array( - 'test' => array( - 'title' => t('A dummy toolkit that works'), - 'available' => TRUE, - ), - 'broken' => array( - 'title' => t('A dummy toolkit that is "broken"'), - 'available' => FALSE, - ), - ); -} - -/** - * Reset/initialize the history of calls to the toolkit functions. - * - * @see image_test_get_all_calls() - */ -function image_test_reset() { - // Keep track of calls to these operations - $results = array( - 'load' => array(), - 'save' => array(), - 'settings' => array(), - 'resize' => array(), - 'rotate' => array(), - 'crop' => array(), - 'desaturate' => array(), - ); - variable_set('image_test_results', $results); -} - -/** - * Get an array with the all the calls to the toolkits since image_test_reset() - * was called. - * - * @return - * An array keyed by operation name ('load', 'save', 'settings', 'resize', - * 'rotate', 'crop', 'desaturate') with values being arrays of parameters - * passed to each call. - */ -function image_test_get_all_calls() { - return variable_get('image_test_results', array()); -} - -/** - * Store the values passed to a toolkit call. - * - * @param $op - * One of the image toolkit operations: 'get_info', 'load', 'save', - * 'settings', 'resize', 'rotate', 'crop', 'desaturate'. - * @param $args - * Values passed to hook. - * - * @see image_test_get_all_calls() - * @see image_test_reset() - */ -function _image_test_log_call($op, $args) { - $results = variable_get('image_test_results', array()); - $results[$op][] = $args; - variable_set('image_test_results', $results); -} - -/** - * Image tookit's settings operation. - */ -function image_test_settings() { - _image_test_log_call('settings', array()); - return array(); -} - -/** - * Image toolkit's get_info operation. - */ -function image_test_get_info(stdClass $image) { - _image_test_log_call('get_info', array($image)); - return array(); -} - -/** - * Image tookit's load operation. - */ -function image_test_load(stdClass $image) { - _image_test_log_call('load', array($image)); - return $image; -} - -/** - * Image tookit's save operation. - */ -function image_test_save(stdClass $image, $destination) { - _image_test_log_call('save', array($image, $destination)); - // Return false so that image_save() doesn't try to chmod the destination - // file that we didn't bother to create. - return FALSE; -} - -/** - * Image tookit's crop operation. - */ -function image_test_crop(stdClass $image, $x, $y, $width, $height) { - _image_test_log_call('crop', array($image, $x, $y, $width, $height)); - return TRUE; -} - -/** - * Image tookit's resize operation. - */ -function image_test_resize(stdClass $image, $width, $height) { - _image_test_log_call('resize', array($image, $width, $height)); - return TRUE; -} - -/** - * Image tookit's rotate operation. - */ -function image_test_rotate(stdClass $image, $degrees, $background = NULL) { - _image_test_log_call('rotate', array($image, $degrees, $background)); - return TRUE; -} - -/** - * Image tookit's desaturate operation. - */ -function image_test_desaturate(stdClass $image) { - _image_test_log_call('desaturate', array($image)); - return TRUE; -} diff --git a/modules/simpletest/tests/lock.test b/modules/simpletest/tests/lock.test deleted file mode 100644 index 0b423ffdd4d..00000000000 --- a/modules/simpletest/tests/lock.test +++ /dev/null @@ -1,57 +0,0 @@ -<?php - -/** - * Tests for the lock system. - */ -class LockFunctionalTest extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Locking framework tests', - 'description' => 'Confirm locking works between two separate requests.', - 'group' => 'System', - ); - } - - function setUp() { - parent::setUp('system_test'); - } - - /** - * Confirm that we can acquire and release locks in two parallel requests. - */ - function testLockAcquire() { - $lock_acquired = 'TRUE: Lock successfully acquired in system_test_lock_acquire()'; - $lock_not_acquired = 'FALSE: Lock not acquired in system_test_lock_acquire()'; - $this->assertTrue(lock_acquire('system_test_lock_acquire'), t('Lock acquired by this request.'), t('Lock')); - $this->assertTrue(lock_acquire('system_test_lock_acquire'), t('Lock extended by this request.'), t('Lock')); - lock_release('system_test_lock_acquire'); - - // Cause another request to acquire the lock. - $this->drupalGet('system-test/lock-acquire'); - $this->assertText($lock_acquired, t('Lock acquired by the other request.'), t('Lock')); - // The other request has finished, thus it should have released its lock. - $this->assertTrue(lock_acquire('system_test_lock_acquire'), t('Lock acquired by this request.'), t('Lock')); - // This request holds the lock, so the other request cannot acquire it. - $this->drupalGet('system-test/lock-acquire'); - $this->assertText($lock_not_acquired, t('Lock not acquired by the other request.'), t('Lock')); - lock_release('system_test_lock_acquire'); - - // Try a very short timeout and lock breaking. - $this->assertTrue(lock_acquire('system_test_lock_acquire', 0.5), t('Lock acquired by this request.'), t('Lock')); - sleep(1); - // The other request should break our lock. - $this->drupalGet('system-test/lock-acquire'); - $this->assertText($lock_acquired, t('Lock acquired by the other request, breaking our lock.'), t('Lock')); - // We cannot renew it, since the other thread took it. - $this->assertFalse(lock_acquire('system_test_lock_acquire'), t('Lock cannot be extended by this request.'), t('Lock')); - - // Check the shut-down function. - $lock_acquired_exit = 'TRUE: Lock successfully acquired in system_test_lock_exit()'; - $lock_not_acquired_exit = 'FALSE: Lock not acquired in system_test_lock_exit()'; - $this->drupalGet('system-test/lock-exit'); - $this->assertText($lock_acquired_exit, t('Lock acquired by the other request before exit.'), t('Lock')); - $this->assertTrue(lock_acquire('system_test_lock_exit'), t('Lock acquired by this request after the other request exits.'), t('Lock')); - } -} - diff --git a/modules/simpletest/tests/mail.test b/modules/simpletest/tests/mail.test deleted file mode 100644 index a6c7b40e5ef..00000000000 --- a/modules/simpletest/tests/mail.test +++ /dev/null @@ -1,418 +0,0 @@ -<?php - -/** - * @file - * Test the Drupal mailing system. - */ -class MailTestCase extends DrupalWebTestCase implements MailSystemInterface { - /** - * The most recent message that was sent through the test case. - * - * We take advantage here of the fact that static variables are shared among - * all instance of the same class. - */ - private static $sent_message; - - public static function getInfo() { - return array( - 'name' => 'Mail system', - 'description' => 'Performs tests on the pluggable mailing framework.', - 'group' => 'System', - ); - } - - function setUp() { - parent::setUp(); - - // Set MailTestCase (i.e. this class) as the SMTP library - variable_set('mail_system', array('default-system' => 'MailTestCase')); - } - - /** - * Assert that the pluggable mail system is functional. - */ - function testPluggableFramework() { - global $language; - - // Use MailTestCase for sending a message. - $message = drupal_mail('simpletest', 'mail_test', 'testing@drupal.org', $language); - - // Assert whether the message was sent through the send function. - $this->assertEqual(self::$sent_message['to'], 'testing@drupal.org', t('Pluggable mail system is extendable.')); - } - - /** - * Concatenate and wrap the e-mail body for plain-text mails. - * - * @see DefaultMailSystem - */ - public function format(array $message) { - // Join the body array into one string. - $message['body'] = implode("\n\n", $message['body']); - // Convert any HTML to plain-text. - $message['body'] = drupal_html_to_text($message['body']); - // Wrap the mail body for sending. - $message['body'] = drupal_wrap_mail($message['body']); - return $message; - } - - /** - * Send function that is called through the mail system. - */ - public function mail(array $message) { - self::$sent_message = $message; - } -} - -/** - * Unit tests for drupal_html_to_text(). - */ -class DrupalHtmlToTextTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'HTML to text conversion', - 'description' => 'Tests drupal_html_to_text().', - 'group' => 'Mail', - ); - } - - /** - * Converts a string to its PHP source equivalent for display in test messages. - * - * @param $text - * The text string to convert. - * - * @return - * An HTML representation of the text string that, when displayed in a - * browser, represents the PHP source code equivalent of $text. - */ - function stringToHtml($text) { - return '"' . - str_replace( - array("\n", ' '), - array('\n', ' '), - check_plain($text) - ) . '"'; - } - - /** - * Helper function for testing drupal_html_to_text(). - * - * @param $html - * The source HTML string to be converted. - * @param $text - * The expected result of converting $html to text. - * @param $message - * A text message to display in the assertion message. - * @param $allowed_tags - * (optional) An array of allowed tags, or NULL to default to the full - * set of tags supported by drupal_html_to_text(). - */ - function assertHtmlToText($html, $text, $message, $allowed_tags = NULL) { - preg_match_all('/<([a-z0-6]+)/', drupal_strtolower($html), $matches); - $tested_tags = implode(', ', array_unique($matches[1])); - $message .= ' (' . $tested_tags . ')'; - $result = drupal_html_to_text($html, $allowed_tags); - $pass = $this->assertEqual($result, $text, check_plain($message)); - $verbose = 'html = <pre>' . $this->stringToHtml($html) - . '</pre><br />' . 'result = <pre>' . $this->stringToHtml($result) - . '</pre><br />' . 'expected = <pre>' . $this->stringToHtml($text) - . '</pre>'; - $this->verbose($verbose); - if (!$pass) { - $this->pass("Previous test verbose info:<br />$verbose"); - } - } - - /** - * Test all supported tags of drupal_html_to_text(). - */ - function testTags() { - global $base_path, $base_url; - $tests = array( - // @todo Trailing linefeeds should be trimmed. - '<a href = "http://drupal.org">Drupal.org</a>' => "Drupal.org [1]\n\n[1] http://drupal.org\n", - // @todo Footer urls should be absolute. - "<a href = \"$base_path\">Homepage</a>" => "Homepage [1]\n\n[1] $base_url/\n", - '<address>Drupal</address>' => "Drupal\n", - // @todo The <address> tag is currently not supported. - '<address>Drupal</address><address>Drupal</address>' => "DrupalDrupal\n", - '<b>Drupal</b>' => "*Drupal*\n", - // @todo There should be a space between the '>' and the text. - '<blockquote>Drupal</blockquote>' => ">Drupal\n", - '<blockquote>Drupal</blockquote><blockquote>Drupal</blockquote>' => ">Drupal\n>Drupal\n", - '<br />Drupal<br />Drupal<br /><br />Drupal' => "Drupal\nDrupal\nDrupal\n", - '<br/>Drupal<br/>Drupal<br/><br/>Drupal' => "Drupal\nDrupal\nDrupal\n", - // @todo There should be two line breaks before the paragraph. - '<br/>Drupal<br/>Drupal<br/><br/>Drupal<p>Drupal</p>' => "Drupal\nDrupal\nDrupal\nDrupal\n\n", - '<div>Drupal</div>' => "Drupal\n", - // @todo The <div> tag is currently not supported. - '<div>Drupal</div><div>Drupal</div>' => "DrupalDrupal\n", - '<em>Drupal</em>' => "/Drupal/\n", - '<h1>Drupal</h1>' => "======== DRUPAL ==============================================================\n\n", - '<h1>Drupal</h1><p>Drupal</p>' => "======== DRUPAL ==============================================================\n\nDrupal\n\n", - '<h2>Drupal</h2>' => "-------- DRUPAL --------------------------------------------------------------\n\n", - '<h2>Drupal</h2><p>Drupal</p>' => "-------- DRUPAL --------------------------------------------------------------\n\nDrupal\n\n", - '<h3>Drupal</h3>' => ".... Drupal\n\n", - '<h3>Drupal</h3><p>Drupal</p>' => ".... Drupal\n\nDrupal\n\n", - '<h4>Drupal</h4>' => ".. Drupal\n\n", - '<h4>Drupal</h4><p>Drupal</p>' => ".. Drupal\n\nDrupal\n\n", - '<h5>Drupal</h5>' => "Drupal\n\n", - '<h5>Drupal</h5><p>Drupal</p>' => "Drupal\n\nDrupal\n\n", - '<h6>Drupal</h6>' => "Drupal\n\n", - '<h6>Drupal</h6><p>Drupal</p>' => "Drupal\n\nDrupal\n\n", - '<hr />Drupal<hr />' => "------------------------------------------------------------------------------\nDrupal\n------------------------------------------------------------------------------\n", - '<hr/>Drupal<hr/>' => "------------------------------------------------------------------------------\nDrupal\n------------------------------------------------------------------------------\n", - '<hr/>Drupal<hr/><p>Drupal</p>' => "------------------------------------------------------------------------------\nDrupal\n------------------------------------------------------------------------------\nDrupal\n\n", - '<i>Drupal</i>' => "/Drupal/\n", - '<p>Drupal</p>' => "Drupal\n\n", - '<p>Drupal</p><p>Drupal</p>' => "Drupal\n\nDrupal\n\n", - '<strong>Drupal</strong>' => "*Drupal*\n", - // @todo Tables are currently not supported. - '<table><tr><td>Drupal</td><td>Drupal</td></tr><tr><td>Drupal</td><td>Drupal</td></tr></table>' => "DrupalDrupalDrupalDrupal\n", - '<table><tr><td>Drupal</td></tr></table><p>Drupal</p>' => "Drupal\nDrupal\n\n", - // @todo The <u> tag is currently not supported. - '<u>Drupal</u>' => "Drupal\n", - '<ul><li>Drupal</li></ul>' => " * Drupal\n\n", - '<ul><li>Drupal <em>Drupal</em> Drupal</li></ul>' => " * Drupal /Drupal/ Drupal\n\n", - // @todo Lines containing nothing but spaces should be trimmed. - '<ul><li>Drupal</li><li><ol><li>Drupal</li><li>Drupal</li></ol></li></ul>' => " * Drupal\n * 1) Drupal\n 2) Drupal\n \n\n", - '<ul><li>Drupal</li><li><ol><li>Drupal</li></ol></li><li>Drupal</li></ul>' => " * Drupal\n * 1) Drupal\n \n * Drupal\n\n", - '<ul><li>Drupal</li><li>Drupal</li></ul>' => " * Drupal\n * Drupal\n\n", - '<ul><li>Drupal</li></ul><p>Drupal</p>' => " * Drupal\n\nDrupal\n\n", - '<ol><li>Drupal</li></ol>' => " 1) Drupal\n\n", - '<ol><li>Drupal</li><li><ul><li>Drupal</li><li>Drupal</li></ul></li></ol>' => " 1) Drupal\n 2) * Drupal\n * Drupal\n \n\n", - '<ol><li>Drupal</li><li>Drupal</li></ol>' => " 1) Drupal\n 2) Drupal\n\n", - '<ol>Drupal</ol>' => "Drupal\n\n", - '<ol><li>Drupal</li></ol><p>Drupal</p>' => " 1) Drupal\n\nDrupal\n\n", - '<dl><dt>Drupal</dt></dl>' => "Drupal\n\n", - '<dl><dt>Drupal</dt><dd>Drupal</dd></dl>' => "Drupal\n Drupal\n\n", - '<dl><dt>Drupal</dt><dd>Drupal</dd><dt>Drupal</dt><dd>Drupal</dd></dl>' => "Drupal\n Drupal\nDrupal\n Drupal\n\n", - '<dl><dt>Drupal</dt><dd>Drupal</dd></dl><p>Drupal</p>' => "Drupal\n Drupal\n\nDrupal\n\n", - '<dl><dt>Drupal<dd>Drupal</dl>' => "Drupal\n Drupal\n\n", - '<dl><dt>Drupal</dt></dl><p>Drupal</p>' => "Drupal\n\nDrupal\n\n", - // @todo Again, lines containing only spaces should be trimmed. - '<ul><li>Drupal</li><li><dl><dt>Drupal</dt><dd>Drupal</dd><dt>Drupal</dt><dd>Drupal</dd></dl></li><li>Drupal</li></ul>' => " * Drupal\n * Drupal\n Drupal\n Drupal\n Drupal\n \n * Drupal\n\n", - // Tests malformed HTML tags. - '<br>Drupal<br>Drupal' => "Drupal\nDrupal\n", - '<hr>Drupal<hr>Drupal' => "------------------------------------------------------------------------------\nDrupal\n------------------------------------------------------------------------------\nDrupal\n", - '<ol><li>Drupal<li>Drupal</ol>' => " 1) Drupal\n 2) Drupal\n\n", - '<ul><li>Drupal <em>Drupal</em> Drupal</ul></ul>' => " * Drupal /Drupal/ Drupal\n\n", - '<ul><li>Drupal<li>Drupal</ol>' => " * Drupal\n * Drupal\n\n", - '<ul><li>Drupal<li>Drupal</ul>' => " * Drupal\n * Drupal\n\n", - '<ul>Drupal</ul>' => "Drupal\n\n", - 'Drupal</ul></ol></dl><li>Drupal' => "Drupal\n * Drupal\n", - '<dl>Drupal</dl>' => "Drupal\n\n", - '<dl>Drupal</dl><p>Drupal</p>' => "Drupal\n\nDrupal\n\n", - '<dt>Drupal</dt>' => "Drupal\n", - // Tests some unsupported HTML tags. - '<html>Drupal</html>' => "Drupal\n", - // @todo Perhaps the contents of <script> tags should be dropped. - '<script type="text/javascript">Drupal</script>' => "Drupal\n", - ); - - foreach ($tests as $html => $text) { - $this->assertHtmlToText($html, $text, 'Supported tags'); - } - } - - /** - * Test $allowed_tags argument of drupal_html_to_text(). - */ - function testDrupalHtmlToTextArgs() { - // The second parameter of drupal_html_to_text() overrules the allowed tags. - $this->assertHtmlToText( - 'Drupal <b>Drupal</b> Drupal', - "Drupal *Drupal* Drupal\n", - 'Allowed <b> tag found', - array('b') - ); - $this->assertHtmlToText( - 'Drupal <h1>Drupal</h1> Drupal', - "Drupal Drupal Drupal\n", - 'Disallowed <h1> tag not found', - array('b') - ); - - $this->assertHtmlToText( - 'Drupal <p><em><b>Drupal</b></em><p> Drupal', - "Drupal Drupal Drupal\n", - 'Disallowed <p>, <em>, and <b> tags not found', - array('a', 'br', 'h1') - ); - - $this->assertHtmlToText( - '<html><body>Drupal</body></html>', - "Drupal\n", - 'Unsupported <html> and <body> tags not found', - array('html', 'body') - ); - } - - /** - * Test that whitespace is collapsed. - */ - function testDrupalHtmltoTextCollapsesWhitespace() { - $input = "<p>Drupal Drupal\n\nDrupal<pre>Drupal Drupal\n\nDrupal</pre>Drupal Drupal\n\nDrupal</p>"; - // @todo The whitespace should be collapsed. - $collapsed = "Drupal Drupal\n\nDrupalDrupal Drupal\n\nDrupalDrupal Drupal\n\nDrupal\n\n"; - $this->assertHtmlToText( - $input, - $collapsed, - 'Whitespace is collapsed', - array('p') - ); - } - - /** - * Test that text separated by block-level tags in HTML get separated by - * (at least) a newline in the plaintext version. - */ - function testDrupalHtmlToTextBlockTagToNewline() { - $input = '[text]' - . '<blockquote>[blockquote]</blockquote>' - . '<br />[br]' - . '<dl><dt>[dl-dt]</dt>' - . '<dt>[dt]</dt>' - . '<dd>[dd]</dd>' - . '<dd>[dd-dl]</dd></dl>' - . '<h1>[h1]</h1>' - . '<h2>[h2]</h2>' - . '<h3>[h3]</h3>' - . '<h4>[h4]</h4>' - . '<h5>[h5]</h5>' - . '<h6>[h6]</h6>' - . '<hr />[hr]' - . '<ol><li>[ol-li]</li>' - . '<li>[li]</li>' - . '<li>[li-ol]</li></ol>' - . '<p>[p]</p>' - . '<ul><li>[ul-li]</li>' - . '<li>[li-ul]</li></ul>' - . '[text]'; - $output = drupal_html_to_text($input); - $pass = $this->assertFalse( - preg_match('/\][^\n]*\[/s', $output), - 'Block-level HTML tags should force newlines' - ); - if (!$pass) { - $this->verbose($this->stringToHtml($output)); - } - $output_upper = drupal_strtoupper($output); - $upper_input = drupal_strtoupper($input); - $upper_output = drupal_html_to_text($upper_input); - $pass = $this->assertEqual( - $upper_output, - $output_upper, - 'Tag recognition should be case-insensitive' - ); - if (!$pass) { - $this->verbose( - $upper_output - . '<br />should be equal to <br />' - . $output_upper - ); - } - } - - /** - * Test that headers are properly separated from surrounding text. - */ - function testHeaderSeparation() { - $html = 'Drupal<h1>Drupal</h1>Drupal'; - // @todo There should be more space above the header than below it. - $text = "Drupal\n======== DRUPAL ==============================================================\n\nDrupal\n"; - $this->assertHtmlToText($html, $text, - 'Text before and after <h1> tag'); - $html = '<p>Drupal</p><h1>Drupal</h1>Drupal'; - // @todo There should be more space above the header than below it. - $text = "Drupal\n\n======== DRUPAL ==============================================================\n\nDrupal\n"; - $this->assertHtmlToText($html, $text, - 'Paragraph before and text after <h1> tag'); - $html = 'Drupal<h1>Drupal</h1><p>Drupal</p>'; - // @todo There should be more space above the header than below it. - $text = "Drupal\n======== DRUPAL ==============================================================\n\nDrupal\n\n"; - $this->assertHtmlToText($html, $text, - 'Text before and paragraph after <h1> tag'); - $html = '<p>Drupal</p><h1>Drupal</h1><p>Drupal</p>'; - $text = "Drupal\n\n======== DRUPAL ==============================================================\n\nDrupal\n\n"; - $this->assertHtmlToText($html, $text, - 'Paragraph before and after <h1> tag'); - } - - /** - * Test that footnote references are properly generated. - */ - function testFootnoteReferences() { - global $base_path, $base_url; - $source = '<a href="http://www.example.com/node/1">Host and path</a>' - . '<br /><a href="http://www.example.com">Host, no path</a>' - . '<br /><a href="' . $base_path . 'node/1">Path, no host</a>' - . '<br /><a href="node/1">Relative path</a>'; - // @todo Footnote urls should be absolute. - $tt = "Host and path [1]" - . "\nHost, no path [2]" - // @todo The following two references should be combined. - . "\nPath, no host [3]" - . "\nRelative path [4]" - . "\n" - . "\n[1] http://www.example.com/node/1" - . "\n[2] http://www.example.com" - // @todo The following two references should be combined. - . "\n[3] $base_url/node/1" - . "\n[4] node/1\n"; - $this->assertHtmlToText($source, $tt, 'Footnotes'); - } - - /** - * Test that combinations of paragraph breaks, line breaks, linefeeds, - * and spaces are properly handled. - */ - function testDrupalHtmlToTextParagraphs() { - $tests = array(); - $tests[] = array( - 'html' => "<p>line 1<br />\nline 2<br />line 3\n<br />line 4</p><p>paragraph</p>", - // @todo Trailing line breaks should be trimmed. - 'text' => "line 1\nline 2\nline 3\nline 4\n\nparagraph\n\n", - ); - $tests[] = array( - 'html' => "<p>line 1<br /> line 2</p> <p>line 4<br /> line 5</p> <p>0</p>", - // @todo Trailing line breaks should be trimmed. - 'text' => "line 1\nline 2\n\nline 4\nline 5\n\n0\n\n", - ); - foreach ($tests as $test) { - $this->assertHtmlToText($test['html'], $test['text'], 'Paragraph breaks'); - } - } - - /** - * Tests that drupal_html_to_text() wraps before 1000 characters. - * - * RFC 3676 says, "The Text/Plain media type is the lowest common - * denominator of Internet email, with lines of no more than 998 characters." - * - * RFC 2046 says, "SMTP [RFC-821] allows a maximum of 998 octets before the - * next CRLF sequence." - * - * RFC 821 says, "The maximum total length of a text line including the - * <CRLF> is 1000 characters." - */ - function testVeryLongLineWrap() { - $input = 'Drupal<br /><p>' . str_repeat('x', 2100) . '</><br />Drupal'; - $output = drupal_html_to_text($input); - // This awkward construct comes from includes/mail.inc lines 8-13. - $eol = variable_get('mail_line_endings', MAIL_LINE_ENDINGS); - // We must use strlen() rather than drupal_strlen() in order to count - // octets rather than characters. - $line_length_limit = 1000 - drupal_strlen($eol); - $maximum_line_length = 0; - foreach (explode($eol, $output) as $line) { - // We must use strlen() rather than drupal_strlen() in order to count - // octets rather than characters. - $maximum_line_length = max($maximum_line_length, strlen($line . $eol)); - } - $verbose = 'Maximum line length found was ' . $maximum_line_length . ' octets.'; - // @todo This should assert that $maximum_line_length <= 1000. - $this->pass($verbose); - } -} diff --git a/modules/simpletest/tests/menu.test b/modules/simpletest/tests/menu.test deleted file mode 100644 index d0612ac77a6..00000000000 --- a/modules/simpletest/tests/menu.test +++ /dev/null @@ -1,1621 +0,0 @@ -<?php - -/** - * @file - * Provides SimpleTests for menu.inc. - */ - -class MenuWebTestCase extends DrupalWebTestCase { - function setUp() { - $modules = func_get_args(); - if (isset($modules[0]) && is_array($modules[0])) { - $modules = $modules[0]; - } - parent::setUp($modules); - } - - /** - * Assert that a given path shows certain breadcrumb links. - * - * @param string $goto - * (optional) A system path to pass to DrupalWebTestCase::drupalGet(). - * @param array $trail - * An associative array whose keys are expected breadcrumb link paths and - * whose values are expected breadcrumb link texts (not sanitized). - * @param string $page_title - * (optional) A page title to additionally assert via - * DrupalWebTestCase::assertTitle(). Without site name suffix. - * @param array $tree - * (optional) An associative array whose keys are link paths and whose - * values are link titles (not sanitized) of an expected active trail in a - * menu tree output on the page. - * @param $last_active - * (optional) Whether the last link in $tree is expected to be active (TRUE) - * or just to be in the active trail (FALSE). - */ - protected function assertBreadcrumb($goto, array $trail, $page_title = NULL, array $tree = array(), $last_active = TRUE) { - if (isset($goto)) { - $this->drupalGet($goto); - } - // Compare paths with actual breadcrumb. - $parts = $this->getParts(); - $pass = TRUE; - foreach ($trail as $path => $title) { - $url = url($path); - $part = array_shift($parts); - $pass = ($pass && $part['href'] === $url && $part['text'] === check_plain($title)); - } - // No parts must be left, or an expected "Home" will always pass. - $pass = ($pass && empty($parts)); - - $this->assertTrue($pass, t('Breadcrumb %parts found on @path.', array( - '%parts' => implode(' » ', $trail), - '@path' => $this->getUrl(), - ))); - - // Additionally assert page title, if given. - if (isset($page_title)) { - $this->assertTitle(strtr('@title | Drupal', array('@title' => $page_title))); - } - - // Additionally assert active trail in a menu tree output, if given. - if ($tree) { - end($tree); - $active_link_path = key($tree); - $active_link_title = array_pop($tree); - $xpath = ''; - if ($tree) { - $i = 0; - foreach ($tree as $link_path => $link_title) { - $part_xpath = (!$i ? '//' : '/following-sibling::ul/descendant::'); - $part_xpath .= 'li[contains(@class, :class)]/a[contains(@href, :href) and contains(text(), :title)]'; - $part_args = array( - ':class' => 'active-trail', - ':href' => url($link_path), - ':title' => $link_title, - ); - $xpath .= $this->buildXPathQuery($part_xpath, $part_args); - $i++; - } - $elements = $this->xpath($xpath); - $this->assertTrue(!empty($elements), t('Active trail to current page was found in menu tree.')); - - // Append prefix for active link asserted below. - $xpath .= '/following-sibling::ul/descendant::'; - } - else { - $xpath .= '//'; - } - $xpath_last_active = ($last_active ? 'and contains(@class, :class-active)' : ''); - $xpath .= 'li[contains(@class, :class-trail)]/a[contains(@href, :href) ' . $xpath_last_active . 'and contains(text(), :title)]'; - $args = array( - ':class-trail' => 'active-trail', - ':class-active' => 'active', - ':href' => url($active_link_path), - ':title' => $active_link_title, - ); - $elements = $this->xpath($xpath, $args); - $this->assertTrue(!empty($elements), t('Active link %title was found in menu tree, including active trail links %tree.', array( - '%title' => $active_link_title, - '%tree' => implode(' » ', $tree), - ))); - } - } - - /** - * Returns the breadcrumb contents of the current page in the internal browser. - */ - protected function getParts() { - $parts = array(); - $elements = $this->xpath('//div[@class="breadcrumb"]/a'); - if (!empty($elements)) { - foreach ($elements as $element) { - $parts[] = array( - 'text' => (string) $element, - 'href' => (string) $element['href'], - 'title' => (string) $element['title'], - ); - } - } - return $parts; - } -} - -class MenuRouterTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Menu router', - 'description' => 'Tests menu router and hook_menu() functionality.', - 'group' => 'Menu', - ); - } - - function setUp() { - // Enable dummy module that implements hook_menu. - parent::setUp('menu_test'); - // Make the tests below more robust by explicitly setting the default theme - // and administrative theme that they expect. - theme_enable(array('bartik')); - variable_set('theme_default', 'bartik'); - variable_set('admin_theme', 'seven'); - } - - /** - * Test title callback set to FALSE. - */ - function testTitleCallbackFalse() { - $this->drupalGet('node'); - $this->assertText('A title with @placeholder', t('Raw text found on the page')); - $this->assertNoText(t('A title with @placeholder', array('@placeholder' => 'some other text')), t('Text with placeholder substitutions not found.')); - } - - /** - * Tests page title of MENU_CALLBACKs. - */ - function testTitleMenuCallback() { - // Verify that the menu router item title is not visible. - $this->drupalGet(''); - $this->assertNoText(t('Menu Callback Title')); - // Verify that the menu router item title is output as page title. - $this->drupalGet('menu_callback_title'); - $this->assertText(t('Menu Callback Title')); - } - - /** - * Test the theme callback when it is set to use an administrative theme. - */ - function testThemeCallbackAdministrative() { - $this->drupalGet('menu-test/theme-callback/use-admin-theme'); - $this->assertText('Custom theme: seven. Actual theme: seven.', t('The administrative theme can be correctly set in a theme callback.')); - $this->assertRaw('seven/style.css', t("The administrative theme's CSS appears on the page.")); - } - - /** - * Test that the theme callback is properly inherited. - */ - function testThemeCallbackInheritance() { - $this->drupalGet('menu-test/theme-callback/use-admin-theme/inheritance'); - $this->assertText('Custom theme: seven. Actual theme: seven. Theme callback inheritance is being tested.', t('Theme callback inheritance correctly uses the administrative theme.')); - $this->assertRaw('seven/style.css', t("The administrative theme's CSS appears on the page.")); - } - - /** - * Test that 'page callback', 'file' and 'file path' keys are properly - * inherited from parent menu paths. - */ - function testFileInheritance() { - $this->drupalGet('admin/config/development/file-inheritance'); - $this->assertText('File inheritance test description', t('File inheritance works.')); - } - - /** - * Test path containing "exotic" characters. - */ - function testExoticPath() { - $path = "menu-test/ -._~!$'\"()*@[]?&+%#,;=:" . // "Special" ASCII characters. - "%23%25%26%2B%2F%3F" . // Characters that look like a percent-escaped string. - "éøïвβ中國書۞"; // Characters from various non-ASCII alphabets. - $this->drupalGet($path); - $this->assertRaw('This is menu_test_callback().'); - } - - /** - * Test the theme callback when the site is in maintenance mode. - */ - function testThemeCallbackMaintenanceMode() { - variable_set('maintenance_mode', TRUE); - - // For a regular user, the fact that the site is in maintenance mode means - // we expect the theme callback system to be bypassed entirely. - $this->drupalGet('menu-test/theme-callback/use-admin-theme'); - $this->assertRaw('bartik/css/style.css', t("The maintenance theme's CSS appears on the page.")); - - // An administrator, however, should continue to see the requested theme. - $admin_user = $this->drupalCreateUser(array('access site in maintenance mode')); - $this->drupalLogin($admin_user); - $this->drupalGet('menu-test/theme-callback/use-admin-theme'); - $this->assertText('Custom theme: seven. Actual theme: seven.', t('The theme callback system is correctly triggered for an administrator when the site is in maintenance mode.')); - $this->assertRaw('seven/style.css', t("The administrative theme's CSS appears on the page.")); - } - - /** - * Make sure the maintenance mode can be bypassed using hook_menu_site_status_alter(). - * - * @see hook_menu_site_status_alter(). - */ - function testMaintenanceModeLoginPaths() { - variable_set('maintenance_mode', TRUE); - - $offline_message = t('@site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('@site' => variable_get('site_name', 'Drupal'))); - $this->drupalLogout(); - $this->drupalGet('node'); - $this->assertText($offline_message); - $this->drupalGet('menu_login_callback'); - $this->assertText('This is menu_login_callback().', t('Maintenance mode can be bypassed through hook_login_paths().')); - } - - /** - * Test that an authenticated user hitting 'user/login' gets redirected to - * 'user' and 'user/register' gets redirected to the user edit page. - */ - function testAuthUserUserLogin() { - $loggedInUser = $this->drupalCreateUser(array()); - $this->drupalLogin($loggedInUser); - - $this->DrupalGet('user/login'); - // Check that we got to 'user'. - $this->assertTrue($this->url == url('user', array('absolute' => TRUE)), t("Logged-in user redirected to q=user on accessing q=user/login")); - - // user/register should redirect to user/UID/edit. - $this->DrupalGet('user/register'); - $this->assertTrue($this->url == url('user/' . $this->loggedInUser->uid . '/edit', array('absolute' => TRUE)), t("Logged-in user redirected to q=user/UID/edit on accessing q=user/register")); - } - - /** - * Test the theme callback when it is set to use an optional theme. - */ - function testThemeCallbackOptionalTheme() { - // Request a theme that is not enabled. - $this->drupalGet('menu-test/theme-callback/use-stark-theme'); - $this->assertText('Custom theme: NONE. Actual theme: bartik.', t('The theme callback system falls back on the default theme when a theme that is not enabled is requested.')); - $this->assertRaw('bartik/css/style.css', t("The default theme's CSS appears on the page.")); - - // Now enable the theme and request it again. - theme_enable(array('stark')); - $this->drupalGet('menu-test/theme-callback/use-stark-theme'); - $this->assertText('Custom theme: stark. Actual theme: stark.', t('The theme callback system uses an optional theme once it has been enabled.')); - $this->assertRaw('stark/layout.css', t("The optional theme's CSS appears on the page.")); - } - - /** - * Test the theme callback when it is set to use a theme that does not exist. - */ - function testThemeCallbackFakeTheme() { - $this->drupalGet('menu-test/theme-callback/use-fake-theme'); - $this->assertText('Custom theme: NONE. Actual theme: bartik.', t('The theme callback system falls back on the default theme when a theme that does not exist is requested.')); - $this->assertRaw('bartik/css/style.css', t("The default theme's CSS appears on the page.")); - } - - /** - * Test the theme callback when no theme is requested. - */ - function testThemeCallbackNoThemeRequested() { - $this->drupalGet('menu-test/theme-callback/no-theme-requested'); - $this->assertText('Custom theme: NONE. Actual theme: bartik.', t('The theme callback system falls back on the default theme when no theme is requested.')); - $this->assertRaw('bartik/css/style.css', t("The default theme's CSS appears on the page.")); - } - - /** - * Test that hook_custom_theme() can control the theme of a page. - */ - function testHookCustomTheme() { - // Trigger hook_custom_theme() to dynamically request the Stark theme for - // the requested page. - variable_set('menu_test_hook_custom_theme_name', 'stark'); - theme_enable(array('stark')); - - // Visit a page that does not implement a theme callback. The above request - // should be honored. - $this->drupalGet('menu-test/no-theme-callback'); - $this->assertText('Custom theme: stark. Actual theme: stark.', t('The result of hook_custom_theme() is used as the theme for the current page.')); - $this->assertRaw('stark/layout.css', t("The Stark theme's CSS appears on the page.")); - } - - /** - * Test that the theme callback wins out over hook_custom_theme(). - */ - function testThemeCallbackHookCustomTheme() { - // Trigger hook_custom_theme() to dynamically request the Stark theme for - // the requested page. - variable_set('menu_test_hook_custom_theme_name', 'stark'); - theme_enable(array('stark')); - - // The menu "theme callback" should take precedence over a value set in - // hook_custom_theme(). - $this->drupalGet('menu-test/theme-callback/use-admin-theme'); - $this->assertText('Custom theme: seven. Actual theme: seven.', t('The result of hook_custom_theme() does not override what was set in a theme callback.')); - $this->assertRaw('seven/style.css', t("The Seven theme's CSS appears on the page.")); - } - - /** - * Tests for menu_link_maintain(). - */ - function testMenuLinkMaintain() { - $admin_user = $this->drupalCreateUser(array('administer site configuration')); - $this->drupalLogin($admin_user); - - // Create three menu items. - menu_link_maintain('menu_test', 'insert', 'menu_test_maintain/1', 'Menu link #1'); - menu_link_maintain('menu_test', 'insert', 'menu_test_maintain/1', 'Menu link #1-1'); - menu_link_maintain('menu_test', 'insert', 'menu_test_maintain/2', 'Menu link #2'); - - // Move second link to the main-menu, to test caching later on. - db_update('menu_links') - ->fields(array('menu_name' => 'main-menu')) - ->condition('link_title', 'Menu link #1-1') - ->condition('customized', 0) - ->condition('module', 'menu_test') - ->execute(); - menu_cache_clear('main-menu'); - - // Load front page. - $this->drupalGet('node'); - $this->assertLink(t('Menu link #1'), 0, 'Found menu link #1'); - $this->assertLink(t('Menu link #1-1'), 0, 'Found menu link #1-1'); - $this->assertLink(t('Menu link #2'), 0, 'Found menu link #2'); - - // Rename all links for the given path. - menu_link_maintain('menu_test', 'update', 'menu_test_maintain/1', 'Menu link updated'); - // Load a different page to be sure that we have up to date information. - $this->drupalGet('menu_test_maintain/1'); - $this->assertLink(t('Menu link updated'), 0, t('Found updated menu link')); - $this->assertNoLink(t('Menu link #1'), 0, t('Not found menu link #1')); - $this->assertNoLink(t('Menu link #1'), 0, t('Not found menu link #1-1')); - $this->assertLink(t('Menu link #2'), 0, t('Found menu link #2')); - - // Delete all links for the given path. - menu_link_maintain('menu_test', 'delete', 'menu_test_maintain/1', ''); - // Load a different page to be sure that we have up to date information. - $this->drupalGet('menu_test_maintain/2'); - $this->assertNoLink(t('Menu link updated'), 0, t('Not found deleted menu link')); - $this->assertNoLink(t('Menu link #1'), 0, t('Not found menu link #1')); - $this->assertNoLink(t('Menu link #1'), 0, t('Not found menu link #1-1')); - $this->assertLink(t('Menu link #2'), 0, t('Found menu link #2')); - } - - /** - * Tests for menu_name parameter for hook_menu(). - */ - function testMenuName() { - $admin_user = $this->drupalCreateUser(array('administer site configuration')); - $this->drupalLogin($admin_user); - - $sql = "SELECT menu_name FROM {menu_links} WHERE router_path = 'menu_name_test'"; - $name = db_query($sql)->fetchField(); - $this->assertEqual($name, 'original', t('Menu name is "original".')); - - // Change the menu_name parameter in menu_test.module, then force a menu - // rebuild. - menu_test_menu_name('changed'); - menu_rebuild(); - - $sql = "SELECT menu_name FROM {menu_links} WHERE router_path = 'menu_name_test'"; - $name = db_query($sql)->fetchField(); - $this->assertEqual($name, 'changed', t('Menu name was successfully changed after rebuild.')); - } - - /** - * Tests for menu hierarchy. - */ - function testMenuHierarchy() { - $parent_link = db_query('SELECT * FROM {menu_links} WHERE link_path = :link_path', array(':link_path' => 'menu-test/hierarchy/parent'))->fetchAssoc(); - $child_link = db_query('SELECT * FROM {menu_links} WHERE link_path = :link_path', array(':link_path' => 'menu-test/hierarchy/parent/child'))->fetchAssoc(); - $unattached_child_link = db_query('SELECT * FROM {menu_links} WHERE link_path = :link_path', array(':link_path' => 'menu-test/hierarchy/parent/child2/child'))->fetchAssoc(); - - $this->assertEqual($child_link['plid'], $parent_link['mlid'], t('The parent of a directly attached child is correct.')); - $this->assertEqual($unattached_child_link['plid'], $parent_link['mlid'], t('The parent of a non-directly attached child is correct.')); - } - - /** - * Tests menu link depth and parents of local tasks and menu callbacks. - */ - function testMenuHidden() { - // Verify links for one dynamic argument. - $links = db_select('menu_links', 'ml') - ->fields('ml') - ->condition('ml.router_path', 'menu-test/hidden/menu%', 'LIKE') - ->orderBy('ml.router_path') - ->execute() - ->fetchAllAssoc('router_path', PDO::FETCH_ASSOC); - - $parent = $links['menu-test/hidden/menu']; - $depth = $parent['depth'] + 1; - $plid = $parent['mlid']; - - $link = $links['menu-test/hidden/menu/list']; - $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); - $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); - - $link = $links['menu-test/hidden/menu/add']; - $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); - $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); - - $link = $links['menu-test/hidden/menu/settings']; - $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); - $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); - - $link = $links['menu-test/hidden/menu/manage/%']; - $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); - $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); - - $parent = $links['menu-test/hidden/menu/manage/%']; - $depth = $parent['depth'] + 1; - $plid = $parent['mlid']; - - $link = $links['menu-test/hidden/menu/manage/%/list']; - $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); - $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); - - $link = $links['menu-test/hidden/menu/manage/%/add']; - $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); - $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); - - $link = $links['menu-test/hidden/menu/manage/%/edit']; - $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); - $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); - - $link = $links['menu-test/hidden/menu/manage/%/delete']; - $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); - $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); - - // Verify links for two dynamic arguments. - $links = db_select('menu_links', 'ml') - ->fields('ml') - ->condition('ml.router_path', 'menu-test/hidden/block%', 'LIKE') - ->orderBy('ml.router_path') - ->execute() - ->fetchAllAssoc('router_path', PDO::FETCH_ASSOC); - - $parent = $links['menu-test/hidden/block']; - $depth = $parent['depth'] + 1; - $plid = $parent['mlid']; - - $link = $links['menu-test/hidden/block/list']; - $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); - $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); - - $link = $links['menu-test/hidden/block/add']; - $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); - $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); - - $link = $links['menu-test/hidden/block/manage/%/%']; - $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); - $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); - - $parent = $links['menu-test/hidden/block/manage/%/%']; - $depth = $parent['depth'] + 1; - $plid = $parent['mlid']; - - $link = $links['menu-test/hidden/block/manage/%/%/configure']; - $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); - $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); - - $link = $links['menu-test/hidden/block/manage/%/%/delete']; - $this->assertEqual($link['depth'], $depth, t('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth))); - $this->assertEqual($link['plid'], $plid, t('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid))); - } - - /** - * Test menu_get_item() with empty ancestors. - */ - function testMenuGetItemNoAncestors() { - variable_set('menu_masks', array()); - $this->drupalGet(''); - } - - /** - * Test menu_set_item(). - */ - function testMenuSetItem() { - $item = menu_get_item('node'); - - $this->assertEqual($item['path'], 'node', t("Path from menu_get_item('node') is equal to 'node'"), 'menu'); - - // Modify the path for the item then save it. - $item['path'] = 'node_test'; - $item['href'] = 'node_test'; - - menu_set_item('node', $item); - $compare_item = menu_get_item('node'); - $this->assertEqual($compare_item, $item, t('Modified menu item is equal to newly retrieved menu item.'), 'menu'); - } - - /** - * Test menu maintenance hooks. - */ - function testMenuItemHooks() { - // Create an item. - menu_link_maintain('menu_test', 'insert', 'menu_test_maintain/4', 'Menu link #4'); - $this->assertEqual(menu_test_static_variable(), 'insert', t('hook_menu_link_insert() fired correctly')); - // Update the item. - menu_link_maintain('menu_test', 'update', 'menu_test_maintain/4', 'Menu link updated'); - $this->assertEqual(menu_test_static_variable(), 'update', t('hook_menu_link_update() fired correctly')); - // Delete the item. - menu_link_maintain('menu_test', 'delete', 'menu_test_maintain/4', ''); - $this->assertEqual(menu_test_static_variable(), 'delete', t('hook_menu_link_delete() fired correctly')); - } - - /** - * Test menu link 'options' storage and rendering. - */ - function testMenuLinkOptions() { - // Create a menu link with options. - $menu_link = array( - 'link_title' => 'Menu link options test', - 'link_path' => 'node', - 'module' => 'menu_test', - 'options' => array( - 'attributes' => array( - 'title' => 'Test title attribute', - ), - 'query' => array( - 'testparam' => 'testvalue', - ), - ), - ); - menu_link_save($menu_link); - - // Load front page. - $this->drupalGet('node'); - $this->assertRaw('title="Test title attribute"', t('Title attribute of a menu link renders.')); - $this->assertRaw('testparam=testvalue', t('Query parameter added to menu link.')); - } - - /** - * Tests the possible ways to set the title for menu items. - * Also tests that menu item titles work with string overrides. - */ - function testMenuItemTitlesCases() { - - // Build array with string overrides. - $test_data = array( - 1 => array('Example title - Case 1' => 'Alternative example title - Case 1'), - 2 => array('Example @sub1 - Case @op2' => 'Alternative example @sub1 - Case @op2'), - 3 => array('Example title' => 'Alternative example title'), - 4 => array('Example title' => 'Alternative example title'), - ); - - foreach ($test_data as $case_no => $override) { - $this->menuItemTitlesCasesHelper($case_no); - variable_set('locale_custom_strings_en', array('' => $override)); - $this->menuItemTitlesCasesHelper($case_no, TRUE); - variable_set('locale_custom_strings_en', array()); - } - } - - /** - * Get a url and assert the title given a case number. If override is true, - * the title is asserted to begin with "Alternative". - */ - private function menuItemTitlesCasesHelper($case_no, $override = FALSE) { - $this->drupalGet('menu-title-test/case' . $case_no); - $this->assertResponse(200); - $asserted_title = $override ? 'Alternative example title - Case ' . $case_no : 'Example title - Case ' . $case_no; - $this->assertTitle($asserted_title . ' | Drupal', t('Menu title is') . ': ' . $asserted_title, 'Menu'); - } - - /** - * Load the router for a given path. - */ - protected function menuLoadRouter($router_path) { - return db_query('SELECT * FROM {menu_router} WHERE path = :path', array(':path' => $router_path))->fetchAssoc(); - } - - /** - * Tests inheritance of 'load arguments'. - */ - function testMenuLoadArgumentsInheritance() { - $expected = array( - 'menu-test/arguments/%/%' => array( - 2 => array('menu_test_argument_load' => array(3)), - 3 => NULL, - ), - // Arguments are inherited to normal children. - 'menu-test/arguments/%/%/default' => array( - 2 => array('menu_test_argument_load' => array(3)), - 3 => NULL, - ), - // Arguments are inherited to tab children. - 'menu-test/arguments/%/%/task' => array( - 2 => array('menu_test_argument_load' => array(3)), - 3 => NULL, - ), - // Arguments are only inherited to the same loader functions. - 'menu-test/arguments/%/%/common-loader' => array( - 2 => array('menu_test_argument_load' => array(3)), - 3 => 'menu_test_other_argument_load', - ), - // Arguments are not inherited to children not using the same loader - // function. - 'menu-test/arguments/%/%/different-loaders-1' => array( - 2 => NULL, - 3 => 'menu_test_argument_load', - ), - 'menu-test/arguments/%/%/different-loaders-2' => array( - 2 => 'menu_test_other_argument_load', - 3 => NULL, - ), - 'menu-test/arguments/%/%/different-loaders-3' => array( - 2 => NULL, - 3 => NULL, - ), - // Explicit loader arguments should not be overriden by parent. - 'menu-test/arguments/%/%/explicit-arguments' => array( - 2 => array('menu_test_argument_load' => array()), - 3 => NULL, - ), - ); - - foreach ($expected as $router_path => $load_functions) { - $router_item = $this->menuLoadRouter($router_path); - $this->assertIdentical(unserialize($router_item['load_functions']), $load_functions, t('Expected load functions for router %router_path' , array('%router_path' => $router_path))); - } - } -} - -/** - * Tests for menu links. - */ -class MenuLinksUnitTestCase extends DrupalWebTestCase { - // Use the lightweight testing profile for this test. - protected $profile = 'testing'; - - public static function getInfo() { - return array( - 'name' => 'Menu links', - 'description' => 'Test handling of menu links hierarchies.', - 'group' => 'Menu', - ); - } - - /** - * Create a simple hierarchy of links. - */ - function createLinkHierarchy($module = 'menu_test') { - // First remove all the menu links. - db_truncate('menu_links')->execute(); - - // Then create a simple link hierarchy: - // - $parent - // - $child-1 - // - $child-1-1 - // - $child-1-2 - // - $child-2 - $base_options = array( - 'link_title' => 'Menu link test', - 'module' => $module, - 'menu_name' => 'menu_test', - ); - - $links['parent'] = $base_options + array( - 'link_path' => 'menu-test/parent', - ); - menu_link_save($links['parent']); - - $links['child-1'] = $base_options + array( - 'link_path' => 'menu-test/parent/child-1', - 'plid' => $links['parent']['mlid'], - ); - menu_link_save($links['child-1']); - - $links['child-1-1'] = $base_options + array( - 'link_path' => 'menu-test/parent/child-1/child-1-1', - 'plid' => $links['child-1']['mlid'], - ); - menu_link_save($links['child-1-1']); - - $links['child-1-2'] = $base_options + array( - 'link_path' => 'menu-test/parent/child-1/child-1-2', - 'plid' => $links['child-1']['mlid'], - ); - menu_link_save($links['child-1-2']); - - $links['child-2'] = $base_options + array( - 'link_path' => 'menu-test/parent/child-2', - 'plid' => $links['parent']['mlid'], - ); - menu_link_save($links['child-2']); - - return $links; - } - - /** - * Assert that at set of links is properly parented. - */ - function assertMenuLinkParents($links, $expected_hierarchy) { - foreach ($expected_hierarchy as $child => $parent) { - $mlid = $links[$child]['mlid']; - $plid = $parent ? $links[$parent]['mlid'] : 0; - - $menu_link = menu_link_load($mlid); - menu_link_save($menu_link); - $this->assertEqual($menu_link['plid'], $plid, t('Menu link %mlid has parent of %plid, expected %expected_plid.', array('%mlid' => $mlid, '%plid' => $menu_link['plid'], '%expected_plid' => $plid))); - } - } - - /** - * Test automatic reparenting of menu links. - */ - function testMenuLinkReparenting($module = 'menu_test') { - // Check the initial hierarchy. - $links = $this->createLinkHierarchy($module); - - $expected_hierarchy = array( - 'parent' => FALSE, - 'child-1' => 'parent', - 'child-1-1' => 'child-1', - 'child-1-2' => 'child-1', - 'child-2' => 'parent', - ); - $this->assertMenuLinkParents($links, $expected_hierarchy); - - // Start over, and move child-1 under child-2, and check that all the - // childs of child-1 have been moved too. - $links = $this->createLinkHierarchy($module); - $links['child-1']['plid'] = $links['child-2']['mlid']; - menu_link_save($links['child-1']); - - $expected_hierarchy = array( - 'parent' => FALSE, - 'child-1' => 'child-2', - 'child-1-1' => 'child-1', - 'child-1-2' => 'child-1', - 'child-2' => 'parent', - ); - $this->assertMenuLinkParents($links, $expected_hierarchy); - - // Start over, and delete child-1, and check that the children of child-1 - // have been reassigned to the parent. menu_link_delete() will cowardly - // refuse to delete a menu link defined by the system module, so skip the - // test in that case. - if ($module != 'system') { - $links = $this->createLinkHierarchy($module); - menu_link_delete($links['child-1']['mlid']); - - $expected_hierarchy = array( - 'parent' => FALSE, - 'child-1-1' => 'parent', - 'child-1-2' => 'parent', - 'child-2' => 'parent', - ); - $this->assertMenuLinkParents($links, $expected_hierarchy); - } - - // Start over, forcefully delete child-1 from the database, simulating a - // database crash. Check that the children of child-1 have been reassigned - // to the parent, going up on the old path hierarchy stored in each of the - // links. - $links = $this->createLinkHierarchy($module); - // Don't do that at home. - db_delete('menu_links') - ->condition('mlid', $links['child-1']['mlid']) - ->execute(); - - $expected_hierarchy = array( - 'parent' => FALSE, - 'child-1-1' => 'parent', - 'child-1-2' => 'parent', - 'child-2' => 'parent', - ); - $this->assertMenuLinkParents($links, $expected_hierarchy); - - // Start over, forcefully delete the parent from the database, simulating a - // database crash. Check that the children of parent are now top-level. - $links = $this->createLinkHierarchy($module); - // Don't do that at home. - db_delete('menu_links') - ->condition('mlid', $links['parent']['mlid']) - ->execute(); - - $expected_hierarchy = array( - 'child-1-1' => 'child-1', - 'child-1-2' => 'child-1', - 'child-2' => FALSE, - ); - $this->assertMenuLinkParents($links, $expected_hierarchy); - } - - /** - * Test automatic reparenting of menu links derived from menu routers. - */ - function testMenuLinkRouterReparenting() { - // Run all the standard parenting tests on menu links derived from - // menu routers. - $this->testMenuLinkReparenting('system'); - - // Additionnaly, test reparenting based on path. - $links = $this->createLinkHierarchy('system'); - - // Move child-1-2 has a child of child-2, making the link hierarchy - // inconsistent with the path hierarchy. - $links['child-1-2']['plid'] = $links['child-2']['mlid']; - menu_link_save($links['child-1-2']); - - // Check the new hierarchy. - $expected_hierarchy = array( - 'parent' => FALSE, - 'child-1' => 'parent', - 'child-1-1' => 'child-1', - 'child-2' => 'parent', - 'child-1-2' => 'child-2', - ); - $this->assertMenuLinkParents($links, $expected_hierarchy); - - // Now delete 'parent' directly from the database, simulating a database - // crash. 'child-1' and 'child-2' should get moved to the - // top-level. - // Don't do that at home. - db_delete('menu_links') - ->condition('mlid', $links['parent']['mlid']) - ->execute(); - $expected_hierarchy = array( - 'child-1' => FALSE, - 'child-1-1' => 'child-1', - 'child-2' => FALSE, - 'child-1-2' => 'child-2', - ); - $this->assertMenuLinkParents($links, $expected_hierarchy); - - // Now delete 'child-2' directly from the database, simulating a database - // crash. 'child-1-2' will get reparented under 'child-1' based on its - // path. - // Don't do that at home. - db_delete('menu_links') - ->condition('mlid', $links['child-2']['mlid']) - ->execute(); - $expected_hierarchy = array( - 'child-1' => FALSE, - 'child-1-1' => 'child-1', - 'child-1-2' => 'child-1', - ); - $this->assertMenuLinkParents($links, $expected_hierarchy); - } -} - -/** - * Tests rebuilding the menu by setting 'menu_rebuild_needed.' - */ -class MenuRebuildTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Menu rebuild test', - 'description' => 'Test rebuilding of menu.', - 'group' => 'Menu', - ); - } - - /** - * Test if the 'menu_rebuild_needed' variable triggers a menu_rebuild() call. - */ - function testMenuRebuildByVariable() { - // Check if 'admin' path exists. - $admin_exists = db_query('SELECT path from {menu_router} WHERE path = :path', array(':path' => 'admin'))->fetchField(); - $this->assertEqual($admin_exists, 'admin', t("The path 'admin/' exists prior to deleting.")); - - // Delete the path item 'admin', and test that the path doesn't exist in the database. - $delete = db_delete('menu_router') - ->condition('path', 'admin') - ->execute(); - $admin_exists = db_query('SELECT path from {menu_router} WHERE path = :path', array(':path' => 'admin'))->fetchField(); - $this->assertFalse($admin_exists, t("The path 'admin/' has been deleted and doesn't exist in the database.")); - - // Now we enable the rebuild variable and trigger menu_execute_active_handler() - // to rebuild the menu item. Now 'admin' should exist. - variable_set('menu_rebuild_needed', TRUE); - // menu_execute_active_handler() should trigger the rebuild. - $this->drupalGet('<front>'); - $admin_exists = db_query('SELECT path from {menu_router} WHERE path = :path', array(':path' => 'admin'))->fetchField(); - $this->assertEqual($admin_exists, 'admin', t("The menu has been rebuilt, the path 'admin' now exists again.")); - } - -} - -/** - * Menu tree data related tests. - */ -class MenuTreeDataTestCase extends DrupalUnitTestCase { - /** - * Dummy link structure acceptable for menu_tree_data(). - */ - var $links = array( - 1 => array('mlid' => 1, 'depth' => 1), - 2 => array('mlid' => 2, 'depth' => 1), - 3 => array('mlid' => 3, 'depth' => 2), - 4 => array('mlid' => 4, 'depth' => 3), - 5 => array('mlid' => 5, 'depth' => 1), - ); - - public static function getInfo() { - return array( - 'name' => 'Menu tree generation', - 'description' => 'Tests recursive menu tree generation functions.', - 'group' => 'Menu', - ); - } - - /** - * Validate the generation of a proper menu tree hierarchy. - */ - function testMenuTreeData() { - $tree = menu_tree_data($this->links); - - // Validate that parent items #1, #2, and #5 exist on the root level. - $this->assertSameLink($this->links[1], $tree[1]['link'], t('Parent item #1 exists.')); - $this->assertSameLink($this->links[2], $tree[2]['link'], t('Parent item #2 exists.')); - $this->assertSameLink($this->links[5], $tree[5]['link'], t('Parent item #5 exists.')); - - // Validate that child item #4 exists at the correct location in the hierarchy. - $this->assertSameLink($this->links[4], $tree[2]['below'][3]['below'][4]['link'], t('Child item #4 exists in the hierarchy.')); - } - - /** - * Check that two menu links are the same by comparing the mlid. - * - * @param $link1 - * A menu link item. - * @param $link2 - * A menu link item. - * @param $message - * The message to display along with the assertion. - * @return - * TRUE if the assertion succeeded, FALSE otherwise. - */ - protected function assertSameLink($link1, $link2, $message = '') { - return $this->assert($link1['mlid'] == $link2['mlid'], $message ? $message : t('First link is identical to second link')); - } -} - -/** - * Menu tree output related tests. - */ -class MenuTreeOutputTestCase extends DrupalWebTestCase { - /** - * Dummy link structure acceptable for menu_tree_output(). - */ - var $tree_data = array( - '1'=> array( - 'link' => array( 'menu_name' => 'main-menu', 'mlid' => 1, 'hidden'=>0, 'has_children' => 1, 'title' => 'Item 1', 'in_active_trail' => 1, 'access'=>1, 'href' => 'a', 'localized_options' => array('attributes' => array('title' =>'')) ), - 'below' => array( - '2' => array('link' => array( 'menu_name' => 'main-menu', 'mlid' => 2, 'hidden'=>0, 'has_children' => 1, 'title' => 'Item 2', 'in_active_trail' => 1, 'access'=>1, 'href' => 'a/b', 'localized_options' => array('attributes' => array('title' =>'')) ), - 'below' => array( - '3' => array('link' => array( 'menu_name' => 'main-menu', 'mlid' => 3, 'hidden'=>0, 'has_children' => 0, 'title' => 'Item 3', 'in_active_trail' => 0, 'access'=>1, 'href' => 'a/b/c', 'localized_options' => array('attributes' => array('title' =>'')) ), - 'below' => array() ), - '4' => array('link' => array( 'menu_name' => 'main-menu', 'mlid' => 4, 'hidden'=>0, 'has_children' => 0, 'title' => 'Item 4', 'in_active_trail' => 0, 'access'=>1, 'href' => 'a/b/d', 'localized_options' => array('attributes' => array('title' =>'')) ), - 'below' => array() ) - ) - ) - ) - ), - '5' => array('link' => array( 'menu_name' => 'main-menu', 'mlid' => 5, 'hidden'=>1, 'has_children' => 0, 'title' => 'Item 5', 'in_active_trail' => 0, 'access'=>1, 'href' => 'e', 'localized_options' => array('attributes' => array('title' =>'')) ), 'below' => array( ) ), - '6' => array('link' => array( 'menu_name' => 'main-menu', 'mlid' => 6, 'hidden'=>0, 'has_children' => 0, 'title' => 'Item 6', 'in_active_trail' => 0, 'access'=>0, 'href' => 'f', 'localized_options' => array('attributes' => array('title' =>'')) ), 'below' => array( ) ), - '7' => array('link' => array( 'menu_name' => 'main-menu', 'mlid' => 7, 'hidden'=>0, 'has_children' => 0, 'title' => 'Item 7', 'in_active_trail' => 0, 'access'=>1, 'href' => 'g', 'localized_options' => array('attributes' => array('title' =>'')) ), 'below' => array( ) ) - ); - - public static function getInfo() { - return array( - 'name' => 'Menu tree output', - 'description' => 'Tests menu tree output functions.', - 'group' => 'Menu', - ); - } - - function setUp() { - parent::setUp(); - } - - /** - * Validate the generation of a proper menu tree output. - */ - function testMenuTreeData() { - $output = menu_tree_output($this->tree_data); - - // Validate that the - in main-menu is changed into an underscore - $this->assertEqual( $output['1']['#theme'], 'menu_link__main_menu', t('Hyphen is changed to a dash on menu_link')); - $this->assertEqual( $output['#theme_wrappers'][0], 'menu_tree__main_menu', t('Hyphen is changed to a dash on menu_tree wrapper')); - // Looking for child items in the data - $this->assertEqual( $output['1']['#below']['2']['#href'], 'a/b', t('Checking the href on a child item')); - $this->assertTrue( in_array('active-trail',$output['1']['#below']['2']['#attributes']['class']) , t('Checking the active trail class')); - // Validate that the hidden and no access items are missing - $this->assertFalse( isset($output['5']), t('Hidden item should be missing')); - $this->assertFalse( isset($output['6']), t('False access should be missing')); - // Item 7 is after a couple hidden items. Just to make sure that 5 and 6 are skipped and 7 still included - $this->assertTrue( isset($output['7']), t('Item after hidden items is present')); - } -} - -/** - * Menu breadcrumbs related tests. - */ -class MenuBreadcrumbTestCase extends MenuWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Breadcrumbs', - 'description' => 'Tests breadcrumbs functionality.', - 'group' => 'Menu', - ); - } - - function setUp() { - $modules = func_get_args(); - if (isset($modules[0]) && is_array($modules[0])) { - $modules = $modules[0]; - } - $modules[] = 'menu_test'; - parent::setUp($modules); - $perms = array_keys(module_invoke_all('permission')); - $this->admin_user = $this->drupalCreateUser($perms); - $this->drupalLogin($this->admin_user); - - // This test puts menu links in the Navigation menu and then tests for - // their presence on the page, so we need to ensure that the Navigation - // block will be displayed in all active themes. - db_update('block') - ->fields(array( - // Use a region that is valid for all themes. - 'region' => 'content', - 'status' => 1, - )) - ->condition('module', 'system') - ->condition('delta', 'navigation') - ->execute(); - } - - /** - * Tests breadcrumbs on node and administrative paths. - */ - function testBreadCrumbs() { - // Prepare common base breadcrumb elements. - $home = array('<front>' => 'Home'); - $admin = $home + array('admin' => t('Administration')); - $config = $admin + array('admin/config' => t('Configuration')); - $type = 'article'; - $langcode = LANGUAGE_NONE; - - // Verify breadcrumbs for default local tasks. - $expected = array( - 'menu-test' => t('Menu test root'), - ); - $title = t('Breadcrumbs test: Local tasks'); - $trail = $home + $expected; - $tree = $expected + array( - 'menu-test/breadcrumb/tasks' => $title, - ); - $this->assertBreadcrumb('menu-test/breadcrumb/tasks', $trail, $title, $tree); - $this->assertBreadcrumb('menu-test/breadcrumb/tasks/first', $trail, $title, $tree); - $this->assertBreadcrumb('menu-test/breadcrumb/tasks/first/first', $trail, $title, $tree); - $trail += array( - 'menu-test/breadcrumb/tasks' => t('Breadcrumbs test: Local tasks'), - ); - $this->assertBreadcrumb('menu-test/breadcrumb/tasks/first/second', $trail, $title, $tree); - $this->assertBreadcrumb('menu-test/breadcrumb/tasks/second', $trail, $title, $tree); - $this->assertBreadcrumb('menu-test/breadcrumb/tasks/second/first', $trail, $title, $tree); - $trail += array( - 'menu-test/breadcrumb/tasks/second' => t('Second'), - ); - $this->assertBreadcrumb('menu-test/breadcrumb/tasks/second/second', $trail, $title, $tree); - - // Verify Taxonomy administration breadcrumbs. - $trail = $admin + array( - 'admin/structure' => t('Structure'), - ); - $this->assertBreadcrumb('admin/structure/taxonomy', $trail); - - $trail += array( - 'admin/structure/taxonomy' => t('Taxonomy'), - ); - $this->assertBreadcrumb('admin/structure/taxonomy/tags', $trail); - $trail += array( - 'admin/structure/taxonomy/tags' => t('Tags'), - ); - $this->assertBreadcrumb('admin/structure/taxonomy/tags/edit', $trail); - $this->assertBreadcrumb('admin/structure/taxonomy/tags/fields', $trail); - $this->assertBreadcrumb('admin/structure/taxonomy/tags/add', $trail); - - // Verify Menu administration breadcrumbs. - $trail = $admin + array( - 'admin/structure' => t('Structure'), - ); - $this->assertBreadcrumb('admin/structure/menu', $trail); - - $trail += array( - 'admin/structure/menu' => t('Menus'), - ); - $this->assertBreadcrumb('admin/structure/menu/manage/navigation', $trail); - $trail += array( - 'admin/structure/menu/manage/navigation' => t('Navigation'), - ); - $this->assertBreadcrumb('admin/structure/menu/manage/navigation/edit', $trail); - $this->assertBreadcrumb('admin/structure/menu/manage/navigation/add', $trail); - - // Verify Node administration breadcrumbs. - $trail = $admin + array( - 'admin/structure' => t('Structure'), - 'admin/structure/types' => t('Content types'), - ); - $this->assertBreadcrumb('admin/structure/types/add', $trail); - $this->assertBreadcrumb("admin/structure/types/manage/$type", $trail); - $trail += array( - "admin/structure/types/manage/$type" => t('Article'), - ); - $this->assertBreadcrumb("admin/structure/types/manage/$type/fields", $trail); - $this->assertBreadcrumb("admin/structure/types/manage/$type/display", $trail); - $trail_teaser = $trail + array( - "admin/structure/types/manage/$type/display" => t('Manage display'), - ); - $this->assertBreadcrumb("admin/structure/types/manage/$type/display/teaser", $trail_teaser); - $this->assertBreadcrumb("admin/structure/types/manage/$type/comment/fields", $trail); - $this->assertBreadcrumb("admin/structure/types/manage/$type/comment/display", $trail); - $this->assertBreadcrumb("admin/structure/types/manage/$type/delete", $trail); - $trail += array( - "admin/structure/types/manage/$type/fields" => t('Manage fields'), - ); - $this->assertBreadcrumb("admin/structure/types/manage/$type/fields/body", $trail); - $trail += array( - "admin/structure/types/manage/$type/fields/body" => t('Body'), - ); - $this->assertBreadcrumb("admin/structure/types/manage/$type/fields/body/widget-type", $trail); - - // Verify Filter text format administration breadcrumbs. - $format = db_query_range("SELECT format, name FROM {filter_format}", 1, 1)->fetch(); - $format_id = $format->format; - $trail = $config + array( - 'admin/config/content' => t('Content authoring'), - ); - $this->assertBreadcrumb('admin/config/content/formats', $trail); - - $trail += array( - 'admin/config/content/formats' => t('Text formats'), - ); - $this->assertBreadcrumb('admin/config/content/formats/add', $trail); - $this->assertBreadcrumb("admin/config/content/formats/$format_id", $trail); - $trail += array( - "admin/config/content/formats/$format_id" => $format->name, - ); - $this->assertBreadcrumb("admin/config/content/formats/$format_id/disable", $trail); - - // Verify node breadcrumbs (without menu link). - $node1 = $this->drupalCreateNode(); - $nid1 = $node1->nid; - $trail = $home; - $this->assertBreadcrumb("node/$nid1", $trail); - // Also verify that the node does not appear elsewhere (e.g., menu trees). - $this->assertNoLink($node1->title); - // The node itself should not be contained in the breadcrumb on the default - // local task, since there is no difference between both pages. - $this->assertBreadcrumb("node/$nid1/view", $trail); - // Also verify that the node does not appear elsewhere (e.g., menu trees). - $this->assertNoLink($node1->title); - - $trail += array( - "node/$nid1" => $node1->title, - ); - $this->assertBreadcrumb("node/$nid1/edit", $trail); - - // Verify that breadcrumb on node listing page contains "Home" only. - $trail = array(); - $this->assertBreadcrumb('node', $trail); - - // Verify node breadcrumbs (in menu). - // Do this separately for Main menu and Navigation menu, since only the - // latter is a preferred menu by default. - // @todo Also test all themes? Manually testing led to the suspicion that - // breadcrumbs may differ, possibly due to template.php overrides. - $menus = array('main-menu', 'navigation'); - // Alter node type menu settings. - variable_set("menu_options_$type", $menus); - variable_set("menu_parent_$type", 'navigation:0'); - - foreach ($menus as $menu) { - // Create a parent node in the current menu. - $title = $this->randomName(); - $node2 = $this->drupalCreateNode(array( - 'type' => $type, - 'title' => $title, - 'menu' => array( - 'enabled' => 1, - 'link_title' => 'Parent ' . $title, - 'description' => '', - 'menu_name' => $menu, - 'plid' => 0, - ), - )); - $nid2 = $node2->nid; - - $trail = $home; - $tree = array( - "node/$nid2" => $node2->menu['link_title'], - ); - $this->assertBreadcrumb("node/$nid2", $trail, $node2->title, $tree); - // The node itself should not be contained in the breadcrumb on the - // default local task, since there is no difference between both pages. - $this->assertBreadcrumb("node/$nid2/view", $trail, $node2->title, $tree); - $trail += array( - "node/$nid2" => $node2->menu['link_title'], - ); - $this->assertBreadcrumb("node/$nid2/edit", $trail); - - // Create a child node in the current menu. - $title = $this->randomName(); - $node3 = $this->drupalCreateNode(array( - 'type' => $type, - 'title' => $title, - 'menu' => array( - 'enabled' => 1, - 'link_title' => 'Child ' . $title, - 'description' => '', - 'menu_name' => $menu, - 'plid' => $node2->menu['mlid'], - ), - )); - $nid3 = $node3->nid; - - $this->assertBreadcrumb("node/$nid3", $trail, $node3->title, $tree, FALSE); - // The node itself should not be contained in the breadcrumb on the - // default local task, since there is no difference between both pages. - $this->assertBreadcrumb("node/$nid3/view", $trail, $node3->title, $tree, FALSE); - $trail += array( - "node/$nid3" => $node3->menu['link_title'], - ); - $tree += array( - "node/$nid3" => $node3->menu['link_title'], - ); - $this->assertBreadcrumb("node/$nid3/edit", $trail); - - // Verify that node listing page still contains "Home" only. - $trail = array(); - $this->assertBreadcrumb('node', $trail); - - if ($menu == 'navigation') { - $parent = $node2; - $child = $node3; - } - } - - // Create a Navigation menu link for 'node', move the last parent node menu - // link below it, and verify a full breadcrumb for the last child node. - $menu = 'navigation'; - $edit = array( - 'link_title' => 'Root', - 'link_path' => 'node', - ); - $this->drupalPost("admin/structure/menu/manage/$menu/add", $edit, t('Save')); - $link = db_query('SELECT * FROM {menu_links} WHERE link_title = :title', array(':title' => 'Root'))->fetchAssoc(); - - $edit = array( - 'menu[parent]' => $link['menu_name'] . ':' . $link['mlid'], - ); - $this->drupalPost("node/{$parent->nid}/edit", $edit, t('Save')); - $expected = array( - "node" => $link['link_title'], - ); - $trail = $home + $expected; - $tree = $expected + array( - "node/{$parent->nid}" => $parent->menu['link_title'], - ); - $this->assertBreadcrumb(NULL, $trail, $parent->title, $tree); - $trail += array( - "node/{$parent->nid}" => $parent->menu['link_title'], - ); - $tree += array( - "node/{$child->nid}" => $child->menu['link_title'], - ); - $this->assertBreadcrumb("node/{$child->nid}", $trail, $child->title, $tree); - - // Add a taxonomy term/tag to last node, and add a link for that term to the - // Navigation menu. - $tags = array( - 'Drupal' => array(), - 'Breadcrumbs' => array(), - ); - $edit = array( - "field_tags[$langcode]" => implode(',', array_keys($tags)), - ); - $this->drupalPost("node/{$parent->nid}/edit", $edit, t('Save')); - - // Put both terms into a hierarchy Drupal » Breadcrumbs. Required for both - // the menu links and the terms itself, since taxonomy_term_page() resets - // the breadcrumb based on taxonomy term hierarchy. - $parent_tid = 0; - foreach ($tags as $name => $null) { - $terms = taxonomy_term_load_multiple(NULL, array('name' => $name)); - $term = reset($terms); - $tags[$name]['term'] = $term; - if ($parent_tid) { - $edit = array( - 'parent[]' => array($parent_tid), - ); - $this->drupalPost("taxonomy/term/{$term->tid}/edit", $edit, t('Save')); - } - $parent_tid = $term->tid; - } - $parent_mlid = 0; - foreach ($tags as $name => $data) { - $term = $data['term']; - $edit = array( - 'link_title' => "$name link", - 'link_path' => "taxonomy/term/{$term->tid}", - 'parent' => "$menu:{$parent_mlid}", - ); - $this->drupalPost("admin/structure/menu/manage/$menu/add", $edit, t('Save')); - $tags[$name]['link'] = db_query('SELECT * FROM {menu_links} WHERE link_title = :title AND link_path = :href', array( - ':title' => $edit['link_title'], - ':href' => $edit['link_path'], - ))->fetchAssoc(); - $tags[$name]['link']['link_path'] = $edit['link_path']; - $parent_mlid = $tags[$name]['link']['mlid']; - } - - // Verify expected breadcrumbs for menu links. - $trail = $home; - $tree = array(); - foreach ($tags as $name => $data) { - $term = $data['term']; - $link = $data['link']; - - $tree += array( - $link['link_path'] => $link['link_title'], - ); - $this->assertBreadcrumb($link['link_path'], $trail, $term->name, $tree); - $this->assertRaw(check_plain($parent->title), 'Tagged node found.'); - - // Additionally make sure that this link appears only once; i.e., the - // untranslated menu links automatically generated from menu router items - // ('taxonomy/term/%') should never be translated and appear in any menu - // other than the breadcrumb trail. - $elements = $this->xpath('//div[@id=:menu]/descendant::a[@href=:href]', array( - ':menu' => 'block-system-navigation', - ':href' => url($link['link_path']), - )); - $this->assertTrue(count($elements) == 1, "Link to {$link['link_path']} appears only once."); - - // Next iteration should expect this tag as parent link. - // Note: Term name, not link name, due to taxonomy_term_page(). - $trail += array( - $link['link_path'] => $term->name, - ); - } - - // Verify breadcrumbs on user and user/%. - // We need to log back in and out below, and cannot simply grant the - // 'administer users' permission, since user_page() makes your head explode. - user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array( - 'access user profiles', - )); - $this->drupalLogout(); - - // Verify breadcrumb on front page. - $this->assertBreadcrumb('<front>', array()); - - // Verify breadcrumb on user pages (without menu link) for anonymous user. - $trail = $home; - $this->assertBreadcrumb('user', $trail, t('User account')); - $this->assertBreadcrumb('user/' . $this->admin_user->uid, $trail, $this->admin_user->name); - - // Verify breadcrumb on user pages (without menu link) for registered users. - $this->drupalLogin($this->admin_user); - $trail = $home; - $this->assertBreadcrumb('user', $trail, $this->admin_user->name); - $this->assertBreadcrumb('user/' . $this->admin_user->uid, $trail, $this->admin_user->name); - $trail += array( - 'user/' . $this->admin_user->uid => $this->admin_user->name, - ); - $this->assertBreadcrumb('user/' . $this->admin_user->uid . '/edit', $trail, $this->admin_user->name); - - // Create a second user to verify breadcrumb on user pages again. - $this->web_user = $this->drupalCreateUser(array( - 'administer users', - 'access user profiles', - )); - $this->drupalLogin($this->web_user); - - // Verify correct breadcrumb and page title on another user's account pages - // (without menu link). - $trail = $home; - $this->assertBreadcrumb('user/' . $this->admin_user->uid, $trail, $this->admin_user->name); - $trail += array( - 'user/' . $this->admin_user->uid => $this->admin_user->name, - ); - $this->assertBreadcrumb('user/' . $this->admin_user->uid . '/edit', $trail, $this->admin_user->name); - - // Verify correct breadcrumb and page title when viewing own user account - // pages (without menu link). - $trail = $home; - $this->assertBreadcrumb('user/' . $this->web_user->uid, $trail, $this->web_user->name); - $trail += array( - 'user/' . $this->web_user->uid => $this->web_user->name, - ); - $this->assertBreadcrumb('user/' . $this->web_user->uid . '/edit', $trail, $this->web_user->name); - - // Add a Navigation menu links for 'user' and $this->admin_user. - // Although it may be faster to manage these links via low-level API - // functions, there's a lot that can go wrong in doing so. - $this->drupalLogin($this->admin_user); - $edit = array( - 'link_title' => 'User', - 'link_path' => 'user', - ); - $this->drupalPost("admin/structure/menu/manage/$menu/add", $edit, t('Save')); - $link_user = db_query('SELECT * FROM {menu_links} WHERE link_title = :title AND link_path = :href', array( - ':title' => $edit['link_title'], - ':href' => $edit['link_path'], - ))->fetchAssoc(); - - $edit = array( - 'link_title' => $this->admin_user->name . ' link', - 'link_path' => 'user/' . $this->admin_user->uid, - ); - $this->drupalPost("admin/structure/menu/manage/$menu/add", $edit, t('Save')); - $link_admin_user = db_query('SELECT * FROM {menu_links} WHERE link_title = :title AND link_path = :href', array( - ':title' => $edit['link_title'], - ':href' => $edit['link_path'], - ))->fetchAssoc(); - - // Verify expected breadcrumbs for the two separate links. - $this->drupalLogout(); - $trail = $home; - $tree = array( - $link_user['link_path'] => $link_user['link_title'], - ); - $this->assertBreadcrumb('user', $trail, $link_user['link_title'], $tree); - $tree = array( - $link_admin_user['link_path'] => $link_admin_user['link_title'], - ); - $this->assertBreadcrumb('user/' . $this->admin_user->uid, $trail, $link_admin_user['link_title'], $tree); - - $this->drupalLogin($this->admin_user); - $trail += array( - $link_admin_user['link_path'] => $link_admin_user['link_title'], - ); - $this->assertBreadcrumb('user/' . $this->admin_user->uid . '/edit', $trail, $link_admin_user['link_title'], $tree, FALSE); - - // Move 'user/%' below 'user' and verify again. - $edit = array( - 'parent' => "$menu:{$link_user['mlid']}", - ); - $this->drupalPost("admin/structure/menu/item/{$link_admin_user['mlid']}/edit", $edit, t('Save')); - - $this->drupalLogout(); - $trail = $home; - $tree = array( - $link_user['link_path'] => $link_user['link_title'], - ); - $this->assertBreadcrumb('user', $trail, $link_user['link_title'], $tree); - $trail += array( - $link_user['link_path'] => $link_user['link_title'], - ); - $tree += array( - $link_admin_user['link_path'] => $link_admin_user['link_title'], - ); - $this->assertBreadcrumb('user/' . $this->admin_user->uid, $trail, $link_admin_user['link_title'], $tree); - - $this->drupalLogin($this->admin_user); - $trail += array( - $link_admin_user['link_path'] => $link_admin_user['link_title'], - ); - $this->assertBreadcrumb('user/' . $this->admin_user->uid . '/edit', $trail, $link_admin_user['link_title'], $tree, FALSE); - - // Create an only slightly privileged user being able to access site reports - // but not administration pages. - $this->web_user = $this->drupalCreateUser(array( - 'access site reports', - )); - $this->drupalLogin($this->web_user); - - // Verify that we can access recent log entries, there is a corresponding - // page title, and that the breadcrumb is empty (because the user is not - // able to access "Administer", so the trail cannot recurse into it). - $trail = array(); - $this->assertBreadcrumb('admin', $trail, t('Access denied')); - $this->assertResponse(403); - - $trail = $home; - $this->assertBreadcrumb('admin/reports', $trail, t('Reports')); - $this->assertNoResponse(403); - - $this->assertBreadcrumb('admin/reports/dblog', $trail, t('Recent log messages')); - $this->assertNoResponse(403); - } -} - -/** - * Tests active menu trails. - */ -class MenuTrailTestCase extends MenuWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Active trail', - 'description' => 'Tests active menu trails and alteration functionality.', - 'group' => 'Menu', - ); - } - - function setUp() { - $modules = func_get_args(); - if (isset($modules[0]) && is_array($modules[0])) { - $modules = $modules[0]; - } - $modules[] = 'menu_test'; - parent::setUp($modules); - $this->admin_user = $this->drupalCreateUser(array('administer site configuration', 'access administration pages')); - $this->drupalLogin($this->admin_user); - - // This test puts menu links in the Navigation menu and then tests for - // their presence on the page, so we need to ensure that the Navigation - // block will be displayed in all active themes. - db_update('block') - ->fields(array( - // Use a region that is valid for all themes. - 'region' => 'content', - 'status' => 1, - )) - ->condition('module', 'system') - ->condition('delta', 'navigation') - ->execute(); - - // This test puts menu links in the Management menu and then tests for - // their presence on the page, so we need to ensure that the Management - // block will be displayed in all active themes. - db_update('block') - ->fields(array( - // Use a region that is valid for all themes. - 'region' => 'content', - 'status' => 1, - )) - ->condition('module', 'system') - ->condition('delta', 'management') - ->execute(); - } - - /** - * Tests active trails are properly affected by menu_tree_set_path(). - */ - function testMenuTreeSetPath() { - $home = array('<front>' => 'Home'); - $config_tree = array( - 'admin' => t('Administration'), - 'admin/config' => t('Configuration'), - ); - $config = $home + $config_tree; - - // The menu_test_menu_tree_set_path system variable controls whether or not - // the menu_test_menu_trail_callback() callback (used by all paths in these - // tests) issues an overriding call to menu_trail_set_path(). - $test_menu_path = array( - 'menu_name' => 'management', - 'path' => 'admin/config/system/site-information', - ); - - $breadcrumb = $home + array( - 'menu-test' => t('Menu test root'), - ); - $tree = array( - 'menu-test' => t('Menu test root'), - 'menu-test/menu-trail' => t('Menu trail - Case 1'), - ); - - // Test the tree generation for the Navigation menu. - variable_del('menu_test_menu_tree_set_path'); - $this->assertBreadcrumb('menu-test/menu-trail', $breadcrumb, t('Menu trail - Case 1'), $tree); - - // Override the active trail for the Management tree; it should not affect - // the Navigation tree. - variable_set('menu_test_menu_tree_set_path', $test_menu_path); - $this->assertBreadcrumb('menu-test/menu-trail', $breadcrumb, t('Menu trail - Case 1'), $tree); - - $breadcrumb = $config + array( - 'admin/config/development' => t('Development'), - ); - $tree = $config_tree + array( - 'admin/config/development' => t('Development'), - 'admin/config/development/menu-trail' => t('Menu trail - Case 2'), - ); - - $override_breadcrumb = $config + array( - 'admin/config/system' => t('System'), - 'admin/config/system/site-information' => t('Site information'), - ); - $override_tree = $config_tree + array( - 'admin/config/system' => t('System'), - 'admin/config/system/site-information' => t('Site information'), - ); - - // Test the tree generation for the Management menu. - variable_del('menu_test_menu_tree_set_path'); - $this->assertBreadcrumb('admin/config/development/menu-trail', $breadcrumb, t('Menu trail - Case 2'), $tree); - - // Override the active trail for the Management tree; it should affect the - // breadcrumbs and Management tree. - variable_set('menu_test_menu_tree_set_path', $test_menu_path); - $this->assertBreadcrumb('admin/config/development/menu-trail', $override_breadcrumb, t('Menu trail - Case 2'), $override_tree); - } -} diff --git a/modules/simpletest/tests/menu_test.info b/modules/simpletest/tests/menu_test.info deleted file mode 100644 index 4549a25c7cb..00000000000 --- a/modules/simpletest/tests/menu_test.info +++ /dev/null @@ -1,6 +0,0 @@ -name = "Hook menu tests" -description = "Support module for menu hook testing." -package = Testing -version = VERSION -core = 8.x -hidden = TRUE diff --git a/modules/simpletest/tests/menu_test.module b/modules/simpletest/tests/menu_test.module deleted file mode 100644 index c42aca60fe6..00000000000 --- a/modules/simpletest/tests/menu_test.module +++ /dev/null @@ -1,527 +0,0 @@ -<?php - -/** - * @file - * Dummy module implementing hook menu. - */ - -/** - * Implements hook_menu(). - */ -function menu_test_menu() { - // The name of the menu changes during the course of the test. Using a $_GET. - $items['menu_name_test'] = array( - 'title' => 'Test menu_name router item', - 'page callback' => 'node_save', - 'menu_name' => menu_test_menu_name(), - ); - // This item is of type MENU_CALLBACK with no parents to test title. - $items['menu_callback_title'] = array( - 'title' => 'Menu Callback Title', - 'page callback' => 'menu_test_callback', - 'type' => MENU_CALLBACK, - 'access arguments' => array('access content'), - ); - // Use FALSE as 'title callback' to bypass t(). - $items['menu_no_title_callback'] = array( - 'title' => 'A title with @placeholder', - 'title callback' => FALSE, - 'title arguments' => array('@placeholder' => 'some other text'), - 'page callback' => 'menu_test_callback', - 'access arguments' => array('access content'), - ); - - // Hidden link for menu_link_maintain tests - $items['menu_test_maintain/%'] = array( - 'title' => 'Menu maintain test', - 'page callback' => 'node_page_default', - 'access arguments' => array('access content'), - ); - // Hierarchical tests. - $items['menu-test/hierarchy/parent'] = array( - 'title' => 'Parent menu router', - 'page callback' => 'node_page_default', - ); - $items['menu-test/hierarchy/parent/child'] = array( - 'title' => 'Child menu router', - 'page callback' => 'node_page_default', - ); - $items['menu-test/hierarchy/parent/child2/child'] = array( - 'title' => 'Unattached subchild router', - 'page callback' => 'node_page_default', - ); - // Theme callback tests. - $items['menu-test/theme-callback/%'] = array( - 'title' => 'Page that displays different themes', - 'page callback' => 'menu_test_theme_page_callback', - 'access arguments' => array('access content'), - 'theme callback' => 'menu_test_theme_callback', - 'theme arguments' => array(2), - ); - $items['menu-test/theme-callback/%/inheritance'] = array( - 'title' => 'Page that tests theme callback inheritance.', - 'page callback' => 'menu_test_theme_page_callback', - 'page arguments' => array(TRUE), - 'access arguments' => array('access content'), - ); - $items['menu-test/no-theme-callback'] = array( - 'title' => 'Page that displays different themes without using a theme callback.', - 'page callback' => 'menu_test_theme_page_callback', - 'access arguments' => array('access content'), - ); - // Path containing "exotic" characters. - $path = "menu-test/ -._~!$'\"()*@[]?&+%#,;=:" . // "Special" ASCII characters. - "%23%25%26%2B%2F%3F" . // Characters that look like a percent-escaped string. - "éøïвβ中國書۞"; // Characters from various non-ASCII alphabets. - $items[$path] = array( - 'title' => '"Exotic" path', - 'page callback' => 'menu_test_callback', - 'access arguments' => array('access content'), - ); - - // Hidden tests; base parents. - // Same structure as in Menu and Block modules. Since those structures can - // change, we need to simulate our own in here. - $items['menu-test'] = array( - 'title' => 'Menu test root', - 'page callback' => 'node_page_default', - 'access arguments' => array('access content'), - ); - $items['menu-test/hidden'] = array( - 'title' => 'Hidden test root', - 'page callback' => 'node_page_default', - 'access arguments' => array('access content'), - ); - - // Hidden tests; one dynamic argument. - $items['menu-test/hidden/menu'] = array( - 'title' => 'Menus', - 'page callback' => 'node_page_default', - 'access arguments' => array('access content'), - ); - $items['menu-test/hidden/menu/list'] = array( - 'title' => 'List menus', - 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, - ); - $items['menu-test/hidden/menu/add'] = array( - 'title' => 'Add menu', - 'page callback' => 'node_page_default', - 'access arguments' => array('access content'), - 'type' => MENU_LOCAL_ACTION, - ); - $items['menu-test/hidden/menu/settings'] = array( - 'title' => 'Settings', - 'page callback' => 'node_page_default', - 'access arguments' => array('access content'), - 'type' => MENU_LOCAL_TASK, - 'weight' => 5, - ); - $items['menu-test/hidden/menu/manage/%menu'] = array( - 'title' => 'Customize menu', - 'page callback' => 'node_page_default', - 'access arguments' => array('access content'), - ); - $items['menu-test/hidden/menu/manage/%menu/list'] = array( - 'title' => 'List links', - 'weight' => -10, - 'type' => MENU_DEFAULT_LOCAL_TASK, - 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE, - ); - $items['menu-test/hidden/menu/manage/%menu/add'] = array( - 'title' => 'Add link', - 'page callback' => 'node_page_default', - 'access arguments' => array('access content'), - 'type' => MENU_LOCAL_ACTION, - ); - $items['menu-test/hidden/menu/manage/%menu/edit'] = array( - 'title' => 'Edit menu', - 'page callback' => 'node_page_default', - 'access arguments' => array('access content'), - 'type' => MENU_LOCAL_TASK, - 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE, - ); - $items['menu-test/hidden/menu/manage/%menu/delete'] = array( - 'title' => 'Delete menu', - 'page callback' => 'node_page_default', - 'access arguments' => array('access content'), - ); - - // Hidden tests; two dynamic arguments. - $items['menu-test/hidden/block'] = array( - 'title' => 'Blocks', - 'page callback' => 'node_page_default', - 'access arguments' => array('access content'), - ); - $items['menu-test/hidden/block/list'] = array( - 'title' => 'List', - 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, - ); - $items['menu-test/hidden/block/add'] = array( - 'title' => 'Add block', - 'page callback' => 'node_page_default', - 'access arguments' => array('access content'), - 'type' => MENU_LOCAL_ACTION, - ); - $items['menu-test/hidden/block/manage/%/%'] = array( - 'title' => 'Configure block', - 'page callback' => 'node_page_default', - 'access arguments' => array('access content'), - ); - $items['menu-test/hidden/block/manage/%/%/configure'] = array( - 'title' => 'Configure block', - 'type' => MENU_DEFAULT_LOCAL_TASK, - 'context' => MENU_CONTEXT_INLINE, - ); - $items['menu-test/hidden/block/manage/%/%/delete'] = array( - 'title' => 'Delete block', - 'page callback' => 'node_page_default', - 'access arguments' => array('access content'), - 'type' => MENU_LOCAL_TASK, - 'context' => MENU_CONTEXT_NONE, - ); - - // Breadcrumbs tests. - // @see MenuBreadcrumbTestCase - $base = array( - 'page callback' => 'menu_test_callback', - 'access callback' => TRUE, - ); - // Local tasks: Second level below default local task. - $items['menu-test/breadcrumb/tasks'] = array( - 'title' => 'Breadcrumbs test: Local tasks', - ) + $base; - $items['menu-test/breadcrumb/tasks/first'] = array( - 'title' => 'First', - 'type' => MENU_DEFAULT_LOCAL_TASK, - ) + $base; - $items['menu-test/breadcrumb/tasks/second'] = array( - 'title' => 'Second', - 'type' => MENU_LOCAL_TASK, - ) + $base; - $items['menu-test/breadcrumb/tasks/first/first'] = array( - 'title' => 'First first', - 'type' => MENU_DEFAULT_LOCAL_TASK, - ) + $base; - $items['menu-test/breadcrumb/tasks/first/second'] = array( - 'title' => 'First second', - 'type' => MENU_LOCAL_TASK, - ) + $base; - $items['menu-test/breadcrumb/tasks/second/first'] = array( - 'title' => 'Second first', - 'type' => MENU_DEFAULT_LOCAL_TASK, - ) + $base; - $items['menu-test/breadcrumb/tasks/second/second'] = array( - 'title' => 'Second second', - 'type' => MENU_LOCAL_TASK, - ) + $base; - - // Menu trail tests. - // @see MenuTrailTestCase - $items['menu-test/menu-trail'] = array( - 'title' => 'Menu trail - Case 1', - 'page callback' => 'menu_test_menu_trail_callback', - 'access arguments' => array('access content'), - ); - $items['admin/config/development/menu-trail'] = array( - 'title' => 'Menu trail - Case 2', - 'description' => 'Tests menu_tree_set_path()', - 'page callback' => 'menu_test_menu_trail_callback', - 'access arguments' => array('access administration pages'), - ); - - // File inheritance tests. This menu item should inherit the page callback - // system_admin_menu_block_page() and therefore render its children as links - // on the page. - $items['admin/config/development/file-inheritance'] = array( - 'title' => 'File inheritance', - 'description' => 'Test file inheritance', - 'access arguments' => array('access content'), - ); - $items['admin/config/development/file-inheritance/inherit'] = array( - 'title' => 'Inherit', - 'description' => 'File inheritance test description', - 'page callback' => 'menu_test_callback', - 'access arguments' => array('access content'), - ); - - $items['menu_login_callback'] = array( - 'title' => 'Used as a login path', - 'page callback' => 'menu_login_callback', - 'access callback' => TRUE, - ); - - $items['menu-title-test/case1'] = array( - 'title' => 'Example title - Case 1', - 'access callback' => TRUE, - 'page callback' => 'menu_test_callback', - ); - $items['menu-title-test/case2'] = array( - 'title' => 'Example @sub1 - Case @op2', - // If '2' is not in quotes, the argument becomes arg(2). - 'title arguments' => array('@sub1' => 'title', '@op2' => '2'), - 'access callback' => TRUE, - 'page callback' => 'menu_test_callback', - ); - $items['menu-title-test/case3'] = array( - 'title' => 'Example title', - 'title callback' => 'menu_test_title_callback', - 'access callback' => TRUE, - 'page callback' => 'menu_test_callback', - ); - $items['menu-title-test/case4'] = array( - // Title gets completely ignored. Good thing, too. - 'title' => 'Bike sheds full of blue smurfs', - 'title callback' => 'menu_test_title_callback', - // If '4' is not in quotes, the argument becomes arg(4). - 'title arguments' => array('Example title', '4'), - 'access callback' => TRUE, - 'page callback' => 'menu_test_callback', - ); - - // Load arguments inheritance test. - $items['menu-test/arguments/%menu_test_argument/%'] = array( - 'title' => 'Load arguments inheritance test', - 'load arguments' => array(3), - 'page callback' => 'menu_test_callback', - 'access callback' => TRUE, - ); - $items['menu-test/arguments/%menu_test_argument/%/default'] = array( - 'title' => 'Default local task', - 'type' => MENU_DEFAULT_LOCAL_TASK, - ); - $items['menu-test/arguments/%menu_test_argument/%/task'] = array( - 'title' => 'Local task', - 'page callback' => 'menu_test_callback', - 'access callback' => TRUE, - 'type' => MENU_LOCAL_TASK, - ); - // For this path, load arguments should be inherited for the first loader only. - $items['menu-test/arguments/%menu_test_argument/%menu_test_other_argument/common-loader'] = array( - 'title' => 'Local task', - 'page callback' => 'menu_test_callback', - 'access callback' => TRUE, - 'type' => MENU_LOCAL_TASK, - ); - // For these paths, no load arguments should be inherited. - // Not on the same position. - $items['menu-test/arguments/%/%menu_test_argument/different-loaders-1'] = array( - 'title' => 'An item not sharing the same loader', - 'page callback' => 'menu_test_callback', - 'access callback' => TRUE, - ); - // Not the same loader. - $items['menu-test/arguments/%menu_test_other_argument/%/different-loaders-2'] = array( - 'title' => 'An item not sharing the same loader', - 'page callback' => 'menu_test_callback', - 'access callback' => TRUE, - ); - // Not the same loader. - $items['menu-test/arguments/%/%/different-loaders-3'] = array( - 'title' => 'An item not sharing the same loader', - 'page callback' => 'menu_test_callback', - 'access callback' => TRUE, - ); - // Explict load arguments should not be overriden (even if empty). - $items['menu-test/arguments/%menu_test_argument/%/explicit-arguments'] = array( - 'title' => 'An item defining explicit load arguments', - 'load arguments' => array(), - 'page callback' => 'menu_test_callback', - 'access callback' => TRUE, - ); - - return $items; -} - -/** - * Dummy argument loader for hook_menu() to point to. - */ -function menu_test_argument_load($arg1) { - return FALSE; -} - -/** - * Dummy argument loader for hook_menu() to point to. - */ -function menu_test_other_argument_load($arg1) { - return FALSE; -} - -/** - * Dummy callback for hook_menu() to point to. - * - * @return - * A random string. - */ -function menu_test_callback() { - return 'This is menu_test_callback().'; -} - -/** - * Callback that test menu_test_menu_tree_set_path(). - */ -function menu_test_menu_trail_callback() { - $menu_path = variable_get('menu_test_menu_tree_set_path', array()); - if (!empty($menu_path)) { - menu_tree_set_path($menu_path['menu_name'], $menu_path['path']); - } - return 'This is menu_test_menu_trail_callback().'; -} - -/** - * Page callback to use when testing the theme callback functionality. - * - * @param $inherited - * An optional boolean to set to TRUE when the requested page is intended to - * inherit the theme of its parent. - * @return - * A string describing the requested custom theme and actual theme being used - * for the current page request. - */ -function menu_test_theme_page_callback($inherited = FALSE) { - global $theme_key; - // Initialize the theme system so that $theme_key will be populated. - drupal_theme_initialize(); - // Now check both the requested custom theme and the actual theme being used. - $custom_theme = menu_get_custom_theme(); - $requested_theme = empty($custom_theme) ? 'NONE' : $custom_theme; - $output = "Custom theme: $requested_theme. Actual theme: $theme_key."; - if ($inherited) { - $output .= ' Theme callback inheritance is being tested.'; - } - return $output; -} - -/** - * Theme callback to use when testing the theme callback functionality. - * - * @param $argument - * The argument passed in from the URL. - * @return - * The name of the custom theme to request for the current page. - */ -function menu_test_theme_callback($argument) { - // Test using the variable administrative theme. - if ($argument == 'use-admin-theme') { - return variable_get('admin_theme'); - } - // Test using a theme that exists, but may or may not be enabled. - elseif ($argument == 'use-stark-theme') { - return 'stark'; - } - // Test using a theme that does not exist. - elseif ($argument == 'use-fake-theme') { - return 'fake_theme'; - } - // For any other value of the URL argument, do not return anything. This - // allows us to test that returning nothing from a theme callback function - // causes the page to correctly fall back on using the main site theme. -} - -/** - * Implement hook_custom_theme(). - * - * @return - * The name of the custom theme to use for the current page. - */ -function menu_test_custom_theme() { - // If an appropriate variable has been set in the database, request the theme - // that is stored there. Otherwise, do not attempt to dynamically set the - // theme. - if ($theme = variable_get('menu_test_hook_custom_theme_name', FALSE)) { - return $theme; - } -} - -/** - * Helper function for the testMenuName() test. Used to change the menu_name - * parameter of a menu. - * - * @param $new_name - * If set, will change the menu_name value. - * @return - * The menu_name value to use. - */ -function menu_test_menu_name($new_name = '') { - static $name = 'original'; - if ($new_name) { - $name = $new_name; - } - return $name; -} - -/** - * Implements hook_menu_link_insert(). - * - * @return - * A random string. - */ -function menu_test_menu_link_insert($item) { - menu_test_static_variable('insert'); -} - -/** - * Implements hook_menu_link_update(). - * - * @return - * A random string. - */ -function menu_test_menu_link_update($item) { - menu_test_static_variable('update'); -} - -/** - * Implements hook_menu_link_delete(). - * - * @return - * A random string. - */ -function menu_test_menu_link_delete($item) { - menu_test_static_variable('delete'); -} - -/** - * Static function for testing hook results. - * - * @param $value - * The value to set or NULL to return the current value. - * @return - * A text string for comparison to test assertions. - */ -function menu_test_static_variable($value = NULL) { - static $variable; - if (!empty($value)) { - $variable = $value; - } - return $variable; -} - -/** - * Implements hook_menu_site_status_alter(). - */ -function menu_test_menu_site_status_alter(&$menu_site_status, $path) { - // Allow access to ?q=menu_login_callback even if in maintenance mode. - if ($menu_site_status == MENU_SITE_OFFLINE && $path == 'menu_login_callback') { - $menu_site_status = MENU_SITE_ONLINE; - } -} - -/** - * Menu callback to be used as a login path. - */ -function menu_login_callback() { - return 'This is menu_login_callback().'; -} - -/** - * Concatenates a string, by using the t() function and a case number. - * - * @param $title - * Title string. - * @param $case_number - * The current case number which is tests (defaults to 3). - */ -function menu_test_title_callback($title, $case_no = 3) { - return t($title) . ' - Case ' . $case_no; -} diff --git a/modules/simpletest/tests/module.test b/modules/simpletest/tests/module.test deleted file mode 100644 index c9601c9b9ab..00000000000 --- a/modules/simpletest/tests/module.test +++ /dev/null @@ -1,304 +0,0 @@ -<?php - -/** - * @file - * Tests for the module API. - */ - -/** - * Unit tests for the module API. - */ -class ModuleUnitTest extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Module API', - 'description' => 'Test low-level module functions.', - 'group' => 'Module', - ); - } - - /** - * The basic functionality of module_list(). - */ - function testModuleList() { - // Build a list of modules, sorted alphabetically. - $profile_info = install_profile_info('standard', 'en'); - $module_list = $profile_info['dependencies']; - - // Install profile is a module that is expected to be loaded. - $module_list[] = 'standard'; - - sort($module_list); - // Compare this list to the one returned by module_list(). We expect them - // to match, since all default profile modules have a weight equal to 0 - // (except for block.module, which has a lower weight but comes first in - // the alphabet anyway). - $this->assertModuleList($module_list, t('Standard profile')); - - // Try to install a new module. - module_enable(array('contact')); - $module_list[] = 'contact'; - sort($module_list); - $this->assertModuleList($module_list, t('After adding a module')); - - // Try to mess with the module weights. - db_update('system') - ->fields(array('weight' => 20)) - ->condition('name', 'contact') - ->condition('type', 'module') - ->execute(); - // Reset the module list. - module_list(TRUE); - // Move contact to the end of the array. - unset($module_list[array_search('contact', $module_list)]); - $module_list[] = 'contact'; - $this->assertModuleList($module_list, t('After changing weights')); - - // Test the fixed list feature. - $fixed_list = array( - 'system' => array('filename' => drupal_get_path('module', 'system')), - 'menu' => array('filename' => drupal_get_path('module', 'menu')), - ); - module_list(FALSE, FALSE, FALSE, $fixed_list); - $new_module_list = array_combine(array_keys($fixed_list), array_keys($fixed_list)); - $this->assertModuleList($new_module_list, t('When using a fixed list')); - - // Reset the module list. - module_list(TRUE); - $this->assertModuleList($module_list, t('After reset')); - } - - /** - * Assert that module_list() return the expected values. - * - * @param $expected_values - * The expected values, sorted by weight and module name. - */ - protected function assertModuleList(Array $expected_values, $condition) { - $expected_values = array_combine($expected_values, $expected_values); - $this->assertEqual($expected_values, module_list(), t('@condition: module_list() returns correct results', array('@condition' => $condition))); - ksort($expected_values); - $this->assertIdentical($expected_values, module_list(FALSE, FALSE, TRUE), t('@condition: module_list() returns correctly sorted results', array('@condition' => $condition))); - } - - /** - * Test module_implements() caching. - */ - function testModuleImplements() { - // Clear the cache. - cache('bootstrap')->delete('module_implements'); - $this->assertFalse(cache('bootstrap')->get('module_implements'), t('The module implements cache is empty.')); - $this->drupalGet(''); - $this->assertTrue(cache('bootstrap')->get('module_implements'), t('The module implements cache is populated after requesting a page.')); - - // Test again with an authenticated user. - $this->user = $this->drupalCreateUser(); - $this->drupalLogin($this->user); - cache('bootstrap')->delete('module_implements'); - $this->drupalGet(''); - $this->assertTrue(cache('bootstrap')->get('module_implements'), t('The module implements cache is populated after requesting a page.')); - - // Make sure group include files are detected properly even when the file is - // already loaded when the cache is rebuilt. - // For that activate the module_test which provides the file to load. - module_enable(array('module_test')); - - module_load_include('inc', 'module_test', 'module_test.file'); - $modules = module_implements('test_hook'); - $static = drupal_static('module_implements'); - $this->assertTrue(in_array('module_test', $modules), 'Hook found.'); - $this->assertEqual($static['test_hook']['module_test'], 'file', 'Include file detected.'); - } - - /** - * Test that module_invoke() can load a hook defined in hook_hook_info(). - */ - function testModuleInvoke() { - module_enable(array('module_test'), FALSE); - $this->resetAll(); - $this->drupalGet('module-test/hook-dynamic-loading-invoke'); - $this->assertText('success!', t('module_invoke() dynamically loads a hook defined in hook_hook_info().')); - } - - /** - * Test that module_invoke_all() can load a hook defined in hook_hook_info(). - */ - function testModuleInvokeAll() { - module_enable(array('module_test'), FALSE); - $this->resetAll(); - $this->drupalGet('module-test/hook-dynamic-loading-invoke-all'); - $this->assertText('success!', t('module_invoke_all() dynamically loads a hook defined in hook_hook_info().')); - } - - /** - * Test dependency resolution. - */ - function testDependencyResolution() { - // Enable the test module, and make sure that other modules we are testing - // are not already enabled. (If they were, the tests below would not work - // correctly.) - module_enable(array('module_test'), FALSE); - $this->assertTrue(module_exists('module_test'), t('Test module is enabled.')); - $this->assertFalse(module_exists('forum'), t('Forum module is disabled.')); - $this->assertFalse(module_exists('poll'), t('Poll module is disabled.')); - $this->assertFalse(module_exists('php'), t('PHP module is disabled.')); - - // First, create a fake missing dependency. Forum depends on poll, which - // depends on a made-up module, foo. Nothing should be installed. - variable_set('dependency_test', 'missing dependency'); - drupal_static_reset('system_rebuild_module_data'); - $result = module_enable(array('forum')); - $this->assertFalse($result, t('module_enable() returns FALSE if dependencies are missing.')); - $this->assertFalse(module_exists('forum'), t('module_enable() aborts if dependencies are missing.')); - - // Now, fix the missing dependency. Forum module depends on poll, but poll - // depends on the PHP module. module_enable() should work. - variable_set('dependency_test', 'dependency'); - drupal_static_reset('system_rebuild_module_data'); - $result = module_enable(array('forum')); - $this->assertTrue($result, t('module_enable() returns the correct value.')); - // Verify that the fake dependency chain was installed. - $this->assertTrue(module_exists('poll') && module_exists('php'), t('Dependency chain was installed by module_enable().')); - // Verify that the original module was installed. - $this->assertTrue(module_exists('forum'), t('Module installation with unlisted dependencies succeeded.')); - // Finally, verify that the modules were enabled in the correct order. - $this->assertEqual(variable_get('test_module_enable_order', array()), array('php', 'poll', 'forum'), t('Modules were enabled in the correct order by module_enable().')); - - // Now, disable the PHP module. Both forum and poll should be disabled as - // well, in the correct order. - module_disable(array('php')); - $this->assertTrue(!module_exists('forum') && !module_exists('poll'), t('Depedency chain was disabled by module_disable().')); - $this->assertFalse(module_exists('php'), t('Disabling a module with unlisted dependents succeeded.')); - $this->assertEqual(variable_get('test_module_disable_order', array()), array('forum', 'poll', 'php'), t('Modules were disabled in the correct order by module_disable().')); - - // Disable a module that is listed as a dependency by the install profile. - // Make sure that the profile itself is not on the list of dependent - // modules to be disabled. - $profile = drupal_get_profile(); - $info = install_profile_info($profile); - $this->assertTrue(in_array('comment', $info['dependencies']), t('Comment module is listed as a dependency of the install profile.')); - $this->assertTrue(module_exists('comment'), t('Comment module is enabled.')); - module_disable(array('comment')); - $this->assertFalse(module_exists('comment'), t('Comment module was disabled.')); - $disabled_modules = variable_get('test_module_disable_order', array()); - $this->assertTrue(in_array('comment', $disabled_modules), t('Comment module is in the list of disabled modules.')); - $this->assertFalse(in_array($profile, $disabled_modules), t('The installation profile is not in the list of disabled modules.')); - - // Try to uninstall the PHP module by itself. This should be rejected, - // since the modules which it depends on need to be uninstalled first, and - // that is too destructive to perform automatically. - $result = drupal_uninstall_modules(array('php')); - $this->assertFalse($result, t('Calling drupal_uninstall_modules() on a module whose dependents are not uninstalled fails.')); - foreach (array('forum', 'poll', 'php') as $module) { - $this->assertNotEqual(drupal_get_installed_schema_version($module), SCHEMA_UNINSTALLED, t('The @module module was not uninstalled.', array('@module' => $module))); - } - - // Now uninstall all three modules explicitly, but in the incorrect order, - // and make sure that drupal_uninstal_modules() uninstalled them in the - // correct sequence. - $result = drupal_uninstall_modules(array('poll', 'php', 'forum')); - $this->assertTrue($result, t('drupal_uninstall_modules() returns the correct value.')); - foreach (array('forum', 'poll', 'php') as $module) { - $this->assertEqual(drupal_get_installed_schema_version($module), SCHEMA_UNINSTALLED, t('The @module module was uninstalled.', array('@module' => $module))); - } - $this->assertEqual(variable_get('test_module_uninstall_order', array()), array('forum', 'poll', 'php'), t('Modules were uninstalled in the correct order by drupal_uninstall_modules().')); - - // Uninstall the profile module from above, and make sure that the profile - // itself is not on the list of dependent modules to be uninstalled. - $result = drupal_uninstall_modules(array('comment')); - $this->assertTrue($result, t('drupal_uninstall_modules() returns the correct value.')); - $this->assertEqual(drupal_get_installed_schema_version('comment'), SCHEMA_UNINSTALLED, t('Comment module was uninstalled.')); - $uninstalled_modules = variable_get('test_module_uninstall_order', array()); - $this->assertTrue(in_array('comment', $uninstalled_modules), t('Comment module is in the list of uninstalled modules.')); - $this->assertFalse(in_array($profile, $uninstalled_modules), t('The installation profile is not in the list of uninstalled modules.')); - - // Enable forum module again, which should enable both the poll module and - // php module. But, this time do it with poll module declaring a dependency - // on a specific version of php module in its info file. Make sure that - // module_enable() still works. - variable_set('dependency_test', 'version dependency'); - drupal_static_reset('system_rebuild_module_data'); - $result = module_enable(array('forum')); - $this->assertTrue($result, t('module_enable() returns the correct value.')); - // Verify that the fake dependency chain was installed. - $this->assertTrue(module_exists('poll') && module_exists('php'), t('Dependency chain was installed by module_enable().')); - // Verify that the original module was installed. - $this->assertTrue(module_exists('forum'), t('Module installation with version dependencies succeeded.')); - // Finally, verify that the modules were enabled in the correct order. - $enable_order = variable_get('test_module_enable_order', array()); - $php_position = array_search('php', $enable_order); - $poll_position = array_search('poll', $enable_order); - $forum_position = array_search('forum', $enable_order); - $php_before_poll = $php_position !== FALSE && $poll_position !== FALSE && $php_position < $poll_position; - $poll_before_forum = $poll_position !== FALSE && $forum_position !== FALSE && $poll_position < $forum_position; - $this->assertTrue($php_before_poll && $poll_before_forum, t('Modules were enabled in the correct order by module_enable().')); - } -} - -/** - * Unit tests for module installation. - */ -class ModuleInstallTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Module installation', - 'description' => 'Tests the installation of modules.', - 'group' => 'Module', - ); - } - - function setUp() { - parent::setUp('module_test'); - } - - /** - * Test that calls to drupal_write_record() work during module installation. - * - * This is a useful function to test because modules often use it to insert - * initial data in their database tables when they are being installed or - * enabled. Furthermore, drupal_write_record() relies on the module schema - * information being available, so this also checks that the data from one of - * the module's hook implementations, in particular hook_schema(), is - * properly available during this time. Therefore, this test helps ensure - * that modules are fully functional while Drupal is installing and enabling - * them. - */ - function testDrupalWriteRecord() { - // Check for data that was inserted using drupal_write_record() while the - // 'module_test' module was being installed and enabled. - $data = db_query("SELECT data FROM {module_test}")->fetchCol(); - $this->assertTrue(in_array('Data inserted in hook_install()', $data), t('Data inserted using drupal_write_record() in hook_install() is correctly saved.')); - $this->assertTrue(in_array('Data inserted in hook_enable()', $data), t('Data inserted using drupal_write_record() in hook_enable() is correctly saved.')); - } -} - -/** - * Unit tests for module uninstallation and related hooks. - */ -class ModuleUninstallTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Module uninstallation', - 'description' => 'Tests the uninstallation of modules.', - 'group' => 'Module', - ); - } - - function setUp() { - parent::setUp('module_test', 'user'); - } - - /** - * Tests the hook_modules_uninstalled() of the user module. - */ - function testUserPermsUninstalled() { - // Uninstalls the module_test module, so hook_modules_uninstalled() - // is executed. - module_disable(array('module_test')); - drupal_uninstall_modules(array('module_test')); - - // Are the perms defined by module_test removed from {role_permission}. - $count = db_query("SELECT COUNT(rid) FROM {role_permission} WHERE permission = :perm", array(':perm' => 'module_test perm'))->fetchField(); - $this->assertEqual(0, $count, t('Permissions were all removed.')); - } -} diff --git a/modules/simpletest/tests/module_test.file.inc b/modules/simpletest/tests/module_test.file.inc deleted file mode 100644 index c0d3ec41e68..00000000000 --- a/modules/simpletest/tests/module_test.file.inc +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @file - * A file to test module_implements() loading includes. - */ - -/** - * Implements hook_test_hook(). - */ -function module_test_test_hook() { - return array('module_test' => 'success!'); -} diff --git a/modules/simpletest/tests/module_test.info b/modules/simpletest/tests/module_test.info deleted file mode 100644 index c0b243c2dcc..00000000000 --- a/modules/simpletest/tests/module_test.info +++ /dev/null @@ -1,6 +0,0 @@ -name = "Module test" -description = "Support module for module system testing." -package = Testing -version = VERSION -core = 8.x -hidden = TRUE diff --git a/modules/simpletest/tests/module_test.install b/modules/simpletest/tests/module_test.install deleted file mode 100644 index 4cc09df5a8d..00000000000 --- a/modules/simpletest/tests/module_test.install +++ /dev/null @@ -1,42 +0,0 @@ -<?php - -/** - * @file - * Install, update and uninstall functions for the module_test module. - */ - -/** - * Implements hook_schema(). - */ -function module_test_schema() { - $schema['module_test'] = array( - 'description' => 'Dummy table to test the behavior of hook_schema() during module installation.', - 'fields' => array( - 'data' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - 'description' => 'An example data column for the module.', - ), - ), - ); - return $schema; -} - -/** - * Implements hook_install(). - */ -function module_test_install() { - $record = array('data' => 'Data inserted in hook_install()'); - drupal_write_record('module_test', $record); -} - -/** - * Implements hook_enable(). - */ -function module_test_enable() { - $record = array('data' => 'Data inserted in hook_enable()'); - drupal_write_record('module_test', $record); -} - diff --git a/modules/simpletest/tests/module_test.module b/modules/simpletest/tests/module_test.module deleted file mode 100644 index bd850498ce5..00000000000 --- a/modules/simpletest/tests/module_test.module +++ /dev/null @@ -1,131 +0,0 @@ -<?php - -/** - * Implements hook_permission(). - */ -function module_test_permission() { - return array( - 'module_test perm' => t('example perm for module_test module'), - ); -} - -/** - * Implements hook_system_info_alter(). - * - * Manipulate module dependencies to test dependency chains. - */ -function module_test_system_info_alter(&$info, $file, $type) { - if (variable_get('dependency_test', FALSE) == 'missing dependency') { - if ($file->name == 'forum') { - // Make forum module depend on poll. - $info['dependencies'][] = 'poll'; - } - elseif ($file->name == 'poll') { - // Make poll depend on a made-up module. - $info['dependencies'][] = 'foo'; - } - } - elseif (variable_get('dependency_test', FALSE) == 'dependency') { - if ($file->name == 'forum') { - // Make the forum module depend on poll. - $info['dependencies'][] = 'poll'; - } - elseif ($file->name == 'poll') { - // Make poll depend on php module. - $info['dependencies'][] = 'php'; - } - } - elseif (variable_get('dependency_test', FALSE) == 'version dependency') { - if ($file->name == 'forum') { - // Make the forum module depend on poll. - $info['dependencies'][] = 'poll'; - } - elseif ($file->name == 'poll') { - // Make poll depend on a specific version of php module. - $info['dependencies'][] = 'php (1.x)'; - } - elseif ($file->name == 'php') { - // Set php module to a version compatible with the above. - $info['version'] = '8.x-1.0'; - } - } - if ($file->name == 'seven' && $type == 'theme') { - $info['regions']['test_region'] = t('Test region'); - } -} - -/** - * Implements hook_hook_info(). - */ -function module_test_hook_info() { - $hooks['test_hook'] = array( - 'group' => 'file', - ); - return $hooks; -} - -/** - * Implements hook_menu(). - */ -function module_test_menu() { - $items['module-test/hook-dynamic-loading-invoke'] = array( - 'title' => 'Test hook dynamic loading (invoke)', - 'page callback' => 'module_test_hook_dynamic_loading_invoke', - 'access arguments' => array('access content'), - ); - $items['module-test/hook-dynamic-loading-invoke-all'] = array( - 'title' => 'Test hook dynamic loading (invoke_all)', - 'page callback' => 'module_test_hook_dynamic_loading_invoke_all', - 'access arguments' => array('access content'), - ); - return $items; -} - -/** - * Page callback for 'hook dynamic loading' test. - * - * If the hook is dynamically loaded correctly, the menu callback should - * return 'success!'. - */ -function module_test_hook_dynamic_loading_invoke() { - $result = module_invoke('module_test', 'test_hook'); - return $result['module_test']; -} - -/** - * Page callback for 'hook dynamic loading' test. - * - * If the hook is dynamically loaded correctly, the menu callback should - * return 'success!'. - */ -function module_test_hook_dynamic_loading_invoke_all() { - $result = module_invoke_all('test_hook'); - return $result['module_test']; -} - -/** - * Implements hook_modules_enabled(). - */ -function module_test_modules_enabled($modules) { - // Record the ordered list of modules that were passed in to this hook so we - // can check that the modules were enabled in the correct sequence. - variable_set('test_module_enable_order', $modules); -} - -/** - * Implements hook_modules_disabled(). - */ -function module_test_modules_disabled($modules) { - // Record the ordered list of modules that were passed in to this hook so we - // can check that the modules were disabled in the correct sequence. - variable_set('test_module_disable_order', $modules); -} - -/** - * Implements hook_modules_uninstalled(). - */ -function module_test_modules_uninstalled($modules) { - // Record the ordered list of modules that were passed in to this hook so we - // can check that the modules were uninstalled in the correct sequence. - variable_set('test_module_uninstall_order', $modules); -} diff --git a/modules/simpletest/tests/password.test b/modules/simpletest/tests/password.test deleted file mode 100644 index e100c2ef630..00000000000 --- a/modules/simpletest/tests/password.test +++ /dev/null @@ -1,60 +0,0 @@ -<?php - -/** - * @file - * Provides unit tests for password.inc. - */ - -/** - * Unit tests for password hashing API. - */ -class PasswordHashingTest extends DrupalWebTestCase { - protected $profile = 'testing'; - - public static function getInfo() { - return array( - 'name' => 'Password hashing', - 'description' => 'Password hashing unit tests.', - 'group' => 'System', - ); - } - - function setUp() { - require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc'); - parent::setUp(); - } - - /** - * Test password hashing. - */ - function testPasswordHashing() { - // Set a log2 iteration count that is deliberately out of bounds to test - // that it is corrected to be within bounds. - variable_set('password_count_log2', 1); - // Set up a fake $account with a password 'baz', hashed with md5. - $password = 'baz'; - $account = (object) array('name' => 'foo', 'pass' => md5($password)); - // The md5 password should be flagged as needing an update. - $this->assertTrue(user_needs_new_hash($account), t('User with md5 password needs a new hash.')); - // Re-hash the password. - $old_hash = $account->pass; - $account->pass = user_hash_password($password); - $this->assertIdentical(_password_get_count_log2($account->pass), DRUPAL_MIN_HASH_COUNT, t('Re-hashed password has the minimum number of log2 iterations.')); - $this->assertTrue($account->pass != $old_hash, t('Password hash changed.')); - $this->assertTrue(user_check_password($password, $account), t('Password check succeeds.')); - // Since the log2 setting hasn't changed and the user has a valid password, - // user_needs_new_hash() should return FALSE. - $this->assertFalse(user_needs_new_hash($account), t('User does not need a new hash.')); - // Increment the log2 iteration to MIN + 1. - variable_set('password_count_log2', DRUPAL_MIN_HASH_COUNT + 1); - $this->assertTrue(user_needs_new_hash($account), t('User needs a new hash after incrementing the log2 count.')); - // Re-hash the password. - $old_hash = $account->pass; - $account->pass = user_hash_password($password); - $this->assertIdentical(_password_get_count_log2($account->pass), DRUPAL_MIN_HASH_COUNT + 1, t('Re-hashed password has the correct number of log2 iterations.')); - $this->assertTrue($account->pass != $old_hash, t('Password hash changed again.')); - // Now the hash should be OK. - $this->assertFalse(user_needs_new_hash($account), t('Re-hashed password does not need a new hash.')); - $this->assertTrue(user_check_password($password, $account), t('Password check succeeds with re-hashed password.')); - } -} diff --git a/modules/simpletest/tests/path.test b/modules/simpletest/tests/path.test deleted file mode 100644 index 82598b5a2d3..00000000000 --- a/modules/simpletest/tests/path.test +++ /dev/null @@ -1,327 +0,0 @@ -<?php - -/** - * @file - * Tests for path.inc. - */ - -/** - * Unit tests for the drupal_match_path() function in path.inc. - * - * @see drupal_match_path(). - */ -class DrupalMatchPathTestCase extends DrupalWebTestCase { - protected $front; - - public static function getInfo() { - return array( - 'name' => 'Drupal match path', - 'description' => 'Tests the drupal_match_path() function to make sure it works properly.', - 'group' => 'Path API', - ); - } - - function setUp() { - // Set up the database and testing environment. - parent::setUp(); - - // Set up a random site front page to test the '<front>' placeholder. - $this->front = $this->randomName(); - variable_set('site_frontpage', $this->front); - // Refresh our static variables from the database. - $this->refreshVariables(); - } - - /** - * Run through our test cases, making sure each one works as expected. - */ - function testDrupalMatchPath() { - // Set up our test cases. - $tests = $this->drupalMatchPathTests(); - foreach ($tests as $patterns => $cases) { - foreach ($cases as $path => $expected_result) { - $actual_result = drupal_match_path($path, $patterns); - $this->assertIdentical($actual_result, $expected_result, t('Tried matching the path <code>@path</code> to the pattern <pre>@patterns</pre> - expected @expected, got @actual.', array('@path' => $path, '@patterns' => $patterns, '@expected' => var_export($expected_result, TRUE), '@actual' => var_export($actual_result, TRUE)))); - } - } - } - - /** - * Helper function for testDrupalMatchPath(): set up an array of test cases. - * - * @return - * An array of test cases to cycle through. - */ - private function drupalMatchPathTests() { - return array( - // Single absolute paths. - 'example/1' => array( - 'example/1' => TRUE, - 'example/2' => FALSE, - 'test' => FALSE, - ), - // Single paths with wildcards. - 'example/*' => array( - 'example/1' => TRUE, - 'example/2' => TRUE, - 'example/3/edit' => TRUE, - 'example/' => TRUE, - 'example' => FALSE, - 'test' => FALSE, - ), - // Single paths with multiple wildcards. - 'node/*/revisions/*' => array( - 'node/1/revisions/3' => TRUE, - 'node/345/revisions/test' => TRUE, - 'node/23/edit' => FALSE, - 'test' => FALSE, - ), - // Single paths with '<front>'. - '<front>' => array( - $this->front => TRUE, - "$this->front/" => FALSE, - "$this->front/edit" => FALSE, - 'node' => FALSE, - '' => FALSE, - ), - // Paths with both '<front>' and wildcards (should not work). - '<front>/*' => array( - $this->front => FALSE, - "$this->front/" => FALSE, - "$this->front/edit" => FALSE, - 'node/12' => FALSE, - '' => FALSE, - ), - // Multiple paths with the \n delimiter. - "node/*\nnode/*/edit" => array( - 'node/1' => TRUE, - 'node/view' => TRUE, - 'node/32/edit' => TRUE, - 'node/delete/edit' => TRUE, - 'node/50/delete' => TRUE, - 'test/example' => FALSE, - ), - // Multiple paths with the \r delimiter. - "user/*\rexample/*" => array( - 'user/1' => TRUE, - 'example/1' => TRUE, - 'user/1/example/1' => TRUE, - 'user/example' => TRUE, - 'test/example' => FALSE, - 'user' => FALSE, - 'example' => FALSE, - ), - // Multiple paths with the \r\n delimiter. - "test\r\n<front>" => array( - 'test' => TRUE, - $this->front => TRUE, - 'example' => FALSE, - ), - // Test existing regular expressions (should be escaped). - '[^/]+?/[0-9]' => array( - 'test/1' => FALSE, - '[^/]+?/[0-9]' => TRUE, - ), - ); - } -} - -/** - * Tests hook_url_alter functions. - */ -class UrlAlterFunctionalTest extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => t('URL altering'), - 'description' => t('Tests hook_url_inbound_alter() and hook_url_outbound_alter().'), - 'group' => t('Path API'), - ); - } - - function setUp() { - parent::setUp('path', 'forum', 'url_alter_test'); - } - - /** - * Test that URL altering works and that it occurs in the correct order. - */ - function testUrlAlter() { - $account = $this->drupalCreateUser(array('administer url aliases')); - $this->drupalLogin($account); - - $uid = $account->uid; - $name = $account->name; - - // Test a single altered path. - $this->assertUrlInboundAlter("user/$name", "user/$uid"); - $this->assertUrlOutboundAlter("user/$uid", "user/$name"); - - // Test that a path always uses its alias. - $path = array('source' => "user/$uid/test1", 'alias' => 'alias/test1'); - path_save($path); - $this->assertUrlInboundAlter('alias/test1', "user/$uid/test1"); - $this->assertUrlOutboundAlter("user/$uid/test1", 'alias/test1'); - - // Test that alias source paths are normalized in the interface. - $edit = array('source' => "user/$name/edit", 'alias' => 'alias/test2'); - $this->drupalPost('admin/config/search/path/add', $edit, t('Save')); - $this->assertText(t('The alias has been saved.')); - - // Test that a path always uses its alias. - $this->assertUrlInboundAlter('alias/test2', "user/$uid/edit"); - $this->assertUrlOutboundAlter("user/$uid/edit", 'alias/test2'); - - // Test a non-existent user is not altered. - $uid++; - $this->assertUrlInboundAlter("user/$uid", "user/$uid"); - $this->assertUrlOutboundAlter("user/$uid", "user/$uid"); - - // Test that 'forum' is altered to 'community' correctly, both at the root - // level and for a specific existing forum. - $this->assertUrlInboundAlter('community', 'forum'); - $this->assertUrlOutboundAlter('forum', 'community'); - $forum_vid = variable_get('forum_nav_vocabulary'); - $tid = db_insert('taxonomy_term_data') - ->fields(array( - 'name' => $this->randomName(), - 'vid' => $forum_vid, - )) - ->execute(); - $this->assertUrlInboundAlter("community/$tid", "forum/$tid"); - $this->assertUrlOutboundAlter("forum/$tid", "community/$tid"); - } - - /** - * Test current_path() and request_path(). - */ - function testCurrentUrlRequestedPath() { - $this->drupalGet('url-alter-test/bar'); - $this->assertRaw('request_path=url-alter-test/bar', t('request_path() returns the requested path.')); - $this->assertRaw('current_path=url-alter-test/foo', t('current_path() returns the internal path.')); - } - - /** - * Assert that an outbound path is altered to an expected value. - * - * @param $original - * A string with the original path that is run through url(). - * @param $final - * A string with the expected result after url(). - * @return - * TRUE if $original was correctly altered to $final, FALSE otherwise. - */ - protected function assertUrlOutboundAlter($original, $final) { - // Test outbound altering. - $result = url($original); - $base_path = base_path() . (variable_get('clean_url', '0') ? '' : '?q='); - $result = substr($result, strlen($base_path)); - $this->assertIdentical($result, $final, t('Altered outbound URL %original, expected %final, and got %result.', array('%original' => $original, '%final' => $final, '%result' => $result))); - } - - /** - * Assert that a inbound path is altered to an expected value. - * - * @param $original - * A string with the aliased or un-normal path that is run through - * drupal_get_normal_path(). - * @param $final - * A string with the expected result after url(). - * @return - * TRUE if $original was correctly altered to $final, FALSE otherwise. - */ - protected function assertUrlInboundAlter($original, $final) { - // Test inbound altering. - $result = drupal_get_normal_path($original); - $this->assertIdentical($result, $final, t('Altered inbound URL %original, expected %final, and got %result.', array('%original' => $original, '%final' => $final, '%result' => $result))); - } -} - -/** - * Unit test for drupal_lookup_path(). - */ -class PathLookupTest extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => t('Path lookup'), - 'description' => t('Tests that drupal_lookup_path() returns correct paths.'), - 'group' => t('Path API'), - ); - } - - /** - * Test that drupal_lookup_path() returns the correct path. - */ - function testDrupalLookupPath() { - $account = $this->drupalCreateUser(); - $uid = $account->uid; - $name = $account->name; - - // Test the situation where the source is the same for multiple aliases. - // Start with a language-neutral alias, which we will override. - $path = array( - 'source' => "user/$uid", - 'alias' => 'foo', - ); - path_save($path); - $this->assertEqual(drupal_lookup_path('alias', $path['source']), $path['alias'], t('Basic alias lookup works.')); - $this->assertEqual(drupal_lookup_path('source', $path['alias']), $path['source'], t('Basic source lookup works.')); - - // Create a language specific alias for the default language (English). - $path = array( - 'source' => "user/$uid", - 'alias' => "users/$name", - 'language' => 'en', - ); - path_save($path); - $this->assertEqual(drupal_lookup_path('alias', $path['source']), $path['alias'], t('English alias overrides language-neutral alias.')); - $this->assertEqual(drupal_lookup_path('source', $path['alias']), $path['source'], t('English source overrides language-neutral source.')); - - // Create a language-neutral alias for the same path, again. - $path = array( - 'source' => "user/$uid", - 'alias' => 'bar', - ); - path_save($path); - $this->assertEqual(drupal_lookup_path('alias', $path['source']), "users/$name", t('English alias still returned after entering a language-neutral alias.')); - - // Create a language-specific (xx-lolspeak) alias for the same path. - $path = array( - 'source' => "user/$uid", - 'alias' => 'LOL', - 'language' => 'xx-lolspeak', - ); - path_save($path); - $this->assertEqual(drupal_lookup_path('alias', $path['source']), "users/$name", t('English alias still returned after entering a LOLspeak alias.')); - // The LOLspeak alias should be returned if we really want LOLspeak. - $this->assertEqual(drupal_lookup_path('alias', $path['source'], 'xx-lolspeak'), 'LOL', t('LOLspeak alias returned if we specify xx-lolspeak to drupal_lookup_path().')); - - // Create a new alias for this path in English, which should override the - // previous alias for "user/$uid". - $path = array( - 'source' => "user/$uid", - 'alias' => 'users/my-new-path', - 'language' => 'en', - ); - path_save($path); - $this->assertEqual(drupal_lookup_path('alias', $path['source']), $path['alias'], t('Recently created English alias returned.')); - $this->assertEqual(drupal_lookup_path('source', $path['alias']), $path['source'], t('Recently created English source returned.')); - - // Remove the English aliases, which should cause a fallback to the most - // recently created language-neutral alias, 'bar'. - db_delete('url_alias') - ->condition('language', 'en') - ->execute(); - drupal_clear_path_cache(); - $this->assertEqual(drupal_lookup_path('alias', $path['source']), 'bar', t('Path lookup falls back to recently created language-neutral alias.')); - - // Test the situation where the alias and language are the same, but - // the source differs. The newer alias record should be returned. - $account2 = $this->drupalCreateUser(); - $path = array( - 'source' => 'user/' . $account2->uid, - 'alias' => 'bar', - ); - path_save($path); - $this->assertEqual(drupal_lookup_path('source', $path['alias']), $path['source'], t('Newer alias record is returned when comparing two LANGUAGE_NONE paths with the same alias.')); - } -} diff --git a/modules/simpletest/tests/registry.test b/modules/simpletest/tests/registry.test deleted file mode 100644 index bcd8d4e0dec..00000000000 --- a/modules/simpletest/tests/registry.test +++ /dev/null @@ -1,142 +0,0 @@ -<?php - -class RegistryParseFileTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Registry parse file test', - 'description' => 'Parse a simple file and check that its resources are saved to the database.', - 'group' => 'System' - ); - } - - function setUp() { - $chrs = hash('sha256', microtime() . mt_rand()); - $this->fileName = 'registry_test_' . substr($chrs, 0, 16); - $this->className = 'registry_test_class' . substr($chrs, 16, 16); - $this->interfaceName = 'registry_test_interface' . substr($chrs, 32, 16); - parent::setUp(); - } - - /** - * testRegistryParseFile - */ - function testRegistryParseFile() { - _registry_parse_file($this->fileName, $this->getFileContents()); - foreach (array('className', 'interfaceName') as $resource) { - $foundName = db_query('SELECT name FROM {registry} WHERE name = :name', array(':name' => $this->$resource))->fetchField(); - $this->assertTrue($this->$resource == $foundName, t('Resource "@resource" found.', array('@resource' => $this->$resource))); - } - } - - /** - * getFileContents - */ - function getFileContents() { - $file_contents = <<<CONTENTS -<?php - -class {$this->className} {} - -interface {$this->interfaceName} {} - -CONTENTS; - return $file_contents; - } - -} - -class RegistryParseFilesTestCase extends DrupalWebTestCase { - protected $fileTypes = array('new', 'existing_changed'); - - public static function getInfo() { - return array( - 'name' => 'Registry parse files test', - 'description' => 'Read two a simple files from disc, and check that their resources are saved to the database.', - 'group' => 'System' - ); - } - - function setUp() { - parent::setUp(); - // Create files with some php to parse - one 'new', one 'existing' so - // we test all the important code paths in _registry_parse_files. - foreach ($this->fileTypes as $fileType) { - $chrs = hash('sha256', microtime() . mt_rand()); - $this->$fileType = new stdClass(); - $this->$fileType->fileName = 'public://registry_test_' . substr($chrs, 0, 16); - $this->$fileType->className = 'registry_test_class' . substr($chrs, 16, 16); - $this->$fileType->interfaceName = 'registry_test_interface' . substr($chrs, 32, 16); - $this->$fileType->contents = $this->getFileContents($fileType); - file_save_data($this->$fileType->contents, $this->$fileType->fileName); - - if ($fileType == 'existing_changed') { - // Add a record with an incorrect hash. - $this->$fileType->fakeHash = hash('sha256', mt_rand()); - db_insert('registry_file') - ->fields(array( - 'hash' => $this->$fileType->fakeHash, - 'filename' => $this->$fileType->fileName, - )) - ->execute(); - - // Insert some fake resource records. - foreach (array('class', 'interface') as $type) { - db_insert('registry') - ->fields(array( - 'name' => $type . hash('sha256', microtime() . mt_rand()), - 'type' => $type, - 'filename' => $this->$fileType->fileName, - )) - ->execute(); - } - } - } - } - - /** - * testRegistryParseFiles - */ - function testRegistryParseFiles() { - _registry_parse_files($this->getFiles()); - foreach ($this->fileTypes as $fileType) { - // Test that we have all the right resources. - foreach (array('className', 'interfaceName') as $resource) { - $foundName = db_query('SELECT name FROM {registry} WHERE name = :name', array(':name' => $this->$fileType->$resource))->fetchField(); - $this->assertTrue($this->$fileType->$resource == $foundName, t('Resource "@resource" found.', array('@resource' => $this->$fileType->$resource))); - } - // Test that we have the right hash. - $hash = db_query('SELECT hash FROM {registry_file} WHERE filename = :filename', array(':filename' => $this->$fileType->fileName))->fetchField(); - $this->assertTrue(hash('sha256', $this->$fileType->contents) == $hash, t('sha-256 for "@filename" matched.' . $fileType . $hash, array('@filename' => $this->$fileType->fileName))); - } - } - - /** - * getFiles - */ - function getFiles() { - $files = array(); - foreach ($this->fileTypes as $fileType) { - $files[$this->$fileType->fileName] = array('module' => '', 'weight' => 0); - if ($fileType == 'existing_changed') { - $files[$this->$fileType->fileName]['hash'] = $this->$fileType->fakeHash; - } - } - return $files; - } - - /** - * getFileContents - */ - function getFileContents($fileType) { - $file_contents = <<<CONTENTS -<?php - -class {$this->$fileType->className} {} - -interface {$this->$fileType->interfaceName} {} - -CONTENTS; - return $file_contents; - } - -} diff --git a/modules/simpletest/tests/requirements1_test.info b/modules/simpletest/tests/requirements1_test.info deleted file mode 100644 index 6daa75e66db..00000000000 --- a/modules/simpletest/tests/requirements1_test.info +++ /dev/null @@ -1,6 +0,0 @@ -name = Requirements 1 Test -description = "Tests that a module is not installed when it fails hook_requirements('install')." -package = Testing -version = VERSION -core = 8.x -hidden = TRUE diff --git a/modules/simpletest/tests/requirements1_test.install b/modules/simpletest/tests/requirements1_test.install deleted file mode 100644 index 651d911abb8..00000000000 --- a/modules/simpletest/tests/requirements1_test.install +++ /dev/null @@ -1,21 +0,0 @@ -<?php - -/** - * Implements hook_requirements(). - */ -function requirements1_test_requirements($phase) { - $requirements = array(); - // Ensure translations don't break at install time. - $t = get_t(); - - // Always fails requirements. - if ('install' == $phase) { - $requirements['requirements1_test'] = array( - 'title' => $t('Requirements 1 Test'), - 'severity' => REQUIREMENT_ERROR, - 'description' => $t('Requirements 1 Test failed requirements.'), - ); - } - - return $requirements; -} diff --git a/modules/simpletest/tests/requirements1_test.module b/modules/simpletest/tests/requirements1_test.module deleted file mode 100644 index e52266b2ec7..00000000000 --- a/modules/simpletest/tests/requirements1_test.module +++ /dev/null @@ -1,7 +0,0 @@ -<?php - -/** - * @file - * Tests that a module is not installed when it fails - * hook_requirements('install'). - */ diff --git a/modules/simpletest/tests/requirements2_test.info b/modules/simpletest/tests/requirements2_test.info deleted file mode 100644 index 270be65cd62..00000000000 --- a/modules/simpletest/tests/requirements2_test.info +++ /dev/null @@ -1,8 +0,0 @@ -name = Requirements 2 Test -description = "Tests that a module is not installed when the one it depends on fails hook_requirements('install)." -dependencies[] = requirements1_test -dependencies[] = comment -package = Testing -version = VERSION -core = 8.x -hidden = TRUE diff --git a/modules/simpletest/tests/requirements2_test.module b/modules/simpletest/tests/requirements2_test.module deleted file mode 100644 index a4f43051557..00000000000 --- a/modules/simpletest/tests/requirements2_test.module +++ /dev/null @@ -1,7 +0,0 @@ -<?php - -/** - * @file - * Tests that a module is not installed when the one it depends on fails - * hook_requirements('install'). - */ diff --git a/modules/simpletest/tests/schema.test b/modules/simpletest/tests/schema.test deleted file mode 100644 index 8945117cbbd..00000000000 --- a/modules/simpletest/tests/schema.test +++ /dev/null @@ -1,384 +0,0 @@ -<?php - -/** - * @file - * Tests for the Database Schema API. - */ - -/** - * Unit tests for the Schema API. - */ -class SchemaTestCase extends DrupalWebTestCase { - /** - * A global counter for table and field creation. - */ - var $counter; - - public static function getInfo() { - return array( - 'name' => 'Schema API', - 'description' => 'Tests table creation and modification via the schema API.', - 'group' => 'Database', - ); - } - - /** - * - */ - function testSchema() { - // Try creating a table. - $table_specification = array( - 'description' => 'Schema table description.', - 'fields' => array( - 'id' => array( - 'type' => 'int', - 'default' => NULL, - ), - 'test_field' => array( - 'type' => 'int', - 'not null' => TRUE, - 'description' => 'Schema column description.', - ), - ), - ); - db_create_table('test_table', $table_specification); - - // Assert that the table exists. - $this->assertTrue(db_table_exists('test_table'), t('The table exists.')); - - // Assert that the table comment has been set. - $this->checkSchemaComment($table_specification['description'], 'test_table'); - - // Assert that the column comment has been set. - $this->checkSchemaComment($table_specification['fields']['test_field']['description'], 'test_table', 'test_field'); - - // An insert without a value for the column 'test_table' should fail. - $this->assertFalse($this->tryInsert(), t('Insert without a default failed.')); - - // Add a default value to the column. - db_field_set_default('test_table', 'test_field', 0); - // The insert should now succeed. - $this->assertTrue($this->tryInsert(), t('Insert with a default succeeded.')); - - // Remove the default. - db_field_set_no_default('test_table', 'test_field'); - // The insert should fail again. - $this->assertFalse($this->tryInsert(), t('Insert without a default failed.')); - - // Test for fake index and test for the boolean result of indexExists(). - $index_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field'); - $this->assertIdentical($index_exists, FALSE, t('Fake index does not exists')); - // Add index. - db_add_index('test_table', 'test_field', array('test_field')); - // Test for created index and test for the boolean result of indexExists(). - $index_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field'); - $this->assertIdentical($index_exists, TRUE, t('Index created.')); - - // Rename the table. - db_rename_table('test_table', 'test_table2'); - - // Index should be renamed. - $index_exists = Database::getConnection()->schema()->indexExists('test_table2', 'test_field'); - $this->assertTrue($index_exists, t('Index was renamed.')); - - // We need the default so that we can insert after the rename. - db_field_set_default('test_table2', 'test_field', 0); - $this->assertFalse($this->tryInsert(), t('Insert into the old table failed.')); - $this->assertTrue($this->tryInsert('test_table2'), t('Insert into the new table succeeded.')); - - // We should have successfully inserted exactly two rows. - $count = db_query('SELECT COUNT(*) FROM {test_table2}')->fetchField(); - $this->assertEqual($count, 2, t('Two fields were successfully inserted.')); - - // Try to drop the table. - db_drop_table('test_table2'); - $this->assertFalse(db_table_exists('test_table2'), t('The dropped table does not exist.')); - - // Recreate the table. - db_create_table('test_table', $table_specification); - db_field_set_default('test_table', 'test_field', 0); - db_add_field('test_table', 'test_serial', array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'description' => 'Added column description.')); - - // Assert that the column comment has been set. - $this->checkSchemaComment('Added column description.', 'test_table', 'test_serial'); - - // Change the new field to a serial column. - db_change_field('test_table', 'test_serial', 'test_serial', array('type' => 'serial', 'not null' => TRUE, 'description' => 'Changed column description.'), array('primary key' => array('test_serial'))); - - // Assert that the column comment has been set. - $this->checkSchemaComment('Changed column description.', 'test_table', 'test_serial'); - - $this->assertTrue($this->tryInsert(), t('Insert with a serial succeeded.')); - $max1 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField(); - $this->assertTrue($this->tryInsert(), t('Insert with a serial succeeded.')); - $max2 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField(); - $this->assertTrue($max2 > $max1, t('The serial is monotone.')); - - $count = db_query('SELECT COUNT(*) FROM {test_table}')->fetchField(); - $this->assertEqual($count, 2, t('There were two rows.')); - - // Use database specific data type and ensure that table is created. - $table_specification = array( - 'description' => 'Schema table description.', - 'fields' => array( - 'timestamp' => array( - 'mysql_type' => 'timestamp', - 'pgsql_type' => 'timestamp', - 'sqlite_type' => 'datetime', - 'not null' => FALSE, - 'default' => NULL, - ), - ), - ); - try { - db_create_table('test_timestamp', $table_specification); - } - catch (Exception $e) {} - $this->assertTrue(db_table_exists('test_timestamp'), t('Table with database specific datatype was created.')); - } - - function tryInsert($table = 'test_table') { - try { - db_insert($table) - ->fields(array('id' => mt_rand(10, 20))) - ->execute(); - return TRUE; - } - catch (Exception $e) { - return FALSE; - } - } - - /** - * Checks that a table or column comment matches a given description. - * - * @param $description - * The asserted description. - * @param $table - * The table to test. - * @param $column - * Optional column to test. - */ - function checkSchemaComment($description, $table, $column = NULL) { - if (method_exists(Database::getConnection()->schema(), 'getComment')) { - $comment = Database::getConnection()->schema()->getComment($table, $column); - $this->assertEqual($comment, $description, t('The comment matches the schema description.')); - } - } - - /** - * Tests creating unsigned columns and data integrity thereof. - */ - function testUnsignedColumns() { - // First create the table with just a serial column. - $table_name = 'unsigned_table'; - $table_spec = array( - 'fields' => array('serial_column' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE)), - 'primary key' => array('serial_column'), - ); - $ret = array(); - db_create_table($table_name, $table_spec); - - // Now set up columns for the other types. - $types = array('int', 'float', 'numeric'); - foreach ($types as $type) { - $column_spec = array('type' => $type, 'unsigned'=> TRUE); - if ($type == 'numeric') { - $column_spec += array('precision' => 10, 'scale' => 0); - } - $column_name = $type . '_column'; - $table_spec['fields'][$column_name] = $column_spec; - db_add_field($table_name, $column_name, $column_spec); - } - - // Finally, check each column and try to insert invalid values into them. - foreach ($table_spec['fields'] as $column_name => $column_spec) { - $this->assertTrue(db_field_exists($table_name, $column_name), t('Unsigned @type column was created.', array('@type' => $column_spec['type']))); - $this->assertFalse($this->tryUnsignedInsert($table_name, $column_name), t('Unsigned @type column rejected a negative value.', array('@type' => $column_spec['type']))); - } - } - - /** - * Tries to insert a negative value into columns defined as unsigned. - * - * @param $table_name - * The table to insert - * @param $column_name - * The column to insert - * @return - * TRUE if the insert succeeded, FALSE otherwise - */ - function tryUnsignedInsert($table_name, $column_name) { - try { - db_insert($table_name) - ->fields(array($column_name => -1)) - ->execute(); - return TRUE; - } - catch (Exception $e) { - return FALSE; - } - } - - /** - * Test adding columns to an existing table. - */ - function testSchemaAddField() { - // Test varchar types. - foreach (array(1, 32, 128, 256, 512) as $length) { - $base_field_spec = array( - 'type' => 'varchar', - 'length' => $length, - ); - $variations = array( - array('not null' => FALSE), - array('not null' => FALSE, 'default' => '7'), - array('not null' => TRUE, 'initial' => 'd'), - array('not null' => TRUE, 'initial' => 'd', 'default' => '7'), - ); - - foreach ($variations as $variation) { - $field_spec = $variation + $base_field_spec; - $this->assertFieldAdditionRemoval($field_spec); - } - } - - // Test int and float types. - foreach (array('int', 'float') as $type) { - foreach (array('tiny', 'small', 'medium', 'normal', 'big') as $size) { - $base_field_spec = array( - 'type' => $type, - 'size' => $size, - ); - $variations = array( - array('not null' => FALSE), - array('not null' => FALSE, 'default' => 7), - array('not null' => TRUE, 'initial' => 1), - array('not null' => TRUE, 'initial' => 1, 'default' => 7), - ); - - foreach ($variations as $variation) { - $field_spec = $variation + $base_field_spec; - $this->assertFieldAdditionRemoval($field_spec); - } - } - } - - // Test numeric types. - foreach (array(1, 5, 10, 40, 65) as $precision) { - foreach (array(0, 2, 10, 30) as $scale) { - if ($precision <= $scale) { - // Precision must be smaller then scale. - continue; - } - - $base_field_spec = array( - 'type' => 'numeric', - 'scale' => $scale, - 'precision' => $precision, - ); - $variations = array( - array('not null' => FALSE), - array('not null' => FALSE, 'default' => 7), - array('not null' => TRUE, 'initial' => 1), - array('not null' => TRUE, 'initial' => 1, 'default' => 7), - ); - - foreach ($variations as $variation) { - $field_spec = $variation + $base_field_spec; - $this->assertFieldAdditionRemoval($field_spec); - } - } - } - } - - /** - * Assert that a given field can be added and removed from a table. - * - * The addition test covers both defining a field of a given specification - * when initially creating at table and extending an existing table. - * - * @param $field_spec - * The schema specification of the field. - */ - protected function assertFieldAdditionRemoval($field_spec) { - // Try creating the field on a new table. - $table_name = 'test_table_' . ($this->counter++); - $table_spec = array( - 'fields' => array( - 'serial_column' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE), - 'test_field' => $field_spec, - ), - 'primary key' => array('serial_column'), - ); - db_create_table($table_name, $table_spec); - $this->pass(t('Table %table created.', array('%table' => $table_name))); - - // Check the characteristics of the field. - $this->assertFieldCharacteristics($table_name, 'test_field', $field_spec); - - // Clean-up. - db_drop_table($table_name); - - // Try adding a field to an existing table. - $table_name = 'test_table_' . ($this->counter++); - $table_spec = array( - 'fields' => array( - 'serial_column' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE), - ), - 'primary key' => array('serial_column'), - ); - db_create_table($table_name, $table_spec); - $this->pass(t('Table %table created.', array('%table' => $table_name))); - - // Insert some rows to the table to test the handling of initial values. - for ($i = 0; $i < 3; $i++) { - db_insert($table_name) - ->useDefaults(array('serial_column')) - ->execute(); - } - - db_add_field($table_name, 'test_field', $field_spec); - $this->pass(t('Column %column created.', array('%column' => 'test_field'))); - - // Check the characteristics of the field. - $this->assertFieldCharacteristics($table_name, 'test_field', $field_spec); - - // Clean-up. - db_drop_field($table_name, 'test_field'); - db_drop_table($table_name); - } - - /** - * Assert that a newly added field has the correct characteristics. - */ - protected function assertFieldCharacteristics($table_name, $field_name, $field_spec) { - // Check that the initial value has been registered. - if (isset($field_spec['initial'])) { - // There should be no row with a value different then $field_spec['initial']. - $count = db_select($table_name) - ->fields($table_name, array('serial_column')) - ->condition($field_name, $field_spec['initial'], '<>') - ->countQuery() - ->execute() - ->fetchField(); - $this->assertEqual($count, 0, t('Initial values filled out.')); - } - - // Check that the default value has been registered. - if (isset($field_spec['default'])) { - // Try inserting a row, and check the resulting value of the new column. - $id = db_insert($table_name) - ->useDefaults(array('serial_column')) - ->execute(); - $field_value = db_select($table_name) - ->fields($table_name, array($field_name)) - ->condition('serial_column', $id) - ->execute() - ->fetchField(); - $this->assertEqual($field_value, $field_spec['default'], t('Default value registered.')); - } - - db_drop_field($table_name, $field_name); - } -} diff --git a/modules/simpletest/tests/session.test b/modules/simpletest/tests/session.test deleted file mode 100644 index bc72e5c5ee7..00000000000 --- a/modules/simpletest/tests/session.test +++ /dev/null @@ -1,536 +0,0 @@ -<?php - -/** - * @file - * Provides SimpleTests for core session handling functionality. - */ - -class SessionTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Session tests', - 'description' => 'Drupal session handling tests.', - 'group' => 'Session' - ); - } - - function setUp() { - parent::setUp('session_test'); - } - - /** - * Tests for drupal_save_session() and drupal_session_regenerate(). - */ - function testSessionSaveRegenerate() { - $this->assertFalse(drupal_save_session(), t('drupal_save_session() correctly returns FALSE (inside of testing framework) when initially called with no arguments.'), t('Session')); - $this->assertFalse(drupal_save_session(FALSE), t('drupal_save_session() correctly returns FALSE when called with FALSE.'), t('Session')); - $this->assertFalse(drupal_save_session(), t('drupal_save_session() correctly returns FALSE when saving has been disabled.'), t('Session')); - $this->assertTrue(drupal_save_session(TRUE), t('drupal_save_session() correctly returns TRUE when called with TRUE.'), t('Session')); - $this->assertTrue(drupal_save_session(), t('drupal_save_session() correctly returns TRUE when saving has been enabled.'), t('Session')); - - // Test session hardening code from SA-2008-044. - $user = $this->drupalCreateUser(array('access content')); - - // Enable sessions. - $this->sessionReset($user->uid); - - // Make sure the session cookie is set as HttpOnly. - $this->drupalLogin($user); - $this->assertTrue(preg_match('/HttpOnly/i', $this->drupalGetHeader('Set-Cookie', TRUE)), t('Session cookie is set as HttpOnly.')); - $this->drupalLogout(); - - // Verify that the session is regenerated if a module calls exit - // in hook_user_login(). - user_save($user, array('name' => 'session_test_user')); - $user->name = 'session_test_user'; - $this->drupalGet('session-test/id'); - $matches = array(); - preg_match('/\s*session_id:(.*)\n/', $this->drupalGetContent(), $matches); - $this->assertTrue(!empty($matches[1]) , t('Found session ID before logging in.')); - $original_session = $matches[1]; - - // We cannot use $this->drupalLogin($user); because we exit in - // session_test_user_login() which breaks a normal assertion. - $edit = array( - 'name' => $user->name, - 'pass' => $user->pass_raw - ); - $this->drupalPost('user', $edit, t('Log in')); - $this->drupalGet('user'); - $pass = $this->assertText($user->name, t('Found name: %name', array('%name' => $user->name)), t('User login')); - $this->_logged_in = $pass; - - $this->drupalGet('session-test/id'); - $matches = array(); - preg_match('/\s*session_id:(.*)\n/', $this->drupalGetContent(), $matches); - $this->assertTrue(!empty($matches[1]) , t('Found session ID after logging in.')); - $this->assertTrue($matches[1] != $original_session, t('Session ID changed after login.')); - } - - /** - * Test data persistence via the session_test module callbacks. Also tests - * drupal_session_count() since session data is already generated here. - */ - function testDataPersistence() { - $user = $this->drupalCreateUser(array('access content')); - // Enable sessions. - $this->sessionReset($user->uid); - - $this->drupalLogin($user); - - $value_1 = $this->randomName(); - $this->drupalGet('session-test/set/' . $value_1); - $this->assertText($value_1, t('The session value was stored.'), t('Session')); - $this->drupalGet('session-test/get'); - $this->assertText($value_1, t('Session correctly returned the stored data for an authenticated user.'), t('Session')); - - // Attempt to write over val_1. If drupal_save_session(FALSE) is working. - // properly, val_1 will still be set. - $value_2 = $this->randomName(); - $this->drupalGet('session-test/no-set/' . $value_2); - $this->assertText($value_2, t('The session value was correctly passed to session-test/no-set.'), t('Session')); - $this->drupalGet('session-test/get'); - $this->assertText($value_1, t('Session data is not saved for drupal_save_session(FALSE).'), t('Session')); - - // Switch browser cookie to anonymous user, then back to user 1. - $this->sessionReset(); - $this->sessionReset($user->uid); - $this->assertText($value_1, t('Session data persists through browser close.'), t('Session')); - - // Logout the user and make sure the stored value no longer persists. - $this->drupalLogout(); - $this->sessionReset(); - $this->drupalGet('session-test/get'); - $this->assertNoText($value_1, t("After logout, previous user's session data is not available."), t('Session')); - - // Now try to store some data as an anonymous user. - $value_3 = $this->randomName(); - $this->drupalGet('session-test/set/' . $value_3); - $this->assertText($value_3, t('Session data stored for anonymous user.'), t('Session')); - $this->drupalGet('session-test/get'); - $this->assertText($value_3, t('Session correctly returned the stored data for an anonymous user.'), t('Session')); - - // Try to store data when drupal_save_session(FALSE). - $value_4 = $this->randomName(); - $this->drupalGet('session-test/no-set/' . $value_4); - $this->assertText($value_4, t('The session value was correctly passed to session-test/no-set.'), t('Session')); - $this->drupalGet('session-test/get'); - $this->assertText($value_3, t('Session data is not saved for drupal_save_session(FALSE).'), t('Session')); - - // Login, the data should persist. - $this->drupalLogin($user); - $this->sessionReset($user->uid); - $this->drupalGet('session-test/get'); - $this->assertNoText($value_1, t('Session has persisted for an authenticated user after logging out and then back in.'), t('Session')); - - // Change session and create another user. - $user2 = $this->drupalCreateUser(array('access content')); - $this->sessionReset($user2->uid); - $this->drupalLogin($user2); - } - - /** - * Test that empty anonymous sessions are destroyed. - */ - function testEmptyAnonymousSession() { - // Verify that no session is automatically created for anonymous user. - $this->drupalGet(''); - $this->assertSessionCookie(FALSE); - $this->assertSessionEmpty(TRUE); - - // The same behavior is expected when caching is enabled. - variable_set('cache', 1); - $this->drupalGet(''); - $this->assertSessionCookie(FALSE); - $this->assertSessionEmpty(TRUE); - $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', t('Page was not cached.')); - - // Start a new session by setting a message. - $this->drupalGet('session-test/set-message'); - $this->assertSessionCookie(TRUE); - $this->assertTrue($this->drupalGetHeader('Set-Cookie'), t('New session was started.')); - - // Display the message, during the same request the session is destroyed - // and the session cookie is unset. - $this->drupalGet(''); - $this->assertSessionCookie(FALSE); - $this->assertSessionEmpty(FALSE); - $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), t('Caching was bypassed.')); - $this->assertText(t('This is a dummy message.'), t('Message was displayed.')); - $this->assertTrue(preg_match('/SESS\w+=deleted/', $this->drupalGetHeader('Set-Cookie')), t('Session cookie was deleted.')); - - // Verify that session was destroyed. - $this->drupalGet(''); - $this->assertSessionCookie(FALSE); - $this->assertSessionEmpty(TRUE); - $this->assertNoText(t('This is a dummy message.'), t('Message was not cached.')); - $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.')); - $this->assertFalse($this->drupalGetHeader('Set-Cookie'), t('New session was not started.')); - - // Verify that no session is created if drupal_save_session(FALSE) is called. - $this->drupalGet('session-test/set-message-but-dont-save'); - $this->assertSessionCookie(FALSE); - $this->assertSessionEmpty(TRUE); - - // Verify that no message is displayed. - $this->drupalGet(''); - $this->assertSessionCookie(FALSE); - $this->assertSessionEmpty(TRUE); - $this->assertNoText(t('This is a dummy message.'), t('The message was not saved.')); - } - - /** - * Test that sessions are only saved when necessary. - */ - function testSessionWrite() { - $user = $this->drupalCreateUser(array('access content')); - $this->drupalLogin($user); - - $sql = 'SELECT u.access, s.timestamp FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE u.uid = :uid'; - $times1 = db_query($sql, array(':uid' => $user->uid))->fetchObject(); - - // Before every request we sleep one second to make sure that if the session - // is saved, its timestamp will change. - - // Modify the session. - sleep(1); - $this->drupalGet('session-test/set/foo'); - $times2 = db_query($sql, array(':uid' => $user->uid))->fetchObject(); - $this->assertEqual($times2->access, $times1->access, t('Users table was not updated.')); - $this->assertNotEqual($times2->timestamp, $times1->timestamp, t('Sessions table was updated.')); - - // Write the same value again, i.e. do not modify the session. - sleep(1); - $this->drupalGet('session-test/set/foo'); - $times3 = db_query($sql, array(':uid' => $user->uid))->fetchObject(); - $this->assertEqual($times3->access, $times1->access, t('Users table was not updated.')); - $this->assertEqual($times3->timestamp, $times2->timestamp, t('Sessions table was not updated.')); - - // Do not change the session. - sleep(1); - $this->drupalGet(''); - $times4 = db_query($sql, array(':uid' => $user->uid))->fetchObject(); - $this->assertEqual($times4->access, $times3->access, t('Users table was not updated.')); - $this->assertEqual($times4->timestamp, $times3->timestamp, t('Sessions table was not updated.')); - - // Force updating of users and sessions table once per second. - variable_set('session_write_interval', 0); - $this->drupalGet(''); - $times5 = db_query($sql, array(':uid' => $user->uid))->fetchObject(); - $this->assertNotEqual($times5->access, $times4->access, t('Users table was updated.')); - $this->assertNotEqual($times5->timestamp, $times4->timestamp, t('Sessions table was updated.')); - } - - /** - * Test that empty session IDs are not allowed. - */ - function testEmptySessionID() { - $user = $this->drupalCreateUser(array('access content')); - $this->drupalLogin($user); - $this->drupalGet('session-test/is-logged-in'); - $this->assertResponse(200, t('User is logged in.')); - - // Reset the sid in {sessions} to a blank string. This may exist in the - // wild in some cases, although we normally prevent it from happening. - db_query("UPDATE {sessions} SET sid = '' WHERE uid = :uid", array(':uid' => $user->uid)); - // Send a blank sid in the session cookie, and the session should no longer - // be valid. Closing the curl handler will stop the previous session ID - // from persisting. - $this->curlClose(); - $this->additionalCurlOptions[CURLOPT_COOKIE] = rawurlencode($this->session_name) . '=;'; - $this->drupalGet('session-test/id-from-cookie'); - $this->assertRaw("session_id:\n", t('Session ID is blank as sent from cookie header.')); - // Assert that we have an anonymous session now. - $this->drupalGet('session-test/is-logged-in'); - $this->assertResponse(403, t('An empty session ID is not allowed.')); - } - - /** - * Reset the cookie file so that it refers to the specified user. - * - * @param $uid User id to set as the active session. - */ - function sessionReset($uid = 0) { - // Close the internal browser. - $this->curlClose(); - $this->loggedInUser = FALSE; - - // Change cookie file for user. - $this->cookieFile = file_stream_wrapper_get_instance_by_scheme('temporary')->getDirectoryPath() . '/cookie.' . $uid . '.txt'; - $this->additionalCurlOptions[CURLOPT_COOKIEFILE] = $this->cookieFile; - $this->additionalCurlOptions[CURLOPT_COOKIESESSION] = TRUE; - $this->drupalGet('session-test/get'); - $this->assertResponse(200, t('Session test module is correctly enabled.'), t('Session')); - } - - /** - * Assert whether the SimpleTest browser sent a session cookie. - */ - function assertSessionCookie($sent) { - if ($sent) { - $this->assertNotNull($this->session_id, t('Session cookie was sent.')); - } - else { - $this->assertNull($this->session_id, t('Session cookie was not sent.')); - } - } - - /** - * Assert whether $_SESSION is empty at the beginning of the request. - */ - function assertSessionEmpty($empty) { - if ($empty) { - $this->assertIdentical($this->drupalGetHeader('X-Session-Empty'), '1', t('Session was empty.')); - } - else { - $this->assertIdentical($this->drupalGetHeader('X-Session-Empty'), '0', t('Session was not empty.')); - } - } -} - -/** - * Ensure that when running under https two session cookies are generated. - */ -class SessionHttpsTestCase extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Session https handling', - 'description' => 'Ensure that when running under https two session cookies are generated.', - 'group' => 'Session' - ); - } - - public function setUp() { - parent::setUp('session_test'); - } - - protected function testHttpsSession() { - global $is_https; - - if ($is_https) { - $secure_session_name = session_name(); - $insecure_session_name = substr(session_name(), 1); - } - else { - $secure_session_name = 'S' . session_name(); - $insecure_session_name = session_name(); - } - - $user = $this->drupalCreateUser(array('access administration pages')); - - // Test HTTPS session handling by altering the form action to submit the - // login form through https.php, which creates a mock HTTPS request. - $this->drupalGet('user'); - $form = $this->xpath('//form[@id="user-login"]'); - $form[0]['action'] = $this->httpsUrl('user'); - $edit = array('name' => $user->name, 'pass' => $user->pass_raw); - $this->drupalPost(NULL, $edit, t('Log in')); - - // Test a second concurrent session. - $this->curlClose(); - $this->drupalGet('user'); - $form = $this->xpath('//form[@id="user-login"]'); - $form[0]['action'] = $this->httpsUrl('user'); - $this->drupalPost(NULL, $edit, t('Log in')); - - // Check secure cookie on secure page. - $this->assertTrue($this->cookies[$secure_session_name]['secure'], 'The secure cookie has the secure attribute'); - // Check insecure cookie is not set. - $this->assertFalse(isset($this->cookies[$insecure_session_name])); - $ssid = $this->cookies[$secure_session_name]['value']; - $this->assertSessionIds($ssid, $ssid, 'Session has a non-empty SID and a correct secure SID.'); - $cookie = $secure_session_name . '=' . $ssid; - - // Verify that user is logged in on secure URL. - $this->curlClose(); - $this->drupalGet($this->httpsUrl('admin/config'), array(), array('Cookie: ' . $cookie)); - $this->assertText(t('Configuration')); - $this->assertResponse(200); - - // Verify that user is not logged in on non-secure URL. - $this->curlClose(); - $this->drupalGet($this->httpUrl('admin/config'), array(), array('Cookie: ' . $cookie)); - $this->assertNoText(t('Configuration')); - $this->assertResponse(403); - - // Verify that empty SID cannot be used on the non-secure site. - $this->curlClose(); - $cookie = $insecure_session_name . '='; - $this->drupalGet($this->httpUrl('admin/config'), array(), array('Cookie: ' . $cookie)); - $this->assertResponse(403); - - // Test HTTP session handling by altering the form action to submit the - // login form through http.php, which creates a mock HTTP request on HTTPS - // test environments. - $this->curlClose(); - $this->drupalGet('user'); - $form = $this->xpath('//form[@id="user-login"]'); - $form[0]['action'] = $this->httpUrl('user'); - $edit = array('name' => $user->name, 'pass' => $user->pass_raw); - $this->drupalPost(NULL, $edit, t('Log in')); - $this->drupalGet($this->httpUrl('admin/config')); - $this->assertResponse(200); - $sid = $this->cookies[$insecure_session_name]['value']; - $this->assertSessionIds($sid, '', 'Session has the correct SID and an empty secure SID.'); - - // Verify that empty secure SID cannot be used on the secure site. - $this->curlClose(); - $cookie = $secure_session_name . '='; - $this->drupalGet($this->httpsUrl('admin/config'), array(), array('Cookie: ' . $cookie)); - $this->assertResponse(403); - - // Clear browser cookie jar. - $this->cookies = array(); - - if ($is_https) { - // The functionality does not make sense when running on https. - return; - } - - // Enable secure pages. - variable_set('https', TRUE); - - $this->curlClose(); - // Start an anonymous session on the insecure site. - $session_data = $this->randomName(); - $this->drupalGet('session-test/set/' . $session_data); - // Check secure cookie on insecure page. - $this->assertFalse(isset($this->cookies[$secure_session_name]), 'The secure cookie is not sent on insecure pages.'); - // Check insecure cookie on insecure page. - $this->assertFalse($this->cookies[$insecure_session_name]['secure'], 'The insecure cookie does not have the secure attribute'); - - // Store the anonymous cookie so we can validate that its session is killed - // after login. - $anonymous_cookie = $insecure_session_name . '=' . $this->cookies[$insecure_session_name]['value']; - - // Check that password request form action is not secure. - $this->drupalGet('user/password'); - $form = $this->xpath('//form[@id="user-pass"]'); - $this->assertNotEqual(substr($form[0]['action'], 0, 6), 'https:', 'Password request form action is not secure'); - $form[0]['action'] = $this->httpsUrl('user'); - - // Check that user login form action is secure. - $this->drupalGet('user'); - $form = $this->xpath('//form[@id="user-login"]'); - $this->assertEqual(substr($form[0]['action'], 0, 6), 'https:', 'Login form action is secure'); - $form[0]['action'] = $this->httpsUrl('user'); - - $edit = array( - 'name' => $user->name, - 'pass' => $user->pass_raw, - ); - $this->drupalPost(NULL, $edit, t('Log in')); - // Check secure cookie on secure page. - $this->assertTrue($this->cookies[$secure_session_name]['secure'], 'The secure cookie has the secure attribute'); - // Check insecure cookie on secure page. - $this->assertFalse($this->cookies[$insecure_session_name]['secure'], 'The insecure cookie does not have the secure attribute'); - - $sid = $this->cookies[$insecure_session_name]['value']; - $ssid = $this->cookies[$secure_session_name]['value']; - $this->assertSessionIds($sid, $ssid, 'Session has both secure and insecure SIDs'); - $cookies = array( - $insecure_session_name . '=' . $sid, - $secure_session_name . '=' . $ssid, - ); - - // Test that session data saved before login is still available on the - // authenticated session. - $this->drupalGet('session-test/get'); - $this->assertText($session_data, 'Session correctly returned the stored data set by the anonymous session.'); - - foreach ($cookies as $cookie_key => $cookie) { - foreach (array('admin/config', $this->httpsUrl('admin/config')) as $url_key => $url) { - $this->curlClose(); - - $this->drupalGet($url, array(), array('Cookie: ' . $cookie)); - if ($cookie_key == $url_key) { - $this->assertText(t('Configuration')); - $this->assertResponse(200); - } - else { - $this->assertNoText(t('Configuration')); - $this->assertResponse(403); - } - } - } - - // Test that session data saved before login is not available using the - // pre-login anonymous cookie. - $this->cookies = array(); - $this->drupalGet('session-test/get', array('Cookie: ' . $anonymous_cookie)); - $this->assertNoText($session_data, 'Initial anonymous session is inactive after login.'); - - // Clear browser cookie jar. - $this->cookies = array(); - - // Start an anonymous session on the secure site. - $this->drupalGet($this->httpsUrl('session-test/set/1')); - - // Mock a login to the secure site using the secure session cookie. - $this->drupalGet('user'); - $form = $this->xpath('//form[@id="user-login"]'); - $form[0]['action'] = $this->httpsUrl('user'); - $this->drupalPost(NULL, $edit, t('Log in'), array(), array('Cookie: ' . $secure_session_name . '=' . $this->cookies[$secure_session_name]['value'])); - - // Get the insecure session cookie set by the secure login POST request. - $headers = $this->drupalGetHeaders(TRUE); - strtok($headers[0]['set-cookie'], ';='); - $session_id = strtok(';='); - - // Test that the user is also authenticated on the insecure site. - $this->drupalGet("user/{$user->uid}/edit", array(), array('Cookie: ' . $insecure_session_name . '=' . $session_id)); - $this->assertResponse(200); - } - - /** - * Test that there exists a session with two specific session IDs. - * - * @param $sid - * The insecure session ID to search for. - * @param $ssid - * The secure session ID to search for. - * @param $assertion_text - * The text to display when we perform the assertion. - * - * @return - * The result of assertTrue() that there's a session in the system that - * has the given insecure and secure session IDs. - */ - protected function assertSessionIds($sid, $ssid, $assertion_text) { - $args = array( - ':sid' => $sid, - ':ssid' => $ssid, - ); - return $this->assertTrue(db_query('SELECT timestamp FROM {sessions} WHERE sid = :sid AND ssid = :ssid', $args)->fetchField(), $assertion_text); - } - - /** - * Builds a URL for submitting a mock HTTPS request to HTTP test environments. - * - * @param $url - * A Drupal path such as 'user'. - * - * @return - * An absolute URL. - */ - protected function httpsUrl($url) { - global $base_url; - return $base_url . '/modules/simpletest/tests/https.php?q=' . $url; - } - - /** - * Builds a URL for submitting a mock HTTP request to HTTPS test environments. - * - * @param $url - * A Drupal path such as 'user'. - * - * @return - * An absolute URL. - */ - protected function httpUrl($url) { - global $base_url; - return $base_url . '/modules/simpletest/tests/http.php?q=' . $url; - } -} - diff --git a/modules/simpletest/tests/session_test.info b/modules/simpletest/tests/session_test.info deleted file mode 100644 index 73de0a1a63c..00000000000 --- a/modules/simpletest/tests/session_test.info +++ /dev/null @@ -1,6 +0,0 @@ -name = "Session test" -description = "Support module for session data testing." -package = Testing -version = VERSION -core = 8.x -hidden = TRUE diff --git a/modules/simpletest/tests/session_test.module b/modules/simpletest/tests/session_test.module deleted file mode 100644 index 689ff099ae0..00000000000 --- a/modules/simpletest/tests/session_test.module +++ /dev/null @@ -1,192 +0,0 @@ -<?php - -/** - * Implements hook_menu(). - */ -function session_test_menu() { - $items['session-test/get'] = array( - 'title' => 'Session value', - 'page callback' => '_session_test_get', - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - $items['session-test/id'] = array( - 'title' => 'Session ID', - 'page callback' => '_session_test_id', - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - $items['session-test/id-from-cookie'] = array( - 'title' => 'Session ID from cookie', - 'page callback' => '_session_test_id_from_cookie', - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - $items['session-test/set/%'] = array( - 'title' => 'Set session value', - 'page callback' => '_session_test_set', - 'page arguments' => array(2), - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - $items['session-test/no-set/%'] = array( - 'title' => 'Set session value but do not save session', - 'page callback' => '_session_test_no_set', - 'page arguments' => array(2), - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - $items['session-test/set-message'] = array( - 'title' => 'Set message', - 'page callback' => '_session_test_set_message', - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - $items['session-test/set-message-but-dont-save'] = array( - 'title' => 'Set message but do not save session', - 'page callback' => '_session_test_set_message_but_dont_save', - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - $items['session-test/set-not-started'] = array( - 'title' => 'Set message when session is not started', - 'page callback' => '_session_test_set_not_started', - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - $items['session-test/is-logged-in'] = array( - 'title' => 'Check if user is logged in', - 'page callback' => '_session_test_is_logged_in', - 'access callback' => 'user_is_logged_in', - 'type' => MENU_CALLBACK, - ); - - return $items; -} - -/** - * Implements hook_boot(). - */ -function session_test_boot() { - header('X-Session-Empty: ' . intval(empty($_SESSION))); -} - -/** - * Page callback, prints the stored session value to the screen. - */ -function _session_test_get() { - if (!empty($_SESSION['session_test_value'])) { - return t('The current value of the stored session variable is: %val', array('%val' => $_SESSION['session_test_value'])); - } - else { - return ""; - } -} - -/** - * Page callback, stores a value in $_SESSION['session_test_value']. - */ -function _session_test_set($value) { - $_SESSION['session_test_value'] = $value; - return t('The current value of the stored session variable has been set to %val', array('%val' => $value)); -} - -/** - * Menu callback: turns off session saving and then tries to save a value - * anyway. - */ -function _session_test_no_set($value) { - drupal_save_session(FALSE); - _session_test_set($value); - return t('session saving was disabled, and then %val was set', array('%val' => $value)); -} - -/** - * Menu callback: print the current session ID. - */ -function _session_test_id() { - // Set a value in $_SESSION, so that drupal_session_commit() will start - // a session. - $_SESSION['test'] = 'test'; - - drupal_session_commit(); - - return 'session_id:' . session_id() . "\n"; -} - -/** - * Menu callback: print the current session ID as read from the cookie. - */ -function _session_test_id_from_cookie() { - return 'session_id:' . $_COOKIE[session_name()] . "\n"; -} - -/** - * Menu callback, sets a message to me displayed on the following page. - */ -function _session_test_set_message() { - drupal_set_message(t('This is a dummy message.')); - print t('A message was set.'); - // Do not return anything, so the current request does not result in a themed - // page with messages. The message will be displayed in the following request - // instead. -} - -/** - * Menu callback, sets a message but call drupal_save_session(FALSE). - */ -function _session_test_set_message_but_dont_save() { - drupal_save_session(FALSE); - _session_test_set_message(); -} - -/** - * Menu callback, stores a value in $_SESSION['session_test_value'] without - * having started the session in advance. - */ -function _session_test_set_not_started() { - if (!drupal_session_will_start()) { - $_SESSION['session_test_value'] = t('Session was not started'); - } -} - -/** - * Implements hook_user(). - */ -function session_test_user_login($edit = array(), $user = NULL) { - if ($user->name == 'session_test_user') { - // Exit so we can verify that the session was regenerated - // before hook_user() was called. - exit; - } -} - -/** - * Implements hook_form_FORM_ID_alter(). - */ -function session_test_form_user_login_alter(&$form) { - $form['#https'] = TRUE; -} - -/** - * Implements hook_drupal_goto_alter(). - * - * Force the redirection to go to a non-secure page after being on a secure - * page through https.php. - */ -function session_test_drupal_goto_alter(&$path, &$options, &$http_response_code) { - global $base_insecure_url, $is_https_mock; - // Alter the redirect to use HTTP when using a mock HTTPS request through - // https.php because form submissions would otherwise redirect to a - // non-existent HTTPS site. - if (!empty($is_https_mock)) { - $path = $base_insecure_url . '/' . $path; - } -} - -/** - * Menu callback, only available if current user is logged in. - */ -function _session_test_is_logged_in() { - return t('User is logged in.'); -} diff --git a/modules/simpletest/tests/symfony.test b/modules/simpletest/tests/symfony.test deleted file mode 100644 index 8a7ecb967a6..00000000000 --- a/modules/simpletest/tests/symfony.test +++ /dev/null @@ -1,31 +0,0 @@ -<?php - -/** - * @file - * Tests for Symfony2-related functionality. - */ - -/** - * Tests related to Symfony class loading. - */ -class SymfonyClassLoaderTestCase extends DrupalUnitTestCase { - public static function getInfo() { - return array( - 'name' => 'Class loader', - 'description' => 'Confirm that the PSR-0 class loader is connected properly', - 'group' => 'Symfony', - ); - } - - function setUp() { - parent::setUp(); - } - - /** - * Test that we can lazy-load classes from the Symfony framework. - */ - function testClassesLoad() { - $class_name = 'Symfony\\Component\\HttpFoundation\\Request'; - $this->assertTrue(class_exists($class_name), t('Class !class_name exists', array('!class_name' => $class_name))); - } -} diff --git a/modules/simpletest/tests/system.base.css b/modules/simpletest/tests/system.base.css deleted file mode 100644 index c14ae9b27ba..00000000000 --- a/modules/simpletest/tests/system.base.css +++ /dev/null @@ -1,6 +0,0 @@ - -/** - * This file is for testing CSS file override in - * CascadingStylesheetsTestCase::testRenderOverride(). - * No contents are necessary. - */ diff --git a/modules/simpletest/tests/system_dependencies_test.info b/modules/simpletest/tests/system_dependencies_test.info deleted file mode 100644 index c90706d4d7f..00000000000 --- a/modules/simpletest/tests/system_dependencies_test.info +++ /dev/null @@ -1,7 +0,0 @@ -name = "System dependency test" -description = "Support module for testing system dependencies." -package = Testing -version = VERSION -core = 8.x -hidden = TRUE -dependencies[] = _missing_dependency diff --git a/modules/simpletest/tests/system_dependencies_test.module b/modules/simpletest/tests/system_dependencies_test.module deleted file mode 100644 index b3d9bbc7f37..00000000000 --- a/modules/simpletest/tests/system_dependencies_test.module +++ /dev/null @@ -1 +0,0 @@ -<?php diff --git a/modules/simpletest/tests/system_test.info b/modules/simpletest/tests/system_test.info deleted file mode 100644 index 3b9aebc929c..00000000000 --- a/modules/simpletest/tests/system_test.info +++ /dev/null @@ -1,7 +0,0 @@ -name = System test -description = Support module for system testing. -package = Testing -version = VERSION -core = 8.x -files[] = system_test.module -hidden = TRUE diff --git a/modules/simpletest/tests/system_test.module b/modules/simpletest/tests/system_test.module deleted file mode 100644 index 73727649487..00000000000 --- a/modules/simpletest/tests/system_test.module +++ /dev/null @@ -1,399 +0,0 @@ -<?php - -/** - * Implements hook_menu(). - */ -function system_test_menu() { - $items['system-test/sleep/%'] = array( - 'page callback' => 'system_test_sleep', - 'page arguments' => array(2), - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); - $items['system-test/auth'] = array( - 'page callback' => 'system_test_basic_auth_page', - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); - $items['system-test/authorize-init/%'] = array( - 'page callback' => 'system_test_authorize_init_page', - 'page arguments' => array(2), - 'access arguments' => array('administer software updates'), - 'type' => MENU_CALLBACK, - ); - $items['system-test/redirect/%'] = array( - 'title' => 'Redirect', - 'page callback' => 'system_test_redirect', - 'page arguments' => array(2), - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - $items['system-test/multiple-redirects/%'] = array( - 'title' => 'Redirect', - 'page callback' => 'system_test_multiple_redirects', - 'page arguments' => array(2), - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - $items['system-test/set-header'] = array( - 'page callback' => 'system_test_set_header', - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - $items['system-test/redirect-noscheme'] = array( - 'page callback' => 'system_test_redirect_noscheme', - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - $items['system-test/redirect-noparse'] = array( - 'page callback' => 'system_test_redirect_noparse', - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - $items['system-test/redirect-invalid-scheme'] = array( - 'page callback' => 'system_test_redirect_invalid_scheme', - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - - $items['system-test/variable-get'] = array( - 'title' => 'Variable Get', - 'page callback' => 'variable_get', - 'page arguments' => array('simpletest_bootstrap_variable_test', NULL), - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - - $items['system-test/lock-acquire'] = array( - 'title' => 'Lock acquire', - 'page callback' => 'system_test_lock_acquire', - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); - - $items['system-test/lock-exit'] = array( - 'title' => 'Lock acquire then exit', - 'page callback' => 'system_test_lock_exit', - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); - - $items['system-test/main-content-handling'] = array( - 'title' => 'Test main content handling', - 'page callback' => 'system_test_main_content_fallback', - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); - - $items['system-test/main-content-fallback'] = array( - 'title' => 'Test main content fallback', - 'page callback' => 'system_test_main_content_fallback', - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); - - $items['system-test/main-content-duplication'] = array( - 'title' => 'Test main content duplication', - 'page callback' => 'system_test_main_content_fallback', - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); - - $items['system-test/shutdown-functions'] = array( - 'title' => 'Test main content duplication', - 'page callback' => 'system_test_page_shutdown_functions', - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); - - return $items; -} - -function system_test_sleep($seconds) { - sleep($seconds); -} - -function system_test_basic_auth_page() { - $output = t('$_SERVER[\'PHP_AUTH_USER\'] is @username.', array('@username' => $_SERVER['PHP_AUTH_USER'])); - $output .= t('$_SERVER[\'PHP_AUTH_PW\'] is @password.', array('@password' => $_SERVER['PHP_AUTH_PW'])); - return $output; -} - -function system_test_redirect($code) { - $code = (int) $code; - if ($code != 200) { - // Header names are case-insensitive. - header("locaTION: " . url('system-test/redirect/200', array('absolute' => TRUE)), TRUE, $code); - exit; - } - return ''; -} - -/** - * Menu callback; sends a redirect header to itself until $count argument is 0. - * - * Emulates the variable number of redirects (given by initial $count argument) - * to the final destination URL by continuous sending of 301 HTTP redirect - * headers to itself together with decrementing the $count parameter until the - * $count parameter reaches 0. After that it returns an empty string to render - * the final destination page. - * - * @param $count - * The count of redirects left until the final destination page. - * - * @returns - * The location redirect if the $count > 0, otherwise an empty string. - */ -function system_test_multiple_redirects($count) { - $count = (int) $count; - if ($count > 0) { - header("location: " . url('system-test/multiple-redirects/' . --$count, array('absolute' => TRUE)), TRUE, 301); - exit; - } - return ''; -} - -function system_test_set_header() { - drupal_add_http_header($_GET['name'], $_GET['value']); - return t('The following header was set: %name: %value', array('%name' => $_GET['name'], '%value' => $_GET['value'])); -} - -function system_test_redirect_noscheme() { - header("Location: localhost/path", TRUE, 301); - exit; -} - -function system_test_redirect_noparse() { - header("Location: http:///path", TRUE, 301); - exit; -} - -function system_test_redirect_invalid_scheme() { - header("Location: ftp://localhost/path", TRUE, 301); - exit; -} - -/** - * Implements hook_modules_installed(). - */ -function system_test_modules_installed($modules) { - if (variable_get('test_verbose_module_hooks')) { - foreach ($modules as $module) { - drupal_set_message(t('hook_modules_installed fired for @module', array('@module' => $module))); - } - } -} - -/** - * Implements hook_modules_enabled(). - */ -function system_test_modules_enabled($modules) { - if (variable_get('test_verbose_module_hooks')) { - foreach ($modules as $module) { - drupal_set_message(t('hook_modules_enabled fired for @module', array('@module' => $module))); - } - } -} - -/** - * Implements hook_modules_disabled(). - */ -function system_test_modules_disabled($modules) { - if (variable_get('test_verbose_module_hooks')) { - foreach ($modules as $module) { - drupal_set_message(t('hook_modules_disabled fired for @module', array('@module' => $module))); - } - } -} - -/** - * Implements hook_modules_uninstalled(). - */ -function system_test_modules_uninstalled($modules) { - if (variable_get('test_verbose_module_hooks')) { - foreach ($modules as $module) { - drupal_set_message(t('hook_modules_uninstalled fired for @module', array('@module' => $module))); - } - } -} - -/** - * Implements hook_boot(). - */ -function system_test_boot() { - watchdog('system_test', 'hook_boot'); -} - -/** - * Implements hook_init(). - */ -function system_test_init() { - // Used by FrontPageTestCase to get the results of drupal_is_front_page(). - if (variable_get('front_page_output', 0) && drupal_is_front_page()) { - drupal_set_message(t('On front page.')); - } -} - -/** - * Implements hook_exit(). - */ -function system_test_exit() { - watchdog('system_test', 'hook_exit'); -} - -/** - * Implements hook_system_info_alter(). - */ -function system_test_system_info_alter(&$info, $file, $type) { - // We need a static otherwise the last test will fail to alter common_test. - static $test; - if (($dependencies = variable_get('dependencies', array())) || $test) { - if ($file->name == 'module_test') { - $info['hidden'] = FALSE; - $info['dependencies'][] = array_shift($dependencies); - variable_set('dependencies', $dependencies); - $test = TRUE; - } - if ($file->name == 'common_test') { - $info['hidden'] = FALSE; - $info['version'] = '8.x-2.4-beta3'; - } - } - - // Make the system_dependencies_test visible by default. - if ($file->name == 'system_dependencies_test') { - $info['hidden'] = FALSE; - } - if ($file->name == 'requirements1_test' || $file->name == 'requirements2_test') { - $info['hidden'] = FALSE; - } -} - -/** - * Try to acquire a named lock and report the outcome. - */ -function system_test_lock_acquire() { - if (lock_acquire('system_test_lock_acquire')) { - lock_release('system_test_lock_acquire'); - return 'TRUE: Lock successfully acquired in system_test_lock_acquire()'; - } - else { - return 'FALSE: Lock not acquired in system_test_lock_acquire()'; - } -} - -/** - * Try to acquire a specific lock, and then exit. - */ -function system_test_lock_exit() { - if (lock_acquire('system_test_lock_exit', 900)) { - echo 'TRUE: Lock successfully acquired in system_test_lock_exit()'; - // The shut-down function should release the lock. - exit(); - } - else { - return 'FALSE: Lock not acquired in system_test_lock_exit()'; - } -} - -/** - * Implements hook_page_build(). - */ -function system_test_page_build(&$page) { - $menu_item = menu_get_item(); - $main_content_display = &drupal_static('system_main_content_added', FALSE); - - if ($menu_item['path'] == 'system-test/main-content-handling') { - $page['footer'] = drupal_set_page_content(); - $page['footer']['main']['#markup'] = '<div id="system-test-content">' . $page['footer']['main']['#markup'] . '</div>'; - } - elseif ($menu_item['path'] == 'system-test/main-content-fallback') { - drupal_set_page_content(); - $main_content_display = FALSE; - } - elseif ($menu_item['path'] == 'system-test/main-content-duplication') { - drupal_set_page_content(); - } -} - -/** - * Menu callback to test main content fallback(). - */ -function system_test_main_content_fallback() { - return t('Content to test main content fallback'); -} - -/** - * A simple page callback which adds a register shutdown function. - */ -function system_test_page_shutdown_functions($arg1, $arg2) { - drupal_register_shutdown_function('_system_test_first_shutdown_function', $arg1, $arg2); -} - -/** - * Dummy shutdown function which registers another shutdown function. - */ -function _system_test_first_shutdown_function($arg1, $arg2) { - // Output something, page has already been printed and the session stored - // so we can't use drupal_set_message. - print t('First shutdown function, arg1 : @arg1, arg2: @arg2', array('@arg1' => $arg1, '@arg2' => $arg2)); - drupal_register_shutdown_function('_system_test_second_shutdown_function', $arg1, $arg2); -} - -/** - * Dummy shutdown function. - */ -function _system_test_second_shutdown_function($arg1, $arg2) { - // Output something, page has already been printed and the session stored - // so we can't use drupal_set_message. - print t('Second shutdown function, arg1 : @arg1, arg2: @arg2', array('@arg1' => $arg1, '@arg2' => $arg2)); - - // Throw an exception with an HTML tag. Since this is called in a shutdown - // function, it will not bubble up to the default exception handler but will - // be caught in _drupal_shutdown_function() and be displayed through - // _drupal_render_exception_safe(). - throw new Exception('Drupal is <blink>awesome</blink>.'); -} - -/** - * Implements hook_filetransfer_info(). - */ -function system_test_filetransfer_info() { - return array( - 'system_test' => array( - 'title' => t('System Test FileTransfer'), - 'file' => 'system_test.module', // Should be a .inc, but for test, ok. - 'class' => 'SystemTestFileTransfer', - 'weight' => -10, - ), - ); -} - -/** - * Mock FileTransfer object to test the settings form functionality. - */ -class SystemTestFileTransfer { - public static function factory() { - return new SystemTestFileTransfer; - } - - public function getSettingsForm() { - $form = array(); - $form['system_test_username'] = array( - '#type' => 'textfield', - '#title' => t('System Test Username'), - ); - return $form; - } -} - -/** - * Page callback to initialize authorize.php during testing. - * - * @see system_authorized_init(). - */ -function system_test_authorize_init_page($page_title) { - $authorize_url = $GLOBALS['base_url'] . '/authorize.php'; - system_authorized_init('system_test_authorize_run', drupal_get_path('module', 'system_test') . '/system_test.module', array(), $page_title); - drupal_goto($authorize_url); -} diff --git a/modules/simpletest/tests/tablesort.test b/modules/simpletest/tests/tablesort.test deleted file mode 100644 index 9c068f86145..00000000000 --- a/modules/simpletest/tests/tablesort.test +++ /dev/null @@ -1,166 +0,0 @@ -<?php - -/** - * @file - * Various tablesort tests. - */ - -/** - * Test unicode handling features implemented in unicode.inc. - */ -class TableSortTest extends DrupalUnitTestCase { - - /** - * Storage for initial value of $_GET. - * - * @var array - */ - protected $GET = array(); - - public static function getInfo() { - return array( - 'name' => 'Tablesort', - 'description' => 'Tests table sorting.', - 'group' => 'System', - ); - } - - function setUp() { - // Save the original $_GET to be restored later. - $this->GET = $_GET; - - parent::setUp(); - } - - function tearDown() { - // Revert $_GET. - $_GET = $this->GET; - - parent::tearDown(); - } - - /** - * Test tablesort_init(). - */ - function testTableSortInit() { - - // Test simple table headers. - - $headers = array('foo', 'bar', 'baz'); - // Reset $_GET to prevent parameters from Simpletest and Batch API ending - // up in $ts['query']. - $_GET = array('q' => 'jahwohl'); - $expected_ts = array( - 'name' => 'foo', - 'sql' => '', - 'sort' => 'asc', - 'query' => array(), - ); - $ts = tablesort_init($headers); - $this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => check_plain(var_export($ts, TRUE))))); - $this->assertEqual($ts, $expected_ts, t('Simple table headers sorted correctly.')); - - // Test with simple table headers plus $_GET parameters that should _not_ - // override the default. - - $_GET = array( - 'q' => 'jahwohl', - // This should not override the table order because only complex - // headers are overridable. - 'order' => 'bar', - ); - $ts = tablesort_init($headers); - $this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => check_plain(var_export($ts, TRUE))))); - $this->assertEqual($ts, $expected_ts, t('Simple table headers plus non-overriding $_GET parameters sorted correctly.')); - - // Test with simple table headers plus $_GET parameters that _should_ - // override the default. - - $_GET = array( - 'q' => 'jahwohl', - 'sort' => 'DESC', - // Add an unrelated parameter to ensure that tablesort will include - // it in the links that it creates. - 'alpha' => 'beta', - ); - $expected_ts['sort'] = 'desc'; - $expected_ts['query'] = array('alpha' => 'beta'); - $ts = tablesort_init($headers); - $this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => check_plain(var_export($ts, TRUE))))); - $this->assertEqual($ts, $expected_ts, t('Simple table headers plus $_GET parameters sorted correctly.')); - - // Test complex table headers. - - $headers = array( - 'foo', - array( - 'data' => '1', - 'field' => 'one', - 'sort' => 'asc', - 'colspan' => 1, - ), - array( - 'data' => '2', - 'field' => 'two', - 'sort' => 'desc', - ), - ); - // Reset $_GET from previous assertion. - $_GET = array( - 'q' => 'jahwohl', - 'order' => '2', - ); - $ts = tablesort_init($headers); - $expected_ts = array( - 'name' => '2', - 'sql' => 'two', - 'sort' => 'desc', - 'query' => array(), - ); - $this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => check_plain(var_export($ts, TRUE))))); - $this->assertEqual($ts, $expected_ts, t('Complex table headers sorted correctly.')); - - // Test complex table headers plus $_GET parameters that should _not_ - // override the default. - - $_GET = array( - 'q' => 'jahwohl', - // This should not override the table order because this header does not - // exist. - 'order' => 'bar', - ); - $ts = tablesort_init($headers); - $expected_ts = array( - 'name' => '1', - 'sql' => 'one', - 'sort' => 'asc', - 'query' => array(), - ); - $this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => check_plain(var_export($ts, TRUE))))); - $this->assertEqual($ts, $expected_ts, t('Complex table headers plus non-overriding $_GET parameters sorted correctly.')); - unset($_GET['sort'], $_GET['order'], $_GET['alpha']); - - // Test complex table headers plus $_GET parameters that _should_ - // override the default. - - $_GET = array( - 'q' => 'jahwohl', - 'order' => '1', - 'sort' => 'ASC', - // Add an unrelated parameter to ensure that tablesort will include - // it in the links that it creates. - 'alpha' => 'beta', - ); - $expected_ts = array( - 'name' => '1', - 'sql' => 'one', - 'sort' => 'asc', - 'query' => array('alpha' => 'beta'), - ); - $ts = tablesort_init($headers); - $this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => check_plain(var_export($ts, TRUE))))); - $this->assertEqual($ts, $expected_ts, t('Complex table headers plus $_GET parameters sorted correctly.')); - unset($_GET['sort'], $_GET['order'], $_GET['alpha']); - - } -} diff --git a/modules/simpletest/tests/taxonomy_test.info b/modules/simpletest/tests/taxonomy_test.info deleted file mode 100644 index b4489d6cecc..00000000000 --- a/modules/simpletest/tests/taxonomy_test.info +++ /dev/null @@ -1,7 +0,0 @@ -name = "Taxonomy test module" -description = "Tests functions and hooks not used in core". -package = Testing -version = VERSION -core = 8.x -hidden = TRUE -dependencies[] = taxonomy diff --git a/modules/simpletest/tests/taxonomy_test.install b/modules/simpletest/tests/taxonomy_test.install deleted file mode 100644 index d5c94da5f7b..00000000000 --- a/modules/simpletest/tests/taxonomy_test.install +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -/** - * @file - * Install, update and uninstall functions for the taxonomy_test module. - */ - -/** - * Implements hook_schema(). - */ -function taxonomy_test_schema() { - $schema['taxonomy_term_antonym'] = array( - 'description' => 'Stores term antonym.', - 'fields' => array( - 'tid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'description' => 'The {taxonomy_term_data}.tid of the term.', - ), - 'name' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The name of the antonym.', - ), - ), - 'primary key' => array('tid'), - ); - - return $schema; -} diff --git a/modules/simpletest/tests/taxonomy_test.module b/modules/simpletest/tests/taxonomy_test.module deleted file mode 100644 index aae13a2d448..00000000000 --- a/modules/simpletest/tests/taxonomy_test.module +++ /dev/null @@ -1,81 +0,0 @@ -<?php - -/** - * @file - * Test module for Taxonomy hooks and functions not used in core. - */ - -/** - * Implements hook_taxonomy_term_load(). - */ -function taxonomy_test_taxonomy_term_load($terms) { - foreach ($terms as $term) { - $antonym = taxonomy_test_get_antonym($term->tid); - if ($antonym) { - $term->antonym = $antonym; - } - } -} - -/** - * Implements hook_taxonomy_term_insert(). - */ -function taxonomy_test_taxonomy_term_insert($term) { - if (!empty($term->antonym)) { - db_insert('taxonomy_term_antonym') - ->fields(array( - 'tid' => $term->tid, - 'name' => trim($term->antonym) - )) - ->execute(); - } -} - -/** - * Implements hook_taxonomy_term_update(). - */ -function taxonomy_test_taxonomy_term_update($term) { - if (!empty($term->antonym)) { - db_merge('taxonomy_term_antonym') - ->key(array('tid' => $term->tid)) - ->fields(array( - 'name' => trim($term->antonym) - )) - ->execute(); - } -} - -/** - * Implements hook_taxonomy_term_delete(). - */ -function taxonomy_test_taxonomy_term_delete($term) { - db_delete('taxonomy_term_antonym') - ->condition('tid', $term->tid) - ->execute(); -} - -/** - * Implements hook_form_alter(). - */ -function taxonomy_test_form_alter(&$form, $form_state, $form_id) { - if ($form_id == 'taxonomy_form_term') { - $antonym = taxonomy_test_get_antonym($form['#term']['tid']); - $form['advanced']['antonym'] = array( - '#type' => 'textfield', - '#title' => t('Antonym'), - '#default_value' => !empty($antonym) ? $antonym : '', - '#description' => t('Antonym of this term.') - ); - } -} - -/** - * Return the antonym of the given term ID. - */ -function taxonomy_test_get_antonym($tid) { - return db_select('taxonomy_term_antonym', 'ta') - ->fields('ta', array('name')) - ->condition('tid', $tid) - ->execute() - ->fetchField(); -} diff --git a/modules/simpletest/tests/theme.test b/modules/simpletest/tests/theme.test deleted file mode 100644 index fd881243f66..00000000000 --- a/modules/simpletest/tests/theme.test +++ /dev/null @@ -1,490 +0,0 @@ -<?php - -/** - * @file - * Tests for the theme API. - */ - -/** - * Unit tests for the Theme API. - */ -class ThemeUnitTest extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Theme API', - 'description' => 'Test low-level theme functions.', - 'group' => 'Theme', - ); - } - - function setUp() { - parent::setUp('theme_test'); - theme_enable(array('test_theme')); - } - - /** - * Test function theme_get_suggestions() for SA-CORE-2009-003. - */ - function testThemeSuggestions() { - // Set the front page as something random otherwise the CLI - // test runner fails. - variable_set('site_frontpage', 'nobody-home'); - $args = array('node', '1', 'edit'); - $suggestions = theme_get_suggestions($args, 'page'); - $this->assertEqual($suggestions, array('page__node', 'page__node__%', 'page__node__1', 'page__node__edit'), t('Found expected node edit page suggestions')); - // Check attack vectors. - $args = array('node', '\\1'); - $suggestions = theme_get_suggestions($args, 'page'); - $this->assertEqual($suggestions, array('page__node', 'page__node__%', 'page__node__1'), t('Removed invalid \\ from suggestions')); - $args = array('node', '1/'); - $suggestions = theme_get_suggestions($args, 'page'); - $this->assertEqual($suggestions, array('page__node', 'page__node__%', 'page__node__1'), t('Removed invalid / from suggestions')); - $args = array('node', "1\0"); - $suggestions = theme_get_suggestions($args, 'page'); - $this->assertEqual($suggestions, array('page__node', 'page__node__%', 'page__node__1'), t('Removed invalid \\0 from suggestions')); - // Define path with hyphens to be used to generate suggestions. - $args = array('node', '1', 'hyphen-path'); - $result = array('page__node', 'page__node__%', 'page__node__1', 'page__node__hyphen_path'); - $suggestions = theme_get_suggestions($args, 'page'); - $this->assertEqual($suggestions, $result, t('Found expected page suggestions for paths containing hyphens.')); - } - - /** - * Preprocess functions for the base hook should run even for suggestion implementations. - */ - function testPreprocessForSuggestions() { - $this->drupalGet('theme-test/suggestion'); - $this->assertText('test_theme_breadcrumb__suggestion: 1', t('Theme hook suggestion ran with data available from a preprocess function for the base hook.')); - } - - /** - * Ensure page-front template suggestion is added when on front page. - */ - function testFrontPageThemeSuggestion() { - $q = $_GET['q']; - // Set $_GET['q'] to node because theme_get_suggestions() will query it to - // see if we are on the front page. - $_GET['q'] = variable_get('site_frontpage', 'node'); - $suggestions = theme_get_suggestions(explode('/', $_GET['q']), 'page'); - // Set it back to not annoy the batch runner. - $_GET['q'] = $q; - $this->assertTrue(in_array('page__front', $suggestions), t('Front page template was suggested.')); - } - - /** - * Ensures theme hook_*_alter() implementations can run before anything is rendered. - */ - function testAlter() { - $this->drupalGet('theme-test/alter'); - $this->assertText('The altered data is test_theme_theme_test_alter_alter was invoked.', t('The theme was able to implement an alter hook during page building before anything was rendered.')); - } - - /** - * Ensures a theme's .info file is able to override a module CSS file from being added to the page. - * - * @see test_theme.info - */ - function testCSSOverride() { - // Reuse the same page as in testPreprocessForSuggestions(). We're testing - // what is output to the HTML HEAD based on what is in a theme's .info file, - // so it doesn't matter what page we get, as long as it is themed with the - // test theme. First we test with CSS aggregation disabled. - variable_set('preprocess_css', 0); - $this->drupalGet('theme-test/suggestion'); - $this->assertNoText('system.base.css', t('The theme\'s .info file is able to override a module CSS file from being added to the page.')); - - // Also test with aggregation enabled, simply ensuring no PHP errors are - // triggered during drupal_build_css_cache() when a source file doesn't - // exist. Then allow remaining tests to continue with aggregation disabled - // by default. - variable_set('preprocess_css', 1); - $this->drupalGet('theme-test/suggestion'); - variable_set('preprocess_css', 0); - } - - /** - * Ensures a themes template is overrideable based on the 'template' filename. - */ - function testTemplateOverride() { - variable_set('theme_default', 'test_theme'); - $this->drupalGet('theme-test/template-test'); - $this->assertText('Success: Template overridden.', t('Template overridden by defined \'template\' filename.')); - } -} - -/** - * Unit tests for theme_table(). - */ -class ThemeTableUnitTest extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Theme Table', - 'description' => 'Tests built-in theme functions.', - 'group' => 'Theme', - ); - } - - /** - * Tableheader.js provides 'sticky' table headers, and is included by default. - */ - function testThemeTableStickyHeaders() { - $header = array('one', 'two', 'three'); - $rows = array(array(1,2,3), array(4,5,6), array(7,8,9)); - $this->content = theme('table', array('header' => $header, 'rows' => $rows)); - $js = drupal_add_js(); - $this->assertTrue(isset($js['misc/tableheader.js']), t('tableheader.js was included when $sticky = TRUE.')); - $this->assertRaw('sticky-enabled', t('Table has a class of sticky-enabled when $sticky = TRUE.')); - drupal_static_reset('drupal_add_js'); - } - - /** - * If $sticky is FALSE, no tableheader.js should be included. - */ - function testThemeTableNoStickyHeaders() { - $header = array('one', 'two', 'three'); - $rows = array(array(1,2,3), array(4,5,6), array(7,8,9)); - $attributes = array(); - $caption = NULL; - $colgroups = array(); - $this->content = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => $attributes, 'caption' => $caption, 'colgroups' => $colgroups, 'sticky' => FALSE)); - $js = drupal_add_js(); - $this->assertFalse(isset($js['misc/tableheader.js']), t('tableheader.js was not included because $sticky = FALSE.')); - $this->assertNoRaw('sticky-enabled', t('Table does not have a class of sticky-enabled because $sticky = FALSE.')); - drupal_static_reset('drupal_add_js'); - } - - /** - * Tests that the table header is printed correctly even if there are no rows, - * and that the empty text is displayed correctly. - */ - function testThemeTableWithEmptyMessage() { - $header = array( - t('Header 1'), - array( - 'data' => t('Header 2'), - 'colspan' => 2, - ), - ); - $this->content = theme('table', array('header' => $header, 'rows' => array(), 'empty' => t('No strings available.'))); - $this->assertRaw('<tr class="odd"><td colspan="3" class="empty message">No strings available.</td>', t('Correct colspan was set on empty message.')); - $this->assertRaw('<thead><tr><th>Header 1</th>', t('Table header was printed.')); - } - -} - -/** - * Tests for common theme functions. - */ -class ThemeFunctionsTestCase extends DrupalWebTestCase { - protected $profile = 'testing'; - - public static function getInfo() { - return array( - 'name' => 'Theme functions', - 'description' => 'Tests common theme functions.', - 'group' => 'Theme', - ); - } - - /** - * Tests theme_item_list(). - */ - function testItemList() { - // Verify that empty variables produce no output. - $variables = array(); - $expected = ''; - $this->assertThemeOutput('item_list', $variables, $expected, 'Empty %callback generates no output.'); - - $variables = array(); - $variables['title'] = 'Some title'; - $expected = ''; - $this->assertThemeOutput('item_list', $variables, $expected, 'Empty %callback with title generates no output.'); - - // Verify nested item lists. - $variables = array(); - $variables['title'] = 'Some title'; - $variables['attributes'] = array( - 'id' => 'parentlist', - ); - $variables['items'] = array( - 'a', - array( - 'data' => 'b', - 'children' => array( - 'c', - // Nested children may use additional attributes. - array( - 'data' => 'd', - 'class' => array('dee'), - ), - // Any string key is treated as child list attribute. - 'id' => 'childlist', - ), - // Any other keys are treated as item attributes. - 'id' => 'bee', - ), - array( - 'data' => 'e', - 'id' => 'E', - ), - ); - $inner = '<div class="item-list"><ul id="childlist">'; - $inner .= '<li class="odd first">c</li>'; - $inner .= '<li class="dee even last">d</li>'; - $inner .= '</ul></div>'; - - $expected = '<div class="item-list">'; - $expected .= '<h3>Some title</h3>'; - $expected .= '<ul id="parentlist">'; - $expected .= '<li class="odd first">a</li>'; - $expected .= '<li id="bee" class="even">b' . $inner . '</li>'; - $expected .= '<li id="E" class="odd last">e</li>'; - $expected .= '</ul></div>'; - - $this->assertThemeOutput('item_list', $variables, $expected); - } - - /** - * Asserts themed output. - * - * @param $callback - * The name of the theme function to invoke; e.g. 'links' for theme_links(). - * @param $variables - * An array of variables to pass to the theme function. - * @param $expected - * The expected themed output string. - * @param $message - * (optional) An assertion message. - */ - protected function assertThemeOutput($callback, array $variables = array(), $expected, $message = '') { - $output = theme($callback, $variables); - $this->verbose('Variables:' . '<pre>' . check_plain(var_export($variables, TRUE)) . '</pre>' - . '<hr />' . 'Result:' . '<pre>' . check_plain(var_export($output, TRUE)) . '</pre>' - . '<hr />' . 'Expected:' . '<pre>' . check_plain(var_export($expected, TRUE)) . '</pre>' - . '<hr />' . $output - ); - if (!$message) { - $message = '%callback rendered correctly.'; - } - $message = t($message, array('%callback' => 'theme_' . $callback . '()')); - $this->assertIdentical($output, $expected, $message); - } -} - -/** - * Unit tests for theme_links(). - */ -class ThemeLinksTest extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Links', - 'description' => 'Test the theme_links() function and rendering groups of links.', - 'group' => 'Theme', - ); - } - - /** - * Test the use of drupal_pre_render_links() on a nested array of links. - */ - function testDrupalPreRenderLinks() { - // Define the base array to be rendered, containing a variety of different - // kinds of links. - $base_array = array( - '#theme' => 'links', - '#pre_render' => array('drupal_pre_render_links'), - '#links' => array( - 'parent_link' => array( - 'title' => 'Parent link original', - 'href' => 'parent-link-original', - ), - ), - 'first_child' => array( - '#theme' => 'links', - '#links' => array( - // This should be rendered if 'first_child' is rendered separately, - // but ignored if the parent is being rendered (since it duplicates - // one of the parent's links). - 'parent_link' => array( - 'title' => 'Parent link copy', - 'href' => 'parent-link-copy', - ), - // This should always be rendered. - 'first_child_link' => array( - 'title' => 'First child link', - 'href' => 'first-child-link', - ), - ), - ), - // This should always be rendered as part of the parent. - 'second_child' => array( - '#theme' => 'links', - '#links' => array( - 'second_child_link' => array( - 'title' => 'Second child link', - 'href' => 'second-child-link', - ), - ), - ), - // This should never be rendered, since the user does not have access to - // it. - 'third_child' => array( - '#theme' => 'links', - '#links' => array( - 'third_child_link' => array( - 'title' => 'Third child link', - 'href' => 'third-child-link', - ), - ), - '#access' => FALSE, - ), - ); - - // Start with a fresh copy of the base array, and try rendering the entire - // thing. We expect a single <ul> with appropriate links contained within - // it. - $render_array = $base_array; - $html = drupal_render($render_array); - $dom = new DOMDocument(); - $dom->loadHTML($html); - $this->assertEqual($dom->getElementsByTagName('ul')->length, 1, t('One "ul" tag found in the rendered HTML.')); - $list_elements = $dom->getElementsByTagName('li'); - $this->assertEqual($list_elements->length, 3, t('Three "li" tags found in the rendered HTML.')); - $this->assertEqual($list_elements->item(0)->nodeValue, 'Parent link original', t('First expected link found.')); - $this->assertEqual($list_elements->item(1)->nodeValue, 'First child link', t('Second expected link found.')); - $this->assertEqual($list_elements->item(2)->nodeValue, 'Second child link', t('Third expected link found.')); - $this->assertIdentical(strpos($html, 'Parent link copy'), FALSE, t('"Parent link copy" link not found.')); - $this->assertIdentical(strpos($html, 'Third child link'), FALSE, t('"Third child link" link not found.')); - - // Now render 'first_child', followed by the rest of the links, and make - // sure we get two separate <ul>'s with the appropriate links contained - // within each. - $render_array = $base_array; - $child_html = drupal_render($render_array['first_child']); - $parent_html = drupal_render($render_array); - // First check the child HTML. - $dom = new DOMDocument(); - $dom->loadHTML($child_html); - $this->assertEqual($dom->getElementsByTagName('ul')->length, 1, t('One "ul" tag found in the rendered child HTML.')); - $list_elements = $dom->getElementsByTagName('li'); - $this->assertEqual($list_elements->length, 2, t('Two "li" tags found in the rendered child HTML.')); - $this->assertEqual($list_elements->item(0)->nodeValue, 'Parent link copy', t('First expected link found.')); - $this->assertEqual($list_elements->item(1)->nodeValue, 'First child link', t('Second expected link found.')); - // Then check the parent HTML. - $dom = new DOMDocument(); - $dom->loadHTML($parent_html); - $this->assertEqual($dom->getElementsByTagName('ul')->length, 1, t('One "ul" tag found in the rendered parent HTML.')); - $list_elements = $dom->getElementsByTagName('li'); - $this->assertEqual($list_elements->length, 2, t('Two "li" tags found in the rendered parent HTML.')); - $this->assertEqual($list_elements->item(0)->nodeValue, 'Parent link original', t('First expected link found.')); - $this->assertEqual($list_elements->item(1)->nodeValue, 'Second child link', t('Second expected link found.')); - $this->assertIdentical(strpos($parent_html, 'First child link'), FALSE, t('"First child link" link not found.')); - $this->assertIdentical(strpos($parent_html, 'Third child link'), FALSE, t('"Third child link" link not found.')); - } -} - -/** - * Functional test for initialization of the theme system in hook_init(). - */ -class ThemeHookInitUnitTest extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Theme initialization in hook_init()', - 'description' => 'Tests that the theme system can be correctly initialized in hook_init().', - 'group' => 'Theme', - ); - } - - function setUp() { - parent::setUp('theme_test'); - } - - /** - * Test that the theme system can generate output when called by hook_init(). - */ - function testThemeInitializationHookInit() { - $this->drupalGet('theme-test/hook-init'); - $this->assertRaw('Themed output generated in hook_init()', t('Themed output generated in hook_init() correctly appears on the page.')); - $this->assertRaw('bartik/css/style.css', t("The default theme's CSS appears on the page when the theme system is initialized in hook_init().")); - } -} - -/** - * Tests autocompletion not loading registry. - */ -class ThemeFastTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Theme fast initialization', - 'description' => 'Test that autocompletion does not load the registry.', - 'group' => 'Theme' - ); - } - - function setUp() { - parent::setUp('theme_test'); - $this->account = $this->drupalCreateUser(array('access user profiles')); - } - - /** - * Tests access to user autocompletion and verify the correct results. - */ - function testUserAutocomplete() { - $this->drupalLogin($this->account); - $this->drupalGet('user/autocomplete/' . $this->account->name); - $this->assertText('registry not initialized', t('The registry was not initialized')); - } -} - -/** - * Unit tests for theme_html_tag(). - */ -class ThemeHtmlTag extends DrupalUnitTestCase { - public static function getInfo() { - return array( - 'name' => 'Theme HTML Tag', - 'description' => 'Tests theme_html_tag() built-in theme functions.', - 'group' => 'Theme', - ); - } - - /** - * Test function theme_html_tag() - */ - function testThemeHtmlTag() { - // Test auto-closure meta tag generation - $tag['element'] = array('#tag' => 'meta', '#attributes' => array('name' => 'description', 'content' => 'Drupal test')); - $this->assertEqual('<meta name="description" content="Drupal test" />'."\n", theme_html_tag($tag), t('Test auto-closure meta tag generation.')); - - // Test title tag generation - $tag['element'] = array('#tag' => 'title', '#value' => 'title test'); - $this->assertEqual('<title>title test</title>'."\n", theme_html_tag($tag), t('Test title tag generation.')); - } -} - -/** - * Functional test for attributes of html.tpl.php. - */ -class ThemeHtmlTplPhpAttributesTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'html.tpl.php html and body attributes', - 'description' => 'Tests attributes inserted in the html and body elements of html.tpl.php.', - 'group' => 'Theme', - ); - } - - function setUp() { - parent::setUp('theme_test'); - } - - /** - * Tests that modules and themes can alter variables in html.tpl.php. - */ - function testThemeHtmlTplPhpAttributes() { - $this->drupalGet(''); - $attributes = $this->xpath('/html[@theme_test_html_attribute="theme test html attribute value"]'); - $this->assertTrue(count($attributes) == 1, t('Attribute set in the html element via hook_preprocess_html() found.')); - $attributes = $this->xpath('/html/body[@theme_test_body_attribute="theme test body attribute value"]'); - $this->assertTrue(count($attributes) == 1, t('Attribute set in the body element via hook_preprocess_html() found.')); - } -} diff --git a/modules/simpletest/tests/theme_test.info b/modules/simpletest/tests/theme_test.info deleted file mode 100644 index c4d06591908..00000000000 --- a/modules/simpletest/tests/theme_test.info +++ /dev/null @@ -1,6 +0,0 @@ -name = "Theme test" -description = "Support module for theme system testing." -package = Testing -version = VERSION -core = 8.x -hidden = TRUE diff --git a/modules/simpletest/tests/theme_test.module b/modules/simpletest/tests/theme_test.module deleted file mode 100644 index 597c090da0c..00000000000 --- a/modules/simpletest/tests/theme_test.module +++ /dev/null @@ -1,137 +0,0 @@ -<?php - -/** - * Implements hook_theme(). - */ -function theme_test_theme($existing, $type, $theme, $path) { - $items['theme_test_template_test'] = array( - 'template' => 'theme_test.template_test', - ); - - return $items; -} - -/** - * Implements hook_menu(). - */ -function theme_test_menu() { - $items['theme-test/suggestion'] = array( - 'title' => 'Suggestion', - 'page callback' => '_theme_test_suggestion', - 'access arguments' => array('access content'), - 'theme callback' => '_theme_custom_theme', - 'type' => MENU_CALLBACK, - ); - $items['theme-test/alter'] = array( - 'title' => 'Suggestion', - 'page callback' => '_theme_test_alter', - 'access arguments' => array('access content'), - 'theme callback' => '_theme_custom_theme', - 'type' => MENU_CALLBACK, - ); - $items['theme-test/hook-init'] = array( - 'page callback' => 'theme_test_hook_init_page_callback', - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); - $items['theme-test/template-test'] = array( - 'page callback' => 'theme_test_template_test_page_callback', - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); - return $items; -} - -/** - * Implements hook_init(). - */ -function theme_test_init() { - if (arg(0) == 'theme-test' && arg(1) == 'hook-init') { - // First, force the theme registry to be rebuilt on this page request. This - // allows us to test a full initialization of the theme system in the code - // below. - drupal_theme_rebuild(); - // Next, initialize the theme system by storing themed text in a global - // variable. We will use this later in theme_test_hook_init_page_callback() - // to test that even when the theme system is initialized this early, it is - // still capable of returning output and theming the page as a whole. - $GLOBALS['theme_test_output'] = theme('more_link', array('url' => 'user', 'title' => 'Themed output generated in hook_init()')); - } -} - -/** - * Implements hook_exit(). - */ -function theme_test_exit() { - if (arg(0) == 'user') { - // Register a fake registry loading callback. If it gets called by - // theme_get_registry(), the registry has not been initialized yet. - _theme_registry_callback('_theme_test_load_registry', array()); - print theme_get_registry() ? 'registry initialized' : 'registry not initialized'; - } -} - -/** - * Fake registry loading callback. - */ -function _theme_test_load_registry() { - return array(); -} - -/** - * Menu callback for testing themed output generated in hook_init(). - */ -function theme_test_hook_init_page_callback() { - return $GLOBALS['theme_test_output']; -} - -/** - * Menu callback for testing template overridding based on filename. - */ -function theme_test_template_test_page_callback() { - return theme('theme_test_template_test'); -} - -/** - * Custom theme callback. - */ -function _theme_custom_theme() { - return 'test_theme'; -} - -/** - * Page callback, calls drupal_alter(). - * - * This is for testing that the theme can have hook_*_alter() implementations - * that run during page callback execution, even before theme() is called for - * the first time. - */ -function _theme_test_alter() { - $data = 'foo'; - drupal_alter('theme_test_alter', $data); - return "The altered data is $data."; -} - -/** - * Page callback, calls a theme hook suggestion. - */ -function _theme_test_suggestion() { - return theme(array('breadcrumb__suggestion', 'breadcrumb'), array()); -} - -/** - * Implements hook_preprocess_breadcrumb(). - * - * Set a variable that can later be tested to see if this function ran. - */ -function theme_test_preprocess_breadcrumb(&$variables) { - $variables['theme_test_preprocess_breadcrumb'] = 1; -} - -/** - * Implements hook_preprocess_html(). - */ -function theme_test_preprocess_html(&$variables) { - $variables['html_attributes_array']['theme_test_html_attribute'] = 'theme test html attribute value'; - $variables['body_attributes_array']['theme_test_body_attribute'] = 'theme test body attribute value'; -} diff --git a/modules/simpletest/tests/theme_test.template_test.tpl.php b/modules/simpletest/tests/theme_test.template_test.tpl.php deleted file mode 100644 index cde8faadd3c..00000000000 --- a/modules/simpletest/tests/theme_test.template_test.tpl.php +++ /dev/null @@ -1,2 +0,0 @@ -<!-- Output for Theme API test --> -Fail: Template not overridden. diff --git a/modules/simpletest/tests/unicode.test b/modules/simpletest/tests/unicode.test deleted file mode 100644 index 47a4938fe85..00000000000 --- a/modules/simpletest/tests/unicode.test +++ /dev/null @@ -1,305 +0,0 @@ -<?php - -/** - * @file - * Various unicode handling tests. - */ - -/** - * Test unicode handling features implemented in unicode.inc. - */ -class UnicodeUnitTest extends DrupalWebTestCase { - - /** - * Whether to run the extended version of the tests (including non latin1 characters). - * - * @var boolean - */ - protected $extendedMode = FALSE; - - public static function getInfo() { - return array( - 'name' => 'Unicode handling', - 'description' => 'Tests Drupal Unicode handling.', - 'group' => 'System', - ); - } - - /** - * Test full unicode features implemented using the mbstring extension. - */ - function testMbStringUnicode() { - global $multibyte; - - // mbstring was not detected on this installation, there is no way to test - // multibyte features. Treat that as an exception. - if ($multibyte == UNICODE_SINGLEBYTE) { - $this->error(t('Unable to test Multibyte features: mbstring extension was not detected.')); - } - - $multibyte = UNICODE_MULTIBYTE; - - $this->extendedMode = TRUE; - $this->pass(t('Testing in mbstring mode')); - - $this->helperTestStrToLower(); - $this->helperTestStrToUpper(); - $this->helperTestUcFirst(); - $this->helperTestStrLen(); - $this->helperTestSubStr(); - $this->helperTestTruncate(); - } - - /** - * Test emulated unicode features. - */ - function testEmulatedUnicode() { - global $multibyte; - - $multibyte = UNICODE_SINGLEBYTE; - - $this->extendedMode = FALSE; - - $this->pass(t('Testing in emulated (best-effort) mode')); - - $this->helperTestStrToLower(); - $this->helperTestStrToUpper(); - $this->helperTestUcFirst(); - $this->helperTestStrLen(); - $this->helperTestSubStr(); - $this->helperTestTruncate(); - } - - function helperTestStrToLower() { - $testcase = array( - 'tHe QUIcK bRoWn' => 'the quick brown', - 'FrançAIS is ÜBER-åwesome' => 'français is über-åwesome', - ); - if ($this->extendedMode) { - $testcase['ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΣὨ'] = 'αβγδεζηθικλμνξοσὠ'; - } - - foreach ($testcase as $input => $output) { - $this->assertEqual(drupal_strtolower($input), $output, t('%input is lowercased as %output', array('%input' => $input, '%output' => $output))); - } - } - - function helperTestStrToUpper() { - $testcase = array( - 'tHe QUIcK bRoWn' => 'THE QUICK BROWN', - 'FrançAIS is ÜBER-åwesome' => 'FRANÇAIS IS ÜBER-ÅWESOME', - ); - if ($this->extendedMode) { - $testcase['αβγδεζηθικλμνξοσὠ'] = 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΣὨ'; - } - - foreach ($testcase as $input => $output) { - $this->assertEqual(drupal_strtoupper($input), $output, t('%input is uppercased as %output', array('%input' => $input, '%output' => $output))); - } - } - - function helperTestUcFirst() { - $testcase = array( - 'tHe QUIcK bRoWn' => 'THe QUIcK bRoWn', - 'françAIS' => 'FrançAIS', - 'über' => 'Über', - 'åwesome' => 'Åwesome' - ); - if ($this->extendedMode) { - $testcase['σion'] = 'Σion'; - } - - foreach ($testcase as $input => $output) { - $this->assertEqual(drupal_ucfirst($input), $output, t('%input is ucfirst-ed as %output', array('%input' => $input, '%output' => $output))); - } - } - - function helperTestStrLen() { - $testcase = array( - 'tHe QUIcK bRoWn' => 15, - 'ÜBER-åwesome' => 12, - ); - - foreach ($testcase as $input => $output) { - $this->assertEqual(drupal_strlen($input), $output, t('%input length is %output', array('%input' => $input, '%output' => $output))); - } - } - - function helperTestSubStr() { - $testcase = array( - // 012345678901234567890123 - array('frànçAIS is über-åwesome', 0, 0, - ''), - array('frànçAIS is über-åwesome', 0, 1, - 'f'), - array('frànçAIS is über-åwesome', 0, 8, - 'frànçAIS'), - array('frànçAIS is über-åwesome', 0, 23, - 'frànçAIS is über-åwesom'), - array('frànçAIS is über-åwesome', 0, 24, - 'frànçAIS is über-åwesome'), - array('frànçAIS is über-åwesome', 0, 25, - 'frànçAIS is über-åwesome'), - array('frànçAIS is über-åwesome', 0, 100, - 'frànçAIS is über-åwesome'), - array('frànçAIS is über-åwesome', 4, 4, - 'çAIS'), - array('frànçAIS is über-åwesome', 1, 0, - ''), - array('frànçAIS is über-åwesome', 100, 0, - ''), - array('frànçAIS is über-åwesome', -4, 2, - 'so'), - array('frànçAIS is über-åwesome', -4, 3, - 'som'), - array('frànçAIS is über-åwesome', -4, 4, - 'some'), - array('frànçAIS is über-åwesome', -4, 5, - 'some'), - array('frànçAIS is über-åwesome', -7, 10, - 'åwesome'), - array('frànçAIS is über-åwesome', 5, -10, - 'AIS is üb'), - array('frànçAIS is über-åwesome', 0, -10, - 'frànçAIS is üb'), - array('frànçAIS is über-åwesome', 0, -1, - 'frànçAIS is über-åwesom'), - array('frànçAIS is über-åwesome', -7, -2, - 'åweso'), - array('frànçAIS is über-åwesome', -7, -6, - 'å'), - array('frànçAIS is über-åwesome', -7, -7, - ''), - array('frànçAIS is über-åwesome', -7, -8, - ''), - array('...', 0, 2, '..'), - array('以呂波耳・ほへとち。リヌルヲ。', 1, 3, - '呂波耳'), - - ); - - foreach ($testcase as $test) { - list($input, $start, $length, $output) = $test; - $result = drupal_substr($input, $start, $length); - $this->assertEqual($result, $output, t('%input substring at offset %offset for %length characters is %output (got %result)', array('%input' => $input, '%offset' => $start, '%length' => $length, '%output' => $output, '%result' => $result))); - } - } - - /** - * Test decode_entities(). - */ - function testDecodeEntities() { - $testcase = array( - 'Drupal' => 'Drupal', - '<script>' => '<script>', - '<script>' => '<script>', - '<script>' => '<script>', - '&lt;script&gt;' => '<script>', - '"' => '"', - '"' => '"', - '&#34;' => '"', - '"' => '"', - '&quot;' => '"', - "'" => "'", - ''' => "'", - '&#39;' => ''', - '©' => '©', - '©' => '©', - '©' => '©', - '→' => '→', - '→' => '→', - '➼' => '➼', - '➼' => '➼', - '€' => '€', - ); - foreach ($testcase as $input => $output) { - $this->assertEqual(decode_entities($input), $output, t('Make sure the decoded entity of @input is @output', array('@input' => $input, '@output' => $output))); - } - } - - /** - * Tests truncate_utf8(). - */ - function helperTestTruncate() { - // Each case is an array with input string, length to truncate to, and - // expected return value. - - // Test non-wordsafe, non-ellipsis cases. - $non_wordsafe_non_ellipsis_cases = array( - array('frànçAIS is über-åwesome', 24, 'frànçAIS is über-åwesome'), - array('frànçAIS is über-åwesome', 23, 'frànçAIS is über-åwesom'), - array('frànçAIS is über-åwesome', 17, 'frànçAIS is über-'), - array('以呂波耳・ほへとち。リヌルヲ。', 6, '以呂波耳・ほ'), - ); - $this->runTruncateTests($non_wordsafe_non_ellipsis_cases, FALSE, FALSE); - - // Test non-wordsafe, ellipsis cases. - $non_wordsafe_ellipsis_cases = array( - array('frànçAIS is über-åwesome', 24, 'frànçAIS is über-åwesome'), - array('frànçAIS is über-åwesome', 23, 'frànçAIS is über-åwe...'), - array('frànçAIS is über-åwesome', 17, 'frànçAIS is üb...'), - ); - $this->runTruncateTests($non_wordsafe_ellipsis_cases, FALSE, TRUE); - - // Test wordsafe, ellipsis cases. - $wordsafe_ellipsis_cases = array( - array('123', 1, '.'), - array('123', 2, '..'), - array('123', 3, '123'), - array('1234', 3, '...'), - array('1234567890', 10, '1234567890'), - array('12345678901', 10, '1234567...'), - array('12345678901', 11, '12345678901'), - array('123456789012', 11, '12345678...'), - array('12345 7890', 10, '12345 7890'), - array('12345 7890', 9, '12345...'), - array('123 567 90', 10, '123 567 90'), - array('123 567 901', 10, '123 567...'), - array('Stop. Hammertime.', 17, 'Stop. Hammertime.'), - array('Stop. Hammertime.', 16, 'Stop....'), - array('frànçAIS is über-åwesome', 24, 'frànçAIS is über-åwesome'), - array('frànçAIS is über-åwesome', 23, 'frànçAIS is über...'), - array('frànçAIS is über-åwesome', 17, 'frànçAIS is...'), - array('¿Dónde está el niño?', 20, '¿Dónde está el niño?'), - array('¿Dónde está el niño?', 19, '¿Dónde está el...'), - array('¿Dónde está el niño?', 15, '¿Dónde está...'), - array('¿Dónde está el niño?', 10, '¿Dónde...'), - array('Help! Help! Help!', 17, 'Help! Help! Help!'), - array('Help! Help! Help!', 16, 'Help! Help!...'), - array('Help! Help! Help!', 15, 'Help! Help!...'), - array('Help! Help! Help!', 14, 'Help! Help!...'), - array('Help! Help! Help!', 13, 'Help! Help...'), - array('Help! Help! Help!', 12, 'Help!...'), - array('Help! Help! Help!', 11, 'Help!...'), - array('Help! Help! Help!', 10, 'Help!...'), - array('Help! Help! Help!', 9, 'Help!...'), - array('Help! Help! Help!', 8, 'Help!...'), - array('Help! Help! Help!', 7, 'Help...'), - array('Help! Help! Help!', 6, 'Hel...'), - array('Help! Help! Help!', 5, 'He...'), - ); - $this->runTruncateTests($wordsafe_ellipsis_cases, TRUE, TRUE); - } - - /** - * Runs test cases for helperTestTruncate(). - * - * Runs each test case through truncate_utf8() and compares the output - * to the expected output. - * - * @param $cases - * Cases array. Each case is an array with the input string, length to - * truncate to, and expected output. - * @param $wordsafe - * TRUE to use word-safe truncation, FALSE to not use word-safe truncation. - * @param $ellipsis - * TRUE to append ... if the input is truncated, FALSE to not append .... - */ - function runTruncateTests($cases, $wordsafe, $ellipsis) { - foreach ($cases as $case) { - list($input, $max_length, $expected) = $case; - $output = truncate_utf8($input, $max_length, $wordsafe, $ellipsis); - $this->assertEqual($output, $expected, t('%input truncate to %length characters with %wordsafe, %ellipsis is %expected (got %output)', array('%input' => $input, '%length' => $max_length, '%output' => $output, '%expected' => $expected, '%wordsafe' => ($wordsafe ? 'word-safe' : 'not word-safe'), '%ellipsis' => ($ellipsis ? 'ellipsis' : 'not ellipsis')))); - } - } -} diff --git a/modules/simpletest/tests/update.test b/modules/simpletest/tests/update.test deleted file mode 100644 index 2f55dc5b652..00000000000 --- a/modules/simpletest/tests/update.test +++ /dev/null @@ -1,115 +0,0 @@ -<?php - -/** - * @file - * Tests for the update system. - */ - -/** - * Tests for the update dependency ordering system. - */ -class UpdateDependencyOrderingTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Update dependency ordering', - 'description' => 'Test that update functions are run in the proper order.', - 'group' => 'Update API', - ); - } - - function setUp() { - parent::setUp('update_test_1', 'update_test_2', 'update_test_3'); - require_once DRUPAL_ROOT . '/includes/update.inc'; - } - - /** - * Test that updates within a single module run in the correct order. - */ - function testUpdateOrderingSingleModule() { - $starting_updates = array( - 'update_test_1' => 8000, - ); - $expected_updates = array( - 'update_test_1_update_8000', - 'update_test_1_update_8001', - 'update_test_1_update_8002', - ); - $actual_updates = array_keys(update_resolve_dependencies($starting_updates)); - $this->assertEqual($expected_updates, $actual_updates, t('Updates within a single module run in the correct order.')); - } - - /** - * Test that dependencies between modules are resolved correctly. - */ - function testUpdateOrderingModuleInterdependency() { - $starting_updates = array( - 'update_test_2' => 8000, - 'update_test_3' => 8000, - ); - $update_order = array_keys(update_resolve_dependencies($starting_updates)); - // Make sure that each dependency is satisfied. - $first_dependency_satisfied = array_search('update_test_2_update_8000', $update_order) < array_search('update_test_3_update_8000', $update_order); - $this->assertTrue($first_dependency_satisfied, t('The dependency of the second module on the first module is respected by the update function order.')); - $second_dependency_satisfied = array_search('update_test_3_update_8000', $update_order) < array_search('update_test_2_update_8001', $update_order); - $this->assertTrue($second_dependency_satisfied, t('The dependency of the first module on the second module is respected by the update function order.')); - } -} - -/** - * Tests for missing update dependencies. - */ -class UpdateDependencyMissingTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Missing update dependencies', - 'description' => 'Test that missing update dependencies are correctly flagged.', - 'group' => 'Update API', - ); - } - - function setUp() { - // Only install update_test_2.module, even though its updates have a - // dependency on update_test_3.module. - parent::setUp('update_test_2'); - require_once DRUPAL_ROOT . '/includes/update.inc'; - } - - function testMissingUpdate() { - $starting_updates = array( - 'update_test_2' => 8000, - ); - $update_graph = update_resolve_dependencies($starting_updates); - $this->assertTrue($update_graph['update_test_2_update_8000']['allowed'], t("The module's first update function is allowed to run, since it does not have any missing dependencies.")); - $this->assertFalse($update_graph['update_test_2_update_8001']['allowed'], t("The module's second update function is not allowed to run, since it has a direct dependency on a missing update.")); - $this->assertFalse($update_graph['update_test_2_update_8002']['allowed'], t("The module's third update function is not allowed to run, since it has an indirect dependency on a missing update.")); - } -} - -/** - * Tests for the invocation of hook_update_dependencies(). - */ -class UpdateDependencyHookInvocationTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Update dependency hook invocation', - 'description' => 'Test that the hook invocation for determining update dependencies works correctly.', - 'group' => 'Update API', - ); - } - - function setUp() { - parent::setUp('update_test_1', 'update_test_2'); - require_once DRUPAL_ROOT . '/includes/update.inc'; - } - - /** - * Test the structure of the array returned by hook_update_dependencies(). - */ - function testHookUpdateDependencies() { - $update_dependencies = update_retrieve_dependencies(); - $this->assertTrue($update_dependencies['system'][8000]['update_test_1'] == 8000, t('An update function that has a dependency on two separate modules has the first dependency recorded correctly.')); - $this->assertTrue($update_dependencies['system'][8000]['update_test_2'] == 8001, t('An update function that has a dependency on two separate modules has the second dependency recorded correctly.')); - $this->assertTrue($update_dependencies['system'][8001]['update_test_1'] == 8002, t('An update function that depends on more than one update from the same module only has the dependency on the higher-numbered update function recorded.')); - } -} - diff --git a/modules/simpletest/tests/update_script_test.info b/modules/simpletest/tests/update_script_test.info deleted file mode 100644 index 04bf73ca8b4..00000000000 --- a/modules/simpletest/tests/update_script_test.info +++ /dev/null @@ -1,6 +0,0 @@ -name = "Update script test" -description = "Support module for update script testing." -package = Testing -version = VERSION -core = 8.x -hidden = TRUE diff --git a/modules/simpletest/tests/update_script_test.install b/modules/simpletest/tests/update_script_test.install deleted file mode 100644 index 8af516bc0a3..00000000000 --- a/modules/simpletest/tests/update_script_test.install +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @file - * Install, update and uninstall functions for the update_script_test module. - */ - -/** - * Implements hook_requirements(). - */ -function update_script_test_requirements($phase) { - $requirements = array(); - - if ($phase == 'update') { - // Set a requirements warning or error when the test requests it. - $requirement_type = variable_get('update_script_test_requirement_type'); - switch ($requirement_type) { - case REQUIREMENT_WARNING: - $requirements['update_script_test'] = array( - 'title' => 'Update script test', - 'value' => 'Warning', - 'description' => 'This is a requirements warning provided by the update_script_test module.', - 'severity' => REQUIREMENT_WARNING, - ); - break; - case REQUIREMENT_ERROR: - $requirements['update_script_test'] = array( - 'title' => 'Update script test', - 'value' => 'Error', - 'description' => 'This is a requirements error provided by the update_script_test module.', - 'severity' => REQUIREMENT_ERROR, - ); - break; - } - } - - return $requirements; -} - -/** - * Dummy update function to run during the tests. - */ -function update_script_test_update_8000() { - return t('The update_script_test_update_8000() update was executed successfully.'); -} diff --git a/modules/simpletest/tests/update_script_test.module b/modules/simpletest/tests/update_script_test.module deleted file mode 100644 index b3d9bbc7f37..00000000000 --- a/modules/simpletest/tests/update_script_test.module +++ /dev/null @@ -1 +0,0 @@ -<?php diff --git a/modules/simpletest/tests/update_test_1.info b/modules/simpletest/tests/update_test_1.info deleted file mode 100644 index 5a5b14fdd9a..00000000000 --- a/modules/simpletest/tests/update_test_1.info +++ /dev/null @@ -1,6 +0,0 @@ -name = "Update test" -description = "Support module for update testing." -package = Testing -version = VERSION -core = 8.x -hidden = TRUE diff --git a/modules/simpletest/tests/update_test_1.install b/modules/simpletest/tests/update_test_1.install deleted file mode 100644 index 22398a30f1d..00000000000 --- a/modules/simpletest/tests/update_test_1.install +++ /dev/null @@ -1,53 +0,0 @@ -<?php - -/** - * @file - * Install, update and uninstall functions for the update_test_1 module. - */ - -/** - * Implements hook_update_dependencies(). - * - * @see update_test_2_update_dependencies() - */ -function update_test_1_update_dependencies() { - // These dependencies are used in combination with those declared in - // update_test_2_update_dependencies() for the sole purpose of testing that - // the results of hook_update_dependencies() are collected correctly and have - // the correct array structure. Therefore, we use updates from System module - // (which have already run), so that they will not get in the way of other - // tests. - $dependencies['system'][8000] = array( - // Compare to update_test_2_update_dependencies(), where the same System - // module update function is forced to depend on an update function from a - // different module. This allows us to test that both dependencies are - // correctly recorded. - 'update_test_1' => 8000, - ); - $dependencies['system'][8001] = array( - // Compare to update_test_2_update_dependencies(), where the same System - // module update function is forced to depend on a different update - // function within the same module. This allows us to test that only the - // dependency on the higher-numbered update function is recorded. - 'update_test_1' => 8002, - ); - return $dependencies; -} - -/** - * Dummy update_test_1 update 8000. - */ -function update_test_1_update_8000() { -} - -/** - * Dummy update_test_1 update 8001. - */ -function update_test_1_update_8001() { -} - -/** - * Dummy update_test_1 update 8002. - */ -function update_test_1_update_8002() { -} diff --git a/modules/simpletest/tests/update_test_1.module b/modules/simpletest/tests/update_test_1.module deleted file mode 100644 index b3d9bbc7f37..00000000000 --- a/modules/simpletest/tests/update_test_1.module +++ /dev/null @@ -1 +0,0 @@ -<?php diff --git a/modules/simpletest/tests/update_test_2.info b/modules/simpletest/tests/update_test_2.info deleted file mode 100644 index 5a5b14fdd9a..00000000000 --- a/modules/simpletest/tests/update_test_2.info +++ /dev/null @@ -1,6 +0,0 @@ -name = "Update test" -description = "Support module for update testing." -package = Testing -version = VERSION -core = 8.x -hidden = TRUE diff --git a/modules/simpletest/tests/update_test_2.install b/modules/simpletest/tests/update_test_2.install deleted file mode 100644 index c73271a1fb0..00000000000 --- a/modules/simpletest/tests/update_test_2.install +++ /dev/null @@ -1,53 +0,0 @@ -<?php - -/** - * @file - * Install, update and uninstall functions for the update_test_2 module. - */ - -/** - * Implements hook_update_dependencies(). - * - * @see update_test_1_update_dependencies() - * @see update_test_3_update_dependencies() - */ -function update_test_2_update_dependencies() { - // Combined with update_test_3_update_dependencies(), we are declaring here - // that these two modules run updates in the following order: - // 1. update_test_2_update_8000() - // 2. update_test_3_update_8000() - // 3. update_test_2_update_8001() - // 4. update_test_2_update_8002() - $dependencies['update_test_2'][8001] = array( - 'update_test_3' => 8000, - ); - - // These are coordinated with the corresponding dependencies declared in - // update_test_1_update_dependencies(). - $dependencies['system'][8000] = array( - 'update_test_2' => 8001, - ); - $dependencies['system'][8001] = array( - 'update_test_1' => 8001, - ); - - return $dependencies; -} - -/** - * Dummy update_test_2 update 8000. - */ -function update_test_2_update_8000() { -} - -/** - * Dummy update_test_2 update 8001. - */ -function update_test_2_update_8001() { -} - -/** - * Dummy update_test_2 update 8002. - */ -function update_test_2_update_8002() { -} diff --git a/modules/simpletest/tests/update_test_2.module b/modules/simpletest/tests/update_test_2.module deleted file mode 100644 index b3d9bbc7f37..00000000000 --- a/modules/simpletest/tests/update_test_2.module +++ /dev/null @@ -1 +0,0 @@ -<?php diff --git a/modules/simpletest/tests/update_test_3.info b/modules/simpletest/tests/update_test_3.info deleted file mode 100644 index 5a5b14fdd9a..00000000000 --- a/modules/simpletest/tests/update_test_3.info +++ /dev/null @@ -1,6 +0,0 @@ -name = "Update test" -description = "Support module for update testing." -package = Testing -version = VERSION -core = 8.x -hidden = TRUE diff --git a/modules/simpletest/tests/update_test_3.install b/modules/simpletest/tests/update_test_3.install deleted file mode 100644 index 96830c816f9..00000000000 --- a/modules/simpletest/tests/update_test_3.install +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @file - * Install, update and uninstall functions for the update_test_3 module. - */ - -/** - * Implements hook_update_dependencies(). - * - * @see update_test_2_update_dependencies() - */ -function update_test_3_update_dependencies() { - $dependencies['update_test_3'][8000] = array( - 'update_test_2' => 8000, - ); - return $dependencies; -} - -/** - * Dummy update_test_3 update 8000. - */ -function update_test_3_update_8000() { -} diff --git a/modules/simpletest/tests/update_test_3.module b/modules/simpletest/tests/update_test_3.module deleted file mode 100644 index b3d9bbc7f37..00000000000 --- a/modules/simpletest/tests/update_test_3.module +++ /dev/null @@ -1 +0,0 @@ -<?php diff --git a/modules/simpletest/tests/upgrade/drupal-7.bare.database.php.gz b/modules/simpletest/tests/upgrade/drupal-7.bare.database.php.gz Binary files differdeleted file mode 100644 index 4eddff6ac19..00000000000 --- a/modules/simpletest/tests/upgrade/drupal-7.bare.database.php.gz +++ /dev/null diff --git a/modules/simpletest/tests/upgrade/drupal-7.filled.database.php.gz b/modules/simpletest/tests/upgrade/drupal-7.filled.database.php.gz Binary files differdeleted file mode 100644 index 9ff7c0bc418..00000000000 --- a/modules/simpletest/tests/upgrade/drupal-7.filled.database.php.gz +++ /dev/null diff --git a/modules/simpletest/tests/upgrade/upgrade.test b/modules/simpletest/tests/upgrade/upgrade.test deleted file mode 100644 index a802364fe03..00000000000 --- a/modules/simpletest/tests/upgrade/upgrade.test +++ /dev/null @@ -1,336 +0,0 @@ -<?php - -/** - * Perform end-to-end tests of the upgrade path. - */ -abstract class UpgradePathTestCase extends DrupalWebTestCase { - - /** - * The file path(s) to the dumped database(s) to load into the child site. - * - * @var array - */ - var $databaseDumpFiles = array(); - - /** - * Flag that indicates whether the child site has been upgraded. - */ - var $upgradedSite = FALSE; - - /** - * Array of errors triggered during the upgrade process. - */ - var $upgradeErrors = array(); - - /** - * Array of modules loaded when the test starts. - */ - var $loadedModules = array(); - - /** - * Override of DrupalWebTestCase::setUp() specialized for upgrade testing. - */ - protected function setUp() { - global $user, $language, $conf; - - // We are going to set a missing zlib requirement property for usage during - // the performUpgrade() and tearDown() calls. Also set that the tests failed. - if (!function_exists('gzopen')) { - $this->missing_zlib_requirement = TRUE; - parent::setUp(); - return; - } - - // Load the Update API. - require_once DRUPAL_ROOT . '/includes/update.inc'; - - // Reset flags. - $this->upgradedSite = FALSE; - $this->upgradeErrors = array(); - - $this->loadedModules = module_list(); - - // Generate a temporary prefixed database to ensure that tests have a clean starting point. - $this->databasePrefix = 'simpletest' . mt_rand(1000, 1000000); - db_update('simpletest_test_id') - ->fields(array('last_prefix' => $this->databasePrefix)) - ->condition('test_id', $this->testId) - ->execute(); - - // Clone the current connection and replace the current prefix. - $connection_info = Database::getConnectionInfo('default'); - Database::renameConnection('default', 'simpletest_original_default'); - foreach ($connection_info as $target => $value) { - $connection_info[$target]['prefix'] = array( - 'default' => $value['prefix']['default'] . $this->databasePrefix, - ); - } - Database::addConnectionInfo('default', 'default', $connection_info['default']); - - // Store necessary current values before switching to prefixed database. - $this->originalLanguage = $language; - $this->originalLanguageDefault = variable_get('language_default'); - $this->originalFileDirectory = variable_get('file_public_path', conf_path() . '/files'); - $this->originalProfile = drupal_get_profile(); - $clean_url_original = variable_get('clean_url', 0); - - // Unregister the registry. - // This is required to make sure that the database layer works properly. - spl_autoload_unregister('drupal_autoload_class'); - spl_autoload_unregister('drupal_autoload_interface'); - - // Create test directories ahead of installation so fatal errors and debug - // information can be logged during installation process. - // Use mock files directories with the same prefix as the database. - $public_files_directory = $this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10); - $private_files_directory = $public_files_directory . '/private'; - $temp_files_directory = $private_files_directory . '/temp'; - - // Create the directories. - file_prepare_directory($public_files_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); - file_prepare_directory($private_files_directory, FILE_CREATE_DIRECTORY); - file_prepare_directory($temp_files_directory, FILE_CREATE_DIRECTORY); - $this->generatedTestFiles = FALSE; - - // Log fatal errors. - ini_set('log_errors', 1); - ini_set('error_log', $public_files_directory . '/error.log'); - - // Reset all statics and variables to perform tests in a clean environment. - $conf = array(); - - // Load the database from the portable PHP dump. - // The files can be gzipped. - foreach ($this->databaseDumpFiles as $file) { - if (substr($file, -3) == '.gz') { - $file = "compress.zlib://$file"; - } - require $file; - } - - // Set path variables. - $this->variable_set('file_public_path', $public_files_directory); - $this->variable_set('file_private_path', $private_files_directory); - $this->variable_set('file_temporary_path', $temp_files_directory); - - $this->pass('Finished loading the dump.'); - - // Load user 1. - $this->originalUser = $user; - drupal_save_session(FALSE); - $user = db_query('SELECT * FROM {users} WHERE uid = :uid', array(':uid' => 1))->fetchObject(); - - // Generate and set a D6-compatible session cookie. - $this->curlInitialize(); - $sid = drupal_hash_base64(uniqid(mt_rand(), TRUE) . drupal_random_bytes(55)); - curl_setopt($this->curlHandle, CURLOPT_COOKIE, rawurlencode(session_name()) . '=' . rawurlencode($sid)); - - // Force our way into the session of the child site. - drupal_save_session(TRUE); - _drupal_session_write($sid, ''); - // Remove the temporarily added ssid column. - drupal_save_session(FALSE); - - // Restore necessary variables. - $this->variable_set('clean_url', $clean_url_original); - $this->variable_set('site_mail', 'simpletest@example.com'); - - drupal_set_time_limit($this->timeLimit); - } - - /** - * Override of DrupalWebTestCase::tearDown() specialized for upgrade testing. - */ - protected function tearDown() { - global $user, $language; - - if (!empty($this->missing_zlib_requirement)) { - parent::tearDown(); - return; - } - - // In case a fatal error occurred that was not in the test process read the - // log to pick up any fatal errors. - simpletest_log_read($this->testId, $this->databasePrefix, get_class($this), TRUE); - - // Delete temporary files directory. - file_unmanaged_delete_recursive($this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10)); - - // Get back to the original connection. - Database::removeConnection('default'); - Database::renameConnection('simpletest_original_default', 'default'); - - // Remove all prefixed tables. - $tables = db_find_tables($this->databasePrefix . '%'); - foreach ($tables as $table) { - db_drop_table($table); - } - - // Return the user to the original one. - $user = $this->originalUser; - drupal_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_reset(); - - // Reset the Field API. - field_cache_clear(); - - // Rebuild caches. - parent::refreshVariables(); - - // Reset language. - $language = $this->originalLanguage; - if ($this->originalLanguageDefault) { - $GLOBALS['conf']['language_default'] = $this->originalLanguageDefault; - } - - // Close the CURL handler. - $this->curlClose(); - } - - /** - * Specialized variable_set() that works even if the child site is not upgraded. - * - * @param $name - * The name of the variable to set. - * @param $value - * The value to set. This can be any PHP data type; these functions take care - * of serialization as necessary. - */ - protected function variable_set($name, $value) { - db_delete('variable') - ->condition('name', $name) - ->execute(); - db_insert('variable') - ->fields(array( - 'name' => $name, - 'value' => serialize($value), - )) - ->execute(); - - try { - cache()->delete('variables'); - cache('bootstrap')->delete('variables'); - } - // Since cache_bootstrap won't exist in a Drupal 6 site, ignore the - // exception if the above fails. - catch (Exception $e) {} - } - - /** - * Specialized refreshVariables(). - */ - protected function refreshVariables() { - // No operation if the child has not been upgraded yet. - if (!$this->upgradedSite) { - return parent::refreshVariables(); - } - } - - /** - * Perform the upgrade. - * - * @param $register_errors - * Register the errors during the upgrade process as failures. - * @return - * TRUE if the upgrade succeeded, FALSE otherwise. - */ - protected function performUpgrade($register_errors = TRUE) { - $update_url = $GLOBALS['base_url'] . '/update.php'; - - if (!empty($this->missing_zlib_requirement)) { - $this->fail(t('Missing zlib requirement for upgrade tests.')); - return FALSE; - } - - // Load the first update screen. - $this->drupalGet($update_url, array('external' => TRUE)); - if (!$this->assertResponse(200)) { - return FALSE; - } - - // Continue. - $this->drupalPost(NULL, array(), t('Continue')); - if (!$this->assertResponse(200)) { - return FALSE; - } - - // Go! - $this->drupalPost(NULL, array(), t('Apply pending updates')); - if (!$this->assertResponse(200)) { - return FALSE; - } - - // Check for errors during the update process. - foreach ($this->xpath('//li[@class=:class]', array(':class' => 'failure')) as $element) { - $message = strip_tags($element->asXML()); - $this->upgradeErrors[] = $message; - if ($register_errors) { - $this->fail($message); - } - } - - if (!empty($this->upgradeErrors)) { - // Upgrade failed, the installation might be in an inconsistent state, - // don't process. - return FALSE; - } - - // Check if there still are pending updates. - $this->drupalGet($update_url, array('external' => TRUE)); - $this->drupalPost(NULL, array(), t('Continue')); - if (!$this->assertText(t('No pending updates.'), t('No pending updates at the end of the update process.'))) { - return FALSE; - } - - // Upgrade succeed, rebuild the environment so that we can call the API - // of the child site directly from this request. - $this->upgradedSite = TRUE; - - // Reload module list. For modules that are enabled in the test database, - // but not on the test client, we need to load the code here. - $new_modules = array_diff(module_list(TRUE), $this->loadedModules); - foreach ($new_modules as $module) { - drupal_load('module', $module); - } - - // Reload hook implementations - module_implements_reset(); - - // Rebuild caches. - drupal_static_reset(); - drupal_flush_all_caches(); - - // Reload global $conf array and permissions. - $this->refreshVariables(); - $this->checkPermissions(array(), TRUE); - - return TRUE; - } - - /** - * Force uninstall all modules from a test database, except those listed. - * - * @param $modules - * The list of modules to keep installed. Required core modules will - * always be kept. - */ - protected function uninstallModulesExcept(array $modules) { - $required_modules = array('block', 'dblog', 'filter', 'node', 'system', 'update', 'user'); - - $modules = array_merge($required_modules, $modules); - - db_delete('system') - ->condition('type', 'module') - ->condition('name', $modules, 'NOT IN') - ->execute(); - } - -} diff --git a/modules/simpletest/tests/upgrade/upgrade_bare.test b/modules/simpletest/tests/upgrade/upgrade_bare.test deleted file mode 100644 index db71228575c..00000000000 --- a/modules/simpletest/tests/upgrade/upgrade_bare.test +++ /dev/null @@ -1,31 +0,0 @@ -<?php - -/** - * Upgrade test for the bare database.. - * - * Load an empty installation of Drupal 7 and run the upgrade process on it. - */ -class BareUpgradePathTestCase extends UpgradePathTestCase { - public static function getInfo() { - return array( - 'name' => 'Bare upgrade test', - 'description' => 'Bare upgrade test.', - 'group' => 'Upgrade path', - ); - } - - public function setUp() { - // Path to the database dump. - $this->databaseDumpFiles = array( - drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-7.bare.database.php.gz', - ); - parent::setUp(); - } - - /** - * Test a successful upgrade. - */ - public function testBareUpgrade() { - $this->assertTrue($this->performUpgrade(), t('The upgrade was completed successfully.')); - } -} diff --git a/modules/simpletest/tests/upgrade/upgrade_filled.test b/modules/simpletest/tests/upgrade/upgrade_filled.test deleted file mode 100644 index 9b17bda4caa..00000000000 --- a/modules/simpletest/tests/upgrade/upgrade_filled.test +++ /dev/null @@ -1,31 +0,0 @@ -<?php - -/** - * Upgrade test for the filled database.. - * - * Load a filled installation of Drupal 7 and run the upgrade process on it. - */ -class FilledUpgradePathTestCase extends UpgradePathTestCase { - public static function getInfo() { - return array( - 'name' => 'Filled upgrade test', - 'description' => 'Filled upgrade test.', - 'group' => 'Upgrade path', - ); - } - - public function setUp() { - // Path to the database dump. - $this->databaseDumpFiles = array( - drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-7.filled.database.php.gz', - ); - parent::setUp(); - } - - /** - * Test a successful upgrade. - */ - public function testFilledUpgrade() { - $this->assertTrue($this->performUpgrade(), t('The upgrade was completed successfully.')); - } -} diff --git a/modules/simpletest/tests/url_alter_test.info b/modules/simpletest/tests/url_alter_test.info deleted file mode 100644 index 1947b2e7fb6..00000000000 --- a/modules/simpletest/tests/url_alter_test.info +++ /dev/null @@ -1,6 +0,0 @@ -name = Url_alter tests -description = A support modules for url_alter hook testing. -core = 8.x -package = Testing -version = VERSION -hidden = TRUE diff --git a/modules/simpletest/tests/url_alter_test.install b/modules/simpletest/tests/url_alter_test.install deleted file mode 100644 index 6e09ab5838a..00000000000 --- a/modules/simpletest/tests/url_alter_test.install +++ /dev/null @@ -1,12 +0,0 @@ -<?php - -/** - * Impelement hook_install(). - */ -function url_alter_test_install() { - // Set the weight of this module to one higher than forum.module. - db_update('system') - ->fields(array('weight' => 2)) - ->condition('name', 'url_alter_test') - ->execute(); -} diff --git a/modules/simpletest/tests/url_alter_test.module b/modules/simpletest/tests/url_alter_test.module deleted file mode 100644 index e229ab98652..00000000000 --- a/modules/simpletest/tests/url_alter_test.module +++ /dev/null @@ -1,67 +0,0 @@ -<?php - -/** - * @file - * Module to help test hook_url_inbound_alter() and hook_url_outbound_alter(). - */ - -/** - * Implements hook_menu(). - */ -function url_alter_test_menu() { - $items['url-alter-test/foo'] = array( - 'title' => 'Foo', - 'page callback' => 'url_alter_test_foo', - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); - return $items; -} - -/** - * Menu callback. - */ -function url_alter_test_foo() { - print 'current_path=' . current_path() . ' request_path=' . request_path(); - exit; -} - -/** - * Implements hook_url_inbound_alter(). - */ -function url_alter_test_url_inbound_alter(&$path, $original_path, $path_language) { - // Rewrite user/username to user/uid. - if (preg_match('!^user/([^/]+)(/.*)?!', $path, $matches)) { - if ($account = user_load_by_name($matches[1])) { - $matches += array(2 => ''); - $path = 'user/' . $account->uid . $matches[2]; - } - } - - // Rewrite community/ to forum/. - if ($path == 'community' || strpos($path, 'community/') === 0) { - $path = 'forum' . substr($path, 9); - } - - if ($path == 'url-alter-test/bar') { - $path = 'url-alter-test/foo'; - } -} - -/** - * Implements hook_url_outbound_alter(). - */ -function url_alter_test_url_outbound_alter(&$path, &$options, $original_path) { - // Rewrite user/uid to user/username. - if (preg_match('!^user/([0-9]+)(/.*)?!', $path, $matches)) { - if ($account = user_load($matches[1])) { - $matches += array(2 => ''); - $path = 'user/' . $account->name . $matches[2]; - } - } - - // Rewrite forum/ to community/. - if ($path == 'forum' || strpos($path, 'forum/') === 0) { - $path = 'community' . substr($path, 5); - } -} diff --git a/modules/simpletest/tests/xmlrpc.test b/modules/simpletest/tests/xmlrpc.test deleted file mode 100644 index 1b5bff3841c..00000000000 --- a/modules/simpletest/tests/xmlrpc.test +++ /dev/null @@ -1,244 +0,0 @@ -<?php - -/** - * Perform basic XML-RPC tests that do not require addition callbacks. - */ -class XMLRPCBasicTestCase extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'XML-RPC basic', - 'description' => 'Perform basic XML-RPC tests that do not require additional callbacks.', - 'group' => 'XML-RPC', - ); - } - - /** - * Ensure that a basic XML-RPC call with no parameters works. - */ - protected function testListMethods() { - // Minimum list of methods that should be included. - $minimum = array( - 'system.multicall', - 'system.methodSignature', - 'system.getCapabilities', - 'system.listMethods', - 'system.methodHelp', - ); - - // Invoke XML-RPC call to get list of methods. - $url = url(NULL, array('absolute' => TRUE)) . 'xmlrpc.php'; - $methods = xmlrpc($url, array('system.listMethods' => array())); - - // Ensure that the minimum methods were found. - $count = 0; - foreach ($methods as $method) { - if (in_array($method, $minimum)) { - $count++; - } - } - - $this->assertEqual($count, count($minimum), 'system.listMethods returned at least the minimum listing'); - } - - /** - * Ensure that system.methodSignature returns an array of signatures. - */ - protected function testMethodSignature() { - $url = url(NULL, array('absolute' => TRUE)) . 'xmlrpc.php'; - $signature = xmlrpc($url, array('system.methodSignature' => array('system.listMethods'))); - $this->assert(is_array($signature) && !empty($signature) && is_array($signature[0]), - t('system.methodSignature returns an array of signature arrays.')); - } - - /** - * Ensure that XML-RPC correctly handles invalid messages when parsing. - */ - protected function testInvalidMessageParsing() { - $invalid_messages = array( - array( - 'message' => xmlrpc_message(''), - 'assertion' => t('Empty message correctly rejected during parsing.'), - ), - array( - 'message' => xmlrpc_message('<?xml version="1.0" encoding="ISO-8859-1"?>'), - 'assertion' => t('Empty message with XML declaration correctly rejected during parsing.'), - ), - array( - 'message' => xmlrpc_message('<?xml version="1.0"?><params><param><value><string>value</string></value></param></params>'), - 'assertion' => t('Non-empty message without a valid message type is rejected during parsing.'), - ), - array( - 'message' => xmlrpc_message('<methodResponse><params><param><value><string>value</string></value></param></methodResponse>'), - 'assertion' => t('Non-empty malformed message is rejected during parsing.'), - ), - ); - - foreach ($invalid_messages as $assertion) { - $this->assertFalse(xmlrpc_message_parse($assertion['message']), $assertion['assertion']); - } - } -} - -class XMLRPCValidator1IncTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'XML-RPC validator', - 'description' => 'See <a href="http://www.xmlrpc.com/validator1Docs">the xmlrpc validator1 specification</a>.', - 'group' => 'XML-RPC', - ); - } - - function setUp() { - parent::setUp('xmlrpc_test'); - } - - /** - * Run validator1 tests. - */ - function testValidator1() { - $xml_url = url(NULL, array('absolute' => TRUE)) . 'xmlrpc.php'; - srand(); - mt_srand(); - - $array_1 = array(array('curly' => mt_rand(-100, 100)), - array('curly' => mt_rand(-100, 100)), - array('larry' => mt_rand(-100, 100)), - array('larry' => mt_rand(-100, 100)), - array('moe' => mt_rand(-100, 100)), - array('moe' => mt_rand(-100, 100)), - array('larry' => mt_rand(-100, 100))); - shuffle($array_1); - $l_res_1 = xmlrpc_test_arrayOfStructsTest($array_1); - $r_res_1 = xmlrpc($xml_url, array('validator1.arrayOfStructsTest' => array($array_1))); - $this->assertIdentical($l_res_1, $r_res_1); - - $string_2 = 't\'&>>zf"md>yr>xlcev<h<"k&j<og"w&&>">>uai"np&s>>q\'&b<>"&&&'; - $l_res_2 = xmlrpc_test_countTheEntities($string_2); - $r_res_2 = xmlrpc($xml_url, array('validator1.countTheEntities' => array($string_2))); - $this->assertIdentical($l_res_2, $r_res_2); - - $struct_3 = array('moe' => mt_rand(-100, 100), 'larry' => mt_rand(-100, 100), 'curly' => mt_rand(-100, 100), 'homer' => mt_rand(-100, 100)); - $l_res_3 = xmlrpc_test_easyStructTest($struct_3); - $r_res_3 = xmlrpc($xml_url, array('validator1.easyStructTest' => array($struct_3))); - $this->assertIdentical($l_res_3, $r_res_3); - - $struct_4 = array('sub1' => array('bar' => 13), - 'sub2' => 14, - 'sub3' => array('foo' => 1, 'baz' => 2), - 'sub4' => array('ss' => array('sss' => array('ssss' => 'sssss')))); - $l_res_4 = xmlrpc_test_echoStructTest($struct_4); - $r_res_4 = xmlrpc($xml_url, array('validator1.echoStructTest' => array($struct_4))); - $this->assertIdentical($l_res_4, $r_res_4); - - $int_5 = mt_rand(-100, 100); - $bool_5 = (($int_5 % 2) == 0); - $string_5 = $this->randomName(); - $double_5 = (double)(mt_rand(-1000, 1000) / 100); - $time_5 = REQUEST_TIME; - $base64_5 = $this->randomName(100); - $l_res_5 = xmlrpc_test_manyTypesTest($int_5, $bool_5, $string_5, $double_5, xmlrpc_date($time_5), $base64_5); - // See http://drupal.org/node/37766 why this currently fails - $l_res_5[5] = $l_res_5[5]->data; - $r_res_5 = xmlrpc($xml_url, array('validator1.manyTypesTest' => array($int_5, $bool_5, $string_5, $double_5, xmlrpc_date($time_5), xmlrpc_base64($base64_5)))); - // @todo Contains objects, objects are not equal. - $this->assertEqual($l_res_5, $r_res_5); - - $size = mt_rand(100, 200); - $array_6 = array(); - for ($i = 0; $i < $size; $i++) { - $array_6[] = $this->randomName(mt_rand(8, 12)); - } - - $l_res_6 = xmlrpc_test_moderateSizeArrayCheck($array_6); - $r_res_6 = xmlrpc($xml_url, array('validator1.moderateSizeArrayCheck' => array($array_6))); - $this->assertIdentical($l_res_6, $r_res_6); - - $struct_7 = array(); - for ($y = 2000; $y < 2002; $y++) { - for ($m = 3; $m < 5; $m++) { - for ($d = 1; $d < 6; $d++) { - $ys = (string) $y; - $ms = sprintf('%02d', $m); - $ds = sprintf('%02d', $d); - $struct_7[$ys][$ms][$ds]['moe'] = mt_rand(-100, 100); - $struct_7[$ys][$ms][$ds]['larry'] = mt_rand(-100, 100); - $struct_7[$ys][$ms][$ds]['curly'] = mt_rand(-100, 100); - } - } - } - $l_res_7 = xmlrpc_test_nestedStructTest($struct_7); - $r_res_7 = xmlrpc($xml_url, array('validator1.nestedStructTest' => array($struct_7))); - $this->assertIdentical($l_res_7, $r_res_7); - - - $int_8 = mt_rand(-100, 100); - $l_res_8 = xmlrpc_test_simpleStructReturnTest($int_8); - $r_res_8 = xmlrpc($xml_url, array('validator1.simpleStructReturnTest' => array($int_8))); - $this->assertIdentical($l_res_8, $r_res_8); - - /* Now test multicall */ - $x = array(); - $x['validator1.arrayOfStructsTest'] = array($array_1); - $x['validator1.countTheEntities'] = array($string_2); - $x['validator1.easyStructTest'] = array($struct_3); - $x['validator1.echoStructTest'] = array($struct_4); - $x['validator1.manyTypesTest'] = array($int_5, $bool_5, $string_5, $double_5, xmlrpc_date($time_5), xmlrpc_base64($base64_5)); - $x['validator1.moderateSizeArrayCheck'] = array($array_6); - $x['validator1.nestedStructTest'] = array($struct_7); - $x['validator1.simpleStructReturnTest'] = array($int_8); - - $a_l_res = array($l_res_1, $l_res_2, $l_res_3, $l_res_4, $l_res_5, $l_res_6, $l_res_7, $l_res_8); - $a_r_res = xmlrpc($xml_url, $x); - $this->assertEqual($a_l_res, $a_r_res); - } -} - -class XMLRPCMessagesTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'XML-RPC message and alteration', - 'description' => 'Test large messages and method alterations.', - 'group' => 'XML-RPC', - ); - } - - function setUp() { - parent::setUp('xmlrpc_test'); - } - - /** - * Make sure that XML-RPC can transfer large messages. - */ - function testSizedMessages() { - $xml_url = url(NULL, array('absolute' => TRUE)) . 'xmlrpc.php'; - $sizes = array(8, 80, 160); - foreach ($sizes as $size) { - $xml_message_l = xmlrpc_test_message_sized_in_kb($size); - $xml_message_r = xmlrpc($xml_url, array('messages.messageSizedInKB' => array($size))); - - $this->assertEqual($xml_message_l, $xml_message_r, t('XML-RPC messages.messageSizedInKB of %s Kb size received', array('%s' => $size))); - } - } - - /** - * Ensure that hook_xmlrpc_alter() can hide even builtin methods. - */ - protected function testAlterListMethods() { - - // Ensure xmlrpc_test_xmlrpc_alter() is disabled and retrieve regular list of methods. - variable_set('xmlrpc_test_xmlrpc_alter', FALSE); - $url = url(NULL, array('absolute' => TRUE)) . 'xmlrpc.php'; - $methods1 = xmlrpc($url, array('system.listMethods' => array())); - - // Enable the alter hook and retrieve the list of methods again. - variable_set('xmlrpc_test_xmlrpc_alter', TRUE); - $methods2 = xmlrpc($url, array('system.listMethods' => array())); - - $diff = array_diff($methods1, $methods2); - $this->assertTrue(is_array($diff) && !empty($diff), t('Method list is altered by hook_xmlrpc_alter')); - $removed = reset($diff); - $this->assertEqual($removed, 'system.methodSignature', t('Hiding builting system.methodSignature with hook_xmlrpc_alter works')); - } - -} diff --git a/modules/simpletest/tests/xmlrpc_test.info b/modules/simpletest/tests/xmlrpc_test.info deleted file mode 100644 index 6985439715b..00000000000 --- a/modules/simpletest/tests/xmlrpc_test.info +++ /dev/null @@ -1,6 +0,0 @@ -name = "XML-RPC Test" -description = "Support module for XML-RPC tests according to the validator1 specification." -package = Testing -version = VERSION -core = 8.x -hidden = TRUE diff --git a/modules/simpletest/tests/xmlrpc_test.module b/modules/simpletest/tests/xmlrpc_test.module deleted file mode 100644 index db8f113b6b5..00000000000 --- a/modules/simpletest/tests/xmlrpc_test.module +++ /dev/null @@ -1,111 +0,0 @@ -<?php - -function xmlrpc_test_arrayOfStructsTest($array) { - $sum = 0; - foreach ($array as $struct) { - if (isset($struct['curly'])) { - $sum += $struct['curly']; - } - } - return $sum; -} - -function xmlrpc_test_countTheEntities($string) { - return array( - 'ctLeftAngleBrackets' => substr_count($string, '<'), - 'ctRightAngleBrackets' => substr_count($string, '>'), - 'ctAmpersands' => substr_count($string, '&'), - 'ctApostrophes' => substr_count($string, "'"), - 'ctQuotes' => substr_count($string, '"'), - ); -} - -function xmlrpc_test_easyStructTest($array) { - return $array["curly"] + $array["moe"] + $array["larry"]; -} - -function xmlrpc_test_echoStructTest($array) { - return $array; -} - -function xmlrpc_test_manyTypesTest($number, $boolean, $string, $double, $dateTime, $base64) { - $timestamp = gmmktime($dateTime->hour, $dateTime->minute, $dateTime->second, $dateTime->month, $dateTime->day, $dateTime->year); - return array($number, $boolean, $string, $double, xmlrpc_date($timestamp), xmlrpc_Base64($base64)); -} - -function xmlrpc_test_moderateSizeArrayCheck($array) { - return array_shift($array) . array_pop($array); -} - -function xmlrpc_test_nestedStructTest($array) { - return $array["2000"]["04"]["01"]["larry"] + $array["2000"]["04"]["01"]["moe"] + $array["2000"]["04"]["01"]["curly"]; -} - -function xmlrpc_test_simpleStructReturnTest($number) { - return array("times10" => ($number*10), "times100" => ($number*100), "times1000" => ($number*1000)); -} - -/** - * Implements hook_xmlrpc(). - */ -function xmlrpc_test_xmlrpc() { - return array( - 'validator1.arrayOfStructsTest' => 'xmlrpc_test_arrayOfStructsTest', - 'validator1.countTheEntities' => 'xmlrpc_test_countTheEntities', - 'validator1.easyStructTest' => 'xmlrpc_test_easyStructTest', - 'validator1.echoStructTest' => 'xmlrpc_test_echoStructTest', - 'validator1.manyTypesTest' => 'xmlrpc_test_manyTypesTest', - 'validator1.moderateSizeArrayCheck' => 'xmlrpc_test_moderateSizeArrayCheck', - 'validator1.nestedStructTest' => 'xmlrpc_test_nestedStructTest', - 'validator1.simpleStructReturnTest' => 'xmlrpc_test_simpleStructReturnTest', - 'messages.messageSizedInKB' => 'xmlrpc_test_message_sized_in_kb', - ); -} - -/** - * Implements hook_xmlrpc_alter(). - * - * Hide (or not) the system.methodSignature() service depending on a variable. - */ -function xmlrpc_test_xmlrpc_alter(&$services) { - if (variable_get('xmlrpc_test_xmlrpc_alter', FALSE)) { - $remove = NULL; - foreach ($services as $key => $value) { - if (!is_array($value)) { - continue; - } - if ($value[0] == 'system.methodSignature') { - $remove = $key; - break; - } - } - if (isset($remove)) { - unset($services[$remove]); - } - } -} - -/** - * Created a message of the desired size in KB. - * - * @param $size - * Message size in KB. - * @return array - * Generated message structure. - */ -function xmlrpc_test_message_sized_in_kb($size) { - $message = array(); - - $word = 'abcdefg'; - - // Create a ~1KB sized struct. - for ($i = 0 ; $i < 128; $i++) { - $line['word_' . $i] = $word; - } - - for ($i = 0; $i < $size; $i++) { - $message['line_' . $i] = $line; - } - - return $message; -} |