common.test

Tests for common.inc functionality.

File

drupal/modules/simpletest/tests/common.test
View source
  1. <?php
  2. /**
  3. * @file
  4. * Tests for common.inc functionality.
  5. */
  6. /**
  7. * Tests for URL generation functions.
  8. */
  9. class DrupalAlterTestCase extends DrupalWebTestCase {
  10. public static function getInfo() {
  11. return array(
  12. 'name' => 'drupal_alter() tests',
  13. 'description' => 'Confirm that alteration of arguments passed to drupal_alter() works correctly.',
  14. 'group' => 'System',
  15. );
  16. }
  17. function setUp() {
  18. parent::setUp('common_test');
  19. }
  20. function testDrupalAlter() {
  21. // This test depends on Bartik, so make sure that it is always the current
  22. // active theme.
  23. global $theme, $base_theme_info;
  24. $theme = 'bartik';
  25. $base_theme_info = array();
  26. $array = array('foo' => 'bar');
  27. $entity = new stdClass();
  28. $entity->foo = 'bar';
  29. // Verify alteration of a single argument.
  30. $array_copy = $array;
  31. $array_expected = array('foo' => 'Drupal theme');
  32. drupal_alter('drupal_alter', $array_copy);
  33. $this->assertEqual($array_copy, $array_expected, t('Single array was altered.'));
  34. $entity_copy = clone $entity;
  35. $entity_expected = clone $entity;
  36. $entity_expected->foo = 'Drupal theme';
  37. drupal_alter('drupal_alter', $entity_copy);
  38. $this->assertEqual($entity_copy, $entity_expected, t('Single object was altered.'));
  39. // Verify alteration of multiple arguments.
  40. $array_copy = $array;
  41. $array_expected = array('foo' => 'Drupal theme');
  42. $entity_copy = clone $entity;
  43. $entity_expected = clone $entity;
  44. $entity_expected->foo = 'Drupal theme';
  45. $array2_copy = $array;
  46. $array2_expected = array('foo' => 'Drupal theme');
  47. drupal_alter('drupal_alter', $array_copy, $entity_copy, $array2_copy);
  48. $this->assertEqual($array_copy, $array_expected, t('First argument to drupal_alter() was altered.'));
  49. $this->assertEqual($entity_copy, $entity_expected, t('Second argument to drupal_alter() was altered.'));
  50. $this->assertEqual($array2_copy, $array2_expected, t('Third argument to drupal_alter() was altered.'));
  51. // Verify alteration order when passing an array of types to drupal_alter().
  52. // common_test_module_implements_alter() places 'block' implementation after
  53. // other modules.
  54. $array_copy = $array;
  55. $array_expected = array('foo' => 'Drupal block theme');
  56. drupal_alter(array('drupal_alter', 'drupal_alter_foo'), $array_copy);
  57. $this->assertEqual($array_copy, $array_expected, t('hook_TYPE_alter() implementations ran in correct order.'));
  58. }
  59. }
  60. /**
  61. * Tests for URL generation functions.
  62. *
  63. * url() calls module_implements(), which may issue a db query, which requires
  64. * inheriting from a web test case rather than a unit test case.
  65. */
  66. class CommonURLUnitTest extends DrupalWebTestCase {
  67. public static function getInfo() {
  68. return array(
  69. 'name' => 'URL generation tests',
  70. 'description' => 'Confirm that url(), drupal_get_query_parameters(), drupal_http_build_query(), and l() work correctly with various input.',
  71. 'group' => 'System',
  72. );
  73. }
  74. /**
  75. * Confirm that invalid text given as $path is filtered.
  76. */
  77. function testLXSS() {
  78. $text = $this->randomName();
  79. $path = "<SCRIPT>alert('XSS')</SCRIPT>";
  80. $link = l($text, $path);
  81. $sanitized_path = check_url(url($path));
  82. $this->assertTrue(strpos($link, $sanitized_path) !== FALSE, t('XSS attack @path was filtered', array('@path' => $path)));
  83. }
  84. /*
  85. * Tests for active class in l() function.
  86. */
  87. function testLActiveClass() {
  88. $link = l($this->randomName(), $_GET['q']);
  89. $this->assertTrue($this->hasClass($link, 'active'), t('Class @class is present on link to the current page', array('@class' => 'active')));
  90. }
  91. /**
  92. * Tests for custom class in l() function.
  93. */
  94. function testLCustomClass() {
  95. $class = $this->randomName();
  96. $link = l($this->randomName(), $_GET['q'], array('attributes' => array('class' => array($class))));
  97. $this->assertTrue($this->hasClass($link, $class), t('Custom class @class is present on link when requested', array('@class' => $class)));
  98. $this->assertTrue($this->hasClass($link, 'active'), t('Class @class is present on link to the current page', array('@class' => 'active')));
  99. }
  100. private function hasClass($link, $class) {
  101. return preg_match('|class="([^\"\s]+\s+)*' . $class . '|', $link);
  102. }
  103. /**
  104. * Test drupal_get_query_parameters().
  105. */
  106. function testDrupalGetQueryParameters() {
  107. $original = array(
  108. 'a' => 1,
  109. 'b' => array(
  110. 'd' => 4,
  111. 'e' => array(
  112. 'f' => 5,
  113. ),
  114. ),
  115. 'c' => 3,
  116. 'q' => 'foo/bar',
  117. );
  118. // Default arguments.
  119. $result = $_GET;
  120. unset($result['q']);
  121. $this->assertEqual(drupal_get_query_parameters(), $result, t("\$_GET['q'] was removed."));
  122. // Default exclusion.
  123. $result = $original;
  124. unset($result['q']);
  125. $this->assertEqual(drupal_get_query_parameters($original), $result, t("'q' was removed."));
  126. // First-level exclusion.
  127. $result = $original;
  128. unset($result['b']);
  129. $this->assertEqual(drupal_get_query_parameters($original, array('b')), $result, t("'b' was removed."));
  130. // Second-level exclusion.
  131. $result = $original;
  132. unset($result['b']['d']);
  133. $this->assertEqual(drupal_get_query_parameters($original, array('b[d]')), $result, t("'b[d]' was removed."));
  134. // Third-level exclusion.
  135. $result = $original;
  136. unset($result['b']['e']['f']);
  137. $this->assertEqual(drupal_get_query_parameters($original, array('b[e][f]')), $result, t("'b[e][f]' was removed."));
  138. // Multiple exclusions.
  139. $result = $original;
  140. unset($result['a'], $result['b']['e'], $result['c']);
  141. $this->assertEqual(drupal_get_query_parameters($original, array('a', 'b[e]', 'c')), $result, t("'a', 'b[e]', 'c' were removed."));
  142. }
  143. /**
  144. * Test drupal_http_build_query().
  145. */
  146. function testDrupalHttpBuildQuery() {
  147. $this->assertEqual(drupal_http_build_query(array('a' => ' &#//+%20@۞')), 'a=%20%26%23//%2B%2520%40%DB%9E', t('Value was properly encoded.'));
  148. $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.'));
  149. $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.'));
  150. $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.'));
  151. }
  152. /**
  153. * Test drupal_parse_url().
  154. */
  155. function testDrupalParseUrl() {
  156. // Relative URL.
  157. $url = 'foo/bar?foo=bar&bar=baz&baz#foo';
  158. $result = array(
  159. 'path' => 'foo/bar',
  160. 'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''),
  161. 'fragment' => 'foo',
  162. );
  163. $this->assertEqual(drupal_parse_url($url), $result, t('Relative URL parsed correctly.'));
  164. // Relative URL that is known to confuse parse_url().
  165. $url = 'foo/bar:1';
  166. $result = array(
  167. 'path' => 'foo/bar:1',
  168. 'query' => array(),
  169. 'fragment' => '',
  170. );
  171. $this->assertEqual(drupal_parse_url($url), $result, t('Relative URL parsed correctly.'));
  172. // Absolute URL.
  173. $url = '/foo/bar?foo=bar&bar=baz&baz#foo';
  174. $result = array(
  175. 'path' => '/foo/bar',
  176. 'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''),
  177. 'fragment' => 'foo',
  178. );
  179. $this->assertEqual(drupal_parse_url($url), $result, t('Absolute URL parsed correctly.'));
  180. // External URL testing.
  181. $url = 'http://drupal.org/foo/bar?foo=bar&bar=baz&baz#foo';
  182. // Test that drupal can recognize an absolute URL. Used to prevent attack vectors.
  183. $this->assertTrue(url_is_external($url), t('Correctly identified an external URL.'));
  184. // Test the parsing of absolute URLs.
  185. $result = array(
  186. 'path' => 'http://drupal.org/foo/bar',
  187. 'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''),
  188. 'fragment' => 'foo',
  189. );
  190. $this->assertEqual(drupal_parse_url($url), $result, t('External URL parsed correctly.'));
  191. // Verify proper parsing of URLs when clean URLs are disabled.
  192. $result = array(
  193. 'path' => 'foo/bar',
  194. 'query' => array('bar' => 'baz'),
  195. 'fragment' => 'foo',
  196. );
  197. // Non-clean URLs #1: Absolute URL generated by url().
  198. $url = $GLOBALS['base_url'] . '/?q=foo/bar&bar=baz#foo';
  199. $this->assertEqual(drupal_parse_url($url), $result, t('Absolute URL with clean URLs disabled parsed correctly.'));
  200. // Non-clean URLs #2: Relative URL generated by url().
  201. $url = '?q=foo/bar&bar=baz#foo';
  202. $this->assertEqual(drupal_parse_url($url), $result, t('Relative URL with clean URLs disabled parsed correctly.'));
  203. // Non-clean URLs #3: URL generated by url() on non-Apache webserver.
  204. $url = 'index.php?q=foo/bar&bar=baz#foo';
  205. $this->assertEqual(drupal_parse_url($url), $result, t('Relative URL on non-Apache webserver with clean URLs disabled parsed correctly.'));
  206. // Test that drupal_parse_url() does not allow spoofing a URL to force a malicious redirect.
  207. $parts = drupal_parse_url('forged:http://cwe.mitre.org/data/definitions/601.html');
  208. $this->assertFalse(valid_url($parts['path'], TRUE), t('drupal_parse_url() correctly parsed a forged URL.'));
  209. }
  210. /**
  211. * Test url() with/without query, with/without fragment, absolute on/off and
  212. * assert all that works when clean URLs are on and off.
  213. */
  214. function testUrl() {
  215. global $base_url;
  216. foreach (array(FALSE, TRUE) as $absolute) {
  217. // Get the expected start of the path string.
  218. $base = $absolute ? $base_url . '/' : base_path();
  219. $absolute_string = $absolute ? 'absolute' : NULL;
  220. // Disable Clean URLs.
  221. $GLOBALS['conf']['clean_url'] = 0;
  222. $url = $base . '?q=node/123';
  223. $result = url('node/123', array('absolute' => $absolute));
  224. $this->assertEqual($url, $result, "$url == $result");
  225. $url = $base . '?q=node/123#foo';
  226. $result = url('node/123', array('fragment' => 'foo', 'absolute' => $absolute));
  227. $this->assertEqual($url, $result, "$url == $result");
  228. $url = $base . '?q=node/123&foo';
  229. $result = url('node/123', array('query' => array('foo' => NULL), 'absolute' => $absolute));
  230. $this->assertEqual($url, $result, "$url == $result");
  231. $url = $base . '?q=node/123&foo=bar&bar=baz';
  232. $result = url('node/123', array('query' => array('foo' => 'bar', 'bar' => 'baz'), 'absolute' => $absolute));
  233. $this->assertEqual($url, $result, "$url == $result");
  234. $url = $base . '?q=node/123&foo#bar';
  235. $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute));
  236. $this->assertEqual($url, $result, "$url == $result");
  237. $url = $base . '?q=node/123&foo#0';
  238. $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => '0', 'absolute' => $absolute));
  239. $this->assertEqual($url, $result, "$url == $result");
  240. $url = $base . '?q=node/123&foo';
  241. $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => '', 'absolute' => $absolute));
  242. $this->assertEqual($url, $result, "$url == $result");
  243. $url = $base;
  244. $result = url('<front>', array('absolute' => $absolute));
  245. $this->assertEqual($url, $result, "$url == $result");
  246. // Enable Clean URLs.
  247. $GLOBALS['conf']['clean_url'] = 1;
  248. $url = $base . 'node/123';
  249. $result = url('node/123', array('absolute' => $absolute));
  250. $this->assertEqual($url, $result, "$url == $result");
  251. $url = $base . 'node/123#foo';
  252. $result = url('node/123', array('fragment' => 'foo', 'absolute' => $absolute));
  253. $this->assertEqual($url, $result, "$url == $result");
  254. $url = $base . 'node/123?foo';
  255. $result = url('node/123', array('query' => array('foo' => NULL), 'absolute' => $absolute));
  256. $this->assertEqual($url, $result, "$url == $result");
  257. $url = $base . 'node/123?foo=bar&bar=baz';
  258. $result = url('node/123', array('query' => array('foo' => 'bar', 'bar' => 'baz'), 'absolute' => $absolute));
  259. $this->assertEqual($url, $result, "$url == $result");
  260. $url = $base . 'node/123?foo#bar';
  261. $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute));
  262. $this->assertEqual($url, $result, "$url == $result");
  263. $url = $base;
  264. $result = url('<front>', array('absolute' => $absolute));
  265. $this->assertEqual($url, $result, "$url == $result");
  266. }
  267. }
  268. /**
  269. * Test external URL handling.
  270. */
  271. function testExternalUrls() {
  272. $test_url = 'http://drupal.org/';
  273. // Verify external URL can contain a fragment.
  274. $url = $test_url . '#drupal';
  275. $result = url($url);
  276. $this->assertEqual($url, $result, t('External URL with fragment works without a fragment in $options.'));
  277. // Verify fragment can be overidden in an external URL.
  278. $url = $test_url . '#drupal';
  279. $fragment = $this->randomName(10);
  280. $result = url($url, array('fragment' => $fragment));
  281. $this->assertEqual($test_url . '#' . $fragment, $result, t('External URL fragment is overidden with a custom fragment in $options.'));
  282. // Verify external URL can contain a query string.
  283. $url = $test_url . '?drupal=awesome';
  284. $result = url($url);
  285. $this->assertEqual($url, $result, t('External URL with query string works without a query string in $options.'));
  286. // Verify external URL can be extended with a query string.
  287. $url = $test_url;
  288. $query = array($this->randomName(5) => $this->randomName(5));
  289. $result = url($url, array('query' => $query));
  290. $this->assertEqual($url . '?' . http_build_query($query, '', '&'), $result, t('External URL can be extended with a query string in $options.'));
  291. // Verify query string can be extended in an external URL.
  292. $url = $test_url . '?drupal=awesome';
  293. $query = array($this->randomName(5) => $this->randomName(5));
  294. $result = url($url, array('query' => $query));
  295. $this->assertEqual($url . '&' . http_build_query($query, '', '&'), $result, t('External URL query string can be extended with a custom query string in $options.'));
  296. }
  297. }
  298. /**
  299. * Tests for check_plain(), filter_xss(), format_string(), and check_url().
  300. */
  301. class CommonXssUnitTest extends DrupalUnitTestCase {
  302. public static function getInfo() {
  303. return array(
  304. 'name' => 'String filtering tests',
  305. 'description' => 'Confirm that check_plain(), filter_xss(), format_string() and check_url() work correctly, including invalid multi-byte sequences.',
  306. 'group' => 'System',
  307. );
  308. }
  309. /**
  310. * Check that invalid multi-byte sequences are rejected.
  311. */
  312. function testInvalidMultiByte() {
  313. // Ignore PHP 5.3+ invalid multibyte sequence warning.
  314. $text = @check_plain("Foo\xC0barbaz");
  315. $this->assertEqual($text, '', 'check_plain() rejects invalid sequence "Foo\xC0barbaz"');
  316. // Ignore PHP 5.3+ invalid multibyte sequence warning.
  317. $text = @check_plain("\xc2\"");
  318. $this->assertEqual($text, '', 'check_plain() rejects invalid sequence "\xc2\""');
  319. $text = check_plain("Fooÿñ");
  320. $this->assertEqual($text, "Fooÿñ", 'check_plain() accepts valid sequence "Fooÿñ"');
  321. $text = filter_xss("Foo\xC0barbaz");
  322. $this->assertEqual($text, '', 'filter_xss() rejects invalid sequence "Foo\xC0barbaz"');
  323. $text = filter_xss("Fooÿñ");
  324. $this->assertEqual($text, "Fooÿñ", 'filter_xss() accepts valid sequence Fooÿñ');
  325. }
  326. /**
  327. * Check that special characters are escaped.
  328. */
  329. function testEscaping() {
  330. $text = check_plain("<script>");
  331. $this->assertEqual($text, '&lt;script&gt;', 'check_plain() escapes &lt;script&gt;');
  332. $text = check_plain('<>&"\'');
  333. $this->assertEqual($text, '&lt;&gt;&amp;&quot;&#039;', 'check_plain() escapes reserved HTML characters.');
  334. }
  335. /**
  336. * Test t() and format_string() replacement functionality.
  337. */
  338. function testFormatStringAndT() {
  339. foreach (array('format_string', 't') as $function) {
  340. $text = $function('Simple text');
  341. $this->assertEqual($text, 'Simple text', $function . ' leaves simple text alone.');
  342. $text = $function('Escaped text: @value', array('@value' => '<script>'));
  343. $this->assertEqual($text, 'Escaped text: &lt;script&gt;', $function . ' replaces and escapes string.');
  344. $text = $function('Placeholder text: %value', array('%value' => '<script>'));
  345. $this->assertEqual($text, 'Placeholder text: <em class="placeholder">&lt;script&gt;</em>', $function . ' replaces, escapes and themes string.');
  346. $text = $function('Verbatim text: !value', array('!value' => '<script>'));
  347. $this->assertEqual($text, 'Verbatim text: <script>', $function . ' replaces verbatim string as-is.');
  348. }
  349. }
  350. /**
  351. * Check that harmful protocols are stripped.
  352. */
  353. function testBadProtocolStripping() {
  354. // Ensure that check_url() strips out harmful protocols, and encodes for
  355. // HTML. Ensure drupal_strip_dangerous_protocols() can be used to return a
  356. // plain-text string stripped of harmful protocols.
  357. $url = 'javascript:http://www.example.com/?x=1&y=2';
  358. $expected_plain = 'http://www.example.com/?x=1&y=2';
  359. $expected_html = 'http://www.example.com/?x=1&amp;y=2';
  360. $this->assertIdentical(check_url($url), $expected_html, t('check_url() filters a URL and encodes it for HTML.'));
  361. $this->assertIdentical(drupal_strip_dangerous_protocols($url), $expected_plain, t('drupal_strip_dangerous_protocols() filters a URL and returns plain text.'));
  362. }
  363. }
  364. /**
  365. * Tests file size parsing and formatting functions.
  366. */
  367. class CommonSizeTestCase extends DrupalUnitTestCase {
  368. protected $exact_test_cases;
  369. protected $rounded_test_cases;
  370. public static function getInfo() {
  371. return array(
  372. 'name' => 'Size parsing test',
  373. 'description' => 'Parse a predefined amount of bytes and compare the output with the expected value.',
  374. 'group' => 'System'
  375. );
  376. }
  377. function setUp() {
  378. $kb = DRUPAL_KILOBYTE;
  379. $this->exact_test_cases = array(
  380. '1 byte' => 1,
  381. '1 KB' => $kb,
  382. '1 MB' => $kb * $kb,
  383. '1 GB' => $kb * $kb * $kb,
  384. '1 TB' => $kb * $kb * $kb * $kb,
  385. '1 PB' => $kb * $kb * $kb * $kb * $kb,
  386. '1 EB' => $kb * $kb * $kb * $kb * $kb * $kb,
  387. '1 ZB' => $kb * $kb * $kb * $kb * $kb * $kb * $kb,
  388. '1 YB' => $kb * $kb * $kb * $kb * $kb * $kb * $kb * $kb,
  389. );
  390. $this->rounded_test_cases = array(
  391. '2 bytes' => 2,
  392. '1 MB' => ($kb * $kb) - 1, // rounded to 1 MB (not 1000 or 1024 kilobyte!)
  393. round(3623651 / ($this->exact_test_cases['1 MB']), 2) . ' MB' => 3623651, // megabytes
  394. round(67234178751368124 / ($this->exact_test_cases['1 PB']), 2) . ' PB' => 67234178751368124, // petabytes
  395. round(235346823821125814962843827 / ($this->exact_test_cases['1 YB']), 2) . ' YB' => 235346823821125814962843827, // yottabytes
  396. );
  397. parent::setUp();
  398. }
  399. /**
  400. * Check that format_size() returns the expected string.
  401. */
  402. function testCommonFormatSize() {
  403. foreach (array($this->exact_test_cases, $this->rounded_test_cases) as $test_cases) {
  404. foreach ($test_cases as $expected => $input) {
  405. $this->assertEqual(
  406. ($result = format_size($input, NULL)),
  407. $expected,
  408. $expected . ' == ' . $result . ' (' . $input . ' bytes)'
  409. );
  410. }
  411. }
  412. }
  413. /**
  414. * Check that parse_size() returns the proper byte sizes.
  415. */
  416. function testCommonParseSize() {
  417. foreach ($this->exact_test_cases as $string => $size) {
  418. $this->assertEqual(
  419. $parsed_size = parse_size($string),
  420. $size,
  421. $size . ' == ' . $parsed_size . ' (' . $string . ')'
  422. );
  423. }
  424. // Some custom parsing tests
  425. $string = '23476892 bytes';
  426. $this->assertEqual(
  427. ($parsed_size = parse_size($string)),
  428. $size = 23476892,
  429. $string . ' == ' . $parsed_size . ' bytes'
  430. );
  431. $string = '76MRandomStringThatShouldBeIgnoredByParseSize.'; // 76 MB
  432. $this->assertEqual(
  433. $parsed_size = parse_size($string),
  434. $size = 79691776,
  435. $string . ' == ' . $parsed_size . ' bytes'
  436. );
  437. $string = '76.24 Giggabyte'; // Misspeld text -> 76.24 GB
  438. $this->assertEqual(
  439. $parsed_size = parse_size($string),
  440. $size = 81862076662,
  441. $string . ' == ' . $parsed_size . ' bytes'
  442. );
  443. }
  444. /**
  445. * Cross-test parse_size() and format_size().
  446. */
  447. function testCommonParseSizeFormatSize() {
  448. foreach ($this->exact_test_cases as $size) {
  449. $this->assertEqual(
  450. $size,
  451. ($parsed_size = parse_size($string = format_size($size, NULL))),
  452. $size . ' == ' . $parsed_size . ' (' . $string . ')'
  453. );
  454. }
  455. }
  456. }
  457. /**
  458. * Test drupal_explode_tags() and drupal_implode_tags().
  459. */
  460. class DrupalTagsHandlingTestCase extends DrupalUnitTestCase {
  461. var $validTags = array(
  462. 'Drupal' => 'Drupal',
  463. 'Drupal with some spaces' => 'Drupal with some spaces',
  464. '"Legendary Drupal mascot of doom: ""Druplicon"""' => 'Legendary Drupal mascot of doom: "Druplicon"',
  465. '"Drupal, although it rhymes with sloopal, is as awesome as a troopal!"' => 'Drupal, although it rhymes with sloopal, is as awesome as a troopal!',
  466. );
  467. public static function getInfo() {
  468. return array(
  469. 'name' => 'Drupal tags handling',
  470. 'description' => "Performs tests on Drupal's handling of tags, both explosion and implosion tactics used.",
  471. 'group' => 'System'
  472. );
  473. }
  474. /**
  475. * Explode a series of tags.
  476. */
  477. function testDrupalExplodeTags() {
  478. $string = implode(', ', array_keys($this->validTags));
  479. $tags = drupal_explode_tags($string);
  480. $this->assertTags($tags);
  481. }
  482. /**
  483. * Implode a series of tags.
  484. */
  485. function testDrupalImplodeTags() {
  486. $tags = array_values($this->validTags);
  487. // Let's explode and implode to our heart's content.
  488. for ($i = 0; $i < 10; $i++) {
  489. $string = drupal_implode_tags($tags);
  490. $tags = drupal_explode_tags($string);
  491. }
  492. $this->assertTags($tags);
  493. }
  494. /**
  495. * Helper function: asserts that the ending array of tags is what we wanted.
  496. */
  497. function assertTags($tags) {
  498. $original = $this->validTags;
  499. foreach ($tags as $tag) {
  500. $key = array_search($tag, $original);
  501. $this->assertTrue($key, t('Make sure tag %tag shows up in the final tags array (originally %original)', array('%tag' => $tag, '%original' => $key)));
  502. unset($original[$key]);
  503. }
  504. foreach ($original as $leftover) {
  505. $this->fail(t('Leftover tag %leftover was left over.', array('%leftover' => $leftover)));
  506. }
  507. }
  508. }
  509. /**
  510. * Test the Drupal CSS system.
  511. */
  512. class CascadingStylesheetsTestCase extends DrupalWebTestCase {
  513. public static function getInfo() {
  514. return array(
  515. 'name' => 'Cascading stylesheets',
  516. 'description' => 'Tests adding various cascading stylesheets to the page.',
  517. 'group' => 'System',
  518. );
  519. }
  520. function setUp() {
  521. parent::setUp('php', 'locale', 'common_test');
  522. // Reset drupal_add_css() before each test.
  523. drupal_static_reset('drupal_add_css');
  524. }
  525. /**
  526. * Check default stylesheets as empty.
  527. */
  528. function testDefault() {
  529. $this->assertEqual(array(), drupal_add_css(), t('Default CSS is empty.'));
  530. }
  531. /**
  532. * Test that stylesheets in module .info files are loaded.
  533. */
  534. function testModuleInfo() {
  535. $this->drupalGet('');
  536. // Verify common_test.css in a STYLE media="all" tag.
  537. $elements = $this->xpath('//style[@media=:media and contains(text(), :filename)]', array(
  538. ':media' => 'all',
  539. ':filename' => 'tests/common_test.css',
  540. ));
  541. $this->assertTrue(count($elements), "Stylesheet with media 'all' in module .info file found.");
  542. // Verify common_test.print.css in a STYLE media="print" tag.
  543. $elements = $this->xpath('//style[@media=:media and contains(text(), :filename)]', array(
  544. ':media' => 'print',
  545. ':filename' => 'tests/common_test.print.css',
  546. ));
  547. $this->assertTrue(count($elements), "Stylesheet with media 'print' in module .info file found.");
  548. }
  549. /**
  550. * Tests adding a file stylesheet.
  551. */
  552. function testAddFile() {
  553. $path = drupal_get_path('module', 'simpletest') . '/simpletest.css';
  554. $css = drupal_add_css($path);
  555. $this->assertEqual($css[$path]['data'], $path, t('Adding a CSS file caches it properly.'));
  556. }
  557. /**
  558. * Tests adding an external stylesheet.
  559. */
  560. function testAddExternal() {
  561. $path = 'http://example.com/style.css';
  562. $css = drupal_add_css($path, 'external');
  563. $this->assertEqual($css[$path]['type'], 'external', t('Adding an external CSS file caches it properly.'));
  564. }
  565. /**
  566. * Makes sure that reseting the CSS empties the cache.
  567. */
  568. function testReset() {
  569. drupal_static_reset('drupal_add_css');
  570. $this->assertEqual(array(), drupal_add_css(), t('Resetting the CSS empties the cache.'));
  571. }
  572. /**
  573. * Tests rendering the stylesheets.
  574. */
  575. function testRenderFile() {
  576. $css = drupal_get_path('module', 'simpletest') . '/simpletest.css';
  577. drupal_add_css($css);
  578. $styles = drupal_get_css();
  579. $this->assertTrue(strpos($styles, $css) > 0, t('Rendered CSS includes the added stylesheet.'));
  580. }
  581. /**
  582. * Tests rendering an external stylesheet.
  583. */
  584. function testRenderExternal() {
  585. $css = 'http://example.com/style.css';
  586. drupal_add_css($css, 'external');
  587. $styles = drupal_get_css();
  588. // Stylesheet URL may be the href of a LINK tag or in an @import statement
  589. // of a STYLE tag.
  590. $this->assertTrue(strpos($styles, 'href="' . $css) > 0 || strpos($styles, '@import url("' . $css . '")') > 0, t('Rendering an external CSS file.'));
  591. }
  592. /**
  593. * Tests rendering inline stylesheets with preprocessing on.
  594. */
  595. function testRenderInlinePreprocess() {
  596. $css = 'body { padding: 0px; }';
  597. $css_preprocessed = '<style type="text/css" media="all">' . "\n<!--/*--><![CDATA[/*><!--*/\n" . drupal_load_stylesheet_content($css, TRUE) . "\n/*]]>*/-->\n" . '</style>';
  598. drupal_add_css($css, array('type' => 'inline'));
  599. $styles = drupal_get_css();
  600. $this->assertEqual(trim($styles), $css_preprocessed, t('Rendering preprocessed inline CSS adds it to the page.'));
  601. }
  602. /**
  603. * Tests rendering inline stylesheets with preprocessing off.
  604. */
  605. function testRenderInlineNoPreprocess() {
  606. $css = 'body { padding: 0px; }';
  607. drupal_add_css($css, array('type' => 'inline', 'preprocess' => FALSE));
  608. $styles = drupal_get_css();
  609. $this->assertTrue(strpos($styles, $css) > 0, t('Rendering non-preprocessed inline CSS adds it to the page.'));
  610. }
  611. /**
  612. * Tests rendering inline stylesheets through a full page request.
  613. */
  614. function testRenderInlineFullPage() {
  615. $css = 'body { font-size: 254px; }';
  616. // Inline CSS is minified unless 'preprocess' => FALSE is passed as a
  617. // drupal_add_css() option.
  618. $expected = 'body{font-size:254px;}';
  619. // Create a node, using the PHP filter that tests drupal_add_css().
  620. $php_format_id = 'php_code';
  621. $settings = array(
  622. 'type' => 'page',
  623. 'body' => array(
  624. LANGUAGE_NONE => array(
  625. array(
  626. 'value' => t('This tests the inline CSS!') . "<?php drupal_add_css('$css', 'inline'); ?>",
  627. 'format' => $php_format_id,
  628. ),
  629. ),
  630. ),
  631. 'promote' => 1,
  632. );
  633. $node = $this->drupalCreateNode($settings);
  634. // Fetch the page.
  635. $this->drupalGet('node/' . $node->nid);
  636. $this->assertRaw($expected, t('Inline stylesheets appear in the full page rendering.'));
  637. }
  638. /**
  639. * Test CSS ordering.
  640. */
  641. function testRenderOrder() {
  642. // A module CSS file.
  643. drupal_add_css(drupal_get_path('module', 'simpletest') . '/simpletest.css');
  644. // A few system CSS files, ordered in a strange way.
  645. $system_path = drupal_get_path('module', 'system');
  646. drupal_add_css($system_path . '/system.menus.css', array('group' => CSS_SYSTEM));
  647. drupal_add_css($system_path . '/system.base.css', array('group' => CSS_SYSTEM, 'weight' => -10));
  648. drupal_add_css($system_path . '/system.theme.css', array('group' => CSS_SYSTEM));
  649. $expected = array(
  650. $system_path . '/system.base.css',
  651. $system_path . '/system.menus.css',
  652. $system_path . '/system.theme.css',
  653. drupal_get_path('module', 'simpletest') . '/simpletest.css',
  654. );
  655. $styles = drupal_get_css();
  656. // Stylesheet URL may be the href of a LINK tag or in an @import statement
  657. // of a STYLE tag.
  658. if (preg_match_all('/(href="|url\(")' . preg_quote($GLOBALS['base_url'] . '/', '/') . '([^?]+)\?/', $styles, $matches)) {
  659. $result = $matches[2];
  660. }
  661. else {
  662. $result = array();
  663. }
  664. $this->assertIdentical($result, $expected, t('The CSS files are in the expected order.'));
  665. }
  666. /**
  667. * Test CSS override.
  668. */
  669. function testRenderOverride() {
  670. $system = drupal_get_path('module', 'system');
  671. $simpletest = drupal_get_path('module', 'simpletest');
  672. drupal_add_css($system . '/system.base.css');
  673. drupal_add_css($simpletest . '/tests/system.base.css');
  674. // The dummy stylesheet should be the only one included.
  675. $styles = drupal_get_css();
  676. $this->assert(strpos($styles, $simpletest . '/tests/system.base.css') !== FALSE, t('The overriding CSS file is output.'));
  677. $this->assert(strpos($styles, $system . '/system.base.css') === FALSE, t('The overridden CSS file is not output.'));
  678. drupal_add_css($simpletest . '/tests/system.base.css');
  679. drupal_add_css($system . '/system.base.css');
  680. // The standard stylesheet should be the only one included.
  681. $styles = drupal_get_css();
  682. $this->assert(strpos($styles, $system . '/system.base.css') !== FALSE, t('The overriding CSS file is output.'));
  683. $this->assert(strpos($styles, $simpletest . '/tests/system.base.css') === FALSE, t('The overridden CSS file is not output.'));
  684. }
  685. /**
  686. * Tests Locale module's CSS Alter to include RTL overrides.
  687. */
  688. function testAlter() {
  689. // Switch the language to a right to left language and add system.base.css.
  690. global $language;
  691. $language->direction = LANGUAGE_RTL;
  692. $path = drupal_get_path('module', 'system');
  693. drupal_add_css($path . '/system.base.css');
  694. // Check to see if system.base-rtl.css was also added.
  695. $styles = drupal_get_css();
  696. $this->assert(strpos($styles, $path . '/system.base-rtl.css') !== FALSE, t('CSS is alterable as right to left overrides are added.'));
  697. // Change the language back to left to right.
  698. $language->direction = LANGUAGE_LTR;
  699. }
  700. /**
  701. * Tests that the query string remains intact when adding CSS files that have
  702. * query string parameters.
  703. */
  704. function testAddCssFileWithQueryString() {
  705. $this->drupalGet('common-test/query-string');
  706. $query_string = variable_get('css_js_query_string', '0');
  707. $this->assertRaw(drupal_get_path('module', 'node') . '/node.css?' . $query_string, t('Query string was appended correctly to css.'));
  708. $this->assertRaw(drupal_get_path('module', 'node') . '/node-fake.css?arg1=value1&amp;arg2=value2', t('Query string not escaped on a URI.'));
  709. }
  710. }
  711. /**
  712. * Test for cleaning HTML identifiers.
  713. */
  714. class DrupalHTMLIdentifierTestCase extends DrupalUnitTestCase {
  715. public static function getInfo() {
  716. return array(
  717. 'name' => 'HTML identifiers',
  718. 'description' => 'Test the functions drupal_html_class(), drupal_html_id() and drupal_clean_css_identifier() for expected behavior',
  719. 'group' => 'System',
  720. );
  721. }
  722. /**
  723. * Tests that drupal_clean_css_identifier() cleans the identifier properly.
  724. */
  725. function testDrupalCleanCSSIdentifier() {
  726. // Verify that no valid ASCII characters are stripped from the identifier.
  727. $identifier = 'abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789';
  728. $this->assertIdentical(drupal_clean_css_identifier($identifier, array()), $identifier, t('Verify valid ASCII characters pass through.'));
  729. // Verify that valid UTF-8 characters are not stripped from the identifier.
  730. $identifier = '¡¢£¤¥';
  731. $this->assertIdentical(drupal_clean_css_identifier($identifier, array()), $identifier, t('Verify valid UTF-8 characters pass through.'));
  732. // Verify that invalid characters (including non-breaking space) are stripped from the identifier.
  733. $this->assertIdentical(drupal_clean_css_identifier('invalid !"#$%&\'()*+,./:;<=>?@[\\]^`{|}~ identifier', array()), 'invalididentifier', t('Strip invalid characters.'));
  734. }
  735. /**
  736. * Tests that drupal_html_class() cleans the class name properly.
  737. */
  738. function testDrupalHTMLClass() {
  739. // Verify Drupal coding standards are enforced.
  740. $this->assertIdentical(drupal_html_class('CLASS NAME_[Ü]'), 'class-name--ü', t('Enforce Drupal coding standards.'));
  741. }
  742. /**
  743. * Tests that drupal_html_id() cleans the ID properly.
  744. */
  745. function testDrupalHTMLId() {
  746. // Verify that letters, digits, and hyphens are not stripped from the ID.
  747. $id = 'abcdefghijklmnopqrstuvwxyz-0123456789';
  748. $this->assertIdentical(drupal_html_id($id), $id, t('Verify valid characters pass through.'));
  749. // Verify that invalid characters are stripped from the ID.
  750. $this->assertIdentical(drupal_html_id('invalid,./:@\\^`{Üidentifier'), 'invalididentifier', t('Strip invalid characters.'));
  751. // Verify Drupal coding standards are enforced.
  752. $this->assertIdentical(drupal_html_id('ID NAME_[1]'), 'id-name-1', t('Enforce Drupal coding standards.'));
  753. // Reset the static cache so we can ensure the unique id count is at zero.
  754. drupal_static_reset('drupal_html_id');
  755. // Clean up IDs with invalid starting characters.
  756. $this->assertIdentical(drupal_html_id('test-unique-id'), 'test-unique-id', t('Test the uniqueness of IDs #1.'));
  757. $this->assertIdentical(drupal_html_id('test-unique-id'), 'test-unique-id--2', t('Test the uniqueness of IDs #2.'));
  758. $this->assertIdentical(drupal_html_id('test-unique-id'), 'test-unique-id--3', t('Test the uniqueness of IDs #3.'));
  759. }
  760. }
  761. /**
  762. * CSS Unit Tests.
  763. */
  764. class CascadingStylesheetsUnitTest extends DrupalUnitTestCase {
  765. public static function getInfo() {
  766. return array(
  767. 'name' => 'CSS Unit Tests',
  768. 'description' => 'Unit tests on CSS functions like aggregation.',
  769. 'group' => 'System',
  770. );
  771. }
  772. /**
  773. * Tests basic CSS loading with and without optimization via drupal_load_stylesheet().
  774. *
  775. * Known tests:
  776. * - Retain white-space in selectors. (http://drupal.org/node/472820)
  777. * - Proper URLs in imported files. (http://drupal.org/node/265719)
  778. * - Retain pseudo-selectors. (http://drupal.org/node/460448)
  779. */
  780. function testLoadCssBasic() {
  781. // Array of files to test living in 'simpletest/files/css_test_files/'.
  782. // - Original: name.css
  783. // - Unoptimized expected content: name.css.unoptimized.css
  784. // - Optimized expected content: name.css.optimized.css
  785. $testfiles = array(
  786. 'css_input_without_import.css',
  787. 'css_input_with_import.css',
  788. 'comment_hacks.css'
  789. );
  790. $path = drupal_get_path('module', 'simpletest') . '/files/css_test_files';
  791. foreach ($testfiles as $file) {
  792. $expected = file_get_contents("$path/$file.unoptimized.css");
  793. $unoptimized_output = drupal_load_stylesheet("$path/$file.unoptimized.css", FALSE);
  794. $this->assertEqual($unoptimized_output, $expected, t('Unoptimized CSS file has expected contents (@file)', array('@file' => $file)));
  795. $expected = file_get_contents("$path/$file.optimized.css");
  796. $optimized_output = drupal_load_stylesheet("$path/$file", TRUE);
  797. $this->assertEqual($optimized_output, $expected, t('Optimized CSS file has expected contents (@file)', array('@file' => $file)));
  798. // Repeat the tests by accessing the stylesheets by URL.
  799. $expected = file_get_contents("$path/$file.unoptimized.css");
  800. $unoptimized_output_url = drupal_load_stylesheet($GLOBALS['base_url'] . "/$path/$file.unoptimized.css", FALSE);
  801. $this->assertEqual($unoptimized_output, $expected, t('Unoptimized CSS file (loaded from an URL) has expected contents (@file)', array('@file' => $file)));
  802. $expected = file_get_contents("$path/$file.optimized.css");
  803. $optimized_output = drupal_load_stylesheet($GLOBALS['base_url'] . "/$path/$file", TRUE);
  804. $this->assertEqual($optimized_output, $expected, t('Optimized CSS file (loaded from an URL) has expected contents (@file)', array('@file' => $file)));
  805. }
  806. }
  807. }
  808. /**
  809. * Test drupal_http_request().
  810. */
  811. class DrupalHTTPRequestTestCase extends DrupalWebTestCase {
  812. public static function getInfo() {
  813. return array(
  814. 'name' => 'Drupal HTTP request',
  815. 'description' => "Performs tests on Drupal's HTTP request mechanism.",
  816. 'group' => 'System'
  817. );
  818. }
  819. function setUp() {
  820. parent::setUp('system_test', 'locale');
  821. }
  822. function testDrupalHTTPRequest() {
  823. global $is_https;
  824. // Parse URL schema.
  825. $missing_scheme = drupal_http_request('example.com/path');
  826. $this->assertEqual($missing_scheme->code, -1002, t('Returned with "-1002" error code.'));
  827. $this->assertEqual($missing_scheme->error, 'missing schema', t('Returned with "missing schema" error message.'));
  828. $unable_to_parse = drupal_http_request('http:///path');
  829. $this->assertEqual($unable_to_parse->code, -1001, t('Returned with "-1001" error code.'));
  830. $this->assertEqual($unable_to_parse->error, 'unable to parse URL', t('Returned with "unable to parse URL" error message.'));
  831. // Fetch page.
  832. $result = drupal_http_request(url('node', array('absolute' => TRUE)));
  833. $this->assertEqual($result->code, 200, t('Fetched page successfully.'));
  834. $this->drupalSetContent($result->data);
  835. $this->assertTitle(t('Welcome to @site-name | @site-name', array('@site-name' => variable_get('site_name', 'Drupal'))), t('Site title matches.'));
  836. // Test that code and status message is returned.
  837. $result = drupal_http_request(url('pagedoesnotexist', array('absolute' => TRUE)));
  838. $this->assertTrue(!empty($result->protocol), t('Result protocol is returned.'));
  839. $this->assertEqual($result->code, '404', t('Result code is 404'));
  840. $this->assertEqual($result->status_message, 'Not Found', t('Result status message is "Not Found"'));
  841. // Skip the timeout tests when the testing environment is HTTPS because
  842. // stream_set_timeout() does not work for SSL connections.
  843. // @link http://bugs.php.net/bug.php?id=47929
  844. if (!$is_https) {
  845. // Test that timeout is respected. The test machine is expected to be able
  846. // to make the connection (i.e. complete the fsockopen()) in 2 seconds and
  847. // return within a total of 5 seconds. If the test machine is extremely
  848. // slow, the test will fail. fsockopen() has been seen to time out in
  849. // slightly less than the specified timeout, so allow a little slack on
  850. // the minimum expected time (i.e. 1.8 instead of 2).
  851. timer_start(__METHOD__);
  852. $result = drupal_http_request(url('system-test/sleep/10', array('absolute' => TRUE)), array('timeout' => 2));
  853. $time = timer_read(__METHOD__) / 1000;
  854. $this->assertTrue(1.8 < $time && $time < 5, t('Request timed out (%time seconds).', array('%time' => $time)));
  855. $this->assertTrue($result->error, t('An error message was returned.'));
  856. $this->assertEqual($result->code, HTTP_REQUEST_TIMEOUT, t('Proper error code was returned.'));
  857. }
  858. }
  859. function testDrupalHTTPRequestBasicAuth() {
  860. $username = $this->randomName();
  861. $password = $this->randomName();
  862. $url = url('system-test/auth', array('absolute' => TRUE));
  863. $auth = str_replace('://', '://' . $username . ':' . $password . '@', $url);
  864. $result = drupal_http_request($auth);
  865. $this->drupalSetContent($result->data);
  866. $this->assertRaw($username, t('$_SERVER["PHP_AUTH_USER"] is passed correctly.'));
  867. $this->assertRaw($password, t('$_SERVER["PHP_AUTH_PW"] is passed correctly.'));
  868. }
  869. function testDrupalHTTPRequestRedirect() {
  870. $redirect_301 = drupal_http_request(url('system-test/redirect/301', array('absolute' => TRUE)), array('max_redirects' => 1));
  871. $this->assertEqual($redirect_301->redirect_code, 301, t('drupal_http_request follows the 301 redirect.'));
  872. $redirect_301 = drupal_http_request(url('system-test/redirect/301', array('absolute' => TRUE)), array('max_redirects' => 0));
  873. $this->assertFalse(isset($redirect_301->redirect_code), t('drupal_http_request does not follow 301 redirect if max_redirects = 0.'));
  874. $redirect_invalid = drupal_http_request(url('system-test/redirect-noscheme', array('absolute' => TRUE)), array('max_redirects' => 1));
  875. $this->assertEqual($redirect_invalid->code, -1002, t('301 redirect to invalid URL returned with error code !error.', array('!error' => $redirect_invalid->error)));
  876. $this->assertEqual($redirect_invalid->error, 'missing schema', t('301 redirect to invalid URL returned with error message "!error".', array('!error' => $redirect_invalid->error)));
  877. $redirect_invalid = drupal_http_request(url('system-test/redirect-noparse', array('absolute' => TRUE)), array('max_redirects' => 1));
  878. $this->assertEqual($redirect_invalid->code, -1001, t('301 redirect to invalid URL returned with error message code "!error".', array('!error' => $redirect_invalid->error)));
  879. $this->assertEqual($redirect_invalid->error, 'unable to parse URL', t('301 redirect to invalid URL returned with error message "!error".', array('!error' => $redirect_invalid->error)));
  880. $redirect_invalid = drupal_http_request(url('system-test/redirect-invalid-scheme', array('absolute' => TRUE)), array('max_redirects' => 1));
  881. $this->assertEqual($redirect_invalid->code, -1003, t('301 redirect to invalid URL returned with error code !error.', array('!error' => $redirect_invalid->error)));
  882. $this->assertEqual($redirect_invalid->error, 'invalid schema ftp', t('301 redirect to invalid URL returned with error message "!error".', array('!error' => $redirect_invalid->error)));
  883. $redirect_302 = drupal_http_request(url('system-test/redirect/302', array('absolute' => TRUE)), array('max_redirects' => 1));
  884. $this->assertEqual($redirect_302->redirect_code, 302, t('drupal_http_request follows the 302 redirect.'));
  885. $redirect_302 = drupal_http_request(url('system-test/redirect/302', array('absolute' => TRUE)), array('max_redirects' => 0));
  886. $this->assertFalse(isset($redirect_302->redirect_code), t('drupal_http_request does not follow 302 redirect if $retry = 0.'));
  887. $redirect_307 = drupal_http_request(url('system-test/redirect/307', array('absolute' => TRUE)), array('max_redirects' => 1));
  888. $this->assertEqual($redirect_307->redirect_code, 307, t('drupal_http_request follows the 307 redirect.'));
  889. $redirect_307 = drupal_http_request(url('system-test/redirect/307', array('absolute' => TRUE)), array('max_redirects' => 0));
  890. $this->assertFalse(isset($redirect_307->redirect_code), t('drupal_http_request does not follow 307 redirect if max_redirects = 0.'));
  891. $multiple_redirect_final_url = url('system-test/multiple-redirects/0', array('absolute' => TRUE));
  892. $multiple_redirect_1 = drupal_http_request(url('system-test/multiple-redirects/1', array('absolute' => TRUE)), array('max_redirects' => 1));
  893. $this->assertEqual($multiple_redirect_1->redirect_url, $multiple_redirect_final_url, t('redirect_url contains the final redirection location after 1 redirect.'));
  894. $multiple_redirect_3 = drupal_http_request(url('system-test/multiple-redirects/3', array('absolute' => TRUE)), array('max_redirects' => 3));
  895. $this->assertEqual($multiple_redirect_3->redirect_url, $multiple_redirect_final_url, t('redirect_url contains the final redirection location after 3 redirects.'));
  896. }
  897. /**
  898. * Tests Content-language headers generated by Drupal.
  899. */
  900. function testDrupalHTTPRequestHeaders() {
  901. // Check the default header.
  902. $request = drupal_http_request(url('<front>', array('absolute' => TRUE)));
  903. $this->assertEqual($request->headers['content-language'], 'en', 'Content-Language HTTP header is English.');
  904. // Add German language and set as default.
  905. locale_add_language('de', 'German', 'Deutsch', LANGUAGE_LTR, '', '', TRUE, TRUE);
  906. // Request front page and check for matching Content-Language.
  907. $request = drupal_http_request(url('<front>', array('absolute' => TRUE)));
  908. $this->assertEqual($request->headers['content-language'], 'de', 'Content-Language HTTP header is German.');
  909. }
  910. }
  911. /**
  912. * Testing drupal_add_region_content and drupal_get_region_content.
  913. */
  914. class DrupalSetContentTestCase extends DrupalWebTestCase {
  915. public static function getInfo() {
  916. return array(
  917. 'name' => 'Drupal set/get regions',
  918. 'description' => 'Performs tests on setting and retrieiving content from theme regions.',
  919. 'group' => 'System'
  920. );
  921. }
  922. /**
  923. * Test setting and retrieving content for theme regions.
  924. */
  925. function testRegions() {
  926. global $theme_key;
  927. $block_regions = array_keys(system_region_list($theme_key));
  928. $delimiter = $this->randomName(32);
  929. $values = array();
  930. // Set some random content for each region available.
  931. foreach ($block_regions as $region) {
  932. $first_chunk = $this->randomName(32);
  933. drupal_add_region_content($region, $first_chunk);
  934. $second_chunk = $this->randomName(32);
  935. drupal_add_region_content($region, $second_chunk);
  936. // Store the expected result for a drupal_get_region_content call for this region.
  937. $values[$region] = $first_chunk . $delimiter . $second_chunk;
  938. }
  939. // Ensure drupal_get_region_content returns expected results when fetching all regions.
  940. $content = drupal_get_region_content(NULL, $delimiter);
  941. foreach ($content as $region => $region_content) {
  942. $this->assertEqual($region_content, $values[$region], t('@region region text verified when fetching all regions', array('@region' => $region)));
  943. }
  944. // Ensure drupal_get_region_content returns expected results when fetching a single region.
  945. foreach ($block_regions as $region) {
  946. $region_content = drupal_get_region_content($region, $delimiter);
  947. $this->assertEqual($region_content, $values[$region], t('@region region text verified when fetching single region.', array('@region' => $region)));
  948. }
  949. }
  950. }
  951. /**
  952. * Testing drupal_goto and hook_drupal_goto_alter().
  953. */
  954. class DrupalGotoTest extends DrupalWebTestCase {
  955. public static function getInfo() {
  956. return array(
  957. 'name' => 'Drupal goto',
  958. 'description' => 'Performs tests on the drupal_goto function and hook_drupal_goto_alter',
  959. 'group' => 'System'
  960. );
  961. }
  962. function setUp() {
  963. parent::setUp('common_test');
  964. }
  965. /**
  966. * Test drupal_goto().
  967. */
  968. function testDrupalGoto() {
  969. $this->drupalGet('common-test/drupal_goto/redirect');
  970. $headers = $this->drupalGetHeaders(TRUE);
  971. list(, $status) = explode(' ', $headers[0][':status'], 3);
  972. $this->assertEqual($status, 302, t('Expected response code was sent.'));
  973. $this->assertText('drupal_goto', t('Drupal goto redirect succeeded.'));
  974. $this->assertEqual($this->getUrl(), url('common-test/drupal_goto', array('absolute' => TRUE)), t('Drupal goto redirected to expected URL.'));
  975. $this->drupalGet('common-test/drupal_goto/redirect_advanced');
  976. $headers = $this->drupalGetHeaders(TRUE);
  977. list(, $status) = explode(' ', $headers[0][':status'], 3);
  978. $this->assertEqual($status, 301, t('Expected response code was sent.'));
  979. $this->assertText('drupal_goto', t('Drupal goto redirect succeeded.'));
  980. $this->assertEqual($this->getUrl(), url('common-test/drupal_goto', array('query' => array('foo' => '123'), 'absolute' => TRUE)), t('Drupal goto redirected to expected URL.'));
  981. // Test that drupal_goto() respects ?destination=xxx. Use an complicated URL
  982. // to test that the path is encoded and decoded properly.
  983. $destination = 'common-test/drupal_goto/destination?foo=%2525&bar=123';
  984. $this->drupalGet('common-test/drupal_goto/redirect', array('query' => array('destination' => $destination)));
  985. $this->assertText('drupal_goto', t('Drupal goto redirect with destination succeeded.'));
  986. $this->assertEqual($this->getUrl(), url('common-test/drupal_goto/destination', array('query' => array('foo' => '%25', 'bar' => '123'), 'absolute' => TRUE)), t('Drupal goto redirected to given query string destination.'));
  987. }
  988. /**
  989. * Test hook_drupal_goto_alter().
  990. */
  991. function testDrupalGotoAlter() {
  992. $this->drupalGet('common-test/drupal_goto/redirect_fail');
  993. $this->assertNoText(t("Drupal goto failed to stop program"), t("Drupal goto stopped program."));
  994. $this->assertNoText('drupal_goto_fail', t("Drupal goto redirect failed."));
  995. }
  996. /**
  997. * Test drupal_get_destination().
  998. */
  999. function testDrupalGetDestination() {
  1000. $query = $this->randomName(10);
  1001. // Verify that a 'destination' query string is used as destination.
  1002. $this->drupalGet('common-test/destination', array('query' => array('destination' => $query)));
  1003. $this->assertText('The destination: ' . $query, t('The given query string destination is determined as destination.'));
  1004. // Verify that the current path is used as destination.
  1005. $this->drupalGet('common-test/destination', array('query' => array($query => NULL)));
  1006. $url = 'common-test/destination?' . $query;
  1007. $this->assertText('The destination: ' . $url, t('The current path is determined as destination.'));
  1008. }
  1009. }
  1010. /**
  1011. * Tests for the JavaScript system.
  1012. */
  1013. class JavaScriptTestCase extends DrupalWebTestCase {
  1014. /**
  1015. * Store configured value for JavaScript preprocessing.
  1016. */
  1017. protected $preprocess_js = NULL;
  1018. public static function getInfo() {
  1019. return array(
  1020. 'name' => 'JavaScript',
  1021. 'description' => 'Tests the JavaScript system.',
  1022. 'group' => 'System'
  1023. );
  1024. }
  1025. function setUp() {
  1026. // Enable Locale and SimpleTest in the test environment.
  1027. parent::setUp('locale', 'simpletest', 'common_test');
  1028. // Disable preprocessing
  1029. $this->preprocess_js = variable_get('preprocess_js', 0);
  1030. variable_set('preprocess_js', 0);
  1031. // Reset drupal_add_js() and drupal_add_library() statics before each test.
  1032. drupal_static_reset('drupal_add_js');
  1033. drupal_static_reset('drupal_add_library');
  1034. }
  1035. function tearDown() {
  1036. // Restore configured value for JavaScript preprocessing.
  1037. variable_set('preprocess_js', $this->preprocess_js);
  1038. parent::tearDown();
  1039. }
  1040. /**
  1041. * Test default JavaScript is empty.
  1042. */
  1043. function testDefault() {
  1044. $this->assertEqual(array(), drupal_add_js(), t('Default JavaScript is empty.'));
  1045. }
  1046. /**
  1047. * Test adding a JavaScript file.
  1048. */
  1049. function testAddFile() {
  1050. $javascript = drupal_add_js('misc/collapse.js');
  1051. $this->assertTrue(array_key_exists('misc/jquery.js', $javascript), t('jQuery is added when a file is added.'));
  1052. $this->assertTrue(array_key_exists('misc/drupal.js', $javascript), t('Drupal.js is added when file is added.'));
  1053. $this->assertTrue(array_key_exists('misc/collapse.js', $javascript), t('JavaScript files are correctly added.'));
  1054. $this->assertEqual(base_path(), $javascript['settings']['data'][0]['basePath'], t('Base path JavaScript setting is correctly set.'));
  1055. url('', array('prefix' => &$prefix));
  1056. $this->assertEqual(empty($prefix) ? '' : $prefix, $javascript['settings']['data'][1]['pathPrefix'], t('Path prefix JavaScript setting is correctly set.'));
  1057. }
  1058. /**
  1059. * Test adding settings.
  1060. */
  1061. function testAddSetting() {
  1062. $javascript = drupal_add_js(array('drupal' => 'rocks', 'dries' => 280342800), 'setting');
  1063. $this->assertEqual(280342800, $javascript['settings']['data'][2]['dries'], t('JavaScript setting is set correctly.'));
  1064. $this->assertEqual('rocks', $javascript['settings']['data'][2]['drupal'], t('The other JavaScript setting is set correctly.'));
  1065. }
  1066. /**
  1067. * Tests adding an external JavaScript File.
  1068. */
  1069. function testAddExternal() {
  1070. $path = 'http://example.com/script.js';
  1071. $javascript = drupal_add_js($path, 'external');
  1072. $this->assertTrue(array_key_exists('http://example.com/script.js', $javascript), t('Added an external JavaScript file.'));
  1073. }
  1074. /**
  1075. * Test drupal_get_js() for JavaScript settings.
  1076. */
  1077. function testHeaderSetting() {
  1078. // Only the second of these two entries should appear in Drupal.settings.
  1079. drupal_add_js(array('commonTest' => 'commonTestShouldNotAppear'), 'setting');
  1080. drupal_add_js(array('commonTest' => 'commonTestShouldAppear'), 'setting');
  1081. // All three of these entries should appear in Drupal.settings.
  1082. drupal_add_js(array('commonTestArray' => array('commonTestValue0')), 'setting');
  1083. drupal_add_js(array('commonTestArray' => array('commonTestValue1')), 'setting');
  1084. drupal_add_js(array('commonTestArray' => array('commonTestValue2')), 'setting');
  1085. // Only the second of these two entries should appear in Drupal.settings.
  1086. drupal_add_js(array('commonTestArray' => array('key' => 'commonTestOldValue')), 'setting');
  1087. drupal_add_js(array('commonTestArray' => array('key' => 'commonTestNewValue')), 'setting');
  1088. $javascript = drupal_get_js('header');
  1089. $this->assertTrue(strpos($javascript, 'basePath') > 0, t('Rendered JavaScript header returns basePath setting.'));
  1090. $this->assertTrue(strpos($javascript, 'misc/jquery.js') > 0, t('Rendered JavaScript header includes jQuery.'));
  1091. $this->assertTrue(strpos($javascript, 'pathPrefix') > 0, t('Rendered JavaScript header returns pathPrefix setting.'));
  1092. // Test whether drupal_add_js can be used to override a previous setting.
  1093. $this->assertTrue(strpos($javascript, 'commonTestShouldAppear') > 0, t('Rendered JavaScript header returns custom setting.'));
  1094. $this->assertTrue(strpos($javascript, 'commonTestShouldNotAppear') === FALSE, t('drupal_add_js() correctly overrides a custom setting.'));
  1095. // Test whether drupal_add_js can be used to add numerically indexed values
  1096. // to an array.
  1097. $array_values_appear = strpos($javascript, 'commonTestValue0') > 0 && strpos($javascript, 'commonTestValue1') > 0 && strpos($javascript, 'commonTestValue2') > 0;
  1098. $this->assertTrue($array_values_appear, t('drupal_add_js() correctly adds settings to the end of an indexed array.'));
  1099. // Test whether drupal_add_js can be used to override the entry for an
  1100. // existing key in an associative array.
  1101. $associative_array_override = strpos($javascript, 'commonTestNewValue') > 0 && strpos($javascript, 'commonTestOldValue') === FALSE;
  1102. $this->assertTrue($associative_array_override, t('drupal_add_js() correctly overrides settings within an associative array.'));
  1103. }
  1104. /**
  1105. * Test to see if resetting the JavaScript empties the cache.
  1106. */
  1107. function testReset() {
  1108. drupal_add_js('misc/collapse.js');
  1109. drupal_static_reset('drupal_add_js');
  1110. $this->assertEqual(array(), drupal_add_js(), t('Resetting the JavaScript correctly empties the cache.'));
  1111. }
  1112. /**
  1113. * Test adding inline scripts.
  1114. */
  1115. function testAddInline() {
  1116. $inline = 'jQuery(function () { });';
  1117. $javascript = drupal_add_js($inline, array('type' => 'inline', 'scope' => 'footer'));
  1118. $this->assertTrue(array_key_exists('misc/jquery.js', $javascript), t('jQuery is added when inline scripts are added.'));
  1119. $data = end($javascript);
  1120. $this->assertEqual($inline, $data['data'], t('Inline JavaScript is correctly added to the footer.'));
  1121. }
  1122. /**
  1123. * Test rendering an external JavaScript file.
  1124. */
  1125. function testRenderExternal() {
  1126. $external = 'http://example.com/example.js';
  1127. drupal_add_js($external, 'external');
  1128. $javascript = drupal_get_js();
  1129. // Local files have a base_path() prefix, external files should not.
  1130. $this->assertTrue(strpos($javascript, 'src="' . $external) > 0, t('Rendering an external JavaScript file.'));
  1131. }
  1132. /**
  1133. * Test drupal_get_js() with a footer scope.
  1134. */
  1135. function testFooterHTML() {
  1136. $inline = 'jQuery(function () { });';
  1137. drupal_add_js($inline, array('type' => 'inline', 'scope' => 'footer'));
  1138. $javascript = drupal_get_js('footer');
  1139. $this->assertTrue(strpos($javascript, $inline) > 0, t('Rendered JavaScript footer returns the inline code.'));
  1140. }
  1141. /**
  1142. * Test drupal_add_js() sets preproccess to false when cache is set to false.
  1143. */
  1144. function testNoCache() {
  1145. $javascript = drupal_add_js('misc/collapse.js', array('cache' => FALSE));
  1146. $this->assertFalse($javascript['misc/collapse.js']['preprocess'], t('Setting cache to FALSE sets proprocess to FALSE when adding JavaScript.'));
  1147. }
  1148. /**
  1149. * Test adding a JavaScript file with a different group.
  1150. */
  1151. function testDifferentGroup() {
  1152. $javascript = drupal_add_js('misc/collapse.js', array('group' => JS_THEME));
  1153. $this->assertEqual($javascript['misc/collapse.js']['group'], JS_THEME, t('Adding a JavaScript file with a different group caches the given group.'));
  1154. }
  1155. /**
  1156. * Test adding a JavaScript file with a different weight.
  1157. */
  1158. function testDifferentWeight() {
  1159. $javascript = drupal_add_js('misc/collapse.js', array('weight' => 2));
  1160. $this->assertEqual($javascript['misc/collapse.js']['weight'], 2, t('Adding a JavaScript file with a different weight caches the given weight.'));
  1161. }
  1162. /**
  1163. * Tests JavaScript aggregation when files are added to a different scope.
  1164. */
  1165. function testAggregationOrder() {
  1166. // Enable JavaScript aggregation.
  1167. variable_set('preprocess_js', 1);
  1168. drupal_static_reset('drupal_add_js');
  1169. // Add two JavaScript files to the current request and build the cache.
  1170. drupal_add_js('misc/ajax.js');
  1171. drupal_add_js('misc/autocomplete.js');
  1172. $js_items = drupal_add_js();
  1173. drupal_build_js_cache(array(
  1174. 'misc/ajax.js' => $js_items['misc/ajax.js'],
  1175. 'misc/autocomplete.js' => $js_items['misc/autocomplete.js']
  1176. ));
  1177. // Store the expected key for the first item in the cache.
  1178. $cache = array_keys(variable_get('drupal_js_cache_files', array()));
  1179. $expected_key = $cache[0];
  1180. // Reset variables and add a file in a different scope first.
  1181. variable_del('drupal_js_cache_files');
  1182. drupal_static_reset('drupal_add_js');
  1183. drupal_add_js('some/custom/javascript_file.js', array('scope' => 'footer'));
  1184. drupal_add_js('misc/ajax.js');
  1185. drupal_add_js('misc/autocomplete.js');
  1186. // Rebuild the cache.
  1187. $js_items = drupal_add_js();
  1188. drupal_build_js_cache(array(
  1189. 'misc/ajax.js' => $js_items['misc/ajax.js'],
  1190. 'misc/autocomplete.js' => $js_items['misc/autocomplete.js']
  1191. ));
  1192. // Compare the expected key for the first file to the current one.
  1193. $cache = array_keys(variable_get('drupal_js_cache_files', array()));
  1194. $key = $cache[0];
  1195. $this->assertEqual($key, $expected_key, 'JavaScript aggregation is not affected by ordering in different scopes.');
  1196. }
  1197. /**
  1198. * Test JavaScript ordering.
  1199. */
  1200. function testRenderOrder() {
  1201. // Add a bunch of JavaScript in strange ordering.
  1202. drupal_add_js('(function($){alert("Weight 5 #1");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => 5));
  1203. drupal_add_js('(function($){alert("Weight 0 #1");})(jQuery);', array('type' => 'inline', 'scope' => 'footer'));
  1204. drupal_add_js('(function($){alert("Weight 0 #2");})(jQuery);', array('type' => 'inline', 'scope' => 'footer'));
  1205. drupal_add_js('(function($){alert("Weight -8 #1");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => -8));
  1206. drupal_add_js('(function($){alert("Weight -8 #2");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => -8));
  1207. drupal_add_js('(function($){alert("Weight -8 #3");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => -8));
  1208. drupal_add_js('http://example.com/example.js?Weight -5 #1', array('type' => 'external', 'scope' => 'footer', 'weight' => -5));
  1209. drupal_add_js('(function($){alert("Weight -8 #4");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => -8));
  1210. drupal_add_js('(function($){alert("Weight 5 #2");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => 5));
  1211. drupal_add_js('(function($){alert("Weight 0 #3");})(jQuery);', array('type' => 'inline', 'scope' => 'footer'));
  1212. // Construct the expected result from the regex.
  1213. $expected = array(
  1214. "-8 #1",
  1215. "-8 #2",
  1216. "-8 #3",
  1217. "-8 #4",
  1218. "-5 #1", // The external script.
  1219. "0 #1",
  1220. "0 #2",
  1221. "0 #3",
  1222. "5 #1",
  1223. "5 #2",
  1224. );
  1225. // Retrieve the rendered JavaScript and test against the regex.
  1226. $js = drupal_get_js('footer');
  1227. $matches = array();
  1228. if (preg_match_all('/Weight\s([-0-9]+\s[#0-9]+)/', $js, $matches)) {
  1229. $result = $matches[1];
  1230. }
  1231. else {
  1232. $result = array();
  1233. }
  1234. $this->assertIdentical($result, $expected, t('JavaScript is added in the expected weight order.'));
  1235. }
  1236. /**
  1237. * Test rendering the JavaScript with a file's weight above jQuery's.
  1238. */
  1239. function testRenderDifferentWeight() {
  1240. // JavaScript files are sorted first by group, then by the 'every_page'
  1241. // flag, then by weight (see drupal_sort_css_js()), so to test the effect of
  1242. // weight, we need the other two options to be the same.
  1243. drupal_add_js('misc/collapse.js', array('group' => JS_LIBRARY, 'every_page' => TRUE, 'weight' => -21));
  1244. $javascript = drupal_get_js();
  1245. $this->assertTrue(strpos($javascript, 'misc/collapse.js') < strpos($javascript, 'misc/jquery.js'), t('Rendering a JavaScript file above jQuery.'));
  1246. }
  1247. /**
  1248. * Test altering a JavaScript's weight via hook_js_alter().
  1249. *
  1250. * @see simpletest_js_alter()
  1251. */
  1252. function testAlter() {
  1253. // Add both tableselect.js and simpletest.js, with a larger weight on SimpleTest.
  1254. drupal_add_js('misc/tableselect.js');
  1255. drupal_add_js(drupal_get_path('module', 'simpletest') . '/simpletest.js', array('weight' => 9999));
  1256. // Render the JavaScript, testing if simpletest.js was altered to be before
  1257. // tableselect.js. See simpletest_js_alter() to see where this alteration
  1258. // takes place.
  1259. $javascript = drupal_get_js();
  1260. $this->assertTrue(strpos($javascript, 'simpletest.js') < strpos($javascript, 'misc/tableselect.js'), t('Altering JavaScript weight through the alter hook.'));
  1261. }
  1262. /**
  1263. * Adds a library to the page and tests for both its JavaScript and its CSS.
  1264. */
  1265. function testLibraryRender() {
  1266. $result = drupal_add_library('system', 'farbtastic');
  1267. $this->assertTrue($result !== FALSE, t('Library was added without errors.'));
  1268. $scripts = drupal_get_js();
  1269. $styles = drupal_get_css();
  1270. $this->assertTrue(strpos($scripts, 'misc/farbtastic/farbtastic.js'), t('JavaScript of library was added to the page.'));
  1271. $this->assertTrue(strpos($styles, 'misc/farbtastic/farbtastic.css'), t('Stylesheet of library was added to the page.'));
  1272. }
  1273. /**
  1274. * Adds a JavaScript library to the page and alters it.
  1275. *
  1276. * @see common_test_library_alter()
  1277. */
  1278. function testLibraryAlter() {
  1279. // Verify that common_test altered the title of Farbtastic.
  1280. $library = drupal_get_library('system', 'farbtastic');
  1281. $this->assertEqual($library['title'], 'Farbtastic: Altered Library', t('Registered libraries were altered.'));
  1282. // common_test_library_alter() also added a dependency on jQuery Form.
  1283. drupal_add_library('system', 'farbtastic');
  1284. $scripts = drupal_get_js();
  1285. $this->assertTrue(strpos($scripts, 'misc/jquery.form.js'), t('Altered library dependencies are added to the page.'));
  1286. }
  1287. /**
  1288. * Tests that multiple modules can implement the same library.
  1289. *
  1290. * @see common_test_library()
  1291. */
  1292. function testLibraryNameConflicts() {
  1293. $farbtastic = drupal_get_library('common_test', 'farbtastic');
  1294. $this->assertEqual($farbtastic['title'], 'Custom Farbtastic Library', t('Alternative libraries can be added to the page.'));
  1295. }
  1296. /**
  1297. * Tests non-existing libraries.
  1298. */
  1299. function testLibraryUnknown() {
  1300. $result = drupal_get_library('unknown', 'unknown');
  1301. $this->assertFalse($result, t('Unknown library returned FALSE.'));
  1302. drupal_static_reset('drupal_get_library');
  1303. $result = drupal_add_library('unknown', 'unknown');
  1304. $this->assertFalse($result, t('Unknown library returned FALSE.'));
  1305. $scripts = drupal_get_js();
  1306. $this->assertTrue(strpos($scripts, 'unknown') === FALSE, t('Unknown library was not added to the page.'));
  1307. }
  1308. /**
  1309. * Tests the addition of libraries through the #attached['library'] property.
  1310. */
  1311. function testAttachedLibrary() {
  1312. $element['#attached']['library'][] = array('system', 'farbtastic');
  1313. drupal_render($element);
  1314. $scripts = drupal_get_js();
  1315. $this->assertTrue(strpos($scripts, 'misc/farbtastic/farbtastic.js'), t('The attached_library property adds the additional libraries.'));
  1316. }
  1317. /**
  1318. * Tests retrieval of libraries via drupal_get_library().
  1319. */
  1320. function testGetLibrary() {
  1321. // Retrieve all libraries registered by a module.
  1322. $libraries = drupal_get_library('common_test');
  1323. $this->assertTrue(isset($libraries['farbtastic']), t('Retrieved all module libraries.'));
  1324. // Retrieve all libraries for a module not implementing hook_library().
  1325. // Note: This test installs Locale module.
  1326. $libraries = drupal_get_library('locale');
  1327. $this->assertEqual($libraries, array(), t('Retrieving libraries from a module not implementing hook_library() returns an emtpy array.'));
  1328. // Retrieve a specific library by module and name.
  1329. $farbtastic = drupal_get_library('common_test', 'farbtastic');
  1330. $this->assertEqual($farbtastic['version'], '5.3', t('Retrieved a single library.'));
  1331. // Retrieve a non-existing library by module and name.
  1332. $farbtastic = drupal_get_library('common_test', 'foo');
  1333. $this->assertIdentical($farbtastic, FALSE, t('Retrieving a non-existing library returns FALSE.'));
  1334. }
  1335. /**
  1336. * Tests that the query string remains intact when adding JavaScript files
  1337. * that have query string parameters.
  1338. */
  1339. function testAddJsFileWithQueryString() {
  1340. $this->drupalGet('common-test/query-string');
  1341. $query_string = variable_get('css_js_query_string', '0');
  1342. $this->assertRaw(drupal_get_path('module', 'node') . '/node.js?' . $query_string, t('Query string was appended correctly to js.'));
  1343. }
  1344. }
  1345. /**
  1346. * Tests for drupal_render().
  1347. */
  1348. class DrupalRenderTestCase extends DrupalWebTestCase {
  1349. public static function getInfo() {
  1350. return array(
  1351. 'name' => 'drupal_render()',
  1352. 'description' => 'Performs functional tests on drupal_render().',
  1353. 'group' => 'System',
  1354. );
  1355. }
  1356. function setUp() {
  1357. parent::setUp('common_test');
  1358. }
  1359. /**
  1360. * Test sorting by weight.
  1361. */
  1362. function testDrupalRenderSorting() {
  1363. $first = $this->randomName();
  1364. $second = $this->randomName();
  1365. // Build an array with '#weight' set for each element.
  1366. $elements = array(
  1367. 'second' => array(
  1368. '#weight' => 10,
  1369. '#markup' => $second,
  1370. ),
  1371. 'first' => array(
  1372. '#weight' => 0,
  1373. '#markup' => $first,
  1374. ),
  1375. );
  1376. $output = drupal_render($elements);
  1377. // The lowest weight element should appear last in $output.
  1378. $this->assertTrue(strpos($output, $second) > strpos($output, $first), t('Elements were sorted correctly by weight.'));
  1379. // Confirm that the $elements array has '#sorted' set to TRUE.
  1380. $this->assertTrue($elements['#sorted'], t("'#sorted' => TRUE was added to the array"));
  1381. // Pass $elements through element_children() and ensure it remains
  1382. // sorted in the correct order. drupal_render() will return an empty string
  1383. // if used on the same array in the same request.
  1384. $children = element_children($elements);
  1385. $this->assertTrue(array_shift($children) == 'first', t('Child found in the correct order.'));
  1386. $this->assertTrue(array_shift($children) == 'second', t('Child found in the correct order.'));
  1387. // The same array structure again, but with #sorted set to TRUE.
  1388. $elements = array(
  1389. 'second' => array(
  1390. '#weight' => 10,
  1391. '#markup' => $second,
  1392. ),
  1393. 'first' => array(
  1394. '#weight' => 0,
  1395. '#markup' => $first,
  1396. ),
  1397. '#sorted' => TRUE,
  1398. );
  1399. $output = drupal_render($elements);
  1400. // The elements should appear in output in the same order as the array.
  1401. $this->assertTrue(strpos($output, $second) < strpos($output, $first), t('Elements were not sorted.'));
  1402. }
  1403. /**
  1404. * Test #attached functionality in children elements.
  1405. */
  1406. function testDrupalRenderChildrenAttached() {
  1407. // The cache system is turned off for POST requests.
  1408. $request_method = $_SERVER['REQUEST_METHOD'];
  1409. $_SERVER['REQUEST_METHOD'] = 'GET';
  1410. // Create an element with a child and subchild. Each element loads a
  1411. // different JavaScript file using #attached.
  1412. $parent_js = drupal_get_path('module', 'user') . '/user.js';
  1413. $child_js = drupal_get_path('module', 'forum') . '/forum.js';
  1414. $subchild_js = drupal_get_path('module', 'book') . '/book.js';
  1415. $element = array(
  1416. '#type' => 'fieldset',
  1417. '#cache' => array(
  1418. 'keys' => array('simpletest', 'drupal_render', 'children_attached'),
  1419. ),
  1420. '#attached' => array('js' => array($parent_js)),
  1421. '#title' => 'Parent',
  1422. );
  1423. $element['child'] = array(
  1424. '#type' => 'fieldset',
  1425. '#attached' => array('js' => array($child_js)),
  1426. '#title' => 'Child',
  1427. );
  1428. $element['child']['subchild'] = array(
  1429. '#attached' => array('js' => array($subchild_js)),
  1430. '#markup' => 'Subchild',
  1431. );
  1432. // Render the element and verify the presence of #attached JavaScript.
  1433. drupal_render($element);
  1434. $scripts = drupal_get_js();
  1435. $this->assertTrue(strpos($scripts, $parent_js), t('The element #attached JavaScript was included.'));
  1436. $this->assertTrue(strpos($scripts, $child_js), t('The child #attached JavaScript was included.'));
  1437. $this->assertTrue(strpos($scripts, $subchild_js), t('The subchild #attached JavaScript was included.'));
  1438. // Load the element from cache and verify the presence of the #attached
  1439. // JavaScript.
  1440. drupal_static_reset('drupal_add_js');
  1441. $this->assertTrue(drupal_render_cache_get($element), t('The element was retrieved from cache.'));
  1442. $scripts = drupal_get_js();
  1443. $this->assertTrue(strpos($scripts, $parent_js), t('The element #attached JavaScript was included when loading from cache.'));
  1444. $this->assertTrue(strpos($scripts, $child_js), t('The child #attached JavaScript was included when loading from cache.'));
  1445. $this->assertTrue(strpos($scripts, $subchild_js), t('The subchild #attached JavaScript was included when loading from cache.'));
  1446. $_SERVER['REQUEST_METHOD'] = $request_method;
  1447. }
  1448. /**
  1449. * Test passing arguments to the theme function.
  1450. */
  1451. function testDrupalRenderThemeArguments() {
  1452. $element = array(
  1453. '#theme' => 'common_test_foo',
  1454. );
  1455. // Test that defaults work.
  1456. $this->assertEqual(drupal_render($element), 'foobar', 'Defaults work');
  1457. $element = array(
  1458. '#theme' => 'common_test_foo',
  1459. '#foo' => $this->randomName(),
  1460. '#bar' => $this->randomName(),
  1461. );
  1462. // Test that passing arguments to the theme function works.
  1463. $this->assertEqual(drupal_render($element), $element['#foo'] . $element['#bar'], 'Passing arguments to theme functions works');
  1464. }
  1465. /**
  1466. * Test rendering form elements without passing through form_builder().
  1467. */
  1468. function testDrupalRenderFormElements() {
  1469. // Define a series of form elements.
  1470. $element = array(
  1471. '#type' => 'button',
  1472. '#value' => $this->randomName(),
  1473. );
  1474. $this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'submit'));
  1475. $element = array(
  1476. '#type' => 'textfield',
  1477. '#title' => $this->randomName(),
  1478. '#value' => $this->randomName(),
  1479. );
  1480. $this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'text'));
  1481. $element = array(
  1482. '#type' => 'password',
  1483. '#title' => $this->randomName(),
  1484. );
  1485. $this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'password'));
  1486. $element = array(
  1487. '#type' => 'textarea',
  1488. '#title' => $this->randomName(),
  1489. '#value' => $this->randomName(),
  1490. );
  1491. $this->assertRenderedElement($element, '//textarea');
  1492. $element = array(
  1493. '#type' => 'radio',
  1494. '#title' => $this->randomName(),
  1495. '#value' => FALSE,
  1496. );
  1497. $this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'radio'));
  1498. $element = array(
  1499. '#type' => 'checkbox',
  1500. '#title' => $this->randomName(),
  1501. );
  1502. $this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'checkbox'));
  1503. $element = array(
  1504. '#type' => 'select',
  1505. '#title' => $this->randomName(),
  1506. '#options' => array(
  1507. 0 => $this->randomName(),
  1508. 1 => $this->randomName(),
  1509. ),
  1510. );
  1511. $this->assertRenderedElement($element, '//select');
  1512. $element = array(
  1513. '#type' => 'file',
  1514. '#title' => $this->randomName(),
  1515. );
  1516. $this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'file'));
  1517. $element = array(
  1518. '#type' => 'item',
  1519. '#title' => $this->randomName(),
  1520. '#markup' => $this->randomName(),
  1521. );
  1522. $this->assertRenderedElement($element, '//div[contains(@class, :class) and contains(., :markup)]/label[contains(., :label)]', array(
  1523. ':class' => 'form-type-item',
  1524. ':markup' => $element['#markup'],
  1525. ':label' => $element['#title'],
  1526. ));
  1527. $element = array(
  1528. '#type' => 'hidden',
  1529. '#title' => $this->randomName(),
  1530. '#value' => $this->randomName(),
  1531. );
  1532. $this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'hidden'));
  1533. $element = array(
  1534. '#type' => 'link',
  1535. '#title' => $this->randomName(),
  1536. '#href' => $this->randomName(),
  1537. '#options' => array(
  1538. 'absolute' => TRUE,
  1539. ),
  1540. );
  1541. $this->assertRenderedElement($element, '//a[@href=:href and contains(., :title)]', array(
  1542. ':href' => url($element['#href'], array('absolute' => TRUE)),
  1543. ':title' => $element['#title'],
  1544. ));
  1545. $element = array(
  1546. '#type' => 'fieldset',
  1547. '#title' => $this->randomName(),
  1548. );
  1549. $this->assertRenderedElement($element, '//fieldset/legend[contains(., :title)]', array(
  1550. ':title' => $element['#title'],
  1551. ));
  1552. $element['item'] = array(
  1553. '#type' => 'item',
  1554. '#title' => $this->randomName(),
  1555. '#markup' => $this->randomName(),
  1556. );
  1557. $this->assertRenderedElement($element, '//fieldset/div/div[contains(@class, :class) and contains(., :markup)]', array(
  1558. ':class' => 'form-type-item',
  1559. ':markup' => $element['item']['#markup'],
  1560. ));
  1561. }
  1562. protected function assertRenderedElement(array $element, $xpath, array $xpath_args = array()) {
  1563. $original_element = $element;
  1564. $this->drupalSetContent(drupal_render($element));
  1565. $this->verbose('<pre>' . check_plain(var_export($original_element, TRUE)) . '</pre>'
  1566. . '<pre>' . check_plain(var_export($element, TRUE)) . '</pre>'
  1567. . '<hr />' . $this->drupalGetContent()
  1568. );
  1569. // @see DrupalWebTestCase::xpath()
  1570. $xpath = $this->buildXPathQuery($xpath, $xpath_args);
  1571. $element += array('#value' => NULL);
  1572. $this->assertFieldByXPath($xpath, $element['#value'], t('#type @type was properly rendered.', array(
  1573. '@type' => var_export($element['#type'], TRUE),
  1574. )));
  1575. }
  1576. /**
  1577. * Tests caching of an empty render item.
  1578. */
  1579. function testDrupalRenderCache() {
  1580. // Force a request via GET.
  1581. $request_method = $_SERVER['REQUEST_METHOD'];
  1582. $_SERVER['REQUEST_METHOD'] = 'GET';
  1583. // Create an empty element.
  1584. $test_element = array(
  1585. '#cache' => array(
  1586. 'cid' => 'render_cache_test',
  1587. ),
  1588. '#markup' => '',
  1589. );
  1590. // Render the element and confirm that it goes through the rendering
  1591. // process (which will set $element['#printed']).
  1592. $element = $test_element;
  1593. drupal_render($element);
  1594. $this->assertTrue(isset($element['#printed']), t('No cache hit'));
  1595. // Render the element again and confirm that it is retrieved from the cache
  1596. // instead (so $element['#printed'] will not be set).
  1597. $element = $test_element;
  1598. drupal_render($element);
  1599. $this->assertFalse(isset($element['#printed']), t('Cache hit'));
  1600. // Restore the previous request method.
  1601. $_SERVER['REQUEST_METHOD'] = $request_method;
  1602. }
  1603. }
  1604. /**
  1605. * Test for valid_url().
  1606. */
  1607. class ValidUrlTestCase extends DrupalUnitTestCase {
  1608. public static function getInfo() {
  1609. return array(
  1610. 'name' => 'Valid URL',
  1611. 'description' => "Performs tests on Drupal's valid URL function.",
  1612. 'group' => 'System'
  1613. );
  1614. }
  1615. /**
  1616. * Test valid absolute URLs.
  1617. */
  1618. function testValidAbsolute() {
  1619. $url_schemes = array('http', 'https', 'ftp');
  1620. $valid_absolute_urls = array(
  1621. 'example.com',
  1622. 'www.example.com',
  1623. 'ex-ample.com',
  1624. '3xampl3.com',
  1625. 'example.com/paren(the)sis',
  1626. 'example.com/index.html#pagetop',
  1627. 'example.com:8080',
  1628. 'subdomain.example.com',
  1629. 'example.com/index.php?q=node',
  1630. 'example.com/index.php?q=node&param=false',
  1631. 'user@www.example.com',
  1632. 'user:pass@www.example.com:8080/login.php?do=login&style=%23#pagetop',
  1633. '127.0.0.1',
  1634. 'example.org?',
  1635. 'john%20doe:secret:foo@example.org/',
  1636. 'example.org/~,$\'*;',
  1637. 'caf%C3%A9.example.org',
  1638. '[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html',
  1639. );
  1640. foreach ($url_schemes as $scheme) {
  1641. foreach ($valid_absolute_urls as $url) {
  1642. $test_url = $scheme . '://' . $url;
  1643. $valid_url = valid_url($test_url, TRUE);
  1644. $this->assertTrue($valid_url, t('@url is a valid url.', array('@url' => $test_url)));
  1645. }
  1646. }
  1647. }
  1648. /**
  1649. * Test invalid absolute URLs.
  1650. */
  1651. function testInvalidAbsolute() {
  1652. $url_schemes = array('http', 'https', 'ftp');
  1653. $invalid_ablosule_urls = array(
  1654. '',
  1655. 'ex!ample.com',
  1656. 'ex%ample.com',
  1657. );
  1658. foreach ($url_schemes as $scheme) {
  1659. foreach ($invalid_ablosule_urls as $url) {
  1660. $test_url = $scheme . '://' . $url;
  1661. $valid_url = valid_url($test_url, TRUE);
  1662. $this->assertFalse($valid_url, t('@url is NOT a valid url.', array('@url' => $test_url)));
  1663. }
  1664. }
  1665. }
  1666. /**
  1667. * Test valid relative URLs.
  1668. */
  1669. function testValidRelative() {
  1670. $valid_relative_urls = array(
  1671. 'paren(the)sis',
  1672. 'index.html#pagetop',
  1673. 'index.php?q=node',
  1674. 'index.php?q=node&param=false',
  1675. 'login.php?do=login&style=%23#pagetop',
  1676. );
  1677. foreach (array('', '/') as $front) {
  1678. foreach ($valid_relative_urls as $url) {
  1679. $test_url = $front . $url;
  1680. $valid_url = valid_url($test_url);
  1681. $this->assertTrue($valid_url, t('@url is a valid url.', array('@url' => $test_url)));
  1682. }
  1683. }
  1684. }
  1685. /**
  1686. * Test invalid relative URLs.
  1687. */
  1688. function testInvalidRelative() {
  1689. $invalid_relative_urls = array(
  1690. 'ex^mple',
  1691. 'example<>',
  1692. 'ex%ample',
  1693. );
  1694. foreach (array('', '/') as $front) {
  1695. foreach ($invalid_relative_urls as $url) {
  1696. $test_url = $front . $url;
  1697. $valid_url = valid_url($test_url);
  1698. $this->assertFALSE($valid_url, t('@url is NOT a valid url.', array('@url' => $test_url)));
  1699. }
  1700. }
  1701. }
  1702. }
  1703. /**
  1704. * Tests for CRUD API functions.
  1705. */
  1706. class DrupalDataApiTest extends DrupalWebTestCase {
  1707. public static function getInfo() {
  1708. return array(
  1709. 'name' => 'Data API functions',
  1710. 'description' => 'Tests the performance of CRUD APIs.',
  1711. 'group' => 'System',
  1712. );
  1713. }
  1714. function setUp() {
  1715. parent::setUp('database_test');
  1716. }
  1717. /**
  1718. * Test the drupal_write_record() API function.
  1719. */
  1720. function testDrupalWriteRecord() {
  1721. // Insert a record - no columns allow NULL values.
  1722. $person = new stdClass();
  1723. $person->name = 'John';
  1724. $person->unknown_column = 123;
  1725. $insert_result = drupal_write_record('test', $person);
  1726. $this->assertTrue($insert_result == SAVED_NEW, t('Correct value returned when a record is inserted with drupal_write_record() for a table with a single-field primary key.'));
  1727. $this->assertTrue(isset($person->id), t('Primary key is set on record created with drupal_write_record().'));
  1728. $this->assertIdentical($person->age, 0, t('Age field set to default value.'));
  1729. $this->assertIdentical($person->job, 'Undefined', t('Job field set to default value.'));
  1730. // Verify that the record was inserted.
  1731. $result = db_query("SELECT * FROM {test} WHERE id = :id", array(':id' => $person->id))->fetchObject();
  1732. $this->assertIdentical($result->name, 'John', t('Name field set.'));
  1733. $this->assertIdentical($result->age, '0', t('Age field set to default value.'));
  1734. $this->assertIdentical($result->job, 'Undefined', t('Job field set to default value.'));
  1735. $this->assertFalse(isset($result->unknown_column), t('Unknown column was ignored.'));
  1736. // Update the newly created record.
  1737. $person->name = 'Peter';
  1738. $person->age = 27;
  1739. $person->job = NULL;
  1740. $update_result = drupal_write_record('test', $person, array('id'));
  1741. $this->assertTrue($update_result == SAVED_UPDATED, t('Correct value returned when a record updated with drupal_write_record() for table with single-field primary key.'));
  1742. // Verify that the record was updated.
  1743. $result = db_query("SELECT * FROM {test} WHERE id = :id", array(':id' => $person->id))->fetchObject();
  1744. $this->assertIdentical($result->name, 'Peter', t('Name field set.'));
  1745. $this->assertIdentical($result->age, '27', t('Age field set.'));
  1746. $this->assertIdentical($result->job, '', t('Job field set and cast to string.'));
  1747. // Try to insert NULL in columns that does not allow this.
  1748. $person = new stdClass();
  1749. $person->name = 'Ringo';
  1750. $person->age = NULL;
  1751. $person->job = NULL;
  1752. $insert_result = drupal_write_record('test', $person);
  1753. $this->assertTrue(isset($person->id), t('Primary key is set on record created with drupal_write_record().'));
  1754. $result = db_query("SELECT * FROM {test} WHERE id = :id", array(':id' => $person->id))->fetchObject();
  1755. $this->assertIdentical($result->name, 'Ringo', t('Name field set.'));
  1756. $this->assertIdentical($result->age, '0', t('Age field set.'));
  1757. $this->assertIdentical($result->job, '', t('Job field set.'));
  1758. // Insert a record - the "age" column allows NULL.
  1759. $person = new stdClass();
  1760. $person->name = 'Paul';
  1761. $person->age = NULL;
  1762. $insert_result = drupal_write_record('test_null', $person);
  1763. $this->assertTrue(isset($person->id), t('Primary key is set on record created with drupal_write_record().'));
  1764. $result = db_query("SELECT * FROM {test_null} WHERE id = :id", array(':id' => $person->id))->fetchObject();
  1765. $this->assertIdentical($result->name, 'Paul', t('Name field set.'));
  1766. $this->assertIdentical($result->age, NULL, t('Age field set.'));
  1767. // Insert a record - do not specify the value of a column that allows NULL.
  1768. $person = new stdClass();
  1769. $person->name = 'Meredith';
  1770. $insert_result = drupal_write_record('test_null', $person);
  1771. $this->assertTrue(isset($person->id), t('Primary key is set on record created with drupal_write_record().'));
  1772. $this->assertIdentical($person->age, 0, t('Age field set to default value.'));
  1773. $result = db_query("SELECT * FROM {test_null} WHERE id = :id", array(':id' => $person->id))->fetchObject();
  1774. $this->assertIdentical($result->name, 'Meredith', t('Name field set.'));
  1775. $this->assertIdentical($result->age, '0', t('Age field set to default value.'));
  1776. // Update the newly created record.
  1777. $person->name = 'Mary';
  1778. $person->age = NULL;
  1779. $update_result = drupal_write_record('test_null', $person, array('id'));
  1780. $result = db_query("SELECT * FROM {test_null} WHERE id = :id", array(':id' => $person->id))->fetchObject();
  1781. $this->assertIdentical($result->name, 'Mary', t('Name field set.'));
  1782. $this->assertIdentical($result->age, NULL, t('Age field set.'));
  1783. // Insert a record - the "data" column should be serialized.
  1784. $person = new stdClass();
  1785. $person->name = 'Dave';
  1786. $update_result = drupal_write_record('test_serialized', $person);
  1787. $result = db_query("SELECT * FROM {test_serialized} WHERE id = :id", array(':id' => $person->id))->fetchObject();
  1788. $this->assertIdentical($result->name, 'Dave', t('Name field set.'));
  1789. $this->assertIdentical($result->info, NULL, t('Info field set.'));
  1790. $person->info = array();
  1791. $update_result = drupal_write_record('test_serialized', $person, array('id'));
  1792. $result = db_query("SELECT * FROM {test_serialized} WHERE id = :id", array(':id' => $person->id))->fetchObject();
  1793. $this->assertIdentical(unserialize($result->info), array(), t('Info field updated.'));
  1794. // Update the serialized record.
  1795. $data = array('foo' => 'bar', 1 => 2, 'empty' => '', 'null' => NULL);
  1796. $person->info = $data;
  1797. $update_result = drupal_write_record('test_serialized', $person, array('id'));
  1798. $result = db_query("SELECT * FROM {test_serialized} WHERE id = :id", array(':id' => $person->id))->fetchObject();
  1799. $this->assertIdentical(unserialize($result->info), $data, t('Info field updated.'));
  1800. // Run an update query where no field values are changed. The database
  1801. // layer should return zero for number of affected rows, but
  1802. // db_write_record() should still return SAVED_UPDATED.
  1803. $update_result = drupal_write_record('test_null', $person, array('id'));
  1804. $this->assertTrue($update_result == SAVED_UPDATED, t('Correct value returned when a valid update is run without changing any values.'));
  1805. // Insert an object record for a table with a multi-field primary key.
  1806. $node_access = new stdClass();
  1807. $node_access->nid = mt_rand();
  1808. $node_access->gid = mt_rand();
  1809. $node_access->realm = $this->randomName();
  1810. $insert_result = drupal_write_record('node_access', $node_access);
  1811. $this->assertTrue($insert_result == SAVED_NEW, t('Correct value returned when a record is inserted with drupal_write_record() for a table with a multi-field primary key.'));
  1812. // Update the record.
  1813. $update_result = drupal_write_record('node_access', $node_access, array('nid', 'gid', 'realm'));
  1814. $this->assertTrue($update_result == SAVED_UPDATED, t('Correct value returned when a record is updated with drupal_write_record() for a table with a multi-field primary key.'));
  1815. }
  1816. }
  1817. /**
  1818. * Tests Simpletest error and exception collector.
  1819. */
  1820. class DrupalErrorCollectionUnitTest extends DrupalWebTestCase {
  1821. /**
  1822. * Errors triggered during the test.
  1823. *
  1824. * Errors are intercepted by the overriden implementation
  1825. * of DrupalWebTestCase::error below.
  1826. *
  1827. * @var Array
  1828. */
  1829. protected $collectedErrors = array();
  1830. public static function getInfo() {
  1831. return array(
  1832. 'name' => 'SimpleTest error collector',
  1833. 'description' => 'Performs tests on the Simpletest error and exception collector.',
  1834. 'group' => 'SimpleTest',
  1835. );
  1836. }
  1837. function setUp() {
  1838. parent::setUp('system_test', 'error_test');
  1839. }
  1840. /**
  1841. * Test that simpletest collects errors from the tested site.
  1842. */
  1843. function testErrorCollect() {
  1844. $this->collectedErrors = array();
  1845. $this->drupalGet('error-test/generate-warnings-with-report');
  1846. $this->assertEqual(count($this->collectedErrors), 3, t('Three errors were collected'));
  1847. if (count($this->collectedErrors) == 3) {
  1848. $this->assertError($this->collectedErrors[0], 'Notice', 'error_test_generate_warnings()', 'error_test.module', 'Undefined variable: bananas');
  1849. $this->assertError($this->collectedErrors[1], 'Warning', 'error_test_generate_warnings()', 'error_test.module', 'Division by zero');
  1850. $this->assertError($this->collectedErrors[2], 'User warning', 'error_test_generate_warnings()', 'error_test.module', 'Drupal is awesome');
  1851. }
  1852. else {
  1853. // Give back the errors to the log report.
  1854. foreach ($this->collectedErrors as $error) {
  1855. parent::error($error['message'], $error['group'], $error['caller']);
  1856. }
  1857. }
  1858. }
  1859. /**
  1860. * Error handler that collects errors in an array.
  1861. *
  1862. * This test class is trying to verify that simpletest correctly sees errors
  1863. * and warnings. However, it can't generate errors and warnings that
  1864. * propagate up to the testing framework itself, or these tests would always
  1865. * fail. So, this special copy of error() doesn't propagate the errors up
  1866. * the class hierarchy. It just stuffs them into a protected collectedErrors
  1867. * array for various assertions to inspect.
  1868. */
  1869. protected function error($message = '', $group = 'Other', array $caller = NULL) {
  1870. // Due to a WTF elsewhere, simpletest treats debug() and verbose()
  1871. // messages as if they were an 'error'. But, we don't want to collect
  1872. // those here. This function just wants to collect the real errors (PHP
  1873. // notices, PHP fatal errors, etc.), and let all the 'errors' from the
  1874. // 'User notice' group bubble up to the parent classes to be handled (and
  1875. // eventually displayed) as normal.
  1876. if ($group == 'User notice') {
  1877. parent::error($message, $group, $caller);
  1878. }
  1879. // Everything else should be collected but not propagated.
  1880. else {
  1881. $this->collectedErrors[] = array(
  1882. 'message' => $message,
  1883. 'group' => $group,
  1884. 'caller' => $caller
  1885. );
  1886. }
  1887. }
  1888. /**
  1889. * Assert that a collected error matches what we are expecting.
  1890. */
  1891. function assertError($error, $group, $function, $file, $message = NULL) {
  1892. $this->assertEqual($error['group'], $group, t("Group was %group", array('%group' => $group)));
  1893. $this->assertEqual($error['caller']['function'], $function, t("Function was %function", array('%function' => $function)));
  1894. $this->assertEqual(drupal_basename($error['caller']['file']), $file, t("File was %file", array('%file' => $file)));
  1895. if (isset($message)) {
  1896. $this->assertEqual($error['message'], $message, t("Message was %message", array('%message' => $message)));
  1897. }
  1898. }
  1899. }
  1900. /**
  1901. * Test the drupal_parse_info_file() API function.
  1902. */
  1903. class ParseInfoFilesTestCase extends DrupalUnitTestCase {
  1904. public static function getInfo() {
  1905. return array(
  1906. 'name' => 'Parsing .info files',
  1907. 'description' => 'Tests parsing .info files.',
  1908. 'group' => 'System',
  1909. );
  1910. }
  1911. /**
  1912. * Parse an example .info file an verify the results.
  1913. */
  1914. function testParseInfoFile() {
  1915. $info_values = drupal_parse_info_file(drupal_get_path('module', 'simpletest') . '/tests/common_test_info.txt');
  1916. $this->assertEqual($info_values['simple_string'], 'A simple string', t('Simple string value was parsed correctly.'), t('System'));
  1917. $this->assertEqual($info_values['simple_constant'], WATCHDOG_INFO, t('Constant value was parsed correctly.'), t('System'));
  1918. $this->assertEqual($info_values['double_colon'], 'dummyClassName::', t('Value containing double-colon was parsed correctly.'), t('System'));
  1919. }
  1920. }
  1921. /**
  1922. * Tests for the drupal_system_listing() function.
  1923. */
  1924. class DrupalSystemListingTestCase extends DrupalWebTestCase {
  1925. /**
  1926. * Use the testing profile; this is needed for testDirectoryPrecedence().
  1927. */
  1928. protected $profile = 'testing';
  1929. public static function getInfo() {
  1930. return array(
  1931. 'name' => 'Drupal system listing',
  1932. 'description' => 'Tests the mechanism for scanning system directories in drupal_system_listing().',
  1933. 'group' => 'System',
  1934. );
  1935. }
  1936. /**
  1937. * Test that files in different directories take precedence as expected.
  1938. */
  1939. function testDirectoryPrecedence() {
  1940. // Define the module files we will search for, and the directory precedence
  1941. // we expect.
  1942. $expected_directories = array(
  1943. // When the copy of the module in the profile directory is incompatible
  1944. // with Drupal core, the copy in the core modules directory takes
  1945. // precedence.
  1946. 'drupal_system_listing_incompatible_test' => array(
  1947. 'modules/simpletest/tests',
  1948. 'profiles/testing/modules',
  1949. ),
  1950. // When both copies of the module are compatible with Drupal core, the
  1951. // copy in the profile directory takes precedence.
  1952. 'drupal_system_listing_compatible_test' => array(
  1953. 'profiles/testing/modules',
  1954. 'modules/simpletest/tests',
  1955. ),
  1956. );
  1957. // This test relies on two versions of the same module existing in
  1958. // different places in the filesystem. Without that, the test has no
  1959. // meaning, so assert their presence first.
  1960. foreach ($expected_directories as $module => $directories) {
  1961. foreach ($directories as $directory) {
  1962. $filename = "$directory/$module/$module.module";
  1963. $this->assertTrue(file_exists(DRUPAL_ROOT . '/' . $filename), t('@filename exists.', array('@filename' => $filename)));
  1964. }
  1965. }
  1966. // Now scan the directories and check that the files take precedence as
  1967. // expected.
  1968. $files = drupal_system_listing('/\.module$/', 'modules', 'name', 1);
  1969. foreach ($expected_directories as $module => $directories) {
  1970. $expected_directory = array_shift($directories);
  1971. $expected_filename = "$expected_directory/$module/$module.module";
  1972. $this->assertEqual($files[$module]->uri, $expected_filename, t('Module @module was found at @filename.', array('@module' => $module, '@filename' => $expected_filename)));
  1973. }
  1974. }
  1975. }
  1976. /**
  1977. * Tests for the format_date() function.
  1978. */
  1979. class FormatDateUnitTest extends DrupalWebTestCase {
  1980. /**
  1981. * Arbitrary langcode for a custom language.
  1982. */
  1983. const LANGCODE = 'xx';
  1984. public static function getInfo() {
  1985. return array(
  1986. 'name' => 'Format date',
  1987. 'description' => 'Test the format_date() function.',
  1988. 'group' => 'System',
  1989. );
  1990. }
  1991. function setUp() {
  1992. parent::setUp('locale');
  1993. variable_set('configurable_timezones', 1);
  1994. variable_set('date_format_long', 'l, j. F Y - G:i');
  1995. variable_set('date_format_medium', 'j. F Y - G:i');
  1996. variable_set('date_format_short', 'Y M j - g:ia');
  1997. variable_set('locale_custom_strings_' . self::LANGCODE, array(
  1998. '' => array('Sunday' => 'domingo'),
  1999. 'Long month name' => array('March' => 'marzo'),
  2000. ));
  2001. $this->refreshVariables();
  2002. }
  2003. /**
  2004. * Test admin-defined formats in format_date().
  2005. */
  2006. function testAdminDefinedFormatDate() {
  2007. // Create an admin user.
  2008. $this->admin_user = $this->drupalCreateUser(array('administer site configuration'));
  2009. $this->drupalLogin($this->admin_user);
  2010. // Add new date format.
  2011. $admin_date_format = 'j M y';
  2012. $edit = array('date_format' => $admin_date_format);
  2013. $this->drupalPost('admin/config/regional/date-time/formats/add', $edit, t('Add format'));
  2014. // Add a new date format which just differs in the case.
  2015. $admin_date_format_uppercase = 'j M Y';
  2016. $edit = array('date_format' => $admin_date_format_uppercase);
  2017. $this->drupalPost('admin/config/regional/date-time/formats/add', $edit, t('Add format'));
  2018. $this->assertText(t('Custom date format added.'));
  2019. // Add new date type.
  2020. $edit = array(
  2021. 'date_type' => 'Example Style',
  2022. 'machine_name' => 'example_style',
  2023. 'date_format' => $admin_date_format,
  2024. );
  2025. $this->drupalPost('admin/config/regional/date-time/types/add', $edit, t('Add date type'));
  2026. // Add a second date format with a different case than the first.
  2027. $edit = array(
  2028. 'machine_name' => 'example_style_uppercase',
  2029. 'date_type' => 'Example Style Uppercase',
  2030. 'date_format' => $admin_date_format_uppercase,
  2031. );
  2032. $this->drupalPost('admin/config/regional/date-time/types/add', $edit, t('Add date type'));
  2033. $this->assertText(t('New date type added successfully.'));
  2034. $timestamp = strtotime('2007-03-10T00:00:00+00:00');
  2035. $this->assertIdentical(format_date($timestamp, 'example_style', '', 'America/Los_Angeles'), '9 Mar 07', t('Test format_date() using an admin-defined date type.'));
  2036. $this->assertIdentical(format_date($timestamp, 'example_style_uppercase', '', 'America/Los_Angeles'), '9 Mar 2007', 'Test format_date() using an admin-defined date type with different case.');
  2037. $this->assertIdentical(format_date($timestamp, 'undefined_style'), format_date($timestamp, 'medium'), t('Test format_date() defaulting to medium when $type not found.'));
  2038. }
  2039. /**
  2040. * Tests for the format_date() function.
  2041. */
  2042. function testFormatDate() {
  2043. global $user, $language;
  2044. $timestamp = strtotime('2007-03-26T00:00:00+00:00');
  2045. $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'America/Los_Angeles', 'en'), 'Sunday, 25-Mar-07 17:00:00 PDT', t('Test all parameters.'));
  2046. $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), 'domingo, 25-Mar-07 17:00:00 PDT', t('Test translated format.'));
  2047. $this->assertIdentical(format_date($timestamp, 'custom', '\\l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), 'l, 25-Mar-07 17:00:00 PDT', t('Test an escaped format string.'));
  2048. $this->assertIdentical(format_date($timestamp, 'custom', '\\\\l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), '\\domingo, 25-Mar-07 17:00:00 PDT', t('Test format containing backslash character.'));
  2049. $this->assertIdentical(format_date($timestamp, 'custom', '\\\\\\l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), '\\l, 25-Mar-07 17:00:00 PDT', t('Test format containing backslash followed by escaped format string.'));
  2050. $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'Europe/London', 'en'), 'Monday, 26-Mar-07 01:00:00 BST', t('Test a different time zone.'));
  2051. // Create an admin user and add Spanish language.
  2052. $admin_user = $this->drupalCreateUser(array('administer languages'));
  2053. $this->drupalLogin($admin_user);
  2054. $edit = array(
  2055. 'langcode' => self::LANGCODE,
  2056. 'name' => self::LANGCODE,
  2057. 'native' => self::LANGCODE,
  2058. 'direction' => LANGUAGE_LTR,
  2059. 'prefix' => self::LANGCODE,
  2060. );
  2061. $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
  2062. // Create a test user to carry out the tests.
  2063. $test_user = $this->drupalCreateUser();
  2064. $this->drupalLogin($test_user);
  2065. $edit = array('language' => self::LANGCODE, 'mail' => $test_user->mail, 'timezone' => 'America/Los_Angeles');
  2066. $this->drupalPost('user/' . $test_user->uid . '/edit', $edit, t('Save'));
  2067. // Disable session saving as we are about to modify the global $user.
  2068. drupal_save_session(FALSE);
  2069. // Save the original user and language and then replace it with the test user and language.
  2070. $real_user = $user;
  2071. $user = user_load($test_user->uid, TRUE);
  2072. $real_language = $language->language;
  2073. $language->language = $user->language;
  2074. // Simulate a Drupal bootstrap with the logged-in user.
  2075. date_default_timezone_set(drupal_get_user_timezone());
  2076. $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'America/Los_Angeles', 'en'), 'Sunday, 25-Mar-07 17:00:00 PDT', t('Test a different language.'));
  2077. $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'Europe/London'), 'Monday, 26-Mar-07 01:00:00 BST', t('Test a different time zone.'));
  2078. $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T'), 'domingo, 25-Mar-07 17:00:00 PDT', t('Test custom date format.'));
  2079. $this->assertIdentical(format_date($timestamp, 'long'), 'domingo, 25. marzo 2007 - 17:00', t('Test long date format.'));
  2080. $this->assertIdentical(format_date($timestamp, 'medium'), '25. marzo 2007 - 17:00', t('Test medium date format.'));
  2081. $this->assertIdentical(format_date($timestamp, 'short'), '2007 Mar 25 - 5:00pm', t('Test short date format.'));
  2082. $this->assertIdentical(format_date($timestamp), '25. marzo 2007 - 17:00', t('Test default date format.'));
  2083. // Restore the original user and language, and enable session saving.
  2084. $user = $real_user;
  2085. $language->language = $real_language;
  2086. // Restore default time zone.
  2087. date_default_timezone_set(drupal_get_user_timezone());
  2088. drupal_save_session(TRUE);
  2089. }
  2090. }
  2091. /**
  2092. * Tests for the format_date() function.
  2093. */
  2094. class DrupalAttributesUnitTest extends DrupalUnitTestCase {
  2095. public static function getInfo() {
  2096. return array(
  2097. 'name' => 'HTML Attributes',
  2098. 'description' => 'Perform unit tests on the drupal_attributes() function.',
  2099. 'group' => 'System',
  2100. );
  2101. }
  2102. /**
  2103. * Tests that drupal_html_class() cleans the class name properly.
  2104. */
  2105. function testDrupalAttributes() {
  2106. // Verify that special characters are HTML encoded.
  2107. $this->assertIdentical(drupal_attributes(array('title' => '&"\'<>')), ' title="&amp;&quot;&#039;&lt;&gt;"', t('HTML encode attribute values.'));
  2108. // Verify multi-value attributes are concatenated with spaces.
  2109. $attributes = array('class' => array('first', 'last'));
  2110. $this->assertIdentical(drupal_attributes(array('class' => array('first', 'last'))), ' class="first last"', t('Concatenate multi-value attributes.'));
  2111. // Verify empty attribute values are rendered.
  2112. $this->assertIdentical(drupal_attributes(array('alt' => '')), ' alt=""', t('Empty attribute value #1.'));
  2113. $this->assertIdentical(drupal_attributes(array('alt' => NULL)), ' alt=""', t('Empty attribute value #2.'));
  2114. // Verify multiple attributes are rendered.
  2115. $attributes = array(
  2116. 'id' => 'id-test',
  2117. 'class' => array('first', 'last'),
  2118. 'alt' => 'Alternate',
  2119. );
  2120. $this->assertIdentical(drupal_attributes($attributes), ' id="id-test" class="first last" alt="Alternate"', t('Multiple attributes.'));
  2121. // Verify empty attributes array is rendered.
  2122. $this->assertIdentical(drupal_attributes(array()), '', t('Empty attributes array.'));
  2123. }
  2124. }
  2125. /**
  2126. * Tests converting PHP variables to JSON strings and back.
  2127. */
  2128. class DrupalJSONTest extends DrupalUnitTestCase {
  2129. public static function getInfo() {
  2130. return array(
  2131. 'name' => 'JSON',
  2132. 'description' => 'Perform unit tests on the drupal_json_encode() and drupal_json_decode() functions.',
  2133. 'group' => 'System',
  2134. );
  2135. }
  2136. /**
  2137. * Tests converting PHP variables to JSON strings and back.
  2138. */
  2139. function testJSON() {
  2140. // Setup a string with the full ASCII table.
  2141. // @todo: Add tests for non-ASCII characters and Unicode.
  2142. $str = '';
  2143. for ($i=0; $i < 128; $i++) {
  2144. $str .= chr($i);
  2145. }
  2146. // Characters that must be escaped.
  2147. // We check for unescaped " separately.
  2148. $html_unsafe = array('<', '>', '\'', '&');
  2149. // The following are the encoded forms of: < > ' & "
  2150. $html_unsafe_escaped = array('\u003C', '\u003E', '\u0027', '\u0026', '\u0022');
  2151. // Verify there aren't character encoding problems with the source string.
  2152. $this->assertIdentical(strlen($str), 128, t('A string with the full ASCII table has the correct length.'));
  2153. foreach ($html_unsafe as $char) {
  2154. $this->assertTrue(strpos($str, $char) > 0, t('A string with the full ASCII table includes @s.', array('@s' => $char)));
  2155. }
  2156. // Verify that JSON encoding produces a string with all of the characters.
  2157. $json = drupal_json_encode($str);
  2158. $this->assertTrue(strlen($json) > strlen($str), t('A JSON encoded string is larger than the source string.'));
  2159. // The first and last characters should be ", and no others.
  2160. $this->assertTrue($json[0] == '"', t('A JSON encoded string begins with ".'));
  2161. $this->assertTrue($json[strlen($json) - 1] == '"', t('A JSON encoded string ends with ".'));
  2162. $this->assertTrue(substr_count($json, '"') == 2, t('A JSON encoded string contains exactly two ".'));
  2163. // Verify that encoding/decoding is reversible.
  2164. $json_decoded = drupal_json_decode($json);
  2165. $this->assertIdentical($str, $json_decoded, t('Encoding a string to JSON and decoding back results in the original string.'));
  2166. // Verify reversibility for structured data. Also verify that necessary
  2167. // characters are escaped.
  2168. $source = array(TRUE, FALSE, 0, 1, '0', '1', $str, array('key1' => $str, 'key2' => array('nested' => TRUE)));
  2169. $json = drupal_json_encode($source);
  2170. foreach ($html_unsafe as $char) {
  2171. $this->assertTrue(strpos($json, $char) === FALSE, t('A JSON encoded string does not contain @s.', array('@s' => $char)));
  2172. }
  2173. // Verify that JSON encoding escapes the HTML unsafe characters
  2174. foreach ($html_unsafe_escaped as $char) {
  2175. $this->assertTrue(strpos($json, $char) > 0, t('A JSON encoded string contains @s.', array('@s' => $char)));
  2176. }
  2177. $json_decoded = drupal_json_decode($json);
  2178. $this->assertNotIdentical($source, $json, t('An array encoded in JSON is not identical to the source.'));
  2179. $this->assertIdentical($source, $json_decoded, t('Encoding structured data to JSON and decoding back results in the original data.'));
  2180. }
  2181. }
  2182. /**
  2183. * Tests for RDF namespaces XML serialization.
  2184. */
  2185. class DrupalGetRdfNamespacesTestCase extends DrupalWebTestCase {
  2186. public static function getInfo() {
  2187. return array(
  2188. 'name' => 'RDF namespaces XML serialization tests',
  2189. 'description' => 'Confirm that the serialization of RDF namespaces via drupal_get_rdf_namespaces() is output and parsed correctly in the XHTML document.',
  2190. 'group' => 'System',
  2191. );
  2192. }
  2193. function setUp() {
  2194. parent::setUp('rdf', 'rdf_test');
  2195. }
  2196. /**
  2197. * Test RDF namespaces.
  2198. */
  2199. function testGetRdfNamespaces() {
  2200. // Fetches the front page and extracts XML namespaces.
  2201. $this->drupalGet('');
  2202. $xml = new SimpleXMLElement($this->content);
  2203. $ns = $xml->getDocNamespaces();
  2204. $this->assertEqual($ns['rdfs'], 'http://www.w3.org/2000/01/rdf-schema#', 'A prefix declared once is displayed.');
  2205. $this->assertEqual($ns['foaf'], 'http://xmlns.com/foaf/0.1/', 'The same prefix declared in several implementations of hook_rdf_namespaces() is valid as long as all the namespaces are the same.');
  2206. $this->assertEqual($ns['foaf1'], 'http://xmlns.com/foaf/0.1/', 'Two prefixes can be assigned the same namespace.');
  2207. $this->assertTrue(!isset($ns['dc']), 'A prefix with conflicting namespaces is discarded.');
  2208. }
  2209. }
  2210. /**
  2211. * Basic tests for drupal_add_feed().
  2212. */
  2213. class DrupalAddFeedTestCase extends DrupalWebTestCase {
  2214. public static function getInfo() {
  2215. return array(
  2216. 'name' => 'drupal_add_feed() tests',
  2217. 'description' => 'Make sure that drupal_add_feed() works correctly with various constructs.',
  2218. 'group' => 'System',
  2219. );
  2220. }
  2221. /**
  2222. * Test drupal_add_feed() with paths, URLs, and titles.
  2223. */
  2224. function testBasicFeedAddNoTitle() {
  2225. $path = $this->randomName(12);
  2226. $external_url = 'http://' . $this->randomName(12) . '/' . $this->randomName(12);
  2227. $fully_qualified_local_url = url($this->randomName(12), array('absolute' => TRUE));
  2228. $path_for_title = $this->randomName(12);
  2229. $external_for_title = 'http://' . $this->randomName(12) . '/' . $this->randomName(12);
  2230. $fully_qualified_for_title = url($this->randomName(12), array('absolute' => TRUE));
  2231. // Possible permutations of drupal_add_feed() to test.
  2232. // - 'input_url': the path passed to drupal_add_feed(),
  2233. // - 'output_url': the expected URL to be found in the header.
  2234. // - 'title' == the title of the feed as passed into drupal_add_feed().
  2235. $urls = array(
  2236. 'path without title' => array(
  2237. 'input_url' => $path,
  2238. 'output_url' => url($path, array('absolute' => TRUE)),
  2239. 'title' => '',
  2240. ),
  2241. 'external URL without title' => array(
  2242. 'input_url' => $external_url,
  2243. 'output_url' => $external_url,
  2244. 'title' => '',
  2245. ),
  2246. 'local URL without title' => array(
  2247. 'input_url' => $fully_qualified_local_url,
  2248. 'output_url' => $fully_qualified_local_url,
  2249. 'title' => '',
  2250. ),
  2251. 'path with title' => array(
  2252. 'input_url' => $path_for_title,
  2253. 'output_url' => url($path_for_title, array('absolute' => TRUE)),
  2254. 'title' => $this->randomName(12),
  2255. ),
  2256. 'external URL with title' => array(
  2257. 'input_url' => $external_for_title,
  2258. 'output_url' => $external_for_title,
  2259. 'title' => $this->randomName(12),
  2260. ),
  2261. 'local URL with title' => array(
  2262. 'input_url' => $fully_qualified_for_title,
  2263. 'output_url' => $fully_qualified_for_title,
  2264. 'title' => $this->randomName(12),
  2265. ),
  2266. );
  2267. foreach ($urls as $description => $feed_info) {
  2268. drupal_add_feed($feed_info['input_url'], $feed_info['title']);
  2269. }
  2270. $this->drupalSetContent(drupal_get_html_head());
  2271. foreach ($urls as $description => $feed_info) {
  2272. $this->assertPattern($this->urlToRSSLinkPattern($feed_info['output_url'], $feed_info['title']), t('Found correct feed header for %description', array('%description' => $description)));
  2273. }
  2274. }
  2275. /**
  2276. * Create a pattern representing the RSS feed in the page.
  2277. */
  2278. function urlToRSSLinkPattern($url, $title = '') {
  2279. // Escape any regular expression characters in the URL ('?' is the worst).
  2280. $url = preg_replace('/([+?.*])/', '[$0]', $url);
  2281. $generated_pattern = '%<link +rel="alternate" +type="application/rss.xml" +title="' . $title . '" +href="' . $url . '" */>%';
  2282. return $generated_pattern;
  2283. }
  2284. }
  2285. /**
  2286. * Test for theme_feed_icon().
  2287. */
  2288. class FeedIconTest extends DrupalWebTestCase {
  2289. public static function getInfo() {
  2290. return array(
  2291. 'name' => 'Feed icon',
  2292. 'description' => 'Check escaping of theme_feed_icon()',
  2293. 'group' => 'System',
  2294. );
  2295. }
  2296. /**
  2297. * Check that special characters are correctly escaped. Test for issue #1211668.
  2298. */
  2299. function testFeedIconEscaping() {
  2300. $variables = array();
  2301. $variables['url'] = 'node';
  2302. $variables['title'] = '<>&"\'';
  2303. $text = theme_feed_icon($variables);
  2304. preg_match('/title="(.*?)"/', $text, $matches);
  2305. $this->assertEqual($matches[1], 'Subscribe to &amp;&quot;&#039;', 'theme_feed_icon() escapes reserved HTML characters.');
  2306. }
  2307. }

Classes

Namesort descending Description
CascadingStylesheetsTestCase Test the Drupal CSS system.
CascadingStylesheetsUnitTest CSS Unit Tests.
CommonSizeTestCase Tests file size parsing and formatting functions.
CommonURLUnitTest Tests for URL generation functions.
CommonXssUnitTest Tests for check_plain(), filter_xss(), format_string(), and check_url().
DrupalAddFeedTestCase Basic tests for drupal_add_feed().
DrupalAlterTestCase Tests for URL generation functions.
DrupalAttributesUnitTest Tests for the format_date() function.
DrupalDataApiTest Tests for CRUD API functions.
DrupalErrorCollectionUnitTest Tests Simpletest error and exception collector.
DrupalGetRdfNamespacesTestCase Tests for RDF namespaces XML serialization.
DrupalGotoTest Testing drupal_goto and hook_drupal_goto_alter().
DrupalHTMLIdentifierTestCase Test for cleaning HTML identifiers.
DrupalHTTPRequestTestCase Test drupal_http_request().
DrupalJSONTest Tests converting PHP variables to JSON strings and back.
DrupalRenderTestCase Tests for drupal_render().
DrupalSetContentTestCase Testing drupal_add_region_content and drupal_get_region_content.
DrupalSystemListingTestCase Tests for the drupal_system_listing() function.
DrupalTagsHandlingTestCase Test drupal_explode_tags() and drupal_implode_tags().
FeedIconTest Test for theme_feed_icon().
FormatDateUnitTest Tests for the format_date() function.
JavaScriptTestCase Tests for the JavaScript system.
ParseInfoFilesTestCase Test the drupal_parse_info_file() API function.
ValidUrlTestCase Test for valid_url().