cms/drupal/modules/update/update.test
changeset 541 e756a8c72c3d
equal deleted inserted replaced
540:07239de796bb 541:e756a8c72c3d
       
     1 <?php
       
     2 
       
     3 /**
       
     4  * @file
       
     5  * This file contains tests for the Update Manager module.
       
     6  *
       
     7  * The overarching methodology of these tests is we need to compare a given
       
     8  * state of installed modules and themes (e.g., version, project grouping,
       
     9  * timestamps, etc) against a current state of what the release history XML
       
    10  * files we fetch say is available. We have dummy XML files (in the
       
    11  * modules/update/tests directory) that describe various scenarios of what's
       
    12  * available for different test projects, and we have dummy .info file data
       
    13  * (specified via hook_system_info_alter() in the update_test helper module)
       
    14  * describing what's currently installed. Each test case defines a set of
       
    15  * projects to install, their current state (via the 'update_test_system_info'
       
    16  * variable) and the desired available update data (via the
       
    17  * 'update_test_xml_map' variable), and then performs a series of assertions
       
    18  * that the report matches our expectations given the specific initial state and
       
    19  * availability scenario.
       
    20  */
       
    21 
       
    22 /**
       
    23  * Defines some shared functions used by all update tests.
       
    24  */
       
    25 class UpdateTestHelper extends DrupalWebTestCase {
       
    26 
       
    27   /**
       
    28    * Refreshes the update status based on the desired available update scenario.
       
    29    *
       
    30    * @param $xml_map
       
    31    *   Array that maps project names to availability scenarios to fetch. The key
       
    32    *   '#all' is used if a project-specific mapping is not defined.
       
    33    * @param $url
       
    34    *   (optional) A string containing the URL to fetch update data from.
       
    35    *   Defaults to 'update-test'.
       
    36    *
       
    37    * @see update_test_mock_page()
       
    38    */
       
    39   protected function refreshUpdateStatus($xml_map, $url = 'update-test') {
       
    40     // Tell the Update Manager module to fetch from the URL provided by
       
    41     // update_test module.
       
    42     variable_set('update_fetch_url', url($url, array('absolute' => TRUE)));
       
    43     // Save the map for update_test_mock_page() to use.
       
    44     variable_set('update_test_xml_map', $xml_map);
       
    45     // Manually check the update status.
       
    46     $this->drupalGet('admin/reports/updates/check');
       
    47   }
       
    48 
       
    49   /**
       
    50    * Runs a series of assertions that are applicable to all update statuses.
       
    51    */
       
    52   protected function standardTests() {
       
    53     $this->assertRaw('<h3>' . t('Drupal core') . '</h3>');
       
    54     $this->assertRaw(l(t('Drupal'), 'http://example.com/project/drupal'), 'Link to the Drupal project appears.');
       
    55     $this->assertNoText(t('No available releases found'));
       
    56   }
       
    57 
       
    58 }
       
    59 
       
    60 /**
       
    61  * Tests behavior related to discovering and listing updates to Drupal core.
       
    62  */
       
    63 class UpdateCoreTestCase extends UpdateTestHelper {
       
    64 
       
    65   public static function getInfo() {
       
    66     return array(
       
    67       'name' => 'Update core functionality',
       
    68       'description' => 'Tests the Update Manager module through a series of functional tests using mock XML data.',
       
    69       'group' => 'Update',
       
    70     );
       
    71   }
       
    72 
       
    73   function setUp() {
       
    74     parent::setUp('update_test', 'update');
       
    75     $admin_user = $this->drupalCreateUser(array('administer site configuration', 'administer modules'));
       
    76     $this->drupalLogin($admin_user);
       
    77   }
       
    78 
       
    79   /**
       
    80    * Tests the Update Manager module when no updates are available.
       
    81    */
       
    82   function testNoUpdatesAvailable() {
       
    83     $this->setSystemInfo7_0();
       
    84     $this->refreshUpdateStatus(array('drupal' => '0'));
       
    85     $this->standardTests();
       
    86     $this->assertText(t('Up to date'));
       
    87     $this->assertNoText(t('Update available'));
       
    88     $this->assertNoText(t('Security update required!'));
       
    89   }
       
    90 
       
    91   /**
       
    92    * Tests the Update Manager module when one normal update is available.
       
    93    */
       
    94   function testNormalUpdateAvailable() {
       
    95     $this->setSystemInfo7_0();
       
    96     $this->refreshUpdateStatus(array('drupal' => '1'));
       
    97     $this->standardTests();
       
    98     $this->assertNoText(t('Up to date'));
       
    99     $this->assertText(t('Update available'));
       
   100     $this->assertNoText(t('Security update required!'));
       
   101     $this->assertRaw(l('7.1', 'http://example.com/drupal-7-1-release'), 'Link to release appears.');
       
   102     $this->assertRaw(l(t('Download'), 'http://example.com/drupal-7-1.tar.gz'), 'Link to download appears.');
       
   103     $this->assertRaw(l(t('Release notes'), 'http://example.com/drupal-7-1-release'), 'Link to release notes appears.');
       
   104   }
       
   105 
       
   106   /**
       
   107    * Tests the Update Manager module when a security update is available.
       
   108    */
       
   109   function testSecurityUpdateAvailable() {
       
   110     $this->setSystemInfo7_0();
       
   111     $this->refreshUpdateStatus(array('drupal' => '2-sec'));
       
   112     $this->standardTests();
       
   113     $this->assertNoText(t('Up to date'));
       
   114     $this->assertNoText(t('Update available'));
       
   115     $this->assertText(t('Security update required!'));
       
   116     $this->assertRaw(l('7.2', 'http://example.com/drupal-7-2-release'), 'Link to release appears.');
       
   117     $this->assertRaw(l(t('Download'), 'http://example.com/drupal-7-2.tar.gz'), 'Link to download appears.');
       
   118     $this->assertRaw(l(t('Release notes'), 'http://example.com/drupal-7-2-release'), 'Link to release notes appears.');
       
   119   }
       
   120 
       
   121   /**
       
   122    * Ensures proper results where there are date mismatches among modules.
       
   123    */
       
   124   function testDatestampMismatch() {
       
   125     $system_info = array(
       
   126       '#all' => array(
       
   127         // We need to think we're running a -dev snapshot to see dates.
       
   128         'version' => '7.0-dev',
       
   129         'datestamp' => time(),
       
   130       ),
       
   131       'block' => array(
       
   132         // This is 2001-09-09 01:46:40 GMT, so test for "2001-Sep-".
       
   133         'datestamp' => '1000000000',
       
   134       ),
       
   135     );
       
   136     variable_set('update_test_system_info', $system_info);
       
   137     $this->refreshUpdateStatus(array('drupal' => 'dev'));
       
   138     $this->assertNoText(t('2001-Sep-'));
       
   139     $this->assertText(t('Up to date'));
       
   140     $this->assertNoText(t('Update available'));
       
   141     $this->assertNoText(t('Security update required!'));
       
   142   }
       
   143 
       
   144   /**
       
   145    * Checks that running cron updates the list of available updates.
       
   146    */
       
   147   function testModulePageRunCron() {
       
   148     $this->setSystemInfo7_0();
       
   149     variable_set('update_fetch_url', url('update-test', array('absolute' => TRUE)));
       
   150     variable_set('update_test_xml_map', array('drupal' => '0'));
       
   151 
       
   152     $this->cronRun();
       
   153     $this->drupalGet('admin/modules');
       
   154     $this->assertNoText(t('No update information available.'));
       
   155   }
       
   156 
       
   157   /**
       
   158    * Checks the messages at admin/modules when the site is up to date.
       
   159    */
       
   160   function testModulePageUpToDate() {
       
   161     $this->setSystemInfo7_0();
       
   162     // Instead of using refreshUpdateStatus(), set these manually.
       
   163     variable_set('update_fetch_url', url('update-test', array('absolute' => TRUE)));
       
   164     variable_set('update_test_xml_map', array('drupal' => '0'));
       
   165 
       
   166     $this->drupalGet('admin/reports/updates');
       
   167     $this->clickLink(t('Check manually'));
       
   168     $this->assertText(t('Checked available update data for one project.'));
       
   169     $this->drupalGet('admin/modules');
       
   170     $this->assertNoText(t('There are updates available for your version of Drupal.'));
       
   171     $this->assertNoText(t('There is a security update available for your version of Drupal.'));
       
   172   }
       
   173 
       
   174   /**
       
   175    * Checks the messages at admin/modules when an update is missing.
       
   176    */
       
   177   function testModulePageRegularUpdate() {
       
   178     $this->setSystemInfo7_0();
       
   179     // Instead of using refreshUpdateStatus(), set these manually.
       
   180     variable_set('update_fetch_url', url('update-test', array('absolute' => TRUE)));
       
   181     variable_set('update_test_xml_map', array('drupal' => '1'));
       
   182 
       
   183     $this->drupalGet('admin/reports/updates');
       
   184     $this->clickLink(t('Check manually'));
       
   185     $this->assertText(t('Checked available update data for one project.'));
       
   186     $this->drupalGet('admin/modules');
       
   187     $this->assertText(t('There are updates available for your version of Drupal.'));
       
   188     $this->assertNoText(t('There is a security update available for your version of Drupal.'));
       
   189   }
       
   190 
       
   191   /**
       
   192    * Checks the messages at admin/modules when a security update is missing.
       
   193    */
       
   194   function testModulePageSecurityUpdate() {
       
   195     $this->setSystemInfo7_0();
       
   196     // Instead of using refreshUpdateStatus(), set these manually.
       
   197     variable_set('update_fetch_url', url('update-test', array('absolute' => TRUE)));
       
   198     variable_set('update_test_xml_map', array('drupal' => '2-sec'));
       
   199 
       
   200     $this->drupalGet('admin/reports/updates');
       
   201     $this->clickLink(t('Check manually'));
       
   202     $this->assertText(t('Checked available update data for one project.'));
       
   203     $this->drupalGet('admin/modules');
       
   204     $this->assertNoText(t('There are updates available for your version of Drupal.'));
       
   205     $this->assertText(t('There is a security update available for your version of Drupal.'));
       
   206 
       
   207     // Make sure admin/appearance warns you you're missing a security update.
       
   208     $this->drupalGet('admin/appearance');
       
   209     $this->assertNoText(t('There are updates available for your version of Drupal.'));
       
   210     $this->assertText(t('There is a security update available for your version of Drupal.'));
       
   211 
       
   212     // Make sure duplicate messages don't appear on Update status pages.
       
   213     $this->drupalGet('admin/reports/status');
       
   214     // We're expecting "There is a security update..." inside the status report
       
   215     // itself, but the drupal_set_message() appears as an li so we can prefix
       
   216     // with that and search for the raw HTML.
       
   217     $this->assertNoRaw('<li>' . t('There is a security update available for your version of Drupal.'));
       
   218 
       
   219     $this->drupalGet('admin/reports/updates');
       
   220     $this->assertNoText(t('There is a security update available for your version of Drupal.'));
       
   221 
       
   222     $this->drupalGet('admin/reports/updates/settings');
       
   223     $this->assertNoText(t('There is a security update available for your version of Drupal.'));
       
   224   }
       
   225 
       
   226   /**
       
   227    * Tests the Update Manager module when the update server returns 503 errors.
       
   228    */
       
   229   function testServiceUnavailable() {
       
   230     $this->refreshUpdateStatus(array(), '503-error');
       
   231     // Ensure that no "Warning: SimpleXMLElement..." parse errors are found.
       
   232     $this->assertNoText('SimpleXMLElement');
       
   233     $this->assertUniqueText(t('Failed to get available update data for one project.'));
       
   234   }
       
   235 
       
   236   /**
       
   237    * Tests that exactly one fetch task per project is created and not more.
       
   238    */
       
   239   function testFetchTasks() {
       
   240     $projecta = array(
       
   241       'name' => 'aaa_update_test',
       
   242     );
       
   243     $projectb = array(
       
   244       'name' => 'bbb_update_test',
       
   245     );
       
   246     $queue = DrupalQueue::get('update_fetch_tasks');
       
   247     $this->assertEqual($queue->numberOfItems(), 0, 'Queue is empty');
       
   248     update_create_fetch_task($projecta);
       
   249     $this->assertEqual($queue->numberOfItems(), 1, 'Queue contains one item');
       
   250     update_create_fetch_task($projectb);
       
   251     $this->assertEqual($queue->numberOfItems(), 2, 'Queue contains two items');
       
   252     // Try to add project a again.
       
   253     update_create_fetch_task($projecta);
       
   254     $this->assertEqual($queue->numberOfItems(), 2, 'Queue still contains two items');
       
   255 
       
   256     // Clear cache and try again.
       
   257     _update_cache_clear();
       
   258     drupal_static_reset('_update_create_fetch_task');
       
   259     update_create_fetch_task($projecta);
       
   260     $this->assertEqual($queue->numberOfItems(), 2, 'Queue contains two items');
       
   261   }
       
   262 
       
   263   /**
       
   264    * Sets the version to 7.0 when no project-specific mapping is defined.
       
   265    */
       
   266   protected function setSystemInfo7_0() {
       
   267     $setting = array(
       
   268       '#all' => array(
       
   269         'version' => '7.0',
       
   270       ),
       
   271     );
       
   272     variable_set('update_test_system_info', $setting);
       
   273   }
       
   274 
       
   275 }
       
   276 
       
   277 /**
       
   278  * Tests behavior related to handling updates to contributed modules and themes.
       
   279  */
       
   280 class UpdateTestContribCase extends UpdateTestHelper {
       
   281 
       
   282   public static function getInfo() {
       
   283     return array(
       
   284       'name' => 'Update contrib functionality',
       
   285       'description' => 'Tests how the Update Manager module handles contributed modules and themes in a series of functional tests using mock XML data.',
       
   286       'group' => 'Update',
       
   287     );
       
   288   }
       
   289 
       
   290   function setUp() {
       
   291     parent::setUp('update_test', 'update', 'aaa_update_test', 'bbb_update_test', 'ccc_update_test');
       
   292     $admin_user = $this->drupalCreateUser(array('administer site configuration'));
       
   293     $this->drupalLogin($admin_user);
       
   294   }
       
   295 
       
   296   /**
       
   297    * Tests when there is no available release data for a contrib module.
       
   298    */
       
   299   function testNoReleasesAvailable() {
       
   300     $system_info = array(
       
   301       '#all' => array(
       
   302         'version' => '7.0',
       
   303       ),
       
   304       'aaa_update_test' => array(
       
   305         'project' => 'aaa_update_test',
       
   306         'version' => '7.x-1.0',
       
   307         'hidden' => FALSE,
       
   308       ),
       
   309     );
       
   310     variable_set('update_test_system_info', $system_info);
       
   311     $this->refreshUpdateStatus(array('drupal' => '0', 'aaa_update_test' => 'no-releases'));
       
   312     $this->drupalGet('admin/reports/updates');
       
   313     // Cannot use $this->standardTests() because we need to check for the
       
   314     // 'No available releases found' string.
       
   315     $this->assertRaw('<h3>' . t('Drupal core') . '</h3>');
       
   316     $this->assertRaw(l(t('Drupal'), 'http://example.com/project/drupal'));
       
   317     $this->assertText(t('Up to date'));
       
   318     $this->assertRaw('<h3>' . t('Modules') . '</h3>');
       
   319     $this->assertNoText(t('Update available'));
       
   320     $this->assertText(t('No available releases found'));
       
   321     $this->assertNoRaw(l(t('AAA Update test'), 'http://example.com/project/aaa_update_test'));
       
   322   }
       
   323 
       
   324   /**
       
   325    * Tests the basic functionality of a contrib module on the status report.
       
   326    */
       
   327   function testUpdateContribBasic() {
       
   328     $system_info = array(
       
   329       '#all' => array(
       
   330         'version' => '7.0',
       
   331       ),
       
   332       'aaa_update_test' => array(
       
   333         'project' => 'aaa_update_test',
       
   334         'version' => '7.x-1.0',
       
   335         'hidden' => FALSE,
       
   336       ),
       
   337     );
       
   338     variable_set('update_test_system_info', $system_info);
       
   339     $this->refreshUpdateStatus(
       
   340       array(
       
   341         'drupal' => '0',
       
   342         'aaa_update_test' => '1_0',
       
   343       )
       
   344     );
       
   345     $this->standardTests();
       
   346     $this->assertText(t('Up to date'));
       
   347     $this->assertRaw('<h3>' . t('Modules') . '</h3>');
       
   348     $this->assertNoText(t('Update available'));
       
   349     $this->assertRaw(l(t('AAA Update test'), 'http://example.com/project/aaa_update_test'), 'Link to aaa_update_test project appears.');
       
   350   }
       
   351 
       
   352   /**
       
   353    * Tests that contrib projects are ordered by project name.
       
   354    *
       
   355    * If a project contains multiple modules, we want to make sure that the
       
   356    * available updates report is sorted by the parent project names, not by the
       
   357    * names of the modules included in each project. In this test case, we have
       
   358    * two contrib projects, "BBB Update test" and "CCC Update test". However, we
       
   359    * have a module called "aaa_update_test" that's part of the "CCC Update test"
       
   360    * project. We need to make sure that we see the "BBB" project before the
       
   361    * "CCC" project, even though "CCC" includes a module that's processed first
       
   362    * if you sort alphabetically by module name (which is the order we see things
       
   363    * inside system_rebuild_module_data() for example).
       
   364    */
       
   365   function testUpdateContribOrder() {
       
   366     // We want core to be version 7.0.
       
   367     $system_info = array(
       
   368       '#all' => array(
       
   369         'version' => '7.0',
       
   370       ),
       
   371       // All the rest should be visible as contrib modules at version 7.x-1.0.
       
   372 
       
   373       // aaa_update_test needs to be part of the "CCC Update test" project,
       
   374       // which would throw off the report if we weren't properly sorting by
       
   375       // the project names.
       
   376       'aaa_update_test' => array(
       
   377         'project' => 'ccc_update_test',
       
   378         'version' => '7.x-1.0',
       
   379         'hidden' => FALSE,
       
   380       ),
       
   381 
       
   382       // This should be its own project, and listed first on the report.
       
   383       'bbb_update_test' => array(
       
   384         'project' => 'bbb_update_test',
       
   385         'version' => '7.x-1.0',
       
   386         'hidden' => FALSE,
       
   387       ),
       
   388 
       
   389       // This will contain both aaa_update_test and ccc_update_test, and
       
   390       // should come after the bbb_update_test project.
       
   391       'ccc_update_test' => array(
       
   392         'project' => 'ccc_update_test',
       
   393         'version' => '7.x-1.0',
       
   394         'hidden' => FALSE,
       
   395       ),
       
   396     );
       
   397     variable_set('update_test_system_info', $system_info);
       
   398     $this->refreshUpdateStatus(array('drupal' => '0', '#all' => '1_0'));
       
   399     $this->standardTests();
       
   400     // We're expecting the report to say all projects are up to date.
       
   401     $this->assertText(t('Up to date'));
       
   402     $this->assertNoText(t('Update available'));
       
   403     // We want to see all 3 module names listed, since they'll show up either
       
   404     // as project names or as modules under the "Includes" listing.
       
   405     $this->assertText(t('AAA Update test'));
       
   406     $this->assertText(t('BBB Update test'));
       
   407     $this->assertText(t('CCC Update test'));
       
   408     // We want aaa_update_test included in the ccc_update_test project, not as
       
   409     // its own project on the report.
       
   410     $this->assertNoRaw(l(t('AAA Update test'), 'http://example.com/project/aaa_update_test'), 'Link to aaa_update_test project does not appear.');
       
   411     // The other two should be listed as projects.
       
   412     $this->assertRaw(l(t('BBB Update test'), 'http://example.com/project/bbb_update_test'), 'Link to bbb_update_test project appears.');
       
   413     $this->assertRaw(l(t('CCC Update test'), 'http://example.com/project/ccc_update_test'), 'Link to bbb_update_test project appears.');
       
   414 
       
   415     // We want to make sure we see the BBB project before the CCC project.
       
   416     // Instead of just searching for 'BBB Update test' or something, we want
       
   417     // to use the full markup that starts the project entry itself, so that
       
   418     // we're really testing that the project listings are in the right order.
       
   419     $bbb_project_link = '<div class="project"><a href="http://example.com/project/bbb_update_test">BBB Update test</a>';
       
   420     $ccc_project_link = '<div class="project"><a href="http://example.com/project/ccc_update_test">CCC Update test</a>';
       
   421     $this->assertTrue(strpos($this->drupalGetContent(), $bbb_project_link) < strpos($this->drupalGetContent(), $ccc_project_link), "'BBB Update test' project is listed before the 'CCC Update test' project");
       
   422   }
       
   423 
       
   424   /**
       
   425    * Tests that subthemes are notified about security updates for base themes.
       
   426    */
       
   427   function testUpdateBaseThemeSecurityUpdate() {
       
   428     // Only enable the subtheme, not the base theme.
       
   429     db_update('system')
       
   430       ->fields(array('status' => 1))
       
   431       ->condition('type', 'theme')
       
   432       ->condition('name', 'update_test_subtheme')
       
   433       ->execute();
       
   434 
       
   435     // Define the initial state for core and the subtheme.
       
   436     $system_info = array(
       
   437       // We want core to be version 7.0.
       
   438       '#all' => array(
       
   439         'version' => '7.0',
       
   440       ),
       
   441       // Show the update_test_basetheme
       
   442       'update_test_basetheme' => array(
       
   443         'project' => 'update_test_basetheme',
       
   444         'version' => '7.x-1.0',
       
   445         'hidden' => FALSE,
       
   446       ),
       
   447       // Show the update_test_subtheme
       
   448       'update_test_subtheme' => array(
       
   449         'project' => 'update_test_subtheme',
       
   450         'version' => '7.x-1.0',
       
   451         'hidden' => FALSE,
       
   452       ),
       
   453     );
       
   454     variable_set('update_test_system_info', $system_info);
       
   455     $xml_mapping = array(
       
   456       'drupal' => '0',
       
   457       'update_test_subtheme' => '1_0',
       
   458       'update_test_basetheme' => '1_1-sec',
       
   459     );
       
   460     $this->refreshUpdateStatus($xml_mapping);
       
   461     $this->assertText(t('Security update required!'));
       
   462     $this->assertRaw(l(t('Update test base theme'), 'http://example.com/project/update_test_basetheme'), 'Link to the Update test base theme project appears.');
       
   463   }
       
   464 
       
   465   /**
       
   466    * Tests that the admin theme is always notified about security updates.
       
   467    */
       
   468   function testUpdateAdminThemeSecurityUpdate() {
       
   469     // Disable the admin theme.
       
   470     db_update('system')
       
   471       ->fields(array('status' => 0))
       
   472       ->condition('type', 'theme')
       
   473       ->condition('name', 'update_test_%', 'LIKE')
       
   474       ->execute();
       
   475 
       
   476     variable_set('admin_theme', 'update_test_admintheme');
       
   477 
       
   478     // Define the initial state for core and the themes.
       
   479     $system_info = array(
       
   480       '#all' => array(
       
   481         'version' => '7.0',
       
   482       ),
       
   483       'update_test_admintheme' => array(
       
   484         'project' => 'update_test_admintheme',
       
   485         'version' => '7.x-1.0',
       
   486         'hidden' => FALSE,
       
   487       ),
       
   488       'update_test_basetheme' => array(
       
   489         'project' => 'update_test_basetheme',
       
   490         'version' => '7.x-1.1',
       
   491         'hidden' => FALSE,
       
   492       ),
       
   493       'update_test_subtheme' => array(
       
   494         'project' => 'update_test_subtheme',
       
   495         'version' => '7.x-1.0',
       
   496         'hidden' => FALSE,
       
   497       ),
       
   498     );
       
   499     variable_set('update_test_system_info', $system_info);
       
   500     variable_set('update_check_disabled', FALSE);
       
   501     $xml_mapping = array(
       
   502       // This is enough because we don't check the update status of the admin
       
   503       // theme. We want to check that the admin theme is included in the list.
       
   504       'drupal' => '0',
       
   505     );
       
   506     $this->refreshUpdateStatus($xml_mapping);
       
   507     // The admin theme is displayed even if it's disabled.
       
   508     $this->assertText('update_test_admintheme', "The admin theme is checked for update even if it's disabled");
       
   509     // The other disabled themes are not displayed.
       
   510     $this->assertNoText('update_test_basetheme', 'Disabled theme is not checked for update in the list.');
       
   511     $this->assertNoText('update_test_subtheme', 'Disabled theme is not checked for update in the list.');
       
   512   }
       
   513 
       
   514   /**
       
   515    * Tests that disabled themes are only shown when desired.
       
   516    */
       
   517   function testUpdateShowDisabledThemes() {
       
   518     // Make sure all the update_test_* themes are disabled.
       
   519     db_update('system')
       
   520       ->fields(array('status' => 0))
       
   521       ->condition('type', 'theme')
       
   522       ->condition('name', 'update_test_%', 'LIKE')
       
   523       ->execute();
       
   524 
       
   525     // Define the initial state for core and the test contrib themes.
       
   526     $system_info = array(
       
   527       // We want core to be version 7.0.
       
   528       '#all' => array(
       
   529         'version' => '7.0',
       
   530       ),
       
   531       // The update_test_basetheme should be visible and up to date.
       
   532       'update_test_basetheme' => array(
       
   533         'project' => 'update_test_basetheme',
       
   534         'version' => '7.x-1.1',
       
   535         'hidden' => FALSE,
       
   536       ),
       
   537       // The update_test_subtheme should be visible and up to date.
       
   538       'update_test_subtheme' => array(
       
   539         'project' => 'update_test_subtheme',
       
   540         'version' => '7.x-1.0',
       
   541         'hidden' => FALSE,
       
   542       ),
       
   543     );
       
   544     // When there are contributed modules in the site's file system, the
       
   545     // total number of attempts made in the test may exceed the default value
       
   546     // of update_max_fetch_attempts. Therefore this variable is set very high
       
   547     // to avoid test failures in those cases.
       
   548     variable_set('update_max_fetch_attempts', 99999);
       
   549     variable_set('update_test_system_info', $system_info);
       
   550     $xml_mapping = array(
       
   551       'drupal' => '0',
       
   552       'update_test_subtheme' => '1_0',
       
   553       'update_test_basetheme' => '1_1-sec',
       
   554     );
       
   555     $base_theme_project_link = l(t('Update test base theme'), 'http://example.com/project/update_test_basetheme');
       
   556     $sub_theme_project_link = l(t('Update test subtheme'), 'http://example.com/project/update_test_subtheme');
       
   557     foreach (array(TRUE, FALSE) as $check_disabled) {
       
   558       variable_set('update_check_disabled', $check_disabled);
       
   559       $this->refreshUpdateStatus($xml_mapping);
       
   560       // In neither case should we see the "Themes" heading for enabled themes.
       
   561       $this->assertNoText(t('Themes'));
       
   562       if ($check_disabled) {
       
   563         $this->assertText(t('Disabled themes'));
       
   564         $this->assertRaw($base_theme_project_link, 'Link to the Update test base theme project appears.');
       
   565         $this->assertRaw($sub_theme_project_link, 'Link to the Update test subtheme project appears.');
       
   566       }
       
   567       else {
       
   568         $this->assertNoText(t('Disabled themes'));
       
   569         $this->assertNoRaw($base_theme_project_link, 'Link to the Update test base theme project does not appear.');
       
   570         $this->assertNoRaw($sub_theme_project_link, 'Link to the Update test subtheme project does not appear.');
       
   571       }
       
   572     }
       
   573   }
       
   574 
       
   575   /**
       
   576    * Makes sure that if we fetch from a broken URL, sane things happen.
       
   577    */
       
   578   function testUpdateBrokenFetchURL() {
       
   579     $system_info = array(
       
   580       '#all' => array(
       
   581         'version' => '7.0',
       
   582       ),
       
   583       'aaa_update_test' => array(
       
   584         'project' => 'aaa_update_test',
       
   585         'version' => '7.x-1.0',
       
   586         'hidden' => FALSE,
       
   587       ),
       
   588       'bbb_update_test' => array(
       
   589         'project' => 'bbb_update_test',
       
   590         'version' => '7.x-1.0',
       
   591         'hidden' => FALSE,
       
   592       ),
       
   593       'ccc_update_test' => array(
       
   594         'project' => 'ccc_update_test',
       
   595         'version' => '7.x-1.0',
       
   596         'hidden' => FALSE,
       
   597       ),
       
   598     );
       
   599     variable_set('update_test_system_info', $system_info);
       
   600 
       
   601     $xml_mapping = array(
       
   602       'drupal' => '0',
       
   603       'aaa_update_test' => '1_0',
       
   604       'bbb_update_test' => 'does-not-exist',
       
   605       'ccc_update_test' => '1_0',
       
   606     );
       
   607     $this->refreshUpdateStatus($xml_mapping);
       
   608 
       
   609     $this->assertText(t('Up to date'));
       
   610     // We're expecting the report to say most projects are up to date, so we
       
   611     // hope that 'Up to date' is not unique.
       
   612     $this->assertNoUniqueText(t('Up to date'));
       
   613     // It should say we failed to get data, not that we're missing an update.
       
   614     $this->assertNoText(t('Update available'));
       
   615 
       
   616     // We need to check that this string is found as part of a project row,
       
   617     // not just in the "Failed to get available update data for ..." message
       
   618     // at the top of the page.
       
   619     $this->assertRaw('<div class="version-status">' . t('Failed to get available update data'));
       
   620 
       
   621     // We should see the output messages from fetching manually.
       
   622     $this->assertUniqueText(t('Checked available update data for 3 projects.'));
       
   623     $this->assertUniqueText(t('Failed to get available update data for one project.'));
       
   624 
       
   625     // The other two should be listed as projects.
       
   626     $this->assertRaw(l(t('AAA Update test'), 'http://example.com/project/aaa_update_test'), 'Link to aaa_update_test project appears.');
       
   627     $this->assertNoRaw(l(t('BBB Update test'), 'http://example.com/project/bbb_update_test'), 'Link to bbb_update_test project does not appear.');
       
   628     $this->assertRaw(l(t('CCC Update test'), 'http://example.com/project/ccc_update_test'), 'Link to bbb_update_test project appears.');
       
   629   }
       
   630 
       
   631   /**
       
   632    * Checks that hook_update_status_alter() works to change a status.
       
   633    *
       
   634    * We provide the same external data as if aaa_update_test 7.x-1.0 were
       
   635    * installed and that was the latest release. Then we use
       
   636    * hook_update_status_alter() to try to mark this as missing a security
       
   637    * update, then assert if we see the appropriate warnings on the right pages.
       
   638    */
       
   639   function testHookUpdateStatusAlter() {
       
   640     variable_set('allow_authorize_operations', TRUE);
       
   641     $update_admin_user = $this->drupalCreateUser(array('administer site configuration', 'administer software updates'));
       
   642     $this->drupalLogin($update_admin_user);
       
   643 
       
   644     $system_info = array(
       
   645       '#all' => array(
       
   646         'version' => '7.0',
       
   647       ),
       
   648       'aaa_update_test' => array(
       
   649         'project' => 'aaa_update_test',
       
   650         'version' => '7.x-1.0',
       
   651         'hidden' => FALSE,
       
   652       ),
       
   653     );
       
   654     variable_set('update_test_system_info', $system_info);
       
   655     $update_status = array(
       
   656       'aaa_update_test' => array(
       
   657         'status' => UPDATE_NOT_SECURE,
       
   658       ),
       
   659     );
       
   660     variable_set('update_test_update_status', $update_status);
       
   661     $this->refreshUpdateStatus(
       
   662       array(
       
   663         'drupal' => '0',
       
   664         'aaa_update_test' => '1_0',
       
   665       )
       
   666     );
       
   667     $this->drupalGet('admin/reports/updates');
       
   668     $this->assertRaw('<h3>' . t('Modules') . '</h3>');
       
   669     $this->assertText(t('Security update required!'));
       
   670     $this->assertRaw(l(t('AAA Update test'), 'http://example.com/project/aaa_update_test'), 'Link to aaa_update_test project appears.');
       
   671 
       
   672     // Visit the reports page again without the altering and make sure the
       
   673     // status is back to normal.
       
   674     variable_set('update_test_update_status', array());
       
   675     $this->drupalGet('admin/reports/updates');
       
   676     $this->assertRaw('<h3>' . t('Modules') . '</h3>');
       
   677     $this->assertNoText(t('Security update required!'));
       
   678     $this->assertRaw(l(t('AAA Update test'), 'http://example.com/project/aaa_update_test'), 'Link to aaa_update_test project appears.');
       
   679 
       
   680     // Turn the altering back on and visit the Update manager UI.
       
   681     variable_set('update_test_update_status', $update_status);
       
   682     $this->drupalGet('admin/modules/update');
       
   683     $this->assertText(t('Security update'));
       
   684 
       
   685     // Turn the altering back off and visit the Update manager UI.
       
   686     variable_set('update_test_update_status', array());
       
   687     $this->drupalGet('admin/modules/update');
       
   688     $this->assertNoText(t('Security update'));
       
   689   }
       
   690 
       
   691 }
       
   692 
       
   693 /**
       
   694  * Tests project upload and extract functionality.
       
   695  */
       
   696 class UpdateTestUploadCase extends UpdateTestHelper {
       
   697 
       
   698   public static function getInfo() {
       
   699     return array(
       
   700       'name' => 'Upload and extract module functionality',
       
   701       'description' => 'Tests the Update Manager module\'s upload and extraction functionality.',
       
   702       'group' => 'Update',
       
   703     );
       
   704   }
       
   705 
       
   706   public function setUp() {
       
   707     parent::setUp('update', 'update_test');
       
   708     variable_set('allow_authorize_operations', TRUE);
       
   709     $admin_user = $this->drupalCreateUser(array('administer software updates', 'administer site configuration'));
       
   710     $this->drupalLogin($admin_user);
       
   711   }
       
   712 
       
   713   /**
       
   714    * Tests upload and extraction of a module.
       
   715    */
       
   716   public function testUploadModule() {
       
   717     // Images are not valid archives, so get one and try to install it. We
       
   718     // need an extra variable to store the result of drupalGetTestFiles()
       
   719     // since reset() takes an argument by reference and passing in a constant
       
   720     // emits a notice in strict mode.
       
   721     $imageTestFiles = $this->drupalGetTestFiles('image');
       
   722     $invalidArchiveFile = reset($imageTestFiles);
       
   723     $edit = array(
       
   724       'files[project_upload]' => $invalidArchiveFile->uri,
       
   725     );
       
   726     // This also checks that the correct archive extensions are allowed.
       
   727     $this->drupalPost('admin/modules/install', $edit, t('Install'));
       
   728     $this->assertText(t('Only files with the following extensions are allowed: @archive_extensions.', array('@archive_extensions' => archiver_get_extensions())),'Only valid archives can be uploaded.');
       
   729 
       
   730     // Check to ensure an existing module can't be reinstalled. Also checks that
       
   731     // the archive was extracted since we can't know if the module is already
       
   732     // installed until after extraction.
       
   733     $validArchiveFile = drupal_get_path('module', 'update') . '/tests/aaa_update_test.tar.gz';
       
   734     $edit = array(
       
   735       'files[project_upload]' => $validArchiveFile,
       
   736     );
       
   737     $this->drupalPost('admin/modules/install', $edit, t('Install'));
       
   738     $this->assertText(t('@module_name is already installed.', array('@module_name' => 'AAA Update test')), 'Existing module was extracted and not reinstalled.');
       
   739   }
       
   740 
       
   741   /**
       
   742    * Ensures that archiver extensions are properly merged in the UI.
       
   743    */
       
   744   function testFileNameExtensionMerging() {
       
   745     $this->drupalGet('admin/modules/install');
       
   746     // Make sure the bogus extension supported by update_test.module is there.
       
   747     $this->assertPattern('/file extensions are supported:.*update-test-extension/', "Found 'update-test-extension' extension");
       
   748     // Make sure it didn't clobber the first option from core.
       
   749     $this->assertPattern('/file extensions are supported:.*tar/', "Found 'tar' extension");
       
   750   }
       
   751 
       
   752   /**
       
   753    * Checks the messages on update manager pages when missing a security update.
       
   754    */
       
   755   function testUpdateManagerCoreSecurityUpdateMessages() {
       
   756     $setting = array(
       
   757       '#all' => array(
       
   758         'version' => '7.0',
       
   759       ),
       
   760     );
       
   761     variable_set('update_test_system_info', $setting);
       
   762     variable_set('update_fetch_url', url('update-test', array('absolute' => TRUE)));
       
   763     variable_set('update_test_xml_map', array('drupal' => '2-sec'));
       
   764     // Initialize the update status.
       
   765     $this->drupalGet('admin/reports/updates');
       
   766 
       
   767     // Now, make sure none of the Update manager pages have duplicate messages
       
   768     // about core missing a security update.
       
   769 
       
   770     $this->drupalGet('admin/modules/install');
       
   771     $this->assertNoText(t('There is a security update available for your version of Drupal.'));
       
   772 
       
   773     $this->drupalGet('admin/modules/update');
       
   774     $this->assertNoText(t('There is a security update available for your version of Drupal.'));
       
   775 
       
   776     $this->drupalGet('admin/appearance/install');
       
   777     $this->assertNoText(t('There is a security update available for your version of Drupal.'));
       
   778 
       
   779     $this->drupalGet('admin/appearance/update');
       
   780     $this->assertNoText(t('There is a security update available for your version of Drupal.'));
       
   781 
       
   782     $this->drupalGet('admin/reports/updates/install');
       
   783     $this->assertNoText(t('There is a security update available for your version of Drupal.'));
       
   784 
       
   785     $this->drupalGet('admin/reports/updates/update');
       
   786     $this->assertNoText(t('There is a security update available for your version of Drupal.'));
       
   787 
       
   788     $this->drupalGet('admin/update/ready');
       
   789     $this->assertNoText(t('There is a security update available for your version of Drupal.'));
       
   790   }
       
   791 
       
   792 }
       
   793 
       
   794 /**
       
   795  * Tests update functionality unrelated to the database.
       
   796  */
       
   797 class UpdateCoreUnitTestCase extends DrupalUnitTestCase {
       
   798 
       
   799   public static function getInfo() {
       
   800     return array(
       
   801       'name' => "Unit tests",
       
   802       'description' => 'Test update funcionality unrelated to the database.',
       
   803       'group' => 'Update',
       
   804     );
       
   805   }
       
   806 
       
   807   function setUp() {
       
   808     parent::setUp('update');
       
   809     module_load_include('inc', 'update', 'update.fetch');
       
   810   }
       
   811 
       
   812   /**
       
   813    * Tests that _update_build_fetch_url() builds the URL correctly.
       
   814    */
       
   815   function testUpdateBuildFetchUrl() {
       
   816     //first test that we didn't break the trivial case
       
   817     $project['name'] = 'update_test';
       
   818     $project['project_type'] = '';
       
   819     $project['info']['version'] = '';
       
   820     $project['info']['project status url'] = 'http://www.example.com';
       
   821     $project['includes'] = array('module1' => 'Module 1', 'module2' => 'Module 2');
       
   822     $site_key = '';
       
   823     $expected = 'http://www.example.com/' . $project['name'] . '/' . DRUPAL_CORE_COMPATIBILITY;
       
   824     $url = _update_build_fetch_url($project, $site_key);
       
   825     $this->assertEqual($url, $expected, "'$url' when no site_key provided should be '$expected'.");
       
   826 
       
   827     //For disabled projects it shouldn't add the site key either.
       
   828     $site_key = 'site_key';
       
   829     $project['project_type'] = 'disabled';
       
   830     $expected = 'http://www.example.com/' . $project['name'] . '/' . DRUPAL_CORE_COMPATIBILITY;
       
   831     $url = _update_build_fetch_url($project, $site_key);
       
   832     $this->assertEqual($url, $expected, "'$url' should be '$expected' for disabled projects.");
       
   833 
       
   834     //for enabled projects, adding the site key
       
   835     $project['project_type'] = '';
       
   836     $expected = 'http://www.example.com/' . $project['name'] . '/' . DRUPAL_CORE_COMPATIBILITY;
       
   837     $expected .= '?site_key=site_key';
       
   838     $expected .= '&list=' . rawurlencode('module1,module2');
       
   839     $url = _update_build_fetch_url($project, $site_key);
       
   840     $this->assertEqual($url, $expected, "When site_key provided, '$url' should be '$expected'.");
       
   841 
       
   842     // http://drupal.org/node/1481156 test incorrect logic when URL contains
       
   843     // a question mark.
       
   844     $project['info']['project status url'] = 'http://www.example.com/?project=';
       
   845     $expected = 'http://www.example.com/?project=/' . $project['name'] . '/' . DRUPAL_CORE_COMPATIBILITY;
       
   846     $expected .= '&site_key=site_key';
       
   847     $expected .= '&list=' . rawurlencode('module1,module2');
       
   848     $url = _update_build_fetch_url($project, $site_key);
       
   849     $this->assertEqual($url, $expected, "When ? is present, '$url' should be '$expected'.");
       
   850 
       
   851   }
       
   852 }