Projet

Général

Profil

Paste
Télécharger (32,3 ko) Statistiques
| Branche: | Révision:

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

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 disabled themes are only shown when desired.
467
   */
468
  function testUpdateShowDisabledThemes() {
469
    // Make sure all the update_test_* themes are disabled.
470
    db_update('system')
471
      ->fields(array('status' => 0))
472
      ->condition('type', 'theme')
473
      ->condition('name', 'update_test_%', 'LIKE')
474
      ->execute();
475

    
476
    // Define the initial state for core and the test contrib themes.
477
    $system_info = array(
478
      // We want core to be version 7.0.
479
      '#all' => array(
480
        'version' => '7.0',
481
      ),
482
      // The update_test_basetheme should be visible and up to date.
483
      'update_test_basetheme' => array(
484
        'project' => 'update_test_basetheme',
485
        'version' => '7.x-1.1',
486
        'hidden' => FALSE,
487
      ),
488
      // The update_test_subtheme should be visible and up to date.
489
      'update_test_subtheme' => array(
490
        'project' => 'update_test_subtheme',
491
        'version' => '7.x-1.0',
492
        'hidden' => FALSE,
493
      ),
494
    );
495
    // When there are contributed modules in the site's file system, the
496
    // total number of attempts made in the test may exceed the default value
497
    // of update_max_fetch_attempts. Therefore this variable is set very high
498
    // to avoid test failures in those cases.
499
    variable_set('update_max_fetch_attempts', 99999);
500
    variable_set('update_test_system_info', $system_info);
501
    $xml_mapping = array(
502
      'drupal' => '0',
503
      'update_test_subtheme' => '1_0',
504
      'update_test_basetheme' => '1_1-sec',
505
    );
506
    $base_theme_project_link = l(t('Update test base theme'), 'http://example.com/project/update_test_basetheme');
507
    $sub_theme_project_link = l(t('Update test subtheme'), 'http://example.com/project/update_test_subtheme');
508
    foreach (array(TRUE, FALSE) as $check_disabled) {
509
      variable_set('update_check_disabled', $check_disabled);
510
      $this->refreshUpdateStatus($xml_mapping);
511
      // In neither case should we see the "Themes" heading for enabled themes.
512
      $this->assertNoText(t('Themes'));
513
      if ($check_disabled) {
514
        $this->assertText(t('Disabled themes'));
515
        $this->assertRaw($base_theme_project_link, 'Link to the Update test base theme project appears.');
516
        $this->assertRaw($sub_theme_project_link, 'Link to the Update test subtheme project appears.');
517
      }
518
      else {
519
        $this->assertNoText(t('Disabled themes'));
520
        $this->assertNoRaw($base_theme_project_link, 'Link to the Update test base theme project does not appear.');
521
        $this->assertNoRaw($sub_theme_project_link, 'Link to the Update test subtheme project does not appear.');
522
      }
523
    }
524
  }
525

    
526
  /**
527
   * Makes sure that if we fetch from a broken URL, sane things happen.
528
   */
529
  function testUpdateBrokenFetchURL() {
530
    $system_info = array(
531
      '#all' => array(
532
        'version' => '7.0',
533
      ),
534
      'aaa_update_test' => array(
535
        'project' => 'aaa_update_test',
536
        'version' => '7.x-1.0',
537
        'hidden' => FALSE,
538
      ),
539
      'bbb_update_test' => array(
540
        'project' => 'bbb_update_test',
541
        'version' => '7.x-1.0',
542
        'hidden' => FALSE,
543
      ),
544
      'ccc_update_test' => array(
545
        'project' => 'ccc_update_test',
546
        'version' => '7.x-1.0',
547
        'hidden' => FALSE,
548
      ),
549
    );
550
    variable_set('update_test_system_info', $system_info);
551

    
552
    $xml_mapping = array(
553
      'drupal' => '0',
554
      'aaa_update_test' => '1_0',
555
      'bbb_update_test' => 'does-not-exist',
556
      'ccc_update_test' => '1_0',
557
    );
558
    $this->refreshUpdateStatus($xml_mapping);
559

    
560
    $this->assertText(t('Up to date'));
561
    // We're expecting the report to say most projects are up to date, so we
562
    // hope that 'Up to date' is not unique.
563
    $this->assertNoUniqueText(t('Up to date'));
564
    // It should say we failed to get data, not that we're missing an update.
565
    $this->assertNoText(t('Update available'));
566

    
567
    // We need to check that this string is found as part of a project row,
568
    // not just in the "Failed to get available update data for ..." message
569
    // at the top of the page.
570
    $this->assertRaw('<div class="version-status">' . t('Failed to get available update data'));
571

    
572
    // We should see the output messages from fetching manually.
573
    $this->assertUniqueText(t('Checked available update data for 3 projects.'));
574
    $this->assertUniqueText(t('Failed to get available update data for one project.'));
575

    
576
    // The other two should be listed as projects.
577
    $this->assertRaw(l(t('AAA Update test'), 'http://example.com/project/aaa_update_test'), 'Link to aaa_update_test project appears.');
578
    $this->assertNoRaw(l(t('BBB Update test'), 'http://example.com/project/bbb_update_test'), 'Link to bbb_update_test project does not appear.');
579
    $this->assertRaw(l(t('CCC Update test'), 'http://example.com/project/ccc_update_test'), 'Link to bbb_update_test project appears.');
580
  }
581

    
582
  /**
583
   * Checks that hook_update_status_alter() works to change a status.
584
   *
585
   * We provide the same external data as if aaa_update_test 7.x-1.0 were
586
   * installed and that was the latest release. Then we use
587
   * hook_update_status_alter() to try to mark this as missing a security
588
   * update, then assert if we see the appropriate warnings on the right pages.
589
   */
590
  function testHookUpdateStatusAlter() {
591
    variable_set('allow_authorize_operations', TRUE);
592
    $update_admin_user = $this->drupalCreateUser(array('administer site configuration', 'administer software updates'));
593
    $this->drupalLogin($update_admin_user);
594

    
595
    $system_info = array(
596
      '#all' => array(
597
        'version' => '7.0',
598
      ),
599
      'aaa_update_test' => array(
600
        'project' => 'aaa_update_test',
601
        'version' => '7.x-1.0',
602
        'hidden' => FALSE,
603
      ),
604
    );
605
    variable_set('update_test_system_info', $system_info);
606
    $update_status = array(
607
      'aaa_update_test' => array(
608
        'status' => UPDATE_NOT_SECURE,
609
      ),
610
    );
611
    variable_set('update_test_update_status', $update_status);
612
    $this->refreshUpdateStatus(
613
      array(
614
        'drupal' => '0',
615
        'aaa_update_test' => '1_0',
616
      )
617
    );
618
    $this->drupalGet('admin/reports/updates');
619
    $this->assertRaw('<h3>' . t('Modules') . '</h3>');
620
    $this->assertText(t('Security update required!'));
621
    $this->assertRaw(l(t('AAA Update test'), 'http://example.com/project/aaa_update_test'), 'Link to aaa_update_test project appears.');
622

    
623
    // Visit the reports page again without the altering and make sure the
624
    // status is back to normal.
625
    variable_set('update_test_update_status', array());
626
    $this->drupalGet('admin/reports/updates');
627
    $this->assertRaw('<h3>' . t('Modules') . '</h3>');
628
    $this->assertNoText(t('Security update required!'));
629
    $this->assertRaw(l(t('AAA Update test'), 'http://example.com/project/aaa_update_test'), 'Link to aaa_update_test project appears.');
630

    
631
    // Turn the altering back on and visit the Update manager UI.
632
    variable_set('update_test_update_status', $update_status);
633
    $this->drupalGet('admin/modules/update');
634
    $this->assertText(t('Security update'));
635

    
636
    // Turn the altering back off and visit the Update manager UI.
637
    variable_set('update_test_update_status', array());
638
    $this->drupalGet('admin/modules/update');
639
    $this->assertNoText(t('Security update'));
640
  }
641

    
642
}
643

    
644
/**
645
 * Tests project upload and extract functionality.
646
 */
647
class UpdateTestUploadCase extends UpdateTestHelper {
648

    
649
  public static function getInfo() {
650
    return array(
651
      'name' => 'Upload and extract module functionality',
652
      'description' => 'Tests the Update Manager module\'s upload and extraction functionality.',
653
      'group' => 'Update',
654
    );
655
  }
656

    
657
  public function setUp() {
658
    parent::setUp('update', 'update_test');
659
    variable_set('allow_authorize_operations', TRUE);
660
    $admin_user = $this->drupalCreateUser(array('administer software updates', 'administer site configuration'));
661
    $this->drupalLogin($admin_user);
662
  }
663

    
664
  /**
665
   * Tests upload and extraction of a module.
666
   */
667
  public function testUploadModule() {
668
    // Images are not valid archives, so get one and try to install it. We
669
    // need an extra variable to store the result of drupalGetTestFiles()
670
    // since reset() takes an argument by reference and passing in a constant
671
    // emits a notice in strict mode.
672
    $imageTestFiles = $this->drupalGetTestFiles('image');
673
    $invalidArchiveFile = reset($imageTestFiles);
674
    $edit = array(
675
      'files[project_upload]' => $invalidArchiveFile->uri,
676
    );
677
    // This also checks that the correct archive extensions are allowed.
678
    $this->drupalPost('admin/modules/install', $edit, t('Install'));
679
    $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.');
680

    
681
    // Check to ensure an existing module can't be reinstalled. Also checks that
682
    // the archive was extracted since we can't know if the module is already
683
    // installed until after extraction.
684
    $validArchiveFile = drupal_get_path('module', 'update') . '/tests/aaa_update_test.tar.gz';
685
    $edit = array(
686
      'files[project_upload]' => $validArchiveFile,
687
    );
688
    $this->drupalPost('admin/modules/install', $edit, t('Install'));
689
    $this->assertText(t('@module_name is already installed.', array('@module_name' => 'AAA Update test')), 'Existing module was extracted and not reinstalled.');
690
  }
691

    
692
  /**
693
   * Ensures that archiver extensions are properly merged in the UI.
694
   */
695
  function testFileNameExtensionMerging() {
696
    $this->drupalGet('admin/modules/install');
697
    // Make sure the bogus extension supported by update_test.module is there.
698
    $this->assertPattern('/file extensions are supported:.*update-test-extension/', "Found 'update-test-extension' extension");
699
    // Make sure it didn't clobber the first option from core.
700
    $this->assertPattern('/file extensions are supported:.*tar/', "Found 'tar' extension");
701
  }
702

    
703
  /**
704
   * Checks the messages on update manager pages when missing a security update.
705
   */
706
  function testUpdateManagerCoreSecurityUpdateMessages() {
707
    $setting = array(
708
      '#all' => array(
709
        'version' => '7.0',
710
      ),
711
    );
712
    variable_set('update_test_system_info', $setting);
713
    variable_set('update_fetch_url', url('update-test', array('absolute' => TRUE)));
714
    variable_set('update_test_xml_map', array('drupal' => '2-sec'));
715
    // Initialize the update status.
716
    $this->drupalGet('admin/reports/updates');
717

    
718
    // Now, make sure none of the Update manager pages have duplicate messages
719
    // about core missing a security update.
720

    
721
    $this->drupalGet('admin/modules/install');
722
    $this->assertNoText(t('There is a security update available for your version of Drupal.'));
723

    
724
    $this->drupalGet('admin/modules/update');
725
    $this->assertNoText(t('There is a security update available for your version of Drupal.'));
726

    
727
    $this->drupalGet('admin/appearance/install');
728
    $this->assertNoText(t('There is a security update available for your version of Drupal.'));
729

    
730
    $this->drupalGet('admin/appearance/update');
731
    $this->assertNoText(t('There is a security update available for your version of Drupal.'));
732

    
733
    $this->drupalGet('admin/reports/updates/install');
734
    $this->assertNoText(t('There is a security update available for your version of Drupal.'));
735

    
736
    $this->drupalGet('admin/reports/updates/update');
737
    $this->assertNoText(t('There is a security update available for your version of Drupal.'));
738

    
739
    $this->drupalGet('admin/update/ready');
740
    $this->assertNoText(t('There is a security update available for your version of Drupal.'));
741
  }
742

    
743
}
744

    
745
/**
746
 * Tests update functionality unrelated to the database.
747
 */
748
class UpdateCoreUnitTestCase extends DrupalUnitTestCase {
749

    
750
  public static function getInfo() {
751
    return array(
752
      'name' => "Unit tests",
753
      'description' => 'Test update funcionality unrelated to the database.',
754
      'group' => 'Update',
755
    );
756
  }
757

    
758
  function setUp() {
759
    parent::setUp('update');
760
    module_load_include('inc', 'update', 'update.fetch');
761
  }
762

    
763
  /**
764
   * Tests that _update_build_fetch_url() builds the URL correctly.
765
   */
766
  function testUpdateBuildFetchUrl() {
767
    //first test that we didn't break the trivial case
768
    $project['name'] = 'update_test';
769
    $project['project_type'] = '';
770
    $project['info']['version'] = '';
771
    $project['info']['project status url'] = 'http://www.example.com';
772
    $project['includes'] = array('module1' => 'Module 1', 'module2' => 'Module 2');
773
    $site_key = '';
774
    $expected = 'http://www.example.com/' . $project['name'] . '/' . DRUPAL_CORE_COMPATIBILITY;
775
    $url = _update_build_fetch_url($project, $site_key);
776
    $this->assertEqual($url, $expected, "'$url' when no site_key provided should be '$expected'.");
777

    
778
    //For disabled projects it shouldn't add the site key either.
779
    $site_key = 'site_key';
780
    $project['project_type'] = 'disabled';
781
    $expected = 'http://www.example.com/' . $project['name'] . '/' . DRUPAL_CORE_COMPATIBILITY;
782
    $url = _update_build_fetch_url($project, $site_key);
783
    $this->assertEqual($url, $expected, "'$url' should be '$expected' for disabled projects.");
784

    
785
    //for enabled projects, adding the site key
786
    $project['project_type'] = '';
787
    $expected = 'http://www.example.com/' . $project['name'] . '/' . DRUPAL_CORE_COMPATIBILITY;
788
    $expected .= '?site_key=site_key';
789
    $expected .= '&list=' . rawurlencode('module1,module2');
790
    $url = _update_build_fetch_url($project, $site_key);
791
    $this->assertEqual($url, $expected, "When site_key provided, '$url' should be '$expected'.");
792

    
793
    // http://drupal.org/node/1481156 test incorrect logic when URL contains
794
    // a question mark.
795
    $project['info']['project status url'] = 'http://www.example.com/?project=';
796
    $expected = 'http://www.example.com/?project=/' . $project['name'] . '/' . DRUPAL_CORE_COMPATIBILITY;
797
    $expected .= '&site_key=site_key';
798
    $expected .= '&list=' . rawurlencode('module1,module2');
799
    $url = _update_build_fetch_url($project, $site_key);
800
    $this->assertEqual($url, $expected, "When ? is present, '$url' should be '$expected'.");
801

    
802
  }
803
}