Newer
Older
Dries Buytaert
committed
<?php
/**
* @file
* Tests for common.inc functionality.
*/
/**
* Tests for URL generation functions.
*/
class DrupalAlterTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'drupal_alter() tests',
'description' => 'Confirm that alteration of arguments passed to drupal_alter() works correctly.',
'group' => 'System',
);
}
function setUp() {
parent::setUp('common_test');
}
function testDrupalAlter() {
Dries Buytaert
committed
// This test depends on Bartik, so make sure that it is always the current
Dries Buytaert
committed
// active theme.
global $theme, $base_theme_info;
Dries Buytaert
committed
$theme = 'bartik';
Dries Buytaert
committed
$base_theme_info = array();
$array = array('foo' => 'bar');
Dries Buytaert
committed
$entity = new stdClass();
$entity->foo = 'bar';
// Verify alteration of a single argument.
$array_copy = $array;
$array_expected = array('foo' => 'Drupal theme');
drupal_alter('drupal_alter', $array_copy);
$this->assertEqual($array_copy, $array_expected, t('Single array was altered.'));
$entity_copy = clone $entity;
$entity_expected = clone $entity;
$entity_expected->foo = 'Drupal theme';
drupal_alter('drupal_alter', $entity_copy);
$this->assertEqual($entity_copy, $entity_expected, t('Single object was altered.'));
// Verify alteration of multiple arguments.
$array_copy = $array;
$array_expected = array('foo' => 'Drupal theme');
$entity_copy = clone $entity;
$entity_expected = clone $entity;
$entity_expected->foo = 'Drupal theme';
$array2_copy = $array;
$array2_expected = array('foo' => 'Drupal theme');
drupal_alter('drupal_alter', $array_copy, $entity_copy, $array2_copy);
$this->assertEqual($array_copy, $array_expected, t('First argument to drupal_alter() was altered.'));
$this->assertEqual($entity_copy, $entity_expected, t('Second argument to drupal_alter() was altered.'));
$this->assertEqual($array2_copy, $array2_expected, t('Third argument to drupal_alter() was altered.'));
Dries Buytaert
committed
// Verify alteration order when passing an array of types to drupal_alter().
// common_test_module_implements_alter() places 'block' implementation after
// other modules.
$array_copy = $array;
$array_expected = array('foo' => 'Drupal block theme');
drupal_alter(array('drupal_alter', 'drupal_alter_foo'), $array_copy);
$this->assertEqual($array_copy, $array_expected, t('hook_TYPE_alter() implementations ran in correct order.'));
Dries Buytaert
committed
/**
Dries Buytaert
committed
* Tests for URL generation functions.
Dries Buytaert
committed
*
* url() calls module_implements(), which may issue a db query, which requires
* inheriting from a web test case rather than a unit test case.
Dries Buytaert
committed
*/
Dries Buytaert
committed
class CommonURLUnitTest extends DrupalWebTestCase {
Angie Byron
committed
public static function getInfo() {
Dries Buytaert
committed
return array(
'name' => 'URL generation tests',
Dries Buytaert
committed
'description' => 'Confirm that url(), drupal_get_query_parameters(), drupal_http_build_query(), and l() work correctly with various input.',
'group' => 'System',
Dries Buytaert
committed
);
}
/**
* Confirm that invalid text given as $path is filtered.
*/
function testLXSS() {
$text = $this->randomName();
$path = "<SCRIPT>alert('XSS')</SCRIPT>";
$link = l($text, $path);
$sanitized_path = check_url(url($path));
$this->assertTrue(strpos($link, $sanitized_path) !== FALSE, t('XSS attack @path was filtered', array('@path' => $path)));
Dries Buytaert
committed
}
Dries Buytaert
committed
Dries Buytaert
committed
/*
* Tests for active class in l() function.
*/
function testLActiveClass() {
$link = l($this->randomName(), $_GET['q']);
$this->assertTrue($this->hasClass($link, 'active'), t('Class @class is present on link to the current page', array('@class' => 'active')));
Dries Buytaert
committed
}
/**
* Tests for custom class in l() function.
*/
function testLCustomClass() {
$class = $this->randomName();
$link = l($this->randomName(), $_GET['q'], array('attributes' => array('class' => array($class))));
$this->assertTrue($this->hasClass($link, $class), t('Custom class @class is present on link when requested', array('@class' => $class)));
$this->assertTrue($this->hasClass($link, 'active'), t('Class @class is present on link to the current page', array('@class' => 'active')));
Dries Buytaert
committed
}
private function hasClass($link, $class) {
return preg_match('|class="([^\"\s]+\s+)*' . $class . '|', $link);
}
Dries Buytaert
committed
/**
Dries Buytaert
committed
* Test drupal_get_query_parameters().
*/
function testDrupalGetQueryParameters() {
$original = array(
'a' => 1,
'b' => array(
'd' => 4,
'e' => array(
'f' => 5,
),
),
'c' => 3,
'q' => 'foo/bar',
);
// Default arguments.
$result = $_GET;
unset($result['q']);
$this->assertEqual(drupal_get_query_parameters(), $result, t("\$_GET['q'] was removed."));
Dries Buytaert
committed
// Default exclusion.
$result = $original;
unset($result['q']);
$this->assertEqual(drupal_get_query_parameters($original), $result, t("'q' was removed."));
Dries Buytaert
committed
// First-level exclusion.
$result = $original;
unset($result['b']);
$this->assertEqual(drupal_get_query_parameters($original, array('b')), $result, t("'b' was removed."));
Dries Buytaert
committed
// Second-level exclusion.
$result = $original;
unset($result['b']['d']);
$this->assertEqual(drupal_get_query_parameters($original, array('b[d]')), $result, t("'b[d]' was removed."));
Dries Buytaert
committed
// Third-level exclusion.
$result = $original;
unset($result['b']['e']['f']);
$this->assertEqual(drupal_get_query_parameters($original, array('b[e][f]')), $result, t("'b[e][f]' was removed."));
Dries Buytaert
committed
// Multiple exclusions.
$result = $original;
unset($result['a'], $result['b']['e'], $result['c']);
$this->assertEqual(drupal_get_query_parameters($original, array('a', 'b[e]', 'c')), $result, t("'a', 'b[e]', 'c' were removed."));
Dries Buytaert
committed
}
/**
* Test drupal_http_build_query().
*/
function testDrupalHttpBuildQuery() {
$this->assertEqual(drupal_http_build_query(array('a' => ' &#//+%20@۞')), 'a=%20%26%23//%2B%2520%40%DB%9E', t('Value was properly encoded.'));
$this->assertEqual(drupal_http_build_query(array(' &#//+%20@۞' => 'a')), '%20%26%23%2F%2F%2B%2520%40%DB%9E=a', t('Key was properly encoded.'));
$this->assertEqual(drupal_http_build_query(array('a' => '1', 'b' => '2', 'c' => '3')), 'a=1&b=2&c=3', t('Multiple values were properly concatenated.'));
$this->assertEqual(drupal_http_build_query(array('a' => array('b' => '2', 'c' => '3'), 'd' => 'foo')), 'a[b]=2&a[c]=3&d=foo', t('Nested array was properly encoded.'));
Dries Buytaert
committed
}
/**
* Test drupal_parse_url().
*/
function testDrupalParseUrl() {
// Relative URL.
$url = 'foo/bar?foo=bar&bar=baz&baz#foo';
$result = array(
'path' => 'foo/bar',
'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''),
'fragment' => 'foo',
);
$this->assertEqual(drupal_parse_url($url), $result, t('Relative URL parsed correctly.'));
Dries Buytaert
committed
Dries Buytaert
committed
// Relative URL that is known to confuse parse_url().
$url = 'foo/bar:1';
$result = array(
'path' => 'foo/bar:1',
'query' => array(),
'fragment' => '',
);
$this->assertEqual(drupal_parse_url($url), $result, t('Relative URL parsed correctly.'));
Dries Buytaert
committed
Dries Buytaert
committed
// Absolute URL.
$url = '/foo/bar?foo=bar&bar=baz&baz#foo';
$result = array(
'path' => '/foo/bar',
'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''),
'fragment' => 'foo',
);
$this->assertEqual(drupal_parse_url($url), $result, t('Absolute URL parsed correctly.'));
Dries Buytaert
committed
// External URL testing.
Dries Buytaert
committed
$url = 'http://drupal.org/foo/bar?foo=bar&bar=baz&baz#foo';
// Test that drupal can recognize an absolute URL. Used to prevent attack vectors.
$this->assertTrue(url_is_external($url), t('Correctly identified an external URL.'));
// Test the parsing of absolute URLs.
Dries Buytaert
committed
$result = array(
'path' => 'http://drupal.org/foo/bar',
'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''),
'fragment' => 'foo',
);
$this->assertEqual(drupal_parse_url($url), $result, t('External URL parsed correctly.'));
Angie Byron
committed
// Verify proper parsing of URLs when clean URLs are disabled.
$result = array(
'path' => 'foo/bar',
'query' => array('bar' => 'baz'),
'fragment' => 'foo',
);
// Non-clean URLs #1: Absolute URL generated by url().
$url = $GLOBALS['base_url'] . '/?q=foo/bar&bar=baz#foo';
$this->assertEqual(drupal_parse_url($url), $result, t('Absolute URL with clean URLs disabled parsed correctly.'));
Angie Byron
committed
// Non-clean URLs #2: Relative URL generated by url().
$url = '?q=foo/bar&bar=baz#foo';
$this->assertEqual(drupal_parse_url($url), $result, t('Relative URL with clean URLs disabled parsed correctly.'));
Angie Byron
committed
// Non-clean URLs #3: URL generated by url() on non-Apache webserver.
$url = 'index.php?q=foo/bar&bar=baz#foo';
$this->assertEqual(drupal_parse_url($url), $result, t('Relative URL on non-Apache webserver with clean URLs disabled parsed correctly.'));
// Test that drupal_parse_url() does not allow spoofing a URL to force a malicious redirect.
$parts = drupal_parse_url('forged:http://cwe.mitre.org/data/definitions/601.html');
$this->assertFalse(valid_url($parts['path'], TRUE), t('drupal_parse_url() correctly parsed a forged URL.'));
Dries Buytaert
committed
}
Angie Byron
committed
/**
* Test url() with/without query, with/without fragment, absolute on/off and
* assert all that works when clean URLs are on and off.
Angie Byron
committed
*/
function testUrl() {
global $base_url;
Dries Buytaert
committed
Angie Byron
committed
foreach (array(FALSE, TRUE) as $absolute) {
// Get the expected start of the path string.
$base = $absolute ? $base_url . '/' : base_path();
$absolute_string = $absolute ? 'absolute' : NULL;
Dries Buytaert
committed
// Disable Clean URLs.
Angie Byron
committed
$GLOBALS['conf']['clean_url'] = 0;
Dries Buytaert
committed
$url = $base . '?q=node/123';
$result = url('node/123', array('absolute' => $absolute));
$this->assertEqual($url, $result, "$url == $result");
$url = $base . '?q=node/123#foo';
$result = url('node/123', array('fragment' => 'foo', 'absolute' => $absolute));
$this->assertEqual($url, $result, "$url == $result");
$url = $base . '?q=node/123&foo';
$result = url('node/123', array('query' => array('foo' => NULL), 'absolute' => $absolute));
$this->assertEqual($url, $result, "$url == $result");
$url = $base . '?q=node/123&foo=bar&bar=baz';
$result = url('node/123', array('query' => array('foo' => 'bar', 'bar' => 'baz'), 'absolute' => $absolute));
$this->assertEqual($url, $result, "$url == $result");
$url = $base . '?q=node/123&foo#bar';
$result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute));
$this->assertEqual($url, $result, "$url == $result");
Dries Buytaert
committed
$url = $base . '?q=node/123&foo#0';
$result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => '0', 'absolute' => $absolute));
$this->assertEqual($url, $result, "$url == $result");
$url = $base . '?q=node/123&foo';
$result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => '', 'absolute' => $absolute));
$this->assertEqual($url, $result, "$url == $result");
Dries Buytaert
committed
$url = $base;
$result = url('<front>', array('absolute' => $absolute));
$this->assertEqual($url, $result, "$url == $result");
// Enable Clean URLs.
Angie Byron
committed
$GLOBALS['conf']['clean_url'] = 1;
Dries Buytaert
committed
$url = $base . 'node/123';
$result = url('node/123', array('absolute' => $absolute));
$this->assertEqual($url, $result, "$url == $result");
$url = $base . 'node/123#foo';
$result = url('node/123', array('fragment' => 'foo', 'absolute' => $absolute));
$this->assertEqual($url, $result, "$url == $result");
$url = $base . 'node/123?foo';
$result = url('node/123', array('query' => array('foo' => NULL), 'absolute' => $absolute));
$this->assertEqual($url, $result, "$url == $result");
$url = $base . 'node/123?foo=bar&bar=baz';
$result = url('node/123', array('query' => array('foo' => 'bar', 'bar' => 'baz'), 'absolute' => $absolute));
$this->assertEqual($url, $result, "$url == $result");
$url = $base . 'node/123?foo#bar';
$result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute));
$this->assertEqual($url, $result, "$url == $result");
$url = $base;
$result = url('<front>', array('absolute' => $absolute));
$this->assertEqual($url, $result, "$url == $result");
Angie Byron
committed
}
}
Dries Buytaert
committed
/**
* Test external URL handling.
*/
function testExternalUrls() {
$test_url = 'http://drupal.org/';
// Verify external URL can contain a fragment.
$url = $test_url . '#drupal';
$result = url($url);
$this->assertEqual($url, $result, t('External URL with fragment works without a fragment in $options.'));
Dries Buytaert
committed
// Verify fragment can be overidden in an external URL.
$url = $test_url . '#drupal';
$fragment = $this->randomName(10);
$result = url($url, array('fragment' => $fragment));
$this->assertEqual($test_url . '#' . $fragment, $result, t('External URL fragment is overidden with a custom fragment in $options.'));
Dries Buytaert
committed
// Verify external URL can contain a query string.
$url = $test_url . '?drupal=awesome';
$result = url($url);
$this->assertEqual($url, $result, t('External URL with query string works without a query string in $options.'));
Dries Buytaert
committed
// Verify external URL can be extended with a query string.
$url = $test_url;
$query = array($this->randomName(5) => $this->randomName(5));
$result = url($url, array('query' => $query));
$this->assertEqual($url . '?' . http_build_query($query, '', '&'), $result, t('External URL can be extended with a query string in $options.'));
Dries Buytaert
committed
// Verify query string can be extended in an external URL.
$url = $test_url . '?drupal=awesome';
$query = array($this->randomName(5) => $this->randomName(5));
$result = url($url, array('query' => $query));
$this->assertEqual($url . '&' . http_build_query($query, '', '&'), $result, t('External URL query string can be extended with a custom query string in $options.'));
Dries Buytaert
committed
}
Dries Buytaert
committed
}
Angie Byron
committed
/**
Dries Buytaert
committed
* Tests for the check_plain(), filter_xss() and format_string() functions.
Angie Byron
committed
*/
class CommonXssUnitTest extends DrupalUnitTestCase {
public static function getInfo() {
return array(
'name' => 'String filtering tests',
Dries Buytaert
committed
'description' => 'Confirm that check_plain(), filter_xss(), format_string() and check_url() work correctly, including invalid multi-byte sequences.',
Angie Byron
committed
'group' => 'System',
);
}
/**
* Check that invalid multi-byte sequences are rejected.
*/
function testInvalidMultiByte() {
// Ignore PHP 5.3+ invalid multibyte sequence warning.
$text = @check_plain("Foo\xC0barbaz");
$this->assertEqual($text, '', 'check_plain() rejects invalid sequence "Foo\xC0barbaz"');
Dries Buytaert
committed
// Ignore PHP 5.3+ invalid multibyte sequence warning.
$text = @check_plain("\xc2\"");
$this->assertEqual($text, '', 'check_plain() rejects invalid sequence "\xc2\""');
$text = check_plain("Fooÿñ");
$this->assertEqual($text, "Fooÿñ", 'check_plain() accepts valid sequence "Fooÿñ"');
$text = filter_xss("Foo\xC0barbaz");
$this->assertEqual($text, '', 'filter_xss() rejects invalid sequence "Foo\xC0barbaz"');
$text = filter_xss("Fooÿñ");
$this->assertEqual($text, "Fooÿñ", 'filter_xss() accepts valid sequence Fooÿñ');
Angie Byron
committed
}
/**
* Check that special characters are escaped.
*/
function testEscaping() {
$text = check_plain("<script>");
$this->assertEqual($text, '<script>', 'check_plain() escapes <script>');
Dries Buytaert
committed
$text = check_plain('<>&"\'');
$this->assertEqual($text, '<>&"'', 'check_plain() escapes reserved HTML characters.');
Angie Byron
committed
}
Dries Buytaert
committed
/**
* Test t() and format_string() replacement functionality.
*/
function testFormatStringAndT() {
foreach (array('format_string', 't') as $function) {
$text = $function('Simple text');
$this->assertEqual($text, 'Simple text', $function . ' leaves simple text alone.');
$text = $function('Escaped text: @value', array('@value' => '<script>'));
$this->assertEqual($text, 'Escaped text: <script>', $function . ' replaces and escapes string.');
$text = $function('Placeholder text: %value', array('%value' => '<script>'));
$this->assertEqual($text, 'Placeholder text: <em class="placeholder"><script></em>', $function . ' replaces, escapes and themes string.');
$text = $function('Verbatim text: !value', array('!value' => '<script>'));
$this->assertEqual($text, 'Verbatim text: <script>', $function . ' replaces verbatim string as-is.');
}
}
/**
* Check that harmful protocols are stripped.
*/
function testBadProtocolStripping() {
// Ensure that check_url() strips out harmful protocols, and encodes for
// HTML. Ensure drupal_strip_dangerous_protocols() can be used to return a
// plain-text string stripped of harmful protocols.
$url = 'javascript:http://www.example.com/?x=1&y=2';
$expected_plain = 'http://www.example.com/?x=1&y=2';
$expected_html = 'http://www.example.com/?x=1&y=2';
$this->assertIdentical(check_url($url), $expected_html, t('check_url() filters a URL and encodes it for HTML.'));
$this->assertIdentical(drupal_strip_dangerous_protocols($url), $expected_plain, t('drupal_strip_dangerous_protocols() filters a URL and returns plain text.'));
}
Angie Byron
committed
}
Dries Buytaert
committed
class CommonSizeTestCase extends DrupalUnitTestCase {
Dries Buytaert
committed
protected $exact_test_cases;
protected $rounded_test_cases;
Dries Buytaert
committed
Angie Byron
committed
public static function getInfo() {
Dries Buytaert
committed
return array(
'name' => 'Size parsing test',
'description' => 'Parse a predefined amount of bytes and compare the output with the expected value.',
'group' => 'System'
Dries Buytaert
committed
);
}
function setUp() {
Dries Buytaert
committed
$kb = DRUPAL_KILOBYTE;
Dries Buytaert
committed
$this->exact_test_cases = array(
Dries Buytaert
committed
'1 byte' => 1,
'1 KB' => $kb,
'1 MB' => $kb * $kb,
'1 GB' => $kb * $kb * $kb,
'1 TB' => $kb * $kb * $kb * $kb,
'1 PB' => $kb * $kb * $kb * $kb * $kb,
'1 EB' => $kb * $kb * $kb * $kb * $kb * $kb,
'1 ZB' => $kb * $kb * $kb * $kb * $kb * $kb * $kb,
'1 YB' => $kb * $kb * $kb * $kb * $kb * $kb * $kb * $kb,
Dries Buytaert
committed
);
$this->rounded_test_cases = array(
Dries Buytaert
committed
'2 bytes' => 2,
'1 MB' => ($kb * $kb) - 1, // rounded to 1 MB (not 1000 or 1024 kilobyte!)
round(3623651 / ($this->exact_test_cases['1 MB']), 2) . ' MB' => 3623651, // megabytes
round(67234178751368124 / ($this->exact_test_cases['1 PB']), 2) . ' PB' => 67234178751368124, // petabytes
round(235346823821125814962843827 / ($this->exact_test_cases['1 YB']), 2) . ' YB' => 235346823821125814962843827, // yottabytes
Dries Buytaert
committed
);
parent::setUp();
}
/**
Dries Buytaert
committed
* Check that format_size() returns the expected string.
Dries Buytaert
committed
*/
function testCommonFormatSize() {
foreach (array($this->exact_test_cases, $this->rounded_test_cases) as $test_cases) {
Dries Buytaert
committed
foreach ($test_cases as $expected => $input) {
$this->assertEqual(
($result = format_size($input, NULL)),
$expected,
$expected . ' == ' . $result . ' (' . $input . ' bytes)'
Dries Buytaert
committed
);
}
}
}
Dries Buytaert
committed
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
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
/**
* Check that parse_size() returns the proper byte sizes.
*/
function testCommonParseSize() {
foreach ($this->exact_test_cases as $string => $size) {
$this->assertEqual(
$parsed_size = parse_size($string),
$size,
$size . ' == ' . $parsed_size . ' (' . $string . ')'
);
}
// Some custom parsing tests
$string = '23476892 bytes';
$this->assertEqual(
($parsed_size = parse_size($string)),
$size = 23476892,
$string . ' == ' . $parsed_size . ' bytes'
);
$string = '76MRandomStringThatShouldBeIgnoredByParseSize.'; // 76 MB
$this->assertEqual(
$parsed_size = parse_size($string),
$size = 79691776,
$string . ' == ' . $parsed_size . ' bytes'
);
$string = '76.24 Giggabyte'; // Misspeld text -> 76.24 GB
$this->assertEqual(
$parsed_size = parse_size($string),
$size = 81862076662,
$string . ' == ' . $parsed_size . ' bytes'
);
}
/**
* Cross-test parse_size() and format_size().
*/
function testCommonParseSizeFormatSize() {
foreach ($this->exact_test_cases as $size) {
$this->assertEqual(
$size,
($parsed_size = parse_size($string = format_size($size, NULL))),
$size . ' == ' . $parsed_size . ' (' . $string . ')'
);
}
}
Dries Buytaert
committed
}
/**
* Test drupal_explode_tags() and drupal_implode_tags().
*/
class DrupalTagsHandlingTestCase extends DrupalWebTestCase {
var $validTags = array(
'Drupal' => 'Drupal',
'Drupal with some spaces' => 'Drupal with some spaces',
'"Legendary Drupal mascot of doom: ""Druplicon"""' => 'Legendary Drupal mascot of doom: "Druplicon"',
'"Drupal, although it rhymes with sloopal, is as awesome as a troopal!"' => 'Drupal, although it rhymes with sloopal, is as awesome as a troopal!',
);
Angie Byron
committed
public static function getInfo() {
return array(
'name' => 'Drupal tags handling',
'description' => "Performs tests on Drupal's handling of tags, both explosion and implosion tactics used.",
'group' => 'System'
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
);
}
/**
* Explode a series of tags.
*/
function testDrupalExplodeTags() {
$string = implode(', ', array_keys($this->validTags));
$tags = drupal_explode_tags($string);
$this->assertTags($tags);
}
/**
* Implode a series of tags.
*/
function testDrupalImplodeTags() {
$tags = array_values($this->validTags);
// Let's explode and implode to our heart's content.
for ($i = 0; $i < 10; $i++) {
$string = drupal_implode_tags($tags);
$tags = drupal_explode_tags($string);
}
$this->assertTags($tags);
}
/**
* Helper function: asserts that the ending array of tags is what we wanted.
*/
function assertTags($tags) {
$original = $this->validTags;
foreach ($tags as $tag) {
$key = array_search($tag, $original);
$this->assertTrue($key, t('Make sure tag %tag shows up in the final tags array (originally %original)', array('%tag' => $tag, '%original' => $key)));
unset($original[$key]);
}
foreach ($original as $leftover) {
$this->fail(t('Leftover tag %leftover was left over.', array('%leftover' => $leftover)));
}
}
Dries Buytaert
committed
}
/**
* Test the Drupal CSS system.
*/
class CascadingStylesheetsTestCase extends DrupalWebTestCase {
Angie Byron
committed
public static function getInfo() {
return array(
'name' => 'Cascading stylesheets',
'description' => 'Tests adding various cascading stylesheets to the page.',
'group' => 'System',
);
}
function setUp() {
Dries Buytaert
committed
parent::setUp('php', 'locale', 'common_test');
// Reset drupal_add_css() before each test.
drupal_static_reset('drupal_add_css');
}
/**
* Check default stylesheets as empty.
*/
function testDefault() {
$this->assertEqual(array(), drupal_add_css(), t('Default CSS is empty.'));
}
Dries Buytaert
committed
/**
* Test that stylesheets in module .info files are loaded.
*/
function testModuleInfo() {
$this->drupalGet('');
// Verify common_test.css in a STYLE media="all" tag.
$elements = $this->xpath('//style[@media=:media and contains(text(), :filename)]', array(
':media' => 'all',
':filename' => 'tests/common_test.css',
));
$this->assertTrue(count($elements), "Stylesheet with media 'all' in module .info file found.");
// Verify common_test.print.css in a STYLE media="print" tag.
$elements = $this->xpath('//style[@media=:media and contains(text(), :filename)]', array(
':media' => 'print',
':filename' => 'tests/common_test.print.css',
));
$this->assertTrue(count($elements), "Stylesheet with media 'print' in module .info file found.");
}
/**
* Tests adding a file stylesheet.
*/
function testAddFile() {
$path = drupal_get_path('module', 'simpletest') . '/simpletest.css';
$css = drupal_add_css($path);
$this->assertEqual($css[$path]['data'], $path, t('Adding a CSS file caches it properly.'));
}
Angie Byron
committed
/**
* Tests adding an external stylesheet.
*/
function testAddExternal() {
$path = 'http://example.com/style.css';
$css = drupal_add_css($path, 'external');
$this->assertEqual($css[$path]['type'], 'external', t('Adding an external CSS file caches it properly.'));
Angie Byron
committed
}
/**
* Makes sure that reseting the CSS empties the cache.
*/
function testReset() {
drupal_static_reset('drupal_add_css');
$this->assertEqual(array(), drupal_add_css(), t('Resetting the CSS empties the cache.'));
}
/**
* Tests rendering the stylesheets.
*/
function testRenderFile() {
$css = drupal_get_path('module', 'simpletest') . '/simpletest.css';
drupal_add_css($css);
Angie Byron
committed
$styles = drupal_get_css();
$this->assertTrue(strpos($styles, $css) > 0, t('Rendered CSS includes the added stylesheet.'));
Angie Byron
committed
}
/**
* Tests rendering an external stylesheet.
*/
function testRenderExternal() {
$css = 'http://example.com/style.css';
drupal_add_css($css, 'external');
$styles = drupal_get_css();
Dries Buytaert
committed
// Stylesheet URL may be the href of a LINK tag or in an @import statement
// of a STYLE tag.
$this->assertTrue(strpos($styles, 'href="' . $css) > 0 || strpos($styles, '@import url("' . $css . '")') > 0, t('Rendering an external CSS file.'));
Angie Byron
committed
/**
* Tests rendering inline stylesheets with preprocessing on.
*/
function testRenderInlinePreprocess() {
$css = 'body { padding: 0px; }';
catch
committed
$css_preprocessed = '<style media="all">' . "\n<!--/*--><![CDATA[/*><!--*/\n" . drupal_load_stylesheet_content($css, TRUE) . "\n/*]]>*/-->\n" . '</style>';
Dries Buytaert
committed
drupal_add_css($css, array('type' => 'inline'));
Angie Byron
committed
$styles = drupal_get_css();
$this->assertEqual(trim($styles), $css_preprocessed, t('Rendering preprocessed inline CSS adds it to the page.'));
Angie Byron
committed
}
/**
* Tests rendering inline stylesheets with preprocessing off.
*/
function testRenderInlineNoPreprocess() {
$css = 'body { padding: 0px; }';
Dries Buytaert
committed
drupal_add_css($css, array('type' => 'inline', 'preprocess' => FALSE));
Angie Byron
committed
$styles = drupal_get_css();
$this->assertTrue(strpos($styles, $css) > 0, t('Rendering non-preprocessed inline CSS adds it to the page.'));
Angie Byron
committed
}
/**
* Tests rendering inline stylesheets through a full page request.
*/
function testRenderInlineFullPage() {
$css = 'body { font-size: 254px; }';
Dries Buytaert
committed
// Inline CSS is minified unless 'preprocess' => FALSE is passed as a
// drupal_add_css() option.
$expected = 'body{font-size:254px;}';
Angie Byron
committed
// Create a node, using the PHP filter that tests drupal_add_css().
Angie Byron
committed
$php_format_id = 'php_code';
Angie Byron
committed
$settings = array(
'type' => 'page',
'body' => array(
LANGUAGE_NONE => array(
array(
'value' => t('This tests the inline CSS!') . "<?php drupal_add_css('$css', 'inline'); ?>",
Dries Buytaert
committed
'format' => $php_format_id,
),
),
),
Angie Byron
committed
'promote' => 1,
);
$node = $this->drupalCreateNode($settings);
// Fetch the page.
$this->drupalGet('node/' . $node->nid);
$this->assertRaw($expected, t('Inline stylesheets appear in the full page rendering.'));
Angie Byron
committed
}
Dries Buytaert
committed
/**
* Test CSS ordering.
*/
function testRenderOrder() {
// A module CSS file.
drupal_add_css(drupal_get_path('module', 'simpletest') . '/simpletest.css');
// A few system CSS files, ordered in a strange way.
$system_path = drupal_get_path('module', 'system');
Dries Buytaert
committed
drupal_add_css($system_path . '/system.base.css', array('group' => CSS_SYSTEM, 'weight' => -10));
drupal_add_css($system_path . '/system.theme.css', array('group' => CSS_SYSTEM));
Dries Buytaert
committed
$expected = array(
Dries Buytaert
committed
$system_path . '/system.base.css',
$system_path . '/system.theme.css',
Dries Buytaert
committed
drupal_get_path('module', 'simpletest') . '/simpletest.css',
);
Angie Byron
committed
$styles = drupal_get_css();
Dries Buytaert
committed
// Stylesheet URL may be the href of a LINK tag or in an @import statement
// of a STYLE tag.
if (preg_match_all('/(href="|url\(")' . preg_quote($GLOBALS['base_url'] . '/', '/') . '([^?]+)\?/', $styles, $matches)) {
$result = $matches[2];
Dries Buytaert
committed
}
else {
$result = array();
}
$this->assertIdentical($result, $expected, t('The CSS files are in the expected order.'));
Dries Buytaert
committed
}
/**
* Test CSS override.
*/
function testRenderOverride() {
Dries Buytaert
committed
$system = drupal_get_path('module', 'system');
$simpletest = drupal_get_path('module', 'simpletest');
drupal_add_css($system . '/system.base.css');
drupal_add_css($simpletest . '/tests/system.base.css');
Dries Buytaert
committed
// The dummy stylesheet should be the only one included.
Angie Byron
committed
$styles = drupal_get_css();
Dries Buytaert
committed
$this->assert(strpos($styles, $simpletest . '/tests/system.base.css') !== FALSE, t('The overriding CSS file is output.'));
$this->assert(strpos($styles, $system . '/system.base.css') === FALSE, t('The overridden CSS file is not output.'));
Dries Buytaert
committed
Dries Buytaert
committed
drupal_add_css($simpletest . '/tests/system.base.css');
drupal_add_css($system . '/system.base.css');
Dries Buytaert
committed
// The standard stylesheet should be the only one included.
Angie Byron
committed
$styles = drupal_get_css();
Dries Buytaert
committed
$this->assert(strpos($styles, $system . '/system.base.css') !== FALSE, t('The overriding CSS file is output.'));
$this->assert(strpos($styles, $simpletest . '/tests/system.base.css') === FALSE, t('The overridden CSS file is not output.'));
Dries Buytaert
committed
}
/**
* Tests Locale module's CSS Alter to include RTL overrides.
*/
function testAlter() {
Dries Buytaert
committed
// Switch the language to a right to left language and add system.base.css.
Dries Buytaert
committed
global $language;
$language->direction = LANGUAGE_RTL;
Dries Buytaert
committed
$path = drupal_get_path('module', 'system');
drupal_add_css($path . '/system.base.css');
Dries Buytaert
committed
Dries Buytaert
committed
// Check to see if system.base-rtl.css was also added.
Angie Byron
committed
$styles = drupal_get_css();
Dries Buytaert
committed
$this->assert(strpos($styles, $path . '/system.base-rtl.css') !== FALSE, t('CSS is alterable as right to left overrides are added.'));
Dries Buytaert
committed
// Change the language back to left to right.
$language->direction = LANGUAGE_LTR;
}
Dries Buytaert
committed
/**
* Tests that the query string remains intact when adding CSS files that have
* query string parameters.
*/
function testAddCssFileWithQueryString() {
$this->drupalGet('common-test/query-string');
$query_string = variable_get('css_js_query_string', '0');
$this->assertRaw(drupal_get_path('module', 'node') . '/node.css?' . $query_string, t('Query string was appended correctly to css.'));
$this->assertRaw(drupal_get_path('module', 'node') . '/node-fake.css?arg1=value1&arg2=value2', t('Query string not escaped on a URI.'));
Dries Buytaert
committed
}
}
Dries Buytaert
committed
/**
Angie Byron
committed
* Test for cleaning HTML identifiers.
Dries Buytaert
committed
*/
Angie Byron
committed
class DrupalHTMLIdentifierTestCase extends DrupalUnitTestCase {
Dries Buytaert
committed
public static function getInfo() {
return array(
Angie Byron
committed
'name' => 'HTML identifiers',
'description' => 'Test the functions drupal_html_class(), drupal_html_id() and drupal_clean_css_identifier() for expected behavior',
Dries Buytaert
committed
'group' => 'System',
);
}
/**
Angie Byron
committed
* Tests that drupal_clean_css_identifier() cleans the identifier properly.
Dries Buytaert
committed
*/
function testDrupalCleanCSSIdentifier() {
Angie Byron
committed
// Verify that no valid ASCII characters are stripped from the identifier.
$identifier = 'abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789';
$this->assertIdentical(drupal_clean_css_identifier($identifier, array()), $identifier, t('Verify valid ASCII characters pass through.'));
Dries Buytaert
committed
Angie Byron
committed
// Verify that valid UTF-8 characters are not stripped from the identifier.
$identifier = '¡¢£¤¥';
$this->assertIdentical(drupal_clean_css_identifier($identifier, array()), $identifier, t('Verify valid UTF-8 characters pass through.'));
Dries Buytaert
committed
Angie Byron
committed
// Verify that invalid characters (including non-breaking space) are stripped from the identifier.
$this->assertIdentical(drupal_clean_css_identifier('invalid !"#$%&\'()*+,./:;<=>?@[\\]^`{|}~ identifier', array()), 'invalididentifier', t('Strip invalid characters.'));
Dries Buytaert
committed
}
/**
* Tests that drupal_html_class() cleans the class name properly.
Dries Buytaert
committed
*/
Angie Byron
committed
function testDrupalHTMLClass() {
Dries Buytaert
committed
// Verify Drupal coding standards are enforced.
$this->assertIdentical(drupal_html_class('CLASS NAME_[Ü]'), 'class-name--ü', t('Enforce Drupal coding standards.'));
Dries Buytaert
committed
}
/**
Angie Byron
committed
* Tests that drupal_html_id() cleans the ID properly.
Dries Buytaert
committed
*/
Angie Byron
committed
function testDrupalHTMLId() {
// Verify that letters, digits, and hyphens are not stripped from the ID.
$id = 'abcdefghijklmnopqrstuvwxyz-0123456789';
$this->assertIdentical(drupal_html_id($id), $id, t('Verify valid characters pass through.'));
Angie Byron
committed
// Verify that invalid characters are stripped from the ID.
$this->assertIdentical(drupal_html_id('invalid,./:@\\^`{Üidentifier'), 'invalididentifier', t('Strip invalid characters.'));
Angie Byron
committed
Dries Buytaert
committed
// Verify Drupal coding standards are enforced.
$this->assertIdentical(drupal_html_id('ID NAME_[1]'), 'id-name-1', t('Enforce Drupal coding standards.'));
Dries Buytaert
committed
// Reset the static cache so we can ensure the unique id count is at zero.
drupal_static_reset('drupal_html_id');
Dries Buytaert
committed
// Clean up IDs with invalid starting characters.
$this->assertIdentical(drupal_html_id('test-unique-id'), 'test-unique-id', t('Test the uniqueness of IDs #1.'));
$this->assertIdentical(drupal_html_id('test-unique-id'), 'test-unique-id--2', t('Test the uniqueness of IDs #2.'));
$this->assertIdentical(drupal_html_id('test-unique-id'), 'test-unique-id--3', t('Test the uniqueness of IDs #3.'));
Dries Buytaert
committed
}
}
/**
* CSS Unit Tests.
*/
class CascadingStylesheetsUnitTest extends DrupalUnitTestCase {
public static function getInfo() {
return array(
'name' => 'CSS Unit Tests',
'description' => 'Unit tests on CSS functions like aggregation.',
'group' => 'System',
);
}
/**
* Tests basic CSS loading with and without optimization via drupal_load_stylesheet().
*
Dries Buytaert
committed
* Known tests:
* - Retain white-space in selectors. (http://drupal.org/node/472820)
* - Proper URLs in imported files. (http://drupal.org/node/265719)
* - Retain pseudo-selectors. (http://drupal.org/node/460448)
*/
function testLoadCssBasic() {
// Array of files to test living in 'simpletest/files/css_test_files/'.
// - Original: name.css
// - Unoptimized expected content: name.css.unoptimized.css
// - Optimized expected content: name.css.optimized.css
$testfiles = array(
'css_input_without_import.css',
'css_input_with_import.css',
'comment_hacks.css'
$path = drupal_get_path('module', 'simpletest') . '/files/css_test_files';
foreach ($testfiles as $file) {
$expected = file_get_contents("$path/$file.unoptimized.css");
$unoptimized_output = drupal_load_stylesheet("$path/$file.unoptimized.css", FALSE);
$this->assertEqual($unoptimized_output, $expected, t('Unoptimized CSS file has expected contents (@file)', array('@file' => $file)));
$expected = file_get_contents("$path/$file.optimized.css");
$optimized_output = drupal_load_stylesheet("$path/$file", TRUE);
$this->assertEqual($optimized_output, $expected, t('Optimized CSS file has expected contents (@file)', array('@file' => $file)));
Dries Buytaert
committed
// Repeat the tests by accessing the stylesheets by URL.
$expected = file_get_contents("$path/$file.unoptimized.css");
$unoptimized_output_url = drupal_load_stylesheet($GLOBALS['base_url'] . "/$path/$file.unoptimized.css", FALSE);
$this->assertEqual($unoptimized_output, $expected, t('Unoptimized CSS file (loaded from an URL) has expected contents (@file)', array('@file' => $file)));
$expected = file_get_contents("$path/$file.optimized.css");
$optimized_output = drupal_load_stylesheet($GLOBALS['base_url'] . "/$path/$file", TRUE);
$this->assertEqual($optimized_output, $expected, t('Optimized CSS file (loaded from an URL) has expected contents (@file)', array('@file' => $file)));
}
}
}
Dries Buytaert
committed
/**
* Test drupal_http_request().
*/
class DrupalHTTPRequestTestCase extends DrupalWebTestCase {
Angie Byron
committed
public static function getInfo() {
Dries Buytaert
committed
return array(
'name' => 'Drupal HTTP request',
'description' => "Performs tests on Drupal's HTTP request mechanism.",
'group' => 'System'
Dries Buytaert
committed
);
}
Dries Buytaert
committed
function setUp() {
parent::setUp('system_test');
}
Dries Buytaert
committed
function testDrupalHTTPRequest() {
Angie Byron
committed
global $is_https;
Dries Buytaert
committed
// Parse URL schema.
Dries Buytaert
committed
$missing_scheme = drupal_http_request('example.com/path');
$this->assertEqual($missing_scheme->code, -1002, t('Returned with "-1002" error code.'));
$this->assertEqual($missing_scheme->error, 'missing schema', t('Returned with "missing schema" error message.'));
Dries Buytaert
committed
$unable_to_parse = drupal_http_request('http:///path');
$this->assertEqual($unable_to_parse->code, -1001, t('Returned with "-1001" error code.'));
$this->assertEqual($unable_to_parse->error, 'unable to parse URL', t('Returned with "unable to parse URL" error message.'));
Dries Buytaert
committed
// Fetch page.
$result = drupal_http_request(url('node', array('absolute' => TRUE)));
$this->assertEqual($result->code, 200, t('Fetched page successfully.'));
Dries Buytaert
committed
$this->drupalSetContent($result->data);
$this->assertTitle(t('Welcome to @site-name | @site-name', array('@site-name' => variable_get('site_name', 'Drupal'))), t('Site title matches.'));
Dries Buytaert
committed
// Test that code and status message is returned.
$result = drupal_http_request(url('pagedoesnotexist', array('absolute' => TRUE)));
$this->assertTrue(!empty($result->protocol), t('Result protocol is returned.'));
$this->assertEqual($result->code, '404', t('Result code is 404'));
$this->assertEqual($result->status_message, 'Not Found', t('Result status message is "Not Found"'));
Dries Buytaert
committed
Angie Byron
committed
// Skip the timeout tests when the testing environment is HTTPS because
// stream_set_timeout() does not work for SSL connections.
// @link http://bugs.php.net/bug.php?id=47929
if (!$is_https) {
// Test that timeout is respected. The test machine is expected to be able
// to make the connection (i.e. complete the fsockopen()) in 2 seconds and
// return within a total of 5 seconds. If the test machine is extremely
// slow, the test will fail. fsockopen() has been seen to time out in
// slightly less than the specified timeout, so allow a little slack on
// the minimum expected time (i.e. 1.8 instead of 2).
timer_start(__METHOD__);
$result = drupal_http_request(url('system-test/sleep/10', array('absolute' => TRUE)), array('timeout' => 2));
$time = timer_read(__METHOD__) / 1000;
$this->assertTrue(1.8 < $time && $time < 5, t('Request timed out (%time seconds).', array('%time' => $time)));
$this->assertTrue($result->error, t('An error message was returned.'));
$this->assertEqual($result->code, HTTP_REQUEST_TIMEOUT, t('Proper error code was returned.'));
Angie Byron
committed
}
Dries Buytaert
committed
}
Dries Buytaert
committed
function testDrupalHTTPRequestBasicAuth() {
$username = $this->randomName();
$password = $this->randomName();
$url = url('system-test/auth', array('absolute' => TRUE));
Angie Byron
committed
$auth = str_replace('://', '://' . $username . ':' . $password . '@', $url);
Dries Buytaert
committed
$result = drupal_http_request($auth);
$this->drupalSetContent($result->data);
$this->assertRaw($username, t('$_SERVER["PHP_AUTH_USER"] is passed correctly.'));
$this->assertRaw($password, t('$_SERVER["PHP_AUTH_PW"] is passed correctly.'));
Dries Buytaert
committed
}
function testDrupalHTTPRequestRedirect() {
Dries Buytaert
committed
$redirect_301 = drupal_http_request(url('system-test/redirect/301', array('absolute' => TRUE)), array('max_redirects' => 1));
$this->assertEqual($redirect_301->redirect_code, 301, t('drupal_http_request follows the 301 redirect.'));
Dries Buytaert
committed
Dries Buytaert
committed
$redirect_301 = drupal_http_request(url('system-test/redirect/301', array('absolute' => TRUE)), array('max_redirects' => 0));
$this->assertFalse(isset($redirect_301->redirect_code), t('drupal_http_request does not follow 301 redirect if max_redirects = 0.'));