Project

General

Profile

Paste
Download (34.1 KB) Statistics
| Branch: | Revision:

root / drupal7 / modules / update / update.test @ b0dc3a2e

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
}