cms/drupal/modules/system/system.test
changeset 541 e756a8c72c3d
equal deleted inserted replaced
540:07239de796bb 541:e756a8c72c3d
       
     1 <?php
       
     2 
       
     3 /**
       
     4  * @file
       
     5  * Tests for system.module.
       
     6  */
       
     7 
       
     8 /**
       
     9  * Helper class for module test cases.
       
    10  */
       
    11 class ModuleTestCase extends DrupalWebTestCase {
       
    12   protected $admin_user;
       
    13 
       
    14   function setUp() {
       
    15     parent::setUp('system_test');
       
    16 
       
    17     $this->admin_user = $this->drupalCreateUser(array('access administration pages', 'administer modules'));
       
    18     $this->drupalLogin($this->admin_user);
       
    19   }
       
    20 
       
    21   /**
       
    22    * Assert there are tables that begin with the specified base table name.
       
    23    *
       
    24    * @param $base_table
       
    25    *   Beginning of table name to look for.
       
    26    * @param $count
       
    27    *   (optional) Whether or not to assert that there are tables that match the
       
    28    *   specified base table. Defaults to TRUE.
       
    29    */
       
    30   function assertTableCount($base_table, $count = TRUE) {
       
    31     $tables = db_find_tables(Database::getConnection()->prefixTables('{' . $base_table . '}') . '%');
       
    32 
       
    33     if ($count) {
       
    34       return $this->assertTrue($tables, format_string('Tables matching "@base_table" found.', array('@base_table' => $base_table)));
       
    35     }
       
    36     return $this->assertFalse($tables, format_string('Tables matching "@base_table" not found.', array('@base_table' => $base_table)));
       
    37   }
       
    38 
       
    39   /**
       
    40    * Assert that all tables defined in a module's hook_schema() exist.
       
    41    *
       
    42    * @param $module
       
    43    *   The name of the module.
       
    44    */
       
    45   function assertModuleTablesExist($module) {
       
    46     $tables = array_keys(drupal_get_schema_unprocessed($module));
       
    47     $tables_exist = TRUE;
       
    48     foreach ($tables as $table) {
       
    49       if (!db_table_exists($table)) {
       
    50         $tables_exist = FALSE;
       
    51       }
       
    52     }
       
    53     return $this->assertTrue($tables_exist, format_string('All database tables defined by the @module module exist.', array('@module' => $module)));
       
    54   }
       
    55 
       
    56   /**
       
    57    * Assert that none of the tables defined in a module's hook_schema() exist.
       
    58    *
       
    59    * @param $module
       
    60    *   The name of the module.
       
    61    */
       
    62   function assertModuleTablesDoNotExist($module) {
       
    63     $tables = array_keys(drupal_get_schema_unprocessed($module));
       
    64     $tables_exist = FALSE;
       
    65     foreach ($tables as $table) {
       
    66       if (db_table_exists($table)) {
       
    67         $tables_exist = TRUE;
       
    68       }
       
    69     }
       
    70     return $this->assertFalse($tables_exist, format_string('None of the database tables defined by the @module module exist.', array('@module' => $module)));
       
    71   }
       
    72 
       
    73   /**
       
    74    * Assert the list of modules are enabled or disabled.
       
    75    *
       
    76    * @param $modules
       
    77    *   Module list to check.
       
    78    * @param $enabled
       
    79    *   Expected module state.
       
    80    */
       
    81   function assertModules(array $modules, $enabled) {
       
    82     module_list(TRUE);
       
    83     foreach ($modules as $module) {
       
    84       if ($enabled) {
       
    85         $message = 'Module "@module" is enabled.';
       
    86       }
       
    87       else {
       
    88         $message = 'Module "@module" is not enabled.';
       
    89       }
       
    90       $this->assertEqual(module_exists($module), $enabled, format_string($message, array('@module' => $module)));
       
    91     }
       
    92   }
       
    93 
       
    94   /**
       
    95    * Verify a log entry was entered for a module's status change.
       
    96    * Called in the same way of the expected original watchdog() execution.
       
    97    *
       
    98    * @param $type
       
    99    *   The category to which this message belongs.
       
   100    * @param $message
       
   101    *   The message to store in the log. Keep $message translatable
       
   102    *   by not concatenating dynamic values into it! Variables in the
       
   103    *   message should be added by using placeholder strings alongside
       
   104    *   the variables argument to declare the value of the placeholders.
       
   105    *   See t() for documentation on how $message and $variables interact.
       
   106    * @param $variables
       
   107    *   Array of variables to replace in the message on display or
       
   108    *   NULL if message is already translated or not possible to
       
   109    *   translate.
       
   110    * @param $severity
       
   111    *   The severity of the message, as per RFC 3164.
       
   112    * @param $link
       
   113    *   A link to associate with the message.
       
   114    */
       
   115   function assertLogMessage($type, $message, $variables = array(), $severity = WATCHDOG_NOTICE, $link = '') {
       
   116     $count = db_select('watchdog', 'w')
       
   117       ->condition('type', $type)
       
   118       ->condition('message', $message)
       
   119       ->condition('variables', serialize($variables))
       
   120       ->condition('severity', $severity)
       
   121       ->condition('link', $link)
       
   122       ->countQuery()
       
   123       ->execute()
       
   124       ->fetchField();
       
   125     $this->assertTrue($count > 0, format_string('watchdog table contains @count rows for @message', array('@count' => $count, '@message' => $message)));
       
   126   }
       
   127 }
       
   128 
       
   129 /**
       
   130  * Test module enabling/disabling functionality.
       
   131  */
       
   132 class EnableDisableTestCase extends ModuleTestCase {
       
   133   protected $profile = 'testing';
       
   134 
       
   135   public static function getInfo() {
       
   136     return array(
       
   137       'name' => 'Enable/disable modules',
       
   138       'description' => 'Enable/disable core module and confirm table creation/deletion.',
       
   139       'group' => 'Module',
       
   140     );
       
   141   }
       
   142 
       
   143   /**
       
   144    * Test that all core modules can be enabled, disabled and uninstalled.
       
   145    */
       
   146   function testEnableDisable() {
       
   147     // Try to enable, disable and uninstall all core modules, unless they are
       
   148     // hidden or required.
       
   149     $modules = system_rebuild_module_data();
       
   150     foreach ($modules as $name => $module) {
       
   151       if ($module->info['package'] != 'Core' || !empty($module->info['hidden']) || !empty($module->info['required'])) {
       
   152         unset($modules[$name]);
       
   153       }
       
   154     }
       
   155     $this->assertTrue(count($modules), format_string('Found @count core modules that we can try to enable in this test.', array('@count' => count($modules))));
       
   156 
       
   157     // Enable the dblog module first, since we will be asserting the presence
       
   158     // of log messages throughout the test.
       
   159    if (isset($modules['dblog'])) {
       
   160      $modules = array('dblog' => $modules['dblog']) + $modules;
       
   161    }
       
   162 
       
   163    // Set a variable so that the hook implementations in system_test.module
       
   164    // will display messages via drupal_set_message().
       
   165    variable_set('test_verbose_module_hooks', TRUE);
       
   166 
       
   167     // Throughout this test, some modules may be automatically enabled (due to
       
   168     // dependencies). We'll keep track of them in an array, so we can handle
       
   169     // them separately.
       
   170     $automatically_enabled = array();
       
   171 
       
   172     // Go through each module in the list and try to enable it (unless it was
       
   173     // already enabled automatically due to a dependency).
       
   174     foreach ($modules as $name => $module) {
       
   175       if (empty($automatically_enabled[$name])) {
       
   176         // Start a list of modules that we expect to be enabled this time.
       
   177         $modules_to_enable = array($name);
       
   178 
       
   179         // Find out if the module has any dependencies that aren't enabled yet;
       
   180         // if so, add them to the list of modules we expect to be automatically
       
   181         // enabled.
       
   182         foreach (array_keys($module->requires) as $dependency) {
       
   183           if (isset($modules[$dependency]) && empty($automatically_enabled[$dependency])) {
       
   184             $modules_to_enable[] = $dependency;
       
   185             $automatically_enabled[$dependency] = TRUE;
       
   186           }
       
   187         }
       
   188 
       
   189         // Check that each module is not yet enabled and does not have any
       
   190         // database tables yet.
       
   191         foreach ($modules_to_enable as $module_to_enable) {
       
   192           $this->assertModules(array($module_to_enable), FALSE);
       
   193           $this->assertModuleTablesDoNotExist($module_to_enable);
       
   194         }
       
   195 
       
   196         // Install and enable the module.
       
   197         $edit = array();
       
   198         $edit['modules[Core][' . $name . '][enable]'] = $name;
       
   199         $this->drupalPost('admin/modules', $edit, t('Save configuration'));
       
   200         // Handle the case where modules were installed along with this one and
       
   201         // where we therefore hit a confirmation screen.
       
   202         if (count($modules_to_enable) > 1) {
       
   203           $this->drupalPost(NULL, array(), t('Continue'));
       
   204         }
       
   205         $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.');
       
   206 
       
   207         // Check that hook_modules_installed() and hook_modules_enabled() were
       
   208         // invoked with the expected list of modules, that each module's
       
   209         // database tables now exist, and that appropriate messages appear in
       
   210         // the logs.
       
   211         foreach ($modules_to_enable as $module_to_enable) {
       
   212           $this->assertText(t('hook_modules_installed fired for @module', array('@module' => $module_to_enable)));
       
   213           $this->assertText(t('hook_modules_enabled fired for @module', array('@module' => $module_to_enable)));
       
   214           $this->assertModules(array($module_to_enable), TRUE);
       
   215           $this->assertModuleTablesExist($module_to_enable);
       
   216           $this->assertLogMessage('system', "%module module installed.", array('%module' => $module_to_enable), WATCHDOG_INFO);
       
   217           $this->assertLogMessage('system', "%module module enabled.", array('%module' => $module_to_enable), WATCHDOG_INFO);
       
   218         }
       
   219 
       
   220         // Disable and uninstall the original module, and check appropriate
       
   221         // hooks, tables, and log messages. (Later, we'll go back and do the
       
   222         // same thing for modules that were enabled automatically.) Skip this
       
   223         // for the dblog module, because that is needed for the test; we'll go
       
   224         // back and do that one at the end also.
       
   225         if ($name != 'dblog') {
       
   226           $this->assertSuccessfulDisableAndUninstall($name);
       
   227         }
       
   228       }
       
   229     }
       
   230 
       
   231     // Go through all modules that were automatically enabled, and try to
       
   232     // disable and uninstall them one by one.
       
   233     while (!empty($automatically_enabled)) {
       
   234       $initial_count = count($automatically_enabled);
       
   235       foreach (array_keys($automatically_enabled) as $name) {
       
   236         // If the module can't be disabled due to dependencies, skip it and try
       
   237         // again the next time. Otherwise, try to disable it.
       
   238         $this->drupalGet('admin/modules');
       
   239         $disabled_checkbox = $this->xpath('//input[@type="checkbox" and @disabled="disabled" and @name="modules[Core][' . $name . '][enable]"]');
       
   240         if (empty($disabled_checkbox) && $name != 'dblog') {
       
   241           unset($automatically_enabled[$name]);
       
   242           $this->assertSuccessfulDisableAndUninstall($name);
       
   243         }
       
   244       }
       
   245       $final_count = count($automatically_enabled);
       
   246       // If all checkboxes were disabled, something is really wrong with the
       
   247       // test. Throw a failure and avoid an infinite loop.
       
   248       if ($initial_count == $final_count) {
       
   249         $this->fail(t('Remaining modules could not be disabled.'));
       
   250         break;
       
   251       }
       
   252     }
       
   253 
       
   254     // Disable and uninstall the dblog module last, since we needed it for
       
   255     // assertions in all the above tests.
       
   256     if (isset($modules['dblog'])) {
       
   257       $this->assertSuccessfulDisableAndUninstall('dblog');
       
   258     }
       
   259 
       
   260     // Now that all modules have been tested, go back and try to enable them
       
   261     // all again at once. This tests two things:
       
   262     // - That each module can be successfully enabled again after being
       
   263     //   uninstalled.
       
   264     // - That enabling more than one module at the same time does not lead to
       
   265     //   any errors.
       
   266     $edit = array();
       
   267     foreach (array_keys($modules) as $name) {
       
   268       $edit['modules[Core][' . $name . '][enable]'] = $name;
       
   269     }
       
   270     $this->drupalPost('admin/modules', $edit, t('Save configuration'));
       
   271     $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.');
       
   272   }
       
   273 
       
   274   /**
       
   275    * Ensures entity info cache is updated after changes.
       
   276    */
       
   277   function testEntityInfoChanges() {
       
   278     module_enable(array('entity_cache_test'));
       
   279     $entity_info = entity_get_info();
       
   280     $this->assertTrue(isset($entity_info['entity_cache_test']), 'Test entity type found.');
       
   281 
       
   282     // Change the label of the test entity type and make sure changes appear
       
   283     // after flushing caches.
       
   284     variable_set('entity_cache_test_label', 'New label.');
       
   285     drupal_flush_all_caches();
       
   286     $info = entity_get_info('entity_cache_test');
       
   287     $this->assertEqual($info['label'], 'New label.', 'New label appears in entity info.');
       
   288 
       
   289     // Disable the providing module and make sure the entity type is gone.
       
   290     module_disable(array('entity_cache_test', 'entity_cache_test_dependency'));
       
   291     $entity_info = entity_get_info();
       
   292     $this->assertFalse(isset($entity_info['entity_cache_test']), 'Entity type of the providing module is gone.');
       
   293   }
       
   294 
       
   295   /**
       
   296    * Tests entity info cache after enabling a module with a dependency on an entity providing module.
       
   297    *
       
   298    * @see entity_cache_test_watchdog()
       
   299    */
       
   300   function testEntityInfoCacheWatchdog() {
       
   301     module_enable(array('entity_cache_test'));
       
   302     $info = variable_get('entity_cache_test');
       
   303     $this->assertEqual($info['label'], 'Entity Cache Test', 'Entity info label is correct.');
       
   304     $this->assertEqual($info['controller class'], 'DrupalDefaultEntityController', 'Entity controller class info is correct.');
       
   305   }
       
   306 
       
   307   /**
       
   308    * Disables and uninstalls a module and asserts that it was done correctly.
       
   309    *
       
   310    * @param $module
       
   311    *   The name of the module to disable and uninstall.
       
   312    */
       
   313   function assertSuccessfulDisableAndUninstall($module) {
       
   314     // Disable the module.
       
   315     $edit = array();
       
   316     $edit['modules[Core][' . $module . '][enable]'] = FALSE;
       
   317     $this->drupalPost('admin/modules', $edit, t('Save configuration'));
       
   318     $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.');
       
   319     $this->assertModules(array($module), FALSE);
       
   320 
       
   321     // Check that the appropriate hook was fired and the appropriate log
       
   322     // message appears.
       
   323     $this->assertText(t('hook_modules_disabled fired for @module', array('@module' => $module)));
       
   324     $this->assertLogMessage('system', "%module module disabled.", array('%module' => $module), WATCHDOG_INFO);
       
   325 
       
   326     //  Check that the module's database tables still exist.
       
   327     $this->assertModuleTablesExist($module);
       
   328 
       
   329     // Uninstall the module.
       
   330     $edit = array();
       
   331     $edit['uninstall[' . $module . ']'] = $module;
       
   332     $this->drupalPost('admin/modules/uninstall', $edit, t('Uninstall'));
       
   333     $this->drupalPost(NULL, NULL, t('Uninstall'));
       
   334     $this->assertText(t('The selected modules have been uninstalled.'), 'Modules status has been updated.');
       
   335     $this->assertModules(array($module), FALSE);
       
   336 
       
   337     // Check that the appropriate hook was fired and the appropriate log
       
   338     // message appears. (But don't check for the log message if the dblog
       
   339     // module was just uninstalled, since the {watchdog} table won't be there
       
   340     // anymore.)
       
   341     $this->assertText(t('hook_modules_uninstalled fired for @module', array('@module' => $module)));
       
   342     if ($module != 'dblog') {
       
   343       $this->assertLogMessage('system', "%module module uninstalled.", array('%module' => $module), WATCHDOG_INFO);
       
   344     }
       
   345 
       
   346     // Check that the module's database tables no longer exist.
       
   347     $this->assertModuleTablesDoNotExist($module);
       
   348   }
       
   349 }
       
   350 
       
   351 /**
       
   352  * Tests failure of hook_requirements('install').
       
   353  */
       
   354 class HookRequirementsTestCase extends ModuleTestCase {
       
   355   public static function getInfo() {
       
   356     return array(
       
   357       'name' => 'Requirements hook failure',
       
   358       'description' => "Attempts enabling a module that fails hook_requirements('install').",
       
   359       'group' => 'Module',
       
   360     );
       
   361   }
       
   362 
       
   363   /**
       
   364    * Assert that a module cannot be installed if it fails hook_requirements().
       
   365    */
       
   366   function testHookRequirementsFailure() {
       
   367     $this->assertModules(array('requirements1_test'), FALSE);
       
   368 
       
   369     // Attempt to install the requirements1_test module.
       
   370     $edit = array();
       
   371     $edit['modules[Testing][requirements1_test][enable]'] = 'requirements1_test';
       
   372     $this->drupalPost('admin/modules', $edit, t('Save configuration'));
       
   373 
       
   374     // Makes sure the module was NOT installed.
       
   375     $this->assertText(t('Requirements 1 Test failed requirements'), 'Modules status has been updated.');
       
   376     $this->assertModules(array('requirements1_test'), FALSE);
       
   377   }
       
   378 }
       
   379 
       
   380 /**
       
   381  * Test module dependency functionality.
       
   382  */
       
   383 class ModuleDependencyTestCase extends ModuleTestCase {
       
   384   public static function getInfo() {
       
   385     return array(
       
   386       'name' => 'Module dependencies',
       
   387       'description' => 'Enable module without dependency enabled.',
       
   388       'group' => 'Module',
       
   389     );
       
   390   }
       
   391 
       
   392   /**
       
   393    * Checks functionality of project namespaces for dependencies.
       
   394    */
       
   395   function testProjectNamespaceForDependencies() {
       
   396     // Enable module with project namespace to ensure nothing breaks.
       
   397     $edit = array(
       
   398       'modules[Testing][system_project_namespace_test][enable]' => TRUE,
       
   399     );
       
   400     $this->drupalPost('admin/modules', $edit, t('Save configuration'));
       
   401     $this->assertModules(array('system_project_namespace_test'), TRUE);
       
   402   }
       
   403 
       
   404   /**
       
   405    * Attempt to enable translation module without locale enabled.
       
   406    */
       
   407   function testEnableWithoutDependency() {
       
   408     // Attempt to enable content translation without locale enabled.
       
   409     $edit = array();
       
   410     $edit['modules[Core][translation][enable]'] = 'translation';
       
   411     $this->drupalPost('admin/modules', $edit, t('Save configuration'));
       
   412     $this->assertText(t('Some required modules must be enabled'), 'Dependency required.');
       
   413 
       
   414     $this->assertModules(array('translation', 'locale'), FALSE);
       
   415 
       
   416     // Assert that the locale tables weren't enabled.
       
   417     $this->assertTableCount('languages', FALSE);
       
   418     $this->assertTableCount('locale', FALSE);
       
   419 
       
   420     $this->drupalPost(NULL, NULL, t('Continue'));
       
   421     $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.');
       
   422 
       
   423     $this->assertModules(array('translation', 'locale'), TRUE);
       
   424 
       
   425     // Assert that the locale tables were enabled.
       
   426     $this->assertTableCount('languages', TRUE);
       
   427     $this->assertTableCount('locale', TRUE);
       
   428   }
       
   429 
       
   430   /**
       
   431    * Attempt to enable a module with a missing dependency.
       
   432    */
       
   433   function testMissingModules() {
       
   434     // Test that the system_dependencies_test module is marked
       
   435     // as missing a dependency.
       
   436     $this->drupalGet('admin/modules');
       
   437     $this->assertRaw(t('@module (<span class="admin-missing">missing</span>)', array('@module' => drupal_ucfirst('_missing_dependency'))), 'A module with missing dependencies is marked as such.');
       
   438     $checkbox = $this->xpath('//input[@type="checkbox" and @disabled="disabled" and @name="modules[Testing][system_dependencies_test][enable]"]');
       
   439     $this->assert(count($checkbox) == 1, 'Checkbox for the module is disabled.');
       
   440 
       
   441     // Force enable the system_dependencies_test module.
       
   442     module_enable(array('system_dependencies_test'), FALSE);
       
   443 
       
   444     // Verify that the module is forced to be disabled when submitting
       
   445     // the module page.
       
   446     $this->drupalPost('admin/modules', array(), t('Save configuration'));
       
   447     $this->assertText(t('The @module module is missing, so the following module will be disabled: @depends.', array('@module' => '_missing_dependency', '@depends' => 'system_dependencies_test')), 'The module missing dependencies will be disabled.');
       
   448 
       
   449     // Confirm.
       
   450     $this->drupalPost(NULL, NULL, t('Continue'));
       
   451 
       
   452     // Verify that the module has been disabled.
       
   453     $this->assertModules(array('system_dependencies_test'), FALSE);
       
   454   }
       
   455 
       
   456   /**
       
   457    * Tests enabling a module that depends on an incompatible version of a module.
       
   458    */
       
   459   function testIncompatibleModuleVersionDependency() {
       
   460     // Test that the system_incompatible_module_version_dependencies_test is
       
   461     // marked as having an incompatible dependency.
       
   462     $this->drupalGet('admin/modules');
       
   463     $this->assertRaw(t('@module (<span class="admin-missing">incompatible with</span> version @version)', array(
       
   464       '@module' => 'System incompatible module version test (>2.0)',
       
   465       '@version' => '1.0',
       
   466     )), 'A module that depends on an incompatible version of a module is marked as such.');
       
   467     $checkbox = $this->xpath('//input[@type="checkbox" and @disabled="disabled" and @name="modules[Testing][system_incompatible_module_version_dependencies_test][enable]"]');
       
   468     $this->assert(count($checkbox) == 1, 'Checkbox for the module is disabled.');
       
   469   }
       
   470 
       
   471   /**
       
   472    * Tests enabling a module that depends on a module with an incompatible core version.
       
   473    */
       
   474   function testIncompatibleCoreVersionDependency() {
       
   475     // Test that the system_incompatible_core_version_dependencies_test is
       
   476     // marked as having an incompatible dependency.
       
   477     $this->drupalGet('admin/modules');
       
   478     $this->assertRaw(t('@module (<span class="admin-missing">incompatible with</span> this version of Drupal core)', array(
       
   479       '@module' => 'System incompatible core version test',
       
   480     )), 'A module that depends on a module with an incompatible core version is marked as such.');
       
   481     $checkbox = $this->xpath('//input[@type="checkbox" and @disabled="disabled" and @name="modules[Testing][system_incompatible_core_version_dependencies_test][enable]"]');
       
   482     $this->assert(count($checkbox) == 1, 'Checkbox for the module is disabled.');
       
   483   }
       
   484 
       
   485   /**
       
   486    * Tests enabling a module that depends on a module which fails hook_requirements().
       
   487    */
       
   488   function testEnableRequirementsFailureDependency() {
       
   489     $this->assertModules(array('requirements1_test'), FALSE);
       
   490     $this->assertModules(array('requirements2_test'), FALSE);
       
   491 
       
   492     // Attempt to install both modules at the same time.
       
   493     $edit = array();
       
   494     $edit['modules[Testing][requirements1_test][enable]'] = 'requirements1_test';
       
   495     $edit['modules[Testing][requirements2_test][enable]'] = 'requirements2_test';
       
   496     $this->drupalPost('admin/modules', $edit, t('Save configuration'));
       
   497 
       
   498     // Makes sure the modules were NOT installed.
       
   499     $this->assertText(t('Requirements 1 Test failed requirements'), 'Modules status has been updated.');
       
   500     $this->assertModules(array('requirements1_test'), FALSE);
       
   501     $this->assertModules(array('requirements2_test'), FALSE);
       
   502 
       
   503     // Makes sure that already enabled modules the failing modules depend on
       
   504     // were not disabled.
       
   505     $this->assertModules(array('comment'), TRUE);
       
   506 
       
   507   }
       
   508 
       
   509   /**
       
   510    * Tests that module dependencies are enabled in the correct order via the
       
   511    * UI. Dependencies should be enabled before their dependents.
       
   512    */
       
   513   function testModuleEnableOrder() {
       
   514     module_enable(array('module_test'), FALSE);
       
   515     $this->resetAll();
       
   516     $this->assertModules(array('module_test'), TRUE);
       
   517     variable_set('dependency_test', 'dependency');
       
   518     // module_test creates a dependency chain: forum depends on poll, which
       
   519     // depends on php. The correct enable order is, php, poll, forum.
       
   520     $expected_order = array('php', 'poll', 'forum');
       
   521 
       
   522     // Enable the modules through the UI, verifying that the dependency chain
       
   523     // is correct.
       
   524     $edit = array();
       
   525     $edit['modules[Core][forum][enable]'] = 'forum';
       
   526     $this->drupalPost('admin/modules', $edit, t('Save configuration'));
       
   527     $this->assertModules(array('forum'), FALSE);
       
   528     $this->assertText(t('You must enable the Poll, PHP filter modules to install Forum.'), t('Dependency chain created.'));
       
   529     $edit['modules[Core][poll][enable]'] = 'poll';
       
   530     $edit['modules[Core][php][enable]'] = 'php';
       
   531     $this->drupalPost('admin/modules', $edit, t('Save configuration'));
       
   532     $this->assertModules(array('forum', 'poll', 'php'), TRUE);
       
   533 
       
   534     // Check the actual order which is saved by module_test_modules_enabled().
       
   535     $this->assertIdentical(variable_get('test_module_enable_order', FALSE), $expected_order, t('Modules enabled in the correct order.'));
       
   536   }
       
   537 
       
   538   /**
       
   539    * Tests attempting to uninstall a module that has installed dependents.
       
   540    */
       
   541   function testUninstallDependents() {
       
   542     // Enable the forum module.
       
   543     $edit = array('modules[Core][forum][enable]' => 'forum');
       
   544     $this->drupalPost('admin/modules', $edit, t('Save configuration'));
       
   545     $this->assertModules(array('forum'), TRUE);
       
   546 
       
   547     // Disable forum and comment. Both should now be installed but disabled.
       
   548     $edit = array('modules[Core][forum][enable]' => FALSE);
       
   549     $this->drupalPost('admin/modules', $edit, t('Save configuration'));
       
   550     $this->assertModules(array('forum'), FALSE);
       
   551     $edit = array('modules[Core][comment][enable]' => FALSE);
       
   552     $this->drupalPost('admin/modules', $edit, t('Save configuration'));
       
   553     $this->assertModules(array('comment'), FALSE);
       
   554 
       
   555     // Check that the taxonomy module cannot be uninstalled.
       
   556     $this->drupalGet('admin/modules/uninstall');
       
   557     $checkbox = $this->xpath('//input[@type="checkbox" and @disabled="disabled" and @name="uninstall[comment]"]');
       
   558     $this->assert(count($checkbox) == 1, 'Checkbox for uninstalling the comment module is disabled.');
       
   559 
       
   560     // Uninstall the forum module, and check that taxonomy now can also be
       
   561     // uninstalled.
       
   562     $edit = array('uninstall[forum]' => 'forum');
       
   563     $this->drupalPost('admin/modules/uninstall', $edit, t('Uninstall'));
       
   564     $this->drupalPost(NULL, NULL, t('Uninstall'));
       
   565     $this->assertText(t('The selected modules have been uninstalled.'), 'Modules status has been updated.');
       
   566     $edit = array('uninstall[comment]' => 'comment');
       
   567     $this->drupalPost('admin/modules/uninstall', $edit, t('Uninstall'));
       
   568     $this->drupalPost(NULL, NULL, t('Uninstall'));
       
   569     $this->assertText(t('The selected modules have been uninstalled.'), 'Modules status has been updated.');
       
   570   }
       
   571 
       
   572   /**
       
   573    * Tests whether the correct module metadata is returned.
       
   574    */
       
   575   function testModuleMetaData() {
       
   576     // Generate the list of available modules.
       
   577     $modules = system_rebuild_module_data();
       
   578     // Check that the mtime field exists for the system module.
       
   579     $this->assertTrue(!empty($modules['system']->info['mtime']), 'The system.info file modification time field is present.');
       
   580     // Use 0 if mtime isn't present, to avoid an array index notice.
       
   581     $test_mtime = !empty($modules['system']->info['mtime']) ? $modules['system']->info['mtime'] : 0;
       
   582     // Ensure the mtime field contains a number that is greater than zero.
       
   583     $this->assertTrue(is_numeric($test_mtime) && ($test_mtime > 0), 'The system.info file modification time field contains a timestamp.');
       
   584   }
       
   585 
       
   586   /**
       
   587    * Tests whether the correct theme metadata is returned.
       
   588    */
       
   589   function testThemeMetaData() {
       
   590     // Generate the list of available themes.
       
   591     $themes = system_rebuild_theme_data();
       
   592     // Check that the mtime field exists for the bartik theme.
       
   593     $this->assertTrue(!empty($themes['bartik']->info['mtime']), 'The bartik.info file modification time field is present.');
       
   594     // Use 0 if mtime isn't present, to avoid an array index notice.
       
   595     $test_mtime = !empty($themes['bartik']->info['mtime']) ? $themes['bartik']->info['mtime'] : 0;
       
   596     // Ensure the mtime field contains a number that is greater than zero.
       
   597     $this->assertTrue(is_numeric($test_mtime) && ($test_mtime > 0), 'The bartik.info file modification time field contains a timestamp.');
       
   598   }
       
   599 }
       
   600 
       
   601 /**
       
   602  * Test module dependency on specific versions.
       
   603  */
       
   604 class ModuleVersionTestCase extends ModuleTestCase {
       
   605   public static function getInfo() {
       
   606     return array(
       
   607       'name' => 'Module versions',
       
   608       'description' => 'Check module version dependencies.',
       
   609       'group' => 'Module',
       
   610     );
       
   611   }
       
   612 
       
   613   function setUp() {
       
   614     parent::setUp('module_test');
       
   615   }
       
   616 
       
   617   /**
       
   618    * Test version dependencies.
       
   619    */
       
   620   function testModuleVersions() {
       
   621     $dependencies = array(
       
   622       // Alternating between being compatible and incompatible with 7.x-2.4-beta3.
       
   623       // The first is always a compatible.
       
   624       'common_test',
       
   625       // Branch incompatibility.
       
   626       'common_test (1.x)',
       
   627       // Branch compatibility.
       
   628       'common_test (2.x)',
       
   629       // Another branch incompatibility.
       
   630       'common_test (>2.x)',
       
   631       // Another branch compatibility.
       
   632       'common_test (<=2.x)',
       
   633       // Another branch incompatibility.
       
   634       'common_test (<2.x)',
       
   635       // Another branch compatibility.
       
   636       'common_test (>=2.x)',
       
   637       // Nonsense, misses a dash. Incompatible with everything.
       
   638       'common_test (=7.x2.x, >=2.4)',
       
   639       // Core version is optional. Compatible.
       
   640       'common_test (=7.x-2.x, >=2.4-alpha2)',
       
   641       // Test !=, explicitly incompatible.
       
   642       'common_test (=2.x, !=2.4-beta3)',
       
   643       // Three operations. Compatible.
       
   644       'common_test (=2.x, !=2.3, <2.5)',
       
   645       // Testing extra version. Incompatible.
       
   646       'common_test (<=2.4-beta2)',
       
   647       // Testing extra version. Compatible.
       
   648       'common_test (>2.4-beta2)',
       
   649       // Testing extra version. Incompatible.
       
   650       'common_test (>2.4-rc0)',
       
   651     );
       
   652     variable_set('dependencies', $dependencies);
       
   653     $n = count($dependencies);
       
   654     for ($i = 0; $i < $n; $i++) {
       
   655       $this->drupalGet('admin/modules');
       
   656       $checkbox = $this->xpath('//input[@id="edit-modules-testing-module-test-enable"]');
       
   657       $this->assertEqual(!empty($checkbox[0]['disabled']), $i % 2, $dependencies[$i]);
       
   658     }
       
   659   }
       
   660 }
       
   661 
       
   662 /**
       
   663  * Test required modules functionality.
       
   664  */
       
   665 class ModuleRequiredTestCase extends ModuleTestCase {
       
   666   public static function getInfo() {
       
   667     return array(
       
   668       'name' => 'Required modules',
       
   669       'description' => 'Attempt disabling of required modules.',
       
   670       'group' => 'Module',
       
   671     );
       
   672   }
       
   673 
       
   674   /**
       
   675    * Assert that core required modules cannot be disabled.
       
   676    */
       
   677   function testDisableRequired() {
       
   678     $module_info = system_get_info('module');
       
   679     $this->drupalGet('admin/modules');
       
   680     foreach ($module_info as $module => $info) {
       
   681       // Check to make sure the checkbox for each required module is disabled
       
   682       // and checked (or absent from the page if the module is also hidden).
       
   683       if (!empty($info['required'])) {
       
   684         $field_name = "modules[{$info['package']}][$module][enable]";
       
   685         if (empty($info['hidden'])) {
       
   686           $this->assertFieldByXPath("//input[@name='$field_name' and @disabled='disabled' and @checked='checked']", '', format_string('Field @name was disabled and checked.', array('@name' => $field_name)));
       
   687         }
       
   688         else {
       
   689           $this->assertNoFieldByName($field_name);
       
   690         }
       
   691       }
       
   692     }
       
   693   }
       
   694 }
       
   695 
       
   696 class IPAddressBlockingTestCase extends DrupalWebTestCase {
       
   697   protected $blocking_user;
       
   698 
       
   699   /**
       
   700    * Implement getInfo().
       
   701    */
       
   702   public static function getInfo() {
       
   703     return array(
       
   704       'name' => 'IP address blocking',
       
   705       'description' => 'Test IP address blocking.',
       
   706       'group' => 'System'
       
   707     );
       
   708   }
       
   709 
       
   710   /**
       
   711    * Implement setUp().
       
   712    */
       
   713   function setUp() {
       
   714     parent::setUp();
       
   715 
       
   716     // Create user.
       
   717     $this->blocking_user = $this->drupalCreateUser(array('block IP addresses'));
       
   718     $this->drupalLogin($this->blocking_user);
       
   719   }
       
   720 
       
   721   /**
       
   722    * Test a variety of user input to confirm correct validation and saving of data.
       
   723    */
       
   724   function testIPAddressValidation() {
       
   725     $this->drupalGet('admin/config/people/ip-blocking');
       
   726 
       
   727     // Block a valid IP address.
       
   728     $edit = array();
       
   729     $edit['ip'] = '1.2.3.3';
       
   730     $this->drupalPost('admin/config/people/ip-blocking', $edit, t('Add'));
       
   731     $ip = db_query("SELECT iid from {blocked_ips} WHERE ip = :ip", array(':ip' => $edit['ip']))->fetchField();
       
   732     $this->assertTrue($ip, t('IP address found in database.'));
       
   733     $this->assertRaw(t('The IP address %ip has been blocked.', array('%ip' => $edit['ip'])), t('IP address was blocked.'));
       
   734 
       
   735     // Try to block an IP address that's already blocked.
       
   736     $edit = array();
       
   737     $edit['ip'] = '1.2.3.3';
       
   738     $this->drupalPost('admin/config/people/ip-blocking', $edit, t('Add'));
       
   739     $this->assertText(t('This IP address is already blocked.'));
       
   740 
       
   741     // Try to block a reserved IP address.
       
   742     $edit = array();
       
   743     $edit['ip'] = '255.255.255.255';
       
   744     $this->drupalPost('admin/config/people/ip-blocking', $edit, t('Add'));
       
   745     $this->assertText(t('Enter a valid IP address.'));
       
   746 
       
   747     // Try to block a reserved IP address.
       
   748     $edit = array();
       
   749     $edit['ip'] = 'test.example.com';
       
   750     $this->drupalPost('admin/config/people/ip-blocking', $edit, t('Add'));
       
   751     $this->assertText(t('Enter a valid IP address.'));
       
   752 
       
   753     // Submit an empty form.
       
   754     $edit = array();
       
   755     $edit['ip'] = '';
       
   756     $this->drupalPost('admin/config/people/ip-blocking', $edit, t('Add'));
       
   757     $this->assertText(t('Enter a valid IP address.'));
       
   758 
       
   759     // Pass an IP address as a URL parameter and submit it.
       
   760     $submit_ip = '1.2.3.4';
       
   761     $this->drupalPost('admin/config/people/ip-blocking/' . $submit_ip, NULL, t('Add'));
       
   762     $ip = db_query("SELECT iid from {blocked_ips} WHERE ip = :ip", array(':ip' => $submit_ip))->fetchField();
       
   763     $this->assertTrue($ip, t('IP address found in database'));
       
   764     $this->assertRaw(t('The IP address %ip has been blocked.', array('%ip' => $submit_ip)), t('IP address was blocked.'));
       
   765 
       
   766     // Submit your own IP address. This fails, although it works when testing manually.
       
   767      // TODO: on some systems this test fails due to a bug or inconsistency in cURL.
       
   768      // $edit = array();
       
   769      // $edit['ip'] = ip_address();
       
   770      // $this->drupalPost('admin/config/people/ip-blocking', $edit, t('Save'));
       
   771      // $this->assertText(t('You may not block your own IP address.'));
       
   772   }
       
   773 
       
   774   /**
       
   775    * Test duplicate IP addresses are not present in the 'blocked_ips' table.
       
   776    */
       
   777   function testDuplicateIpAddress() {
       
   778     drupal_static_reset('ip_address');
       
   779     $submit_ip = $_SERVER['REMOTE_ADDR'] = '192.168.1.1';
       
   780     system_block_ip_action();
       
   781     system_block_ip_action();
       
   782     $ip_count = db_query("SELECT iid from {blocked_ips} WHERE ip = :ip", array(':ip' => $submit_ip))->rowCount();
       
   783     $this->assertEqual('1', $ip_count);
       
   784     drupal_static_reset('ip_address');
       
   785     $submit_ip = $_SERVER['REMOTE_ADDR'] = ' ';
       
   786     system_block_ip_action();
       
   787     system_block_ip_action();
       
   788     system_block_ip_action();
       
   789     $ip_count = db_query("SELECT iid from {blocked_ips} WHERE ip = :ip", array(':ip' => $submit_ip))->rowCount();
       
   790     $this->assertEqual('1', $ip_count);
       
   791   }
       
   792 }
       
   793 
       
   794 class CronRunTestCase extends DrupalWebTestCase {
       
   795   /**
       
   796    * Implement getInfo().
       
   797    */
       
   798   public static function getInfo() {
       
   799     return array(
       
   800       'name' => 'Cron run',
       
   801       'description' => 'Test cron run.',
       
   802       'group' => 'System'
       
   803     );
       
   804   }
       
   805 
       
   806   function setUp() {
       
   807     parent::setUp(array('common_test', 'common_test_cron_helper'));
       
   808   }
       
   809 
       
   810   /**
       
   811    * Test cron runs.
       
   812    */
       
   813   function testCronRun() {
       
   814     global $base_url;
       
   815 
       
   816     // Run cron anonymously without any cron key.
       
   817     $this->drupalGet($base_url . '/cron.php', array('external' => TRUE));
       
   818     $this->assertResponse(403);
       
   819 
       
   820     // Run cron anonymously with a random cron key.
       
   821     $key = $this->randomName(16);
       
   822     $this->drupalGet($base_url . '/cron.php', array('external' => TRUE, 'query' => array('cron_key' => $key)));
       
   823     $this->assertResponse(403);
       
   824 
       
   825     // Run cron anonymously with the valid cron key.
       
   826     $key = variable_get('cron_key', 'drupal');
       
   827     $this->drupalGet($base_url . '/cron.php', array('external' => TRUE, 'query' => array('cron_key' => $key)));
       
   828     $this->assertResponse(200);
       
   829   }
       
   830 
       
   831   /**
       
   832    * Ensure that the automatic cron run feature is working.
       
   833    *
       
   834    * In these tests we do not use REQUEST_TIME to track start time, because we
       
   835    * need the exact time when cron is triggered.
       
   836    */
       
   837   function testAutomaticCron() {
       
   838     // Ensure cron does not run when the cron threshold is enabled and was
       
   839     // not passed.
       
   840     $cron_last = time();
       
   841     $cron_safe_threshold = 100;
       
   842     variable_set('cron_last', $cron_last);
       
   843     variable_set('cron_safe_threshold', $cron_safe_threshold);
       
   844     $this->drupalGet('');
       
   845     $this->assertTrue($cron_last == variable_get('cron_last', NULL), 'Cron does not run when the cron threshold is not passed.');
       
   846 
       
   847     // Test if cron runs when the cron threshold was passed.
       
   848     $cron_last = time() - 200;
       
   849     variable_set('cron_last', $cron_last);
       
   850     $this->drupalGet('');
       
   851     sleep(1);
       
   852     $this->assertTrue($cron_last < variable_get('cron_last', NULL), 'Cron runs when the cron threshold is passed.');
       
   853 
       
   854     // Disable the cron threshold through the interface.
       
   855     $admin_user = $this->drupalCreateUser(array('administer site configuration'));
       
   856     $this->drupalLogin($admin_user);
       
   857     $this->drupalPost('admin/config/system/cron', array('cron_safe_threshold' => 0), t('Save configuration'));
       
   858     $this->assertText(t('The configuration options have been saved.'));
       
   859     $this->drupalLogout();
       
   860 
       
   861     // Test if cron does not run when the cron threshold is disabled.
       
   862     $cron_last = time() - 200;
       
   863     variable_set('cron_last', $cron_last);
       
   864     $this->drupalGet('');
       
   865     $this->assertTrue($cron_last == variable_get('cron_last', NULL), 'Cron does not run when the cron threshold is disabled.');
       
   866   }
       
   867 
       
   868   /**
       
   869    * Ensure that temporary files are removed.
       
   870    *
       
   871    * Create files for all the possible combinations of age and status. We are
       
   872    * using UPDATE statements rather than file_save() because it would set the
       
   873    * timestamp.
       
   874    */
       
   875   function testTempFileCleanup() {
       
   876     // Temporary file that is older than DRUPAL_MAXIMUM_TEMP_FILE_AGE.
       
   877     $temp_old = file_save_data('');
       
   878     db_update('file_managed')
       
   879       ->fields(array(
       
   880         'status' => 0,
       
   881         'timestamp' => 1,
       
   882       ))
       
   883       ->condition('fid', $temp_old->fid)
       
   884       ->execute();
       
   885     $this->assertTrue(file_exists($temp_old->uri), 'Old temp file was created correctly.');
       
   886 
       
   887     // Temporary file that is less than DRUPAL_MAXIMUM_TEMP_FILE_AGE.
       
   888     $temp_new = file_save_data('');
       
   889     db_update('file_managed')
       
   890       ->fields(array('status' => 0))
       
   891       ->condition('fid', $temp_new->fid)
       
   892       ->execute();
       
   893     $this->assertTrue(file_exists($temp_new->uri), 'New temp file was created correctly.');
       
   894 
       
   895     // Permanent file that is older than DRUPAL_MAXIMUM_TEMP_FILE_AGE.
       
   896     $perm_old = file_save_data('');
       
   897     db_update('file_managed')
       
   898       ->fields(array('timestamp' => 1))
       
   899       ->condition('fid', $temp_old->fid)
       
   900       ->execute();
       
   901     $this->assertTrue(file_exists($perm_old->uri), 'Old permanent file was created correctly.');
       
   902 
       
   903     // Permanent file that is newer than DRUPAL_MAXIMUM_TEMP_FILE_AGE.
       
   904     $perm_new = file_save_data('');
       
   905     $this->assertTrue(file_exists($perm_new->uri), 'New permanent file was created correctly.');
       
   906 
       
   907     // Run cron and then ensure that only the old, temp file was deleted.
       
   908     $this->cronRun();
       
   909     $this->assertFalse(file_exists($temp_old->uri), 'Old temp file was correctly removed.');
       
   910     $this->assertTrue(file_exists($temp_new->uri), 'New temp file was correctly ignored.');
       
   911     $this->assertTrue(file_exists($perm_old->uri), 'Old permanent file was correctly ignored.');
       
   912     $this->assertTrue(file_exists($perm_new->uri), 'New permanent file was correctly ignored.');
       
   913   }
       
   914 
       
   915   /**
       
   916    * Make sure exceptions thrown on hook_cron() don't affect other modules.
       
   917    */
       
   918   function testCronExceptions() {
       
   919     variable_del('common_test_cron');
       
   920     // The common_test module throws an exception. If it isn't caught, the tests
       
   921     // won't finish successfully.
       
   922     // The common_test_cron_helper module sets the 'common_test_cron' variable.
       
   923     $this->cronRun();
       
   924     $result = variable_get('common_test_cron');
       
   925     $this->assertEqual($result, 'success', 'Cron correctly handles exceptions thrown during hook_cron() invocations.');
       
   926   }
       
   927 
       
   928   /**
       
   929    * Tests that hook_flush_caches() is not invoked on every single cron run.
       
   930    *
       
   931    * @see system_cron()
       
   932    */
       
   933   public function testCronCacheExpiration() {
       
   934     module_enable(array('system_cron_test'));
       
   935     variable_del('system_cron_test_flush_caches');
       
   936 
       
   937     // Invoke cron the first time: hook_flush_caches() should be called and then
       
   938     // get cached.
       
   939     drupal_cron_run();
       
   940     $this->assertEqual(variable_get('system_cron_test_flush_caches'), 1, 'hook_flush_caches() was invoked the first time.');
       
   941     $cache = cache_get('system_cache_tables');
       
   942     $this->assertEqual(empty($cache), FALSE, 'Cache is filled with cache table data.');
       
   943 
       
   944     // Run cron again and ensure that hook_flush_caches() is not called.
       
   945     variable_del('system_cron_test_flush_caches');
       
   946     drupal_cron_run();
       
   947     $this->assertNull(variable_get('system_cron_test_flush_caches'), 'hook_flush_caches() was not invoked the second time.');
       
   948   }
       
   949 
       
   950 }
       
   951 
       
   952 /**
       
   953  * Test execution of the cron queue.
       
   954  */
       
   955 class CronQueueTestCase extends DrupalWebTestCase {
       
   956   /**
       
   957    * Implement getInfo().
       
   958    */
       
   959   public static function getInfo() {
       
   960     return array(
       
   961       'name' => 'Cron queue functionality',
       
   962       'description' => 'Tests the cron queue runner.',
       
   963       'group' => 'System'
       
   964     );
       
   965   }
       
   966 
       
   967   function setUp() {
       
   968     parent::setUp(array('common_test', 'common_test_cron_helper', 'cron_queue_test'));
       
   969   }
       
   970 
       
   971   /**
       
   972    * Tests that exceptions thrown by workers are handled properly.
       
   973    */
       
   974   function testExceptions() {
       
   975     $queue = DrupalQueue::get('cron_queue_test_exception');
       
   976 
       
   977     // Enqueue an item for processing.
       
   978     $queue->createItem(array($this->randomName() => $this->randomName()));
       
   979 
       
   980     // Run cron; the worker for this queue should throw an exception and handle
       
   981     // it.
       
   982     $this->cronRun();
       
   983 
       
   984     // The item should be left in the queue.
       
   985     $this->assertEqual($queue->numberOfItems(), 1, 'Failing item still in the queue after throwing an exception.');
       
   986   }
       
   987 
       
   988   /**
       
   989    * Tests worker defined as a class method callable.
       
   990    */
       
   991   function testCallable() {
       
   992     $queue = DrupalQueue::get('cron_queue_test_callback');
       
   993 
       
   994     // Enqueue an item for processing.
       
   995     $queue->createItem(array($this->randomName() => $this->randomName()));
       
   996 
       
   997     // Run cron; the worker should perform the task and delete the item from the
       
   998     // queue.
       
   999     $this->cronRun();
       
  1000 
       
  1001     // The queue should be empty.
       
  1002     $this->assertEqual($queue->numberOfItems(), 0);
       
  1003   }
       
  1004 
       
  1005 }
       
  1006 
       
  1007 class AdminMetaTagTestCase extends DrupalWebTestCase {
       
  1008   /**
       
  1009    * Implement getInfo().
       
  1010    */
       
  1011   public static function getInfo() {
       
  1012     return array(
       
  1013       'name' => 'Fingerprinting meta tag',
       
  1014       'description' => 'Confirm that the fingerprinting meta tag appears as expected.',
       
  1015       'group' => 'System'
       
  1016     );
       
  1017   }
       
  1018 
       
  1019   /**
       
  1020    * Verify that the meta tag HTML is generated correctly.
       
  1021    */
       
  1022   public function testMetaTag() {
       
  1023     list($version, ) = explode('.', VERSION);
       
  1024     $string = '<meta name="Generator" content="Drupal ' . $version . ' (http://drupal.org)" />';
       
  1025     $this->drupalGet('node');
       
  1026     $this->assertRaw($string, 'Fingerprinting meta tag generated correctly.', 'System');
       
  1027   }
       
  1028 }
       
  1029 
       
  1030 /**
       
  1031  * Tests custom access denied functionality.
       
  1032  */
       
  1033 class AccessDeniedTestCase extends DrupalWebTestCase {
       
  1034   protected $admin_user;
       
  1035 
       
  1036   public static function getInfo() {
       
  1037     return array(
       
  1038       'name' => '403 functionality',
       
  1039       'description' => 'Tests page access denied functionality, including custom 403 pages.',
       
  1040       'group' => 'System'
       
  1041     );
       
  1042   }
       
  1043 
       
  1044   function setUp() {
       
  1045     parent::setUp();
       
  1046 
       
  1047     // Create an administrative user.
       
  1048     $this->admin_user = $this->drupalCreateUser(array('access administration pages', 'administer site configuration', 'administer blocks'));
       
  1049   }
       
  1050 
       
  1051   function testAccessDenied() {
       
  1052     $this->drupalGet('admin');
       
  1053     $this->assertText(t('Access denied'), 'Found the default 403 page');
       
  1054     $this->assertResponse(403);
       
  1055 
       
  1056     $this->drupalLogin($this->admin_user);
       
  1057     $edit = array(
       
  1058       'title' => $this->randomName(10),
       
  1059       'body' => array(LANGUAGE_NONE => array(array('value' => $this->randomName(100)))),
       
  1060     );
       
  1061     $node = $this->drupalCreateNode($edit);
       
  1062 
       
  1063     // Use a custom 403 page.
       
  1064     $this->drupalPost('admin/config/system/site-information', array('site_403' => 'node/' . $node->nid), t('Save configuration'));
       
  1065 
       
  1066     $this->drupalLogout();
       
  1067     $this->drupalGet('admin');
       
  1068     $this->assertText($node->title, 'Found the custom 403 page');
       
  1069 
       
  1070     // Logout and check that the user login block is shown on custom 403 pages.
       
  1071     $this->drupalLogout();
       
  1072 
       
  1073     $this->drupalGet('admin');
       
  1074     $this->assertText($node->title, 'Found the custom 403 page');
       
  1075     $this->assertText(t('User login'), 'Blocks are shown on the custom 403 page');
       
  1076 
       
  1077     // Log back in and remove the custom 403 page.
       
  1078     $this->drupalLogin($this->admin_user);
       
  1079     $this->drupalPost('admin/config/system/site-information', array('site_403' => ''), t('Save configuration'));
       
  1080 
       
  1081     // Logout and check that the user login block is shown on default 403 pages.
       
  1082     $this->drupalLogout();
       
  1083 
       
  1084     $this->drupalGet('admin');
       
  1085     $this->assertText(t('Access denied'), 'Found the default 403 page');
       
  1086     $this->assertResponse(403);
       
  1087     $this->assertText(t('User login'), 'Blocks are shown on the default 403 page');
       
  1088 
       
  1089     // Log back in, set the custom 403 page to /user and remove the block
       
  1090     $this->drupalLogin($this->admin_user);
       
  1091     variable_set('site_403', 'user');
       
  1092     $this->drupalPost('admin/structure/block', array('blocks[user_login][region]' => '-1'), t('Save blocks'));
       
  1093 
       
  1094     // Check that we can log in from the 403 page.
       
  1095     $this->drupalLogout();
       
  1096     $edit = array(
       
  1097       'name' => $this->admin_user->name,
       
  1098       'pass' => $this->admin_user->pass_raw,
       
  1099     );
       
  1100     $this->drupalPost('admin/config/system/site-information', $edit, t('Log in'));
       
  1101 
       
  1102     // Check that we're still on the same page.
       
  1103     $this->assertText(t('Site information'));
       
  1104   }
       
  1105 }
       
  1106 
       
  1107 class PageNotFoundTestCase extends DrupalWebTestCase {
       
  1108   protected $admin_user;
       
  1109 
       
  1110   /**
       
  1111    * Implement getInfo().
       
  1112    */
       
  1113   public static function getInfo() {
       
  1114     return array(
       
  1115       'name' => '404 functionality',
       
  1116       'description' => "Tests page not found functionality, including custom 404 pages.",
       
  1117       'group' => 'System'
       
  1118     );
       
  1119   }
       
  1120 
       
  1121   /**
       
  1122    * Implement setUp().
       
  1123    */
       
  1124   function setUp() {
       
  1125     parent::setUp();
       
  1126 
       
  1127     // Create an administrative user.
       
  1128     $this->admin_user = $this->drupalCreateUser(array('administer site configuration'));
       
  1129     $this->drupalLogin($this->admin_user);
       
  1130   }
       
  1131 
       
  1132   function testPageNotFound() {
       
  1133     $this->drupalGet($this->randomName(10));
       
  1134     $this->assertText(t('Page not found'), 'Found the default 404 page');
       
  1135 
       
  1136     $edit = array(
       
  1137       'title' => $this->randomName(10),
       
  1138       'body' => array(LANGUAGE_NONE => array(array('value' => $this->randomName(100)))),
       
  1139     );
       
  1140     $node = $this->drupalCreateNode($edit);
       
  1141 
       
  1142     // As node IDs must be integers, make sure requests for non-integer IDs
       
  1143     // return a page not found error.
       
  1144     $this->drupalGet('node/invalid');
       
  1145     $this->assertResponse(404);
       
  1146 
       
  1147     // Use a custom 404 page.
       
  1148     $this->drupalPost('admin/config/system/site-information', array('site_404' => 'node/' . $node->nid), t('Save configuration'));
       
  1149 
       
  1150     $this->drupalGet($this->randomName(10));
       
  1151     $this->assertText($node->title, 'Found the custom 404 page');
       
  1152   }
       
  1153 }
       
  1154 
       
  1155 /**
       
  1156  * Tests site maintenance functionality.
       
  1157  */
       
  1158 class SiteMaintenanceTestCase extends DrupalWebTestCase {
       
  1159   protected $admin_user;
       
  1160 
       
  1161   public static function getInfo() {
       
  1162     return array(
       
  1163       'name' => 'Site maintenance mode functionality',
       
  1164       'description' => 'Test access to site while in maintenance mode.',
       
  1165       'group' => 'System',
       
  1166     );
       
  1167   }
       
  1168 
       
  1169   function setUp() {
       
  1170     parent::setUp();
       
  1171 
       
  1172     // Create a user allowed to access site in maintenance mode.
       
  1173     $this->user = $this->drupalCreateUser(array('access site in maintenance mode'));
       
  1174     // Create an administrative user.
       
  1175     $this->admin_user = $this->drupalCreateUser(array('administer site configuration', 'access site in maintenance mode'));
       
  1176     $this->drupalLogin($this->admin_user);
       
  1177   }
       
  1178 
       
  1179   /**
       
  1180    * Verify site maintenance mode functionality.
       
  1181    */
       
  1182   function testSiteMaintenance() {
       
  1183     // Turn on maintenance mode.
       
  1184     $edit = array(
       
  1185       'maintenance_mode' => 1,
       
  1186     );
       
  1187     $this->drupalPost('admin/config/development/maintenance', $edit, t('Save configuration'));
       
  1188 
       
  1189     $admin_message = t('Operating in maintenance mode. <a href="@url">Go online.</a>', array('@url' => url('admin/config/development/maintenance')));
       
  1190     $user_message = t('Operating in maintenance mode.');
       
  1191     $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')));
       
  1192 
       
  1193     $this->drupalGet('');
       
  1194     $this->assertRaw($admin_message, 'Found the site maintenance mode message.');
       
  1195 
       
  1196     // Logout and verify that offline message is displayed.
       
  1197     $this->drupalLogout();
       
  1198     $this->drupalGet('');
       
  1199     $this->assertText($offline_message);
       
  1200     $this->drupalGet('node');
       
  1201     $this->assertText($offline_message);
       
  1202     $this->drupalGet('user/register');
       
  1203     $this->assertText($offline_message);
       
  1204 
       
  1205     // Verify that user is able to log in.
       
  1206     $this->drupalGet('user');
       
  1207     $this->assertNoText($offline_message);
       
  1208     $this->drupalGet('user/login');
       
  1209     $this->assertNoText($offline_message);
       
  1210 
       
  1211     // Log in user and verify that maintenance mode message is displayed
       
  1212     // directly after login.
       
  1213     $edit = array(
       
  1214       'name' => $this->user->name,
       
  1215       'pass' => $this->user->pass_raw,
       
  1216     );
       
  1217     $this->drupalPost(NULL, $edit, t('Log in'));
       
  1218     $this->assertText($user_message);
       
  1219 
       
  1220     // Log in administrative user and configure a custom site offline message.
       
  1221     $this->drupalLogout();
       
  1222     $this->drupalLogin($this->admin_user);
       
  1223     $this->drupalGet('admin/config/development/maintenance');
       
  1224     $this->assertNoRaw($admin_message, 'Site maintenance mode message not displayed.');
       
  1225 
       
  1226     $offline_message = 'Sorry, not online.';
       
  1227     $edit = array(
       
  1228       'maintenance_mode_message' => $offline_message,
       
  1229     );
       
  1230     $this->drupalPost(NULL, $edit, t('Save configuration'));
       
  1231 
       
  1232     // Logout and verify that custom site offline message is displayed.
       
  1233     $this->drupalLogout();
       
  1234     $this->drupalGet('');
       
  1235     $this->assertRaw($offline_message, 'Found the site offline message.');
       
  1236 
       
  1237     // Verify that custom site offline message is not displayed on user/password.
       
  1238     $this->drupalGet('user/password');
       
  1239     $this->assertText(t('Username or e-mail address'), 'Anonymous users can access user/password');
       
  1240 
       
  1241     // Submit password reset form.
       
  1242     $edit = array(
       
  1243       'name' => $this->user->name,
       
  1244     );
       
  1245     $this->drupalPost('user/password', $edit, t('E-mail new password'));
       
  1246     $mails = $this->drupalGetMails();
       
  1247     $start = strpos($mails[0]['body'], 'user/reset/'. $this->user->uid);
       
  1248     $path = substr($mails[0]['body'], $start, 66 + strlen($this->user->uid));
       
  1249 
       
  1250     // Log in with temporary login link.
       
  1251     $this->drupalPost($path, array(), t('Log in'));
       
  1252     $this->assertText($user_message);
       
  1253   }
       
  1254 }
       
  1255 
       
  1256 /**
       
  1257  * Tests generic date and time handling capabilities of Drupal.
       
  1258  */
       
  1259 class DateTimeFunctionalTest extends DrupalWebTestCase {
       
  1260   public static function getInfo() {
       
  1261     return array(
       
  1262       'name' => 'Date and time',
       
  1263       'description' => 'Configure date and time settings. Test date formatting and time zone handling, including daylight saving time.',
       
  1264       'group' => 'System',
       
  1265     );
       
  1266   }
       
  1267 
       
  1268   function setUp() {
       
  1269     parent::setUp(array('locale'));
       
  1270 
       
  1271     // Create admin user and log in admin user.
       
  1272     $this->admin_user = $this->drupalCreateUser(array('administer site configuration'));
       
  1273     $this->drupalLogin($this->admin_user);
       
  1274   }
       
  1275 
       
  1276 
       
  1277   /**
       
  1278    * Test time zones and DST handling.
       
  1279    */
       
  1280   function testTimeZoneHandling() {
       
  1281     // Setup date/time settings for Honolulu time.
       
  1282     variable_set('date_default_timezone', 'Pacific/Honolulu');
       
  1283     variable_set('configurable_timezones', 0);
       
  1284     variable_set('date_format_medium', 'Y-m-d H:i:s O');
       
  1285 
       
  1286     // Create some nodes with different authored-on dates.
       
  1287     $date1 = '2007-01-31 21:00:00 -1000';
       
  1288     $date2 = '2007-07-31 21:00:00 -1000';
       
  1289     $node1 = $this->drupalCreateNode(array('created' => strtotime($date1), 'type' => 'article'));
       
  1290     $node2 = $this->drupalCreateNode(array('created' => strtotime($date2), 'type' => 'article'));
       
  1291 
       
  1292     // Confirm date format and time zone.
       
  1293     $this->drupalGet("node/$node1->nid");
       
  1294     $this->assertText('2007-01-31 21:00:00 -1000', 'Date should be identical, with GMT offset of -10 hours.');
       
  1295     $this->drupalGet("node/$node2->nid");
       
  1296     $this->assertText('2007-07-31 21:00:00 -1000', 'Date should be identical, with GMT offset of -10 hours.');
       
  1297 
       
  1298     // Set time zone to Los Angeles time.
       
  1299     variable_set('date_default_timezone', 'America/Los_Angeles');
       
  1300 
       
  1301     // Confirm date format and time zone.
       
  1302     $this->drupalGet("node/$node1->nid");
       
  1303     $this->assertText('2007-01-31 23:00:00 -0800', 'Date should be two hours ahead, with GMT offset of -8 hours.');
       
  1304     $this->drupalGet("node/$node2->nid");
       
  1305     $this->assertText('2007-08-01 00:00:00 -0700', 'Date should be three hours ahead, with GMT offset of -7 hours.');
       
  1306   }
       
  1307 
       
  1308   /**
       
  1309    * Test date type configuration.
       
  1310    */
       
  1311   function testDateTypeConfiguration() {
       
  1312     // Confirm system date types appear.
       
  1313     $this->drupalGet('admin/config/regional/date-time');
       
  1314     $this->assertText(t('Medium'), 'System date types appear in date type list.');
       
  1315     $this->assertNoRaw('href="/admin/config/regional/date-time/types/medium/delete"', 'No delete link appear for system date types.');
       
  1316 
       
  1317     // Add custom date type.
       
  1318     $this->clickLink(t('Add date type'));
       
  1319     $date_type = strtolower($this->randomName(8));
       
  1320     $machine_name = 'machine_' . $date_type;
       
  1321     $date_format = 'd.m.Y - H:i';
       
  1322     $edit = array(
       
  1323       'date_type' => $date_type,
       
  1324       'machine_name' => $machine_name,
       
  1325       'date_format' => $date_format,
       
  1326     );
       
  1327     $this->drupalPost('admin/config/regional/date-time/types/add', $edit, t('Add date type'));
       
  1328     $this->assertEqual($this->getUrl(), url('admin/config/regional/date-time', array('absolute' => TRUE)), 'Correct page redirection.');
       
  1329     $this->assertText(t('New date type added successfully.'), 'Date type added confirmation message appears.');
       
  1330     $this->assertText($date_type, 'Custom date type appears in the date type list.');
       
  1331     $this->assertText(t('delete'), 'Delete link for custom date type appears.');
       
  1332 
       
  1333     // Delete custom date type.
       
  1334     $this->clickLink(t('delete'));
       
  1335     $this->drupalPost('admin/config/regional/date-time/types/' . $machine_name . '/delete', array(), t('Remove'));
       
  1336     $this->assertEqual($this->getUrl(), url('admin/config/regional/date-time', array('absolute' => TRUE)), 'Correct page redirection.');
       
  1337     $this->assertText(t('Removed date type ' . $date_type), 'Custom date type removed.');
       
  1338   }
       
  1339 
       
  1340   /**
       
  1341    * Test date format configuration.
       
  1342    */
       
  1343   function testDateFormatConfiguration() {
       
  1344     // Confirm 'no custom date formats available' message appears.
       
  1345     $this->drupalGet('admin/config/regional/date-time/formats');
       
  1346     $this->assertText(t('No custom date formats available.'), 'No custom date formats message appears.');
       
  1347 
       
  1348     // Add custom date format.
       
  1349     $this->clickLink(t('Add format'));
       
  1350     $edit = array(
       
  1351       'date_format' => 'Y',
       
  1352     );
       
  1353     $this->drupalPost('admin/config/regional/date-time/formats/add', $edit, t('Add format'));
       
  1354     $this->assertEqual($this->getUrl(), url('admin/config/regional/date-time/formats', array('absolute' => TRUE)), 'Correct page redirection.');
       
  1355     $this->assertNoText(t('No custom date formats available.'), 'No custom date formats message does not appear.');
       
  1356     $this->assertText(t('Custom date format added.'), 'Custom date format added.');
       
  1357 
       
  1358     // Ensure custom date format appears in date type configuration options.
       
  1359     $this->drupalGet('admin/config/regional/date-time');
       
  1360     $this->assertRaw('<option value="Y">', 'Custom date format appears in options.');
       
  1361 
       
  1362     // Edit custom date format.
       
  1363     $this->drupalGet('admin/config/regional/date-time/formats');
       
  1364     $this->clickLink(t('edit'));
       
  1365     $edit = array(
       
  1366       'date_format' => 'Y m',
       
  1367     );
       
  1368     $this->drupalPost($this->getUrl(), $edit, t('Save format'));
       
  1369     $this->assertEqual($this->getUrl(), url('admin/config/regional/date-time/formats', array('absolute' => TRUE)), 'Correct page redirection.');
       
  1370     $this->assertText(t('Custom date format updated.'), 'Custom date format successfully updated.');
       
  1371 
       
  1372     // Check that ajax callback is protected by CSRF token.
       
  1373     $this->drupalGet('admin/config/regional/date-time/formats/lookup', array('query' => array('format' => 'Y m d')));
       
  1374     $this->assertResponse(403, 'Access denied with no token');
       
  1375     $this->drupalGet('admin/config/regional/date-time/formats/lookup', array('query' => array('token' => 'invalid', 'format' => 'Y m d')));
       
  1376     $this->assertResponse(403, 'Access denied with invalid token');
       
  1377     $this->drupalGet('admin/config/regional/date-time/formats');
       
  1378     $this->clickLink(t('edit'));
       
  1379     $settings = $this->drupalGetSettings();
       
  1380     $lookup_url = $settings['dateTime']['date-format']['lookup'];
       
  1381     preg_match('/token=([^&]+)/', $lookup_url, $matches);
       
  1382     $this->assertFalse(empty($matches[1]), 'Found token value');
       
  1383     $this->drupalGet('admin/config/regional/date-time/formats/lookup', array('query' => array('token' => $matches[1], 'format' => 'Y m d')));
       
  1384     $this->assertResponse(200, 'Access allowed with valid token');
       
  1385     $this->assertText(format_date(time(), 'custom', 'Y m d'));
       
  1386 
       
  1387     // Delete custom date format.
       
  1388     $this->drupalGet('admin/config/regional/date-time/formats');
       
  1389     $this->clickLink(t('delete'));
       
  1390     $this->drupalPost($this->getUrl(), array(), t('Remove'));
       
  1391     $this->assertEqual($this->getUrl(), url('admin/config/regional/date-time/formats', array('absolute' => TRUE)), 'Correct page redirection.');
       
  1392     $this->assertText(t('Removed date format'), 'Custom date format removed successfully.');
       
  1393   }
       
  1394 
       
  1395   /**
       
  1396    * Test if the date formats are stored properly.
       
  1397    */
       
  1398   function testDateFormatStorage() {
       
  1399     $date_format = array(
       
  1400       'type' => 'short',
       
  1401       'format' => 'dmYHis',
       
  1402       'locked' => 0,
       
  1403       'is_new' => 1,
       
  1404     );
       
  1405     system_date_format_save($date_format);
       
  1406 
       
  1407     $format = db_select('date_formats', 'df')
       
  1408       ->fields('df', array('format'))
       
  1409       ->condition('type', 'short')
       
  1410       ->condition('format', 'dmYHis')
       
  1411       ->execute()
       
  1412       ->fetchField();
       
  1413     $this->verbose($format);
       
  1414     $this->assertEqual('dmYHis', $format, 'Unlocalized date format resides in general table.');
       
  1415 
       
  1416     $format = db_select('date_format_locale', 'dfl')
       
  1417       ->fields('dfl', array('format'))
       
  1418       ->condition('type', 'short')
       
  1419       ->condition('format', 'dmYHis')
       
  1420       ->execute()
       
  1421       ->fetchField();
       
  1422     $this->assertFalse($format, 'Unlocalized date format resides not in localized table.');
       
  1423 
       
  1424     // Enable German language
       
  1425     locale_add_language('de', NULL, NULL, LANGUAGE_LTR, '', '', TRUE, 'en');
       
  1426 
       
  1427     $date_format = array(
       
  1428       'type' => 'short',
       
  1429       'format' => 'YMDHis',
       
  1430       'locales' => array('de', 'tr'),
       
  1431       'locked' => 0,
       
  1432       'is_new' => 1,
       
  1433     );
       
  1434     system_date_format_save($date_format);
       
  1435 
       
  1436     $format = db_select('date_format_locale', 'dfl')
       
  1437       ->fields('dfl', array('format'))
       
  1438       ->condition('type', 'short')
       
  1439       ->condition('format', 'YMDHis')
       
  1440       ->condition('language', 'de')
       
  1441       ->execute()
       
  1442       ->fetchField();
       
  1443     $this->assertEqual('YMDHis', $format, 'Localized date format resides in localized table.');
       
  1444 
       
  1445     $format = db_select('date_formats', 'df')
       
  1446       ->fields('df', array('format'))
       
  1447       ->condition('type', 'short')
       
  1448       ->condition('format', 'YMDHis')
       
  1449       ->execute()
       
  1450       ->fetchField();
       
  1451     $this->assertEqual('YMDHis', $format, 'Localized date format resides in general table too.');
       
  1452 
       
  1453     $format = db_select('date_format_locale', 'dfl')
       
  1454       ->fields('dfl', array('format'))
       
  1455       ->condition('type', 'short')
       
  1456       ->condition('format', 'YMDHis')
       
  1457       ->condition('language', 'tr')
       
  1458       ->execute()
       
  1459       ->fetchColumn();
       
  1460     $this->assertFalse($format, 'Localized date format for disabled language is ignored.');
       
  1461   }
       
  1462 }
       
  1463 
       
  1464 /**
       
  1465  * Tests date format configuration.
       
  1466  */
       
  1467 class DateFormatTestCase extends DrupalWebTestCase {
       
  1468   public static function getInfo() {
       
  1469     return array(
       
  1470       'name' => 'Date format',
       
  1471       'description' => 'Test date format configuration and defaults.',
       
  1472       'group' => 'System',
       
  1473     );
       
  1474   }
       
  1475 
       
  1476   function setUp() {
       
  1477     parent::setUp();
       
  1478 
       
  1479     // Create admin user and log in admin user.
       
  1480     $this->admin_user = $this->drupalCreateUser(array('administer site configuration'));
       
  1481     $this->drupalLogin($this->admin_user);
       
  1482   }
       
  1483 
       
  1484   /**
       
  1485    * Test the default date type formats are consistent.
       
  1486    */
       
  1487   function testDefaultDateFormats() {
       
  1488     // These are the default format values from format_date().
       
  1489     $default_formats = array(
       
  1490       'short' => 'm/d/Y - H:i',
       
  1491       'medium' => 'D, m/d/Y - H:i',
       
  1492       'long' => 'l, F j, Y - H:i',
       
  1493     );
       
  1494 
       
  1495     // Clear the date format variables.
       
  1496     variable_del('date_format_short');
       
  1497     variable_del('date_format_medium');
       
  1498     variable_del('date_format_long');
       
  1499 
       
  1500     $this->drupalGet('admin/config/regional/date-time');
       
  1501 
       
  1502     foreach ($default_formats as $format_name => $format_value) {
       
  1503       $id = 'edit-date-format-' . $format_name;
       
  1504       // Check that the configuration fields match the default format.
       
  1505       $this->assertOptionSelected(
       
  1506         $id,
       
  1507         $format_value,
       
  1508         format_string('The @type format type matches the expected format @format.',
       
  1509         array(
       
  1510           '@type' => $format_name,
       
  1511           '@format' => $format_value,
       
  1512         )
       
  1513       ));
       
  1514     }
       
  1515   }
       
  1516 }
       
  1517 
       
  1518 class PageTitleFiltering extends DrupalWebTestCase {
       
  1519   protected $content_user;
       
  1520   protected $saved_title;
       
  1521 
       
  1522   /**
       
  1523    * Implement getInfo().
       
  1524    */
       
  1525   public static function getInfo() {
       
  1526     return array(
       
  1527       'name' => 'HTML in page titles',
       
  1528       'description' => 'Tests correct handling or conversion by drupal_set_title() and drupal_get_title() and checks the correct escaping of site name and slogan.',
       
  1529       'group' => 'System'
       
  1530     );
       
  1531   }
       
  1532 
       
  1533   /**
       
  1534    * Implement setUp().
       
  1535    */
       
  1536   function setUp() {
       
  1537     parent::setUp();
       
  1538 
       
  1539     $this->content_user = $this->drupalCreateUser(array('create page content', 'access content', 'administer themes', 'administer site configuration'));
       
  1540     $this->drupalLogin($this->content_user);
       
  1541     $this->saved_title = drupal_get_title();
       
  1542   }
       
  1543 
       
  1544   /**
       
  1545    * Reset page title.
       
  1546    */
       
  1547   function tearDown() {
       
  1548     // Restore the page title.
       
  1549     drupal_set_title($this->saved_title, PASS_THROUGH);
       
  1550 
       
  1551     parent::tearDown();
       
  1552   }
       
  1553 
       
  1554   /**
       
  1555    * Tests the handling of HTML by drupal_set_title() and drupal_get_title()
       
  1556    */
       
  1557   function testTitleTags() {
       
  1558     $title = "string with <em>HTML</em>";
       
  1559     // drupal_set_title's $filter is CHECK_PLAIN by default, so the title should be
       
  1560     // returned with check_plain().
       
  1561     drupal_set_title($title, CHECK_PLAIN);
       
  1562     $this->assertTrue(strpos(drupal_get_title(), '<em>') === FALSE, 'Tags in title converted to entities when $output is CHECK_PLAIN.');
       
  1563     // drupal_set_title's $filter is passed as PASS_THROUGH, so the title should be
       
  1564     // returned with HTML.
       
  1565     drupal_set_title($title, PASS_THROUGH);
       
  1566     $this->assertTrue(strpos(drupal_get_title(), '<em>') !== FALSE, 'Tags in title are not converted to entities when $output is PASS_THROUGH.');
       
  1567     // Generate node content.
       
  1568     $langcode = LANGUAGE_NONE;
       
  1569     $edit = array(
       
  1570       "title" => '!SimpleTest! ' . $title . $this->randomName(20),
       
  1571       "body[$langcode][0][value]" => '!SimpleTest! test body' . $this->randomName(200),
       
  1572     );
       
  1573     // Create the node with HTML in the title.
       
  1574     $this->drupalPost('node/add/page', $edit, t('Save'));
       
  1575 
       
  1576     $node = $this->drupalGetNodeByTitle($edit["title"]);
       
  1577     $this->assertNotNull($node, 'Node created and found in database');
       
  1578     $this->drupalGet("node/" . $node->nid);
       
  1579     $this->assertText(check_plain($edit["title"]), 'Check to make sure tags in the node title are converted.');
       
  1580   }
       
  1581   /**
       
  1582    * Test if the title of the site is XSS proof.
       
  1583    */
       
  1584   function testTitleXSS() {
       
  1585     // Set some title with JavaScript and HTML chars to escape.
       
  1586     $title = '</title><script type="text/javascript">alert("Title XSS!");</script> & < > " \' ';
       
  1587     $title_filtered = check_plain($title);
       
  1588 
       
  1589     $slogan = '<script type="text/javascript">alert("Slogan XSS!");</script>';
       
  1590     $slogan_filtered = filter_xss_admin($slogan);
       
  1591 
       
  1592     // Activate needed appearance settings.
       
  1593     $edit = array(
       
  1594       'toggle_name'           => TRUE,
       
  1595       'toggle_slogan'         => TRUE,
       
  1596       'toggle_main_menu'      => TRUE,
       
  1597       'toggle_secondary_menu' => TRUE,
       
  1598     );
       
  1599     $this->drupalPost('admin/appearance/settings', $edit, t('Save configuration'));
       
  1600 
       
  1601     // Set title and slogan.
       
  1602     $edit = array(
       
  1603       'site_name'    => $title,
       
  1604       'site_slogan'  => $slogan,
       
  1605     );
       
  1606     $this->drupalPost('admin/config/system/site-information', $edit, t('Save configuration'));
       
  1607 
       
  1608     // Load frontpage.
       
  1609     $this->drupalGet('');
       
  1610 
       
  1611     // Test the title.
       
  1612     $this->assertNoRaw($title, 'Check for the unfiltered version of the title.');
       
  1613     // Adding </title> so we do not test the escaped version from drupal_set_title().
       
  1614     $this->assertRaw($title_filtered . '</title>', 'Check for the filtered version of the title.');
       
  1615 
       
  1616     // Test the slogan.
       
  1617     $this->assertNoRaw($slogan, 'Check for the unfiltered version of the slogan.');
       
  1618     $this->assertRaw($slogan_filtered, 'Check for the filtered version of the slogan.');
       
  1619   }
       
  1620 }
       
  1621 
       
  1622 /**
       
  1623  * Test front page functionality and administration.
       
  1624  */
       
  1625 class FrontPageTestCase extends DrupalWebTestCase {
       
  1626 
       
  1627   public static function getInfo() {
       
  1628     return array(
       
  1629       'name' => 'Front page',
       
  1630       'description' => 'Tests front page functionality and administration.',
       
  1631       'group' => 'System',
       
  1632     );
       
  1633   }
       
  1634 
       
  1635   function setUp() {
       
  1636     parent::setUp('system_test');
       
  1637 
       
  1638     // Create admin user, log in admin user, and create one node.
       
  1639     $this->admin_user = $this->drupalCreateUser(array('access content', 'administer site configuration'));
       
  1640     $this->drupalLogin($this->admin_user);
       
  1641     $this->node_path = "node/" . $this->drupalCreateNode(array('promote' => 1))->nid;
       
  1642 
       
  1643     // Enable front page logging in system_test.module.
       
  1644     variable_set('front_page_output', 1);
       
  1645   }
       
  1646 
       
  1647   /**
       
  1648    * Test front page functionality.
       
  1649    */
       
  1650   function testDrupalIsFrontPage() {
       
  1651     $this->drupalGet('');
       
  1652     $this->assertText(t('On front page.'), 'Path is the front page.');
       
  1653     $this->drupalGet('node');
       
  1654     $this->assertText(t('On front page.'), 'Path is the front page.');
       
  1655     $this->drupalGet($this->node_path);
       
  1656     $this->assertNoText(t('On front page.'), 'Path is not the front page.');
       
  1657 
       
  1658     // Change the front page to an invalid path.
       
  1659     $edit = array('site_frontpage' => 'kittens');
       
  1660     $this->drupalPost('admin/config/system/site-information', $edit, t('Save configuration'));
       
  1661     $this->assertText(t("The path '@path' is either invalid or you do not have access to it.", array('@path' => $edit['site_frontpage'])));
       
  1662 
       
  1663     // Change the front page to a valid path.
       
  1664     $edit['site_frontpage'] = $this->node_path;
       
  1665     $this->drupalPost('admin/config/system/site-information', $edit, t('Save configuration'));
       
  1666     $this->assertText(t('The configuration options have been saved.'), 'The front page path has been saved.');
       
  1667 
       
  1668     $this->drupalGet('');
       
  1669     $this->assertText(t('On front page.'), 'Path is the front page.');
       
  1670     $this->drupalGet('node');
       
  1671     $this->assertNoText(t('On front page.'), 'Path is not the front page.');
       
  1672     $this->drupalGet($this->node_path);
       
  1673     $this->assertText(t('On front page.'), 'Path is the front page.');
       
  1674   }
       
  1675 }
       
  1676 
       
  1677 class SystemBlockTestCase extends DrupalWebTestCase {
       
  1678   protected $profile = 'testing';
       
  1679 
       
  1680   public static function getInfo() {
       
  1681     return array(
       
  1682       'name' => 'Block functionality',
       
  1683       'description' => 'Configure and move powered-by block.',
       
  1684       'group' => 'System',
       
  1685     );
       
  1686   }
       
  1687 
       
  1688   function setUp() {
       
  1689     parent::setUp('block');
       
  1690 
       
  1691     // Create and login user
       
  1692     $admin_user = $this->drupalCreateUser(array('administer blocks', 'access administration pages'));
       
  1693     $this->drupalLogin($admin_user);
       
  1694   }
       
  1695 
       
  1696   /**
       
  1697    * Test displaying and hiding the powered-by and help blocks.
       
  1698    */
       
  1699   function testSystemBlocks() {
       
  1700     // Set block title and some settings to confirm that the interface is available.
       
  1701     $this->drupalPost('admin/structure/block/manage/system/powered-by/configure', array('title' => $this->randomName(8)), t('Save block'));
       
  1702     $this->assertText(t('The block configuration has been saved.'), t('Block configuration set.'));
       
  1703 
       
  1704     // Set the powered-by block to the footer region.
       
  1705     $edit = array();
       
  1706     $edit['blocks[system_powered-by][region]'] = 'footer';
       
  1707     $edit['blocks[system_main][region]'] = 'content';
       
  1708     $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
       
  1709     $this->assertText(t('The block settings have been updated.'), t('Block successfully moved to footer region.'));
       
  1710 
       
  1711     // Confirm that the block is being displayed.
       
  1712     $this->drupalGet('node');
       
  1713     $this->assertRaw('id="block-system-powered-by"', t('Block successfully being displayed on the page.'));
       
  1714 
       
  1715     // Set the block to the disabled region.
       
  1716     $edit = array();
       
  1717     $edit['blocks[system_powered-by][region]'] = '-1';
       
  1718     $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
       
  1719 
       
  1720     // Confirm that the block is hidden.
       
  1721     $this->assertNoRaw('id="block-system-powered-by"', t('Block no longer appears on page.'));
       
  1722 
       
  1723     // For convenience of developers, set the block to its default settings.
       
  1724     $edit = array();
       
  1725     $edit['blocks[system_powered-by][region]'] = 'footer';
       
  1726     $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
       
  1727     $this->drupalPost('admin/structure/block/manage/system/powered-by/configure', array('title' => ''), t('Save block'));
       
  1728 
       
  1729     // Set the help block to the help region.
       
  1730     $edit = array();
       
  1731     $edit['blocks[system_help][region]'] = 'help';
       
  1732     $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
       
  1733 
       
  1734     // Test displaying the help block with block caching enabled.
       
  1735     variable_set('block_cache', TRUE);
       
  1736     $this->drupalGet('admin/structure/block/add');
       
  1737     $this->assertRaw(t('Use this page to create a new custom block.'));
       
  1738     $this->drupalGet('admin/index');
       
  1739     $this->assertRaw(t('This page shows you all available administration tasks for each module.'));
       
  1740   }
       
  1741 }
       
  1742 
       
  1743 /**
       
  1744  * Test main content rendering fallback provided by system module.
       
  1745  */
       
  1746 class SystemMainContentFallback extends DrupalWebTestCase {
       
  1747   protected $admin_user;
       
  1748   protected $web_user;
       
  1749 
       
  1750   public static function getInfo() {
       
  1751     return array(
       
  1752       'name' => 'Main content rendering fallback',
       
  1753       'description' => ' Test system module main content rendering fallback.',
       
  1754       'group' => 'System',
       
  1755     );
       
  1756   }
       
  1757 
       
  1758   function setUp() {
       
  1759     parent::setUp('system_test');
       
  1760 
       
  1761     // Create and login admin user.
       
  1762     $this->admin_user = $this->drupalCreateUser(array(
       
  1763       'access administration pages',
       
  1764       'administer site configuration',
       
  1765       'administer modules',
       
  1766       'administer blocks',
       
  1767       'administer nodes',
       
  1768     ));
       
  1769     $this->drupalLogin($this->admin_user);
       
  1770 
       
  1771     // Create a web user.
       
  1772     $this->web_user = $this->drupalCreateUser(array('access user profiles', 'access content'));
       
  1773   }
       
  1774 
       
  1775   /**
       
  1776    * Test availability of main content.
       
  1777    */
       
  1778   function testMainContentFallback() {
       
  1779     $edit = array();
       
  1780     // Disable the dashboard module, which depends on the block module.
       
  1781     $edit['modules[Core][dashboard][enable]'] = FALSE;
       
  1782     $this->drupalPost('admin/modules', $edit, t('Save configuration'));
       
  1783     $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.');
       
  1784     // Disable the block module.
       
  1785     $edit['modules[Core][block][enable]'] = FALSE;
       
  1786     $this->drupalPost('admin/modules', $edit, t('Save configuration'));
       
  1787     $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.');
       
  1788     module_list(TRUE);
       
  1789     $this->assertFalse(module_exists('block'), 'Block module disabled.');
       
  1790 
       
  1791     // At this point, no region is filled and fallback should be triggered.
       
  1792     $this->drupalGet('admin/config/system/site-information');
       
  1793     $this->assertField('site_name', 'Admin interface still available.');
       
  1794 
       
  1795     // Fallback should not trigger when another module is handling content.
       
  1796     $this->drupalGet('system-test/main-content-handling');
       
  1797     $this->assertRaw('id="system-test-content"', 'Content handled by another module');
       
  1798     $this->assertText(t('Content to test main content fallback'), 'Main content still displayed.');
       
  1799 
       
  1800     // Fallback should trigger when another module
       
  1801     // indicates that it is not handling the content.
       
  1802     $this->drupalGet('system-test/main-content-fallback');
       
  1803     $this->assertText(t('Content to test main content fallback'), 'Main content fallback properly triggers.');
       
  1804 
       
  1805     // Fallback should not trigger when another module is handling content.
       
  1806     // Note that this test ensures that no duplicate
       
  1807     // content gets created by the fallback.
       
  1808     $this->drupalGet('system-test/main-content-duplication');
       
  1809     $this->assertNoText(t('Content to test main content fallback'), 'Main content not duplicated.');
       
  1810 
       
  1811     // Request a user* page and see if it is displayed.
       
  1812     $this->drupalLogin($this->web_user);
       
  1813     $this->drupalGet('user/' . $this->web_user->uid . '/edit');
       
  1814     $this->assertField('mail', 'User interface still available.');
       
  1815 
       
  1816     // Enable the block module again.
       
  1817     $this->drupalLogin($this->admin_user);
       
  1818     $edit = array();
       
  1819     $edit['modules[Core][block][enable]'] = 'block';
       
  1820     $this->drupalPost('admin/modules', $edit, t('Save configuration'));
       
  1821     $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.');
       
  1822     module_list(TRUE);
       
  1823     $this->assertTrue(module_exists('block'), 'Block module re-enabled.');
       
  1824   }
       
  1825 }
       
  1826 
       
  1827 /**
       
  1828  * Tests for the theme interface functionality.
       
  1829  */
       
  1830 class SystemThemeFunctionalTest extends DrupalWebTestCase {
       
  1831   public static function getInfo() {
       
  1832     return array(
       
  1833       'name' => 'Theme interface functionality',
       
  1834       'description' => 'Tests the theme interface functionality by enabling and switching themes, and using an administration theme.',
       
  1835       'group' => 'System',
       
  1836     );
       
  1837   }
       
  1838 
       
  1839   function setUp() {
       
  1840     parent::setUp();
       
  1841 
       
  1842     $this->admin_user = $this->drupalCreateUser(array('access administration pages', 'view the administration theme', 'administer themes', 'bypass node access', 'administer blocks'));
       
  1843     $this->drupalLogin($this->admin_user);
       
  1844     $this->node = $this->drupalCreateNode();
       
  1845   }
       
  1846 
       
  1847   /**
       
  1848    * Test the theme settings form.
       
  1849    */
       
  1850   function testThemeSettings() {
       
  1851     // Specify a filesystem path to be used for the logo.
       
  1852     $file = current($this->drupalGetTestFiles('image'));
       
  1853     $file_relative = strtr($file->uri, array('public:/' => variable_get('file_public_path', conf_path() . '/files')));
       
  1854     $default_theme_path = 'themes/stark';
       
  1855 
       
  1856     $supported_paths = array(
       
  1857       // Raw stream wrapper URI.
       
  1858       $file->uri => array(
       
  1859         'form' => file_uri_target($file->uri),
       
  1860         'src' => file_create_url($file->uri),
       
  1861       ),
       
  1862       // Relative path within the public filesystem.
       
  1863       file_uri_target($file->uri) => array(
       
  1864         'form' => file_uri_target($file->uri),
       
  1865         'src' => file_create_url($file->uri),
       
  1866       ),
       
  1867       // Relative path to a public file.
       
  1868       $file_relative => array(
       
  1869         'form' => $file_relative,
       
  1870         'src' => file_create_url($file->uri),
       
  1871       ),
       
  1872       // Relative path to an arbitrary file.
       
  1873       'misc/druplicon.png' => array(
       
  1874         'form' => 'misc/druplicon.png',
       
  1875         'src' => $GLOBALS['base_url'] . '/' . 'misc/druplicon.png',
       
  1876       ),
       
  1877       // Relative path to a file in a theme.
       
  1878       $default_theme_path . '/logo.png' => array(
       
  1879         'form' => $default_theme_path . '/logo.png',
       
  1880         'src' => $GLOBALS['base_url'] . '/' . $default_theme_path . '/logo.png',
       
  1881       ),
       
  1882     );
       
  1883     foreach ($supported_paths as $input => $expected) {
       
  1884       $edit = array(
       
  1885         'default_logo' => FALSE,
       
  1886         'logo_path' => $input,
       
  1887       );
       
  1888       $this->drupalPost('admin/appearance/settings', $edit, t('Save configuration'));
       
  1889       $this->assertNoText('The custom logo path is invalid.');
       
  1890       $this->assertFieldByName('logo_path', $expected['form']);
       
  1891 
       
  1892       // Verify the actual 'src' attribute of the logo being output.
       
  1893       $this->drupalGet('');
       
  1894       $elements = $this->xpath('//*[@id=:id]/img', array(':id' => 'logo'));
       
  1895       $this->assertEqual((string) $elements[0]['src'], $expected['src']);
       
  1896     }
       
  1897 
       
  1898     $unsupported_paths = array(
       
  1899       // Stream wrapper URI to non-existing file.
       
  1900       'public://whatever.png',
       
  1901       'private://whatever.png',
       
  1902       'temporary://whatever.png',
       
  1903       // Bogus stream wrapper URIs.
       
  1904       'public:/whatever.png',
       
  1905       '://whatever.png',
       
  1906       ':whatever.png',
       
  1907       'public://',
       
  1908       // Relative path within the public filesystem to non-existing file.
       
  1909       'whatever.png',
       
  1910       // Relative path to non-existing file in public filesystem.
       
  1911       variable_get('file_public_path', conf_path() . '/files') . '/whatever.png',
       
  1912       // Semi-absolute path to non-existing file in public filesystem.
       
  1913       '/' . variable_get('file_public_path', conf_path() . '/files') . '/whatever.png',
       
  1914       // Relative path to arbitrary non-existing file.
       
  1915       'misc/whatever.png',
       
  1916       // Semi-absolute path to arbitrary non-existing file.
       
  1917       '/misc/whatever.png',
       
  1918       // Absolute paths to any local file (even if it exists).
       
  1919       drupal_realpath($file->uri),
       
  1920     );
       
  1921     $this->drupalGet('admin/appearance/settings');
       
  1922     foreach ($unsupported_paths as $path) {
       
  1923       $edit = array(
       
  1924         'default_logo' => FALSE,
       
  1925         'logo_path' => $path,
       
  1926       );
       
  1927       $this->drupalPost(NULL, $edit, t('Save configuration'));
       
  1928       $this->assertText('The custom logo path is invalid.');
       
  1929     }
       
  1930 
       
  1931     // Upload a file to use for the logo.
       
  1932     $edit = array(
       
  1933       'default_logo' => FALSE,
       
  1934       'logo_path' => '',
       
  1935       'files[logo_upload]' => drupal_realpath($file->uri),
       
  1936     );
       
  1937     $this->drupalPost('admin/appearance/settings', $edit, t('Save configuration'));
       
  1938 
       
  1939     $fields = $this->xpath($this->constructFieldXpath('name', 'logo_path'));
       
  1940     $uploaded_filename = 'public://' . $fields[0]['value'];
       
  1941 
       
  1942     $this->drupalGet('');
       
  1943     $elements = $this->xpath('//*[@id=:id]/img', array(':id' => 'logo'));
       
  1944     $this->assertEqual($elements[0]['src'], file_create_url($uploaded_filename));
       
  1945   }
       
  1946 
       
  1947   /**
       
  1948    * Test the administration theme functionality.
       
  1949    */
       
  1950   function testAdministrationTheme() {
       
  1951     theme_enable(array('stark'));
       
  1952     variable_set('theme_default', 'stark');
       
  1953     // Enable an administration theme and show it on the node admin pages.
       
  1954     $edit = array(
       
  1955       'admin_theme' => 'seven',
       
  1956       'node_admin_theme' => TRUE,
       
  1957     );
       
  1958     $this->drupalPost('admin/appearance', $edit, t('Save configuration'));
       
  1959 
       
  1960     $this->drupalGet('admin/config');
       
  1961     $this->assertRaw('themes/seven', 'Administration theme used on an administration page.');
       
  1962 
       
  1963     $this->drupalGet('node/' . $this->node->nid);
       
  1964     $this->assertRaw('themes/stark', 'Site default theme used on node page.');
       
  1965 
       
  1966     $this->drupalGet('node/add');
       
  1967     $this->assertRaw('themes/seven', 'Administration theme used on the add content page.');
       
  1968 
       
  1969     $this->drupalGet('node/' . $this->node->nid . '/edit');
       
  1970     $this->assertRaw('themes/seven', 'Administration theme used on the edit content page.');
       
  1971 
       
  1972     // Disable the admin theme on the node admin pages.
       
  1973     $edit = array(
       
  1974       'node_admin_theme' => FALSE,
       
  1975     );
       
  1976     $this->drupalPost('admin/appearance', $edit, t('Save configuration'));
       
  1977 
       
  1978     $this->drupalGet('admin/config');
       
  1979     $this->assertRaw('themes/seven', 'Administration theme used on an administration page.');
       
  1980 
       
  1981     $this->drupalGet('node/add');
       
  1982     $this->assertRaw('themes/stark', 'Site default theme used on the add content page.');
       
  1983 
       
  1984     // Reset to the default theme settings.
       
  1985     variable_set('theme_default', 'bartik');
       
  1986     $edit = array(
       
  1987       'admin_theme' => '0',
       
  1988       'node_admin_theme' => FALSE,
       
  1989     );
       
  1990     $this->drupalPost('admin/appearance', $edit, t('Save configuration'));
       
  1991 
       
  1992     $this->drupalGet('admin');
       
  1993     $this->assertRaw('themes/bartik', 'Site default theme used on administration page.');
       
  1994 
       
  1995     $this->drupalGet('node/add');
       
  1996     $this->assertRaw('themes/bartik', 'Site default theme used on the add content page.');
       
  1997   }
       
  1998 
       
  1999   /**
       
  2000    * Test switching the default theme.
       
  2001    */
       
  2002   function testSwitchDefaultTheme() {
       
  2003     // Enable "stark" and set it as the default theme.
       
  2004     theme_enable(array('stark'));
       
  2005     $this->drupalGet('admin/appearance');
       
  2006     $this->clickLink(t('Set default'), 1);
       
  2007     $this->assertTrue(variable_get('theme_default', '') == 'stark', 'Site default theme switched successfully.');
       
  2008 
       
  2009     // Test the default theme on the secondary links (blocks admin page).
       
  2010     $this->drupalGet('admin/structure/block');
       
  2011     $this->assertText('Stark(' . t('active tab') . ')', 'Default local task on blocks admin page is the default theme.');
       
  2012     // Switch back to Bartik and test again to test that the menu cache is cleared.
       
  2013     $this->drupalGet('admin/appearance');
       
  2014     $this->clickLink(t('Set default'), 0);
       
  2015     $this->drupalGet('admin/structure/block');
       
  2016     $this->assertText('Bartik(' . t('active tab') . ')', 'Default local task on blocks admin page has changed.');
       
  2017   }
       
  2018 }
       
  2019 
       
  2020 
       
  2021 /**
       
  2022  * Test the basic queue functionality.
       
  2023  */
       
  2024 class QueueTestCase extends DrupalWebTestCase {
       
  2025   public static function getInfo() {
       
  2026     return array(
       
  2027       'name' => 'Queue functionality',
       
  2028       'description' => 'Queues and dequeues a set of items to check the basic queue functionality.',
       
  2029       'group' => 'System',
       
  2030     );
       
  2031   }
       
  2032 
       
  2033   /**
       
  2034    * Queues and dequeues a set of items to check the basic queue functionality.
       
  2035    */
       
  2036   function testQueue() {
       
  2037     // Create two queues.
       
  2038     $queue1 = DrupalQueue::get($this->randomName());
       
  2039     $queue1->createQueue();
       
  2040     $queue2 = DrupalQueue::get($this->randomName());
       
  2041     $queue2->createQueue();
       
  2042 
       
  2043     // Create four items.
       
  2044     $data = array();
       
  2045     for ($i = 0; $i < 4; $i++) {
       
  2046       $data[] = array($this->randomName() => $this->randomName());
       
  2047     }
       
  2048 
       
  2049     // Queue items 1 and 2 in the queue1.
       
  2050     $queue1->createItem($data[0]);
       
  2051     $queue1->createItem($data[1]);
       
  2052 
       
  2053     // Retrieve two items from queue1.
       
  2054     $items = array();
       
  2055     $new_items = array();
       
  2056 
       
  2057     $items[] = $item = $queue1->claimItem();
       
  2058     $new_items[] = $item->data;
       
  2059 
       
  2060     $items[] = $item = $queue1->claimItem();
       
  2061     $new_items[] = $item->data;
       
  2062 
       
  2063     // First two dequeued items should match the first two items we queued.
       
  2064     $this->assertEqual($this->queueScore($data, $new_items), 2, 'Two items matched');
       
  2065 
       
  2066     // Add two more items.
       
  2067     $queue1->createItem($data[2]);
       
  2068     $queue1->createItem($data[3]);
       
  2069 
       
  2070     $this->assertTrue($queue1->numberOfItems(), 'Queue 1 is not empty after adding items.');
       
  2071     $this->assertFalse($queue2->numberOfItems(), 'Queue 2 is empty while Queue 1 has items');
       
  2072 
       
  2073     $items[] = $item = $queue1->claimItem();
       
  2074     $new_items[] = $item->data;
       
  2075 
       
  2076     $items[] = $item = $queue1->claimItem();
       
  2077     $new_items[] = $item->data;
       
  2078 
       
  2079     // All dequeued items should match the items we queued exactly once,
       
  2080     // therefore the score must be exactly 4.
       
  2081     $this->assertEqual($this->queueScore($data, $new_items), 4, 'Four items matched');
       
  2082 
       
  2083     // There should be no duplicate items.
       
  2084     $this->assertEqual($this->queueScore($new_items, $new_items), 4, 'Four items matched');
       
  2085 
       
  2086     // Delete all items from queue1.
       
  2087     foreach ($items as $item) {
       
  2088       $queue1->deleteItem($item);
       
  2089     }
       
  2090 
       
  2091     // Check that both queues are empty.
       
  2092     $this->assertFalse($queue1->numberOfItems(), 'Queue 1 is empty');
       
  2093     $this->assertFalse($queue2->numberOfItems(), 'Queue 2 is empty');
       
  2094   }
       
  2095 
       
  2096   /**
       
  2097    * This function returns the number of equal items in two arrays.
       
  2098    */
       
  2099   function queueScore($items, $new_items) {
       
  2100     $score = 0;
       
  2101     foreach ($items as $item) {
       
  2102       foreach ($new_items as $new_item) {
       
  2103         if ($item === $new_item) {
       
  2104           $score++;
       
  2105         }
       
  2106       }
       
  2107     }
       
  2108     return $score;
       
  2109   }
       
  2110 }
       
  2111 
       
  2112 /**
       
  2113  * Test token replacement in strings.
       
  2114  */
       
  2115 class TokenReplaceTestCase extends DrupalWebTestCase {
       
  2116   public static function getInfo() {
       
  2117     return array(
       
  2118       'name' => 'Token replacement',
       
  2119       'description' => 'Generates text using placeholders for dummy content to check token replacement.',
       
  2120       'group' => 'System',
       
  2121     );
       
  2122   }
       
  2123 
       
  2124   /**
       
  2125    * Creates a user and a node, then tests the tokens generated from them.
       
  2126    */
       
  2127   function testTokenReplacement() {
       
  2128     // Create the initial objects.
       
  2129     $account = $this->drupalCreateUser();
       
  2130     $node = $this->drupalCreateNode(array('uid' => $account->uid));
       
  2131     $node->title = '<blink>Blinking Text</blink>';
       
  2132     global $user, $language;
       
  2133 
       
  2134     $source  = '[node:title]';         // Title of the node we passed in
       
  2135     $source .= '[node:author:name]';   // Node author's name
       
  2136     $source .= '[node:created:since]'; // Time since the node was created
       
  2137     $source .= '[current-user:name]';  // Current user's name
       
  2138     $source .= '[date:short]';         // Short date format of REQUEST_TIME
       
  2139     $source .= '[user:name]';          // No user passed in, should be untouched
       
  2140     $source .= '[bogus:token]';        // Non-existent token
       
  2141 
       
  2142     $target  = check_plain($node->title);
       
  2143     $target .= check_plain($account->name);
       
  2144     $target .= format_interval(REQUEST_TIME - $node->created, 2, $language->language);
       
  2145     $target .= check_plain($user->name);
       
  2146     $target .= format_date(REQUEST_TIME, 'short', '', NULL, $language->language);
       
  2147 
       
  2148     // Test that the clear parameter cleans out non-existent tokens.
       
  2149     $result = token_replace($source, array('node' => $node), array('language' => $language, 'clear' => TRUE));
       
  2150     $result = $this->assertEqual($target, $result, 'Valid tokens replaced while invalid tokens cleared out.');
       
  2151 
       
  2152     // Test without using the clear parameter (non-existent token untouched).
       
  2153     $target .= '[user:name]';
       
  2154     $target .= '[bogus:token]';
       
  2155     $result = token_replace($source, array('node' => $node), array('language' => $language));
       
  2156     $this->assertEqual($target, $result, 'Valid tokens replaced while invalid tokens ignored.');
       
  2157 
       
  2158     // Check that the results of token_generate are sanitized properly. This does NOT
       
  2159     // test the cleanliness of every token -- just that the $sanitize flag is being
       
  2160     // passed properly through the call stack and being handled correctly by a 'known'
       
  2161     // token, [node:title].
       
  2162     $raw_tokens = array('title' => '[node:title]');
       
  2163     $generated = token_generate('node', $raw_tokens, array('node' => $node));
       
  2164     $this->assertEqual($generated['[node:title]'], check_plain($node->title), 'Token sanitized.');
       
  2165 
       
  2166     $generated = token_generate('node', $raw_tokens, array('node' => $node), array('sanitize' => FALSE));
       
  2167     $this->assertEqual($generated['[node:title]'], $node->title, 'Unsanitized token generated properly.');
       
  2168 
       
  2169     // Test token replacement when the string contains no tokens.
       
  2170     $this->assertEqual(token_replace('No tokens here.'), 'No tokens here.');
       
  2171   }
       
  2172 
       
  2173   /**
       
  2174    * Test whether token-replacement works in various contexts.
       
  2175    */
       
  2176   function testSystemTokenRecognition() {
       
  2177     global $language;
       
  2178 
       
  2179     // Generate prefixes and suffixes for the token context.
       
  2180     $tests = array(
       
  2181       array('prefix' => 'this is the ', 'suffix' => ' site'),
       
  2182       array('prefix' => 'this is the', 'suffix' => 'site'),
       
  2183       array('prefix' => '[', 'suffix' => ']'),
       
  2184       array('prefix' => '', 'suffix' => ']]]'),
       
  2185       array('prefix' => '[[[', 'suffix' => ''),
       
  2186       array('prefix' => ':[:', 'suffix' => '--]'),
       
  2187       array('prefix' => '-[-', 'suffix' => ':]:'),
       
  2188       array('prefix' => '[:', 'suffix' => ']'),
       
  2189       array('prefix' => '[site:', 'suffix' => ':name]'),
       
  2190       array('prefix' => '[site:', 'suffix' => ']'),
       
  2191     );
       
  2192 
       
  2193     // Check if the token is recognized in each of the contexts.
       
  2194     foreach ($tests as $test) {
       
  2195       $input = $test['prefix'] . '[site:name]' . $test['suffix'];
       
  2196       $expected = $test['prefix'] . 'Drupal' . $test['suffix'];
       
  2197       $output = token_replace($input, array(), array('language' => $language));
       
  2198       $this->assertTrue($output == $expected, format_string('Token recognized in string %string', array('%string' => $input)));
       
  2199     }
       
  2200   }
       
  2201 
       
  2202   /**
       
  2203    * Tests the generation of all system site information tokens.
       
  2204    */
       
  2205   function testSystemSiteTokenReplacement() {
       
  2206     global $language;
       
  2207     $url_options = array(
       
  2208       'absolute' => TRUE,
       
  2209       'language' => $language,
       
  2210     );
       
  2211 
       
  2212     // Set a few site variables.
       
  2213     variable_set('site_name', '<strong>Drupal<strong>');
       
  2214     variable_set('site_slogan', '<blink>Slogan</blink>');
       
  2215 
       
  2216     // Generate and test sanitized tokens.
       
  2217     $tests = array();
       
  2218     $tests['[site:name]'] = check_plain(variable_get('site_name', 'Drupal'));
       
  2219     $tests['[site:slogan]'] = check_plain(variable_get('site_slogan', ''));
       
  2220     $tests['[site:mail]'] = 'simpletest@example.com';
       
  2221     $tests['[site:url]'] = url('<front>', $url_options);
       
  2222     $tests['[site:url-brief]'] = preg_replace(array('!^https?://!', '!/$!'), '', url('<front>', $url_options));
       
  2223     $tests['[site:login-url]'] = url('user', $url_options);
       
  2224 
       
  2225     // Test to make sure that we generated something for each token.
       
  2226     $this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.');
       
  2227 
       
  2228     foreach ($tests as $input => $expected) {
       
  2229       $output = token_replace($input, array(), array('language' => $language));
       
  2230       $this->assertEqual($output, $expected, format_string('Sanitized system site information token %token replaced.', array('%token' => $input)));
       
  2231     }
       
  2232 
       
  2233     // Generate and test unsanitized tokens.
       
  2234     $tests['[site:name]'] = variable_get('site_name', 'Drupal');
       
  2235     $tests['[site:slogan]'] = variable_get('site_slogan', '');
       
  2236 
       
  2237     foreach ($tests as $input => $expected) {
       
  2238       $output = token_replace($input, array(), array('language' => $language, 'sanitize' => FALSE));
       
  2239       $this->assertEqual($output, $expected, format_string('Unsanitized system site information token %token replaced.', array('%token' => $input)));
       
  2240     }
       
  2241   }
       
  2242 
       
  2243   /**
       
  2244    * Tests the generation of all system date tokens.
       
  2245    */
       
  2246   function testSystemDateTokenReplacement() {
       
  2247     global $language;
       
  2248 
       
  2249     // Set time to one hour before request.
       
  2250     $date = REQUEST_TIME - 3600;
       
  2251 
       
  2252     // Generate and test tokens.
       
  2253     $tests = array();
       
  2254     $tests['[date:short]'] = format_date($date, 'short', '', NULL, $language->language);
       
  2255     $tests['[date:medium]'] = format_date($date, 'medium', '', NULL, $language->language);
       
  2256     $tests['[date:long]'] = format_date($date, 'long', '', NULL, $language->language);
       
  2257     $tests['[date:custom:m/j/Y]'] = format_date($date, 'custom', 'm/j/Y', NULL, $language->language);
       
  2258     $tests['[date:since]'] = format_interval((REQUEST_TIME - $date), 2, $language->language);
       
  2259     $tests['[date:raw]'] = filter_xss($date);
       
  2260 
       
  2261     // Test to make sure that we generated something for each token.
       
  2262     $this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.');
       
  2263 
       
  2264     foreach ($tests as $input => $expected) {
       
  2265       $output = token_replace($input, array('date' => $date), array('language' => $language));
       
  2266       $this->assertEqual($output, $expected, format_string('Date token %token replaced.', array('%token' => $input)));
       
  2267     }
       
  2268   }
       
  2269 }
       
  2270 
       
  2271 class InfoFileParserTestCase extends DrupalUnitTestCase {
       
  2272   public static function getInfo() {
       
  2273     return array(
       
  2274       'name' => 'Info file format parser',
       
  2275       'description' => 'Tests proper parsing of a .info file formatted string.',
       
  2276       'group' => 'System',
       
  2277     );
       
  2278   }
       
  2279 
       
  2280   /**
       
  2281    * Test drupal_parse_info_format().
       
  2282    */
       
  2283   function testDrupalParseInfoFormat() {
       
  2284     $config = '
       
  2285 simple = Value
       
  2286 quoted = " Value"
       
  2287 multiline = "Value
       
  2288   Value"
       
  2289 array[] = Value1
       
  2290 array[] = Value2
       
  2291 array_assoc[a] = Value1
       
  2292 array_assoc[b] = Value2
       
  2293 array_deep[][][] = Value
       
  2294 array_deep_assoc[a][b][c] = Value
       
  2295 array_space[a b] = Value';
       
  2296 
       
  2297     $expected = array(
       
  2298       'simple' => 'Value',
       
  2299       'quoted' => ' Value',
       
  2300       'multiline' => "Value\n  Value",
       
  2301       'array' => array(
       
  2302         0 => 'Value1',
       
  2303         1 => 'Value2',
       
  2304       ),
       
  2305       'array_assoc' => array(
       
  2306         'a' => 'Value1',
       
  2307         'b' => 'Value2',
       
  2308       ),
       
  2309       'array_deep' => array(
       
  2310         0 => array(
       
  2311           0 => array(
       
  2312             0 => 'Value',
       
  2313           ),
       
  2314         ),
       
  2315       ),
       
  2316       'array_deep_assoc' => array(
       
  2317         'a' => array(
       
  2318           'b' => array(
       
  2319             'c' => 'Value',
       
  2320           ),
       
  2321         ),
       
  2322       ),
       
  2323       'array_space' => array(
       
  2324         'a b' => 'Value',
       
  2325       ),
       
  2326     );
       
  2327 
       
  2328     $parsed = drupal_parse_info_format($config);
       
  2329 
       
  2330     $this->assertEqual($parsed['simple'], $expected['simple'], 'Set a simple value.');
       
  2331     $this->assertEqual($parsed['quoted'], $expected['quoted'], 'Set a simple value in quotes.');
       
  2332     $this->assertEqual($parsed['multiline'], $expected['multiline'], 'Set a multiline value.');
       
  2333     $this->assertEqual($parsed['array'], $expected['array'], 'Set a simple array.');
       
  2334     $this->assertEqual($parsed['array_assoc'], $expected['array_assoc'], 'Set an associative array.');
       
  2335     $this->assertEqual($parsed['array_deep'], $expected['array_deep'], 'Set a nested array.');
       
  2336     $this->assertEqual($parsed['array_deep_assoc'], $expected['array_deep_assoc'], 'Set a nested associative array.');
       
  2337     $this->assertEqual($parsed['array_space'], $expected['array_space'], 'Set an array with a whitespace in the key.');
       
  2338     $this->assertEqual($parsed, $expected, 'Entire parsed .info string and expected array are identical.');
       
  2339   }
       
  2340 }
       
  2341 
       
  2342 /**
       
  2343  * Tests the effectiveness of hook_system_info_alter().
       
  2344  */
       
  2345 class SystemInfoAlterTestCase extends DrupalWebTestCase {
       
  2346   public static function getInfo() {
       
  2347     return array(
       
  2348       'name' => 'System info alter',
       
  2349       'description' => 'Tests the effectiveness of hook_system_info_alter().',
       
  2350       'group' => 'System',
       
  2351     );
       
  2352   }
       
  2353 
       
  2354   /**
       
  2355    * Tests that {system}.info is rebuilt after a module that implements
       
  2356    * hook_system_info_alter() is enabled. Also tests if core *_list() functions
       
  2357    * return freshly altered info.
       
  2358    */
       
  2359   function testSystemInfoAlter() {
       
  2360     // Enable our test module. Flush all caches, which we assert is the only
       
  2361     // thing necessary to use the rebuilt {system}.info.
       
  2362     module_enable(array('module_test'), FALSE);
       
  2363     drupal_flush_all_caches();
       
  2364     $this->assertTrue(module_exists('module_test'), 'Test module is enabled.');
       
  2365 
       
  2366     $info = $this->getSystemInfo('seven', 'theme');
       
  2367     $this->assertTrue(isset($info['regions']['test_region']), 'Altered theme info was added to {system}.info.');
       
  2368     $seven_regions = system_region_list('seven');
       
  2369     $this->assertTrue(isset($seven_regions['test_region']), 'Altered theme info was returned by system_region_list().');
       
  2370     $system_list_themes = system_list('theme');
       
  2371     $info = $system_list_themes['seven']->info;
       
  2372     $this->assertTrue(isset($info['regions']['test_region']), 'Altered theme info was returned by system_list().');
       
  2373     $list_themes = list_themes();
       
  2374     $this->assertTrue(isset($list_themes['seven']->info['regions']['test_region']), 'Altered theme info was returned by list_themes().');
       
  2375 
       
  2376     // Disable the module and verify that {system}.info is rebuilt without it.
       
  2377     module_disable(array('module_test'), FALSE);
       
  2378     drupal_flush_all_caches();
       
  2379     $this->assertFalse(module_exists('module_test'), 'Test module is disabled.');
       
  2380 
       
  2381     $info = $this->getSystemInfo('seven', 'theme');
       
  2382     $this->assertFalse(isset($info['regions']['test_region']), 'Altered theme info was removed from {system}.info.');
       
  2383     $seven_regions = system_region_list('seven');
       
  2384     $this->assertFalse(isset($seven_regions['test_region']), 'Altered theme info was not returned by system_region_list().');
       
  2385     $system_list_themes = system_list('theme');
       
  2386     $info = $system_list_themes['seven']->info;
       
  2387     $this->assertFalse(isset($info['regions']['test_region']), 'Altered theme info was not returned by system_list().');
       
  2388     $list_themes = list_themes();
       
  2389     $this->assertFalse(isset($list_themes['seven']->info['regions']['test_region']), 'Altered theme info was not returned by list_themes().');
       
  2390   }
       
  2391 
       
  2392   /**
       
  2393    * Returns the info array as it is stored in {system}.
       
  2394    *
       
  2395    * @param $name
       
  2396    *   The name of the record in {system}.
       
  2397    * @param $type
       
  2398    *   The type of record in {system}.
       
  2399    *
       
  2400    * @return
       
  2401    *   Array of info, or FALSE if the record is not found.
       
  2402    */
       
  2403   function getSystemInfo($name, $type) {
       
  2404     $raw_info = db_query("SELECT info FROM {system} WHERE name = :name AND type = :type", array(':name' => $name, ':type' => $type))->fetchField();
       
  2405     return $raw_info ? unserialize($raw_info) : FALSE;
       
  2406   }
       
  2407 }
       
  2408 
       
  2409 /**
       
  2410  * Tests for the update system functionality.
       
  2411  */
       
  2412 class UpdateScriptFunctionalTest extends DrupalWebTestCase {
       
  2413   private $update_url;
       
  2414   private $update_user;
       
  2415 
       
  2416   public static function getInfo() {
       
  2417     return array(
       
  2418       'name' => 'Update functionality',
       
  2419       'description' => 'Tests the update script access and functionality.',
       
  2420       'group' => 'System',
       
  2421     );
       
  2422   }
       
  2423 
       
  2424   function setUp() {
       
  2425     parent::setUp('update_script_test');
       
  2426     $this->update_url = $GLOBALS['base_url'] . '/update.php';
       
  2427     $this->update_user = $this->drupalCreateUser(array('administer software updates'));
       
  2428   }
       
  2429 
       
  2430   /**
       
  2431    * Tests that there are no pending updates for the first test method.
       
  2432    */
       
  2433   function testNoPendingUpdates() {
       
  2434     // Ensure that for the first test method in a class, there are no pending
       
  2435     // updates. This tests a drupal_get_schema_versions() bug that previously
       
  2436     // led to the wrong schema version being recorded for the initial install
       
  2437     // of a child site during automated testing.
       
  2438     $this->drupalLogin($this->update_user);
       
  2439     $this->drupalGet($this->update_url, array('external' => TRUE));
       
  2440     $this->drupalPost(NULL, array(), t('Continue'));
       
  2441     $this->assertText(t('No pending updates.'), 'End of update process was reached.');
       
  2442   }
       
  2443 
       
  2444   /**
       
  2445    * Tests access to the update script.
       
  2446    */
       
  2447   function testUpdateAccess() {
       
  2448     // Try accessing update.php without the proper permission.
       
  2449     $regular_user = $this->drupalCreateUser();
       
  2450     $this->drupalLogin($regular_user);
       
  2451     $this->drupalGet($this->update_url, array('external' => TRUE));
       
  2452     $this->assertResponse(403);
       
  2453 
       
  2454     // Try accessing update.php as an anonymous user.
       
  2455     $this->drupalLogout();
       
  2456     $this->drupalGet($this->update_url, array('external' => TRUE));
       
  2457     $this->assertResponse(403);
       
  2458 
       
  2459     // Access the update page with the proper permission.
       
  2460     $this->drupalLogin($this->update_user);
       
  2461     $this->drupalGet($this->update_url, array('external' => TRUE));
       
  2462     $this->assertResponse(200);
       
  2463 
       
  2464     // Access the update page as user 1.
       
  2465     $user1 = user_load(1);
       
  2466     $user1->pass_raw = user_password();
       
  2467     require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc');
       
  2468     $user1->pass = user_hash_password(trim($user1->pass_raw));
       
  2469     db_query("UPDATE {users} SET pass = :pass WHERE uid = :uid", array(':pass' => $user1->pass, ':uid' => $user1->uid));
       
  2470     $this->drupalLogin($user1);
       
  2471     $this->drupalGet($this->update_url, array('external' => TRUE));
       
  2472     $this->assertResponse(200);
       
  2473   }
       
  2474 
       
  2475   /**
       
  2476    * Tests that requirements warnings and errors are correctly displayed.
       
  2477    */
       
  2478   function testRequirements() {
       
  2479     $this->drupalLogin($this->update_user);
       
  2480 
       
  2481     // If there are no requirements warnings or errors, we expect to be able to
       
  2482     // go through the update process uninterrupted.
       
  2483     $this->drupalGet($this->update_url, array('external' => TRUE));
       
  2484     $this->drupalPost(NULL, array(), t('Continue'));
       
  2485     $this->assertText(t('No pending updates.'), 'End of update process was reached.');
       
  2486     // Confirm that all caches were cleared.
       
  2487     $this->assertText(t('hook_flush_caches() invoked for update_script_test.module.'), 'Caches were cleared when there were no requirements warnings or errors.');
       
  2488 
       
  2489     // If there is a requirements warning, we expect it to be initially
       
  2490     // displayed, but clicking the link to proceed should allow us to go
       
  2491     // through the rest of the update process uninterrupted.
       
  2492 
       
  2493     // First, run this test with pending updates to make sure they can be run
       
  2494     // successfully.
       
  2495     variable_set('update_script_test_requirement_type', REQUIREMENT_WARNING);
       
  2496     drupal_set_installed_schema_version('update_script_test', drupal_get_installed_schema_version('update_script_test') - 1);
       
  2497     $this->drupalGet($this->update_url, array('external' => TRUE));
       
  2498     $this->assertText('This is a requirements warning provided by the update_script_test module.');
       
  2499     $this->clickLink('try again');
       
  2500     $this->assertNoText('This is a requirements warning provided by the update_script_test module.');
       
  2501     $this->drupalPost(NULL, array(), t('Continue'));
       
  2502     $this->drupalPost(NULL, array(), t('Apply pending updates'));
       
  2503     $this->assertText(t('The update_script_test_update_7000() update was executed successfully.'), 'End of update process was reached.');
       
  2504     // Confirm that all caches were cleared.
       
  2505     $this->assertText(t('hook_flush_caches() invoked for update_script_test.module.'), 'Caches were cleared after resolving a requirements warning and applying updates.');
       
  2506 
       
  2507     // Now try again without pending updates to make sure that works too.
       
  2508     $this->drupalGet($this->update_url, array('external' => TRUE));
       
  2509     $this->assertText('This is a requirements warning provided by the update_script_test module.');
       
  2510     $this->clickLink('try again');
       
  2511     $this->assertNoText('This is a requirements warning provided by the update_script_test module.');
       
  2512     $this->drupalPost(NULL, array(), t('Continue'));
       
  2513     $this->assertText(t('No pending updates.'), 'End of update process was reached.');
       
  2514     // Confirm that all caches were cleared.
       
  2515     $this->assertText(t('hook_flush_caches() invoked for update_script_test.module.'), 'Caches were cleared after applying updates and re-running the script.');
       
  2516 
       
  2517     // If there is a requirements error, it should be displayed even after
       
  2518     // clicking the link to proceed (since the problem that triggered the error
       
  2519     // has not been fixed).
       
  2520     variable_set('update_script_test_requirement_type', REQUIREMENT_ERROR);
       
  2521     $this->drupalGet($this->update_url, array('external' => TRUE));
       
  2522     $this->assertText('This is a requirements error provided by the update_script_test module.');
       
  2523     $this->clickLink('try again');
       
  2524     $this->assertText('This is a requirements error provided by the update_script_test module.');
       
  2525 
       
  2526     // Check if the optional 'value' key displays without a notice.
       
  2527     variable_set('update_script_test_requirement_type', REQUIREMENT_INFO);
       
  2528     $this->drupalGet($this->update_url, array('external' => TRUE));
       
  2529     $this->assertText('This is a requirements info provided by the update_script_test module.');
       
  2530     $this->assertNoText('Notice: Undefined index: value in theme_status_report()');
       
  2531   }
       
  2532 
       
  2533   /**
       
  2534    * Tests the effect of using the update script on the theme system.
       
  2535    */
       
  2536   function testThemeSystem() {
       
  2537     // Since visiting update.php triggers a rebuild of the theme system from an
       
  2538     // unusual maintenance mode environment, we check that this rebuild did not
       
  2539     // put any incorrect information about the themes into the database.
       
  2540     $original_theme_data = db_query("SELECT * FROM {system} WHERE type = 'theme' ORDER BY name")->fetchAll();
       
  2541     $this->drupalLogin($this->update_user);
       
  2542     $this->drupalGet($this->update_url, array('external' => TRUE));
       
  2543     $final_theme_data = db_query("SELECT * FROM {system} WHERE type = 'theme' ORDER BY name")->fetchAll();
       
  2544     $this->assertEqual($original_theme_data, $final_theme_data, 'Visiting update.php does not alter the information about themes stored in the database.');
       
  2545   }
       
  2546 
       
  2547   /**
       
  2548    * Tests update.php when there are no updates to apply.
       
  2549    */
       
  2550   function testNoUpdateFunctionality() {
       
  2551     // Click through update.php with 'administer software updates' permission.
       
  2552     $this->drupalLogin($this->update_user);
       
  2553     $this->drupalPost($this->update_url, array(), t('Continue'), array('external' => TRUE));
       
  2554     $this->assertText(t('No pending updates.'));
       
  2555     $this->assertNoLink('Administration pages');
       
  2556     $this->clickLink('Front page');
       
  2557     $this->assertResponse(200);
       
  2558 
       
  2559     // Click through update.php with 'access administration pages' permission.
       
  2560     $admin_user = $this->drupalCreateUser(array('administer software updates', 'access administration pages'));
       
  2561     $this->drupalLogin($admin_user);
       
  2562     $this->drupalPost($this->update_url, array(), t('Continue'), array('external' => TRUE));
       
  2563     $this->assertText(t('No pending updates.'));
       
  2564     $this->clickLink('Administration pages');
       
  2565     $this->assertResponse(200);
       
  2566   }
       
  2567 
       
  2568   /**
       
  2569    * Tests update.php after performing a successful update.
       
  2570    */
       
  2571   function testSuccessfulUpdateFunctionality() {
       
  2572     drupal_set_installed_schema_version('update_script_test', drupal_get_installed_schema_version('update_script_test') - 1);
       
  2573     // Click through update.php with 'administer software updates' permission.
       
  2574     $this->drupalLogin($this->update_user);
       
  2575     $this->drupalPost($this->update_url, array(), t('Continue'), array('external' => TRUE));
       
  2576     $this->drupalPost(NULL, array(), t('Apply pending updates'));
       
  2577     $this->assertText('Updates were attempted.');
       
  2578     $this->assertLink('site');
       
  2579     $this->assertNoLink('Administration pages');
       
  2580     $this->assertNoLink('logged');
       
  2581     $this->clickLink('Front page');
       
  2582     $this->assertResponse(200);
       
  2583 
       
  2584     drupal_set_installed_schema_version('update_script_test', drupal_get_installed_schema_version('update_script_test') - 1);
       
  2585     // Click through update.php with 'access administration pages' and
       
  2586     // 'access site reports' permissions.
       
  2587     $admin_user = $this->drupalCreateUser(array('administer software updates', 'access administration pages', 'access site reports'));
       
  2588     $this->drupalLogin($admin_user);
       
  2589     $this->drupalPost($this->update_url, array(), t('Continue'), array('external' => TRUE));
       
  2590     $this->drupalPost(NULL, array(), t('Apply pending updates'));
       
  2591     $this->assertText('Updates were attempted.');
       
  2592     $this->assertLink('logged');
       
  2593     $this->clickLink('Administration pages');
       
  2594     $this->assertResponse(200);
       
  2595   }
       
  2596 }
       
  2597 
       
  2598 /**
       
  2599  * Functional tests for the flood control mechanism.
       
  2600  */
       
  2601 class FloodFunctionalTest extends DrupalWebTestCase {
       
  2602   public static function getInfo() {
       
  2603     return array(
       
  2604       'name' => 'Flood control mechanism',
       
  2605       'description' => 'Functional tests for the flood control mechanism.',
       
  2606       'group' => 'System',
       
  2607     );
       
  2608   }
       
  2609 
       
  2610   /**
       
  2611    * Test flood control mechanism clean-up.
       
  2612    */
       
  2613   function testCleanUp() {
       
  2614     $threshold = 1;
       
  2615     $window_expired = -1;
       
  2616     $name = 'flood_test_cleanup';
       
  2617 
       
  2618     // Register expired event.
       
  2619     flood_register_event($name, $window_expired);
       
  2620     // Verify event is not allowed.
       
  2621     $this->assertFalse(flood_is_allowed($name, $threshold));
       
  2622     // Run cron and verify event is now allowed.
       
  2623     $this->cronRun();
       
  2624     $this->assertTrue(flood_is_allowed($name, $threshold));
       
  2625 
       
  2626     // Register unexpired event.
       
  2627     flood_register_event($name);
       
  2628     // Verify event is not allowed.
       
  2629     $this->assertFalse(flood_is_allowed($name, $threshold));
       
  2630     // Run cron and verify event is still not allowed.
       
  2631     $this->cronRun();
       
  2632     $this->assertFalse(flood_is_allowed($name, $threshold));
       
  2633   }
       
  2634 }
       
  2635 
       
  2636 /**
       
  2637  * Test HTTP file downloading capability.
       
  2638  */
       
  2639 class RetrieveFileTestCase extends DrupalWebTestCase {
       
  2640   public static function getInfo() {
       
  2641     return array(
       
  2642       'name' => 'HTTP file retrieval',
       
  2643       'description' => 'Checks HTTP file fetching and error handling.',
       
  2644       'group' => 'System',
       
  2645     );
       
  2646   }
       
  2647 
       
  2648   /**
       
  2649    * Invokes system_retrieve_file() in several scenarios.
       
  2650    */
       
  2651   function testFileRetrieving() {
       
  2652     // Test 404 handling by trying to fetch a randomly named file.
       
  2653     drupal_mkdir($sourcedir = 'public://' . $this->randomName());
       
  2654     $filename = 'Файл для тестирования ' . $this->randomName();
       
  2655     $url = file_create_url($sourcedir . '/' . $filename);
       
  2656     $retrieved_file = system_retrieve_file($url);
       
  2657     $this->assertFalse($retrieved_file, 'Non-existent file not fetched.');
       
  2658 
       
  2659     // Actually create that file, download it via HTTP and test the returned path.
       
  2660     file_put_contents($sourcedir . '/' . $filename, 'testing');
       
  2661     $retrieved_file = system_retrieve_file($url);
       
  2662 
       
  2663     // URLs could not contains characters outside the ASCII set so $filename
       
  2664     // has to be encoded.
       
  2665     $encoded_filename = rawurlencode($filename);
       
  2666 
       
  2667     $this->assertEqual($retrieved_file, 'public://' . $encoded_filename, 'Sane path for downloaded file returned (public:// scheme).');
       
  2668     $this->assertTrue(is_file($retrieved_file), 'Downloaded file does exist (public:// scheme).');
       
  2669     $this->assertEqual(filesize($retrieved_file), 7, 'File size of downloaded file is correct (public:// scheme).');
       
  2670     file_unmanaged_delete($retrieved_file);
       
  2671 
       
  2672     // Test downloading file to a different location.
       
  2673     drupal_mkdir($targetdir = 'temporary://' . $this->randomName());
       
  2674     $retrieved_file = system_retrieve_file($url, $targetdir);
       
  2675     $this->assertEqual($retrieved_file, "$targetdir/$encoded_filename", 'Sane path for downloaded file returned (temporary:// scheme).');
       
  2676     $this->assertTrue(is_file($retrieved_file), 'Downloaded file does exist (temporary:// scheme).');
       
  2677     $this->assertEqual(filesize($retrieved_file), 7, 'File size of downloaded file is correct (temporary:// scheme).');
       
  2678     file_unmanaged_delete($retrieved_file);
       
  2679 
       
  2680     file_unmanaged_delete_recursive($sourcedir);
       
  2681     file_unmanaged_delete_recursive($targetdir);
       
  2682   }
       
  2683 }
       
  2684 
       
  2685 /**
       
  2686  * Functional tests shutdown functions.
       
  2687  */
       
  2688 class ShutdownFunctionsTest extends DrupalWebTestCase {
       
  2689   public static function getInfo() {
       
  2690     return array(
       
  2691       'name' => 'Shutdown functions',
       
  2692       'description' => 'Functional tests for shutdown functions',
       
  2693       'group' => 'System',
       
  2694     );
       
  2695   }
       
  2696 
       
  2697   function setUp() {
       
  2698     parent::setUp('system_test');
       
  2699   }
       
  2700 
       
  2701   /**
       
  2702    * Test shutdown functions.
       
  2703    */
       
  2704   function testShutdownFunctions() {
       
  2705     $arg1 = $this->randomName();
       
  2706     $arg2 = $this->randomName();
       
  2707     $this->drupalGet('system-test/shutdown-functions/' . $arg1 . '/' . $arg2);
       
  2708     $this->assertText(t('First shutdown function, arg1 : @arg1, arg2: @arg2', array('@arg1' => $arg1, '@arg2' => $arg2)));
       
  2709     $this->assertText(t('Second shutdown function, arg1 : @arg1, arg2: @arg2', array('@arg1' => $arg1, '@arg2' => $arg2)));
       
  2710 
       
  2711     // Make sure exceptions displayed through _drupal_render_exception_safe()
       
  2712     // are correctly escaped.
       
  2713     $this->assertRaw('Drupal is &amp;lt;blink&amp;gt;awesome&amp;lt;/blink&amp;gt;.');
       
  2714   }
       
  2715 }
       
  2716 
       
  2717 /**
       
  2718  * Tests administrative overview pages.
       
  2719  */
       
  2720 class SystemAdminTestCase extends DrupalWebTestCase {
       
  2721   public static function getInfo() {
       
  2722     return array(
       
  2723       'name' => 'Administrative pages',
       
  2724       'description' => 'Tests output on administrative pages and compact mode functionality.',
       
  2725       'group' => 'System',
       
  2726     );
       
  2727   }
       
  2728 
       
  2729   function setUp() {
       
  2730     // testAdminPages() requires Locale module.
       
  2731     parent::setUp(array('locale'));
       
  2732 
       
  2733     // Create an administrator with all permissions, as well as a regular user
       
  2734     // who can only access administration pages and perform some Locale module
       
  2735     // administrative tasks, but not all of them.
       
  2736     $this->admin_user = $this->drupalCreateUser(array_keys(module_invoke_all('permission')));
       
  2737     $this->web_user = $this->drupalCreateUser(array(
       
  2738       'access administration pages',
       
  2739       'translate interface',
       
  2740     ));
       
  2741     $this->drupalLogin($this->admin_user);
       
  2742   }
       
  2743 
       
  2744   /**
       
  2745    * Tests output on administrative listing pages.
       
  2746    */
       
  2747   function testAdminPages() {
       
  2748     // Go to Administration.
       
  2749     $this->drupalGet('admin');
       
  2750 
       
  2751     // Verify that all visible, top-level administration links are listed on
       
  2752     // the main administration page.
       
  2753     foreach (menu_get_router() as $path => $item) {
       
  2754       if (strpos($path, 'admin/') === 0 && ($item['type'] & MENU_VISIBLE_IN_TREE) && $item['_number_parts'] == 2) {
       
  2755         $this->assertLink($item['title']);
       
  2756         $this->assertLinkByHref($path);
       
  2757         $this->assertText($item['description']);
       
  2758       }
       
  2759     }
       
  2760 
       
  2761     // For each administrative listing page on which the Locale module appears,
       
  2762     // verify that there are links to the module's primary configuration pages,
       
  2763     // but no links to its individual sub-configuration pages. Also verify that
       
  2764     // a user with access to only some Locale module administration pages only
       
  2765     // sees links to the pages they have access to.
       
  2766     $admin_list_pages = array(
       
  2767       'admin/index',
       
  2768       'admin/config',
       
  2769       'admin/config/regional',
       
  2770     );
       
  2771 
       
  2772     foreach ($admin_list_pages as $page) {
       
  2773       // For the administrator, verify that there are links to Locale's primary
       
  2774       // configuration pages, but no links to individual sub-configuration
       
  2775       // pages.
       
  2776       $this->drupalLogin($this->admin_user);
       
  2777       $this->drupalGet($page);
       
  2778       $this->assertLinkByHref('admin/config');
       
  2779       $this->assertLinkByHref('admin/config/regional/settings');
       
  2780       $this->assertLinkByHref('admin/config/regional/date-time');
       
  2781       $this->assertLinkByHref('admin/config/regional/language');
       
  2782       $this->assertNoLinkByHref('admin/config/regional/language/configure/session');
       
  2783       $this->assertNoLinkByHref('admin/config/regional/language/configure/url');
       
  2784       $this->assertLinkByHref('admin/config/regional/translate');
       
  2785       // On admin/index only, the administrator should also see a "Configure
       
  2786       // permissions" link for the Locale module.
       
  2787       if ($page == 'admin/index') {
       
  2788         $this->assertLinkByHref("admin/people/permissions#module-locale");
       
  2789       }
       
  2790 
       
  2791       // For a less privileged user, verify that there are no links to Locale's
       
  2792       // primary configuration pages, but a link to the translate page exists.
       
  2793       $this->drupalLogin($this->web_user);
       
  2794       $this->drupalGet($page);
       
  2795       $this->assertLinkByHref('admin/config');
       
  2796       $this->assertNoLinkByHref('admin/config/regional/settings');
       
  2797       $this->assertNoLinkByHref('admin/config/regional/date-time');
       
  2798       $this->assertNoLinkByHref('admin/config/regional/language');
       
  2799       $this->assertNoLinkByHref('admin/config/regional/language/configure/session');
       
  2800       $this->assertNoLinkByHref('admin/config/regional/language/configure/url');
       
  2801       $this->assertLinkByHref('admin/config/regional/translate');
       
  2802       // This user cannot configure permissions, so even on admin/index should
       
  2803       // not see a "Configure permissions" link for the Locale module.
       
  2804       if ($page == 'admin/index') {
       
  2805         $this->assertNoLinkByHref("admin/people/permissions#module-locale");
       
  2806       }
       
  2807     }
       
  2808   }
       
  2809 
       
  2810   /**
       
  2811    * Test compact mode.
       
  2812    */
       
  2813   function testCompactMode() {
       
  2814     $this->drupalGet('admin/compact/on');
       
  2815     $this->assertTrue($this->cookies['Drupal.visitor.admin_compact_mode']['value'], 'Compact mode turns on.');
       
  2816     $this->drupalGet('admin/compact/on');
       
  2817     $this->assertTrue($this->cookies['Drupal.visitor.admin_compact_mode']['value'], 'Compact mode remains on after a repeat call.');
       
  2818     $this->drupalGet('');
       
  2819     $this->assertTrue($this->cookies['Drupal.visitor.admin_compact_mode']['value'], 'Compact mode persists on new requests.');
       
  2820 
       
  2821     $this->drupalGet('admin/compact/off');
       
  2822     $this->assertEqual($this->cookies['Drupal.visitor.admin_compact_mode']['value'], 'deleted', 'Compact mode turns off.');
       
  2823     $this->drupalGet('admin/compact/off');
       
  2824     $this->assertEqual($this->cookies['Drupal.visitor.admin_compact_mode']['value'], 'deleted', 'Compact mode remains off after a repeat call.');
       
  2825     $this->drupalGet('');
       
  2826     $this->assertTrue($this->cookies['Drupal.visitor.admin_compact_mode']['value'], 'Compact mode persists on new requests.');
       
  2827   }
       
  2828 }
       
  2829 
       
  2830 /**
       
  2831  * Tests authorize.php and related hooks.
       
  2832  */
       
  2833 class SystemAuthorizeCase extends DrupalWebTestCase {
       
  2834   public static function getInfo() {
       
  2835     return array(
       
  2836       'name' => 'Authorize API',
       
  2837       'description' => 'Tests the authorize.php script and related API.',
       
  2838       'group' => 'System',
       
  2839     );
       
  2840   }
       
  2841 
       
  2842   function setUp() {
       
  2843     parent::setUp(array('system_test'));
       
  2844 
       
  2845     variable_set('allow_authorize_operations', TRUE);
       
  2846 
       
  2847     // Create an administrator user.
       
  2848     $this->admin_user = $this->drupalCreateUser(array('administer software updates'));
       
  2849     $this->drupalLogin($this->admin_user);
       
  2850   }
       
  2851 
       
  2852   /**
       
  2853    * Helper function to initialize authorize.php and load it via drupalGet().
       
  2854    *
       
  2855    * Initializing authorize.php needs to happen in the child Drupal
       
  2856    * installation, not the parent. So, we visit a menu callback provided by
       
  2857    * system_test.module which calls system_authorized_init() to initialize the
       
  2858    * $_SESSION inside the test site, not the framework site. This callback
       
  2859    * redirects to authorize.php when it's done initializing.
       
  2860    *
       
  2861    * @see system_authorized_init().
       
  2862    */
       
  2863   function drupalGetAuthorizePHP($page_title = 'system-test-auth') {
       
  2864     $this->drupalGet('system-test/authorize-init/' . $page_title);
       
  2865   }
       
  2866 
       
  2867   /**
       
  2868    * Tests the FileTransfer hooks
       
  2869    */
       
  2870   function testFileTransferHooks() {
       
  2871     $page_title = $this->randomName(16);
       
  2872     $this->drupalGetAuthorizePHP($page_title);
       
  2873     $this->assertTitle(strtr('@title | Drupal', array('@title' => $page_title)), 'authorize.php page title is correct.');
       
  2874     $this->assertNoText('It appears you have reached this page in error.');
       
  2875     $this->assertText('To continue, provide your server connection details');
       
  2876     // Make sure we see the new connection method added by system_test.
       
  2877     $this->assertRaw('System Test FileTransfer');
       
  2878     // Make sure the settings form callback works.
       
  2879     $this->assertText('System Test Username');
       
  2880   }
       
  2881 }
       
  2882 
       
  2883 /**
       
  2884  * Test the handling of requests containing 'index.php'.
       
  2885  */
       
  2886 class SystemIndexPhpTest extends DrupalWebTestCase {
       
  2887   public static function getInfo() {
       
  2888     return array(
       
  2889       'name' => 'Index.php handling',
       
  2890       'description' => "Test the handling of requests containing 'index.php'.",
       
  2891       'group' => 'System',
       
  2892     );
       
  2893   }
       
  2894 
       
  2895   function setUp() {
       
  2896     parent::setUp();
       
  2897   }
       
  2898 
       
  2899   /**
       
  2900    * Test index.php handling.
       
  2901    */
       
  2902   function testIndexPhpHandling() {
       
  2903     $index_php = $GLOBALS['base_url'] . '/index.php';
       
  2904 
       
  2905     $this->drupalGet($index_php, array('external' => TRUE));
       
  2906     $this->assertResponse(200, 'Make sure index.php returns a valid page.');
       
  2907 
       
  2908     $this->drupalGet($index_php, array('external' => TRUE, 'query' => array('q' => 'user')));
       
  2909     $this->assertResponse(200, 'Make sure index.php?q=user returns a valid page.');
       
  2910 
       
  2911     $this->drupalGet($index_php .'/user', array('external' => TRUE));
       
  2912     $this->assertResponse(404, "Make sure index.php/user returns a 'page not found'.");
       
  2913   }
       
  2914 }
       
  2915 
       
  2916 /**
       
  2917  * Test token replacement in strings.
       
  2918  */
       
  2919 class TokenScanTest extends DrupalWebTestCase {
       
  2920 
       
  2921   public static function getInfo() {
       
  2922     return array(
       
  2923       'name' => 'Token scanning',
       
  2924       'description' => 'Scan token-like patterns in a dummy text to check token scanning.',
       
  2925       'group' => 'System',
       
  2926     );
       
  2927   }
       
  2928 
       
  2929   /**
       
  2930    * Scans dummy text, then tests the output.
       
  2931    */
       
  2932   function testTokenScan() {
       
  2933     // Define text with valid and not valid, fake and existing token-like
       
  2934     // strings.
       
  2935     $text = 'First a [valid:simple], but dummy token, and a dummy [valid:token with: spaces].';
       
  2936     $text .= 'Then a [not valid:token].';
       
  2937     $text .= 'Last an existing token: [node:author:name].';
       
  2938     $token_wannabes = token_scan($text);
       
  2939 
       
  2940     $this->assertTrue(isset($token_wannabes['valid']['simple']), 'A simple valid token has been matched.');
       
  2941     $this->assertTrue(isset($token_wannabes['valid']['token with: spaces']), 'A valid token with space characters in the token name has been matched.');
       
  2942     $this->assertFalse(isset($token_wannabes['not valid']), 'An invalid token with spaces in the token type has not been matched.');
       
  2943     $this->assertTrue(isset($token_wannabes['node']), 'An existing valid token has been matched.');
       
  2944   }
       
  2945 }
       
  2946 
       
  2947 /**
       
  2948  * Test case for drupal_valid_token().
       
  2949  */
       
  2950 class SystemValidTokenTest extends DrupalUnitTestCase {
       
  2951 
       
  2952   /**
       
  2953    * Flag to indicate whether PHP error reportings should be asserted.
       
  2954    *
       
  2955    * @var bool
       
  2956    */
       
  2957   protected $assertErrors = TRUE;
       
  2958 
       
  2959   public static function getInfo() {
       
  2960     return array(
       
  2961       'name' => 'Token validation',
       
  2962       'description' => 'Test the security token validation.',
       
  2963       'group' => 'System',
       
  2964     );
       
  2965   }
       
  2966 
       
  2967   /**
       
  2968    * Tests invalid invocations of drupal_valid_token() that must return FALSE.
       
  2969    */
       
  2970   public function testTokenValidation() {
       
  2971     // The following checks will throw PHP notices, so we disable error
       
  2972     // assertions.
       
  2973     $this->assertErrors = FALSE;
       
  2974     $this->assertFalse(drupal_valid_token(NULL, new stdClass()), 'Token NULL, value object returns FALSE.');
       
  2975     $this->assertFalse(drupal_valid_token(0, array()), 'Token 0, value array returns FALSE.');
       
  2976     $this->assertFalse(drupal_valid_token('', array()), "Token '', value array returns FALSE.");
       
  2977     $this->assertFalse('' === drupal_get_token(array()), 'Token generation does not return an empty string on invalid parameters.');
       
  2978     $this->assertErrors = TRUE;
       
  2979 
       
  2980     $this->assertFalse(drupal_valid_token(TRUE, 'foo'), 'Token TRUE, value foo returns FALSE.');
       
  2981     $this->assertFalse(drupal_valid_token(0, 'foo'), 'Token 0, value foo returns FALSE.');
       
  2982   }
       
  2983 
       
  2984   /**
       
  2985    * Overrides DrupalTestCase::errorHandler().
       
  2986    */
       
  2987   public function errorHandler($severity, $message, $file = NULL, $line = NULL) {
       
  2988     if ($this->assertErrors) {
       
  2989       return parent::errorHandler($severity, $message, $file, $line);
       
  2990     }
       
  2991     return TRUE;
       
  2992   }
       
  2993 }
       
  2994 
       
  2995 /**
       
  2996  * Tests drupal_set_message() and related functions.
       
  2997  */
       
  2998 class DrupalSetMessageTest extends DrupalWebTestCase {
       
  2999 
       
  3000   public static function getInfo() {
       
  3001     return array(
       
  3002       'name' => 'Messages',
       
  3003       'description' => 'Tests that messages can be displayed using drupal_set_message().',
       
  3004       'group' => 'System',
       
  3005     );
       
  3006   }
       
  3007 
       
  3008   function setUp() {
       
  3009     parent::setUp('system_test');
       
  3010   }
       
  3011 
       
  3012   /**
       
  3013    * Tests setting messages and removing one before it is displayed.
       
  3014    */
       
  3015   function testSetRemoveMessages() {
       
  3016     // The page at system-test/drupal-set-message sets two messages and then
       
  3017     // removes the first before it is displayed.
       
  3018     $this->drupalGet('system-test/drupal-set-message');
       
  3019     $this->assertNoText('First message (removed).');
       
  3020     $this->assertText('Second message (not removed).');
       
  3021   }
       
  3022 }
       
  3023 
       
  3024 /**
       
  3025  * Tests confirm form destinations.
       
  3026  */
       
  3027 class ConfirmFormTest extends DrupalWebTestCase {
       
  3028   protected $admin_user;
       
  3029 
       
  3030   public static function getInfo() {
       
  3031     return array(
       
  3032       'name' => 'Confirm form',
       
  3033       'description' => 'Tests that the confirm form does not use external destinations.',
       
  3034       'group' => 'System',
       
  3035     );
       
  3036   }
       
  3037 
       
  3038   function setUp() {
       
  3039     parent::setUp();
       
  3040 
       
  3041     $this->admin_user = $this->drupalCreateUser(array('administer users'));
       
  3042     $this->drupalLogin($this->admin_user);
       
  3043   }
       
  3044 
       
  3045   /**
       
  3046    * Tests that the confirm form does not use external destinations.
       
  3047    */
       
  3048   function testConfirmForm() {
       
  3049     $this->drupalGet('user/1/cancel');
       
  3050     $this->assertCancelLinkUrl(url('user/1'));
       
  3051     $this->drupalGet('user/1/cancel', array('query' => array('destination' => 'node')));
       
  3052     $this->assertCancelLinkUrl(url('node'));
       
  3053     $this->drupalGet('user/1/cancel', array('query' => array('destination' => 'http://example.com')));
       
  3054     $this->assertCancelLinkUrl(url('user/1'));
       
  3055   }
       
  3056 
       
  3057   /**
       
  3058    * Asserts that a cancel link is present pointing to the provided URL.
       
  3059    */
       
  3060   function assertCancelLinkUrl($url, $message = '', $group = 'Other') {
       
  3061     $links = $this->xpath('//a[normalize-space(text())=:label and @href=:url]', array(':label' => t('Cancel'), ':url' => $url));
       
  3062     $message = ($message ? $message : format_string('Cancel link with url %url found.', array('%url' => $url)));
       
  3063     return $this->assertTrue(isset($links[0]), $message, $group);
       
  3064   }
       
  3065 }