summaryrefslogtreecommitdiff
path: root/modules/simpletest/tests/bootstrap.test
blob: 333f34b76177198b36eb547c92fcc1d2078c8fee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
<?php
// $Id$

class BootstrapIPAddressTestCase extends DrupalWebTestCase {

  public static function getInfo() {
    return array(
      'name' => 'IP address and HTTP_HOST test',
      'description' => 'Get the IP address from the current visitor from the server variables, check hostname validation.',
      'group' => 'Bootstrap'
    );
  }

  function setUp() {
    $this->oldserver = $_SERVER;

    $this->remote_ip = '127.0.0.1';
    $this->proxy_ip = '127.0.0.2';
    $this->proxy2_ip = '127.0.0.3';
    $this->forwarded_ip = '127.0.0.4';
    $this->cluster_ip = '127.0.0.5';
    $this->untrusted_ip = '0.0.0.0';

    drupal_static_reset('ip_address');

    $_SERVER['REMOTE_ADDR'] = $this->remote_ip;
    unset($_SERVER['HTTP_X_FORWARDED_FOR']);
    unset($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']);

    parent::setUp();
  }

  function tearDown() {
    $_SERVER = $this->oldserver;
    drupal_static_reset('ip_address');
    parent::tearDown();
  }

  /**
   * test IP Address and hostname
   */
  function testIPAddressHost() {
    // Test the normal IP address.
    $this->assertTrue(
      ip_address() == $this->remote_ip,
      t('Got remote IP address.')
    );

    // Proxy forwarding on but no proxy addresses defined.
    variable_set('reverse_proxy', 1);
    $this->assertTrue(
      ip_address() == $this->remote_ip,
      t('Proxy forwarding without trusted proxies got remote IP address.')
    );

    // Proxy forwarding on and proxy address not trusted.
    variable_set('reverse_proxy_addresses', array($this->proxy_ip, $this->proxy2_ip));
    drupal_static_reset('ip_address');
    $_SERVER['REMOTE_ADDR'] = $this->untrusted_ip;
    $this->assertTrue(
      ip_address() == $this->untrusted_ip,
      t('Proxy forwarding with untrusted proxy got remote IP address.')
    );

    // Proxy forwarding on and proxy address trusted.
    $_SERVER['REMOTE_ADDR'] = $this->proxy_ip;
    $_SERVER['HTTP_X_FORWARDED_FOR'] = $this->forwarded_ip;
    drupal_static_reset('ip_address');
    $this->assertTrue(
      ip_address() == $this->forwarded_ip,
      t('Proxy forwarding with trusted proxy got forwarded IP address.')
    );

    // Multi-tier architecture with comma separated values in header.
    $_SERVER['REMOTE_ADDR'] = $this->proxy_ip;
    $_SERVER['HTTP_X_FORWARDED_FOR'] = implode(', ', array($this->untrusted_ip, $this->forwarded_ip, $this->proxy2_ip));
    drupal_static_reset('ip_address');
    $this->assertTrue(
      ip_address() == $this->forwarded_ip,
      t('Proxy forwarding with trusted 2-tier proxy got forwarded IP address.')
    );

    // Custom client-IP header.
    variable_set('reverse_proxy_header', 'HTTP_X_CLUSTER_CLIENT_IP');
    $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'] = $this->cluster_ip;
    drupal_static_reset('ip_address');
    $this->assertTrue(
      ip_address() == $this->cluster_ip,
      t('Cluster environment got cluster client IP.')
    );

    // Verifies that drupal_valid_http_host() prevents invalid characters.
    $this->assertFalse(drupal_valid_http_host('security/.drupal.org:80'), t('HTTP_HOST with / is invalid'));
    $this->assertFalse(drupal_valid_http_host('security\\.drupal.org:80'), t('HTTP_HOST with \\ is invalid'));
    $this->assertFalse(drupal_valid_http_host('security<.drupal.org:80'), t('HTTP_HOST with &lt; is invalid'));
    $this->assertFalse(drupal_valid_http_host('security..drupal.org:80'), t('HTTP_HOST with .. is invalid'));
    // IPv6 loopback address
    $this->assertTrue(drupal_valid_http_host('[::1]:80'), t('HTTP_HOST containing IPv6 loopback is valid'));
  }
}

class BootstrapPageCacheTestCase extends DrupalWebTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Page cache test',
      'description' => 'Enable the page cache and test it with various HTTP requests.',
      'group' => 'Bootstrap'
    );
  }

  function setUp() {
    parent::setUp('system_test');
  }

  /**
   * Test support for requests containing If-Modified-Since and If-None-Match headers.
   */
  function testConditionalRequests() {
    variable_set('cache', 1);

    // Fill the cache.
    $this->drupalGet('');

    $this->drupalHead('');
    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.'));
    $etag = $this->drupalGetHeader('ETag');
    $last_modified = $this->drupalGetHeader('Last-Modified');

    $this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified, 'If-None-Match: ' . $etag));
    $this->assertResponse(304, t('Conditional request returned 304 Not Modified.'));

    $this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC822, strtotime($last_modified)), 'If-None-Match: ' . $etag));
    $this->assertResponse(304, t('Conditional request with obsolete If-Modified-Since date returned 304 Not Modified.'));

    $this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC850, strtotime($last_modified)), 'If-None-Match: ' . $etag));
    $this->assertResponse(304, t('Conditional request with obsolete If-Modified-Since date returned 304 Not Modified.'));

    $this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified));
    $this->assertResponse(200, t('Conditional request without If-None-Match returned 200 OK.'));
    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.'));

    $this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC1123, strtotime($last_modified) + 1), 'If-None-Match: ' . $etag));
    $this->assertResponse(200, t('Conditional request with new a If-Modified-Since date newer than Last-Modified returned 200 OK.'));
    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.'));

    $user = $this->drupalCreateUser();
    $this->drupalLogin($user);
    $this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified, 'If-None-Match: ' . $etag));
    $this->assertResponse(200, t('Conditional request returned 200 OK for authenticated user.'));
    $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), t('Absense of Page was not cached.'));
  }

  /**
   * Test cache headers.
   */
  function testPageCache() {
    variable_set('cache', 1);

    // Fill the cache.
    $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', t('Page was not cached.'));
    $this->assertEqual($this->drupalGetHeader('Vary'), 'Cookie,Accept-Encoding', t('Vary header was sent.'));
    $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'public, max-age=0', t('Cache-Control header was sent.'));
    $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', t('Expires header was sent.'));
    $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', t('Custom header was sent.'));

    // Check cache.
    $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.'));
    $this->assertEqual($this->drupalGetHeader('Vary'), 'Cookie,Accept-Encoding', t('Vary: Cookie header was sent.'));
    $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'public, max-age=0', t('Cache-Control header was sent.'));
    $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', t('Expires header was sent.'));
    $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', t('Custom header was sent.'));

    // Check replacing default headers.
    $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Expires', 'value' => 'Fri, 19 Nov 2008 05:00:00 GMT')));
    $this->assertEqual($this->drupalGetHeader('Expires'), 'Fri, 19 Nov 2008 05:00:00 GMT', t('Default header was replaced.'));
    $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Vary', 'value' => 'User-Agent')));
    $this->assertEqual($this->drupalGetHeader('Vary'), 'User-Agent,Accept-Encoding', t('Default header was replaced.'));

    // Check that authenticated users bypass the cache.
    $user = $this->drupalCreateUser();
    $this->drupalLogin($user);
    $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
    $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), t('Caching was bypassed.'));
    $this->assertTrue(strpos($this->drupalGetHeader('Vary'), 'Cookie') === FALSE, t('Vary: Cookie header was not sent.'));
    $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'no-cache, must-revalidate, post-check=0, pre-check=0', t('Cache-Control header was sent.'));
    $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', t('Expires header was sent.'));
    $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', t('Custom header was sent.'));

  }

  /**
   * Test page compression.
   *
   * The test should pass even if zlib.output_compression is enabled in php.ini,
   * .htaccess or similar, or if compression is done outside PHP, e.g. by the
   * mod_deflate Apache module.
   */
  function testPageCompression() {
    variable_set('cache', 1);

    // Fill the cache and verify that output is compressed.
    $this->drupalGet('', array(), array('Accept-Encoding: gzip,deflate'));
    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', t('Page was not cached.'));
    $this->drupalSetContent(gzinflate(substr($this->drupalGetContent(), 10, -8)));
    $this->assertRaw('</html>', t('Page was gzip compressed.'));

    // Verify that cached output is compressed.
    $this->drupalGet('', array(), array('Accept-Encoding: gzip,deflate'));
    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.'));
    $this->assertEqual($this->drupalGetHeader('Content-Encoding'), 'gzip', t('A Content-Encoding header was sent.'));
    $this->drupalSetContent(gzinflate(substr($this->drupalGetContent(), 10, -8)));
    $this->assertRaw('</html>', t('Page was gzip compressed.'));

    // Verify that a client without compression support gets an uncompressed page.
    $this->drupalGet('');
    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.'));
    $this->assertFalse($this->drupalGetHeader('Content-Encoding'), t('A Content-Encoding header was not sent.'));
    $this->assertTitle(t('Welcome to @site-name | @site-name', array('@site-name' => variable_get('site_name', 'Drupal'))), t('Site title matches.'));
    $this->assertRaw('</html>', t('Page was not compressed.'));
  }
}

class BootstrapVariableTestCase extends DrupalWebTestCase {

  function setUp() {
    parent::setUp('system_test');
  }

  public static function getInfo() {
    return array(
      'name' => 'Variable test',
      'description' => 'Make sure the variable system functions correctly.',
      'group' => 'Bootstrap'
    );
  }

  /**
   * testVariable
   */
  function testVariable() {
    // Setting and retrieving values.
    $variable = $this->randomName();
    variable_set('simpletest_bootstrap_variable_test', $variable);
    $this->assertIdentical($variable, variable_get('simpletest_bootstrap_variable_test'), t('Setting and retrieving values'));

    // Make sure the variable persists across multiple requests.
    $this->drupalGet('system-test/variable-get');
    $this->assertText($variable, t('Variable persists across multiple requests'));

    // Deleting variables.
    $default_value = $this->randomName();
    variable_del('simpletest_bootstrap_variable_test');
    $variable = variable_get('simpletest_bootstrap_variable_test', $default_value);
    $this->assertIdentical($variable, $default_value, t('Deleting variables'));
  }

  /**
   * Makes sure that the default variable parameter is passed through okay.
   */
  function testVariableDefaults() {
    // Tests passing nothing through to the default.
    $this->assertIdentical(NULL, variable_get('simpletest_bootstrap_variable_test'), t('Variables are correctly defaulting to NULL.'));

    // Tests passing 5 to the default parameter.
    $this->assertIdentical(5, variable_get('simpletest_bootstrap_variable_test', 5), t('The default variable parameter is passed through correctly.'));
  }

}

/**
 * Test hook_boot() and hook_exit().
 */
class HookBootExitTestCase extends DrupalWebTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Boot and exit hook invocation',
      'description' => 'Test that hook_boot() and hook_exit() are called correctly.',
      'group' => 'Bootstrap',
    );
  }

  function setUp() {
    parent::setUp('system_test', 'dblog');
  }

  /**
   * Test calling of hook_boot() and hook_exit().
   */
  function testHookBootExit() {
    // Test with cache disabled. Boot and exit should always fire.
    variable_set('cache', 0);
    $this->drupalGet('');
    $calls = 1;
    $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot called with disabled cache.'));
    $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit called with disabled cache.'));

    // Test with normal cache. Boot and exit should be called.
    variable_set('cache', 1);
    $this->drupalGet('');
    $calls++;
    $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot called with normal cache.'));
    $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit called with normal cache.'));

    // Boot and exit should not fire since the page is cached.
    variable_set('page_cache_invoke_hooks', FALSE);
    $this->assertTrue(cache_get(url('', array('absolute' => TRUE)), 'cache_page'), t('Page has been cached.'));
    $this->drupalGet('');
    $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot not called with agressive cache and a cached page.'));
    $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit not called with agressive cache and a cached page.'));

    // Test with page cache cleared, boot and exit should be called.
    $this->assertTrue(db_delete('cache_page')->execute(), t('Page cache cleared.'));
    $this->drupalGet('');
    $calls++;
    $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot called with agressive cache and no cached page.'));
    $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit called with agressive cache and no cached page.'));
  }
}

/**
 * Test drupal_get_filename()'s availability.
 */
class BootstrapGetFilenameTestCase extends DrupalUnitTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Get filename test',
      'description' => 'Test that drupal_get_filename() works correctly when the file is not found in the database.',
      'group' => 'Bootstrap',
    );
  }

  /**
   * Test that drupal_get_filename() works correctly when the file is not found in the database.
   */
  function testDrupalGetFilename() {
    // Reset the static cache so we can test the "db is not active" code of
    // drupal_get_filename().
    drupal_static_reset('drupal_get_filename');

    // Retrieving the location of a module.
    $this->assertIdentical(drupal_get_filename('module', 'php'), 'modules/php/php.module', t('Retrieve module location.'));

    // Retrieving the location of a theme.
    $this->assertIdentical(drupal_get_filename('theme', 'stark'), 'themes/stark/stark.info', t('Retrieve theme location.'));

    // Retrieving the location of a theme engine.
    $this->assertIdentical(drupal_get_filename('theme_engine', 'phptemplate'), 'themes/engines/phptemplate/phptemplate.engine', t('Retrieve theme engine location.'));

    // Retrieving a file that is definitely not stored in the database.
    $this->assertIdentical(drupal_get_filename('profile', 'standard'), 'profiles/standard/standard.profile', t('Retrieve install profile location.'));
  }
}

class BootstrapTimerTestCase extends DrupalUnitTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Timer test',
      'description' => 'Test that timer_read() works both when a timer is running and when a timer is stopped.',
      'group' => 'Bootstrap',
    );
  }

  /**
   * Test timer_read() to ensure it properly accumulates time when the timer
   * started and stopped multiple times.
   * @return
   */
  function testTimer() {
    timer_start('test');
    sleep(1);
    $this->assertTrue(timer_read('test') >= 1000, t('Timer measured 1 second of sleeping while running.'));
    sleep(1);
    timer_stop('test');
    $this->assertTrue(timer_read('test') >= 2000, t('Timer measured 2 seconds of sleeping after being stopped.'));
    timer_start('test');
    sleep(1);
    $this->assertTrue(timer_read('test') >= 3000, t('Timer measured 3 seconds of sleeping after being restarted.'));
    sleep(1);
    $timer = timer_stop('test');
    $this->assertTrue(timer_read('test') >= 4000, t('Timer measured 4 seconds of sleeping after being stopped for a second time.'));
    $this->assertEqual($timer['count'], 2, t('Timer counted 2 instances of being started.'));
  }
}

/**
 * Test that resetting static variables works.
 */
class BootstrapResettableStaticTestCase extends DrupalUnitTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Resettable static variables test',
      'description' => 'Test that drupal_static() and drupal_static_reset() work.',
      'group' => 'Bootstrap',
    );
  }

  /**
   * Test that a variable reference returned by drupal_static() gets reset when
   * drupal_static_reset() is called.
   */
  function testDrupalStatic() {
    $name = __CLASS__ . '_' . __METHOD__;
    $var = &drupal_static($name, 'foo');
    $this->assertEqual($var, 'foo', t('Variable returned by drupal_static() was set to its default.'));

    // Call the specific reset and the global reset each twice to ensure that
    // multiple resets can be issued without odd side effects.
    $var = 'bar';
    drupal_static_reset($name);
    $this->assertEqual($var, 'foo', t('Variable was reset after first invocation of name-specific reset.'));
    $var = 'bar';
    drupal_static_reset($name);
    $this->assertEqual($var, 'foo', t('Variable was reset after second invocation of name-specific reset.'));
    $var = 'bar';
    drupal_static_reset();
    $this->assertEqual($var, 'foo', t('Variable was reset after first invocation of global reset.'));
    $var = 'bar';
    drupal_static_reset();
    $this->assertEqual($var, 'foo', t('Variable was reset after second invocation of global reset.'));
  }
}

/**
 * Test miscellaneous functions in bootstrap.inc.
 */
class BootstrapMiscTestCase extends DrupalUnitTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Miscellaneous bootstrap unit tests',
      'description' => 'Test miscellaneous functions in bootstrap.inc.',
      'group' => 'Bootstrap',
    );
  }

  /**
   * Test miscellaneous functions in bootstrap.inc.
   */
  function testMisc() {
    // Test drupal_array_merge_deep().
    $link_options_1 = array('fragment' => 'x', 'attributes' => array('title' => 'X', 'class' => array('a', 'b')), 'language' => 'en');
    $link_options_2 = array('fragment' => 'y', 'attributes' => array('title' => 'Y', 'class' => array('c', 'd')), 'html' => TRUE);
    $expected = array('fragment' => 'y', 'attributes' => array('title' => 'Y', 'class' => array('a', 'b', 'c', 'd')), 'language' => 'en', 'html' => TRUE);
    $this->assertIdentical(drupal_array_merge_deep($link_options_1, $link_options_2), $expected, t('drupal_array_merge_deep() returned a properly merged array.'));
  }
}

/**
 * Tests for overriding server variables via the API.
 */
class BootstrapOverrideServerVariablesTestCase extends DrupalUnitTestCase {
  public static function getInfo() {
    return array(
      'name' => 'Overriding server variables',
      'description' => 'Test that drupal_override_server_variables() works correctly.',
      'group' => 'Bootstrap',
    );
  }

  /**
   * Test providing a direct URL to to drupal_override_server_variables().
   */
  function testDrupalOverrideServerVariablesProvidedURL() {
    $tests = array(
      'http://example.com' => array(
        'HTTP_HOST' => 'example.com',
        'SCRIPT_NAME' => isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : NULL,
      ),
      'http://example.com/index.php' => array(
        'HTTP_HOST' => 'example.com',
        'SCRIPT_NAME' => '/index.php',
      ),
      'http://example.com/subdirectory/index.php' => array(
        'HTTP_HOST' => 'example.com',
        'SCRIPT_NAME' => '/subdirectory/index.php',
      ),
    );
    foreach ($tests as $url => $expected_server_values) {
      // Remember the original value of $_SERVER, since the function call below
      // will modify it.
      $original_server = $_SERVER;
      // Call drupal_override_server_variables() and ensure that all expected
      // $_SERVER variables were modified correctly.
      drupal_override_server_variables(array('url' => $url));
      foreach ($expected_server_values as $key => $value) {
        $this->assertIdentical($_SERVER[$key], $value);
      }
      // Restore the original value of $_SERVER.
      $_SERVER = $original_server;
    }
  }
}