commiting uncommited changes on live site
[weblabels.fsf.org.git] / crm.fsf.org / 20131203 / files / modules / update / update.test
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 }