You are here

common.test in SimpleTest 7

Tests for common.inc functionality.

File

tests/common.test
View source
<?php

/**
 * @file
 * Tests for common.inc functionality.
 */

/**
 * Tests for URL generation functions.
 */
class DrupalAlterTestCase extends DrupalWebTestCase {
  public static function getInfo() {
    return array(
      'name' => 'drupal_alter() tests',
      'description' => 'Confirm that alteration of arguments passed to drupal_alter() works correctly.',
      'group' => 'System',
    );
  }
  function setUp() {
    parent::setUp('common_test');
  }
  function testDrupalAlter() {
    $array = array(
      'foo' => 'bar',
    );
    $object = new stdClass();
    $object->foo = 'bar';

    // Verify alteration of a single argument.
    $array_copy = $array;
    $array_expected = array(
      'foo' => 'Drupal theme',
    );
    drupal_alter('drupal_alter', $array_copy);
    $this
      ->assertEqual($array_copy, $array_expected, t('Single array was altered.'));
    $object_copy = clone $object;
    $object_expected = clone $object;
    $object_expected->foo = 'Drupal theme';
    drupal_alter('drupal_alter', $object_copy);
    $this
      ->assertEqual($object_copy, $object_expected, t('Single object was altered.'));

    // Verify alteration of multiple arguments.
    $array_copy = $array;
    $array_expected = array(
      'foo' => 'Drupal theme',
    );
    $object_copy = clone $object;
    $object_expected = clone $object;
    $object_expected->foo = 'Drupal theme';
    $array2_copy = $array;
    $array2_expected = array(
      'foo' => 'Drupal theme',
    );
    drupal_alter('drupal_alter', $array_copy, $object_copy, $array2_copy);
    $this
      ->assertEqual($array_copy, $array_expected, t('First argument to drupal_alter() was altered.'));
    $this
      ->assertEqual($object_copy, $object_expected, t('Second argument to drupal_alter() was altered.'));
    $this
      ->assertEqual($array2_copy, $array2_expected, t('Third argument to drupal_alter() was altered.'));
  }

}

/**
 * Tests for URL generation functions.
 */
class CommonURLUnitTest extends DrupalUnitTestCase {
  public static function getInfo() {
    return array(
      'name' => 'URL generation tests',
      'description' => 'Confirm that url(), drupal_get_query_parameters(), drupal_http_build_query(), and l() work correctly with various input.',
      'group' => 'System',
    );
  }

  /**
   * Confirm that invalid text given as $path is filtered.
   */
  function testLXSS() {
    $text = $this
      ->randomName();
    $path = "<SCRIPT>alert('XSS')</SCRIPT>";
    $link = l($text, $path);
    $sanitized_path = check_url(url($path));
    $this
      ->assertTrue(strpos($link, $sanitized_path) !== FALSE, t('XSS attack @path was filtered', array(
      '@path' => $path,
    )));
  }

  /**
   * Test drupal_get_query_parameters().
   */
  function testDrupalGetQueryParameters() {
    $original = array(
      'a' => 1,
      'b' => array(
        'd' => 4,
        'e' => array(
          'f' => 5,
        ),
      ),
      'c' => 3,
      'q' => 'foo/bar',
    );

    // Default arguments.
    $result = $_GET;
    unset($result['q']);
    $this
      ->assertEqual(drupal_get_query_parameters(), $result, t("\$_GET['q'] was removed."));

    // Default exclusion.
    $result = $original;
    unset($result['q']);
    $this
      ->assertEqual(drupal_get_query_parameters($original), $result, t("'q' was removed."));

    // First-level exclusion.
    $result = $original;
    unset($result['b']);
    $this
      ->assertEqual(drupal_get_query_parameters($original, array(
      'b',
    )), $result, t("'b' was removed."));

    // Second-level exclusion.
    $result = $original;
    unset($result['b']['d']);
    $this
      ->assertEqual(drupal_get_query_parameters($original, array(
      'b[d]',
    )), $result, t("'b[d]' was removed."));

    // Third-level exclusion.
    $result = $original;
    unset($result['b']['e']['f']);
    $this
      ->assertEqual(drupal_get_query_parameters($original, array(
      'b[e][f]',
    )), $result, t("'b[e][f]' was removed."));

    // Multiple exclusions.
    $result = $original;
    unset($result['a'], $result['b']['e'], $result['c']);
    $this
      ->assertEqual(drupal_get_query_parameters($original, array(
      'a',
      'b[e]',
      'c',
    )), $result, t("'a', 'b[e]', 'c' were removed."));
  }

  /**
   * Test drupal_http_build_query().
   */
  function testDrupalHttpBuildQuery() {
    $this
      ->assertEqual(drupal_http_build_query(array(
      'a' => ' &#//+%20@۞',
    )), 'a=%20%26%23//%2B%2520%40%DB%9E', t('Value was properly encoded.'));
    $this
      ->assertEqual(drupal_http_build_query(array(
      ' &#//+%20@۞' => 'a',
    )), '%20%26%23%2F%2F%2B%2520%40%DB%9E=a', t('Key was properly encoded.'));
    $this
      ->assertEqual(drupal_http_build_query(array(
      'a' => '1',
      'b' => '2',
      'c' => '3',
    )), 'a=1&b=2&c=3', t('Multiple values were properly concatenated.'));
    $this
      ->assertEqual(drupal_http_build_query(array(
      'a' => array(
        'b' => '2',
        'c' => '3',
      ),
      'd' => 'foo',
    )), 'a[b]=2&a[c]=3&d=foo', t('Nested array was properly encoded.'));
  }

  /**
   * Test drupal_parse_url().
   */
  function testDrupalParseUrl() {

    // Relative URL.
    $url = 'foo/bar?foo=bar&bar=baz&baz#foo';
    $result = array(
      'path' => 'foo/bar',
      'query' => array(
        'foo' => 'bar',
        'bar' => 'baz',
        'baz' => '',
      ),
      'fragment' => 'foo',
    );
    $this
      ->assertEqual(drupal_parse_url($url), $result, t('Relative URL parsed correctly.'));

    // Relative URL that is known to confuse parse_url().
    $url = 'foo/bar:1';
    $result = array(
      'path' => 'foo/bar:1',
      'query' => array(),
      'fragment' => '',
    );
    $this
      ->assertEqual(drupal_parse_url($url), $result, t('Relative URL parsed correctly.'));

    // Absolute URL.
    $url = '/foo/bar?foo=bar&bar=baz&baz#foo';
    $result = array(
      'path' => '/foo/bar',
      'query' => array(
        'foo' => 'bar',
        'bar' => 'baz',
        'baz' => '',
      ),
      'fragment' => 'foo',
    );
    $this
      ->assertEqual(drupal_parse_url($url), $result, t('Absolute URL parsed correctly.'));

    // External URL.
    $url = 'http://drupal.org/foo/bar?foo=bar&bar=baz&baz#foo';
    $result = array(
      'path' => 'http://drupal.org/foo/bar',
      'query' => array(
        'foo' => 'bar',
        'bar' => 'baz',
        'baz' => '',
      ),
      'fragment' => 'foo',
    );
    $this
      ->assertEqual(drupal_parse_url($url), $result, t('External URL parsed correctly.'));

    // Verify proper parsing of URLs when clean URLs are disabled.
    $result = array(
      'path' => 'foo/bar',
      'query' => array(
        'bar' => 'baz',
      ),
      'fragment' => 'foo',
    );

    // Non-clean URLs #1: Absolute URL generated by url().
    $url = $GLOBALS['base_url'] . '/?q=foo/bar&bar=baz#foo';
    $this
      ->assertEqual(drupal_parse_url($url), $result, t('Absolute URL with clean URLs disabled parsed correctly.'));

    // Non-clean URLs #2: Relative URL generated by url().
    $url = '?q=foo/bar&bar=baz#foo';
    $this
      ->assertEqual(drupal_parse_url($url), $result, t('Relative URL with clean URLs disabled parsed correctly.'));

    // Non-clean URLs #3: URL generated by url() on non-Apache webserver.
    $url = 'index.php?q=foo/bar&bar=baz#foo';
    $this
      ->assertEqual(drupal_parse_url($url), $result, t('Relative URL on non-Apache webserver with clean URLs disabled parsed correctly.'));
  }

  /**
   * Test url() with/without query, with/without fragment, absolute on/off and
   * assert all that works when clean URLs are on and off.
   */
  function testUrl() {
    global $base_url;
    foreach (array(
      FALSE,
      TRUE,
    ) as $absolute) {

      // Get the expected start of the path string.
      $base = $absolute ? $base_url . '/' : base_path();
      $absolute_string = $absolute ? 'absolute' : NULL;

      // Disable Clean URLs.
      $GLOBALS['conf']['clean_url'] = 0;
      $url = $base . '?q=node/123';
      $result = url('node/123', array(
        'absolute' => $absolute,
      ));
      $this
        ->assertEqual($url, $result, "{$url} == {$result}");
      $url = $base . '?q=node/123#foo';
      $result = url('node/123', array(
        'fragment' => 'foo',
        'absolute' => $absolute,
      ));
      $this
        ->assertEqual($url, $result, "{$url} == {$result}");
      $url = $base . '?q=node/123&foo';
      $result = url('node/123', array(
        'query' => array(
          'foo' => NULL,
        ),
        'absolute' => $absolute,
      ));
      $this
        ->assertEqual($url, $result, "{$url} == {$result}");
      $url = $base . '?q=node/123&foo=bar&bar=baz';
      $result = url('node/123', array(
        'query' => array(
          'foo' => 'bar',
          'bar' => 'baz',
        ),
        'absolute' => $absolute,
      ));
      $this
        ->assertEqual($url, $result, "{$url} == {$result}");
      $url = $base . '?q=node/123&foo#bar';
      $result = url('node/123', array(
        'query' => array(
          'foo' => NULL,
        ),
        'fragment' => 'bar',
        'absolute' => $absolute,
      ));
      $this
        ->assertEqual($url, $result, "{$url} == {$result}");
      $url = $base;
      $result = url('<front>', array(
        'absolute' => $absolute,
      ));
      $this
        ->assertEqual($url, $result, "{$url} == {$result}");

      // Enable Clean URLs.
      $GLOBALS['conf']['clean_url'] = 1;
      $url = $base . 'node/123';
      $result = url('node/123', array(
        'absolute' => $absolute,
      ));
      $this
        ->assertEqual($url, $result, "{$url} == {$result}");
      $url = $base . 'node/123#foo';
      $result = url('node/123', array(
        'fragment' => 'foo',
        'absolute' => $absolute,
      ));
      $this
        ->assertEqual($url, $result, "{$url} == {$result}");
      $url = $base . 'node/123?foo';
      $result = url('node/123', array(
        'query' => array(
          'foo' => NULL,
        ),
        'absolute' => $absolute,
      ));
      $this
        ->assertEqual($url, $result, "{$url} == {$result}");
      $url = $base . 'node/123?foo=bar&bar=baz';
      $result = url('node/123', array(
        'query' => array(
          'foo' => 'bar',
          'bar' => 'baz',
        ),
        'absolute' => $absolute,
      ));
      $this
        ->assertEqual($url, $result, "{$url} == {$result}");
      $url = $base . 'node/123?foo#bar';
      $result = url('node/123', array(
        'query' => array(
          'foo' => NULL,
        ),
        'fragment' => 'bar',
        'absolute' => $absolute,
      ));
      $this
        ->assertEqual($url, $result, "{$url} == {$result}");
      $url = $base;
      $result = url('<front>', array(
        'absolute' => $absolute,
      ));
      $this
        ->assertEqual($url, $result, "{$url} == {$result}");
    }
  }

  /**
   * Test external URL handling.
   */
  function testExternalUrls() {
    $test_url = 'http://drupal.org/';

    // Verify external URL can contain a fragment.
    $url = $test_url . '#drupal';
    $result = url($url);
    $this
      ->assertEqual($url, $result, t('External URL with fragment works without a fragment in $options.'));

    // Verify fragment can be overidden in an external URL.
    $url = $test_url . '#drupal';
    $fragment = $this
      ->randomName(10);
    $result = url($url, array(
      'fragment' => $fragment,
    ));
    $this
      ->assertEqual($test_url . '#' . $fragment, $result, t('External URL fragment is overidden with a custom fragment in $options.'));

    // Verify external URL can contain a query string.
    $url = $test_url . '?drupal=awesome';
    $result = url($url);
    $this
      ->assertEqual($url, $result, t('External URL with query string works without a query string in $options.'));

    // Verify external URL can be extended with a query string.
    $url = $test_url;
    $query = array(
      $this
        ->randomName(5) => $this
        ->randomName(5),
    );
    $result = url($url, array(
      'query' => $query,
    ));
    $this
      ->assertEqual($url . '?' . http_build_query($query, '', '&'), $result, t('External URL can be extended with a query string in $options.'));

    // Verify query string can be extended in an external URL.
    $url = $test_url . '?drupal=awesome';
    $query = array(
      $this
        ->randomName(5) => $this
        ->randomName(5),
    );
    $result = url($url, array(
      'query' => $query,
    ));
    $this
      ->assertEqual($url . '&' . http_build_query($query, '', '&'), $result, t('External URL query string can be extended with a custom query string in $options.'));
  }

}
class CommonSizeTestCase extends DrupalUnitTestCase {
  protected $exact_test_cases;
  protected $rounded_test_cases;
  public static function getInfo() {
    return array(
      'name' => 'Size parsing test',
      'description' => 'Parse a predefined amount of bytes and compare the output with the expected value.',
      'group' => 'System',
    );
  }
  function setUp() {
    $kb = DRUPAL_KILOBYTE;
    $this->exact_test_cases = array(
      '1 byte' => 1,
      '1 KB' => $kb,
      '1 MB' => $kb * $kb,
      '1 GB' => $kb * $kb * $kb,
      '1 TB' => $kb * $kb * $kb * $kb,
      '1 PB' => $kb * $kb * $kb * $kb * $kb,
      '1 EB' => $kb * $kb * $kb * $kb * $kb * $kb,
      '1 ZB' => $kb * $kb * $kb * $kb * $kb * $kb * $kb,
      '1 YB' => $kb * $kb * $kb * $kb * $kb * $kb * $kb * $kb,
    );
    $this->rounded_test_cases = array(
      '2 bytes' => 2,
      '1 MB' => $kb * $kb - 1,
      // rounded to 1 MB (not 1000 or 1024 kilobyte!)
      round(3623651 / $this->exact_test_cases['1 MB'], 2) . ' MB' => 3623651,
      // megabytes
      round(67234178751368124 / $this->exact_test_cases['1 PB'], 2) . ' PB' => 67234178751368124,
      // petabytes
      round(2.3534682382112583E+26 / $this->exact_test_cases['1 YB'], 2) . ' YB' => 2.3534682382112583E+26,
    );
    parent::setUp();
  }

  /**
   * Check that format_size() returns the expected string.
   */
  function testCommonFormatSize() {
    foreach (array(
      $this->exact_test_cases,
      $this->rounded_test_cases,
    ) as $test_cases) {
      foreach ($test_cases as $expected => $input) {
        $this
          ->assertEqual($result = format_size($input, NULL), $expected, $expected . ' == ' . $result . ' (' . $input . ' bytes)');
      }
    }
  }

  /**
   * Check that parse_size() returns the proper byte sizes.
   */
  function testCommonParseSize() {
    foreach ($this->exact_test_cases as $string => $size) {
      $this
        ->assertEqual($parsed_size = parse_size($string), $size, $size . ' == ' . $parsed_size . ' (' . $string . ')');
    }

    // Some custom parsing tests
    $string = '23476892 bytes';
    $this
      ->assertEqual($parsed_size = parse_size($string), $size = 23476892, $string . ' == ' . $parsed_size . ' bytes');
    $string = '76MRandomStringThatShouldBeIgnoredByParseSize.';

    // 76 MB
    $this
      ->assertEqual($parsed_size = parse_size($string), $size = 79691776, $string . ' == ' . $parsed_size . ' bytes');
    $string = '76.24 Giggabyte';

    // Misspeld text -> 76.24 GB
    $this
      ->assertEqual($parsed_size = parse_size($string), $size = 81862076662, $string . ' == ' . $parsed_size . ' bytes');
  }

  /**
   * Cross-test parse_size() and format_size().
   */
  function testCommonParseSizeFormatSize() {
    foreach ($this->exact_test_cases as $size) {
      $this
        ->assertEqual($size, $parsed_size = parse_size($string = format_size($size, NULL)), $size . ' == ' . $parsed_size . ' (' . $string . ')');
    }
  }

}

/**
 * Test drupal_explode_tags() and drupal_implode_tags().
 */
class DrupalTagsHandlingTestCase extends DrupalWebTestCase {
  var $validTags = array(
    'Drupal' => 'Drupal',
    'Drupal with some spaces' => 'Drupal with some spaces',
    '"Legendary Drupal mascot of doom: ""Druplicon"""' => 'Legendary Drupal mascot of doom: "Druplicon"',
    '"Drupal, although it rhymes with sloopal, is as awesome as a troopal!"' => 'Drupal, although it rhymes with sloopal, is as awesome as a troopal!',
  );
  public static function getInfo() {
    return array(
      'name' => 'Drupal tags handling',
      'description' => "Performs tests on Drupal's handling of tags, both explosion and implosion tactics used.",
      'group' => 'System',
    );
  }

  /**
   * Explode a series of tags.
   */
  function testDrupalExplodeTags() {
    $string = implode(', ', array_keys($this->validTags));
    $tags = drupal_explode_tags($string);
    $this
      ->assertTags($tags);
  }

  /**
   * Implode a series of tags.
   */
  function testDrupalImplodeTags() {
    $tags = array_values($this->validTags);

    // Let's explode and implode to our heart's content.
    for ($i = 0; $i < 10; $i++) {
      $string = drupal_implode_tags($tags);
      $tags = drupal_explode_tags($string);
    }
    $this
      ->assertTags($tags);
  }

  /**
   * Helper function: asserts that the ending array of tags is what we wanted.
   */
  function assertTags($tags) {
    $original = $this->validTags;
    foreach ($tags as $tag) {
      $key = array_search($tag, $original);
      $this
        ->assertTrue($key, t('Make sure tag %tag shows up in the final tags array (originally %original)', array(
        '%tag' => $tag,
        '%original' => $key,
      )));
      unset($original[$key]);
    }
    foreach ($original as $leftover) {
      $this
        ->fail(t('Leftover tag %leftover was left over.', array(
        '%leftover' => $leftover,
      )));
    }
  }

}

/**
 * Test the Drupal CSS system.
 */
class CascadingStylesheetsTestCase extends DrupalWebTestCase {
  public static function getInfo() {
    return array(
      'name' => 'Cascading stylesheets',
      'description' => 'Tests adding various cascading stylesheets to the page.',
      'group' => 'System',
    );
  }
  function setUp() {
    parent::setUp('php', 'locale');

    // Reset drupal_add_css() before each test.
    drupal_static_reset('drupal_add_css');
  }

  /**
   * Check default stylesheets as empty.
   */
  function testDefault() {
    $this
      ->assertEqual(array(), drupal_add_css(), t('Default CSS is empty.'));
  }

  /**
   * Tests adding a file stylesheet.
   */
  function testAddFile() {
    $path = drupal_get_path('module', 'simpletest') . '/simpletest.css';
    $css = drupal_add_css($path);
    $this
      ->assertEqual($css[$path]['data'], $path, t('Adding a CSS file caches it properly.'));
  }

  /**
   * Tests adding an external stylesheet.
   */
  function testAddExternal() {
    $path = 'http://example.com/style.css';
    $css = drupal_add_css($path, 'external');
    $this
      ->assertEqual($css[$path]['type'], 'external', t('Adding an external CSS file caches it properly.'));
  }

  /**
   * Makes sure that reseting the CSS empties the cache.
   */
  function testReset() {
    drupal_static_reset('drupal_add_css');
    $this
      ->assertEqual(array(), drupal_add_css(), t('Resetting the CSS empties the cache.'));
  }

  /**
   * Tests rendering the stylesheets.
   */
  function testRenderFile() {
    $css = drupal_get_path('module', 'simpletest') . '/simpletest.css';
    drupal_add_css($css);
    $styles = drupal_get_css();
    $this
      ->assertTrue(strpos($styles, $css) > 0, t('Rendered CSS includes the added stylesheet.'));
  }

  /**
   * Tests rendering an external stylesheet.
   */
  function testRenderExternal() {
    $css = 'http://example.com/style.css';
    drupal_add_css($css, 'external');
    $styles = drupal_get_css();
    $this
      ->assertTrue(strpos($styles, 'href="' . $css) > 0, t('Rendering an external CSS file.'));
  }

  /**
   * Tests rendering inline stylesheets with preprocessing on.
   */
  function testRenderInlinePreprocess() {
    $css = 'body { padding: 0px; }';
    $css_preprocessed = '<style type="text/css">' . drupal_load_stylesheet_content($css, TRUE) . '</style>';
    drupal_add_css($css, 'inline');
    $styles = drupal_get_css();
    $this
      ->assertEqual($styles, "\n" . $css_preprocessed, t('Rendering preprocessed inline CSS adds it to the page.'));
  }

  /**
   * Tests rendering inline stylesheets with preprocessing off.
   */
  function testRenderInlineNoPreprocess() {
    $css = 'body { padding: 0px; }';
    drupal_add_css($css, array(
      'type' => 'inline',
      'preprocess' => FALSE,
    ));
    $styles = drupal_get_css();
    $this
      ->assertTrue(strpos($styles, $css) > 0, t('Rendering non-preprocessed inline CSS adds it to the page.'));
  }

  /**
   * Tests rendering inline stylesheets through a full page request.
   */
  function testRenderInlineFullPage() {
    $css = 'body { font-size: 254px; }';
    $expected = 'font-size:254px;';

    // Create a node, using the PHP filter that tests drupal_add_css().
    $settings = array(
      'type' => 'page',
      'body' => array(
        FIELD_LANGUAGE_NONE => array(
          array(
            'value' => t('This tests the inline CSS!') . "<?php drupal_add_css('{$css}', 'inline'); ?>",
            // The "PHP code" format is always the most recent one added, since
            // the PHP module was enabled in the setUp() method of the current
            // test.
            'format' => db_query("SELECT MAX(format) FROM {filter_format}")
              ->fetchField(),
          ),
        ),
      ),
      'promote' => 1,
    );
    $node = $this
      ->drupalCreateNode($settings);

    // Fetch the page.
    $this
      ->drupalGet('node/' . $node->nid);
    $this
      ->assertRaw($expected, t('Inline stylesheets appear in the full page rendering.'));
  }

  /**
   * Test CSS ordering.
   */
  function testRenderOrder() {

    // A module CSS file.
    drupal_add_css(drupal_get_path('module', 'simpletest') . '/simpletest.css');

    // A few system CSS files, ordered in a strange way.
    $system_path = drupal_get_path('module', 'system');
    drupal_add_css($system_path . '/defaults.css', array(
      'weight' => CSS_SYSTEM,
    ));
    drupal_add_css($system_path . '/system.css', array(
      'weight' => CSS_SYSTEM - 10,
    ));
    drupal_add_css($system_path . '/system-menus.css', array(
      'weight' => CSS_SYSTEM,
    ));
    $expected = array(
      $system_path . '/system.css',
      $system_path . '/defaults.css',
      $system_path . '/system-menus.css',
      drupal_get_path('module', 'simpletest') . '/simpletest.css',
    );
    $styles = drupal_get_css();
    if (preg_match_all('/href="' . preg_quote($GLOBALS['base_url'] . '/', '/') . '([^?]+)\\?/', $styles, $matches)) {
      $result = $matches[1];
    }
    else {
      $result = array();
    }
    $this
      ->assertIdentical($result, $expected, t('The CSS files are in the expected order.'));
  }

  /**
   * Test CSS override.
   */
  function testRenderOverride() {
    drupal_add_css(drupal_get_path('module', 'system') . '/system.css');
    drupal_add_css(drupal_get_path('module', 'simpletest') . '/tests/system.css');

    // The dummy stylesheet should be the only one included.
    $styles = drupal_get_css();
    $this
      ->assert(strpos($styles, drupal_get_path('module', 'simpletest') . '/tests/system.css') !== FALSE, t('The overriding CSS file is output.'));
    $this
      ->assert(strpos($styles, drupal_get_path('module', 'system') . '/system.css') === FALSE, t('The overriden CSS file is not output.'));
    drupal_add_css(drupal_get_path('module', 'simpletest') . '/tests/system.css');
    drupal_add_css(drupal_get_path('module', 'system') . '/system.css');

    // The standard stylesheet should be the only one included.
    $styles = drupal_get_css();
    $this
      ->assert(strpos($styles, drupal_get_path('module', 'system') . '/system.css') !== FALSE, t('The overriding CSS file is output.'));
    $this
      ->assert(strpos($styles, drupal_get_path('module', 'simpletest') . '/tests/system.css') === FALSE, t('The overriden CSS file is not output.'));
  }

  /**
   * Tests Locale module's CSS Alter to include RTL overrides.
   */
  function testAlter() {

    // Switch the language to a right to left language and add system.css.
    global $language;
    $language->direction = LANGUAGE_RTL;
    drupal_add_css(drupal_get_path('module', 'system') . '/system.css');

    // Check to see if system-rtl.css was also added.
    $styles = drupal_get_css();
    $this
      ->assert(strpos($styles, drupal_get_path('module', 'system') . '/system-rtl.css') !== FALSE, t('CSS is alterable as right to left overrides are added.'));

    // Change the language back to left to right.
    $language->direction = LANGUAGE_LTR;
  }

}

/**
 * Test for cleaning HTML identifiers.
 */
class DrupalHTMLIdentifierTestCase extends DrupalUnitTestCase {
  public static function getInfo() {
    return array(
      'name' => 'HTML identifiers',
      'description' => 'Test the functions drupal_html_class(), drupal_html_id() and drupal_clean_css_identifier() for expected behavior',
      'group' => 'System',
    );
  }

  /**
   * Tests that drupal_clean_css_identifier() cleans the identifier properly.
   */
  function testDrupalCleanCSSIdentifier() {

    // Verify that no valid ASCII characters are stripped from the identifier.
    $identifier = 'abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789';
    $this
      ->assertIdentical(drupal_clean_css_identifier($identifier, array()), $identifier, t('Verify valid ASCII characters pass through.'));

    // Verify that valid UTF-8 characters are not stripped from the identifier.
    $identifier = '¡¢£¤¥';
    $this
      ->assertIdentical(drupal_clean_css_identifier($identifier, array()), $identifier, t('Verify valid UTF-8 characters pass through.'));

    // Verify that invalid characters (including non-breaking space) are stripped from the identifier.
    $this
      ->assertIdentical(drupal_clean_css_identifier('invalid !"#$%&\'()*+,./:;<=>?@[\\]^`{|}~ identifier', array()), 'invalididentifier', t('Strip invalid characters.'));
  }

  /**
   * Tests that drupal_html_class() cleans the class name properly.
   */
  function testDrupalHTMLClass() {

    // Verify Drupal coding standards are enforced.
    $this
      ->assertIdentical(drupal_html_class('CLASS NAME_[Ü]'), 'class-name--ü', t('Enforce Drupal coding standards.'));
  }

  /**
   * Tests that drupal_html_id() cleans the ID properly.
   */
  function testDrupalHTMLId() {

    // Verify that letters, digits, and hyphens are not stripped from the ID.
    $id = 'abcdefghijklmnopqrstuvwxyz-0123456789';
    $this
      ->assertIdentical(drupal_html_id($id), $id, t('Verify valid characters pass through.'));

    // Verify that invalid characters are stripped from the ID.
    $this
      ->assertIdentical(drupal_html_id('invalid,./:@\\^`{Üidentifier'), 'invalididentifier', t('Strip invalid characters.'));

    // Verify Drupal coding standards are enforced.
    $this
      ->assertIdentical(drupal_html_id('ID NAME_[1]'), 'id-name--1', t('Enforce Drupal coding standards.'));

    // Reset the static cache so we can ensure the unique id count is at zero.
    drupal_static_reset('drupal_html_id');

    // Clean up IDs with invalid starting characters.
    $this
      ->assertIdentical(drupal_html_id('test-unique-id'), 'test-unique-id', t('Test the uniqueness of IDs #1.'));
    $this
      ->assertIdentical(drupal_html_id('test-unique-id'), 'test-unique-id-2', t('Test the uniqueness of IDs #2.'));
    $this
      ->assertIdentical(drupal_html_id('test-unique-id'), 'test-unique-id-3', t('Test the uniqueness of IDs #3.'));
  }

}

/**
 * CSS Unit Tests.
 */
class CascadingStylesheetsUnitTest extends DrupalUnitTestCase {
  public static function getInfo() {
    return array(
      'name' => 'CSS Unit Tests',
      'description' => 'Unit tests on CSS functions like aggregation.',
      'group' => 'System',
    );
  }

  /**
   * Tests basic CSS loading with and without optimization via drupal_load_stylesheet().
   *
   * This can be enhanced by adding additional CSS files with variant test cases.
   * Currently, this is specifically testing to make sure that whitespace
   * is treated with adequate respect (not arbitrarily removing linefeeds).
   */
  function testLoadCssBasic() {

    // Array of files to test living in 'simpletest/files/css_test_files/'.
    // - Original: name.css
    // - Unoptimized expected content: name.css.unoptimized.css
    // - Optimized expected content: name.css.optimized.css
    $testfiles = array(
      'css_input_without_import.css',
    );
    $path = drupal_get_path('module', 'simpletest') . '/files/css_test_files';
    foreach ($testfiles as $file) {
      $expected = file_get_contents("{$path}/{$file}.unoptimized.css");
      $unoptimized_output = drupal_load_stylesheet("{$path}/{$file}.unoptimized.css", FALSE);
      $this
        ->assertEqual($unoptimized_output, $expected, t('Unoptimized CSS file has expected contents (@file)', array(
        '@file' => $file,
      )));
      $expected = file_get_contents("{$path}/{$file}.optimized.css");
      $optimized_output = drupal_load_stylesheet("{$path}/{$file}", TRUE);
      $this
        ->assertEqual($optimized_output, $expected, t('Optimized CSS file has expected contents (@file)', array(
        '@file' => $file,
      )));
    }
  }

}

/**
 * Test drupal_http_request().
 */
class DrupalHTTPRequestTestCase extends DrupalWebTestCase {
  public static function getInfo() {
    return array(
      'name' => 'Drupal HTTP request',
      'description' => "Performs tests on Drupal's HTTP request mechanism.",
      'group' => 'System',
    );
  }
  function setUp() {
    parent::setUp('system_test');
  }
  function testDrupalHTTPRequest() {

    // Parse URL schema.
    $missing_scheme = drupal_http_request('example.com/path');
    $this
      ->assertEqual($missing_scheme->code, -1002, t('Returned with "-1002" error code.'));
    $this
      ->assertEqual($missing_scheme->error, 'missing schema', t('Returned with "missing schema" error message.'));
    $unable_to_parse = drupal_http_request('http:///path');
    $this
      ->assertEqual($unable_to_parse->code, -1001, t('Returned with "-1001" error code.'));
    $this
      ->assertEqual($unable_to_parse->error, 'unable to parse URL', t('Returned with "unable to parse URL" error message.'));

    // Fetch page.
    $result = drupal_http_request(url('node', array(
      'absolute' => TRUE,
    )));
    $this
      ->assertEqual($result->code, 200, t('Fetched page successfully.'));
    $this
      ->drupalSetContent($result->data);
    $this
      ->assertTitle(t('Welcome to @site-name | @site-name', array(
      '@site-name' => variable_get('site_name', 'Drupal'),
    )), t('Site title matches.'));

    // Test that code and status message is returned.
    $result = drupal_http_request(url('pagedoesnotexist', array(
      'absolute' => TRUE,
    )));
    $this
      ->assertTrue(!empty($result->protocol), t('Result protocol is returned.'));
    $this
      ->assertEqual($result->code, '404', t('Result code is 404'));
    $this
      ->assertEqual($result->status_message, 'Not Found', t('Result status message is "Not Found"'));

    // Test that timeout is respected. The test machine is expected to be able
    // to make the connection (i.e. complete the fsockopen()) in 2 seconds and
    // return within a total of 5 seconds. If the test machine is extremely
    // slow, the test will fail. fsockopen() has been seen to time out in
    // slightly less than the specified timeout, so allow a little slack on the
    // minimum expected time (i.e. 1.8 instead of 2).
    timer_start(__METHOD__);
    $result = drupal_http_request(url('system-test/sleep/10', array(
      'absolute' => TRUE,
    )), array(
      'timeout' => 2,
    ));
    $time = timer_read(__METHOD__) / 1000;
    $this
      ->assertTrue(1.8 < $time && $time < 5, t('Request timed out (%time seconds).', array(
      '%time' => $time,
    )));
    $this
      ->assertTrue($result->error, t('An error message was returned.'));
    $this
      ->assertEqual($result->code, HTTP_REQUEST_TIMEOUT, t('Proper error code was returned.'));
  }
  function testDrupalHTTPRequestBasicAuth() {
    $username = $this
      ->randomName();
    $password = $this
      ->randomName();
    $url = url('system-test/auth', array(
      'absolute' => TRUE,
    ));
    $auth = str_replace('http://', 'http://' . $username . ':' . $password . '@', $url);
    $result = drupal_http_request($auth);
    $this
      ->drupalSetContent($result->data);
    $this
      ->assertRaw($username, t('$_SERVER["PHP_AUTH_USER"] is passed correctly.'));
    $this
      ->assertRaw($password, t('$_SERVER["PHP_AUTH_PW"] is passed correctly.'));
  }
  function testDrupalHTTPRequestRedirect() {
    $redirect_301 = drupal_http_request(url('system-test/redirect/301', array(
      'absolute' => TRUE,
    )), array(
      'max_redirects' => 1,
    ));
    $this
      ->assertEqual($redirect_301->redirect_code, 301, t('drupal_http_request follows the 301 redirect.'));
    $redirect_301 = drupal_http_request(url('system-test/redirect/301', array(
      'absolute' => TRUE,
    )), array(
      'max_redirects' => 0,
    ));
    $this
      ->assertFalse(isset($redirect_301->redirect_code), t('drupal_http_request does not follow 301 redirect if max_redirects = 0.'));
    $redirect_invalid = drupal_http_request(url('system-test/redirect-noscheme', array(
      'absolute' => TRUE,
    )), array(
      'max_redirects' => 1,
    ));
    $this
      ->assertEqual($redirect_invalid->code, -1002, t('301 redirect to invalid URL returned with error code !error.', array(
      '!error' => $redirect_invalid->error,
    )));
    $this
      ->assertEqual($redirect_invalid->error, 'missing schema', t('301 redirect to invalid URL returned with error message "!error".', array(
      '!error' => $redirect_invalid->error,
    )));
    $redirect_invalid = drupal_http_request(url('system-test/redirect-noparse', array(
      'absolute' => TRUE,
    )), array(
      'max_redirects' => 1,
    ));
    $this
      ->assertEqual($redirect_invalid->code, -1001, t('301 redirect to invalid URL returned with error message code "!error".', array(
      '!error' => $redirect_invalid->error,
    )));
    $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,
    )));
    $redirect_invalid = drupal_http_request(url('system-test/redirect-invalid-scheme', array(
      'absolute' => TRUE,
    )), array(
      'max_redirects' => 1,
    ));
    $this
      ->assertEqual($redirect_invalid->code, -1003, t('301 redirect to invalid URL returned with error code !error.', array(
      '!error' => $redirect_invalid->error,
    )));
    $this
      ->assertEqual($redirect_invalid->error, 'invalid schema ftp', t('301 redirect to invalid URL returned with error message "!error".', array(
      '!error' => $redirect_invalid->error,
    )));
    $redirect_302 = drupal_http_request(url('system-test/redirect/302', array(
      'absolute' => TRUE,
    )), array(
      'max_redirects' => 1,
    ));
    $this
      ->assertEqual($redirect_302->redirect_code, 302, t('drupal_http_request follows the 302 redirect.'));
    $redirect_302 = drupal_http_request(url('system-test/redirect/302', array(
      'absolute' => TRUE,
    )), array(
      'max_redirects' => 0,
    ));
    $this
      ->assertFalse(isset($redirect_302->redirect_code), t('drupal_http_request does not follow 302 redirect if $retry = 0.'));
    $redirect_307 = drupal_http_request(url('system-test/redirect/307', array(
      'absolute' => TRUE,
    )), array(
      'max_redirects' => 1,
    ));
    $this
      ->assertEqual($redirect_307->redirect_code, 307, t('drupal_http_request follows the 307 redirect.'));
    $redirect_307 = drupal_http_request(url('system-test/redirect/307', array(
      'absolute' => TRUE,
    )), array(
      'max_redirects' => 0,
    ));
    $this
      ->assertFalse(isset($redirect_307->redirect_code), t('drupal_http_request does not follow 307 redirect if max_redirects = 0.'));
  }
  function testDrupalGetDestination() {
    $query = $this
      ->randomName(10);

    // Verify that a 'destination' query string is used as destination.
    $this
      ->drupalGet('system-test/destination', array(
      'query' => array(
        'destination' => $query,
      ),
    ));
    $this
      ->assertText('The destination: ' . $query, t('The given query string destination is determined as destination.'));

    // Verify that the current path is used as destination.
    $this
      ->drupalGet('system-test/destination', array(
      'query' => array(
        $query => NULL,
      ),
    ));
    $url = 'system-test/destination?' . $query;
    $this
      ->assertText('The destination: ' . $url, t('The current path is determined as destination.'));
  }

}

/**
 * Testing drupal_add_region_content and drupal_get_region_content.
 */
class DrupalSetContentTestCase extends DrupalWebTestCase {
  public static function getInfo() {
    return array(
      'name' => 'Drupal set/get regions',
      'description' => 'Performs tests on setting and retrieiving content from theme regions.',
      'group' => 'System',
    );
  }

  /**
   * Test setting and retrieving content for theme regions.
   */
  function testRegions() {
    global $theme_key;
    $block_regions = array_keys(system_region_list($theme_key));
    $delimiter = $this
      ->randomName(32);
    $values = array();

    // Set some random content for each region available.
    foreach ($block_regions as $region) {
      $first_chunk = $this
        ->randomName(32);
      drupal_add_region_content($region, $first_chunk);
      $second_chunk = $this
        ->randomName(32);
      drupal_add_region_content($region, $second_chunk);

      // Store the expected result for a drupal_get_region_content call for this region.
      $values[$region] = $first_chunk . $delimiter . $second_chunk;
    }

    // Ensure drupal_get_region_content returns expected results when fetching all regions.
    $content = drupal_get_region_content(NULL, $delimiter);
    foreach ($content as $region => $region_content) {
      $this
        ->assertEqual($region_content, $values[$region], t('@region region text verified when fetching all regions', array(
        '@region' => $region,
      )));
    }

    // Ensure drupal_get_region_content returns expected results when fetching a single region.
    foreach ($block_regions as $region) {
      $region_content = drupal_get_region_content($region, $delimiter);
      $this
        ->assertEqual($region_content, $values[$region], t('@region region text verified when fetching single region.', array(
        '@region' => $region,
      )));
    }
  }

}

/**
 * Testing drupal_goto and hook_drupal_goto_alter().
 */
class DrupalGotoTest extends DrupalWebTestCase {
  public static function getInfo() {
    return array(
      'name' => 'Drupal goto',
      'description' => 'Performs tests on the drupal_goto function and hook_drupal_goto_alter',
      'group' => 'System',
    );
  }
  function setUp() {
    parent::setUp('common_test');
  }

  /**
   * Test setting and retrieving content for theme regions.
   */
  function testDrupalGoto() {
    $this
      ->drupalGet('common-test/drupal_goto/redirect');
    $this
      ->assertNoText(t("Drupal goto failed to stop program"), t("Drupal goto stopped program."));
    $this
      ->assertText('drupal_goto', t("Drupal goto redirect failed."));
  }

  /**
   * Test setting and retrieving content for theme regions.
   */
  function testDrupalGotoAlter() {
    $this
      ->drupalGet('common-test/drupal_goto/redirect_fail');
    $this
      ->assertNoText(t("Drupal goto failed to stop program"), t("Drupal goto stopped program."));
    $this
      ->assertNoText('drupal_goto_fail', t("Drupal goto redirect failed."));
  }

}

/**
 * Tests for the JavaScript system.
 */
class JavaScriptTestCase extends DrupalWebTestCase {

  /**
   * Store configured value for JavaScript preprocessing.
   */
  protected $preprocess_js = NULL;
  public static function getInfo() {
    return array(
      'name' => 'JavaScript',
      'description' => 'Tests the JavaScript system.',
      'group' => 'System',
    );
  }
  function setUp() {

    // Enable Locale and SimpleTest in the test environment.
    parent::setUp('locale', 'simpletest', 'common_test');

    // Disable preprocessing
    $this->preprocess_js = variable_get('preprocess_js', 0);
    variable_set('preprocess_js', 0);

    // Reset drupal_add_js() and drupal_add_library() statics before each test.
    drupal_static_reset('drupal_add_js');
    drupal_static_reset('drupal_add_library');
  }
  function tearDown() {

    // Restore configured value for JavaScript preprocessing.
    variable_set('preprocess_js', $this->preprocess_js);
    parent::tearDown();
  }

  /**
   * Test default JavaScript is empty.
   */
  function testDefault() {
    $this
      ->assertEqual(array(), drupal_add_js(), t('Default JavaScript is empty.'));
  }

  /**
   * Test adding a JavaScript file.
   */
  function testAddFile() {
    $javascript = drupal_add_js('misc/collapse.js');
    $this
      ->assertTrue(array_key_exists('misc/jquery.js', $javascript), t('jQuery is added when a file is added.'));
    $this
      ->assertTrue(array_key_exists('misc/drupal.js', $javascript), t('Drupal.js is added when file is added.'));
    $this
      ->assertTrue(array_key_exists('misc/collapse.js', $javascript), t('JavaScript files are correctly added.'));
    $this
      ->assertEqual(base_path(), $javascript['settings']['data'][0]['basePath'], t('Base path JavaScript setting is correctly set.'));
  }

  /**
   * Test adding settings.
   */
  function testAddSetting() {
    $javascript = drupal_add_js(array(
      'drupal' => 'rocks',
      'dries' => 280342800,
    ), 'setting');
    $this
      ->assertEqual(280342800, $javascript['settings']['data'][1]['dries'], t('JavaScript setting is set correctly.'));
    $this
      ->assertEqual('rocks', $javascript['settings']['data'][1]['drupal'], t('The other JavaScript setting is set correctly.'));
  }

  /**
   * Tests adding an external JavaScript File.
   */
  function testAddExternal() {
    $path = 'http://example.com/script.js';
    $javascript = drupal_add_js($path, 'external');
    $this
      ->assertTrue(array_key_exists('http://example.com/script.js', $javascript), t('Added an external JavaScript file.'));
  }

  /**
   * Test drupal_get_js() for JavaScript settings.
   */
  function testHeaderSetting() {
    drupal_add_js(array(
      'testSetting' => 'testValue',
    ), 'setting');
    $javascript = drupal_get_js('header');
    $this
      ->assertTrue(strpos($javascript, 'basePath') > 0, t('Rendered JavaScript header returns basePath setting.'));
    $this
      ->assertTrue(strpos($javascript, 'testSetting') > 0, t('Rendered JavaScript header returns custom setting.'));
    $this
      ->assertTrue(strpos($javascript, 'misc/jquery.js') > 0, t('Rendered JavaScript header includes jQuery.'));
  }

  /**
   * Test to see if resetting the JavaScript empties the cache.
   */
  function testReset() {
    drupal_add_js('misc/collapse.js');
    drupal_static_reset('drupal_add_js');
    $this
      ->assertEqual(array(), drupal_add_js(), t('Resetting the JavaScript correctly empties the cache.'));
  }

  /**
   * Test adding inline scripts.
   */
  function testAddInline() {
    $inline = 'jQuery(function () { });';
    $javascript = drupal_add_js($inline, array(
      'type' => 'inline',
      'scope' => 'footer',
    ));
    $this
      ->assertTrue(array_key_exists('misc/jquery.js', $javascript), t('jQuery is added when inline scripts are added.'));
    $data = end($javascript);
    $this
      ->assertEqual($inline, $data['data'], t('Inline JavaScript is correctly added to the footer.'));
  }

  /**
   * Test rendering an external JavaScript file.
   */
  function testRenderExternal() {
    $external = 'http://example.com/example.js';
    drupal_add_js($external, 'external');
    $javascript = drupal_get_js();

    // Local files have a base_path() prefix, external files should not.
    $this
      ->assertTrue(strpos($javascript, 'src="' . $external) > 0, t('Rendering an external JavaScript file.'));
  }

  /**
   * Test drupal_get_js() with a footer scope.
   */
  function testFooterHTML() {
    $inline = 'jQuery(function () { });';
    drupal_add_js($inline, array(
      'type' => 'inline',
      'scope' => 'footer',
    ));
    $javascript = drupal_get_js('footer');
    $this
      ->assertTrue(strpos($javascript, $inline) > 0, t('Rendered JavaScript footer returns the inline code.'));
  }

  /**
   * Test drupal_add_js() sets preproccess to false when cache is set to false.
   */
  function testNoCache() {
    $javascript = drupal_add_js('misc/collapse.js', array(
      'cache' => FALSE,
    ));
    $this
      ->assertFalse($javascript['misc/collapse.js']['preprocess'], t('Setting cache to FALSE sets proprocess to FALSE when adding JavaScript.'));
  }

  /**
   * Test adding a JavaScript file with a different weight.
   */
  function testDifferentWeight() {
    $javascript = drupal_add_js('misc/collapse.js', array(
      'weight' => JS_THEME,
    ));
    $this
      ->assertEqual($javascript['misc/collapse.js']['weight'], JS_THEME, t('Adding a JavaScript file with a different weight caches the given weight.'));
  }

  /**
   * Test JavaScript ordering.
   */
  function testRenderOrder() {

    // Add a bunch of JavaScript in strange ordering.
    drupal_add_js('(function($){alert("Weight 5 #1");})(jQuery);', array(
      'type' => 'inline',
      'scope' => 'footer',
      'weight' => 5,
    ));
    drupal_add_js('(function($){alert("Weight 0 #1");})(jQuery);', array(
      'type' => 'inline',
      'scope' => 'footer',
    ));
    drupal_add_js('(function($){alert("Weight 0 #2");})(jQuery);', array(
      'type' => 'inline',
      'scope' => 'footer',
    ));
    drupal_add_js('(function($){alert("Weight -8 #1");})(jQuery);', array(
      'type' => 'inline',
      'scope' => 'footer',
      'weight' => -8,
    ));
    drupal_add_js('(function($){alert("Weight -8 #2");})(jQuery);', array(
      'type' => 'inline',
      'scope' => 'footer',
      'weight' => -8,
    ));
    drupal_add_js('(function($){alert("Weight -8 #3");})(jQuery);', array(
      'type' => 'inline',
      'scope' => 'footer',
      'weight' => -8,
    ));
    drupal_add_js('(function($){alert("Weight -8 #4");})(jQuery);', array(
      'type' => 'inline',
      'scope' => 'footer',
      'weight' => -8,
    ));
    drupal_add_js('(function($){alert("Weight 5 #2");})(jQuery);', array(
      'type' => 'inline',
      'scope' => 'footer',
      'weight' => 5,
    ));
    drupal_add_js('(function($){alert("Weight 0 #3");})(jQuery);', array(
      'type' => 'inline',
      'scope' => 'footer',
    ));

    // Construct the expected result from the regex.
    $expected = array(
      "-8 #1",
      "-8 #2",
      "-8 #3",
      "-8 #4",
      "0 #1",
      "0 #2",
      "0 #3",
      "5 #1",
      "5 #2",
    );

    // Retrieve the rendered JavaScript and test against the regex.
    $js = drupal_get_js('footer');
    $matches = array();
    if (preg_match_all('/Weight\\s([-0-9]+\\s[#0-9]+)/', $js, $matches)) {
      $result = $matches[1];
    }
    else {
      $result = array();
    }
    $this
      ->assertIdentical($result, $expected, t('JavaScript is added in the expected weight order.'));
  }

  /**
   * Test rendering the JavaScript with a file's weight above jQuery's.
   */
  function testRenderDifferentWeight() {
    drupal_add_js('misc/collapse.js', array(
      'weight' => JS_LIBRARY - 21,
    ));
    $javascript = drupal_get_js();
    $this
      ->assertTrue(strpos($javascript, 'misc/collapse.js') < strpos($javascript, 'misc/jquery.js'), t('Rendering a JavaScript file above jQuery.'));
  }

  /**
   * Test altering a JavaScript's weight via hook_js_alter().
   *
   * @see simpletest_js_alter()
   */
  function testAlter() {

    // Add both tableselect.js and simpletest.js, with a larger weight on SimpleTest.
    drupal_add_js('misc/tableselect.js');
    drupal_add_js(drupal_get_path('module', 'simpletest') . '/simpletest.js', array(
      'weight' => JS_THEME,
    ));

    // Render the JavaScript, testing if simpletest.js was altered to be before
    // tableselect.js. See simpletest_js_alter() to see where this alteration
    // takes place.
    $javascript = drupal_get_js();
    $this
      ->assertTrue(strpos($javascript, 'simpletest.js') < strpos($javascript, 'misc/tableselect.js'), t('Altering JavaScript weight through the alter hook.'));
  }

  /**
   * Adds a library to the page and tests for both its JavaScript and its CSS.
   */
  function testLibraryRender() {
    $result = drupal_add_library('system', 'farbtastic');
    $this
      ->assertTrue($result !== FALSE, t('Library was added without errors.'));
    $scripts = drupal_get_js();
    $styles = drupal_get_css();
    $this
      ->assertTrue(strpos($scripts, 'misc/farbtastic/farbtastic.js'), t('JavaScript of library was added to the page.'));
    $this
      ->assertTrue(strpos($styles, 'misc/farbtastic/farbtastic.css'), t('Stylesheet of library was added to the page.'));
  }

  /**
   * Adds a JavaScript library to the page and alters it.
   *
   * @see common_test_library_alter()
   */
  function testLibraryAlter() {

    // Verify that common_test altered the title of Farbtastic.
    $library = drupal_get_library('system', 'farbtastic');
    $this
      ->assertEqual($library['title'], 'Farbtastic: Altered Library', t('Registered libraries were altered.'));

    // common_test_library_alter() also added a dependency on jQuery Form.
    drupal_add_library('system', 'farbtastic');
    $scripts = drupal_get_js();
    $this
      ->assertTrue(strpos($scripts, 'misc/jquery.form.js'), t('Altered library dependencies are added to the page.'));
  }

  /**
   * Tests that multiple modules can implement the same library.
   *
   * @see common_test_library()
   */
  function testLibraryNameConflicts() {
    $farbtastic = drupal_get_library('common_test', 'farbtastic');
    $this
      ->assertEqual($farbtastic['title'], 'Custom Farbtastic Library', t('Alternative libraries can be added to the page.'));
  }

  /**
   * Tests non-existing libraries.
   */
  function testLibraryUnknown() {
    $result = drupal_get_library('unknown', 'unknown');
    $this
      ->assertFalse($result, t('Unknown library returned FALSE.'));
    drupal_static_reset('drupal_get_library');
    $result = drupal_add_library('unknown', 'unknown');
    $this
      ->assertFalse($result, t('Unknown library returned FALSE.'));
    $scripts = drupal_get_js();
    $this
      ->assertTrue(strpos($scripts, 'unknown') === FALSE, t('Unknown library was not added to the page.'));
  }

  /**
   * Tests the addition of libraries through the #attached['library'] property.
   */
  function testAttachedLibrary() {
    $element['#attached']['library'][] = array(
      'system',
      'farbtastic',
    );
    drupal_render($element);
    $scripts = drupal_get_js();
    $this
      ->assertTrue(strpos($scripts, 'misc/farbtastic/farbtastic.js'), t('The attached_library property adds the additional libraries.'));
  }

}

/**
 * Tests for drupal_render().
 */
class DrupalRenderUnitTestCase extends DrupalWebTestCase {
  public static function getInfo() {
    return array(
      'name' => 'Drupal render',
      'description' => 'Performs unit tests on drupal_render().',
      'group' => 'System',
    );
  }
  function setUp() {
    parent::setUp('common_test');
  }

  /**
   * Test sorting by weight.
   */
  function testDrupalRenderSorting() {
    $first = $this
      ->randomName();
    $second = $this
      ->randomName();

    // Build an array with '#weight' set for each element.
    $elements = array(
      'second' => array(
        '#weight' => 10,
        '#markup' => $second,
      ),
      'first' => array(
        '#weight' => 0,
        '#markup' => $first,
      ),
    );
    $output = drupal_render($elements);

    // The lowest weight element should appear last in $output.
    $this
      ->assertTrue(strpos($output, $second) > strpos($output, $first), t('Elements were sorted correctly by weight.'));

    // Confirm that the $elements array has '#sorted' set to TRUE.
    $this
      ->assertTrue($elements['#sorted'], t("'#sorted' => TRUE was added to the array"));

    // Pass $elements through element_children() and ensure it remains
    // sorted in the correct order. drupal_render() will return an empty string
    // if used on the same array in the same request.
    $children = element_children($elements);
    $this
      ->assertTrue(array_shift($children) == 'first', t('Child found in the correct order.'));
    $this
      ->assertTrue(array_shift($children) == 'second', t('Child found in the correct order.'));

    // The same array structure again, but with #sorted set to TRUE.
    $elements = array(
      'second' => array(
        '#weight' => 10,
        '#markup' => $second,
      ),
      'first' => array(
        '#weight' => 0,
        '#markup' => $first,
      ),
      '#sorted' => TRUE,
    );
    $output = drupal_render($elements);

    // The elements should appear in output in the same order as the array.
    $this
      ->assertTrue(strpos($output, $second) < strpos($output, $first), t('Elements were not sorted.'));
  }

  /**
   * Test passing arguments to the theme function.
   */
  function testDrupalRenderThemeArguments() {
    $element = array(
      '#theme' => 'common_test_foo',
    );

    // Test that defaults work.
    $this
      ->assertEqual(drupal_render($element), 'foobar', 'Defaults work');
    $element = array(
      '#theme' => 'common_test_foo',
      '#foo' => $this
        ->randomName(),
      '#bar' => $this
        ->randomName(),
    );

    // Test that passing arguments to the theme function works.
    $this
      ->assertEqual(drupal_render($element), $element['#foo'] . $element['#bar'], 'Passing arguments to theme functions works');
  }

}

/**
 * Test for valid_url().
 */
class ValidUrlTestCase extends DrupalUnitTestCase {
  public static function getInfo() {
    return array(
      'name' => 'Valid Url',
      'description' => "Performs tests on Drupal's valid url function.",
      'group' => 'System',
    );
  }

  /**
   * Test valid absolute urls.
   */
  function testValidAbsolute() {
    $url_schemes = array(
      'http',
      'https',
      'ftp',
    );
    $valid_absolute_urls = array(
      'example.com',
      'www.example.com',
      'ex-ample.com',
      '3xampl3.com',
      'example.com/paren(the)sis',
      'example.com/index.html#pagetop',
      'example.com:8080',
      'subdomain.example.com',
      'example.com/index.php?q=node',
      'example.com/index.php?q=node&param=false',
      'user@www.example.com',
      'user:pass@www.example.com:8080/login.php?do=login&style=%23#pagetop',
      '127.0.0.1',
      'example.org?',
      'john%20doe:secret:foo@example.org/',
      'example.org/~,$\'*;',
      'caf%C3%A9.example.org',
      '[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html',
    );
    foreach ($url_schemes as $scheme) {
      foreach ($valid_absolute_urls as $url) {
        $test_url = $scheme . '://' . $url;
        $valid_url = valid_url($test_url, TRUE);
        $this
          ->assertTrue($valid_url, t('@url is a valid url.', array(
          '@url' => $test_url,
        )));
      }
    }
  }

  /**
   * Test invalid absolute urls.
   */
  function testInvalidAbsolute() {
    $url_schemes = array(
      'http',
      'https',
      'ftp',
    );
    $invalid_ablosule_urls = array(
      '',
      'ex!ample.com',
      'ex%ample.com',
    );
    foreach ($url_schemes as $scheme) {
      foreach ($invalid_ablosule_urls as $url) {
        $test_url = $scheme . '://' . $url;
        $valid_url = valid_url($test_url, TRUE);
        $this
          ->assertFalse($valid_url, t('@url is NOT a valid url.', array(
          '@url' => $test_url,
        )));
      }
    }
  }

  /**
   * Test valid relative urls.
   */
  function testValidRelative() {
    $valid_relative_urls = array(
      'paren(the)sis',
      'index.html#pagetop',
      'index.php?q=node',
      'index.php?q=node&param=false',
      'login.php?do=login&style=%23#pagetop',
    );
    foreach (array(
      '',
      '/',
    ) as $front) {
      foreach ($valid_relative_urls as $url) {
        $test_url = $front . $url;
        $valid_url = valid_url($test_url);
        $this
          ->assertTrue($valid_url, t('@url is a valid url.', array(
          '@url' => $test_url,
        )));
      }
    }
  }

  /**
   * Test invalid relative urls.
   */
  function testInvalidRelative() {
    $invalid_relative_urls = array(
      'ex^mple',
      'example<>',
      'ex%ample',
    );
    foreach (array(
      '',
      '/',
    ) as $front) {
      foreach ($invalid_relative_urls as $url) {
        $test_url = $front . $url;
        $valid_url = valid_url($test_url);
        $this
          ->assertFALSE($valid_url, t('@url is NOT a valid url.', array(
          '@url' => $test_url,
        )));
      }
    }
  }

}

/**
 * Tests for CRUD API functions.
 */
class DrupalDataApiTest extends DrupalWebTestCase {
  public static function getInfo() {
    return array(
      'name' => 'Data API functions',
      'description' => 'Tests the performance of CRUD APIs.',
      'group' => 'System',
    );
  }
  function setUp() {
    parent::setUp('taxonomy');
  }

  /**
   * Test the drupal_write_record() API function.
   */
  function testDrupalWriteRecord() {

    // Insert an object record for a table with a single-field primary key.
    $vocabulary = new stdClass();
    $vocabulary->name = 'test';
    $insert_result = drupal_write_record('taxonomy_vocabulary', $vocabulary);
    $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.'));
    $this
      ->assertTrue(isset($vocabulary->vid), t('Primary key is set on record created with drupal_write_record().'));

    // Update the initial record after changing a property.
    $vocabulary->name = 'testing';
    $update_result = drupal_write_record('taxonomy_vocabulary', $vocabulary, array(
      'vid',
    ));
    $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.'));

    // Insert an object record for a table with a multi-field primary key.
    $node_access = new stdClass();
    $node_access->nid = mt_rand();
    $node_access->gid = mt_rand();
    $node_access->realm = $this
      ->randomName();
    $insert_result = drupal_write_record('node_access', $node_access);
    $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.'));

    // Update the record.
    $update_result = drupal_write_record('node_access', $node_access, array(
      'nid',
      'gid',
      'realm',
    ));
    $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.'));
  }

}

/**
 * Tests Simpletest error and exception collector.
 */
class DrupalErrorCollectionUnitTest extends DrupalWebTestCase {

  /**
   * Errors triggered during the test.
   *
   * Errors are intercepted by the overriden implementation
   * of DrupalWebTestCase::error below.
   *
   * @var Array
   */
  protected $collectedErrors = array();
  public static function getInfo() {
    return array(
      'name' => 'SimpleTest error collector',
      'description' => 'Performs tests on the Simpletest error and exception collector.',
      'group' => 'SimpleTest',
    );
  }
  function setUp() {
    parent::setUp('system_test', 'error_test');
  }

  /**
   * Test that simpletest collects errors from the tested site.
   */
  function testErrorCollect() {
    $this->collectedErrors = array();
    $this
      ->drupalGet('error-test/generate-warnings-with-report');
    $this
      ->assertEqual(count($this->collectedErrors), 3, t('Three errors were collected'));
    if (count($this->collectedErrors) == 3) {
      $this
        ->assertError($this->collectedErrors[0], 'Notice', 'error_test_generate_warnings()', 'error_test.module', 'Undefined variable: bananas');
      $this
        ->assertError($this->collectedErrors[1], 'Warning', 'error_test_generate_warnings()', 'error_test.module', 'Division by zero');
      $this
        ->assertError($this->collectedErrors[2], 'User warning', 'error_test_generate_warnings()', 'error_test.module', 'Drupal is awesome');
    }
    else {

      // Give back the errors to the log report.
      foreach ($this->collectedErrors as $error) {
        parent::error($error['message'], $error['group'], $error['caller']);
      }
    }
  }
  protected function error($message = '', $group = 'Other', array $caller = NULL) {

    // This function overiddes DrupalWebTestCase::error(). We collect an error...
    $this->collectedErrors[] = array(
      'message' => $message,
      'group' => $group,
      'caller' => $caller,
    );

    // ... and ignore it.
  }

  /**
   * Assert that a collected error matches what we are expecting.
   */
  function assertError($error, $group, $function, $file, $message = NULL) {
    $this
      ->assertEqual($error['group'], $group, t("Group was %group", array(
      '%group' => $group,
    )));
    $this
      ->assertEqual($error['caller']['function'], $function, t("Function was %function", array(
      '%function' => $function,
    )));
    $this
      ->assertEqual(basename($error['caller']['file']), $file, t("File was %file", array(
      '%file' => $file,
    )));
    if (isset($message)) {
      $this
        ->assertEqual($error['message'], $message, t("Message was %message", array(
        '%message' => $message,
      )));
    }
  }

}

/**
 * Tests for the format_date() function.
 */
class FormatDateUnitTest extends DrupalWebTestCase {

  /**
   * Arbitrary langcode for a custom language.
   */
  const LANGCODE = 'xx';
  public static function getInfo() {
    return array(
      'name' => 'Format date',
      'description' => 'Test the format_date() function.',
      'group' => 'System',
    );
  }
  function setUp() {
    parent::setUp('locale');
    variable_set('configurable_timezones', 1);
    variable_set('date_format_long', 'l, j. F Y - G:i');
    variable_set('date_format_medium', 'j. F Y - G:i');
    variable_set('date_format_short', 'Y M j - g:ia');
    variable_set('locale_custom_strings_' . self::LANGCODE, array(
      '' => array(
        'Sunday' => 'domingo',
      ),
      'Long month name' => array(
        'March' => 'marzo',
      ),
    ));
    $this
      ->refreshVariables();
  }

  /**
   * Tests for the format_date() function.
   */
  function testFormatDate() {
    global $user, $language;
    $timestamp = strtotime('2007-03-26T00:00:00+00:00');
    $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.'));
    $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.'));
    $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.'));
    $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.'));
    $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.'));
    $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.'));

    // Create an admin user and add Spanish language.
    $admin_user = $this
      ->drupalCreateUser(array(
      'administer languages',
    ));
    $this
      ->drupalLogin($admin_user);
    $edit = array(
      'langcode' => self::LANGCODE,
      'name' => self::LANGCODE,
      'native' => self::LANGCODE,
      'direction' => LANGUAGE_LTR,
      'prefix' => self::LANGCODE,
    );
    $this
      ->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));

    // Create a test user to carry out the tests.
    $test_user = $this
      ->drupalCreateUser();
    $this
      ->drupalLogin($test_user);
    $edit = array(
      'language' => self::LANGCODE,
      'mail' => $test_user->mail,
      'timezone' => 'America/Los_Angeles',
    );
    $this
      ->drupalPost('user/' . $test_user->uid . '/edit', $edit, t('Save'));

    // Disable session saving as we are about to modify the global $user.
    drupal_save_session(FALSE);

    // Save the original user and language and then replace it with the test user and language.
    $real_user = $user;
    $user = user_load($test_user->uid, TRUE);
    $real_language = $language->language;
    $language->language = $user->language;
    $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.'));
    $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.'));
    $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.'));
    $this
      ->assertIdentical(format_date($timestamp, 'long'), 'domingo, 25. marzo 2007 - 17:00', t('Test long date format.'));
    $this
      ->assertIdentical(format_date($timestamp, 'medium'), '25. marzo 2007 - 17:00', t('Test medium date format.'));
    $this
      ->assertIdentical(format_date($timestamp, 'short'), '2007 Mar 25 - 5:00pm', t('Test short date format.'));
    $this
      ->assertIdentical(format_date($timestamp), '25. marzo 2007 - 17:00', t('Test default date format.'));

    // Restore the original user and language, and enable session saving.
    $user = $real_user;
    $language->language = $real_language;
    drupal_save_session(TRUE);
  }

}

/**
 * Tests for the format_date() function.
 */
class DrupalAttributesUnitTest extends DrupalUnitTestCase {
  public static function getInfo() {
    return array(
      'name' => 'HTML Attributes',
      'description' => 'Perform unit tests on the drupal_attributes() function.',
      'group' => 'System',
    );
  }

  /**
   * Tests that drupal_html_class() cleans the class name properly.
   */
  function testDrupalAttributes() {

    // Verify that special characters are HTML encoded.
    $this
      ->assertIdentical(drupal_attributes(array(
      'title' => '&"\'<>',
    )), ' title="&amp;&quot;&#039;&lt;&gt;"', t('HTML encode attribute values.'));

    // Verify multi-value attributes are concatenated with spaces.
    $attributes = array(
      'class' => array(
        'first',
        'last',
      ),
    );
    $this
      ->assertIdentical(drupal_attributes(array(
      'class' => array(
        'first',
        'last',
      ),
    )), ' class="first last"', t('Concatenate multi-value attributes.'));

    // Verify empty attribute values are rendered.
    $this
      ->assertIdentical(drupal_attributes(array(
      'alt' => '',
    )), ' alt=""', t('Empty attribute value #1.'));
    $this
      ->assertIdentical(drupal_attributes(array(
      'alt' => NULL,
    )), ' alt=""', t('Empty attribute value #2.'));

    // Verify multiple attributes are rendered.
    $attributes = array(
      'id' => 'id-test',
      'class' => array(
        'first',
        'last',
      ),
      'alt' => 'Alternate',
    );
    $this
      ->assertIdentical(drupal_attributes($attributes), ' id="id-test" class="first last" alt="Alternate"', t('Multiple attributes.'));

    // Verify empty attributes array is rendered.
    $this
      ->assertIdentical(drupal_attributes(array()), '', t('Empty attributes array.'));
  }

}

Classes

Namesort descending Description
CascadingStylesheetsTestCase Test the Drupal CSS system.
CascadingStylesheetsUnitTest CSS Unit Tests.
CommonSizeTestCase
CommonURLUnitTest Tests for URL generation functions.
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.
DrupalGotoTest Testing drupal_goto and hook_drupal_goto_alter().
DrupalHTMLIdentifierTestCase Test for cleaning HTML identifiers.
DrupalHTTPRequestTestCase Test drupal_http_request().
DrupalRenderUnitTestCase Tests for drupal_render().
DrupalSetContentTestCase Testing drupal_add_region_content and drupal_get_region_content.
DrupalTagsHandlingTestCase Test drupal_explode_tags() and drupal_implode_tags().
FormatDateUnitTest Tests for the format_date() function.
JavaScriptTestCase Tests for the JavaScript system.
ValidUrlTestCase Test for valid_url().