Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176

This commit is contained in:
Pantheon Automation 2015-08-17 17:00:26 -07:00 committed by Greg Anderson
commit 9921556621
13277 changed files with 1459781 additions and 0 deletions

View file

@ -0,0 +1,221 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Component\Utility\ArgumentsResolverTest.
*/
namespace Drupal\Tests\Component\Utility {
use Drupal\Component\Utility\ArgumentsResolver;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Component\Utility\ArgumentsResolver
* @group Access
*/
class ArgumentsResolverTest extends UnitTestCase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
}
/**
* Tests the getArgument() method.
*
* @dataProvider providerTestGetArgument
*/
public function testGetArgument($callable, $scalars, $objects, $wildcards, $expected) {
$arguments = (new ArgumentsResolver($scalars, $objects, $wildcards))->getArguments($callable);
$this->assertSame($expected, $arguments);
}
/**
* Provides test data to testGetArgument().
*/
public function providerTestGetArgument() {
$data = [];
// Test an optional parameter with no provided value.
$data[] = [
function($foo = 'foo') {}, [], [], [] , ['foo'],
];
// Test an optional parameter with a provided value.
$data[] = [
function($foo = 'foo') {}, ['foo' => 'bar'], [], [], ['bar'],
];
// Test with a provided value.
$data[] = [
function($foo) {}, ['foo' => 'bar'], [], [], ['bar'],
];
// Test with an explicitly NULL value.
$data[] = [
function($foo) {}, [], ['foo' => NULL], [], [NULL],
];
// Test with a raw value that overrides the provided upcast value, since
// it is not typehinted.
$scalars = ['foo' => 'baz'];
$objects = ['foo' => new \stdClass()];
$data[] = [
function($foo) {}, $scalars, $objects, [], ['baz'],
];
return $data;
}
/**
* Tests getArgument() with an object.
*/
public function testGetArgumentObject() {
$callable = function(\stdClass $object) {};
$object = new \stdClass();
$arguments = (new ArgumentsResolver([], ['object' => $object], []))->getArguments($callable);
$this->assertSame([$object], $arguments);
}
/**
* Tests getArgument() with a wildcard object for a parameter with a custom name.
*/
public function testGetWildcardArgument() {
$callable = function(\stdClass $custom_name) {};
$object = new \stdClass();
$arguments = (new ArgumentsResolver([], [], [$object]))->getArguments($callable);
$this->assertSame([$object], $arguments);
}
/**
* Tests getArgument() with a Route, Request, and Account object.
*/
public function testGetArgumentOrder() {
$a1 = $this->getMock('\Drupal\Tests\Component\Utility\TestInterface1');
$a2 = $this->getMock('\Drupal\Tests\Component\Utility\TestClass');
$a3 = $this->getMock('\Drupal\Tests\Component\Utility\TestInterface2');
$objects = [
't1' => $a1,
'tc' => $a2,
];
$wildcards = [$a3];
$resolver = new ArgumentsResolver([], $objects, $wildcards);
$callable = function(TestInterface1 $t1, TestClass $tc, TestInterface2 $t2) {};
$arguments = $resolver->getArguments($callable);
$this->assertSame([$a1, $a2, $a3], $arguments);
// Test again, but with the arguments in a different order.
$callable = function(TestInterface2 $t2, TestClass $tc, TestInterface1 $t1) {};
$arguments = $resolver->getArguments($callable);
$this->assertSame([$a3, $a2, $a1], $arguments);
}
/**
* Tests getArgument() with a wildcard parameter with no typehint.
*
* Without the typehint, the wildcard object will not be passed to the callable.
*
* @expectedException \RuntimeException
* @expectedExceptionMessage requires a value for the "$route" argument.
*/
public function testGetWildcardArgumentNoTypehint() {
$a = $this->getMock('\Drupal\Tests\Component\Utility\TestInterface1');
$wildcards = [$a];
$resolver = new ArgumentsResolver([], [], $wildcards);
$callable = function($route) {};
$arguments = $resolver->getArguments($callable);
$this->assertNull($arguments);
}
/**
* Tests getArgument() with a named parameter with no typehint and a value.
*
* Without the typehint, passing a value to a named parameter will still
* receive the provided value.
*/
public function testGetArgumentRouteNoTypehintAndValue() {
$scalars = ['route' => 'foo'];
$resolver = new ArgumentsResolver($scalars, [], []);
$callable = function($route) {};
$arguments = $resolver->getArguments($callable);
$this->assertSame(['foo'], $arguments);
}
/**
* Tests handleUnresolvedArgument() for a scalar argument.
*
* @expectedException \RuntimeException
* @expectedExceptionMessage requires a value for the "$foo" argument.
*/
public function testHandleNotUpcastedArgument() {
$objects = ['foo' => 'bar'];
$scalars = ['foo' => 'baz'];
$resolver = new ArgumentsResolver($scalars, $objects, []);
$callable = function(\stdClass $foo) {};
$arguments = $resolver->getArguments($callable);
$this->assertNull($arguments);
}
/**
* Tests handleUnresolvedArgument() for missing arguments.
*
* @expectedException \RuntimeException
* @expectedExceptionMessage requires a value for the "$foo" argument.
*
* @dataProvider providerTestHandleUnresolvedArgument
*/
public function testHandleUnresolvedArgument($callable) {
$resolver = new ArgumentsResolver([], [], []);
$arguments = $resolver->getArguments($callable);
$this->assertNull($arguments);
}
/**
* Provides test data to testHandleUnresolvedArgument().
*/
public function providerTestHandleUnresolvedArgument() {
$data = [];
$data[] = [function($foo) {}];
$data[] = [[new TestClass(), 'access']];
$data[] = ['test_access_arguments_resolver_access'];
return $data;
}
}
/**
* Provides a test class.
*/
class TestClass {
public function access($foo) {
}
}
/**
* Provides a test interface.
*/
interface TestInterface1 {
}
/**
* Provides a different test interface.
*/
interface TestInterface2 {
}
}
namespace {
function test_access_arguments_resolver_access($foo) {
}
}

View file

@ -0,0 +1,65 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Component\Utility\BytesTest.
*/
namespace Drupal\Tests\Component\Utility;
use Drupal\Component\Utility\Bytes;
use Drupal\Tests\UnitTestCase;
/**
* Tests bytes size parsing helper methods.
*
* @group Utility
*
* @coversDefaultClass \Drupal\Component\Utility\Bytes
*/
class BytesTest extends UnitTestCase {
/**
* Tests \Drupal\Component\Utility\Bytes::toInt().
*
* @param int $size
* The value for the size argument for
* \Drupal\Component\Utility\Bytes::toInt().
* @param int $expected_int
* The expected return value from
* \Drupal\Component\Utility\Bytes::toInt().
*
* @dataProvider providerTestToInt
* @covers ::toInt
*/
public function testToInt($size, $expected_int) {
$this->assertEquals($expected_int, Bytes::toInt($size));
}
/**
* Provides data for testToInt.
*
* @return array
* An array of arrays, each containing the argument for
* \Drupal\Component\Utility\Bytes::toInt(): size, and the expected return
* value.
*/
public function providerTestToInt() {
return array(
array('1', 1),
array('1 byte', 1),
array('1 KB' , Bytes::KILOBYTE),
array('1 MB' , pow(Bytes::KILOBYTE, 2)),
array('1 GB' , pow(Bytes::KILOBYTE, 3)),
array('1 TB' , pow(Bytes::KILOBYTE, 4)),
array('1 PB' , pow(Bytes::KILOBYTE, 5)),
array('1 EB' , pow(Bytes::KILOBYTE, 6)),
array('1 ZB' , pow(Bytes::KILOBYTE, 7)),
array('1 YB' , pow(Bytes::KILOBYTE, 8)),
array('23476892 bytes', 23476892),
array('76MRandomStringThatShouldBeIgnoredByParseSize.', 79691776), // 76 MB
array('76.24 Giggabyte', 81862076662), // 76.24 GB (with typo)
);
}
}

View file

@ -0,0 +1,126 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Component\Utility\ColorTest.
*/
namespace Drupal\Tests\Component\Utility;
use Drupal\Component\Utility\Color;
use Drupal\Tests\UnitTestCase;
/**
* Tests Color utility class conversions.
*
* @group Utility
*/
class ColorTest extends UnitTestCase {
/**
* Tests Color::hexToRgb().
*
* @param string $value
* The hex color value.
* @param string $expected
* The expected rgb color value.
* @param bool $invalid
* Whether this value is invalid and exception should be expected.
*
* @dataProvider providerTestHexToRgb
*/
public function testHexToRgb($value, $expected, $invalid = FALSE) {
if ($invalid) {
$this->setExpectedException('InvalidArgumentException');
}
$this->assertSame($expected, Color::hexToRgb($value));
}
/**
* Data provider for testHexToRgb().
*
* @see testHexToRgb()
*
* @return array
* An array of arrays containing:
* - The hex color value.
* - The rgb color array value.
* - (optional) Boolean indicating invalid status. Defaults to FALSE.
*/
public function providerTestHexToRgb() {
$invalid = array();
// Any invalid arguments should throw an exception.
foreach (array('', '-1', '1', '12', '12345', '1234567', '123456789', '123456789a', 'foo') as $value) {
$invalid[] = array($value, '', TRUE);
}
// Duplicate all invalid value tests with additional '#' prefix.
// The '#' prefix inherently turns the data type into a string.
foreach ($invalid as $value) {
$invalid[] = array('#' . $value[0], '', TRUE);
}
// Add invalid data types (hex value must be a string).
foreach (array(
1, 12, 1234, 12345, 123456, 1234567, 12345678, 123456789, 123456789,
-1, PHP_INT_MAX, PHP_INT_MAX + 1, -PHP_INT_MAX, 0x0, 0x010
) as $value) {
$invalid[] = array($value, '', TRUE);
}
// And some valid values.
$valid = array(
// Shorthands without alpha.
array('hex' => '#000', 'rgb' => array('red' => 0, 'green' => 0, 'blue' => 0)),
array('hex' => '#fff', 'rgb' => array('red' => 255, 'green' => 255, 'blue' => 255)),
array('hex' => '#abc', 'rgb' => array('red' => 170, 'green' => 187, 'blue' => 204)),
array('hex' => 'cba', 'rgb' => array('red' => 204, 'green' => 187, 'blue' => 170)),
// Full without alpha.
array('hex' => '#000000', 'rgb' => array('red' => 0, 'green' => 0, 'blue' => 0)),
array('hex' => '#ffffff', 'rgb' => array('red' => 255, 'green' => 255, 'blue' => 255)),
array('hex' => '#010203', 'rgb' => array('red' => 1, 'green' => 2, 'blue' => 3)),
);
return array_merge($invalid, $valid);
}
/**
* Tests Color::rgbToHex().
*
* @param string $value
* The rgb color value.
* @param string $expected
* The expected hex color value.
*
* @dataProvider providerTestRbgToHex
*/
public function testRgbToHex($value, $expected) {
$this->assertSame($expected, Color::rgbToHex($value));
}
/**
* Data provider for testRgbToHex().
*
* @see testRgbToHex()
*
* @return array
* An array of arrays containing:
* - The rgb color array value.
* - The hex color value.
*/
public function providerTestRbgToHex() {
// Input using named RGB array (e.g., as returned by Color::hexToRgb()).
$tests = array(
array(array('red' => 0, 'green' => 0, 'blue' => 0), '#000000'),
array(array('red' => 255, 'green' => 255, 'blue' => 255), '#ffffff'),
array(array('red' => 119, 'green' => 119, 'blue' => 119), '#777777'),
array(array('red' => 1, 'green' => 2, 'blue' => 3), '#010203'),
);
// Input using indexed RGB array (e.g.: array(10, 10, 10)).
foreach ($tests as $test) {
$tests[] = array(array_values($test[0]), $test[1]);
}
// Input using CSS RGB string notation (e.g.: 10, 10, 10).
foreach ($tests as $test) {
$tests[] = array(implode(', ', $test[0]), $test[1]);
}
return $tests;
}
}

View file

@ -0,0 +1,155 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Component\Utility\CryptTest.
*/
namespace Drupal\Tests\Component\Utility;
use Drupal\Tests\UnitTestCase;
use Drupal\Component\Utility\Crypt;
/**
* Tests random byte generation.
*
* @group Utility
*
* @coversDefaultClass \Drupal\Component\Utility\Crypt
*/
class CryptTest extends UnitTestCase {
/**
* Tests random byte generation.
*
* @covers ::randomBytes
*/
public function testRandomBytes() {
for ($i = 1; $i < 10; $i++) {
$count = rand(10, 10000);
// Check that different values are being generated.
$this->assertNotEquals(Crypt::randomBytes($count), Crypt::randomBytes($count));
// Check the length.
$this->assertEquals(strlen(Crypt::randomBytes($count)), $count);
}
}
/**
* Tests hash generation.
*
* @dataProvider providerTestHashBase64
* @covers ::hashBase64
*
* @param string $data
* Data to hash.
* @param string $expected_hash
* Expected result from hashing $data.
*/
public function testHashBase64($data, $expected_hash) {
$hash = Crypt::hashBase64($data);
$this->assertEquals($expected_hash, $hash, 'The correct hash was not calculated.');
}
/**
* Tests HMAC generation.
*
* @dataProvider providerTestHmacBase64
* @covers ::hmacBase64
*
* @param string $data
* Data to hash.
* @param string $key
* Key to use in hashing process.
* @param string $expected_hmac
* Expected result from hashing $data using $key.
*/
public function testHmacBase64($data, $key, $expected_hmac) {
$hmac = Crypt::hmacBase64($data, $key);
$this->assertEquals($expected_hmac, $hmac, 'The correct hmac was not calculated.');
}
/**
* Tests the hmacBase64 method with invalid parameters.
*
* @dataProvider providerTestHmacBase64Invalid
* @expectedException InvalidArgumentException
* @covers ::hmacBase64
*
* @param string $data
* Data to hash.
* @param string $key
* Key to use in hashing process.
*/
public function testHmacBase64Invalid($data, $key) {
Crypt::hmacBase64($data, $key);
}
/**
* Provides data for self::testHashBase64().
*
* @return array Test data.
*/
public function providerTestHashBase64() {
return array(
array(
'data' => 'The SHA (Secure Hash Algorithm) is one of a number of cryptographic hash functions. A cryptographic hash is like a signature for a text or a data file. SHA-256 algorithm generates an almost-unique, fixed size 256-bit (32-byte) hash. Hash is a one way function it cannot be decrypted back. This makes it suitable for password validation, challenge hash authentication, anti-tamper, digital signatures.',
'expectedHash' => '034rT6smZAVRxpq8O98cFFNLIVx_Ph1EwLZQKcmRR_s',
),
array(
'data' => 'SHA-256 is one of the successor hash functions to SHA-1, and is one of the strongest hash functions available.',
'expected_hash' => 'yuqkDDYqprL71k4xIb6K6D7n76xldO4jseRhEkEE6SI',
),
);
}
/**
* Provides data for self::testHmacBase64().
*
* @return array Test data.
*/
public function providerTestHmacBase64() {
return array(
array(
'data' => 'Calculates a base-64 encoded, URL-safe sha-256 hmac.',
'key' => 'secret-key',
'expected_hmac' => '2AaH63zwjhekWZlEpAiufyfhAHIzbQhl9Hd9oCi3_c8',
),
);
}
/**
* Provides data for self::testHmacBase64().
*
* @return array Test data.
*/
public function providerTestHmacBase64Invalid() {
return array(
array(new \stdClass(), new \stdClass()),
array(new \stdClass(), 'string'),
array(new \stdClass(), 1),
array(new \stdClass(), 0),
array(NULL, new \stdClass()),
array('string', new \stdClass()),
array(1, new \stdClass()),
array(0, new \stdClass()),
array(array(), array()),
array(array(), NULL),
array(array(), 'string'),
array(array(), 1),
array(array(), 0),
array(NULL, array()),
array(1, array()),
array(0, array()),
array('string', array()),
array(array(), NULL),
array(NULL, NULL),
array(NULL, 'string'),
array(NULL, 1),
array(NULL, 0),
array(1, NULL),
array(0, NULL),
array('string', NULL),
);
}
}

View file

@ -0,0 +1,67 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Component\Utility\EnvironmentTest.
*/
namespace Drupal\Tests\Component\Utility;
use Drupal\Component\Utility\Environment;
use Drupal\Tests\UnitTestCase;
/**
* Test PHP Environment helper methods.
*
* @group Utility
*
* @coversDefaultClass \Drupal\Component\Utility\Environment
*/
class EnvironmentTest extends UnitTestCase {
/**
* Tests \Drupal\Component\Utility\Environment::checkMemoryLimit().
*
* @dataProvider providerTestCheckMemoryLimit
* @covers ::checkMemoryLimit
*
* @param string $required
* The required memory argument for
* \Drupal\Component\Utility\Environment::checkMemoryLimit().
* @param string $custom_memory_limit
* The custom memory limit argument for
* \Drupal\Component\Utility\Environment::checkMemoryLimit().
* @param bool $expected
* The expected return value from
* \Drupal\Component\Utility\Environment::checkMemoryLimit().
*/
public function testCheckMemoryLimit($required, $custom_memory_limit, $expected) {
$actual = Environment::checkMemoryLimit($required, $custom_memory_limit);
$this->assertEquals($expected, $actual);
}
/**
* Provides data for testCheckMemoryLimit().
*
* @return array
* An array of arrays, each containing the arguments for
* \Drupal\Component\Utility\Environment::checkMemoryLimit():
* required and memory_limit, and the expected return value.
*/
public function providerTestCheckMemoryLimit() {
$memory_limit = ini_get('memory_limit');
$twice_avail_memory = ($memory_limit * 2) . 'MB';
return array(
// Minimal amount of memory should be available.
array('30MB', NULL, TRUE),
// Exceed a custom (unlimited) memory limit.
array($twice_avail_memory, -1, TRUE),
// Exceed a custom memory limit.
array('30MB', '16MB', FALSE),
// Available = required.
array('30MB', '30MB', TRUE),
);
}
}

View file

@ -0,0 +1,262 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Component\Utility\HtmlTest.
*/
namespace Drupal\Tests\Component\Utility;
use Drupal\Component\Utility\Html;
use Drupal\Tests\UnitTestCase;
/**
* Tests \Drupal\Component\Utility\Html.
*
* @group Common
*
* @coversDefaultClass \Drupal\Component\Utility\Html
*/
class HtmlTest extends UnitTestCase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$property = new \ReflectionProperty('Drupal\Component\Utility\Html', 'seenIdsInit');
$property->setAccessible(TRUE);
$property->setValue(NULL);
}
/**
* Tests the Html::cleanCssIdentifier() method.
*
* @param string $expected
* The expected result.
* @param string $source
* The string being transformed to an ID.
* @param array|null $filter
* (optional) An array of string replacements to use on the identifier. If
* NULL, no filter will be passed and a default will be used.
*
* @dataProvider providerTestCleanCssIdentifier
*
* @covers ::cleanCssIdentifier
*/
public function testCleanCssIdentifier($expected, $source, $filter = NULL) {
if ($filter !== NULL) {
$this->assertSame($expected, Html::cleanCssIdentifier($source, $filter));
}
else {
$this->assertSame($expected, Html::cleanCssIdentifier($source));
}
}
/**
* Provides test data for testCleanCssIdentifier().
*
* @return array
* Test data.
*/
public function providerTestCleanCssIdentifier() {
$id1 = 'abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789';
$id2 = '¡¢£¤¥';
$id3 = 'css__identifier__with__double__underscores';
return array(
// Verify that no valid ASCII characters are stripped from the identifier.
array($id1, $id1, array()),
// Verify that valid UTF-8 characters are not stripped from the identifier.
array($id2, $id2, array()),
// Verify that invalid characters (including non-breaking space) are stripped from the identifier.
array($id3, $id3),
// Verify that double underscores are not stripped from the identifier.
array('invalididentifier', 'invalid !"#$%&\'()*+,./:;<=>?@[\\]^`{|}~ identifier', array()),
// Verify that an identifier starting with a digit is replaced.
array('_cssidentifier', '1cssidentifier', array()),
// Verify that an identifier starting with a hyphen followed by a digit is
// replaced.
array('__cssidentifier', '-1cssidentifier', array()),
// Verify that an identifier starting with two hyphens is replaced.
array('__cssidentifier', '--cssidentifier', array())
);
}
/**
* Tests that Html::getClass() cleans the class name properly.
*
* @coversDefaultClass ::getClass
*/
public function testHtmlClass() {
// Verify Drupal coding standards are enforced.
$this->assertSame(Html::getClass('CLASS NAME_[Ü]'), 'class-name--ü', 'Enforce Drupal coding standards.');
}
/**
* Tests the Html::getUniqueId() method.
*
* @param string $expected
* The expected result.
* @param string $source
* The string being transformed to an ID.
* @param bool $reset
* (optional) If TRUE, reset the list of seen IDs. Defaults to FALSE.
*
* @dataProvider providerTestHtmlGetUniqueId
*
* @covers ::getUniqueId
*/
public function testHtmlGetUniqueId($expected, $source, $reset = FALSE) {
if ($reset) {
Html::resetSeenIds();
}
$this->assertSame($expected, Html::getUniqueId($source));
}
/**
* Provides test data for testHtmlGetId().
*
* @return array
* Test data.
*/
public function providerTestHtmlGetUniqueId() {
$id = 'abcdefghijklmnopqrstuvwxyz-0123456789';
return array(
// Verify that letters, digits, and hyphens are not stripped from the ID.
array($id, $id),
// Verify that invalid characters are stripped from the ID.
array('invalididentifier', 'invalid,./:@\\^`{Üidentifier'),
// Verify Drupal coding standards are enforced.
array('id-name-1', 'ID NAME_[1]'),
// Verify that a repeated ID is made unique.
array('test-unique-id', 'test-unique-id', TRUE),
array('test-unique-id--2', 'test-unique-id'),
array('test-unique-id--3', 'test-unique-id'),
);
}
/**
* Tests the Html::getUniqueId() method.
*
* @param string $expected
* The expected result.
* @param string $source
* The string being transformed to an ID.
*
* @dataProvider providerTestHtmlGetUniqueIdWithAjaxIds
*
* @covers ::getUniqueId
*/
public function testHtmlGetUniqueIdWithAjaxIds($expected, $source) {
Html::setIsAjax(TRUE);
$id = Html::getUniqueId($source);
// Note, we truncate two hyphens at the end.
// @see \Drupal\Component\Utility\Html::getId()
if (strpos($source, '--') !== FALSE) {
$random_suffix = substr($id, strlen($source) + 1);
}
else {
$random_suffix = substr($id, strlen($source) + 2);
}
$expected = $expected . $random_suffix;
$this->assertSame($expected, $id);
}
/**
* Provides test data for testHtmlGetId().
*
* @return array
* Test data.
*/
public function providerTestHtmlGetUniqueIdWithAjaxIds() {
return array(
array('test-unique-id1--', 'test-unique-id1'),
// Note, we truncate two hyphens at the end.
// @see \Drupal\Component\Utility\Html::getId()
array('test-unique-id1---', 'test-unique-id1--'),
array('test-unique-id2--', 'test-unique-id2'),
);
}
/**
* Tests the Html::getUniqueId() method.
*
* @param string $expected
* The expected result.
* @param string $source
* The string being transformed to an ID.
*
* @dataProvider providerTestHtmlGetId
*
* @covers ::getId
*/
public function testHtmlGetId($expected, $source) {
Html::setIsAjax(FALSE);
$this->assertSame($expected, Html::getId($source));
}
/**
* Provides test data for testHtmlGetId().
*
* @return array
* Test data.
*/
public function providerTestHtmlGetId() {
$id = 'abcdefghijklmnopqrstuvwxyz-0123456789';
return array(
// Verify that letters, digits, and hyphens are not stripped from the ID.
array($id, $id),
// Verify that invalid characters are stripped from the ID.
array('invalididentifier', 'invalid,./:@\\^`{Üidentifier'),
// Verify Drupal coding standards are enforced.
array('id-name-1', 'ID NAME_[1]'),
// Verify that a repeated ID is made unique.
array('test-unique-id', 'test-unique-id'),
array('test-unique-id', 'test-unique-id'),
);
}
/**
* Tests Html::decodeEntities().
*
* @dataProvider providerDecodeEntities
* @covers ::decodeEntities
*/
public function testDecodeEntities($text, $expected) {
$this->assertEquals($expected, Html::decodeEntities($text));
}
/**
* Data provider for testDecodeEntities().
*
* @see testCheckPlain()
*/
public function providerDecodeEntities() {
return array(
array('Drupal', 'Drupal'),
array('<script>', '<script>'),
array('&lt;script&gt;', '<script>'),
array('&#60;script&#62;', '<script>'),
array('&amp;lt;script&amp;gt;', '&lt;script&gt;'),
array('"', '"'),
array('&#34;', '"'),
array('&amp;#34;', '&#34;'),
array('&quot;', '"'),
array('&amp;quot;', '&quot;'),
array("'", "'"),
array('&#39;', "'"),
array('&amp;#39;', '&#39;'),
array('©', '©'),
array('&copy;', '©'),
array('&#169;', '©'),
array('→', '→'),
array('&#8594;', '→'),
array('➼', '➼'),
array('&#10172;', '➼'),
array('&euro;', '€'),
);
}
}

View file

@ -0,0 +1,164 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Component\Utility\ImageTest.
*/
namespace Drupal\Tests\Component\Utility;
use Drupal\Component\Utility\Image;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Component\Utility\Image
* @group Image
*/
class ImageTest extends UnitTestCase {
/**
* Tests all control flow branches in image_dimensions_scale().
*
* @dataProvider providerTestScaleDimensions
*/
public function testScaleDimensions($input, $output) {
// Process the test dataset.
$return_value = Image::scaleDimensions($input['dimensions'], $input['width'], $input['height'], $input['upscale']);
// Check the width.
$this->assertEquals($output['dimensions']['width'], $input['dimensions']['width'], sprintf('Computed width (%s) does not equal expected width (%s)', $output['dimensions']['width'], $input['dimensions']['width']));
// Check the height.
$this->assertEquals($output['dimensions']['height'], $input['dimensions']['height'], sprintf('Computed height (%s) does not equal expected height (%s)', $output['dimensions']['height'], $input['dimensions']['height']));
// Check the return value.
$this->assertEquals($output['return_value'], $return_value, 'Incorrect return value.');
}
/**
* Provides data for image dimension scale tests.
*
* @return array
* Keyed array containing:
* - 'input' - Array which contains input for
* Image::scaleDimensions().
* - 'output' - Array which contains expected output after passing
* through Image::scaleDimensions. Also contains a boolean
* 'return_value' which should match the expected return value.
*
* @see testScaleDimensions()
*/
public function providerTestScaleDimensions() {
// Define input / output datasets to test different branch conditions.
$tests = array();
// Test branch conditions:
// - No height.
// - Upscale, don't need to upscale.
$tests[] = array(
'input' => array(
'dimensions' => array(
'width' => 1000,
'height' => 2000,
),
'width' => 200,
'height' => NULL,
'upscale' => TRUE,
),
'output' => array(
'dimensions' => array(
'width' => 200,
'height' => 400,
),
'return_value' => TRUE,
),
);
// Test branch conditions:
// - No width.
// - Don't upscale, don't need to upscale.
$tests[] = array(
'input' => array(
'dimensions' => array(
'width' => 1000,
'height' => 800,
),
'width' => NULL,
'height' => 140,
'upscale' => FALSE,
),
'output' => array(
'dimensions' => array(
'width' => 175,
'height' => 140,
),
'return_value' => TRUE,
),
);
// Test branch conditions:
// - Source aspect ratio greater than target.
// - Upscale, need to upscale.
$tests[] = array(
'input' => array(
'dimensions' => array(
'width' => 8,
'height' => 20,
),
'width' => 200,
'height' => 140,
'upscale' => TRUE,
),
'output' => array(
'dimensions' => array(
'width' => 56,
'height' => 140,
),
'return_value' => TRUE,
),
);
// Test branch condition: target aspect ratio greater than source.
$tests[] = array(
'input' => array(
'dimensions' => array(
'width' => 2000,
'height' => 800,
),
'width' => 200,
'height' => 140,
'upscale' => FALSE,
),
'output' => array(
'dimensions' => array(
'width' => 200,
'height' => 80,
),
'return_value' => TRUE,
),
);
// Test branch condition: don't upscale, need to upscale.
$tests[] = array(
'input' => array(
'dimensions' => array(
'width' => 100,
'height' => 50,
),
'width' => 200,
'height' => 140,
'upscale' => FALSE,
),
'output' => array(
'dimensions' => array(
'width' => 100,
'height' => 50,
),
'return_value' => FALSE,
),
);
return $tests;
}
}

View file

@ -0,0 +1,262 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Component\Utility\NestedArrayTest.
*/
namespace Drupal\Tests\Component\Utility;
use Drupal\Component\Utility\NestedArray;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Component\Utility\NestedArray
* @group Utility
*/
class NestedArrayTest extends UnitTestCase {
/**
* Form array to check.
*
* @var array
*/
protected $form;
/**
* Array of parents for the nested element.
*
* @var array
*/
protected $parents;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Create a form structure with a nested element.
$this->form['details']['element'] = array(
'#value' => 'Nested element',
);
// Set up parent array.
$this->parents = array('details', 'element');
}
/**
* Tests getting nested array values.
*
* @covers ::getValue
*/
public function testGetValue() {
// Verify getting a value of a nested element.
$value = NestedArray::getValue($this->form, $this->parents);
$this->assertSame('Nested element', $value['#value'], 'Nested element value found.');
// Verify changing a value of a nested element by reference.
$value = &NestedArray::getValue($this->form, $this->parents);
$value['#value'] = 'New value';
$value = NestedArray::getValue($this->form, $this->parents);
$this->assertSame('New value', $value['#value'], 'Nested element value was changed by reference.');
$this->assertSame('New value', $this->form['details']['element']['#value'], 'Nested element value was changed by reference.');
// Verify that an existing key is reported back.
$key_exists = NULL;
NestedArray::getValue($this->form, $this->parents, $key_exists);
$this->assertTrue($key_exists, 'Existing key found.');
// Verify that a non-existing key is reported back and throws no errors.
$key_exists = NULL;
$parents = $this->parents;
$parents[] = 'foo';
NestedArray::getValue($this->form, $parents, $key_exists);
$this->assertFalse($key_exists, 'Non-existing key not found.');
}
/**
* Tests setting nested array values.
*
* @covers ::setValue
*/
public function testSetValue() {
$new_value = array(
'#value' => 'New value',
'#required' => TRUE,
);
// Verify setting the value of a nested element.
NestedArray::setValue($this->form, $this->parents, $new_value);
$this->assertSame('New value', $this->form['details']['element']['#value'], 'Changed nested element value found.');
$this->assertTrue($this->form['details']['element']['#required'], 'New nested element value found.');
}
/**
* Tests force-setting values.
*
* @covers ::setValue
*/
public function testSetValueForce() {
$new_value = array(
'one',
);
$this->form['details']['non-array-parent'] = 'string';
$parents = array('details', 'non-array-parent', 'child');
NestedArray::setValue($this->form, $parents, $new_value, TRUE);
$this->assertSame($new_value, $this->form['details']['non-array-parent']['child'], 'The nested element was not forced to the new value.');
}
/**
* Tests unsetting nested array values.
*
* @covers ::unsetValue
*/
public function testUnsetValue() {
// Verify unsetting a non-existing nested element throws no errors and the
// non-existing key is properly reported.
$key_existed = NULL;
$parents = $this->parents;
$parents[] = 'foo';
NestedArray::unsetValue($this->form, $parents, $key_existed);
$this->assertTrue(isset($this->form['details']['element']['#value']), 'Outermost nested element key still exists.');
$this->assertFalse($key_existed, 'Non-existing key not found.');
// Verify unsetting a nested element.
$key_existed = NULL;
NestedArray::unsetValue($this->form, $this->parents, $key_existed);
$this->assertFalse(isset($this->form['details']['element']), 'Removed nested element not found.');
$this->assertTrue($key_existed, 'Existing key was found.');
}
/**
* Tests existence of array key.
*/
public function testKeyExists() {
// Verify that existing key is found.
$this->assertTrue(NestedArray::keyExists($this->form, $this->parents), 'Nested key found.');
// Verify that non-existing keys are not found.
$parents = $this->parents;
$parents[] = 'foo';
$this->assertFalse(NestedArray::keyExists($this->form, $parents), 'Non-existing nested key not found.');
}
/**
* Tests NestedArray::mergeDeepArray().
*
* @covers ::mergeDeep
* @covers ::mergeDeepArray
*/
public function testMergeDeepArray() {
$link_options_1 = array(
'fragment' => 'x',
'attributes' => array('title' => 'X', 'class' => array('a', 'b')),
'language' => 'en',
);
$link_options_2 = array(
'fragment' => 'y',
'attributes' => array('title' => 'Y', 'class' => array('c', 'd')),
'absolute' => TRUE,
);
$expected = array(
'fragment' => 'y',
'attributes' => array('title' => 'Y', 'class' => array('a', 'b', 'c', 'd')),
'language' => 'en',
'absolute' => TRUE,
);
$this->assertSame($expected, NestedArray::mergeDeepArray(array($link_options_1, $link_options_2)), 'NestedArray::mergeDeepArray() returned a properly merged array.');
// Test wrapper function, NestedArray::mergeDeep().
$this->assertSame($expected, NestedArray::mergeDeep($link_options_1, $link_options_2), 'NestedArray::mergeDeep() returned a properly merged array.');
}
/**
* Tests that arrays with implicit keys are appended, not merged.
*
* @covers ::mergeDeepArray
*/
public function testMergeImplicitKeys() {
$a = array(
'subkey' => array('X', 'Y'),
);
$b = array(
'subkey' => array('X'),
);
// Drupal core behavior.
$expected = array(
'subkey' => array('X', 'Y', 'X'),
);
$actual = NestedArray::mergeDeepArray(array($a, $b));
$this->assertSame($expected, $actual, 'drupal_array_merge_deep() creates new numeric keys in the implicit sequence.');
}
/**
* Tests that even with explicit keys, values are appended, not merged.
*
* @covers ::mergeDeepArray
*/
public function testMergeExplicitKeys() {
$a = array(
'subkey' => array(
0 => 'A',
1 => 'B',
),
);
$b = array(
'subkey' => array(
0 => 'C',
1 => 'D',
),
);
// Drupal core behavior.
$expected = array(
'subkey' => array(
0 => 'A',
1 => 'B',
2 => 'C',
3 => 'D',
),
);
$actual = NestedArray::mergeDeepArray(array($a, $b));
$this->assertSame($expected, $actual, 'drupal_array_merge_deep() creates new numeric keys in the explicit sequence.');
}
/**
* Tests that array keys values on the first array are ignored when merging.
*
* Even if the initial ordering would place the data from the second array
* before those in the first one, they are still appended, and the keys on
* the first array are deleted and regenerated.
*
* @covers ::mergeDeepArray
*/
public function testMergeOutOfSequenceKeys() {
$a = array(
'subkey' => array(
10 => 'A',
30 => 'B',
),
);
$b = array(
'subkey' => array(
20 => 'C',
0 => 'D',
),
);
// Drupal core behavior.
$expected = array(
'subkey' => array(
0 => 'A',
1 => 'B',
2 => 'C',
3 => 'D',
),
);
$actual = NestedArray::mergeDeepArray(array($a, $b));
$this->assertSame($expected, $actual, 'drupal_array_merge_deep() ignores numeric key order when merging.');
}
}

View file

@ -0,0 +1,163 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Component\Utility\NumberTest.
*
* @see \Drupal\Component\Utility\Number
*/
namespace Drupal\Tests\Component\Utility;
use Drupal\Component\Utility\Number;
use Drupal\Tests\UnitTestCase;
/**
* Tests number manipulation utilities.
*
* @group Utility
*
* @coversDefaultClass \Drupal\Component\Utility\Number
*/
class NumberTest extends UnitTestCase {
/**
* Tests Number::validStep() without offset.
*
* @dataProvider providerTestValidStep
* @covers ::validStep
*
* @param numeric $value
* The value argument for Number::validStep().
* @param numeric $step
* The step argument for Number::validStep().
* @param bool $expected
* Expected return value from Number::validStep().
*/
public function testValidStep($value, $step, $expected) {
$return = Number::validStep($value, $step);
$this->assertEquals($expected, $return);
}
/**
* Tests Number::validStep() with offset.
*
* @dataProvider providerTestValidStepOffset
* @covers ::validStep
*
* @param numeric $value
* The value argument for Number::validStep().
* @param numeric $step
* The step argument for Number::validStep().
* @param numeric $offset
* The offset argument for Number::validStep().
* @param bool $expected
* Expected return value from Number::validStep().
*/
public function testValidStepOffset($value, $step, $offset, $expected) {
$return = Number::validStep($value, $step, $offset);
$this->assertEquals($expected, $return);
}
/**
* Provides data for self::testNumberStep().
*
* @see \Drupal\Tests\Component\Utility\Number::testValidStep
*/
public static function providerTestValidStep() {
return array(
// Value and step equal.
array(10.3, 10.3, TRUE),
// Valid integer steps.
array(42, 21, TRUE),
array(42, 3, TRUE),
// Valid float steps.
array(42, 10.5, TRUE),
array(1, 1/3, TRUE),
array(-100, 100/7, TRUE),
array(1000, -10, TRUE),
// Valid and very small float steps.
array(1000.12345, 1e-10, TRUE),
array(3.9999999999999, 1e-13, TRUE),
// Invalid integer steps.
array(100, 30, FALSE),
array(-10, 4, FALSE),
// Invalid float steps.
array(6, 5/7, FALSE),
array(10.3, 10.25, FALSE),
// Step mismatches very close to being valid.
array(70 + 9e-7, 10 + 9e-7, FALSE),
array(1936.5, 3e-8, FALSE),
);
}
/**
* Data provider for \Drupal\Test\Component\Utility\NumberTest::testValidStepOffset().
*
* @see \Drupal\Test\Component\Utility\NumberTest::testValidStepOffset()
*/
public static function providerTestValidStepOffset() {
return array(
// Try obvious fits.
array(11.3, 10.3, 1, TRUE),
array(100, 10, 50, TRUE),
array(-100, 90/7, -10, TRUE),
array(2/7 + 5/9, 1/7, 5/9, TRUE),
// Ensure a small offset is still invalid.
array(10.3, 10.3, 0.0001, FALSE),
array(1/5, 1/7, 1/11, FALSE),
// Try negative values and offsets.
array(1000, 10, -5, FALSE),
array(-10, 4, 0, FALSE),
array(-10, 4, -4, FALSE),
);
}
/**
* Tests the alphadecimal conversion functions.
*
* @dataProvider providerTestConversions
* @covers ::intToAlphadecimal
* @covers ::alphadecimalToInt
*
* @param int $value
* The integer value.
* @param string $expected
* The expected alphadecimal value.
*/
public function testConversions($value, $expected) {
$this->assertSame(Number::intToAlphadecimal($value), $expected);
$this->assertSame($value, Number::alphadecimalToInt($expected));
}
/**
* Data provider for testConversions().
*
* @see testConversions()
*
* @return array
* An array containing:
* - The integer value.
* - The alphadecimal value.
*/
public function providerTestConversions() {
return array(
array(0, '00'),
array(1, '01'),
array(10, '0a'),
array(20, '0k'),
array(35, '0z'),
array(36, '110'),
array(100, '12s'),
);
}
}

View file

@ -0,0 +1,172 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Component\Utility\RandomTest.
*/
namespace Drupal\Tests\Component\Utility;
use Drupal\Component\Utility\Random;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Tests\UnitTestCase;
/**
* Tests random data generation.
*
* @group Utility
*
* @coversDefaultClass \Drupal\Component\Utility\Random
*/
class RandomTest extends UnitTestCase {
/**
* The first random string passed to the test callback.
*
* @see \Drupal\Tests\Component\Utility\RandomTest::_RandomStringValidate()
*
* @var string
*/
protected $firstStringGenerated = '';
/**
* Tests unique random string generation.
*
* @covers ::string
*/
public function testRandomStringUniqueness() {
$strings = array();
$random = new Random();
for ($i = 0; $i <= 50; $i++) {
$str = $random->string(1, TRUE);
$this->assertFalse(isset($strings[$str]), SafeMarkup::format('Generated duplicate random string !string', array('!string' => $str)));
$strings[$str] = TRUE;
}
}
/**
* Tests unique random name generation.
*
* @covers ::name
*/
public function testRandomNamesUniqueness() {
$names = array();
$random = new Random();
for ($i = 0; $i <= 10; $i++) {
$str = $random->name(1, TRUE);
$this->assertFalse(isset($names[$str]), SafeMarkup::format('Generated duplicate random name !name', array('!name' => $str)));
$names[$str] = TRUE;
}
}
/**
* Tests infinite loop prevention whilst generating random names.
*
* @covers ::name
* @expectedException \RuntimeException
*/
public function testRandomNameException() {
// There are fewer than 100 possibilities so an exception should occur to
// prevent infinite loops.
$random = new Random();
for ($i = 0; $i <= 100; $i++) {
$str = $random->name(1, TRUE);
$names[$str] = TRUE;
}
}
/**
* Tests infinite loop prevention whilst generating random strings.
*
* @covers ::string
* @expectedException \RuntimeException
*/
public function testRandomStringException() {
// There are fewer than 100 possibilities so an exception should occur to
// prevent infinite loops.
$random = new Random();
for ($i = 0; $i <= 100; $i++) {
$str = $random->string(1, TRUE);
$names[$str] = TRUE;
}
}
/**
* Tests random name generation if uniqueness is not enforced.
*
* @covers ::name
*/
public function testRandomNameNonUnique() {
// There are fewer than 100 possibilities if we were forcing uniqueness so
// exception would occur.
$random = new Random();
for ($i = 0; $i <= 100; $i++) {
$random->name(1);
}
$this->assertTrue(TRUE, 'No exception thrown when uniqueness is not enforced.');
}
/**
* Tests random string if uniqueness is not enforced.
*
* @covers ::string
*/
public function testRandomStringNonUnique() {
// There are fewer than 100 possibilities if we were forcing uniqueness so
// exception would occur.
$random = new Random();
for ($i = 0; $i <= 100; $i++) {
$random->string(1);
}
$this->assertTrue(TRUE, 'No exception thrown when uniqueness is not enforced.');
}
/**
* Tests random object generation to ensure the expected number of properties.
*
* @covers ::object
*/
public function testRandomObject() {
// For values of 0 and 1 \Drupal\Component\Utility\Random::object() will
// have different execution paths.
$random = new Random();
for ($i = 0; $i <= 1; $i++) {
$obj = $random->object($i);
$this->assertEquals($i, count(get_object_vars($obj)), 'Generated random object has expected number of properties');
}
}
/**
* Tests random string validation callbacks.
*
* @covers ::string
*/
public function testRandomStringValidator() {
$random = new Random();
$this->firstStringGenerated = '';
$str = $random->string(1, TRUE, array($this, '_RandomStringValidate'));
$this->assertNotEquals($this->firstStringGenerated, $str);
}
/**
* Callback for random string validation.
*
* @see \Drupal\Component\Utility\Random::name()
* @see \Drupal\Tests\Component\Utility\RandomTest::testRandomStringValidator()
*
* @param string $string
* The random string to validate.
*
* @return bool
* TRUE if the random string is valid, FALSE if not.
*/
public function _RandomStringValidate($string) {
// Return FALSE for the first generated string and any string that is the
// same, as the test expects a different string to be returned.
if (empty($this->firstStringGenerated) || $string == $this->firstStringGenerated) {
$this->firstStringGenerated = $string;
return FALSE;
}
return TRUE;
}
}

View file

@ -0,0 +1,286 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Component\Utility\SafeMarkupTest.
*/
namespace Drupal\Tests\Component\Utility;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Tests\UnitTestCase;
/**
* Tests marking strings as safe.
*
* @group Utility
* @coversDefaultClass \Drupal\Component\Utility\SafeMarkup
*/
class SafeMarkupTest extends UnitTestCase {
/**
* Tests SafeMarkup::set() and SafeMarkup::isSafe().
*
* @dataProvider providerSet
*
* @param string $text
* The text or object to provide to SafeMarkup::set().
* @param string $message
* The message to provide as output for the test.
*
* @covers ::set
*/
public function testSet($text, $message) {
$returned = SafeMarkup::set($text);
$this->assertTrue(is_string($returned), 'The return value of SafeMarkup::set() is really a string');
$this->assertEquals($returned, $text, 'The passed in value should be equal to the string value according to PHP');
$this->assertTrue(SafeMarkup::isSafe($text), $message);
$this->assertTrue(SafeMarkup::isSafe($returned), 'The return value has been marked as safe');
}
/**
* Data provider for testSet().
*
* @see testSet()
*/
public function providerSet() {
// Checks that invalid multi-byte sequences are rejected.
$tests[] = array("Foo\xC0barbaz", '', 'SafeMarkup::checkPlain() rejects invalid sequence "Foo\xC0barbaz"', TRUE);
$tests[] = array("Fooÿñ", 'SafeMarkup::set() accepts valid sequence "Fooÿñ"');
$tests[] = array(new TextWrapper("Fooÿñ"), 'SafeMarkup::set() accepts valid sequence "Fooÿñ" in an object implementing __toString()');
$tests[] = array("<div>", 'SafeMarkup::set() accepts HTML');
return $tests;
}
/**
* Tests SafeMarkup::set() and SafeMarkup::isSafe() with different providers.
*
* @covers ::isSafe
*/
public function testStrategy() {
$returned = SafeMarkup::set('string0', 'html');
$this->assertTrue(SafeMarkup::isSafe($returned), 'String set with "html" provider is safe for default (html)');
$returned = SafeMarkup::set('string1', 'all');
$this->assertTrue(SafeMarkup::isSafe($returned), 'String set with "all" provider is safe for default (html)');
$returned = SafeMarkup::set('string2', 'css');
$this->assertFalse(SafeMarkup::isSafe($returned), 'String set with "css" provider is not safe for default (html)');
$returned = SafeMarkup::set('string3');
$this->assertFalse(SafeMarkup::isSafe($returned, 'all'), 'String set with "html" provider is not safe for "all"');
}
/**
* Tests SafeMarkup::isSafe() with different objects.
*
* @covers ::isSafe
*/
public function testIsSafe() {
$safe_string = $this->getMock('\Drupal\Component\Utility\SafeStringInterface');
$this->assertTrue(SafeMarkup::isSafe($safe_string));
$string_object = new SafeMarkupTestString('test');
$this->assertFalse(SafeMarkup::isSafe($string_object));
}
/**
* Tests SafeMarkup::setMultiple().
*
* @covers ::setMultiple
*/
public function testSetMultiple() {
$texts = array(
'multistring0' => array('html' => TRUE),
'multistring1' => array('all' => TRUE),
);
SafeMarkup::setMultiple($texts);
foreach ($texts as $string => $providers) {
$this->assertTrue(SafeMarkup::isSafe($string), 'The value has been marked as safe for html');
}
}
/**
* Tests SafeMarkup::setMultiple().
*
* Only TRUE may be passed in as the value.
*
* @covers ::setMultiple
*
* @expectedException \UnexpectedValueException
*/
public function testInvalidSetMultiple() {
$texts = array(
'invalidstring0' => array('html' => 1),
);
SafeMarkup::setMultiple($texts);
}
/**
* Tests SafeMarkup::checkPlain().
*
* @dataProvider providerCheckPlain
* @covers ::checkPlain
*
* @param string $text
* The text to provide to SafeMarkup::checkPlain().
* @param string $expected
* The expected output from the function.
* @param string $message
* The message to provide as output for the test.
* @param bool $ignorewarnings
* Whether or not to ignore PHP 5.3+ invalid multibyte sequence warnings.
*/
function testCheckPlain($text, $expected, $message, $ignorewarnings = FALSE) {
$result = $ignorewarnings ? @SafeMarkup::checkPlain($text) : SafeMarkup::checkPlain($text);
$this->assertEquals($expected, $result, $message);
}
/**
* Data provider for testCheckPlain().
*
* @see testCheckPlain()
*/
function providerCheckPlain() {
// Checks that invalid multi-byte sequences are rejected.
$tests[] = array("Foo\xC0barbaz", '', 'SafeMarkup::checkPlain() rejects invalid sequence "Foo\xC0barbaz"', TRUE);
$tests[] = array("\xc2\"", '', 'SafeMarkup::checkPlain() rejects invalid sequence "\xc2\""', TRUE);
$tests[] = array("Fooÿñ", "Fooÿñ", 'SafeMarkup::checkPlain() accepts valid sequence "Fooÿñ"');
// Checks that special characters are escaped.
$tests[] = array("<script>", '&lt;script&gt;', 'SafeMarkup::checkPlain() escapes &lt;script&gt;');
$tests[] = array('<>&"\'', '&lt;&gt;&amp;&quot;&#039;', 'SafeMarkup::checkPlain() escapes reserved HTML characters.');
return $tests;
}
/**
* Tests string formatting with SafeMarkup::format().
*
* @dataProvider providerFormat
* @covers ::format
*
* @param string $string
* The string to run through SafeMarkup::format().
* @param string $args
* The arguments to pass into SafeMarkup::format().
* @param string $expected
* The expected result from calling the function.
* @param string $message
* The message to display as output to the test.
* @param bool $expected_is_safe
* Whether the result is expected to be safe for HTML display.
*/
function testFormat($string, $args, $expected, $message, $expected_is_safe) {
$result = SafeMarkup::format($string, $args);
$this->assertEquals($expected, $result, $message);
$this->assertEquals($expected_is_safe, SafeMarkup::isSafe($result), 'SafeMarkup::format correctly sets the result as safe or not safe.');
}
/**
* Data provider for testFormat().
*
* @see testFormat()
*/
function providerFormat() {
$tests[] = array('Simple text', array(), 'Simple text', 'SafeMarkup::format leaves simple text alone.', TRUE);
$tests[] = array('Escaped text: @value', array('@value' => '<script>'), 'Escaped text: &lt;script&gt;', 'SafeMarkup::format replaces and escapes string.', TRUE);
$tests[] = array('Escaped text: @value', array('@value' => SafeMarkup::set('<span>Safe HTML</span>')), 'Escaped text: <span>Safe HTML</span>', 'SafeMarkup::format does not escape an already safe string.', TRUE);
$tests[] = array('Placeholder text: %value', array('%value' => '<script>'), 'Placeholder text: <em class="placeholder">&lt;script&gt;</em>', 'SafeMarkup::format replaces, escapes and themes string.', TRUE);
$tests[] = array('Placeholder text: %value', array('%value' => SafeMarkup::set('<span>Safe HTML</span>')), 'Placeholder text: <em class="placeholder"><span>Safe HTML</span></em>', 'SafeMarkup::format does not escape an already safe string themed as a placeholder.', TRUE);
$tests[] = array('Verbatim text: !value', array('!value' => '<script>'), 'Verbatim text: <script>', 'SafeMarkup::format replaces verbatim string as-is.', FALSE);
$tests[] = array('Verbatim text: !value', array('!value' => SafeMarkup::set('<span>Safe HTML</span>')), 'Verbatim text: <span>Safe HTML</span>', 'SafeMarkup::format replaces verbatim string as-is.', TRUE);
return $tests;
}
/**
* Tests SafeMarkup::placeholder().
*
* @covers ::placeholder
*/
function testPlaceholder() {
$this->assertEquals('<em class="placeholder">Some text</em>', SafeMarkup::placeholder('Some text'));
}
/**
* Tests SafeMarkup::replace().
*
* @dataProvider providerReplace
* @covers ::replace
*/
public function testReplace($search, $replace, $subject, $expected, $is_safe) {
$result = SafeMarkup::replace($search, $replace, $subject);
$this->assertEquals($expected, $result);
$this->assertEquals($is_safe, SafeMarkup::isSafe($result));
}
/**
* Data provider for testReplace().
*
* @see testReplace()
*/
public function providerReplace() {
$tests = [];
// Subject unsafe.
$tests[] = [
'<placeholder>',
SafeMarkup::set('foo'),
'<placeholder>bazqux',
'foobazqux',
FALSE,
];
// All safe.
$tests[] = [
'<placeholder>',
SafeMarkup::set('foo'),
SafeMarkup::set('<placeholder>barbaz'),
'foobarbaz',
TRUE,
];
// Safe subject, but should result in unsafe string because replacement is
// unsafe.
$tests[] = [
'<placeholder>',
'fubar',
SafeMarkup::set('<placeholder>barbaz'),
'fubarbarbaz',
FALSE,
];
// Array with all safe.
$tests[] = [
['<placeholder1>', '<placeholder2>', '<placeholder3>'],
[SafeMarkup::set('foo'), SafeMarkup::set('bar'), SafeMarkup::set('baz')],
SafeMarkup::set('<placeholder1><placeholder2><placeholder3>'),
'foobarbaz',
TRUE,
];
// Array with unsafe replacement.
$tests[] = [
['<placeholder1>', '<placeholder2>', '<placeholder3>',],
[SafeMarkup::set('bar'), SafeMarkup::set('baz'), 'qux'],
SafeMarkup::set('<placeholder1><placeholder2><placeholder3>'),
'barbazqux',
FALSE,
];
return $tests;
}
}
class SafeMarkupTestString {
protected $string;
public function __construct($string) {
$this->string = $string;
}
public function __toString() {
return $this->string;
}
}

View file

@ -0,0 +1,328 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Component\Utility\SortArrayTest.
*/
namespace Drupal\Tests\Component\Utility;
use Drupal\Tests\UnitTestCase;
use Drupal\Component\Utility\SortArray;
/**
* Tests the SortArray component.
*
* @group Utility
*
* @coversDefaultClass \Drupal\Component\Utility\SortArray
*/
class SortArrayTest extends UnitTestCase {
/**
* Tests SortArray::sortByWeightElement() input against expected output.
*
* @dataProvider providerSortByWeightElement
* @covers ::sortByWeightElement
* @covers ::sortByKeyInt
*
* @param array $a
* The first input array for the SortArray::sortByWeightElement() method.
* @param array $b
* The second input array for the SortArray::sortByWeightElement().
* @param integer $expected
* The expected output from calling the method.
*/
public function testSortByWeightElement($a, $b, $expected) {
$result = SortArray::sortByWeightElement($a, $b);
$this->assertBothNegativePositiveOrZero($expected, $result);
}
/**
* Data provider for SortArray::sortByWeightElement().
*
* @return array
* An array of tests, matching the parameter inputs for
* testSortByWeightElement.
*
* @see \Drupal\Tests\Component\Utility\SortArrayTest::testSortByWeightElement()
*/
public function providerSortByWeightElement() {
$tests = array();
// Weights set and equal.
$tests[] = array(
array('weight' => 1),
array('weight' => 1),
0
);
// Weights set and $a is less (lighter) than $b.
$tests[] = array(
array('weight' => 1),
array('weight' => 2),
-1
);
// Weights set and $a is greater (heavier) than $b.
$tests[] = array(
array('weight' => 2),
array('weight' => 1),
1
);
// Weights not set.
$tests[] = array(
array(),
array(),
0
);
// Weights for $b not set.
$tests[] = array(
array('weight' => 1),
array(),
1
);
// Weights for $a not set.
$tests[] = array(
array(),
array('weight' => 1),
-1
);
return $tests;
}
/**
* Tests SortArray::sortByWeightProperty() input against expected output.
*
* @dataProvider providerSortByWeightProperty
* @covers ::sortByWeightProperty
* @covers ::sortByKeyInt
*
* @param array $a
* The first input array for the SortArray::sortByWeightProperty() method.
* @param array $b
* The second input array for the SortArray::sortByWeightProperty().
* @param integer $expected
* The expected output from calling the method.
*/
public function testSortByWeightProperty($a, $b, $expected) {
$result = SortArray::sortByWeightProperty($a, $b);
$this->assertBothNegativePositiveOrZero($expected, $result);
}
/**
* Data provider for SortArray::sortByWeightProperty().
*
* @return array
* An array of tests, matching the parameter inputs for
* testSortByWeightProperty.
*
* @see \Drupal\Tests\Component\Utility\SortArrayTest::testSortByWeightProperty()
*/
public function providerSortByWeightProperty() {
$tests = array();
// Weights set and equal.
$tests[] = array(
array('#weight' => 1),
array('#weight' => 1),
0
);
// Weights set and $a is less (lighter) than $b.
$tests[] = array(
array('#weight' => 1),
array('#weight' => 2),
-1
);
// Weights set and $a is greater (heavier) than $b.
$tests[] = array(
array('#weight' => 2),
array('#weight' => 1),
1
);
// Weights not set.
$tests[] = array(
array(),
array(),
0
);
// Weights for $b not set.
$tests[] = array(
array('#weight' => 1),
array(),
1
);
// Weights for $a not set.
$tests[] = array(
array(),
array('#weight' => 1),
-1
);
return $tests;
}
/**
* Tests SortArray::sortByTitleElement() input against expected output.
*
* @dataProvider providerSortByTitleElement
* @covers ::sortByTitleElement
* @covers ::sortByKeyString
*
* @param array $a
* The first input item for comparison.
* @param array $b
* The second item for comparison.
* @param integer $expected
* The expected output from calling the method.
*/
public function testSortByTitleElement($a, $b, $expected) {
$result = SortArray::sortByTitleElement($a, $b);
$this->assertBothNegativePositiveOrZero($expected, $result);
}
/**
* Data provider for SortArray::sortByTitleElement().
*
* @return array
* An array of tests, matching the parameter inputs for
* testSortByTitleElement.
*
* @see \Drupal\Tests\Component\Utility\SortArrayTest::testSortByTitleElement()
*/
public function providerSortByTitleElement() {
$tests = array();
// Titles set and equal.
$tests[] = array(
array('title' => 'test'),
array('title' => 'test'),
0
);
// Title $a not set.
$tests[] = array(
array(),
array('title' => 'test'),
-4
);
// Title $b not set.
$tests[] = array(
array('title' => 'test'),
array(),
4
);
// Titles set but not equal.
$tests[] = array(
array('title' => 'test'),
array('title' => 'testing'),
-1
);
// Titles set but not equal.
$tests[] = array(
array('title' => 'testing'),
array('title' => 'test'),
1
);
return $tests;
}
/**
* Tests SortArray::sortByTitleProperty() input against expected output.
*
* @dataProvider providerSortByTitleProperty
* @covers ::sortByTitleProperty
* @covers ::sortByKeyString
*
* @param array $a
* The first input item for comparison.
* @param array $b
* The second item for comparison.
* @param integer $expected
* The expected output from calling the method.
*/
public function testSortByTitleProperty($a, $b, $expected) {
$result = SortArray::sortByTitleProperty($a, $b);
$this->assertBothNegativePositiveOrZero($expected, $result);
}
/**
* Data provider for SortArray::sortByTitleProperty().
*
* @return array
* An array of tests, matching the parameter inputs for
* testSortByTitleProperty.
*
* @see \Drupal\Tests\Component\Utility\SortArrayTest::testSortByTitleProperty()
*/
public function providerSortByTitleProperty() {
$tests = array();
// Titles set and equal.
$tests[] = array(
array('#title' => 'test'),
array('#title' => 'test'),
0
);
// Title $a not set.
$tests[] = array(
array(),
array('#title' => 'test'),
-4
);
// Title $b not set.
$tests[] = array(
array('#title' => 'test'),
array(),
4
);
// Titles set but not equal.
$tests[] = array(
array('#title' => 'test'),
array('#title' => 'testing'),
-1
);
// Titles set but not equal.
$tests[] = array(
array('#title' => 'testing'),
array('#title' => 'test'),
1
);
return $tests;
}
/**
* Asserts that numbers are either both negative, both positive or both zero.
*
* The exact values returned by comparison functions differ between PHP
* versions and are considered an "implementation detail".
*
* @param int $expected
* Expected comparison function return value.
* @param int $result
* Actual comparison function return value.
*/
protected function assertBothNegativePositiveOrZero($expected, $result) {
$this->assertTrue(is_numeric($expected) && is_numeric($result), 'Parameters are numeric.');
$this->assertTrue(($expected < 0 && $result < 0) || ($expected > 0 && $result > 0) || ($expected === 0 && $result === 0), 'Numbers are either both negative, both positive or both zero.');
}
}

View file

@ -0,0 +1,39 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Component\Utility\TextWrapper.
*/
namespace Drupal\Tests\Component\Utility;
/**
* Used by SafeMarkupTest to test that a class with a __toString() method works.
*/
class TextWrapper {
/**
* The text value.
*
* @var string
*/
protected $text = '';
/**
* Constructs a \Drupal\Tests\Component\Utility\TextWrapper
*
* @param string $text
*/
public function __construct($text) {
$this->text = $text;
}
/**
* Magic method
*
* @return string
*/
public function __toString() {
return $this->text;
}
}

View file

@ -0,0 +1,73 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Component\Utility\TimerTest.
*/
namespace Drupal\Tests\Component\Utility;
use Drupal\Tests\UnitTestCase;
use Drupal\Component\Utility\Timer;
/**
* Tests the Timer system.
*
* @group Utility
*
* @coversDefaultClass \Drupal\Component\Utility\Timer
*/
class TimerTest extends UnitTestCase {
/**
* Tests Timer::read() time accumulation accuracy across multiple restarts.
*
* @covers ::start
* @covers ::stop
* @covers ::read
*/
public function testTimer() {
Timer::start('test');
usleep(5000);
$value = Timer::read('test');
usleep(5000);
$value2 = Timer::read('test');
usleep(5000);
$value3 = Timer::read('test');
usleep(5000);
$value4 = Timer::read('test');
// Although we sleep for 5 milliseconds, we should test that at least 4 ms
// have past because usleep() is not reliable on Windows. See
// http://php.net/manual/en/function.usleep.php for more information. The
// purpose of the test to validate that the Timer class can measure elapsed
// time not the granularity of usleep() on a particular OS.
$this->assertGreaterThanOrEqual(4, $value, 'Timer failed to measure at least 4 milliseconds of sleeping while running.');
$this->assertGreaterThanOrEqual($value + 4, $value2, 'Timer failed to measure at least 8 milliseconds of sleeping while running.');
$this->assertGreaterThanOrEqual($value2 + 4, $value3, 'Timer failed to measure at least 12 milliseconds of sleeping while running.');
$this->assertGreaterThanOrEqual($value3 + 4, $value4, 'Timer failed to measure at least 16 milliseconds of sleeping while running.');
// Stop the timer.
$value5 = Timer::stop('test');
$this->assertGreaterThanOrEqual($value4, $value5['time'], 'Timer measured after stopping was not greater than last measurement.');
// Read again.
$value6 = Timer::read('test');
$this->assertEquals($value5['time'], $value6, 'Timer measured after stopping was not equal to the stopped time.');
// Restart.
Timer::start('test');
usleep(5000);
$value7 = Timer::read('test');
$this->assertGreaterThanOrEqual($value6 + 4, $value7, 'Timer failed to measure at least 16 milliseconds of sleeping while running.');
// Stop again.
$value8 = Timer::stop('test');
$value9 = Timer::read('test');
$this->assertEquals($value8['time'], $value9, 'Timer measured after stopping not equal to stop time.');
}
}

View file

@ -0,0 +1,532 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Component\Utility\UnicodeTest.
*/
namespace Drupal\Tests\Component\Utility;
use Drupal\Tests\UnitTestCase;
use Drupal\Component\Utility\Unicode;
/**
* Test unicode handling features implemented in Unicode component.
*
* @group Utility
*
* @coversDefaultClass \Drupal\Component\Utility\Unicode
*/
class UnicodeTest extends UnitTestCase {
/**
* {@inheritdoc}
*
* @covers ::check
*/
public function setUp() {
// Initialize unicode component.
Unicode::check();
}
/**
* Getting and settings the multibyte environment status.
*
* @dataProvider providerTestStatus
* @covers ::getStatus
* @covers ::setStatus
*/
public function testStatus($value, $expected, $invalid = FALSE) {
if ($invalid) {
$this->setExpectedException('InvalidArgumentException');
}
Unicode::setStatus($value);
$this->assertEquals($expected, Unicode::getStatus());
}
/**
* Data provider for testStatus().
*
* @see testStatus()
*
* @return array
* An array containing:
* - The status value to set.
* - The status value to expect after setting the new value.
* - (optional) Boolean indicating invalid status. Defaults to FALSE.
*/
public function providerTestStatus() {
return array(
array(Unicode::STATUS_SINGLEBYTE, Unicode::STATUS_SINGLEBYTE),
array(rand(10, 100), Unicode::STATUS_SINGLEBYTE, TRUE),
array(rand(10, 100), Unicode::STATUS_SINGLEBYTE, TRUE),
array(Unicode::STATUS_MULTIBYTE, Unicode::STATUS_MULTIBYTE),
array(rand(10, 100), Unicode::STATUS_MULTIBYTE, TRUE),
array(Unicode::STATUS_ERROR, Unicode::STATUS_ERROR),
array(Unicode::STATUS_MULTIBYTE, Unicode::STATUS_MULTIBYTE),
);
}
/**
* Tests multibyte encoding and decoding.
*
* @dataProvider providerTestMimeHeader
* @covers ::mimeHeaderEncode
* @covers ::mimeHeaderDecode
*/
public function testMimeHeader($value, $encoded) {
$this->assertEquals($encoded, Unicode::mimeHeaderEncode($value));
$this->assertEquals($value, Unicode::mimeHeaderDecode($encoded));
}
/**
* Data provider for testMimeHeader().
*
* @see testMimeHeader()
*
* @return array
* An array containing a string and its encoded value.
*/
public function providerTestMimeHeader() {
return array(
array('tést.txt', '=?UTF-8?B?dMOpc3QudHh0?='),
// Simple ASCII characters.
array('ASCII', 'ASCII'),
);
}
/**
* Tests multibyte strtolower.
*
* @dataProvider providerStrtolower
* @covers ::strtolower
* @covers ::caseFlip
*/
public function testStrtolower($text, $expected, $multibyte = FALSE) {
$status = $multibyte ? Unicode::STATUS_MULTIBYTE : Unicode::STATUS_SINGLEBYTE;
Unicode::setStatus($status);
$this->assertEquals($expected, Unicode::strtolower($text));
}
/**
* Data provider for testStrtolower().
*
* @see testStrtolower()
*
* @return array
* An array containing a string, its lowercase version and whether it should
* be processed as multibyte.
*/
public function providerStrtolower() {
$cases = array(
array('tHe QUIcK bRoWn', 'the quick brown'),
array('FrançAIS is ÜBER-åwesome', 'français is über-åwesome'),
);
foreach ($cases as $case) {
// Test the same string both in multibyte and singlebyte conditions.
array_push($case, TRUE);
$cases[] = $case;
}
// Add a multibyte string.
$cases[] = array('ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΣὨ', 'αβγδεζηθικλμνξοσὠ', TRUE);
return $cases;
}
/**
* Tests multibyte strtoupper.
*
* @dataProvider providerStrtoupper
* @covers ::strtoupper
* @covers ::caseFlip
*/
public function testStrtoupper($text, $expected, $multibyte = FALSE) {
$status = $multibyte ? Unicode::STATUS_MULTIBYTE : Unicode::STATUS_SINGLEBYTE;
Unicode::setStatus($status);
$this->assertEquals($expected, Unicode::strtoupper($text));
}
/**
* Data provider for testStrtoupper().
*
* @see testStrtoupper()
*
* @return array
* An array containing a string, its uppercase version and whether it should
* be processed as multibyte.
*/
public function providerStrtoupper() {
$cases = array(
array('tHe QUIcK bRoWn', 'THE QUICK BROWN'),
array('FrançAIS is ÜBER-åwesome', 'FRANÇAIS IS ÜBER-ÅWESOME'),
);
foreach ($cases as $case) {
// Test the same string both in multibyte and singlebyte conditions.
array_push($case, TRUE);
$cases[] = $case;
}
// Add a multibyte string.
$cases[] = array('αβγδεζηθικλμνξοσὠ', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΣὨ', TRUE);
return $cases;
}
/**
* Tests multibyte ucfirst.
*
* @dataProvider providerUcfirst
* @covers ::ucfirst
*/
public function testUcfirst($text, $expected) {
$this->assertEquals($expected, Unicode::ucfirst($text));
}
/**
* Data provider for testUcfirst().
*
* @see testUcfirst()
*
* @return array
* An array containing a string and its uppercase first version.
*/
public function providerUcfirst() {
return array(
array('tHe QUIcK bRoWn', 'THe QUIcK bRoWn'),
array('françAIS', 'FrançAIS'),
array('über', 'Über'),
array('åwesome', 'Åwesome'),
// A multibyte string.
array('σion', 'Σion'),
);
}
/**
* Tests multibyte lcfirst.
*
* @dataProvider providerLcfirst
* @covers ::lcfirst
*/
public function testLcfirst($text, $expected, $multibyte = FALSE) {
$status = $multibyte ? Unicode::STATUS_MULTIBYTE : Unicode::STATUS_SINGLEBYTE;
Unicode::setStatus($status);
$this->assertEquals($expected, Unicode::lcfirst($text));
}
/**
* Data provider for testLcfirst().
*
* @see testLcfirst()
*
* @return array
* An array containing a string, its lowercase version and whether it should
* be processed as multibyte.
*/
public function providerLcfirst() {
return array(
array('tHe QUIcK bRoWn', 'tHe QUIcK bRoWn'),
array('FrançAIS is ÜBER-åwesome', 'françAIS is ÜBER-åwesome'),
array('Über', 'über'),
array('Åwesome', 'åwesome'),
// Add a multibyte string.
array('ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΣὨ', 'αΒΓΔΕΖΗΘΙΚΛΜΝΞΟΣὨ', TRUE),
);
}
/**
* Tests multibyte ucwords.
*
* @dataProvider providerUcwords
* @covers ::ucwords
*/
public function testUcwords($text, $expected, $multibyte = FALSE) {
$status = $multibyte ? Unicode::STATUS_MULTIBYTE : Unicode::STATUS_SINGLEBYTE;
Unicode::setStatus($status);
$this->assertEquals($expected, Unicode::ucwords($text));
}
/**
* Data provider for testUcwords().
*
* @see testUcwords()
*
* @return array
* An array containing a string, its capitalized version and whether it should
* be processed as multibyte.
*/
public function providerUcwords() {
return array(
array('tHe QUIcK bRoWn', 'THe QUIcK BRoWn'),
array('françAIS', 'FrançAIS'),
array('über', 'Über'),
array('åwesome', 'Åwesome'),
// Make sure we don't mangle extra spaces.
array('frànçAIS is über-åwesome', 'FrànçAIS Is Über-Åwesome'),
// Add a multibyte string.
array('σion', 'Σion', TRUE),
);
}
/**
* Tests multibyte strlen.
*
* @dataProvider providerStrlen
* @covers ::strlen
*/
public function testStrlen($text, $expected) {
// Run through multibyte code path.
Unicode::setStatus(Unicode::STATUS_MULTIBYTE);
$this->assertEquals($expected, Unicode::strlen($text));
// Run through singlebyte code path.
Unicode::setStatus(Unicode::STATUS_SINGLEBYTE);
$this->assertEquals($expected, Unicode::strlen($text));
}
/**
* Data provider for testStrlen().
*
* @see testStrlen()
*
* @return array
* An array containing a string and its length.
*/
public function providerStrlen() {
return array(
array('tHe QUIcK bRoWn', 15),
array('ÜBER-åwesome', 12),
);
}
/**
* Tests multibyte substr.
*
* @dataProvider providerSubstr
* @covers ::substr
*/
public function testSubstr($text, $start, $length, $expected) {
// Run through multibyte code path.
Unicode::setStatus(Unicode::STATUS_MULTIBYTE);
$this->assertEquals($expected, Unicode::substr($text, $start, $length));
// Run through singlebyte code path.
Unicode::setStatus(Unicode::STATUS_SINGLEBYTE);
$this->assertEquals($expected, Unicode::substr($text, $start, $length));
}
/**
* Data provider for testSubstr().
*
* @see testSubstr()
*
* @return array
* An array containing:
* - The string to test.
* - The start number to be processed by substr.
* - The length number to be processed by substr.
* - The expected string result.
*/
public function providerSubstr() {
return array(
array('frànçAIS is über-åwesome', 0, NULL, 'frànçAIS is über-åwesome'),
array('frànçAIS is über-åwesome', 0, 0, ''),
array('frànçAIS is über-åwesome', 0, 1, 'f'),
array('frànçAIS is über-åwesome', 0, 8, 'frànçAIS'),
array('frànçAIS is über-åwesome', 0, 23, 'frànçAIS is über-åwesom'),
array('frànçAIS is über-åwesome', 0, 24, 'frànçAIS is über-åwesome'),
array('frànçAIS is über-åwesome', 0, 25, 'frànçAIS is über-åwesome'),
array('frànçAIS is über-åwesome', 0, 100, 'frànçAIS is über-åwesome'),
array('frànçAIS is über-åwesome', 4, 4, 'çAIS'),
array('frànçAIS is über-åwesome', 1, 0, ''),
array('frànçAIS is über-åwesome', 100, 0, ''),
array('frànçAIS is über-åwesome', -4, 2, 'so'),
array('frànçAIS is über-åwesome', -4, 3, 'som'),
array('frànçAIS is über-åwesome', -4, 4, 'some'),
array('frànçAIS is über-åwesome', -4, 5, 'some'),
array('frànçAIS is über-åwesome', -7, 10, 'åwesome'),
array('frànçAIS is über-åwesome', 5, -10, 'AIS is üb'),
array('frànçAIS is über-åwesome', 0, -10, 'frànçAIS is üb'),
array('frànçAIS is über-åwesome', 0, -1, 'frànçAIS is über-åwesom'),
array('frànçAIS is über-åwesome', -7, -2, 'åweso'),
array('frànçAIS is über-åwesome', -7, -6, 'å'),
array('frànçAIS is über-åwesome', -7, -7, ''),
array('frànçAIS is über-åwesome', -7, -8, ''),
array('...', 0, 2, '..'),
array('以呂波耳・ほへとち。リヌルヲ。', 1, 3, '呂波耳'),
);
}
/**
* Tests multibyte truncate.
*
* @dataProvider providerTruncate
* @covers ::truncate
*/
public function testTruncate($text, $max_length, $expected, $wordsafe = FALSE, $add_ellipsis = FALSE) {
$this->assertEquals($expected, Unicode::truncate($text, $max_length, $wordsafe, $add_ellipsis));
}
/**
* Data provider for testTruncate().
*
* @see testTruncate()
*
* @return array
* An array containing:
* - The string to test.
* - The max length to truncate this string to.
* - The expected string result.
* - (optional) Boolean for the $wordsafe flag. Defaults to FALSE.
* - (optional) Boolean for the $add_ellipsis flag. Defaults to FALSE.
*/
public function providerTruncate() {
return array(
array('frànçAIS is über-åwesome', 24, 'frànçAIS is über-åwesome'),
array('frànçAIS is über-åwesome', 23, 'frànçAIS is über-åwesom'),
array('frànçAIS is über-åwesome', 17, 'frànçAIS is über-'),
array('以呂波耳・ほへとち。リヌルヲ。', 6, '以呂波耳・ほ'),
array('frànçAIS is über-åwesome', 24, 'frànçAIS is über-åwesome', FALSE, TRUE),
array('frànçAIS is über-åwesome', 23, 'frànçAIS is über-åweso…', FALSE, TRUE),
array('frànçAIS is über-åwesome', 17, 'frànçAIS is über…', FALSE, TRUE),
array('123', 1, '…', TRUE, TRUE),
array('123', 2, '1…', TRUE, TRUE),
array('123', 3, '123', TRUE, TRUE),
array('1234', 3, '12…', TRUE, TRUE),
array('1234567890', 10, '1234567890', TRUE, TRUE),
array('12345678901', 10, '123456789…', TRUE, TRUE),
array('12345678901', 11, '12345678901', TRUE, TRUE),
array('123456789012', 11, '1234567890…', TRUE, TRUE),
array('12345 7890', 10, '12345 7890', TRUE, TRUE),
array('12345 7890', 9, '12345…', TRUE, TRUE),
array('123 567 90', 10, '123 567 90', TRUE, TRUE),
array('123 567 901', 10, '123 567…', TRUE, TRUE),
array('Stop. Hammertime.', 17, 'Stop. Hammertime.', TRUE, TRUE),
array('Stop. Hammertime.', 16, 'Stop…', TRUE, TRUE),
array('frànçAIS is über-åwesome', 24, 'frànçAIS is über-åwesome', TRUE, TRUE),
array('frànçAIS is über-åwesome', 23, 'frànçAIS is über…', TRUE, TRUE),
array('frànçAIS is über-åwesome', 17, 'frànçAIS is über…', TRUE, TRUE),
array('¿Dónde está el niño?', 20, '¿Dónde está el niño?', TRUE, TRUE),
array('¿Dónde está el niño?', 19, '¿Dónde está el…', TRUE, TRUE),
array('¿Dónde está el niño?', 13, '¿Dónde está…', TRUE, TRUE),
array('¿Dónde está el niño?', 10, '¿Dónde…', TRUE, TRUE),
array('Help! Help! Help!', 17, 'Help! Help! Help!', TRUE, TRUE),
array('Help! Help! Help!', 16, 'Help! Help!…', TRUE, TRUE),
array('Help! Help! Help!', 15, 'Help! Help!…', TRUE, TRUE),
array('Help! Help! Help!', 14, 'Help! Help!…', TRUE, TRUE),
array('Help! Help! Help!', 13, 'Help! Help!…', TRUE, TRUE),
array('Help! Help! Help!', 12, 'Help! Help!…', TRUE, TRUE),
array('Help! Help! Help!', 11, 'Help! Help…', TRUE, TRUE),
array('Help! Help! Help!', 10, 'Help!…', TRUE, TRUE),
array('Help! Help! Help!', 9, 'Help!…', TRUE, TRUE),
array('Help! Help! Help!', 8, 'Help!…', TRUE, TRUE),
array('Help! Help! Help!', 7, 'Help!…', TRUE, TRUE),
array('Help! Help! Help!', 6, 'Help!…', TRUE, TRUE),
array('Help! Help! Help!', 5, 'Help…', TRUE, TRUE),
array('Help! Help! Help!', 4, 'Hel…', TRUE, TRUE),
array('Help! Help! Help!', 3, 'He…', TRUE, TRUE),
array('Help! Help! Help!', 2, 'H…', TRUE, TRUE),
);
}
/**
* Tests multibyte truncate bytes.
*
* @dataProvider providerTestTruncateBytes
* @covers ::truncateBytes
*
* @param string $text
* The string to truncate.
* @param int $max_length
* The upper limit on the returned string length.
* @param string $expected
* The expected return from Unicode::truncateBytes().
*/
public function testTruncateBytes($text, $max_length, $expected) {
$this->assertEquals($expected, Unicode::truncateBytes($text, $max_length), 'The string was not correctly truncated.');
}
/**
* Provides data for self::testTruncateBytes().
*
* @return array
* An array of arrays, each containing the parameters to
* self::testTruncateBytes().
*/
public function providerTestTruncateBytes() {
return array(
// String shorter than max length.
array('Short string', 42, 'Short string'),
// Simple string longer than max length.
array('Longer string than previous.', 10, 'Longer str'),
// Unicode.
array('以呂波耳・ほへとち。リヌルヲ。', 10, '以呂波'),
);
}
/**
* Tests UTF-8 validation.
*
* @dataProvider providerTestValidateUtf8
* @covers ::validateUtf8
*
* @param string $text
* The text to validate.
* @param bool $expected
* The expected return value from Unicode::validateUtf8().
* @param string $message
* The message to display on failure.
*/
public function testValidateUtf8($text, $expected, $message) {
$this->assertEquals($expected, Unicode::validateUtf8($text), $message);
}
/**
* Provides data for self::testValidateUtf8().
*
* @return array
* An array of arrays, each containing the parameters for
* self::testValidateUtf8().
*
* Invalid UTF-8 examples sourced from http://stackoverflow.com/a/11709412/109119.
*/
public function providerTestValidateUtf8() {
return array(
// Empty string.
array('', TRUE, 'An empty string did not validate.'),
// Simple text string.
array('Simple text.', TRUE, 'A simple ASCII text string did not validate.'),
// Invalid UTF-8, overlong 5 byte encoding.
array(chr(0xF8) . chr(0x80) . chr(0x80) . chr(0x80) . chr(0x80), FALSE, 'Invalid UTF-8 was validated.'),
// High code-point without trailing characters.
array(chr(0xD0) . chr(0x01), FALSE, 'Invalid UTF-8 was validated.'),
);
}
/**
* Tests UTF-8 conversion.
*
* @dataProvider providerTestConvertToUtf8
* @covers ::convertToUtf8
*
* @param string $data
* The data to be converted.
* @param string $encoding
* The encoding the data is in.
* @param string|bool $expected
* The expected result.
*/
public function testConvertToUtf8($data, $encoding, $expected) {
$this->assertEquals($expected, Unicode::convertToUtf8($data, $encoding));
}
/**
* Provides data to self::testConvertToUtf8().
*
* @return array
* An array of arrays, each containing the parameters to
* self::testConvertUtf8(). }
*/
public function providerTestConvertToUtf8() {
return array(
array(chr(0x97), 'Windows-1252', '—'),
array(chr(0x99), 'Windows-1252', '™'),
array(chr(0x80), 'Windows-1252', '€'),
);
}
}

View file

@ -0,0 +1,567 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Component\Utility\UrlHelperTest.
*/
namespace Drupal\Tests\Component\Utility;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Tests\UnitTestCase;
/**
* @group Utility
*
* @coversDefaultClass \Drupal\Component\Utility\UrlHelper
*/
class UrlHelperTest extends UnitTestCase {
/**
* Provides test data for testBuildQuery().
*
* @return array
*/
public function providerTestBuildQuery() {
return array(
array(array('a' => ' &#//+%20@۞'), 'a=%20%26%23//%2B%2520%40%DB%9E', 'Value was properly encoded.'),
array(array(' &#//+%20@۞' => 'a'), '%20%26%23%2F%2F%2B%2520%40%DB%9E=a', 'Key was properly encoded.'),
array(array('a' => '1', 'b' => '2', 'c' => '3'), 'a=1&b=2&c=3', 'Multiple values were properly concatenated.'),
array(array('a' => array('b' => '2', 'c' => '3'), 'd' => 'foo'), 'a[b]=2&a[c]=3&d=foo', 'Nested array was properly encoded.'),
array(array('foo' => NULL), 'foo', 'Simple parameters are properly added.'),
);
}
/**
* Tests query building.
*
* @dataProvider providerTestBuildQuery
* @covers ::buildQuery
*
* @param array $query
* The array of query parameters.
* @param string $expected
* The expected query string.
* @param string $message
* The assertion message.
*/
public function testBuildQuery($query, $expected, $message) {
$this->assertEquals(UrlHelper::buildQuery($query), $expected, $message);
}
/**
* Data provider for testValidAbsolute().
*
* @return array
*/
public function providerTestValidAbsoluteData() {
$urls = array(
'example.com',
'www.example.com',
'ex-ample.com',
'3xampl3.com',
'example.com/parenthesis',
'example.com/index.html#pagetop',
'example.com:8080',
'subdomain.example.com',
'example.com/index.php/node',
'example.com/index.php/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',
);
return $this->dataEnhanceWithScheme($urls);
}
/**
* Tests valid absolute URLs.
*
* @dataProvider providerTestValidAbsoluteData
* @covers ::isValid
*
* @param string $url
* The url to test.
* @param string $scheme
* The scheme to test.
*/
public function testValidAbsolute($url, $scheme) {
$test_url = $scheme . '://' . $url;
$valid_url = UrlHelper::isValid($test_url, TRUE);
$this->assertTrue($valid_url, SafeMarkup::format('@url is a valid URL.', array('@url' => $test_url)));
}
/**
* Provides data for testInvalidAbsolute().
*
* @return array
*/
public function providerTestInvalidAbsolute() {
$data = array(
'',
'ex!ample.com',
'ex%ample.com',
);
return $this->dataEnhanceWithScheme($data);
}
/**
* Tests invalid absolute URLs.
*
* @dataProvider providerTestInvalidAbsolute
* @covers ::isValid
*
* @param string $url
* The url to test.
* @param string $scheme
* The scheme to test.
*/
public function testInvalidAbsolute($url, $scheme) {
$test_url = $scheme . '://' . $url;
$valid_url = UrlHelper::isValid($test_url, TRUE);
$this->assertFalse($valid_url, SafeMarkup::format('@url is NOT a valid URL.', array('@url' => $test_url)));
}
/**
* Provides data for testValidRelative().
*
* @return array
*/
public function providerTestValidRelativeData() {
$data = array(
'paren(the)sis',
'index.html#pagetop',
'index.php/node',
'index.php/node?param=false',
'login.php?do=login&style=%23#pagetop',
);
return $this->dataEnhanceWithPrefix($data);
}
/**
* Tests valid relative URLs.
*
* @dataProvider providerTestValidRelativeData
* @covers ::isValid
*
* @param string $url
* The url to test.
* @param string $prefix
* The prefix to test.
*/
public function testValidRelative($url, $prefix) {
$test_url = $prefix . $url;
$valid_url = UrlHelper::isValid($test_url);
$this->assertTrue($valid_url, SafeMarkup::format('@url is a valid URL.', array('@url' => $test_url)));
}
/**
* Provides data for testInvalidRelative().
*
* @return array
*/
public function providerTestInvalidRelativeData() {
$data = array(
'ex^mple',
'example<>',
'ex%ample',
);
return $this->dataEnhanceWithPrefix($data);
}
/**
* Tests invalid relative URLs.
*
* @dataProvider providerTestInvalidRelativeData
* @covers ::isValid
*
* @param string $url
* The url to test.
* @param string $prefix
* The prefix to test.
*/
public function testInvalidRelative($url, $prefix) {
$test_url = $prefix . $url;
$valid_url = UrlHelper::isValid($test_url);
$this->assertFalse($valid_url, SafeMarkup::format('@url is NOT a valid URL.', array('@url' => $test_url)));
}
/**
* Tests query filtering.
*
* @dataProvider providerTestFilterQueryParameters
* @covers ::filterQueryParameters
*
* @param array $query
* The array of query parameters.
* @param array $exclude
* A list of $query array keys to remove. Use "parent[child]" to exclude
* nested items.
* @param array $expected
* An array containing query parameters.
*/
public function testFilterQueryParameters($query, $exclude, $expected) {
$filtered = UrlHelper::filterQueryParameters($query, $exclude);
$this->assertEquals($expected, $filtered, 'The query was not properly filtered.');
}
/**
* Provides data to self::testFilterQueryParameters().
*
* @return array
*/
public static function providerTestFilterQueryParameters() {
return array(
// Test without an exclude filter.
array(
'query' => array('a' => array('b' => 'c')),
'exclude' => array(),
'expected' => array('a' => array('b' => 'c')),
),
// Exclude the 'b' element.
array(
'query' => array('a' => array('b' => 'c', 'd' => 'e')),
'exclude' => array('a[b]'),
'expected' => array('a' => array('d' => 'e')),
),
);
}
/**
* Tests url parsing.
*
* @dataProvider providerTestParse
* @covers ::parse
*
* @param string $url
* URL to test.
* @param array $expected
* Associative array with expected parameters.
*/
public function testParse($url, $expected) {
$parsed = UrlHelper::parse($url);
$this->assertEquals($expected, $parsed, 'The url was not properly parsed.');
}
/**
* Provides data for self::testParse().
*
* @return array
*/
public static function providerTestParse() {
return array(
array(
'http://www.example.com/my/path',
array(
'path' => 'http://www.example.com/my/path',
'query' => array(),
'fragment' => '',
),
),
array(
'http://www.example.com/my/path?destination=home#footer',
array(
'path' => 'http://www.example.com/my/path',
'query' => array(
'destination' => 'home',
),
'fragment' => 'footer',
),
),
array(
'http://',
array(
'path' => '',
'query' => array(),
'fragment' => '',
),
),
array(
'https://',
array(
'path' => '',
'query' => array(),
'fragment' => '',
),
),
array(
'/my/path?destination=home#footer',
array(
'path' => '/my/path',
'query' => array(
'destination' => 'home',
),
'fragment' => 'footer',
),
),
);
}
/**
* Tests path encoding.
*
* @dataProvider providerTestEncodePath
* @covers ::encodePath
*
* @param string $path
* A path to encode.
* @param string $expected
* The expected encoded path.
*/
public function testEncodePath($path, $expected) {
$encoded = UrlHelper::encodePath($path);
$this->assertEquals($expected, $encoded);
}
/**
* Provides data for self::testEncodePath().
*
* @return array
*/
public static function providerTestEncodePath() {
return array(
array('unencoded path with spaces', 'unencoded%20path%20with%20spaces'),
array('slashes/should/be/preserved', 'slashes/should/be/preserved'),
);
}
/**
* Tests external versus internal paths.
*
* @dataProvider providerTestIsExternal
* @covers ::isExternal
*
* @param string $path
* URL or path to test.
* @param bool $expected
* Expected result.
*/
public function testIsExternal($path, $expected) {
$isExternal = UrlHelper::isExternal($path);
$this->assertEquals($expected, $isExternal);
}
/**
* Provides data for self::testIsExternal().
*
* @return array
*/
public static function providerTestIsExternal() {
return array(
array('/internal/path', FALSE),
array('https://example.com/external/path', TRUE),
array('javascript://fake-external-path', FALSE),
// External URL without an explicit protocol.
array('//www.drupal.org/foo/bar?foo=bar&bar=baz&baz#foo', TRUE),
// Internal URL starting with a slash.
array('/www.drupal.org', FALSE),
);
}
/**
* Tests bad protocol filtering and escaping.
*
* @dataProvider providerTestFilterBadProtocol
* @covers ::setAllowedProtocols
* @covers ::filterBadProtocol
*
* @param string $uri
* Protocol URI.
* @param string $expected
* Expected escaped value.
* @param array $protocols
* Protocols to allow.
*/
public function testFilterBadProtocol($uri, $expected, $protocols) {
UrlHelper::setAllowedProtocols($protocols);
$filtered = UrlHelper::filterBadProtocol($uri);
$this->assertEquals($expected, $filtered);
}
/**
* Provides data for self::testTestFilterBadProtocol().
*
* @return array
*/
public static function providerTestFilterBadProtocol() {
return array(
array('javascript://example.com?foo&bar', '//example.com?foo&amp;bar', array('http', 'https')),
// Test custom protocols.
array('http://example.com?foo&bar', '//example.com?foo&amp;bar', array('https')),
// Valid protocol.
array('http://example.com?foo&bar', 'http://example.com?foo&amp;bar', array('https', 'http')),
// Colon not part of the URL scheme.
array('/test:8888?foo&bar', '/test:8888?foo&amp;bar', array('http')),
);
}
/**
* Tests dangerous url protocol filtering.
*
* @dataProvider providerTestStripDangerousProtocols
* @covers ::setAllowedProtocols
* @covers ::stripDangerousProtocols
*
* @param string $uri
* Protocol URI.
* @param string $expected
* Expected escaped value.
* @param array $protocols
* Protocols to allow.
*/
public function testStripDangerousProtocols($uri, $expected, $protocols) {
UrlHelper::setAllowedProtocols($protocols);
$stripped = UrlHelper::stripDangerousProtocols($uri);
$this->assertEquals($expected, $stripped);
}
/**
* Provides data for self::testStripDangerousProtocols().
*
* @return array
*/
public static function providerTestStripDangerousProtocols() {
return array(
array('javascript://example.com', '//example.com', array('http', 'https')),
// Test custom protocols.
array('http://example.com', '//example.com', array('https')),
// Valid protocol.
array('http://example.com', 'http://example.com', array('https', 'http')),
// Colon not part of the URL scheme.
array('/test:8888', '/test:8888', array('http')),
);
}
/**
* Enhances test urls with schemes
*
* @param array $urls
* The list of urls.
*
* @return array
* A list of provider data with schemes.
*/
protected function dataEnhanceWithScheme(array $urls) {
$url_schemes = array('http', 'https', 'ftp');
$data = array();
foreach ($url_schemes as $scheme) {
foreach ($urls as $url) {
$data[] = array($url, $scheme);
}
}
return $data;
}
/**
* Enhances test urls with prefixes.
*
* @param array $urls
* The list of urls.
*
* @return array
* A list of provider data with prefixes.
*/
protected function dataEnhanceWithPrefix(array $urls) {
$prefixes = array('', '/');
$data = array();
foreach ($prefixes as $prefix) {
foreach ($urls as $url) {
$data[] = array($url, $prefix);
}
}
return $data;
}
/**
* Test detecting external urls that point to local resources.
*
* @param string $url
* The external url to test.
* @param string $base_url
* The base url.
* @param bool $expected
* TRUE if an external URL points to this installation as determined by the
* base url.
*
* @covers ::externalIsLocal
* @dataProvider providerTestExternalIsLocal
*/
public function testExternalIsLocal($url, $base_url, $expected) {
$this->assertSame($expected, UrlHelper::externalIsLocal($url, $base_url));
}
/**
* Provider for local external url detection.
*
* @see \Drupal\Tests\Component\Utility\UrlHelperTest::testExternalIsLocal()
*/
public function providerTestExternalIsLocal() {
return array(
// Different mixes of trailing slash.
array('http://example.com', 'http://example.com', TRUE),
array('http://example.com/', 'http://example.com', TRUE),
array('http://example.com', 'http://example.com/', TRUE),
array('http://example.com/', 'http://example.com/', TRUE),
// Sub directory of site.
array('http://example.com/foo', 'http://example.com/', TRUE),
array('http://example.com/foo/bar', 'http://example.com/foo', TRUE),
array('http://example.com/foo/bar', 'http://example.com/foo/', TRUE),
// Different sub-domain.
array('http://example.com', 'http://www.example.com/', FALSE),
array('http://example.com/', 'http://www.example.com/', FALSE),
array('http://example.com/foo', 'http://www.example.com/', FALSE),
// Different TLD.
array('http://example.com', 'http://example.ca', FALSE),
array('http://example.com', 'http://example.ca/', FALSE),
array('http://example.com/', 'http://example.ca/', FALSE),
array('http://example.com/foo', 'http://example.ca', FALSE),
array('http://example.com/foo', 'http://example.ca/', FALSE),
// Different site path.
array('http://example.com/foo', 'http://example.com/bar', FALSE),
array('http://example.com', 'http://example.com/bar', FALSE),
array('http://example.com/bar', 'http://example.com/bar/', FALSE),
);
}
/**
* Test invalid url arguments.
*
* @param string $url
* The url to test.
* @param string $base_url
* The base url.
*
* @covers ::externalIsLocal
* @dataProvider providerTestExternalIsLocalInvalid
* @expectedException \InvalidArgumentException
*/
public function testExternalIsLocalInvalid($url, $base_url) {
UrlHelper::externalIsLocal($url, $base_url);
}
/**
* Provides invalid argument data for local external url detection.
*
* @see \Drupal\Tests\Component\Utility\UrlHelperTest::testExternalIsLocalInvalid()
*/
public function providerTestExternalIsLocalInvalid() {
return array(
array('http://example.com/foo', ''),
array('http://example.com/foo', 'bar'),
array('http://example.com/foo', 'http://'),
// Invalid destination urls.
array('', 'http://example.com/foo'),
array('bar', 'http://example.com/foo'),
array('/bar', 'http://example.com/foo'),
array('bar/', 'http://example.com/foo'),
array('http://', 'http://example.com/foo'),
);
}
}

View file

@ -0,0 +1,172 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Component\Utility\UserAgentTest.
*/
namespace Drupal\Tests\Component\Utility;
use Drupal\Component\Utility\UserAgent;
use Drupal\Tests\UnitTestCase;
/**
* Tests bytes size parsing helper methods.
*
* @group Utility
*
* @coversDefaultClass \Drupal\Component\Utility\UserAgent
*/
class UserAgentTest extends UnitTestCase {
/**
* Helper method to supply language codes to testGetBestMatchingLangcode().
*
* @return array
* Language codes, ordered by priority.
*/
protected function getLanguages() {
return array(
// In our test case, 'en' has priority over 'en-US'.
'en',
'en-US',
// But 'fr-CA' has priority over 'fr'.
'fr-CA',
'fr',
// 'es-MX' is alone.
'es-MX',
// 'pt' is alone.
'pt',
// Language codes with more then one dash are actually valid.
// eh-oh-laa-laa is the official language code of the Teletubbies.
'eh-oh-laa-laa',
// Chinese languages.
'zh-hans',
'zh-hant',
'zh-hant-tw',
);
}
/**
* Helper method to supply language mappings to testGetBestMatchingLangcode().
*
* @return array
* Language mappings.
*/
protected function getMappings() {
return array(
'no' => 'nb',
'pt' => 'pt-pt',
'zh' => 'zh-hans',
'zh-tw' => 'zh-hant',
'zh-hk' => 'zh-hant',
'zh-mo' => 'zh-hant',
'zh-cht' => 'zh-hant',
'zh-cn' => 'zh-hans',
'zh-sg' => 'zh-hans',
'zh-chs' => 'zh-hans',
);
}
/**
* Test matching language from user agent.
*
* @dataProvider providerTestGetBestMatchingLangcode
* @covers ::getBestMatchingLangcode
*/
public function testGetBestMatchingLangcode($accept_language, $expected) {
$result = UserAgent::getBestMatchingLangcode($accept_language, $this->getLanguages(), $this->getMappings());
$this->assertSame($expected, $result);
}
/**
* Data provider for testGetBestMatchingLangcode().
*
* @return array
* - An accept-language string.
* - Expected best matching language code.
*/
public function providerTestGetBestMatchingLangcode() {
return array(
// Equal qvalue for each language, choose the site preferred one.
array('en,en-US,fr-CA,fr,es-MX', 'en'),
array('en-US,en,fr-CA,fr,es-MX', 'en'),
array('fr,en', 'en'),
array('en,fr', 'en'),
array('en-US,fr', 'en-US'),
array('fr,en-US', 'en-US'),
array('fr,fr-CA', 'fr-CA'),
array('fr-CA,fr', 'fr-CA'),
array('fr', 'fr-CA'),
array('fr;q=1', 'fr-CA'),
array('fr,es-MX', 'fr-CA'),
array('fr,es', 'fr-CA'),
array('es,fr', 'fr-CA'),
array('es-MX,de', 'es-MX'),
array('de,es-MX', 'es-MX'),
// Different cases and whitespace.
array('en', 'en'),
array('En', 'en'),
array('EN', 'en'),
array(' en', 'en'),
array('en ', 'en'),
array('en, fr', 'en'),
// A less specific language from the browser matches a more specific one
// from the website, and the other way around for compatibility with
// some versions of Internet Explorer.
array('es', 'es-MX'),
array('es-MX', 'es-MX'),
array('pt', 'pt'),
array('pt-PT', 'pt'),
array('pt-PT;q=0.5,pt-BR;q=1,en;q=0.7', 'en'),
array('pt-PT;q=1,pt-BR;q=0.5,en;q=0.7', 'en'),
array('pt-PT;q=0.4,pt-BR;q=0.1,en;q=0.7', 'en'),
array('pt-PT;q=0.1,pt-BR;q=0.4,en;q=0.7', 'en'),
// Language code with several dashes are valid. The less specific language
// from the browser matches the more specific one from the website.
array('eh-oh-laa-laa', 'eh-oh-laa-laa'),
array('eh-oh-laa', 'eh-oh-laa-laa'),
array('eh-oh', 'eh-oh-laa-laa'),
array('eh', 'eh-oh-laa-laa'),
// Different qvalues.
array('fr,en;q=0.5', 'fr-CA'),
array('fr,en;q=0.5,fr-CA;q=0.25', 'fr'),
// Silly wildcards are also valid.
array('*,fr-CA;q=0.5', 'en'),
array('*,en;q=0.25', 'fr-CA'),
array('en,en-US;q=0.5,fr;q=0.25', 'en'),
array('en-US,en;q=0.5,fr;q=0.25', 'en-US'),
// Unresolvable cases.
array('', FALSE),
array('de,pl', FALSE),
array('iecRswK4eh', FALSE),
array($this->randomMachineName(10), FALSE),
// Chinese langcodes.
array('zh-cn, en-us;q=0.90, en;q=0.80, zh;q=0.70', 'zh-hans'),
array('zh-tw, en-us;q=0.90, en;q=0.80, zh;q=0.70', 'zh-hant'),
array('zh-hant, en-us;q=0.90, en;q=0.80, zh;q=0.70', 'zh-hant'),
array('zh-hans, en-us;q=0.90, en;q=0.80, zh;q=0.70', 'zh-hans'),
// @todo: This is copied from RFC4647 but our regex skips the numbers so
// they where removed. Our code should be updated so private1-private2 is
// valid. http://tools.ietf.org/html/rfc4647#section-3.4
array('zh-hant-CN-x-private-private, en-us;q=0.90, en;q=0.80, zh;q=0.70', 'zh-hant'),
array('zh-cn', 'zh-hans'),
array('zh-sg', 'zh-hans'),
array('zh-tw', 'zh-hant'),
array('zh-hk', 'zh-hant'),
array('zh-mo', 'zh-hant'),
array('zh-hans', 'zh-hans'),
array('zh-hant', 'zh-hant'),
array('zh-chs', 'zh-hans'),
array('zh-cht', 'zh-hant'),
);
}
}

View file

@ -0,0 +1,120 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Component\Utility\VariableTest.
*/
namespace Drupal\Tests\Component\Utility;
use Drupal\Tests\UnitTestCase;
use Drupal\Component\Utility\Variable;
/**
* Test variable export functionality in Variable component.
*
* @group Variable
* @group Utility
*
* @coversDefaultClass \Drupal\Component\Utility\Variable
*/
class VariableTest extends UnitTestCase {
/**
* Data provider for testExport().
*
* @return array
* An array containing:
* - The expected export string.
* - The variable to export.
*/
public function providerTestExport() {
return array(
// Array.
array(
'array()',
array(),
),
array(
// non-associative.
"array(\n 1,\n 2,\n 3,\n 4,\n)",
array(1, 2, 3, 4),
),
array(
// associative.
"array(\n 'a' => 1,\n)",
array('a' => 1),
),
// Bool.
array(
'TRUE',
TRUE,
),
array(
'FALSE',
FALSE,
),
// Strings.
array(
"'string'",
'string',
),
array(
'"\n\r\t"',
"\n\r\t",
),
array(
// 2 backslashes. \\
"'\\'",
'\\',
),
array(
// Double-quote "
"'\"'",
"\"",
),
array(
// Single-quote '
'"\'"',
"'",
),
// Object.
array(
// A stdClass object.
'(object) array()',
new \stdClass(),
),
array(
// A not-stdClass object.
"Drupal\Tests\Component\Utility\StubVariableTestClass::__set_state(array(\n))",
new StubVariableTestClass(),
),
);
}
/**
* Tests exporting variables.
*
* @dataProvider providerTestExport
* @covers ::export
*
* @param string $expected
* The expected exported variable.
* @param mixed $variable
* The variable to be exported.
*/
public function testExport($expected, $variable) {
$this->assertEquals($expected, Variable::export($variable));
}
}
/**
* No-op test class for VariableTest::testExport().
*
* @see Drupal\Tests\Component\Utility\VariableTest::testExport()
* @see Drupal\Tests\Component\Utility\VariableTest::providerTestExport()
*/
class StubVariableTestClass {
}

View file

@ -0,0 +1,609 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Component\Utility\XssTest.
*/
namespace Drupal\Tests\Component\Utility;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Component\Utility\Xss;
use Drupal\Tests\UnitTestCase;
/**
* XSS Filtering tests.
*
* @group Utility
*
* @coversDefaultClass \Drupal\Component\Utility\Xss
*
* Script injection vectors mostly adopted from http://ha.ckers.org/xss.html.
*
* Relevant CVEs:
* - CVE-2002-1806, ~CVE-2005-0682, ~CVE-2005-2106, CVE-2005-3973,
* CVE-2006-1226 (= rev. 1.112?), CVE-2008-0273, CVE-2008-3740.
*/
class XssTest extends UnitTestCase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$allowed_protocols = array(
'http',
'https',
'ftp',
'news',
'nntp',
'telnet',
'mailto',
'irc',
'ssh',
'sftp',
'webcal',
'rtsp',
);
UrlHelper::setAllowedProtocols($allowed_protocols);
}
/**
* Tests limiting allowed tags and XSS prevention.
*
* XSS tests assume that script is disallowed by default and src is allowed
* by default, but on* and style attributes are disallowed.
*
* @param string $value
* The value to filter.
* @param string $expected
* The expected result.
* @param string $message
* The assertion message to display upon failure.
* @param array $allowed_tags
* (optional) The allowed HTML tags to be passed to \Drupal\Component\Utility\Xss::filter().
*
* @dataProvider providerTestFilterXssNormalized
*/
public function testFilterXssNormalized($value, $expected, $message, array $allowed_tags = NULL) {
if ($allowed_tags === NULL) {
$value = Xss::filter($value);
}
else {
$value = Xss::filter($value, $allowed_tags);
}
$this->assertNormalized($value, $expected, $message);
}
/**
* Data provider for testFilterXssNormalized().
*
* @see testFilterXssNormalized()
*
* @return array
* An array of arrays containing strings:
* - The value to filter.
* - The value to expect after filtering.
* - The assertion message.
* - (optional) The allowed HTML HTML tags array that should be passed to
* \Drupal\Component\Utility\Xss::filter().
*/
public function providerTestFilterXssNormalized() {
return array(
array(
"Who&#039;s Online",
"who's online",
'HTML filter -- html entity number',
),
array(
"Who&amp;#039;s Online",
"who&#039;s online",
'HTML filter -- encoded html entity number',
),
array(
"Who&amp;amp;#039; Online",
"who&amp;#039; online",
'HTML filter -- double encoded html entity number',
),
// Custom elements with dashes in the tag name.
array(
"<test-element></test-element>",
"<test-element></test-element>",
'Custom element with dashes in tag name.',
array('test-element'),
),
);
}
/**
* Tests limiting to allowed tags and XSS prevention.
*
* XSS tests assume that script is disallowed by default and src is allowed
* by default, but on* and style attributes are disallowed.
*
* @param string $value
* The value to filter.
* @param string $expected
* The string that is expected to be missing.
* @param string $message
* The assertion message to display upon failure.
* @param array $allowed_tags
* (optional) The allowed HTML tags to be passed to \Drupal\Component\Utility\Xss::filter().
*
* @dataProvider providerTestFilterXssNotNormalized
*/
public function testFilterXssNotNormalized($value, $expected, $message, array $allowed_tags = NULL) {
if ($allowed_tags === NULL) {
$value = Xss::filter($value);
}
else {
$value = Xss::filter($value, $allowed_tags);
}
$this->assertNotNormalized($value, $expected, $message);
}
/**
* Data provider for testFilterXssNotNormalized().
*
* @see testFilterXssNotNormalized()
*
* @return array
* An array of arrays containing the following elements:
* - The value to filter.
* - The value to expect that's missing after filtering.
* - The assertion message.
* - (optional) The allowed HTML HTML tags array that should be passed to
* \Drupal\Component\Utility\Xss::filter().
*/
public function providerTestFilterXssNotNormalized() {
$cases = array(
// Tag stripping, different ways to work around removal of HTML tags.
array(
'<script>alert(0)</script>',
'script',
'HTML tag stripping -- simple script without special characters.',
),
array(
'<script src="http://www.example.com" />',
'script',
'HTML tag stripping -- empty script with source.',
),
array(
'<ScRipt sRc=http://www.example.com/>',
'script',
'HTML tag stripping evasion -- varying case.',
),
array(
"<script\nsrc\n=\nhttp://www.example.com/\n>",
'script',
'HTML tag stripping evasion -- multiline tag.',
),
array(
'<script/a src=http://www.example.com/a.js></script>',
'script',
'HTML tag stripping evasion -- non whitespace character after tag name.',
),
array(
'<script/src=http://www.example.com/a.js></script>',
'script',
'HTML tag stripping evasion -- no space between tag and attribute.',
),
// Null between < and tag name works at least with IE6.
array(
"<\0scr\0ipt>alert(0)</script>",
'ipt',
'HTML tag stripping evasion -- breaking HTML with nulls.',
),
array(
"<scrscriptipt src=http://www.example.com/a.js>",
'script',
'HTML tag stripping evasion -- filter just removing "script".',
),
array(
'<<script>alert(0);//<</script>',
'script',
'HTML tag stripping evasion -- double opening brackets.',
),
array(
'<script src=http://www.example.com/a.js?<b>',
'script',
'HTML tag stripping evasion -- no closing tag.',
),
// DRUPAL-SA-2008-047: This doesn't seem exploitable, but the filter should
// work consistently.
array(
'<script>>',
'script',
'HTML tag stripping evasion -- double closing tag.',
),
array(
'<script src=//www.example.com/.a>',
'script',
'HTML tag stripping evasion -- no scheme or ending slash.',
),
array(
'<script src=http://www.example.com/.a',
'script',
'HTML tag stripping evasion -- no closing bracket.',
),
array(
'<script src=http://www.example.com/ <',
'script',
'HTML tag stripping evasion -- opening instead of closing bracket.',
),
array(
'<nosuchtag attribute="newScriptInjectionVector">',
'nosuchtag',
'HTML tag stripping evasion -- unknown tag.',
),
array(
'<t:set attributeName="innerHTML" to="&lt;script defer&gt;alert(0)&lt;/script&gt;">',
't:set',
'HTML tag stripping evasion -- colon in the tag name (namespaces\' tricks).',
),
array(
'<img """><script>alert(0)</script>',
'script',
'HTML tag stripping evasion -- a malformed image tag.',
array('img'),
),
array(
'<blockquote><script>alert(0)</script></blockquote>',
'script',
'HTML tag stripping evasion -- script in a blockqoute.',
array('blockquote'),
),
array(
"<!--[if true]><script>alert(0)</script><![endif]-->",
'script',
'HTML tag stripping evasion -- script within a comment.',
),
// Dangerous attributes removal.
array(
'<p onmouseover="http://www.example.com/">',
'onmouseover',
'HTML filter attributes removal -- events, no evasion.',
array('p'),
),
array(
'<li style="list-style-image: url(javascript:alert(0))">',
'style',
'HTML filter attributes removal -- style, no evasion.',
array('li'),
),
array(
'<img onerror =alert(0)>',
'onerror',
'HTML filter attributes removal evasion -- spaces before equals sign.',
array('img'),
),
array(
'<img onabort!#$%&()*~+-_.,:;?@[/|\]^`=alert(0)>',
'onabort',
'HTML filter attributes removal evasion -- non alphanumeric characters before equals sign.',
array('img'),
),
array(
'<img oNmediAError=alert(0)>',
'onmediaerror',
'HTML filter attributes removal evasion -- varying case.',
array('img'),
),
// Works at least with IE6.
array(
"<img o\0nfocus\0=alert(0)>",
'focus',
'HTML filter attributes removal evasion -- breaking with nulls.',
array('img'),
),
// Only whitelisted scheme names allowed in attributes.
array(
'<img src="javascript:alert(0)">',
'javascript',
'HTML scheme clearing -- no evasion.',
array('img'),
),
array(
'<img src=javascript:alert(0)>',
'javascript',
'HTML scheme clearing evasion -- no quotes.',
array('img'),
),
// A bit like CVE-2006-0070.
array(
'<img src="javascript:confirm(0)">',
'javascript',
'HTML scheme clearing evasion -- no alert ;)',
array('img'),
),
array(
'<img src=`javascript:alert(0)`>',
'javascript',
'HTML scheme clearing evasion -- grave accents.',
array('img'),
),
array(
'<img dynsrc="javascript:alert(0)">',
'javascript',
'HTML scheme clearing -- rare attribute.',
array('img'),
),
array(
'<table background="javascript:alert(0)">',
'javascript',
'HTML scheme clearing -- another tag.',
array('table'),
),
array(
'<base href="javascript:alert(0);//">',
'javascript',
'HTML scheme clearing -- one more attribute and tag.',
array('base'),
),
array(
'<img src="jaVaSCriPt:alert(0)">',
'javascript',
'HTML scheme clearing evasion -- varying case.',
array('img'),
),
array(
'<img src=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#48;&#41;>',
'javascript',
'HTML scheme clearing evasion -- UTF-8 decimal encoding.',
array('img'),
),
array(
'<img src=&#00000106&#0000097&#00000118&#0000097&#00000115&#0000099&#00000114&#00000105&#00000112&#00000116&#0000058&#0000097&#00000108&#00000101&#00000114&#00000116&#0000040&#0000048&#0000041>',
'javascript',
'HTML scheme clearing evasion -- long UTF-8 encoding.',
array('img'),
),
array(
'<img src=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x30&#x29>',
'javascript',
'HTML scheme clearing evasion -- UTF-8 hex encoding.',
array('img'),
),
array(
"<img src=\"jav\tascript:alert(0)\">",
'script',
'HTML scheme clearing evasion -- an embedded tab.',
array('img'),
),
array(
'<img src="jav&#x09;ascript:alert(0)">',
'script',
'HTML scheme clearing evasion -- an encoded, embedded tab.',
array('img'),
),
array(
'<img src="jav&#x000000A;ascript:alert(0)">',
'script',
'HTML scheme clearing evasion -- an encoded, embedded newline.',
array('img'),
),
// With &#xD; this test would fail, but the entity gets turned into
// &amp;#xD;, so it's OK.
array(
'<img src="jav&#x0D;ascript:alert(0)">',
'script',
'HTML scheme clearing evasion -- an encoded, embedded carriage return.',
array('img'),
),
array(
"<img src=\"\n\n\nj\na\nva\ns\ncript:alert(0)\">",
'cript',
'HTML scheme clearing evasion -- broken into many lines.',
array('img'),
),
array(
"<img src=\"jav\0a\0\0cript:alert(0)\">",
'cript',
'HTML scheme clearing evasion -- embedded nulls.',
array('img'),
),
array(
'<img src="vbscript:msgbox(0)">',
'vbscript',
'HTML scheme clearing evasion -- another scheme.',
array('img'),
),
array(
'<img src="nosuchscheme:notice(0)">',
'nosuchscheme',
'HTML scheme clearing evasion -- unknown scheme.',
array('img'),
),
// Netscape 4.x javascript entities.
array(
'<br size="&{alert(0)}">',
'alert',
'Netscape 4.x javascript entities.',
array('br'),
),
// DRUPAL-SA-2008-006: Invalid UTF-8, these only work as reflected XSS with
// Internet Explorer 6.
array(
"<p arg=\"\xe0\">\" style=\"background-image: url(javascript:alert(0));\"\xe0<p>",
'style',
'HTML filter -- invalid UTF-8.',
array('p'),
),
);
// @fixme This dataset currently fails under 5.4 because of
// https://www.drupal.org/node/1210798. Restore after its fixed.
if (version_compare(PHP_VERSION, '5.4.0', '<')) {
$cases[] = array(
'<img src=" &#14; javascript:alert(0)">',
'javascript',
'HTML scheme clearing evasion -- spaces and metacharacters before scheme.',
array('img'),
);
}
return $cases;
}
/**
* Checks that invalid multi-byte sequences are rejected.
*
* @param string $value
* The value to filter.
* @param string $expected
* The expected result.
* @param string $message
* The assertion message to display upon failure.
*
* @dataProvider providerTestInvalidMultiByte
*/
public function testInvalidMultiByte($value, $expected, $message) {
$this->assertEquals(Xss::filter($value), $expected, $message);
}
/**
* Data provider for testInvalidMultiByte().
*
* @see testInvalidMultiByte()
*
* @return array
* An array of arrays containing strings:
* - The value to filter.
* - The value to expect after filtering.
* - The assertion message.
*/
public function providerTestInvalidMultiByte() {
return array(
array("Foo\xC0barbaz", '', 'Xss::filter() accepted invalid sequence "Foo\xC0barbaz"'),
array("Fooÿñ", "Fooÿñ", 'Xss::filter() rejects valid sequence Fooÿñ"'),
array("\xc0aaa", '', 'HTML filter -- overlong UTF-8 sequences.'),
);
}
/**
* Checks that strings starting with a question sign are correctly processed.
*/
public function testQuestionSign() {
$value = Xss::filter('<?xml:namespace ns="urn:schemas-microsoft-com:time">');
$this->assertTrue(stripos($value, '<?xml') === FALSE, 'HTML tag stripping evasion -- starting with a question sign (processing instructions).');
}
/**
* Check that strings in HTML attributes are are correctly processed.
*
* @covers ::attributes
* @dataProvider providerTestAttributes
*/
public function testAttribute($value, $expected, $message, $allowed_tags = NULL) {
$value = Xss::filter($value, $allowed_tags);
$this->assertEquals($expected, $value, $message);
}
/**
* Data provider for testFilterXssAdminNotNormalized().
*/
public function providerTestAttributes() {
return array(
array(
'<img src="http://example.com/foo.jpg" title="Example: title" alt="Example: alt">',
'<img src="http://example.com/foo.jpg" title="Example: title" alt="Example: alt">',
'Image tag with alt and title attribute',
array('img')
),
array(
'<img src="http://example.com/foo.jpg" data-caption="Drupal 8: The best release ever.">',
'<img src="http://example.com/foo.jpg" data-caption="Drupal 8: The best release ever.">',
'Image tag with data attribute',
array('img')
),
);
}
/**
* Checks that \Drupal\Component\Utility\Xss::filterAdmin() correctly strips unallowed tags.
*/
public function testFilterXSSAdmin() {
$value = Xss::filterAdmin('<style /><iframe /><frame /><frameset /><meta /><link /><embed /><applet /><param /><layer />');
$this->assertEquals($value, '', 'Admin HTML filter -- should never allow some tags.');
}
/**
* Tests the loose, admin HTML filter.
*
* @param string $value
* The value to filter.
* @param string $expected
* The expected result.
* @param string $message
* The assertion message to display upon failure.
*
* @dataProvider providerTestFilterXssAdminNotNormalized
*/
public function testFilterXssAdminNotNormalized($value, $expected, $message) {
$this->assertNotNormalized(Xss::filterAdmin($value), $expected, $message);
}
/**
* Data provider for testFilterXssAdminNotNormalized().
*
* @see testFilterXssAdminNotNormalized()
*
* @return array
* An array of arrays containing strings:
* - The value to filter.
* - The value to expect after filtering.
* - The assertion message.
*/
public function providerTestFilterXssAdminNotNormalized() {
return array(
// DRUPAL-SA-2008-044
array('<object />', 'object', 'Admin HTML filter -- should not allow object tag.'),
array('<script />', 'script', 'Admin HTML filter -- should not allow script tag.'),
);
}
/**
* Asserts that a text transformed to lowercase with HTML entities decoded does contain a given string.
*
* Otherwise fails the test with a given message, similar to all the
* SimpleTest assert* functions.
*
* Note that this does not remove nulls, new lines and other characters that
* could be used to obscure a tag or an attribute name.
*
* @param string $haystack
* Text to look in.
* @param string $needle
* Lowercase, plain text to look for.
* @param string $message
* (optional) Message to display if failed. Defaults to an empty string.
* @param string $group
* (optional) The group this message belongs to. Defaults to 'Other'.
*/
protected function assertNormalized($haystack, $needle, $message = '', $group = 'Other') {
$this->assertTrue(strpos(strtolower(Html::decodeEntities($haystack)), $needle) !== FALSE, $message, $group);
}
/**
* Asserts that text transformed to lowercase with HTML entities decoded does not contain a given string.
*
* Otherwise fails the test with a given message, similar to all the
* SimpleTest assert* functions.
*
* Note that this does not remove nulls, new lines, and other character that
* could be used to obscure a tag or an attribute name.
*
* @param string $haystack
* Text to look in.
* @param string $needle
* Lowercase, plain text to look for.
* @param string $message
* (optional) Message to display if failed. Defaults to an empty string.
* @param string $group
* (optional) The group this message belongs to. Defaults to 'Other'.
*/
protected function assertNotNormalized($haystack, $needle, $message = '', $group = 'Other') {
$this->assertTrue(strpos(strtolower(Html::decodeEntities($haystack)), $needle) === FALSE, $message, $group);
}
}