Move into nested docroot
This commit is contained in:
parent
83a0d3a149
commit
c8b70abde9
13405 changed files with 0 additions and 0 deletions
1776
web/core/tests/Drupal/Tests/BrowserTestBase.php
Normal file
1776
web/core/tests/Drupal/Tests/BrowserTestBase.php
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,266 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\Component\Assertion\InspectorTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\Component\Assertion;
|
||||
|
||||
use PHPUnit_Framework_TestCase;
|
||||
use Drupal\Component\Assertion\Inspector;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Component\Assertion\Inspector
|
||||
* @group Assertion
|
||||
*/
|
||||
class InspectorTest extends PHPUnit_Framework_TestCase {
|
||||
|
||||
/**
|
||||
* Tests asserting argument is an array or traversable object.
|
||||
*
|
||||
* @covers ::assertTraversable
|
||||
*/
|
||||
public function testAssertTraversable() {
|
||||
$this->assertTrue(Inspector::assertTraversable([]));
|
||||
$this->assertTrue(Inspector::assertTraversable(new \ArrayObject()));
|
||||
$this->assertFalse(Inspector::assertTraversable(new \stdClass()));
|
||||
$this->assertFalse(Inspector::assertTraversable('foo'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests asserting all members are strings.
|
||||
*
|
||||
* @covers ::assertAllStrings
|
||||
* @dataProvider providerTestAssertAllStrings
|
||||
*/
|
||||
public function testAssertAllStrings($input, $expected) {
|
||||
$this->assertSame($expected, Inspector::assertAllStrings($input));
|
||||
}
|
||||
|
||||
public function providerTestAssertAllStrings() {
|
||||
$data = [
|
||||
'empty-array' => [[], TRUE],
|
||||
'array-with-strings' => [['foo', 'bar'], TRUE],
|
||||
'string' => ['foo', FALSE],
|
||||
'array-with-strings-with-colon' => [['foo', 'bar', 'llama:2001988', 'baz', 'llama:14031991'], TRUE],
|
||||
|
||||
'with-FALSE' => [[FALSE], FALSE],
|
||||
'with-TRUE' => [[TRUE], FALSE],
|
||||
'with-string-and-boolean' => [['foo', FALSE], FALSE],
|
||||
'with-NULL' => [[NULL], FALSE],
|
||||
'string-with-NULL' => [['foo', NULL], FALSE],
|
||||
'integer' => [[1337], FALSE],
|
||||
'string-and-integer' => [['foo', 1337], FALSE],
|
||||
'double' => [[3.14], FALSE],
|
||||
'string-and-double' => [['foo', 3.14], FALSE],
|
||||
'array' => [[[]], FALSE],
|
||||
'string-and-array' => [['foo', []], FALSE],
|
||||
'string-and-nested-array' => [['foo', ['bar']], FALSE],
|
||||
'object' => [[new \stdClass()], FALSE],
|
||||
'string-and-object' => [['foo', new StringObject()], FALSE],
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests asserting all members are strings or objects with __toString().
|
||||
*
|
||||
* @covers ::assertAllStringable
|
||||
*/
|
||||
public function testAssertAllStringable() {
|
||||
$this->assertTrue(Inspector::assertAllStringable([]));
|
||||
$this->assertTrue(Inspector::assertAllStringable(['foo', 'bar']));
|
||||
$this->assertFalse(Inspector::assertAllStringable('foo'));
|
||||
$this->assertTrue(Inspector::assertAllStringable(['foo', new StringObject()]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests asserting all members are arrays.
|
||||
*
|
||||
* @covers ::assertAllArrays
|
||||
*/
|
||||
public function testAssertAllArrays() {
|
||||
$this->assertTrue(Inspector::assertAllArrays([]));
|
||||
$this->assertTrue(Inspector::assertAllArrays([[], []]));
|
||||
$this->assertFalse(Inspector::assertAllArrays([[], 'foo']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests asserting array is 0-indexed - the strict definition of array.
|
||||
*
|
||||
* @covers ::assertStrictArray
|
||||
*/
|
||||
public function testAssertStrictArray() {
|
||||
$this->assertTrue(Inspector::assertStrictArray([]));
|
||||
$this->assertTrue(Inspector::assertStrictArray(['bar', 'foo']));
|
||||
$this->assertFalse(Inspector::assertStrictArray(['foo' => 'bar', 'bar' => 'foo']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests asserting all members are strict arrays.
|
||||
*
|
||||
* @covers ::assertAllStrictArrays
|
||||
*/
|
||||
public function testAssertAllStrictArrays() {
|
||||
$this->assertTrue(Inspector::assertAllStrictArrays([]));
|
||||
$this->assertTrue(Inspector::assertAllStrictArrays([[], []]));
|
||||
$this->assertFalse(Inspector::assertAllStrictArrays([['foo' => 'bar', 'bar' => 'foo']]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests asserting all members have specified keys.
|
||||
*
|
||||
* @covers ::assertAllHaveKey
|
||||
*/
|
||||
public function testAssertAllHaveKey() {
|
||||
$this->assertTrue(Inspector::assertAllHaveKey([]));
|
||||
$this->assertTrue(Inspector::assertAllHaveKey([['foo' => 'bar', 'bar' => 'foo']]));
|
||||
$this->assertTrue(Inspector::assertAllHaveKey([['foo' => 'bar', 'bar' => 'foo']], 'foo'));
|
||||
$this->assertTrue(Inspector::assertAllHaveKey([['foo' => 'bar', 'bar' => 'foo']], 'bar', 'foo'));
|
||||
$this->assertFalse(Inspector::assertAllHaveKey([['foo' => 'bar', 'bar' => 'foo']], 'bar', 'foo', 'moo'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests asserting all members are integers.
|
||||
*
|
||||
* @covers ::assertAllIntegers
|
||||
*/
|
||||
public function testAssertAllIntegers() {
|
||||
$this->assertTrue(Inspector::assertAllIntegers([]));
|
||||
$this->assertTrue(Inspector::assertAllIntegers([1, 2, 3]));
|
||||
$this->assertFalse(Inspector::assertAllIntegers([1, 2, 3.14]));
|
||||
$this->assertFalse(Inspector::assertAllIntegers([1, '2', 3]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests asserting all members are floating point variables.
|
||||
*
|
||||
* @covers ::assertAllFloat
|
||||
*/
|
||||
public function testAssertAllFloat() {
|
||||
$this->assertTrue(Inspector::assertAllFloat([]));
|
||||
$this->assertTrue(Inspector::assertAllFloat([1.0, 2.1, 3.14]));
|
||||
$this->assertFalse(Inspector::assertAllFloat([1, 2.1, 3.14]));
|
||||
$this->assertFalse(Inspector::assertAllFloat([1.0, '2', 3]));
|
||||
$this->assertFalse(Inspector::assertAllFloat(['Titanic']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests asserting all members are callable.
|
||||
*
|
||||
* @covers ::assertAllCallable
|
||||
*/
|
||||
public function testAllCallable() {
|
||||
$this->assertTrue(Inspector::assertAllCallable([
|
||||
'strchr',
|
||||
[$this, 'callMe'],
|
||||
[__CLASS__, 'callMeStatic'],
|
||||
function() {
|
||||
return TRUE;
|
||||
}
|
||||
]));
|
||||
|
||||
$this->assertFalse(Inspector::assertAllCallable([
|
||||
'strchr',
|
||||
[$this, 'callMe'],
|
||||
[__CLASS__, 'callMeStatic'],
|
||||
function() {
|
||||
return TRUE;
|
||||
},
|
||||
"I'm not callable"
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests asserting all members are !empty().
|
||||
*
|
||||
* @covers ::assertAllNotEmpty
|
||||
*/
|
||||
public function testAllNotEmpty() {
|
||||
$this->assertTrue(Inspector::assertAllNotEmpty([1, 'two']));
|
||||
$this->assertFalse(Inspector::assertAllNotEmpty(['']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests asserting all arguments are numbers or strings castable to numbers.
|
||||
*
|
||||
* @covers ::assertAllNumeric
|
||||
*/
|
||||
public function testAssertAllNumeric() {
|
||||
$this->assertTrue(Inspector::assertAllNumeric([1, '2', 3.14]));
|
||||
$this->assertFalse(Inspector::assertAllNumeric([1, 'two', 3.14]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests asserting strstr() or stristr() match.
|
||||
*
|
||||
* @covers ::assertAllMatch
|
||||
*/
|
||||
public function testAssertAllMatch() {
|
||||
$this->assertTrue(Inspector::assertAllMatch('f', ['fee', 'fi', 'fo']));
|
||||
$this->assertTrue(Inspector::assertAllMatch('F', ['fee', 'fi', 'fo']));
|
||||
$this->assertTrue(Inspector::assertAllMatch('f', ['fee', 'fi', 'fo'], TRUE));
|
||||
$this->assertFalse(Inspector::assertAllMatch('F', ['fee', 'fi', 'fo'], TRUE));
|
||||
$this->assertFalse(Inspector::assertAllMatch('e', ['fee', 'fi', 'fo']));
|
||||
$this->assertFalse(Inspector::assertAllMatch('1', [12]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests asserting regular expression match.
|
||||
*
|
||||
* @covers ::assertAllRegularExpressionMatch
|
||||
*/
|
||||
public function testAssertAllRegularExpressionMatch() {
|
||||
$this->assertTrue(Inspector::assertAllRegularExpressionMatch('/f/i', ['fee', 'fi', 'fo']));
|
||||
$this->assertTrue(Inspector::assertAllRegularExpressionMatch('/F/i', ['fee', 'fi', 'fo']));
|
||||
$this->assertTrue(Inspector::assertAllRegularExpressionMatch('/f/', ['fee', 'fi', 'fo']));
|
||||
$this->assertFalse(Inspector::assertAllRegularExpressionMatch('/F/', ['fee', 'fi', 'fo']));
|
||||
$this->assertFalse(Inspector::assertAllRegularExpressionMatch('/e/', ['fee', 'fi', 'fo']));
|
||||
$this->assertFalse(Inspector::assertAllRegularExpressionMatch('/1/', [12]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests asserting all members are objects.
|
||||
*
|
||||
* @covers ::assertAllObjects
|
||||
*/
|
||||
public function testAssertAllObjects() {
|
||||
$this->assertTrue(Inspector::assertAllObjects([new \ArrayObject(), new \ArrayObject()]));
|
||||
$this->assertFalse(Inspector::assertAllObjects([new \ArrayObject(), new \ArrayObject(), 'foo']));
|
||||
$this->assertTrue(Inspector::assertAllObjects([new \ArrayObject(), new \ArrayObject()], '\\Traversable'));
|
||||
$this->assertFalse(Inspector::assertAllObjects([new \ArrayObject(), new \ArrayObject(), 'foo'], '\\Traversable'));
|
||||
$this->assertFalse(Inspector::assertAllObjects([new \ArrayObject(), new StringObject()], '\\Traversable'));
|
||||
$this->assertTrue(Inspector::assertAllObjects([new \ArrayObject(), new StringObject()], '\\Traversable', '\\Drupal\\Tests\\Component\\Assertion\\StringObject'));
|
||||
$this->assertFalse(Inspector::assertAllObjects([new \ArrayObject(), new StringObject(), new \stdClass()], '\\ArrayObject', '\\Drupal\\Tests\\Component\\Assertion\\StringObject'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method referenced by ::testAllCallable().
|
||||
*/
|
||||
public function callMe() {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method referenced by ::testAllCallable().
|
||||
*/
|
||||
public static function callMeStatic() {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Quick class for testing for objects with __toString.
|
||||
*/
|
||||
class StringObject {
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __toString() {
|
||||
return 'foo';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Bridge;
|
||||
|
||||
use Drupal\Component\Bridge\ZfExtensionManagerSfContainer;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Component\Bridge\ZfExtensionManagerSfContainer
|
||||
* @group Bridge
|
||||
*/
|
||||
class ZfExtensionManagerSfContainerTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @covers ::setContainer
|
||||
* @covers ::get
|
||||
*/
|
||||
public function testGet() {
|
||||
$service = new \stdClass();
|
||||
$service->value = 'myvalue';
|
||||
$container = new ContainerBuilder();
|
||||
$container->set('foo', $service);
|
||||
$bridge = new ZfExtensionManagerSfContainer();
|
||||
$bridge->setContainer($container);
|
||||
$this->assertEquals($service, $bridge->get('foo'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::setContainer
|
||||
* @covers ::has
|
||||
*/
|
||||
public function testHas() {
|
||||
$service = new \stdClass();
|
||||
$service->value = 'myvalue';
|
||||
$container = new ContainerBuilder();
|
||||
$container->set('foo', $service);
|
||||
$bridge = new ZfExtensionManagerSfContainer();
|
||||
$bridge->setContainer($container);
|
||||
$this->assertTrue($bridge->has('foo'));
|
||||
$this->assertFalse($bridge->has('bar'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::__construct
|
||||
* @covers ::has
|
||||
* @covers ::get
|
||||
*/
|
||||
public function testPrefix() {
|
||||
$service = new \stdClass();
|
||||
$service->value = 'myvalue';
|
||||
$container = new ContainerBuilder();
|
||||
$container->set('foo.bar', $service);
|
||||
$bridge = new ZfExtensionManagerSfContainer('foo.');
|
||||
$bridge->setContainer($container);
|
||||
$this->assertTrue($bridge->has('bar'));
|
||||
$this->assertFalse($bridge->has('baz'));
|
||||
$this->assertEquals($service, $bridge->get('bar'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::canonicalizeName
|
||||
* @dataProvider canonicalizeNameProvider
|
||||
*/
|
||||
public function testCanonicalizeName($name, $canonical_name) {
|
||||
$service = new \stdClass();
|
||||
$service->value = 'myvalue';
|
||||
$container = new ContainerBuilder();
|
||||
$container->set($canonical_name, $service);
|
||||
$bridge = new ZfExtensionManagerSfContainer();
|
||||
$bridge->setContainer($container);
|
||||
$this->assertTrue($bridge->has($name));
|
||||
$this->assertEquals($service, $bridge->get($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testReverseProxyEnabled.
|
||||
*
|
||||
* Replacements:
|
||||
* array('-' => '', '_' => '', ' ' => '', '\\' => '', '/' => '')
|
||||
*/
|
||||
public function canonicalizeNameProvider() {
|
||||
return array(
|
||||
array(
|
||||
'foobar',
|
||||
'foobar',
|
||||
),
|
||||
array(
|
||||
'foo-bar',
|
||||
'foobar',
|
||||
),
|
||||
array(
|
||||
'foo_bar',
|
||||
'foobar',
|
||||
),
|
||||
array(
|
||||
'foo bar',
|
||||
'foobar',
|
||||
),
|
||||
array(
|
||||
'foo\\bar',
|
||||
'foobar',
|
||||
),
|
||||
array(
|
||||
'foo/bar',
|
||||
'foobar',
|
||||
),
|
||||
// There is also a strtolower in canonicalizeName.
|
||||
array(
|
||||
'Foo/bAr',
|
||||
'foobar',
|
||||
),
|
||||
array(
|
||||
'foo/-_\\ bar',
|
||||
'foobar',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\ClassFinder;
|
||||
|
||||
use Composer\Autoload\ClassLoader;
|
||||
use Drupal\Component\ClassFinder\ClassFinder;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Component\ClassFinder\ClassFinder
|
||||
* @group ClassFinder
|
||||
*/
|
||||
class ClassFinderTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @covers ::findFile
|
||||
*/
|
||||
public function testFindFile() {
|
||||
$finder = new ClassFinder();
|
||||
|
||||
// The full path is returned therefore only tests with
|
||||
// assertStringEndsWith() so the test is portable.
|
||||
$this->assertStringEndsWith('core/tests/Drupal/Tests/UnitTestCase.php', $finder->findFile(UnitTestCase::class));
|
||||
$class = 'Not\\A\\Class';
|
||||
$this->assertNull($finder->findFile($class));
|
||||
|
||||
// Register an autoloader that can find this class.
|
||||
$loader = new ClassLoader();
|
||||
$loader->addClassMap([$class => __FILE__]);
|
||||
$loader->register();
|
||||
$this->assertEquals(__FILE__, $finder->findFile($class));
|
||||
// This shouldn't prevent us from finding the original file.
|
||||
$this->assertStringEndsWith('core/tests/Drupal/Tests/UnitTestCase.php', $finder->findFile(UnitTestCase::class));
|
||||
|
||||
// Clean up the additional autoloader after the test.
|
||||
$loader->unregister();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,669 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Datetime;
|
||||
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\Component\Datetime\DateTimePlus;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Component\Datetime\DateTimePlus
|
||||
* @group Datetime
|
||||
*/
|
||||
class DateTimePlusTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Test creating dates from string and array input.
|
||||
*
|
||||
* @param mixed $input
|
||||
* Input argument for DateTimePlus.
|
||||
* @param string $timezone
|
||||
* Timezone argument for DateTimePlus.
|
||||
* @param string $expected
|
||||
* Expected output from DateTimePlus::format().
|
||||
*
|
||||
* @dataProvider providerTestDates
|
||||
*/
|
||||
public function testDates($input, $timezone, $expected) {
|
||||
$date = new DateTimePlus($input, $timezone);
|
||||
$value = $date->format('c');
|
||||
|
||||
if (is_array($input)) {
|
||||
$input = var_export($input, TRUE);
|
||||
}
|
||||
$this->assertEquals($expected, $value, sprintf("Test new DateTimePlus(%s, %s): should be %s, found %s.", $input, $timezone, $expected, $value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test creating dates from string and array input.
|
||||
*
|
||||
* @param mixed $input
|
||||
* Input argument for DateTimePlus.
|
||||
* @param string $timezone
|
||||
* Timezone argument for DateTimePlus.
|
||||
* @param string $expected
|
||||
* Expected output from DateTimePlus::format().
|
||||
*
|
||||
* @dataProvider providerTestDateArrays
|
||||
*/
|
||||
public function testDateArrays($input, $timezone, $expected) {
|
||||
$date = DateTimePlus::createFromArray($input, $timezone);
|
||||
$value = $date->format('c');
|
||||
|
||||
if (is_array($input)) {
|
||||
$input = var_export($input, TRUE);
|
||||
}
|
||||
$this->assertEquals($expected, $value, sprintf("Test new DateTimePlus(%s, %s): should be %s, found %s.", $input, $timezone, $expected, $value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test date diffs.
|
||||
*
|
||||
* @param mixed $input1
|
||||
* A DateTimePlus object.
|
||||
* @param mixed $input2
|
||||
* Date argument for DateTimePlus::diff method.
|
||||
* @param bool $absolute
|
||||
* Absolute flag for DateTimePlus::diff method.
|
||||
* @param \DateInterval $expected
|
||||
* The expected result of the DateTimePlus::diff operation.
|
||||
*
|
||||
* @dataProvider providerTestDateDiff
|
||||
*/
|
||||
public function testDateDiff($input1, $input2, $absolute, \DateInterval $expected) {
|
||||
$interval = $input1->diff($input2, $absolute);
|
||||
$this->assertEquals($interval, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test date diff exception caused by invalid input.
|
||||
*
|
||||
* @param mixed $input1
|
||||
* A DateTimePlus object.
|
||||
* @param mixed $input2
|
||||
* Date argument for DateTimePlus::diff method.
|
||||
* @param bool $absolute
|
||||
* Absolute flag for DateTimePlus::diff method.
|
||||
*
|
||||
* @dataProvider providerTestInvalidDateDiff
|
||||
*/
|
||||
public function testInvalidDateDiff($input1, $input2, $absolute) {
|
||||
$this->setExpectedException(\BadMethodCallException::class, 'Method Drupal\Component\Datetime\DateTimePlus::diff expects parameter 1 to be a \DateTime or \Drupal\Component\Datetime\DateTimePlus object');
|
||||
$interval = $input1->diff($input2, $absolute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test creating dates from invalid array input.
|
||||
*
|
||||
* @param mixed $input
|
||||
* Input argument for DateTimePlus.
|
||||
* @param string $timezone
|
||||
* Timezone argument for DateTimePlus.
|
||||
*
|
||||
* @dataProvider providerTestInvalidDateArrays
|
||||
* @expectedException \Exception
|
||||
*/
|
||||
public function testInvalidDateArrays($input, $timezone) {
|
||||
$this->assertInstanceOf(
|
||||
'\Drupal\Component\DateTimePlus',
|
||||
DateTimePlus::createFromArray($input, $timezone)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test creating dates from timestamps, and manipulating timezones.
|
||||
*
|
||||
* @param int $input
|
||||
* Input argument for DateTimePlus::createFromTimestamp().
|
||||
* @param array $initial
|
||||
* An array containing:
|
||||
* - 'timezone_initial' - Timezone argument for DateTimePlus.
|
||||
* - 'format_initial' - Format argument for DateTimePlus.
|
||||
* - 'expected_initial_date' - Expected output from DateTimePlus::format().
|
||||
* - 'expected_initial_timezone' - Expected output from
|
||||
* DateTimePlus::getTimeZone()::getName().
|
||||
* - 'expected_initial_offset' - Expected output from DateTimePlus::getOffset().
|
||||
* @param array $transform
|
||||
* An array containing:
|
||||
* - 'timezone_transform' - Argument to transform date to another timezone via
|
||||
* DateTimePlus::setTimezone().
|
||||
* - 'format_transform' - Format argument to use when transforming date to
|
||||
* another timezone.
|
||||
* - 'expected_transform_date' - Expected output from DateTimePlus::format(),
|
||||
* after timezone transform.
|
||||
* - 'expected_transform_timezone' - Expected output from
|
||||
* DateTimePlus::getTimeZone()::getName(), after timezone transform.
|
||||
* - 'expected_transform_offset' - Expected output from
|
||||
* DateTimePlus::getOffset(), after timezone transform.
|
||||
*
|
||||
* @dataProvider providerTestTimestamp
|
||||
*/
|
||||
public function testTimestamp($input, array $initial, array $transform) {
|
||||
// Initialize a new date object.
|
||||
$date = DateTimePlus::createFromTimestamp($input, $initial['timezone']);
|
||||
$this->assertDateTimestamp($date, $input, $initial, $transform);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test creating dates from datetime strings.
|
||||
*
|
||||
* @param string $input
|
||||
* Input argument for DateTimePlus().
|
||||
* @param array $initial
|
||||
* @see testTimestamp()
|
||||
* @param array $transform
|
||||
* @see testTimestamp()
|
||||
*
|
||||
* @dataProvider providerTestDateTimestamp
|
||||
*/
|
||||
public function testDateTimestamp($input, array $initial, array $transform) {
|
||||
// Initialize a new date object.
|
||||
$date = new DateTimePlus($input, $initial['timezone']);
|
||||
$this->assertDateTimestamp($date, $input, $initial, $transform);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assertion helper for testTimestamp and testDateTimestamp since they need
|
||||
* different dataProviders.
|
||||
*
|
||||
* @param DateTimePlus $date
|
||||
* DateTimePlus to test.
|
||||
* @input mixed $input
|
||||
* The original input passed to the test method.
|
||||
* @param array $initial
|
||||
* @see testTimestamp()
|
||||
* @param array $transform
|
||||
* @see testTimestamp()
|
||||
*/
|
||||
public function assertDateTimestamp($date, $input, $initial, $transform) {
|
||||
// Check format.
|
||||
$value = $date->format($initial['format']);
|
||||
$this->assertEquals($initial['expected_date'], $value, sprintf("Test new DateTimePlus(%s, %s): should be %s, found %s.", $input, $initial['timezone'], $initial['expected_date'], $value));
|
||||
|
||||
// Check timezone name.
|
||||
$value = $date->getTimeZone()->getName();
|
||||
$this->assertEquals($initial['expected_timezone'], $value, sprintf("The current timezone is %s: should be %s.", $value, $initial['expected_timezone']));
|
||||
|
||||
// Check offset.
|
||||
$value = $date->getOffset();
|
||||
$this->assertEquals($initial['expected_offset'], $value, sprintf("The current offset is %s: should be %s.", $value, $initial['expected_offset']));
|
||||
|
||||
// Transform the date to another timezone.
|
||||
$date->setTimezone(new \DateTimeZone($transform['timezone']));
|
||||
|
||||
// Check transformed format.
|
||||
$value = $date->format($transform['format']);
|
||||
$this->assertEquals($transform['expected_date'], $value, sprintf("Test \$date->setTimezone(new \\DateTimeZone(%s)): should be %s, found %s.", $transform['timezone'], $transform['expected_date'], $value));
|
||||
|
||||
// Check transformed timezone.
|
||||
$value = $date->getTimeZone()->getName();
|
||||
$this->assertEquals($transform['expected_timezone'], $value, sprintf("The current timezone should be %s, found %s.", $transform['expected_timezone'], $value));
|
||||
|
||||
// Check transformed offset.
|
||||
$value = $date->getOffset();
|
||||
$this->assertEquals($transform['expected_offset'], $value, sprintf("The current offset should be %s, found %s.", $transform['expected_offset'], $value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test creating dates from format strings.
|
||||
*
|
||||
* @param string $input
|
||||
* Input argument for DateTimePlus.
|
||||
* @param string $timezone
|
||||
* Timezone argument for DateTimePlus.
|
||||
* @param string $format_date
|
||||
* Format argument for DateTimePlus::format().
|
||||
* @param string $expected
|
||||
* Expected output from DateTimePlus::format().
|
||||
*
|
||||
* @dataProvider providerTestDateFormat
|
||||
*/
|
||||
public function testDateFormat($input, $timezone, $format, $format_date, $expected) {
|
||||
$date = DateTimePlus::createFromFormat($format, $input, $timezone);
|
||||
$value = $date->format($format_date);
|
||||
$this->assertEquals($expected, $value, sprintf("Test new DateTimePlus(%s, %s, %s): should be %s, found %s.", $input, $timezone, $format, $expected, $value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invalid date handling.
|
||||
*
|
||||
* @param mixed $input
|
||||
* Input argument for DateTimePlus.
|
||||
* @param string $timezone
|
||||
* Timezone argument for DateTimePlus.
|
||||
* @param string $format
|
||||
* Format argument for DateTimePlus.
|
||||
* @param string $message
|
||||
* Message to print if no errors are thrown by the invalid dates.
|
||||
*
|
||||
* @dataProvider providerTestInvalidDates
|
||||
* @expectedException \Exception
|
||||
*/
|
||||
public function testInvalidDates($input, $timezone, $format, $message) {
|
||||
DateTimePlus::createFromFormat($format, $input, $timezone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that DrupalDateTime can detect the right timezone to use.
|
||||
* When specified or not.
|
||||
*
|
||||
* @param mixed $input
|
||||
* Input argument for DateTimePlus.
|
||||
* @param mixed $timezone
|
||||
* Timezone argument for DateTimePlus.
|
||||
* @param string $expected_timezone
|
||||
* Expected timezone returned from DateTimePlus::getTimezone::getName().
|
||||
* @param string $message
|
||||
* Message to print on test failure.
|
||||
*
|
||||
* @dataProvider providerTestDateTimezone
|
||||
*/
|
||||
public function testDateTimezone($input, $timezone, $expected_timezone, $message) {
|
||||
$date = new DateTimePlus($input, $timezone);
|
||||
$timezone = $date->getTimezone()->getName();
|
||||
$this->assertEquals($timezone, $expected_timezone, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that DrupalDateTime can detect the right timezone to use when
|
||||
* constructed from a datetime object.
|
||||
*/
|
||||
public function testDateTimezoneWithDateTimeObject() {
|
||||
// Create a date object with another date object.
|
||||
$input = new \DateTime('now', new \DateTimeZone('Pacific/Midway'));
|
||||
$timezone = NULL;
|
||||
$expected_timezone = 'Pacific/Midway';
|
||||
$message = 'DateTimePlus uses the specified timezone if provided.';
|
||||
|
||||
$date = DateTimePlus::createFromDateTime($input, $timezone);
|
||||
$timezone = $date->getTimezone()->getName();
|
||||
$this->assertEquals($timezone, $expected_timezone, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for date tests.
|
||||
*
|
||||
* @return array
|
||||
* An array of arrays, each containing the input parameters for
|
||||
* DateTimePlusTest::testDates().
|
||||
*
|
||||
* @see DateTimePlusTest::testDates()
|
||||
*/
|
||||
public function providerTestDates() {
|
||||
return array(
|
||||
// String input.
|
||||
// Create date object from datetime string.
|
||||
array('2009-03-07 10:30', 'America/Chicago', '2009-03-07T10:30:00-06:00'),
|
||||
// Same during daylight savings time.
|
||||
array('2009-06-07 10:30', 'America/Chicago', '2009-06-07T10:30:00-05:00'),
|
||||
// Create date object from date string.
|
||||
array('2009-03-07', 'America/Chicago', '2009-03-07T00:00:00-06:00'),
|
||||
// Same during daylight savings time.
|
||||
array('2009-06-07', 'America/Chicago', '2009-06-07T00:00:00-05:00'),
|
||||
// Create date object from date string.
|
||||
array('2009-03-07 10:30', 'Australia/Canberra', '2009-03-07T10:30:00+11:00'),
|
||||
// Same during daylight savings time.
|
||||
array('2009-06-07 10:30', 'Australia/Canberra', '2009-06-07T10:30:00+10:00'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for date tests.
|
||||
*
|
||||
* @return array
|
||||
* An array of arrays, each containing the input parameters for
|
||||
* DateTimePlusTest::testDates().
|
||||
*
|
||||
* @see DateTimePlusTest::testDates()
|
||||
*/
|
||||
public function providerTestDateArrays() {
|
||||
return array(
|
||||
// Array input.
|
||||
// Create date object from date array, date only.
|
||||
array(array('year' => 2010, 'month' => 2, 'day' => 28), 'America/Chicago', '2010-02-28T00:00:00-06:00'),
|
||||
// Create date object from date array with hour.
|
||||
array(array('year' => 2010, 'month' => 2, 'day' => 28, 'hour' => 10), 'America/Chicago', '2010-02-28T10:00:00-06:00'),
|
||||
// Create date object from date array, date only.
|
||||
array(array('year' => 2010, 'month' => 2, 'day' => 28), 'Europe/Berlin', '2010-02-28T00:00:00+01:00'),
|
||||
// Create date object from date array with hour.
|
||||
array(array('year' => 2010, 'month' => 2, 'day' => 28, 'hour' => 10), 'Europe/Berlin', '2010-02-28T10:00:00+01:00'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for testDateFormats.
|
||||
*
|
||||
* @return array
|
||||
* An array of arrays, each containing:
|
||||
* - 'input' - Input to DateTimePlus.
|
||||
* - 'timezone' - Timezone for DateTimePlus.
|
||||
* - 'format' - Date format for DateTimePlus.
|
||||
* - 'format_date' - Date format for use in $date->format() method.
|
||||
* - 'expected' - The expected return from DateTimePlus.
|
||||
*
|
||||
* @see testDateFormats()
|
||||
*/
|
||||
public function providerTestDateFormat() {
|
||||
return array(
|
||||
// Create a year-only date.
|
||||
array('2009', NULL, 'Y', 'Y', '2009'),
|
||||
// Create a month and year-only date.
|
||||
array('2009-10', NULL, 'Y-m', 'Y-m', '2009-10'),
|
||||
// Create a time-only date.
|
||||
array('T10:30:00', NULL, '\TH:i:s', 'H:i:s', '10:30:00'),
|
||||
// Create a time-only date.
|
||||
array('10:30:00', NULL, 'H:i:s', 'H:i:s', '10:30:00'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for testInvalidDates.
|
||||
*
|
||||
* @return array
|
||||
* An array of arrays, each containing:
|
||||
* - 'input' - Input for DateTimePlus.
|
||||
* - 'timezone' - Timezone for DateTimePlus.
|
||||
* - 'format' - Format for DateTimePlus.
|
||||
* - 'message' - Message to display on failure.
|
||||
*
|
||||
* @see testInvalidDates
|
||||
*/
|
||||
public function providerTestInvalidDates() {
|
||||
return array(
|
||||
// Test for invalid month names when we are using a short version
|
||||
// of the month.
|
||||
array('23 abc 2012', NULL, 'd M Y', "23 abc 2012 contains an invalid month name and did not produce errors."),
|
||||
// Test for invalid hour.
|
||||
array('0000-00-00T45:30:00', NULL, 'Y-m-d\TH:i:s', "0000-00-00T45:30:00 contains an invalid hour and did not produce errors."),
|
||||
// Test for invalid day.
|
||||
array('0000-00-99T05:30:00', NULL, 'Y-m-d\TH:i:s', "0000-00-99T05:30:00 contains an invalid day and did not produce errors."),
|
||||
// Test for invalid month.
|
||||
array('0000-75-00T15:30:00', NULL, 'Y-m-d\TH:i:s', "0000-75-00T15:30:00 contains an invalid month and did not produce errors."),
|
||||
// Test for invalid year.
|
||||
array('11-08-01T15:30:00', NULL, 'Y-m-d\TH:i:s', "11-08-01T15:30:00 contains an invalid year and did not produce errors."),
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testInvalidDateArrays.
|
||||
*
|
||||
* @return array
|
||||
* An array of arrays, each containing:
|
||||
* - 'input' - Input for DateTimePlus.
|
||||
* - 'timezone' - Timezone for DateTimePlus.
|
||||
*
|
||||
* @see testInvalidDateArrays
|
||||
*/
|
||||
public function providerTestInvalidDateArrays() {
|
||||
return array(
|
||||
// One year larger than the documented upper limit of checkdate().
|
||||
array(array('year' => 32768, 'month' => 1, 'day' => 8, 'hour' => 8, 'minute' => 0, 'second' => 0), 'America/Chicago'),
|
||||
// One year smaller than the documented lower limit of checkdate().
|
||||
array(array('year' => 0, 'month' => 1, 'day' => 8, 'hour' => 8, 'minute' => 0, 'second' => 0), 'America/Chicago'),
|
||||
// Test for invalid month from date array.
|
||||
array(array('year' => 2010, 'month' => 27, 'day' => 8, 'hour' => 8, 'minute' => 0, 'second' => 0), 'America/Chicago'),
|
||||
// Test for invalid hour from date array.
|
||||
array(array('year' => 2010, 'month' => 2, 'day' => 28, 'hour' => 80, 'minute' => 0, 'second' => 0), 'America/Chicago'),
|
||||
// Test for invalid minute from date array.
|
||||
array(array('year' => 2010, 'month' => 7, 'day' => 8, 'hour' => 8, 'minute' => 88, 'second' => 0), 'America/Chicago'),
|
||||
// Regression test for https://www.drupal.org/node/2084455.
|
||||
array(array('hour' => 59, 'minute' => 1, 'second' => 1), 'America/Chicago'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for testDateTimezone.
|
||||
*
|
||||
* @return array
|
||||
* An array of arrays, each containing:
|
||||
* - 'date' - Date string or object for DateTimePlus.
|
||||
* - 'timezone' - Timezone string for DateTimePlus.
|
||||
* - 'expected' - Expected return from DateTimePlus::getTimezone()::getName().
|
||||
* - 'message' - Message to display on test failure.
|
||||
*
|
||||
* @see testDateTimezone
|
||||
*/
|
||||
public function providerTestDateTimezone() {
|
||||
// Use a common date for most of the tests.
|
||||
$date_string = '2007-01-31 21:00:00';
|
||||
|
||||
// Detect the system timezone.
|
||||
$system_timezone = date_default_timezone_get();
|
||||
|
||||
return array(
|
||||
// Create a date object with an unspecified timezone, which should
|
||||
// end up using the system timezone.
|
||||
array($date_string, NULL, $system_timezone, 'DateTimePlus uses the system timezone when there is no site timezone.'),
|
||||
// Create a date object with a specified timezone name.
|
||||
array($date_string, 'America/Yellowknife', 'America/Yellowknife', 'DateTimePlus uses the specified timezone if provided.'),
|
||||
// Create a date object with a timezone object.
|
||||
array($date_string, new \DateTimeZone('Australia/Canberra'), 'Australia/Canberra', 'DateTimePlus uses the specified timezone if provided.'),
|
||||
// Create a date object with another date object.
|
||||
array(new DateTimePlus('now', 'Pacific/Midway'), NULL, 'Pacific/Midway', 'DateTimePlus uses the specified timezone if provided.'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for testTimestamp.
|
||||
*
|
||||
* @return array
|
||||
* An array of arrays, each containing the arguments required for
|
||||
* self::testTimestamp().
|
||||
*
|
||||
* @see testTimestamp()
|
||||
*/
|
||||
public function providerTestTimestamp() {
|
||||
return array(
|
||||
// Create date object from a unix timestamp and display it in
|
||||
// local time.
|
||||
array(
|
||||
'input' => 0,
|
||||
'initial' => array(
|
||||
'timezone' => 'UTC',
|
||||
'format' => 'c',
|
||||
'expected_date' => '1970-01-01T00:00:00+00:00',
|
||||
'expected_timezone' => 'UTC',
|
||||
'expected_offset' => 0,
|
||||
),
|
||||
'transform' => array(
|
||||
'timezone' => 'America/Los_Angeles',
|
||||
'format' => 'c',
|
||||
'expected_date' => '1969-12-31T16:00:00-08:00',
|
||||
'expected_timezone' => 'America/Los_Angeles',
|
||||
'expected_offset' => '-28800',
|
||||
),
|
||||
),
|
||||
// Create a date using the timestamp of zero, then display its
|
||||
// value both in UTC and the local timezone.
|
||||
array(
|
||||
'input' => 0,
|
||||
'initial' => array(
|
||||
'timezone' => 'America/Los_Angeles',
|
||||
'format' => 'c',
|
||||
'expected_date' => '1969-12-31T16:00:00-08:00',
|
||||
'expected_timezone' => 'America/Los_Angeles',
|
||||
'expected_offset' => '-28800',
|
||||
),
|
||||
'transform' => array(
|
||||
'timezone' => 'UTC',
|
||||
'format' => 'c',
|
||||
'expected_date' => '1970-01-01T00:00:00+00:00',
|
||||
'expected_timezone' => 'UTC',
|
||||
'expected_offset' => 0,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for testDateTimestamp.
|
||||
*
|
||||
* @return array
|
||||
* An array of arrays, each containing the arguments required for
|
||||
* self::testDateTimestamp().
|
||||
*
|
||||
* @see testDateTimestamp()
|
||||
*/
|
||||
public function providerTestDateTimestamp() {
|
||||
return array(
|
||||
// Create date object from datetime string in UTC, and convert
|
||||
// it to a local date.
|
||||
array(
|
||||
'input' => '1970-01-01 00:00:00',
|
||||
'initial' => array(
|
||||
'timezone' => 'UTC',
|
||||
'format' => 'c',
|
||||
'expected_date' => '1970-01-01T00:00:00+00:00',
|
||||
'expected_timezone' => 'UTC',
|
||||
'expected_offset' => 0,
|
||||
),
|
||||
'transform' => array(
|
||||
'timezone' => 'America/Los_Angeles',
|
||||
'format' => 'c',
|
||||
'expected_date' => '1969-12-31T16:00:00-08:00',
|
||||
'expected_timezone' => 'America/Los_Angeles',
|
||||
'expected_offset' => '-28800',
|
||||
),
|
||||
),
|
||||
// Convert the local time to UTC using string input.
|
||||
array(
|
||||
'input' => '1969-12-31 16:00:00',
|
||||
'initial' => array(
|
||||
'timezone' => 'America/Los_Angeles',
|
||||
'format' => 'c',
|
||||
'expected_date' => '1969-12-31T16:00:00-08:00',
|
||||
'expected_timezone' => 'America/Los_Angeles',
|
||||
'expected_offset' => '-28800',
|
||||
),
|
||||
'transform' => array(
|
||||
'timezone' => 'UTC',
|
||||
'format' => 'c',
|
||||
'expected_date' => '1970-01-01T00:00:00+00:00',
|
||||
'expected_timezone' => 'UTC',
|
||||
'expected_offset' => 0,
|
||||
),
|
||||
),
|
||||
// Convert the local time to UTC using string input.
|
||||
array(
|
||||
'input' => '1969-12-31 16:00:00',
|
||||
'initial' => array(
|
||||
'timezone' => 'Europe/Warsaw',
|
||||
'format' => 'c',
|
||||
'expected_date' => '1969-12-31T16:00:00+01:00',
|
||||
'expected_timezone' => 'Europe/Warsaw',
|
||||
'expected_offset' => '+3600',
|
||||
),
|
||||
'transform' => array(
|
||||
'timezone' => 'UTC',
|
||||
'format' => 'c',
|
||||
'expected_date' => '1969-12-31T15:00:00+00:00',
|
||||
'expected_timezone' => 'UTC',
|
||||
'expected_offset' => 0,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for date tests.
|
||||
*
|
||||
* @return array
|
||||
* An array of arrays, each containing the input parameters for
|
||||
* DateTimePlusTest::testDateDiff().
|
||||
*
|
||||
* @see DateTimePlusTest::testDateDiff()
|
||||
*/
|
||||
public function providerTestDateDiff() {
|
||||
|
||||
$empty_interval = new \DateInterval('PT0S');
|
||||
|
||||
$positive_19_hours = new \DateInterval('PT19H');
|
||||
|
||||
$positive_18_hours = new \DateInterval('PT18H');
|
||||
|
||||
$positive_1_hour = new \DateInterval('PT1H');
|
||||
|
||||
$negative_1_hour = new \DateInterval('PT1H');
|
||||
$negative_1_hour->invert = 1;
|
||||
|
||||
return array(
|
||||
// There should be a 19 hour time interval between
|
||||
// new years in Sydney and new years in LA in year 2000.
|
||||
array(
|
||||
'input2' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '2000-01-01 00:00:00', new \DateTimeZone('Australia/Sydney')),
|
||||
'input1' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '2000-01-01 00:00:00', new \DateTimeZone('America/Los_Angeles')),
|
||||
'absolute' => FALSE,
|
||||
'expected' => $positive_19_hours,
|
||||
),
|
||||
// In 1970 Sydney did not observe daylight savings time
|
||||
// So there is only a 18 hour time interval.
|
||||
array(
|
||||
'input2' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '1970-01-01 00:00:00', new \DateTimeZone('Australia/Sydney')),
|
||||
'input1' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '1970-01-01 00:00:00', new \DateTimeZone('America/Los_Angeles')),
|
||||
'absolute' => FALSE,
|
||||
'expected' => $positive_18_hours,
|
||||
),
|
||||
array(
|
||||
'input1' => DateTimePlus::createFromFormat('U', 3600, new \DateTimeZone('America/Los_Angeles')),
|
||||
'input2' => DateTimePlus::createFromFormat('U', 0, new \DateTimeZone('UTC')),
|
||||
'absolute' => FALSE,
|
||||
'expected' => $negative_1_hour,
|
||||
),
|
||||
array(
|
||||
'input1' => DateTimePlus::createFromFormat('U', 3600),
|
||||
'input2' => DateTimePlus::createFromFormat('U', 0),
|
||||
'absolute' => FALSE,
|
||||
'expected' => $negative_1_hour,
|
||||
),
|
||||
array(
|
||||
'input1' => DateTimePlus::createFromFormat('U', 3600),
|
||||
'input2' => \DateTime::createFromFormat('U', 0),
|
||||
'absolute' => FALSE,
|
||||
'expected' => $negative_1_hour,
|
||||
),
|
||||
array(
|
||||
'input1' => DateTimePlus::createFromFormat('U', 3600),
|
||||
'input2' => DateTimePlus::createFromFormat('U', 0),
|
||||
'absolute' => TRUE,
|
||||
'expected' => $positive_1_hour,
|
||||
),
|
||||
array(
|
||||
'input1' => DateTimePlus::createFromFormat('U', 3600),
|
||||
'input2' => \DateTime::createFromFormat('U', 0),
|
||||
'absolute' => TRUE,
|
||||
'expected' => $positive_1_hour,
|
||||
),
|
||||
array(
|
||||
'input1' => DateTimePlus::createFromFormat('U', 0),
|
||||
'input2' => DateTimePlus::createFromFormat('U', 0),
|
||||
'absolute' => FALSE,
|
||||
'expected' => $empty_interval,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for date tests.
|
||||
*
|
||||
* @return array
|
||||
* An array of arrays, each containing the input parameters for
|
||||
* DateTimePlusTest::testInvalidDateDiff().
|
||||
*
|
||||
* @see DateTimePlusTest::testInvalidDateDiff()
|
||||
*/
|
||||
public function providerTestInvalidDateDiff() {
|
||||
return array(
|
||||
array(
|
||||
'input1' => DateTimePlus::createFromFormat('U', 3600),
|
||||
'input2' => '1970-01-01 00:00:00',
|
||||
'absolute' => FALSE,
|
||||
),
|
||||
array(
|
||||
'input1' => DateTimePlus::createFromFormat('U', 3600),
|
||||
'input2' => NULL,
|
||||
'absolute' => FALSE,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,684 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\Component\DependencyInjection\Dumper\OptimizedPhpArrayDumperTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\Component\DependencyInjection\Dumper {
|
||||
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\Parameter;
|
||||
use Symfony\Component\ExpressionLanguage\Expression;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Component\DependencyInjection\Dumper\OptimizedPhpArrayDumper
|
||||
* @group DependencyInjection
|
||||
*/
|
||||
class OptimizedPhpArrayDumperTest extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
/**
|
||||
* The container builder instance.
|
||||
*
|
||||
* @var \Symfony\Component\DependencyInjection\ContainerBuilder
|
||||
*/
|
||||
protected $containerBuilder;
|
||||
|
||||
/**
|
||||
* The definition for the container to build in tests.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $containerDefinition;
|
||||
|
||||
/**
|
||||
* Whether the dumper uses the machine-optimized format or not.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $machineFormat = TRUE;
|
||||
|
||||
/**
|
||||
* Stores the dumper class to use.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $dumperClass = '\Drupal\Component\DependencyInjection\Dumper\OptimizedPhpArrayDumper';
|
||||
|
||||
/**
|
||||
* The dumper instance.
|
||||
*
|
||||
* @var \Symfony\Component\DependencyInjection\Dumper\DumperInterface
|
||||
*/
|
||||
protected $dumper;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
// Setup a mock container builder.
|
||||
$this->containerBuilder = $this->prophesize('\Symfony\Component\DependencyInjection\ContainerBuilder');
|
||||
$this->containerBuilder->getAliases()->willReturn(array());
|
||||
$this->containerBuilder->getParameterBag()->willReturn(new ParameterBag());
|
||||
$this->containerBuilder->getDefinitions()->willReturn(NULL);
|
||||
$this->containerBuilder->isFrozen()->willReturn(TRUE);
|
||||
|
||||
$definition = array();
|
||||
$definition['aliases'] = array();
|
||||
$definition['parameters'] = array();
|
||||
$definition['services'] = array();
|
||||
$definition['frozen'] = TRUE;
|
||||
$definition['machine_format'] = $this->machineFormat;
|
||||
|
||||
$this->containerDefinition = $definition;
|
||||
|
||||
// Create the dumper.
|
||||
$this->dumper = new $this->dumperClass($this->containerBuilder->reveal());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that an empty container works properly.
|
||||
*
|
||||
* @covers ::dump
|
||||
* @covers ::getArray
|
||||
* @covers ::supportsMachineFormat
|
||||
*/
|
||||
public function testDumpForEmptyContainer() {
|
||||
$serialized_definition = $this->dumper->dump();
|
||||
$this->assertEquals(serialize($this->containerDefinition), $serialized_definition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that alias processing works properly.
|
||||
*
|
||||
* @covers ::getAliases
|
||||
*
|
||||
* @dataProvider getAliasesDataProvider
|
||||
*/
|
||||
public function testGetAliases($aliases, $definition_aliases) {
|
||||
$this->containerDefinition['aliases'] = $definition_aliases;
|
||||
$this->containerBuilder->getAliases()->willReturn($aliases);
|
||||
$this->assertEquals($this->containerDefinition, $this->dumper->getArray(), 'Expected definition matches dump.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testGetAliases().
|
||||
*
|
||||
* @return array[]
|
||||
* Returns data-set elements with:
|
||||
* - aliases as returned by ContainerBuilder.
|
||||
* - aliases as expected in the container definition.
|
||||
*/
|
||||
public function getAliasesDataProvider() {
|
||||
return array(
|
||||
array(array(), array()),
|
||||
array(
|
||||
array('foo' => 'foo.alias'),
|
||||
array('foo' => 'foo.alias'),
|
||||
),
|
||||
array(
|
||||
array('foo' => 'foo.alias', 'foo.alias' => 'foo.alias.alias'),
|
||||
array('foo' => 'foo.alias.alias', 'foo.alias' => 'foo.alias.alias'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that parameter processing works properly.
|
||||
*
|
||||
* @covers ::getParameters
|
||||
* @covers ::prepareParameters
|
||||
* @covers ::escape
|
||||
* @covers ::dumpValue
|
||||
* @covers ::getReferenceCall
|
||||
*
|
||||
* @dataProvider getParametersDataProvider
|
||||
*/
|
||||
public function testGetParameters($parameters, $definition_parameters, $is_frozen) {
|
||||
$this->containerDefinition['parameters'] = $definition_parameters;
|
||||
$this->containerDefinition['frozen'] = $is_frozen;
|
||||
|
||||
$parameter_bag = new ParameterBag($parameters);
|
||||
$this->containerBuilder->getParameterBag()->willReturn($parameter_bag);
|
||||
$this->containerBuilder->isFrozen()->willReturn($is_frozen);
|
||||
|
||||
if (isset($parameters['reference'])) {
|
||||
$definition = new Definition('\stdClass');
|
||||
$this->containerBuilder->getDefinition('referenced_service')->willReturn($definition);
|
||||
}
|
||||
|
||||
$this->assertEquals($this->containerDefinition, $this->dumper->getArray(), 'Expected definition matches dump.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testGetParameters().
|
||||
*
|
||||
* @return array[]
|
||||
* Returns data-set elements with:
|
||||
* - parameters as returned by ContainerBuilder.
|
||||
* - parameters as expected in the container definition.
|
||||
* - frozen value
|
||||
*/
|
||||
public function getParametersDataProvider() {
|
||||
return array(
|
||||
array(array(), array(), TRUE),
|
||||
array(
|
||||
array('foo' => 'value_foo'),
|
||||
array('foo' => 'value_foo'),
|
||||
TRUE,
|
||||
),
|
||||
array(
|
||||
array('foo' => array('llama' => 'yes')),
|
||||
array('foo' => array('llama' => 'yes')),
|
||||
TRUE,
|
||||
),
|
||||
array(
|
||||
array('foo' => '%llama%', 'llama' => 'yes'),
|
||||
array('foo' => '%%llama%%', 'llama' => 'yes'),
|
||||
TRUE,
|
||||
),
|
||||
array(
|
||||
array('foo' => '%llama%', 'llama' => 'yes'),
|
||||
array('foo' => '%llama%', 'llama' => 'yes'),
|
||||
FALSE,
|
||||
),
|
||||
array(
|
||||
array('reference' => new Reference('referenced_service')),
|
||||
array('reference' => $this->getServiceCall('referenced_service')),
|
||||
TRUE,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that service processing works properly.
|
||||
*
|
||||
* @covers ::getServiceDefinitions
|
||||
* @covers ::getServiceDefinition
|
||||
* @covers ::dumpMethodCalls
|
||||
* @covers ::dumpCollection
|
||||
* @covers ::dumpCallable
|
||||
* @covers ::dumpValue
|
||||
* @covers ::getPrivateServiceCall
|
||||
* @covers ::getReferenceCall
|
||||
* @covers ::getServiceCall
|
||||
* @covers ::getParameterCall
|
||||
*
|
||||
* @dataProvider getDefinitionsDataProvider
|
||||
*/
|
||||
public function testGetServiceDefinitions($services, $definition_services) {
|
||||
$this->containerDefinition['services'] = $definition_services;
|
||||
|
||||
$this->containerBuilder->getDefinitions()->willReturn($services);
|
||||
|
||||
$bar_definition = new Definition('\stdClass');
|
||||
$this->containerBuilder->getDefinition('bar')->willReturn($bar_definition);
|
||||
|
||||
$private_definition = new Definition('\stdClass');
|
||||
$private_definition->setPublic(FALSE);
|
||||
|
||||
$this->containerBuilder->getDefinition('private_definition')->willReturn($private_definition);
|
||||
|
||||
$this->assertEquals($this->containerDefinition, $this->dumper->getArray(), 'Expected definition matches dump.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testGetServiceDefinitions().
|
||||
*
|
||||
* @return array[]
|
||||
* Returns data-set elements with:
|
||||
* - parameters as returned by ContainerBuilder.
|
||||
* - parameters as expected in the container definition.
|
||||
* - frozen value
|
||||
*/
|
||||
public function getDefinitionsDataProvider() {
|
||||
$base_service_definition = array(
|
||||
'class' => '\stdClass',
|
||||
'public' => TRUE,
|
||||
'file' => FALSE,
|
||||
'synthetic' => FALSE,
|
||||
'lazy' => FALSE,
|
||||
'arguments' => array(),
|
||||
'arguments_count' => 0,
|
||||
'properties' => array(),
|
||||
'calls' => array(),
|
||||
'scope' => ContainerInterface::SCOPE_CONTAINER,
|
||||
'shared' => TRUE,
|
||||
'factory' => FALSE,
|
||||
'configurator' => FALSE,
|
||||
);
|
||||
|
||||
// Test basic flags.
|
||||
$service_definitions[] = array() + $base_service_definition;
|
||||
|
||||
$service_definitions[] = array(
|
||||
'public' => FALSE,
|
||||
) + $base_service_definition;
|
||||
|
||||
$service_definitions[] = array(
|
||||
'file' => 'test_include.php',
|
||||
) + $base_service_definition;
|
||||
|
||||
$service_definitions[] = array(
|
||||
'synthetic' => TRUE,
|
||||
) + $base_service_definition;
|
||||
|
||||
$service_definitions[] = array(
|
||||
'shared' => FALSE,
|
||||
) + $base_service_definition;
|
||||
|
||||
$service_definitions[] = array(
|
||||
'lazy' => TRUE,
|
||||
) + $base_service_definition;
|
||||
|
||||
// Test a basic public Reference.
|
||||
$service_definitions[] = array(
|
||||
'arguments' => array('foo', new Reference('bar')),
|
||||
'arguments_count' => 2,
|
||||
'arguments_expected' => $this->getCollection(array('foo', $this->getServiceCall('bar'))),
|
||||
) + $base_service_definition;
|
||||
|
||||
// Test a public reference that should not throw an Exception.
|
||||
$reference = new Reference('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE);
|
||||
$service_definitions[] = array(
|
||||
'arguments' => array($reference),
|
||||
'arguments_count' => 1,
|
||||
'arguments_expected' => $this->getCollection(array($this->getServiceCall('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE))),
|
||||
) + $base_service_definition;
|
||||
|
||||
// Test a private shared service, denoted by having a Reference.
|
||||
$private_definition = array(
|
||||
'class' => '\stdClass',
|
||||
'public' => FALSE,
|
||||
'arguments_count' => 0,
|
||||
);
|
||||
|
||||
$service_definitions[] = array(
|
||||
'arguments' => array('foo', new Reference('private_definition')),
|
||||
'arguments_count' => 2,
|
||||
'arguments_expected' => $this->getCollection(array(
|
||||
'foo',
|
||||
$this->getPrivateServiceCall('private_definition', $private_definition, TRUE),
|
||||
)),
|
||||
) + $base_service_definition;
|
||||
|
||||
// Test a private non-shared service, denoted by having a Definition.
|
||||
$private_definition_object = new Definition('\stdClass');
|
||||
$private_definition_object->setPublic(FALSE);
|
||||
|
||||
$service_definitions[] = array(
|
||||
'arguments' => array('foo', $private_definition_object),
|
||||
'arguments_count' => 2,
|
||||
'arguments_expected' => $this->getCollection(array(
|
||||
'foo',
|
||||
$this->getPrivateServiceCall(NULL, $private_definition),
|
||||
)),
|
||||
) + $base_service_definition;
|
||||
|
||||
// Test a deep collection without a reference.
|
||||
$service_definitions[] = array(
|
||||
'arguments' => array(array(array('foo'))),
|
||||
'arguments_count' => 1,
|
||||
) + $base_service_definition;
|
||||
|
||||
// Test a deep collection with a reference to resolve.
|
||||
$service_definitions[] = array(
|
||||
'arguments' => array(array(new Reference('bar'))),
|
||||
'arguments_count' => 1,
|
||||
'arguments_expected' => $this->getCollection(array($this->getCollection(array($this->getServiceCall('bar'))))),
|
||||
) + $base_service_definition;
|
||||
|
||||
// Test a collection with a variable to resolve.
|
||||
$service_definitions[] = array(
|
||||
'arguments' => array(new Parameter('llama_parameter')),
|
||||
'arguments_count' => 1,
|
||||
'arguments_expected' => $this->getCollection(array($this->getParameterCall('llama_parameter'))),
|
||||
) + $base_service_definition;
|
||||
|
||||
// Test objects that have _serviceId property.
|
||||
$drupal_service = new \stdClass();
|
||||
$drupal_service->_serviceId = 'bar';
|
||||
|
||||
$service_definitions[] = array(
|
||||
'arguments' => array($drupal_service),
|
||||
'arguments_count' => 1,
|
||||
'arguments_expected' => $this->getCollection(array($this->getServiceCall('bar'))),
|
||||
) + $base_service_definition;
|
||||
|
||||
// Test getMethodCalls.
|
||||
$calls = array(
|
||||
array('method', $this->getCollection(array())),
|
||||
array('method2', $this->getCollection(array())),
|
||||
);
|
||||
$service_definitions[] = array(
|
||||
'calls' => $calls,
|
||||
) + $base_service_definition;
|
||||
|
||||
$service_definitions[] = array(
|
||||
'scope' => ContainerInterface::SCOPE_PROTOTYPE,
|
||||
'shared' => FALSE,
|
||||
) + $base_service_definition;
|
||||
|
||||
$service_definitions[] = array(
|
||||
'shared' => FALSE,
|
||||
) + $base_service_definition;
|
||||
|
||||
// Test factory.
|
||||
$service_definitions[] = array(
|
||||
'factory' => array(new Reference('bar'), 'factoryMethod'),
|
||||
'factory_expected' => array($this->getServiceCall('bar'), 'factoryMethod'),
|
||||
) + $base_service_definition;
|
||||
|
||||
// Test invalid factory - needed to test deep dumpValue().
|
||||
$service_definitions[] = array(
|
||||
'factory' => array(array('foo', 'llama'), 'factoryMethod'),
|
||||
) + $base_service_definition;
|
||||
|
||||
// Test properties.
|
||||
$service_definitions[] = array(
|
||||
'properties' => array('_value' => 'llama'),
|
||||
) + $base_service_definition;
|
||||
|
||||
// Test configurator.
|
||||
$service_definitions[] = array(
|
||||
'configurator' => array(new Reference('bar'), 'configureService'),
|
||||
'configurator_expected' => array($this->getServiceCall('bar'), 'configureService'),
|
||||
) + $base_service_definition;
|
||||
|
||||
$services_provided = array();
|
||||
$services_provided[] = array(
|
||||
array(),
|
||||
array(),
|
||||
);
|
||||
|
||||
foreach ($service_definitions as $service_definition) {
|
||||
$definition = $this->prophesize('\Symfony\Component\DependencyInjection\Definition');
|
||||
$definition->getClass()->willReturn($service_definition['class']);
|
||||
$definition->isPublic()->willReturn($service_definition['public']);
|
||||
$definition->getFile()->willReturn($service_definition['file']);
|
||||
$definition->isSynthetic()->willReturn($service_definition['synthetic']);
|
||||
$definition->isLazy()->willReturn($service_definition['lazy']);
|
||||
$definition->getArguments()->willReturn($service_definition['arguments']);
|
||||
$definition->getProperties()->willReturn($service_definition['properties']);
|
||||
$definition->getMethodCalls()->willReturn($service_definition['calls']);
|
||||
$definition->getScope()->willReturn($service_definition['scope']);
|
||||
$definition->isShared()->willReturn($service_definition['shared']);
|
||||
$definition->getDecoratedService()->willReturn(NULL);
|
||||
$definition->getFactory()->willReturn($service_definition['factory']);
|
||||
$definition->getConfigurator()->willReturn($service_definition['configurator']);
|
||||
|
||||
// Preserve order.
|
||||
$filtered_service_definition = array();
|
||||
foreach ($base_service_definition as $key => $value) {
|
||||
$filtered_service_definition[$key] = $service_definition[$key];
|
||||
unset($service_definition[$key]);
|
||||
|
||||
if ($key == 'class' || $key == 'arguments_count') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($filtered_service_definition[$key] === $base_service_definition[$key]) {
|
||||
unset($filtered_service_definition[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
// Add remaining properties.
|
||||
$filtered_service_definition += $service_definition;
|
||||
|
||||
// Allow to set _expected values.
|
||||
foreach (array('arguments', 'factory', 'configurator') as $key) {
|
||||
$expected = $key . '_expected';
|
||||
if (isset($filtered_service_definition[$expected])) {
|
||||
$filtered_service_definition[$key] = $filtered_service_definition[$expected];
|
||||
unset($filtered_service_definition[$expected]);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any remaining scope.
|
||||
unset($filtered_service_definition['scope']);
|
||||
|
||||
if (isset($filtered_service_definition['public']) && $filtered_service_definition['public'] === FALSE) {
|
||||
$services_provided[] = array(
|
||||
array('foo_service' => $definition->reveal()),
|
||||
array(),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
$services_provided[] = array(
|
||||
array('foo_service' => $definition->reveal()),
|
||||
array('foo_service' => $this->serializeDefinition($filtered_service_definition)),
|
||||
);
|
||||
}
|
||||
|
||||
return $services_provided;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to serialize a definition.
|
||||
*
|
||||
* Used to override serialization.
|
||||
*/
|
||||
protected function serializeDefinition(array $service_definition) {
|
||||
return serialize($service_definition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to return a service definition.
|
||||
*/
|
||||
protected function getServiceCall($id, $invalid_behavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) {
|
||||
return (object) array(
|
||||
'type' => 'service',
|
||||
'id' => $id,
|
||||
'invalidBehavior' => $invalid_behavior,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the correct InvalidArgumentException is thrown for getScope().
|
||||
*
|
||||
* @covers ::getServiceDefinition
|
||||
*
|
||||
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function testGetServiceDefinitionWithInvalidScope() {
|
||||
$bar_definition = new Definition('\stdClass');
|
||||
$bar_definition->setScope('foo_scope');
|
||||
$services['bar'] = $bar_definition;
|
||||
|
||||
$this->containerBuilder->getDefinitions()->willReturn($services);
|
||||
$this->dumper->getArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that references to aliases work correctly.
|
||||
*
|
||||
* @covers ::getReferenceCall
|
||||
*
|
||||
* @dataProvider publicPrivateDataProvider
|
||||
*/
|
||||
public function testGetServiceDefinitionWithReferenceToAlias($public) {
|
||||
$bar_definition = new Definition('\stdClass');
|
||||
$bar_definition_php_array = array(
|
||||
'class' => '\stdClass',
|
||||
);
|
||||
if (!$public) {
|
||||
$bar_definition->setPublic(FALSE);
|
||||
$bar_definition_php_array['public'] = FALSE;
|
||||
}
|
||||
$bar_definition_php_array['arguments_count'] = 0;
|
||||
|
||||
$services['bar'] = $bar_definition;
|
||||
|
||||
$aliases['bar.alias'] = 'bar';
|
||||
|
||||
$foo = new Definition('\stdClass');
|
||||
$foo->addArgument(new Reference('bar.alias'));
|
||||
|
||||
$services['foo'] = $foo;
|
||||
|
||||
$this->containerBuilder->getAliases()->willReturn($aliases);
|
||||
$this->containerBuilder->getDefinitions()->willReturn($services);
|
||||
$this->containerBuilder->getDefinition('bar')->willReturn($bar_definition);
|
||||
$dump = $this->dumper->getArray();
|
||||
if ($public) {
|
||||
$service_definition = $this->getServiceCall('bar');
|
||||
}
|
||||
else {
|
||||
$service_definition = $this->getPrivateServiceCall('bar', $bar_definition_php_array, TRUE);
|
||||
}
|
||||
$data = array(
|
||||
'class' => '\stdClass',
|
||||
'arguments' => $this->getCollection(array(
|
||||
$service_definition,
|
||||
)),
|
||||
'arguments_count' => 1,
|
||||
);
|
||||
$this->assertEquals($this->serializeDefinition($data), $dump['services']['foo'], 'Expected definition matches dump.');
|
||||
}
|
||||
|
||||
public function publicPrivateDataProvider() {
|
||||
return array(
|
||||
array(TRUE),
|
||||
array(FALSE),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that getDecoratedService() is unsupported.
|
||||
*
|
||||
* Tests that the correct InvalidArgumentException is thrown for
|
||||
* getDecoratedService().
|
||||
*
|
||||
* @covers ::getServiceDefinition
|
||||
*
|
||||
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function testGetServiceDefinitionForDecoratedService() {
|
||||
$bar_definition = new Definition('\stdClass');
|
||||
$bar_definition->setDecoratedService(new Reference('foo'));
|
||||
$services['bar'] = $bar_definition;
|
||||
|
||||
$this->containerBuilder->getDefinitions()->willReturn($services);
|
||||
$this->dumper->getArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the correct RuntimeException is thrown for expressions.
|
||||
*
|
||||
* @covers ::dumpValue
|
||||
*
|
||||
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
|
||||
*/
|
||||
public function testGetServiceDefinitionForExpression() {
|
||||
$expression = new Expression();
|
||||
|
||||
$bar_definition = new Definition('\stdClass');
|
||||
$bar_definition->addArgument($expression);
|
||||
$services['bar'] = $bar_definition;
|
||||
|
||||
$this->containerBuilder->getDefinitions()->willReturn($services);
|
||||
$this->dumper->getArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the correct RuntimeException is thrown for dumping an object.
|
||||
*
|
||||
* @covers ::dumpValue
|
||||
*
|
||||
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
|
||||
*/
|
||||
public function testGetServiceDefinitionForObject() {
|
||||
$service = new \stdClass();
|
||||
|
||||
$bar_definition = new Definition('\stdClass');
|
||||
$bar_definition->addArgument($service);
|
||||
$services['bar'] = $bar_definition;
|
||||
|
||||
$this->containerBuilder->getDefinitions()->willReturn($services);
|
||||
$this->dumper->getArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the correct RuntimeException is thrown for dumping a resource.
|
||||
*
|
||||
* @covers ::dumpValue
|
||||
*
|
||||
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
|
||||
*/
|
||||
public function testGetServiceDefinitionForResource() {
|
||||
$resource = fopen('php://memory', 'r');
|
||||
|
||||
$bar_definition = new Definition('\stdClass');
|
||||
$bar_definition->addArgument($resource);
|
||||
$services['bar'] = $bar_definition;
|
||||
|
||||
$this->containerBuilder->getDefinitions()->willReturn($services);
|
||||
$this->dumper->getArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to return a private service definition.
|
||||
*/
|
||||
protected function getPrivateServiceCall($id, $service_definition, $shared = FALSE) {
|
||||
if (!$id) {
|
||||
$hash = sha1(serialize($service_definition));
|
||||
$id = 'private__' . $hash;
|
||||
}
|
||||
return (object) array(
|
||||
'type' => 'private_service',
|
||||
'id' => $id,
|
||||
'value' => $service_definition,
|
||||
'shared' => $shared,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to return a machine-optimized collection.
|
||||
*/
|
||||
protected function getCollection($collection, $resolve = TRUE) {
|
||||
return (object) array(
|
||||
'type' => 'collection',
|
||||
'value' => $collection,
|
||||
'resolve' => $resolve,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to return a parameter definition.
|
||||
*/
|
||||
protected function getParameterCall($name) {
|
||||
return (object) array(
|
||||
'type' => 'parameter',
|
||||
'name' => $name,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* As Drupal Core does not ship with ExpressionLanguage component we need to
|
||||
* define a dummy, else it cannot be tested.
|
||||
*/
|
||||
namespace Symfony\Component\ExpressionLanguage {
|
||||
if (!class_exists('\Symfony\Component\ExpressionLanguage\Expression')) {
|
||||
/**
|
||||
* Dummy class to ensure non-existent Symfony component can be tested.
|
||||
*/
|
||||
class Expression {
|
||||
|
||||
/**
|
||||
* Gets the string representation of the expression.
|
||||
*/
|
||||
public function __toString() {
|
||||
return 'dummy_expression';
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\DependencyInjection\Dumper;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Component\DependencyInjection\Dumper\PhpArrayDumper
|
||||
* @group DependencyInjection
|
||||
*/
|
||||
class PhpArrayDumperTest extends OptimizedPhpArrayDumperTest {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
$this->machineFormat = FALSE;
|
||||
$this->dumperClass = '\Drupal\Component\DependencyInjection\Dumper\PhpArrayDumper';
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function serializeDefinition(array $service_definition) {
|
||||
return $service_definition;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getServiceCall($id, $invalid_behavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) {
|
||||
if ($invalid_behavior !== ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) {
|
||||
return sprintf('@?%s', $id);
|
||||
}
|
||||
|
||||
return sprintf('@%s', $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getParameterCall($name) {
|
||||
return '%' . $name . '%';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getCollection($collection, $resolve = TRUE) {
|
||||
return $collection;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains a test function for container 'file' include testing.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Test function for container testing.
|
||||
*
|
||||
* @return string
|
||||
* A string just for testing.
|
||||
*/
|
||||
function container_test_file_service_test_service_function() {
|
||||
return 'Hello Container';
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\DependencyInjection;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Component\DependencyInjection\PhpArrayContainer
|
||||
* @group DependencyInjection
|
||||
*/
|
||||
class PhpArrayContainerTest extends ContainerTest {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
$this->machineFormat = FALSE;
|
||||
$this->containerClass = '\Drupal\Component\DependencyInjection\PhpArrayContainer';
|
||||
$this->containerDefinition = $this->getMockContainerDefinition();
|
||||
$this->container = new $this->containerClass($this->containerDefinition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to return a service definition.
|
||||
*/
|
||||
protected function getServiceCall($id, $invalid_behavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) {
|
||||
if ($invalid_behavior !== ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) {
|
||||
return sprintf('@?%s', $id);
|
||||
}
|
||||
|
||||
return sprintf('@%s', $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to return a service definition.
|
||||
*/
|
||||
protected function getParameterCall($name) {
|
||||
return '%' . $name . '%';
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to return a machine-optimized '@notation'.
|
||||
*/
|
||||
protected function getCollection($collection, $resolve = TRUE) {
|
||||
return $collection;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Diff;
|
||||
|
||||
use Drupal\Component\Diff\Diff;
|
||||
use Drupal\Component\Diff\DiffFormatter;
|
||||
|
||||
/**
|
||||
* Test DiffFormatter classes.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\Component\Diff\DiffFormatter
|
||||
*
|
||||
* @group Diff
|
||||
*/
|
||||
class DiffFormatterTest extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* - Expected formatted diff output.
|
||||
* - First array of text to diff.
|
||||
* - Second array of text to diff.
|
||||
*/
|
||||
public function provideTestDiff() {
|
||||
return [
|
||||
'empty' => ['', [], []],
|
||||
'add' => [
|
||||
"3a3\n> line2a\n",
|
||||
['line1', 'line2', 'line3'],
|
||||
['line1', 'line2', 'line2a', 'line3'],
|
||||
],
|
||||
'delete' => [
|
||||
"3d3\n< line2a\n",
|
||||
['line1', 'line2', 'line2a', 'line3'],
|
||||
['line1', 'line2', 'line3'],
|
||||
],
|
||||
'change' => [
|
||||
"3c3\n< line2a\n---\n> line2b\n",
|
||||
['line1', 'line2', 'line2a', 'line3'],
|
||||
['line1', 'line2', 'line2b', 'line3'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether op classes returned by DiffEngine::diff() match expectations.
|
||||
*
|
||||
* @covers ::format
|
||||
* @dataProvider provideTestDiff
|
||||
*/
|
||||
public function testDiff($expected, $from, $to) {
|
||||
$diff = new Diff($from, $to);
|
||||
$formatter = new DiffFormatter();
|
||||
$output = $formatter->format($diff);
|
||||
$this->assertEquals($expected, $output);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Diff\Engine;
|
||||
|
||||
use Drupal\Component\Diff\Engine\DiffEngine;
|
||||
use Drupal\Component\Diff\Engine\DiffOpAdd;
|
||||
use Drupal\Component\Diff\Engine\DiffOpCopy;
|
||||
use Drupal\Component\Diff\Engine\DiffOpChange;
|
||||
use Drupal\Component\Diff\Engine\DiffOpDelete;
|
||||
|
||||
/**
|
||||
* Test DiffEngine class.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\Component\Diff\Engine\DiffEngine
|
||||
*
|
||||
* @group Diff
|
||||
*/
|
||||
class DiffEngineTest extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* - Expected output in terms of return class. A list of class names
|
||||
* expected to be returned by DiffEngine::diff().
|
||||
* - An array of strings to change from.
|
||||
* - An array of strings to change to.
|
||||
*/
|
||||
public function provideTestDiff() {
|
||||
return [
|
||||
'empty' => [[], [], []],
|
||||
'add' => [[DiffOpAdd::class], [], ['a']],
|
||||
'copy' => [[DiffOpCopy::class], ['a'], ['a']],
|
||||
'change' => [[DiffOpChange::class], ['a'], ['b']],
|
||||
'copy-and-change' => [
|
||||
[
|
||||
DiffOpCopy::class,
|
||||
DiffOpChange::class,
|
||||
],
|
||||
['a', 'b'],
|
||||
['a', 'c'],
|
||||
],
|
||||
'copy-change-copy' => [
|
||||
[
|
||||
DiffOpCopy::class,
|
||||
DiffOpChange::class,
|
||||
DiffOpCopy::class,
|
||||
],
|
||||
['a', 'b', 'd'],
|
||||
['a', 'c', 'd'],
|
||||
],
|
||||
'copy-change-copy-add' => [
|
||||
[
|
||||
DiffOpCopy::class,
|
||||
DiffOpChange::class,
|
||||
DiffOpCopy::class,
|
||||
DiffOpAdd::class,
|
||||
],
|
||||
['a', 'b', 'd'],
|
||||
['a', 'c', 'd', 'e'],
|
||||
],
|
||||
'copy-delete' => [
|
||||
[
|
||||
DiffOpCopy::class,
|
||||
DiffOpDelete::class,
|
||||
],
|
||||
['a', 'b', 'd'],
|
||||
['a'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether op classes returned by DiffEngine::diff() match expectations.
|
||||
*
|
||||
* @covers ::diff
|
||||
* @dataProvider provideTestDiff
|
||||
*/
|
||||
public function testDiff($expected, $from, $to) {
|
||||
$diff_engine = new DiffEngine();
|
||||
$diff = $diff_engine->diff($from, $to);
|
||||
// Make sure we have the same number of results as expected.
|
||||
$this->assertCount(count($expected), $diff);
|
||||
// Make sure the diff objects match our expectations.
|
||||
foreach ($expected as $index => $op_class) {
|
||||
$this->assertEquals($op_class, get_class($diff[$index]));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Diff\Engine;
|
||||
|
||||
use Drupal\Component\Diff\Engine\DiffOp;
|
||||
|
||||
/**
|
||||
* Test DiffOp base class.
|
||||
*
|
||||
* The only significant behavior here is that ::reverse() should throw an error
|
||||
* if not overridden. In versions of this code in other projects, reverse() is
|
||||
* marked as abstract, which enforces some of this behavior.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\Component\Diff\Engine\DiffOp
|
||||
*
|
||||
* @group Diff
|
||||
*/
|
||||
class DiffOpTest extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
/**
|
||||
* DiffOp::reverse() always throws an error.
|
||||
*
|
||||
* @covers ::reverse
|
||||
*/
|
||||
public function testReverse() {
|
||||
$this->setExpectedException(\PHPUnit_Framework_Error::class);
|
||||
$op = new DiffOp();
|
||||
$result = $op->reverse();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Diff\Engine;
|
||||
|
||||
use Drupal\Component\Diff\Engine\HWLDFWordAccumulator;
|
||||
|
||||
/**
|
||||
* Test HWLDFWordAccumulator.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\Component\Diff\Engine\HWLDFWordAccumulator
|
||||
*
|
||||
* @group Diff
|
||||
*/
|
||||
class HWLDFWordAccumulatorTest extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
/**
|
||||
* Verify that we only get back a NBSP from an empty accumulator.
|
||||
*
|
||||
* @covers ::getLines
|
||||
*
|
||||
* @see Drupal\Component\Diff\Engine\HWLDFWordAccumulator::NBSP
|
||||
*/
|
||||
public function testGetLinesEmpty() {
|
||||
$acc = new HWLDFWordAccumulator();
|
||||
$this->assertEquals([' '], $acc->getLines());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* - Expected array of lines from getLines().
|
||||
* - Array of strings for the $words parameter to addWords().
|
||||
* - String tag for the $tag parameter to addWords().
|
||||
*/
|
||||
public function provideAddWords() {
|
||||
return [
|
||||
[['wordword2'], ['word', 'word2'], 'tag'],
|
||||
[['word', 'word2'], ['word', "\nword2"], 'tag'],
|
||||
[[' ', 'word2'], ['', "\nword2"], 'tag'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::addWords
|
||||
* @dataProvider provideAddWords
|
||||
*/
|
||||
public function testAddWords($expected, $words, $tag) {
|
||||
$acc = new HWLDFWordAccumulator();
|
||||
$acc->addWords($words, $tag);
|
||||
$this->assertEquals($expected, $acc->getLines());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Discovery;
|
||||
|
||||
use Drupal\Component\Discovery\DiscoveryException;
|
||||
use Drupal\Component\Discovery\YamlDirectoryDiscovery;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use org\bovigo\vfs\vfsStream;
|
||||
|
||||
/**
|
||||
* YamlDirectoryDiscoveryTest component unit tests.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\Component\Discovery\YamlDirectoryDiscovery
|
||||
*
|
||||
* @group Discovery
|
||||
*/
|
||||
class YamlDirectoryDiscoveryTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Tests YAML directory discovery.
|
||||
*
|
||||
* @covers ::findAll
|
||||
*/
|
||||
public function testDiscovery() {
|
||||
vfsStream::setup('modules', NULL, [
|
||||
'test_1' => [
|
||||
'subdir1' => [
|
||||
'item_1.test.yml' => "id: item1\nname: 'test1 item 1'",
|
||||
],
|
||||
'subdir2' => [
|
||||
'item_2.test.yml' => "id: item2\nname: 'test1 item 2'",
|
||||
],
|
||||
],
|
||||
'test_2' => [
|
||||
'subdir1' => [
|
||||
'item_3.test.yml' => "id: item3\nname: 'test2 item 3'",
|
||||
],
|
||||
'subdir2' => [],
|
||||
],
|
||||
'test_3' => [],
|
||||
'test_4' => [
|
||||
'subdir1' => [
|
||||
'item_4.test.yml' => "id: item4\nname: 'test4 item 4'",
|
||||
'item_5.test.yml' => "id: item5\nname: 'test4 item 5'",
|
||||
'item_6.test.yml' => "id: item6\nname: 'test4 item 6'",
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
// Set up the directories to search.
|
||||
$directories = [
|
||||
// Multiple directories both with valid items.
|
||||
'test_1' => [
|
||||
vfsStream::url('modules/test_1/subdir1'),
|
||||
vfsStream::url('modules/test_1/subdir2'),
|
||||
],
|
||||
// The subdir2 directory is empty.
|
||||
'test_2' => [
|
||||
vfsStream::url('modules/test_2/subdir1'),
|
||||
vfsStream::url('modules/test_2/subdir2'),
|
||||
],
|
||||
// Directories that do not exist.
|
||||
'test_3' => [
|
||||
vfsStream::url('modules/test_3/subdir1'),
|
||||
vfsStream::url('modules/test_3/subdir2'),
|
||||
],
|
||||
// A single directory.
|
||||
'test_4' => vfsStream::url('modules/test_4/subdir1'),
|
||||
];
|
||||
|
||||
$discovery = new YamlDirectoryDiscovery($directories, 'test');
|
||||
$data = $discovery->findAll();
|
||||
|
||||
$this->assertSame(['id' => 'item1', 'name' => 'test1 item 1', YamlDirectoryDiscovery::FILE_KEY => 'vfs://modules/test_1/subdir1/item_1.test.yml'], $data['test_1']['item1']);
|
||||
$this->assertSame(['id' => 'item2', 'name' => 'test1 item 2', YamlDirectoryDiscovery::FILE_KEY => 'vfs://modules/test_1/subdir2/item_2.test.yml'], $data['test_1']['item2']);
|
||||
$this->assertCount(2, $data['test_1']);
|
||||
|
||||
$this->assertSame(['id' => 'item3', 'name' => 'test2 item 3', YamlDirectoryDiscovery::FILE_KEY => 'vfs://modules/test_2/subdir1/item_3.test.yml'], $data['test_2']['item3']);
|
||||
$this->assertCount(1, $data['test_2']);
|
||||
|
||||
$this->assertTrue(empty($data['test_3']), 'test_3 provides 0 items');
|
||||
|
||||
$this->assertSame(['id' => 'item4', 'name' => 'test4 item 4', YamlDirectoryDiscovery::FILE_KEY => 'vfs://modules/test_4/subdir1/item_4.test.yml'], $data['test_4']['item4']);
|
||||
$this->assertSame(['id' => 'item5', 'name' => 'test4 item 5', YamlDirectoryDiscovery::FILE_KEY => 'vfs://modules/test_4/subdir1/item_5.test.yml'], $data['test_4']['item5']);
|
||||
$this->assertSame(['id' => 'item6', 'name' => 'test4 item 6', YamlDirectoryDiscovery::FILE_KEY => 'vfs://modules/test_4/subdir1/item_6.test.yml'], $data['test_4']['item6']);
|
||||
$this->assertCount(3, $data['test_4']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests YAML directory discovery with an alternate ID key.
|
||||
*
|
||||
* @covers ::findAll
|
||||
*/
|
||||
public function testDiscoveryAlternateId() {
|
||||
vfsStream::setup('modules', NULL, [
|
||||
'test_1' => [
|
||||
'item_1.test.yml' => "alt_id: item1\nid: ignored",
|
||||
],
|
||||
]);
|
||||
|
||||
// Set up the directories to search.
|
||||
$directories = ['test_1' => vfsStream::url('modules/test_1')];
|
||||
|
||||
$discovery = new YamlDirectoryDiscovery($directories, 'test', 'alt_id');
|
||||
$data = $discovery->findAll();
|
||||
|
||||
$this->assertSame(['alt_id' => 'item1', 'id' => 'ignored', YamlDirectoryDiscovery::FILE_KEY => 'vfs://modules/test_1/item_1.test.yml'], $data['test_1']['item1']);
|
||||
$this->assertCount(1, $data['test_1']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests YAML directory discovery with a missing ID key.
|
||||
*
|
||||
* @covers ::findAll
|
||||
* @covers ::getIdentifier
|
||||
*/
|
||||
public function testDiscoveryNoIdException() {
|
||||
$this->setExpectedException(DiscoveryException::class, 'The vfs://modules/test_1/item_1.test.yml contains no data in the identifier key \'id\'');
|
||||
vfsStream::setup('modules', NULL, [
|
||||
'test_1' => [
|
||||
'item_1.test.yml' => "",
|
||||
],
|
||||
]);
|
||||
|
||||
// Set up the directories to search.
|
||||
$directories = ['test_1' => vfsStream::url('modules/test_1')];
|
||||
|
||||
$discovery = new YamlDirectoryDiscovery($directories, 'test');
|
||||
$discovery->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests YAML directory discovery with invalid YAML.
|
||||
*
|
||||
* @covers ::findAll
|
||||
*/
|
||||
public function testDiscoveryInvalidYamlException() {
|
||||
$this->setExpectedException(DiscoveryException::class, 'The vfs://modules/test_1/item_1.test.yml contains invalid YAML');
|
||||
vfsStream::setup('modules', NULL, [
|
||||
'test_1' => [
|
||||
'item_1.test.yml' => "id: invalid\nfoo : [bar}",
|
||||
],
|
||||
]);
|
||||
|
||||
// Set up the directories to search.
|
||||
$directories = ['test_1' => vfsStream::url('modules/test_1')];
|
||||
|
||||
$discovery = new YamlDirectoryDiscovery($directories, 'test');
|
||||
$discovery->findAll();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Discovery;
|
||||
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\Component\Discovery\YamlDiscovery;
|
||||
use org\bovigo\vfs\vfsStream;
|
||||
use org\bovigo\vfs\vfsStreamWrapper;
|
||||
use org\bovigo\vfs\vfsStreamDirectory;
|
||||
|
||||
/**
|
||||
* YamlDiscovery component unit tests.
|
||||
*
|
||||
* @group Discovery
|
||||
*/
|
||||
class YamlDiscoveryTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Tests the YAML file discovery.
|
||||
*/
|
||||
public function testDiscovery() {
|
||||
vfsStreamWrapper::register();
|
||||
$root = new vfsStreamDirectory('modules');
|
||||
vfsStreamWrapper::setRoot($root);
|
||||
$url = vfsStream::url('modules');
|
||||
|
||||
mkdir($url . '/test_1');
|
||||
file_put_contents($url . '/test_1/test_1.test.yml', 'name: test');
|
||||
file_put_contents($url . '/test_1/test_2.test.yml', 'name: test');
|
||||
|
||||
mkdir($url . '/test_2');
|
||||
file_put_contents($url . '/test_2/test_3.test.yml', 'name: test');
|
||||
// Write an empty YAML file.
|
||||
file_put_contents($url . '/test_2/test_4.test.yml', '');
|
||||
|
||||
// Set up the directories to search.
|
||||
$directories = array(
|
||||
'test_1' => $url . '/test_1',
|
||||
'test_2' => $url . '/test_1',
|
||||
'test_3' => $url . '/test_2',
|
||||
'test_4' => $url . '/test_2',
|
||||
);
|
||||
|
||||
$discovery = new YamlDiscovery('test', $directories);
|
||||
$data = $discovery->findAll();
|
||||
|
||||
$this->assertEquals(count($data), count($directories));
|
||||
$this->assertArrayHasKey('test_1', $data);
|
||||
$this->assertArrayHasKey('test_2', $data);
|
||||
$this->assertArrayHasKey('test_3', $data);
|
||||
$this->assertArrayHasKey('test_4', $data);
|
||||
|
||||
foreach (array('test_1', 'test_2', 'test_3') as $key) {
|
||||
$this->assertArrayHasKey('name', $data[$key]);
|
||||
$this->assertEquals($data[$key]['name'], 'test');
|
||||
}
|
||||
|
||||
$this->assertSame(array(), $data['test_4']);
|
||||
}
|
||||
|
||||
}
|
||||
127
web/core/tests/Drupal/Tests/Component/DrupalComponentTest.php
Normal file
127
web/core/tests/Drupal/Tests/Component/DrupalComponentTest.php
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component;
|
||||
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use org\bovigo\vfs\vfsStream;
|
||||
|
||||
/**
|
||||
* General tests for \Drupal\Component that can't go anywhere else.
|
||||
*
|
||||
* @group Component
|
||||
*/
|
||||
class DrupalComponentTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Tests that classes in Component do not use any Core class.
|
||||
*/
|
||||
public function testNoCoreInComponent() {
|
||||
$component_path = dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__))) . '/lib/Drupal/Component';
|
||||
foreach ($this->findPhpClasses($component_path) as $class) {
|
||||
$this->assertNoCoreUsage($class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that classes in Component Tests do not use any Core class.
|
||||
*/
|
||||
public function testNoCoreInComponentTests() {
|
||||
$component_path = dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__))) . '/tests/Drupal/Tests/Component';
|
||||
foreach ($this->findPhpClasses($component_path) as $class) {
|
||||
$this->assertNoCoreUsage($class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches a directory recursively for PHP classes.
|
||||
*
|
||||
* @param string $dir
|
||||
* The full path to the directory that should be checked.
|
||||
*
|
||||
* @return array
|
||||
* An array of class paths.
|
||||
*/
|
||||
protected function findPhpClasses($dir) {
|
||||
$classes = array();
|
||||
foreach (new \DirectoryIterator($dir) as $file) {
|
||||
if ($file->isDir() && !$file->isDot()) {
|
||||
$classes = array_merge($classes, $this->findPhpClasses($file->getPathname()));
|
||||
}
|
||||
elseif ($file->getExtension() == 'php') {
|
||||
$classes[] = $file->getPathname();
|
||||
}
|
||||
}
|
||||
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given class is not using any class from Core namespace.
|
||||
*
|
||||
* @param string $class_path
|
||||
* The full path to the class that should be checked.
|
||||
*/
|
||||
protected function assertNoCoreUsage($class_path) {
|
||||
$contents = file_get_contents($class_path);
|
||||
preg_match_all('/^.*Drupal\\\Core.*$/m', $contents, $matches);
|
||||
$matches = array_filter($matches[0], function($line) {
|
||||
// Filter references to @see as they don't really matter.
|
||||
return strpos($line, '@see') === FALSE;
|
||||
});
|
||||
$this->assertEmpty($matches, "Checking for illegal reference to 'Drupal\\Core' namespace in $class_path");
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testAssertNoCoreUseage().
|
||||
*
|
||||
* @return array
|
||||
* Data for testAssertNoCoreUseage() in the form:
|
||||
* - TRUE if the test passes, FALSE otherwise.
|
||||
* - File data as a string. This will be used as a virtual file.
|
||||
*/
|
||||
public function providerAssertNoCoreUseage() {
|
||||
return array(
|
||||
array(
|
||||
TRUE,
|
||||
'@see \\Drupal\\Core\\Something',
|
||||
),
|
||||
array(
|
||||
FALSE,
|
||||
'\\Drupal\\Core\\Something',
|
||||
),
|
||||
array(
|
||||
FALSE,
|
||||
"@see \\Drupal\\Core\\Something\n" .
|
||||
'\\Drupal\\Core\\Something',
|
||||
),
|
||||
array(
|
||||
FALSE,
|
||||
"\\Drupal\\Core\\Something\n" .
|
||||
'@see \\Drupal\\Core\\Something',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Drupal\Tests\Component\DrupalComponentTest::assertNoCoreUsage
|
||||
* @dataProvider providerAssertNoCoreUseage
|
||||
*/
|
||||
public function testAssertNoCoreUseage($expected_pass, $file_data) {
|
||||
// Set up a virtual file to read.
|
||||
$vfs_root = vfsStream::setup('root');
|
||||
vfsStream::newFile('Test.php')->at($vfs_root)->setContent($file_data);
|
||||
$file_uri = vfsStream::url('root/Test.php');
|
||||
|
||||
try {
|
||||
$pass = TRUE;
|
||||
$this->assertNoCoreUsage($file_uri);
|
||||
}
|
||||
catch (\PHPUnit_Framework_AssertionFailedError $e) {
|
||||
$pass = FALSE;
|
||||
}
|
||||
$this->assertEquals($expected_pass, $pass, $expected_pass ?
|
||||
'Test caused a false positive' :
|
||||
'Test failed to detect Core usage');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
<?php
|
||||
// @codingStandardsIgnoreFile
|
||||
|
||||
namespace Drupal\Tests\Component\EventDispatcher;
|
||||
|
||||
use Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher;
|
||||
use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\EventDispatcher\Tests\CallableClass;
|
||||
use Symfony\Component\EventDispatcher\Tests\TestEventListener;
|
||||
use Symfony\Component\EventDispatcher\Tests\ContainerAwareEventDispatcherTest as SymfonyContainerAwareEventDispatcherTest;
|
||||
|
||||
/**
|
||||
* Unit tests for the ContainerAwareEventDispatcher.
|
||||
*
|
||||
* NOTE: 98% of this code is a literal copy of Symfony's emerging
|
||||
* CompiledEventDispatcherTest.
|
||||
*
|
||||
* This file does NOT follow Drupal coding standards, so as to simplify future
|
||||
* synchronizations.
|
||||
*
|
||||
* @see https://github.com/symfony/symfony/pull/12521
|
||||
*
|
||||
* @group EventDispatcher
|
||||
*/
|
||||
class ContainerAwareEventDispatcherTest extends SymfonyContainerAwareEventDispatcherTest
|
||||
{
|
||||
protected function createEventDispatcher()
|
||||
{
|
||||
$container = new Container();
|
||||
|
||||
return new ContainerAwareEventDispatcher($container);
|
||||
}
|
||||
|
||||
public function testGetListenersWithCallables()
|
||||
{
|
||||
// When passing in callables exclusively as listeners into the event
|
||||
// dispatcher constructor, the event dispatcher must not attempt to
|
||||
// resolve any services.
|
||||
$container = $this->getMock('Symfony\Component\DependencyInjection\IntrospectableContainerInterface');
|
||||
$container->expects($this->never())->method($this->anything());
|
||||
|
||||
$firstListener = new CallableClass();
|
||||
$secondListener = function () {};
|
||||
$thirdListener = array(new TestEventListener(), 'preFoo');
|
||||
$listeners = array(
|
||||
'test_event' => array(
|
||||
0 => array(
|
||||
array('callable' => $firstListener),
|
||||
array('callable' => $secondListener),
|
||||
array('callable' => $thirdListener),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$dispatcher = new ContainerAwareEventDispatcher($container, $listeners);
|
||||
$actualListeners = $dispatcher->getListeners();
|
||||
|
||||
$expectedListeners = array(
|
||||
'test_event' => array(
|
||||
$firstListener,
|
||||
$secondListener,
|
||||
$thirdListener,
|
||||
),
|
||||
);
|
||||
|
||||
$this->assertSame($expectedListeners, $actualListeners);
|
||||
}
|
||||
|
||||
public function testDispatchWithCallables()
|
||||
{
|
||||
// When passing in callables exclusively as listeners into the event
|
||||
// dispatcher constructor, the event dispatcher must not attempt to
|
||||
// resolve any services.
|
||||
$container = $this->getMock('Symfony\Component\DependencyInjection\IntrospectableContainerInterface');
|
||||
$container->expects($this->never())->method($this->anything());
|
||||
|
||||
$firstListener = new CallableClass();
|
||||
$secondListener = function () {};
|
||||
$thirdListener = array(new TestEventListener(), 'preFoo');
|
||||
$listeners = array(
|
||||
'test_event' => array(
|
||||
0 => array(
|
||||
array('callable' => $firstListener),
|
||||
array('callable' => $secondListener),
|
||||
array('callable' => $thirdListener),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$dispatcher = new ContainerAwareEventDispatcher($container, $listeners);
|
||||
$dispatcher->dispatch('test_event');
|
||||
|
||||
$this->assertTrue($thirdListener[0]->preFooInvoked);
|
||||
}
|
||||
|
||||
public function testGetListenersWithServices()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register('listener_service', 'Symfony\Component\EventDispatcher\Tests\TestEventListener');
|
||||
|
||||
$listeners = array(
|
||||
'test_event' => array(
|
||||
0 => array(
|
||||
array('service' => array('listener_service', 'preFoo')),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$dispatcher = new ContainerAwareEventDispatcher($container, $listeners);
|
||||
$actualListeners = $dispatcher->getListeners();
|
||||
|
||||
$listenerService = $container->get('listener_service');
|
||||
$expectedListeners = array(
|
||||
'test_event' => array(
|
||||
array($listenerService, 'preFoo'),
|
||||
),
|
||||
);
|
||||
|
||||
$this->assertSame($expectedListeners, $actualListeners);
|
||||
}
|
||||
|
||||
public function testDispatchWithServices()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register('listener_service', 'Symfony\Component\EventDispatcher\Tests\TestEventListener');
|
||||
|
||||
$listeners = array(
|
||||
'test_event' => array(
|
||||
0 => array(
|
||||
array('service' => array('listener_service', 'preFoo')),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$dispatcher = new ContainerAwareEventDispatcher($container, $listeners);
|
||||
|
||||
$dispatcher->dispatch('test_event');
|
||||
|
||||
$listenerService = $container->get('listener_service');
|
||||
$this->assertTrue($listenerService->preFooInvoked);
|
||||
}
|
||||
|
||||
public function testRemoveService()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register('listener_service', 'Symfony\Component\EventDispatcher\Tests\TestEventListener');
|
||||
$container->register('other_listener_service', 'Symfony\Component\EventDispatcher\Tests\TestEventListener');
|
||||
|
||||
$listeners = array(
|
||||
'test_event' => array(
|
||||
0 => array(
|
||||
array('service' => array('listener_service', 'preFoo')),
|
||||
array('service' => array('other_listener_service', 'preFoo')),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$dispatcher = new ContainerAwareEventDispatcher($container, $listeners);
|
||||
|
||||
$listenerService = $container->get('listener_service');
|
||||
$dispatcher->removeListener('test_event', array($listenerService, 'preFoo'));
|
||||
|
||||
// Ensure that other service was not initialized during removal of the
|
||||
// listener service.
|
||||
$this->assertFalse($container->initialized('other_listener_service'));
|
||||
|
||||
$dispatcher->dispatch('test_event');
|
||||
|
||||
$this->assertFalse($listenerService->preFooInvoked);
|
||||
$otherService = $container->get('other_listener_service');
|
||||
$this->assertTrue($otherService->preFooInvoked);
|
||||
}
|
||||
|
||||
public function testGetListenerPriorityWithServices()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register('listener_service', TestEventListener::class);
|
||||
|
||||
$listeners = array(
|
||||
'test_event' => array(
|
||||
5 => array(
|
||||
array('service' => array('listener_service', 'preFoo')),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$dispatcher = new ContainerAwareEventDispatcher($container, $listeners);
|
||||
$listenerService = $container->get('listener_service');
|
||||
$actualPriority = $dispatcher->getListenerPriority('test_event', [$listenerService, 'preFoo']);
|
||||
|
||||
$this->assertSame(5, $actualPriority);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\FileCache;
|
||||
|
||||
use Drupal\Component\FileCache\FileCache;
|
||||
use Drupal\Component\FileCache\NullFileCache;
|
||||
use Drupal\Component\FileCache\FileCacheFactory;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Component\FileCache\FileCacheFactory
|
||||
* @group FileCache
|
||||
*/
|
||||
class FileCacheFactoryTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$configuration = [
|
||||
'test_foo_settings' => [
|
||||
'collection' => 'test-23',
|
||||
'cache_backend_class' => '\Drupal\Tests\Component\FileCache\StaticFileCacheBackend',
|
||||
'cache_backend_configuration' => [
|
||||
'bin' => 'dog',
|
||||
],
|
||||
],
|
||||
];
|
||||
FileCacheFactory::setConfiguration($configuration);
|
||||
FileCacheFactory::setPrefix('prefix');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::get
|
||||
*/
|
||||
public function testGet() {
|
||||
$file_cache = FileCacheFactory::get('test_foo_settings', []);
|
||||
|
||||
// Ensure the right backend and configuration is used.
|
||||
$filename = __DIR__ . '/Fixtures/llama-23.txt';
|
||||
$realpath = realpath($filename);
|
||||
$cid = 'prefix:test-23:' . $realpath;
|
||||
|
||||
$file_cache->set($filename, 23);
|
||||
|
||||
$static_cache = new StaticFileCacheBackend(['bin' => 'dog']);
|
||||
$result = $static_cache->fetch([$cid]);
|
||||
$this->assertNotEmpty($result);
|
||||
|
||||
// Cleanup static caches.
|
||||
$file_cache->delete($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::get
|
||||
*
|
||||
* @expectedException \InvalidArgumentException
|
||||
* @expectedExceptionMessage Required prefix configuration is missing
|
||||
*/
|
||||
public function testGetNoPrefix() {
|
||||
FileCacheFactory::setPrefix(NULL);
|
||||
FileCacheFactory::get('test_foo_settings', []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::get
|
||||
*/
|
||||
public function testGetDisabledFileCache() {
|
||||
// Ensure the returned FileCache is an instance of FileCache::class.
|
||||
$file_cache = FileCacheFactory::get('test_foo_settings', []);
|
||||
$this->assertInstanceOf(FileCache::class, $file_cache);
|
||||
|
||||
$configuration = FileCacheFactory::getConfiguration();
|
||||
$configuration[FileCacheFactory::DISABLE_CACHE] = TRUE;
|
||||
FileCacheFactory::setConfiguration($configuration);
|
||||
|
||||
// Ensure the returned FileCache is now an instance of NullFileCache::class.
|
||||
$file_cache = FileCacheFactory::get('test_foo_settings', []);
|
||||
$this->assertInstanceOf(NullFileCache::class, $file_cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::get
|
||||
*
|
||||
* @dataProvider configurationDataProvider
|
||||
*/
|
||||
public function testGetConfigurationOverrides($configuration, $arguments, $class) {
|
||||
FileCacheFactory::setConfiguration($configuration);
|
||||
|
||||
$file_cache = FileCacheFactory::get('test_foo_settings', $arguments);
|
||||
$this->assertInstanceOf($class, $file_cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testGetConfigurationOverrides().
|
||||
*/
|
||||
public function configurationDataProvider() {
|
||||
$data = [];
|
||||
|
||||
// Get a unique FileCache class.
|
||||
$file_cache = $this->getMockBuilder(FileCache::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$class = get_class($file_cache);
|
||||
|
||||
// Test fallback configuration.
|
||||
$data['fallback-configuration'] = [[
|
||||
], [], FileCache::class];
|
||||
|
||||
// Test default configuration.
|
||||
$data['default-configuration'] = [[
|
||||
'default' => [
|
||||
'class' => $class,
|
||||
],
|
||||
], [], $class];
|
||||
|
||||
// Test specific per collection setting.
|
||||
$data['collection-setting'] = [[
|
||||
'test_foo_settings' => [
|
||||
'class' => $class,
|
||||
],
|
||||
], [], $class];
|
||||
|
||||
|
||||
// Test default configuration plus specific per collection setting.
|
||||
$data['default-plus-collection-setting'] = [[
|
||||
'default' => [
|
||||
'class' => '\stdClass',
|
||||
],
|
||||
'test_foo_settings' => [
|
||||
'class' => $class,
|
||||
],
|
||||
], [], $class];
|
||||
|
||||
// Test default configuration plus class specific override.
|
||||
$data['default-plus-class-override'] = [[
|
||||
'default' => [
|
||||
'class' => '\stdClass',
|
||||
],
|
||||
], [ 'class' => $class ], $class];
|
||||
|
||||
// Test default configuration plus class specific override plus specific
|
||||
// per collection setting.
|
||||
$data['default-plus-class-plus-collection-setting'] = [[
|
||||
'default' => [
|
||||
'class' => '\stdClass',
|
||||
],
|
||||
'test_foo_settings' => [
|
||||
'class' => $class,
|
||||
],
|
||||
], [ 'class' => '\stdClass'], $class];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getConfiguration
|
||||
* @covers ::setConfiguration
|
||||
*/
|
||||
public function testGetSetConfiguration() {
|
||||
$configuration = FileCacheFactory::getConfiguration();
|
||||
$configuration['test_foo_bar'] = ['bar' => 'llama'];
|
||||
FileCacheFactory::setConfiguration($configuration);
|
||||
$configuration = FileCacheFactory::getConfiguration();
|
||||
$this->assertEquals(['bar' => 'llama'], $configuration['test_foo_bar']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getPrefix
|
||||
* @covers ::setPrefix
|
||||
*/
|
||||
public function testGetSetPrefix() {
|
||||
$prefix = $this->randomMachineName();
|
||||
FileCacheFactory::setPrefix($prefix);
|
||||
$this->assertEquals($prefix, FileCacheFactory::getPrefix());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\FileCache;
|
||||
|
||||
use Drupal\Component\FileCache\FileCache;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Component\FileCache\FileCache
|
||||
* @group FileCache
|
||||
*/
|
||||
class FileCacheTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* FileCache object used for the tests.
|
||||
*
|
||||
* @var \Drupal\Component\FileCache\FileCacheInterface
|
||||
*/
|
||||
protected $fileCache;
|
||||
|
||||
/**
|
||||
* Static FileCache object used for verification of tests.
|
||||
*
|
||||
* @var \Drupal\Component\FileCache\FileCacheBackendInterface
|
||||
*/
|
||||
protected $staticFileCache;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->fileCache = new FileCache('prefix', 'test', '\Drupal\Tests\Component\FileCache\StaticFileCacheBackend', ['bin' => 'llama']);
|
||||
$this->staticFileCache = new StaticFileCacheBackend(['bin' => 'llama']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::get
|
||||
* @covers ::__construct
|
||||
*/
|
||||
public function testGet() {
|
||||
// Test a cache miss.
|
||||
$result = $this->fileCache->get(__DIR__ . '/Fixtures/no-llama-42.yml');
|
||||
$this->assertNull($result);
|
||||
|
||||
// Test a cache hit.
|
||||
$filename = __DIR__ . '/Fixtures/llama-42.txt';
|
||||
$realpath = realpath($filename);
|
||||
$cid = 'prefix:test:' . $realpath;
|
||||
$data = [
|
||||
'mtime' => filemtime($realpath),
|
||||
'filepath' => $realpath,
|
||||
'data' => 42,
|
||||
];
|
||||
|
||||
$this->staticFileCache->store($cid, $data);
|
||||
|
||||
$result = $this->fileCache->get($filename);
|
||||
$this->assertEquals(42, $result);
|
||||
|
||||
// Cleanup static caches.
|
||||
$this->fileCache->delete($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getMultiple
|
||||
*/
|
||||
public function testGetMultiple() {
|
||||
// Test a cache miss.
|
||||
$result = $this->fileCache->getMultiple([__DIR__ . '/Fixtures/no-llama-42.yml']);
|
||||
$this->assertEmpty($result);
|
||||
|
||||
// Test a cache hit.
|
||||
$filename = __DIR__ . '/Fixtures/llama-42.txt';
|
||||
$realpath = realpath($filename);
|
||||
$cid = 'prefix:test:' . $realpath;
|
||||
$data = [
|
||||
'mtime' => filemtime($realpath),
|
||||
'filepath' => $realpath,
|
||||
'data' => 42,
|
||||
];
|
||||
|
||||
$this->staticFileCache->store($cid, $data);
|
||||
|
||||
$result = $this->fileCache->getMultiple([$filename]);
|
||||
$this->assertEquals([$filename => 42], $result);
|
||||
|
||||
// Test a static cache hit.
|
||||
$file2 = __DIR__ . '/Fixtures/llama-23.txt';
|
||||
$this->fileCache->set($file2, 23);
|
||||
|
||||
$result = $this->fileCache->getMultiple([$filename, $file2]);
|
||||
$this->assertEquals([$filename => 42, $file2 => 23], $result);
|
||||
|
||||
// Cleanup static caches.
|
||||
$this->fileCache->delete($filename);
|
||||
$this->fileCache->delete($file2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::set
|
||||
*/
|
||||
public function testSet() {
|
||||
$filename = __DIR__ . '/Fixtures/llama-23.txt';
|
||||
$realpath = realpath($filename);
|
||||
$cid = 'prefix:test:' . $realpath;
|
||||
$data = [
|
||||
'mtime' => filemtime($realpath),
|
||||
'filepath' => $realpath,
|
||||
'data' => 23,
|
||||
];
|
||||
|
||||
$this->fileCache->set($filename, 23);
|
||||
$result = $this->staticFileCache->fetch([$cid]);
|
||||
$this->assertEquals([$cid => $data], $result);
|
||||
|
||||
// Cleanup static caches.
|
||||
$this->fileCache->delete($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::delete
|
||||
*/
|
||||
public function testDelete() {
|
||||
$filename = __DIR__ . '/Fixtures/llama-23.txt';
|
||||
$realpath = realpath($filename);
|
||||
$cid = 'prefix:test:' . $realpath;
|
||||
|
||||
$this->fileCache->set($filename, 23);
|
||||
|
||||
// Ensure data is removed after deletion.
|
||||
$this->fileCache->delete($filename);
|
||||
|
||||
$result = $this->staticFileCache->fetch([$cid]);
|
||||
$this->assertEquals([], $result);
|
||||
|
||||
$result = $this->fileCache->get($filename);
|
||||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
23
|
||||
|
|
@ -0,0 +1 @@
|
|||
42
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\FileCache;
|
||||
|
||||
use Drupal\Component\FileCache\FileCacheBackendInterface;
|
||||
|
||||
/**
|
||||
* Allows to cache data based on file modification dates in a static cache.
|
||||
*/
|
||||
class StaticFileCacheBackend implements FileCacheBackendInterface {
|
||||
|
||||
/**
|
||||
* Internal static cache.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $cache = [];
|
||||
|
||||
/**
|
||||
* Bin used for storing the data in the static cache.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $bin;
|
||||
|
||||
/**
|
||||
* Constructs a PHP Storage FileCache backend.
|
||||
*
|
||||
* @param array $configuration
|
||||
* (optional) Configuration used to configure this object.
|
||||
*/
|
||||
public function __construct($configuration) {
|
||||
$this->bin = isset($configuration['bin']) ? $configuration['bin'] : 'file_cache';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetch(array $cids) {
|
||||
$result = [];
|
||||
foreach ($cids as $cid) {
|
||||
if (isset(static::$cache[$this->bin][$cid])) {
|
||||
$result[$cid] = static::$cache[$this->bin][$cid];
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function store($cid, $data) {
|
||||
static::$cache[$this->bin][$cid] = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete($cid) {
|
||||
unset(static::$cache[$this->bin][$cid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows tests to reset the static cache to avoid side effects.
|
||||
*/
|
||||
public static function reset() {
|
||||
static::$cache = [];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\FileSystem;
|
||||
|
||||
use Drupal\Component\FileSystem\RegexDirectoryIterator;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use org\bovigo\vfs\vfsStream;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Component\FileSystem\RegexDirectoryIterator
|
||||
* @group FileSystem
|
||||
*/
|
||||
class RegexDirectoryIteratorTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @covers ::accept
|
||||
* @dataProvider providerTestRegexDirectoryIterator
|
||||
*/
|
||||
public function testRegexDirectoryIterator(array $directory, $regex, array $expected) {
|
||||
vfsStream::setup('root', NULL, $directory);
|
||||
$iterator = new RegexDirectoryIterator(vfsStream::url('root'), $regex);
|
||||
|
||||
// Create an array of filenames to assert against.
|
||||
$file_list = array_map(function(\SplFileInfo $file) {
|
||||
return $file->getFilename();
|
||||
}, array_values(iterator_to_array($iterator)));
|
||||
|
||||
$this->assertSame($expected, $file_list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider for self::testRegexDirectoryIterator().
|
||||
*/
|
||||
public function providerTestRegexDirectoryIterator() {
|
||||
return [
|
||||
[
|
||||
[
|
||||
'1.yml' => '',
|
||||
],
|
||||
'/\.yml$/',
|
||||
[
|
||||
'1.yml',
|
||||
],
|
||||
],
|
||||
[
|
||||
[
|
||||
'1.yml' => '',
|
||||
'2.yml' => '',
|
||||
'3.txt' => '',
|
||||
],
|
||||
'/\.yml$/',
|
||||
[
|
||||
'1.yml',
|
||||
'2.yml',
|
||||
],
|
||||
],
|
||||
[
|
||||
[
|
||||
'1.yml' => '',
|
||||
'2.yml' => '',
|
||||
'3.txt' => '',
|
||||
],
|
||||
'/\.txt/',
|
||||
[
|
||||
'3.txt',
|
||||
],
|
||||
],
|
||||
[
|
||||
[
|
||||
'1.yml' => '',
|
||||
// Ensure we don't recurse in directories even if that match the
|
||||
// regex.
|
||||
'2.yml' => [
|
||||
'3.yml' => '',
|
||||
'4.yml' => '',
|
||||
],
|
||||
'3.txt' => '',
|
||||
],
|
||||
'/\.yml$/',
|
||||
[
|
||||
'1.yml',
|
||||
],
|
||||
],
|
||||
[
|
||||
[
|
||||
'1.yml' => '',
|
||||
'2.yml' => '',
|
||||
'3.txt' => '',
|
||||
],
|
||||
'/^\d/',
|
||||
[
|
||||
'1.yml',
|
||||
'2.yml',
|
||||
'3.txt'
|
||||
],
|
||||
],
|
||||
[
|
||||
[
|
||||
'1.yml' => '',
|
||||
'2.yml' => '',
|
||||
'3.txt' => '',
|
||||
],
|
||||
'/^\D/',
|
||||
[],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
370
web/core/tests/Drupal/Tests/Component/Gettext/PoHeaderTest.php
Normal file
370
web/core/tests/Drupal/Tests/Component/Gettext/PoHeaderTest.php
Normal file
|
|
@ -0,0 +1,370 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Gettext;
|
||||
|
||||
use Drupal\Component\Gettext\PoHeader;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* Unit tests for the Gettext PO file header handling features.
|
||||
*
|
||||
* @see Drupal\Component\Gettext\PoHeader.
|
||||
*
|
||||
* @group Gettext
|
||||
*/
|
||||
class PoHeaderTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Tests that plural expressions are evaluated correctly.
|
||||
*
|
||||
* Validate that the given plural expressions is evaluated with the correct
|
||||
* plural formula.
|
||||
*
|
||||
* @param string $plural
|
||||
* The plural expression.
|
||||
* @param array $expected
|
||||
* Array of expected plural positions keyed by plural value.
|
||||
*
|
||||
* @dataProvider providerTestPluralsFormula
|
||||
*/
|
||||
public function testPluralsFormula($plural, $expected) {
|
||||
$p = new PoHeader();
|
||||
$parsed = $p->parsePluralForms($plural);
|
||||
list($nplurals, $new_plural) = $parsed;
|
||||
foreach ($expected as $number => $plural_form) {
|
||||
$result = isset($new_plural[$number]) ? $new_plural[$number] : $new_plural['default'];
|
||||
$this->assertEquals($result, $plural_form, 'Difference found at ' . $number . ': ' . $plural_form . ' versus ' . $result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testPluralsFormula.
|
||||
*
|
||||
* Gets pairs of plural expressions and expected plural positions keyed by
|
||||
* plural value.
|
||||
*
|
||||
* @return array
|
||||
* Pairs of plural expressions and expected plural positions keyed by plural
|
||||
* value.
|
||||
*/
|
||||
public function providerTestPluralsFormula() {
|
||||
return array(
|
||||
array(
|
||||
'nplurals=1; plural=0;',
|
||||
array('default' => 0),
|
||||
),
|
||||
array(
|
||||
'nplurals=2; plural=(n > 1);',
|
||||
array(0 => 0, 1 => 0, 'default' => 1),
|
||||
),
|
||||
array(
|
||||
'nplurals=2; plural=(n!=1);',
|
||||
array(1 => 0, 'default' => 1),
|
||||
),
|
||||
array(
|
||||
'nplurals=2; plural=(((n==1)||((n%10)==1))?(0):1);',
|
||||
array(
|
||||
1 => 0,
|
||||
11 => 0,
|
||||
21 => 0,
|
||||
31 => 0,
|
||||
41 => 0,
|
||||
51 => 0,
|
||||
61 => 0,
|
||||
71 => 0,
|
||||
81 => 0,
|
||||
91 => 0,
|
||||
101 => 0,
|
||||
111 => 0,
|
||||
121 => 0,
|
||||
131 => 0,
|
||||
141 => 0,
|
||||
151 => 0,
|
||||
161 => 0,
|
||||
171 => 0,
|
||||
181 => 0,
|
||||
191 => 0,
|
||||
'default' => 1,
|
||||
),
|
||||
),
|
||||
array(
|
||||
'nplurals=3; plural=((((n%10)==1)&&((n%100)!=11))?(0):(((((n%10)>=2)&&((n%10)<=4))&&(((n%100)<10)||((n%100)>=20)))?(1):2));',
|
||||
array(
|
||||
1 => 0,
|
||||
2 => 1,
|
||||
3 => 1,
|
||||
4 => 1,
|
||||
21 => 0,
|
||||
22 => 1,
|
||||
23 => 1,
|
||||
24 => 1,
|
||||
31 => 0,
|
||||
32 => 1,
|
||||
33 => 1,
|
||||
34 => 1,
|
||||
41 => 0,
|
||||
42 => 1,
|
||||
43 => 1,
|
||||
44 => 1,
|
||||
51 => 0,
|
||||
52 => 1,
|
||||
53 => 1,
|
||||
54 => 1,
|
||||
61 => 0,
|
||||
62 => 1,
|
||||
63 => 1,
|
||||
64 => 1,
|
||||
71 => 0,
|
||||
72 => 1,
|
||||
73 => 1,
|
||||
74 => 1,
|
||||
81 => 0,
|
||||
82 => 1,
|
||||
83 => 1,
|
||||
84 => 1,
|
||||
91 => 0,
|
||||
92 => 1,
|
||||
93 => 1,
|
||||
94 => 1,
|
||||
101 => 0,
|
||||
102 => 1,
|
||||
103 => 1,
|
||||
104 => 1,
|
||||
121 => 0,
|
||||
122 => 1,
|
||||
123 => 1,
|
||||
124 => 1,
|
||||
131 => 0,
|
||||
132 => 1,
|
||||
133 => 1,
|
||||
134 => 1,
|
||||
141 => 0,
|
||||
142 => 1,
|
||||
143 => 1,
|
||||
144 => 1,
|
||||
151 => 0,
|
||||
152 => 1,
|
||||
153 => 1,
|
||||
154 => 1,
|
||||
161 => 0,
|
||||
162 => 1,
|
||||
163 => 1,
|
||||
164 => 1,
|
||||
171 => 0,
|
||||
172 => 1,
|
||||
173 => 1,
|
||||
174 => 1,
|
||||
181 => 0,
|
||||
182 => 1,
|
||||
183 => 1,
|
||||
184 => 1,
|
||||
191 => 0,
|
||||
192 => 1,
|
||||
193 => 1,
|
||||
194 => 1,
|
||||
'default' => 2,
|
||||
),
|
||||
),
|
||||
array(
|
||||
'nplurals=3; plural=((n==1)?(0):(((n>=2)&&(n<=4))?(1):2));',
|
||||
array(
|
||||
1 => 0,
|
||||
2 => 1,
|
||||
3 => 1,
|
||||
4 => 1,
|
||||
'default' => 2,
|
||||
),
|
||||
),
|
||||
array(
|
||||
'nplurals=3; plural=((n==1)?(0):(((n==0)||(((n%100)>0)&&((n%100)<20)))?(1):2));',
|
||||
array(
|
||||
0 => 1,
|
||||
1 => 0,
|
||||
2 => 1,
|
||||
3 => 1,
|
||||
4 => 1,
|
||||
5 => 1,
|
||||
6 => 1,
|
||||
7 => 1,
|
||||
8 => 1,
|
||||
9 => 1,
|
||||
10 => 1,
|
||||
11 => 1,
|
||||
12 => 1,
|
||||
13 => 1,
|
||||
14 => 1,
|
||||
15 => 1,
|
||||
16 => 1,
|
||||
17 => 1,
|
||||
18 => 1,
|
||||
19 => 1,
|
||||
101 => 1,
|
||||
102 => 1,
|
||||
103 => 1,
|
||||
104 => 1,
|
||||
105 => 1,
|
||||
106 => 1,
|
||||
107 => 1,
|
||||
108 => 1,
|
||||
109 => 1,
|
||||
110 => 1,
|
||||
111 => 1,
|
||||
112 => 1,
|
||||
113 => 1,
|
||||
114 => 1,
|
||||
115 => 1,
|
||||
116 => 1,
|
||||
117 => 1,
|
||||
118 => 1,
|
||||
119 => 1,
|
||||
'default' => 2,
|
||||
),
|
||||
),
|
||||
array(
|
||||
'nplurals=3; plural=((n==1)?(0):(((((n%10)>=2)&&((n%10)<=4))&&(((n%100)<10)||((n%100)>=20)))?(1):2));',
|
||||
array(
|
||||
1 => 0,
|
||||
2 => 1,
|
||||
3 => 1,
|
||||
4 => 1,
|
||||
22 => 1,
|
||||
23 => 1,
|
||||
24 => 1,
|
||||
32 => 1,
|
||||
33 => 1,
|
||||
34 => 1,
|
||||
42 => 1,
|
||||
43 => 1,
|
||||
44 => 1,
|
||||
52 => 1,
|
||||
53 => 1,
|
||||
54 => 1,
|
||||
62 => 1,
|
||||
63 => 1,
|
||||
64 => 1,
|
||||
72 => 1,
|
||||
73 => 1,
|
||||
74 => 1,
|
||||
82 => 1,
|
||||
83 => 1,
|
||||
84 => 1,
|
||||
92 => 1,
|
||||
93 => 1,
|
||||
94 => 1,
|
||||
102 => 1,
|
||||
103 => 1,
|
||||
104 => 1,
|
||||
122 => 1,
|
||||
123 => 1,
|
||||
124 => 1,
|
||||
132 => 1,
|
||||
133 => 1,
|
||||
134 => 1,
|
||||
142 => 1,
|
||||
143 => 1,
|
||||
144 => 1,
|
||||
152 => 1,
|
||||
153 => 1,
|
||||
154 => 1,
|
||||
162 => 1,
|
||||
163 => 1,
|
||||
164 => 1,
|
||||
172 => 1,
|
||||
173 => 1,
|
||||
174 => 1,
|
||||
182 => 1,
|
||||
183 => 1,
|
||||
184 => 1,
|
||||
192 => 1,
|
||||
193 => 1,
|
||||
194 => 1,
|
||||
'default' => 2,
|
||||
),),
|
||||
array(
|
||||
'nplurals=4; plural=(((n==1)||(n==11))?(0):(((n==2)||(n==12))?(1):(((n>2)&&(n<20))?(2):3)));',
|
||||
array(
|
||||
1 => 0,
|
||||
2 => 1,
|
||||
3 => 2,
|
||||
4 => 2,
|
||||
5 => 2,
|
||||
6 => 2,
|
||||
7 => 2,
|
||||
8 => 2,
|
||||
9 => 2,
|
||||
10 => 2,
|
||||
11 => 0,
|
||||
12 => 1,
|
||||
13 => 2,
|
||||
14 => 2,
|
||||
15 => 2,
|
||||
16 => 2,
|
||||
17 => 2,
|
||||
18 => 2,
|
||||
19 => 2,
|
||||
'default' => 3,
|
||||
),
|
||||
),
|
||||
array(
|
||||
'nplurals=4; plural=(((n%100)==1)?(0):(((n%100)==2)?(1):((((n%100)==3)||((n%100)==4))?(2):3)));',
|
||||
array(
|
||||
1 => 0,
|
||||
2 => 1,
|
||||
3 => 2,
|
||||
4 => 2,
|
||||
101 => 0,
|
||||
102 => 1,
|
||||
103 => 2,
|
||||
104 => 2,
|
||||
'default' => 3,
|
||||
),
|
||||
),
|
||||
array(
|
||||
'nplurals=5; plural=((n==1)?(0):((n==2)?(1):((n<7)?(2):((n<11)?(3):4))));',
|
||||
array(
|
||||
0 => 2,
|
||||
1 => 0,
|
||||
2 => 1,
|
||||
3 => 2,
|
||||
4 => 2,
|
||||
5 => 2,
|
||||
6 => 2,
|
||||
7 => 3,
|
||||
8 => 3,
|
||||
9 => 3,
|
||||
10 => 3,
|
||||
'default' => 4,
|
||||
),
|
||||
),
|
||||
array(
|
||||
'nplurals=6; plural=((n==1)?(0):((n==0)?(1):((n==2)?(2):((((n%100)>=3)&&((n%100)<=10))?(3):((((n%100)>=11)&&((n%100)<=99))?(4):5)))));',
|
||||
array(
|
||||
0 => 1,
|
||||
1 => 0,
|
||||
2 => 2,
|
||||
3 => 3,
|
||||
4 => 3,
|
||||
5 => 3,
|
||||
6 => 3,
|
||||
7 => 3,
|
||||
8 => 3,
|
||||
9 => 3,
|
||||
10 => 3,
|
||||
100 => 5,
|
||||
101 => 5,
|
||||
102 => 5,
|
||||
103 => 3,
|
||||
104 => 3,
|
||||
105 => 3,
|
||||
106 => 3,
|
||||
107 => 3,
|
||||
108 => 3,
|
||||
109 => 3,
|
||||
110 => 3,
|
||||
'default' => 4,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
191
web/core/tests/Drupal/Tests/Component/Graph/GraphTest.php
Normal file
191
web/core/tests/Drupal/Tests/Component/Graph/GraphTest.php
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Graph;
|
||||
|
||||
use Drupal\Component\Graph\Graph;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Component\Graph\Graph
|
||||
* @group Graph
|
||||
*/
|
||||
class GraphTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Test depth-first-search features.
|
||||
*/
|
||||
public function testDepthFirstSearch() {
|
||||
// The sample graph used is:
|
||||
// 1 --> 2 --> 3 5 ---> 6
|
||||
// | ^ ^
|
||||
// | | |
|
||||
// | | |
|
||||
// +---> 4 <-- 7 8 ---> 9
|
||||
$graph = $this->normalizeGraph(array(
|
||||
1 => array(2),
|
||||
2 => array(3, 4),
|
||||
3 => array(),
|
||||
4 => array(3),
|
||||
5 => array(6),
|
||||
7 => array(4, 5),
|
||||
8 => array(9),
|
||||
9 => array(),
|
||||
));
|
||||
$graph_object = new Graph($graph);
|
||||
$graph = $graph_object->searchAndSort();
|
||||
|
||||
$expected_paths = array(
|
||||
1 => array(2, 3, 4),
|
||||
2 => array(3, 4),
|
||||
3 => array(),
|
||||
4 => array(3),
|
||||
5 => array(6),
|
||||
7 => array(4, 3, 5, 6),
|
||||
8 => array(9),
|
||||
9 => array(),
|
||||
);
|
||||
$this->assertPaths($graph, $expected_paths);
|
||||
|
||||
$expected_reverse_paths = array(
|
||||
1 => array(),
|
||||
2 => array(1),
|
||||
3 => array(2, 1, 4, 7),
|
||||
4 => array(2, 1, 7),
|
||||
5 => array(7),
|
||||
7 => array(),
|
||||
8 => array(),
|
||||
9 => array(8),
|
||||
);
|
||||
$this->assertReversePaths($graph, $expected_reverse_paths);
|
||||
|
||||
// Assert that DFS didn't created "missing" vertexes automatically.
|
||||
$this->assertFalse(isset($graph[6]), 'Vertex 6 has not been created');
|
||||
|
||||
$expected_components = array(
|
||||
array(1, 2, 3, 4, 5, 7),
|
||||
array(8, 9),
|
||||
);
|
||||
$this->assertComponents($graph, $expected_components);
|
||||
|
||||
$expected_weights = array(
|
||||
array(1, 2, 3),
|
||||
array(2, 4, 3),
|
||||
array(7, 4, 3),
|
||||
array(7, 5),
|
||||
array(8, 9),
|
||||
);
|
||||
$this->assertWeights($graph, $expected_weights);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a graph.
|
||||
*
|
||||
* @param $graph
|
||||
* A graph array processed by \Drupal\Component\Graph\Graph::searchAndSort()
|
||||
*
|
||||
* @return array
|
||||
* The normalized version of a graph.
|
||||
*/
|
||||
protected function normalizeGraph($graph) {
|
||||
$normalized_graph = array();
|
||||
foreach ($graph as $vertex => $edges) {
|
||||
// Create vertex even if it hasn't any edges.
|
||||
$normalized_graph[$vertex] = array();
|
||||
foreach ($edges as $edge) {
|
||||
$normalized_graph[$vertex]['edges'][$edge] = TRUE;
|
||||
}
|
||||
}
|
||||
return $normalized_graph;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify expected paths in a graph.
|
||||
*
|
||||
* @param $graph
|
||||
* A graph array processed by \Drupal\Component\Graph\Graph::searchAndSort()
|
||||
* @param $expected_paths
|
||||
* An associative array containing vertices with their expected paths.
|
||||
*/
|
||||
protected function assertPaths($graph, $expected_paths) {
|
||||
foreach ($expected_paths as $vertex => $paths) {
|
||||
// Build an array with keys = $paths and values = TRUE.
|
||||
$expected = array_fill_keys($paths, TRUE);
|
||||
$result = isset($graph[$vertex]['paths']) ? $graph[$vertex]['paths'] : array();
|
||||
$this->assertEquals($expected, $result, sprintf('Expected paths for vertex %s: %s, got %s', $vertex, $this->displayArray($expected, TRUE), $this->displayArray($result, TRUE)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify expected reverse paths in a graph.
|
||||
*
|
||||
* @param $graph
|
||||
* A graph array processed by \Drupal\Component\Graph\Graph::searchAndSort()
|
||||
* @param $expected_reverse_paths
|
||||
* An associative array containing vertices with their expected reverse
|
||||
* paths.
|
||||
*/
|
||||
protected function assertReversePaths($graph, $expected_reverse_paths) {
|
||||
foreach ($expected_reverse_paths as $vertex => $paths) {
|
||||
// Build an array with keys = $paths and values = TRUE.
|
||||
$expected = array_fill_keys($paths, TRUE);
|
||||
$result = isset($graph[$vertex]['reverse_paths']) ? $graph[$vertex]['reverse_paths'] : array();
|
||||
$this->assertEquals($expected, $result, sprintf('Expected reverse paths for vertex %s: %s, got %s', $vertex, $this->displayArray($expected, TRUE), $this->displayArray($result, TRUE)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify expected components in a graph.
|
||||
*
|
||||
* @param $graph
|
||||
* A graph array processed by \Drupal\Component\Graph\Graph::searchAndSort().
|
||||
* @param $expected_components
|
||||
* An array containing of components defined as a list of their vertices.
|
||||
*/
|
||||
protected function assertComponents($graph, $expected_components) {
|
||||
$unassigned_vertices = array_fill_keys(array_keys($graph), TRUE);
|
||||
foreach ($expected_components as $component) {
|
||||
$result_components = array();
|
||||
foreach ($component as $vertex) {
|
||||
$result_components[] = $graph[$vertex]['component'];
|
||||
unset($unassigned_vertices[$vertex]);
|
||||
}
|
||||
$this->assertEquals(1, count(array_unique($result_components)), sprintf('Expected one unique component for vertices %s, got %s', $this->displayArray($component), $this->displayArray($result_components)));
|
||||
}
|
||||
$this->assertEquals(array(), $unassigned_vertices, sprintf('Vertices not assigned to a component: %s', $this->displayArray($unassigned_vertices, TRUE)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify expected order in a graph.
|
||||
*
|
||||
* @param $graph
|
||||
* A graph array processed by \Drupal\Component\Graph\Graph::searchAndSort()
|
||||
* @param $expected_orders
|
||||
* An array containing lists of vertices in their expected order.
|
||||
*/
|
||||
protected function assertWeights($graph, $expected_orders) {
|
||||
foreach ($expected_orders as $order) {
|
||||
$previous_vertex = array_shift($order);
|
||||
foreach ($order as $vertex) {
|
||||
$this->assertTrue($graph[$previous_vertex]['weight'] < $graph[$vertex]['weight'], sprintf('Weights of %s and %s are correct relative to each other', $previous_vertex, $vertex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to output vertices as comma-separated list.
|
||||
*
|
||||
* @param $paths
|
||||
* An array containing a list of vertices.
|
||||
* @param $keys
|
||||
* (optional) Whether to output the keys of $paths instead of the values.
|
||||
*/
|
||||
protected function displayArray($paths, $keys = FALSE) {
|
||||
if (!empty($paths)) {
|
||||
return implode(', ', $keys ? array_keys($paths) : $paths);
|
||||
}
|
||||
else {
|
||||
return '(empty)';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\Component\HttpFoundation\SecuredRedirectResponseTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\Component\HttpFoundation;
|
||||
|
||||
use Drupal\Component\HttpFoundation\SecuredRedirectResponse;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Symfony\Component\HttpFoundation\Cookie;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
|
||||
/**
|
||||
* Test secure redirect base class.
|
||||
*
|
||||
* @group Routing
|
||||
* @coversDefaultClass \Drupal\Component\HttpFoundation\SecuredRedirectResponse
|
||||
*/
|
||||
class SecuredRedirectResponseTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Test copying of redirect response.
|
||||
*
|
||||
* @covers ::createFromRedirectResponse
|
||||
* @covers ::fromResponse
|
||||
*/
|
||||
public function testRedirectCopy() {
|
||||
$redirect = new RedirectResponse('/magic_redirect_url', 301, ['x-cache-foobar' => 123]);
|
||||
$redirect->setProtocolVersion('2.0');
|
||||
$redirect->setCharset('ibm-943_P14A-2000');
|
||||
$redirect->headers->setCookie(new Cookie('name', 'value'));
|
||||
|
||||
// Make a cloned redirect.
|
||||
$secureRedirect = SecuredRedirectStub::createFromRedirectResponse($redirect);
|
||||
$this->assertEquals('/magic_redirect_url', $secureRedirect->getTargetUrl());
|
||||
$this->assertEquals(301, $secureRedirect->getStatusCode());
|
||||
// We pull the headers from the original redirect because there are default headers applied.
|
||||
$headers1 = $redirect->headers->allPreserveCase();
|
||||
$headers2 = $secureRedirect->headers->allPreserveCase();
|
||||
// We unset cache headers so we don't test arcane Symfony weirdness.
|
||||
// https://github.com/symfony/symfony/issues/16171
|
||||
unset($headers1['Cache-Control'], $headers2['Cache-Control']);
|
||||
$this->assertEquals($headers1, $headers2);
|
||||
$this->assertEquals('2.0', $secureRedirect->getProtocolVersion());
|
||||
$this->assertEquals('ibm-943_P14A-2000', $secureRedirect->getCharset());
|
||||
$this->assertEquals($redirect->headers->getCookies(), $secureRedirect->headers->getCookies());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SecuredRedirectStub extends SecuredRedirectResponse {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function isSafe($url) {
|
||||
// Empty implementation for testing.
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\PhpStorage;
|
||||
|
||||
use Drupal\Component\PhpStorage\FileStorage;
|
||||
use Drupal\Component\PhpStorage\FileReadOnlyStorage;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Component\PhpStorage\FileReadOnlyStorage
|
||||
*
|
||||
* @group Drupal
|
||||
* @group PhpStorage
|
||||
*/
|
||||
class FileStorageReadOnlyTest extends PhpStorageTestBase {
|
||||
|
||||
/**
|
||||
* Standard test settings to pass to storage instances.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $standardSettings;
|
||||
|
||||
/**
|
||||
* Read only test settings to pass to storage instances.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $readonlyStorage;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->standardSettings = array(
|
||||
'directory' => $this->directory,
|
||||
'bin' => 'test',
|
||||
);
|
||||
$this->readonlyStorage = array(
|
||||
'directory' => $this->directory,
|
||||
// Let this read from the bin where the other instance is writing.
|
||||
'bin' => 'test',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests writing with one class and reading with another.
|
||||
*/
|
||||
public function testReadOnly() {
|
||||
$php = new FileStorage($this->standardSettings);
|
||||
$name = $this->randomMachineName() . '/' . $this->randomMachineName() . '.php';
|
||||
|
||||
// Find a global that doesn't exist.
|
||||
do {
|
||||
$random = mt_rand(10000, 100000);
|
||||
} while (isset($GLOBALS[$random]));
|
||||
|
||||
// Write out a PHP file and ensure it's successfully loaded.
|
||||
$code = "<?php\n\$GLOBALS[$random] = TRUE;";
|
||||
$success = $php->save($name, $code);
|
||||
$this->assertSame($success, TRUE);
|
||||
$php_read = new FileReadOnlyStorage($this->readonlyStorage);
|
||||
$php_read->load($name);
|
||||
$this->assertTrue($GLOBALS[$random]);
|
||||
|
||||
// If the file was successfully loaded, it must also exist, but ensure the
|
||||
// exists() method returns that correctly.
|
||||
$this->assertSame($php_read->exists($name), TRUE);
|
||||
// Saving and deleting should always fail.
|
||||
$this->assertFalse($php_read->save($name, $code));
|
||||
$this->assertFalse($php_read->delete($name));
|
||||
unset($GLOBALS[$random]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::writeable
|
||||
*/
|
||||
public function testWriteable() {
|
||||
$php_read = new FileReadOnlyStorage($this->readonlyStorage);
|
||||
$this->assertFalse($php_read->writeable());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::deleteAll
|
||||
*/
|
||||
public function testDeleteAll() {
|
||||
$php = new FileStorage($this->standardSettings);
|
||||
$name = $this->randomMachineName() . '/' . $this->randomMachineName() . '.php';
|
||||
|
||||
// Find a global that doesn't exist.
|
||||
do {
|
||||
$random = mt_rand(10000, 100000);
|
||||
} while (isset($GLOBALS[$random]));
|
||||
|
||||
// Write our the file so we can test deleting.
|
||||
$code = "<?php\n\$GLOBALS[$random] = TRUE;";
|
||||
$this->assertTrue($php->save($name, $code));
|
||||
|
||||
$php_read = new FileReadOnlyStorage($this->readonlyStorage);
|
||||
$this->assertFalse($php_read->deleteAll());
|
||||
|
||||
// Make sure directory exists prior to removal.
|
||||
$this->assertTrue(file_exists($this->directory . '/test'), 'File storage directory does not exist.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\PhpStorage;
|
||||
|
||||
use Drupal\Component\PhpStorage\FileStorage;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Component\PhpStorage\FileStorage
|
||||
* @group Drupal
|
||||
* @group PhpStorage
|
||||
*/
|
||||
class FileStorageTest extends PhpStorageTestBase {
|
||||
|
||||
/**
|
||||
* Standard test settings to pass to storage instances.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $standardSettings;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->standardSettings = array(
|
||||
'directory' => $this->directory,
|
||||
'bin' => 'test',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests basic load/save/delete operations.
|
||||
*
|
||||
* @covers ::load
|
||||
* @covers ::save
|
||||
* @covers ::exists
|
||||
* @covers ::delete
|
||||
*/
|
||||
public function testCRUD() {
|
||||
$php = new FileStorage($this->standardSettings);
|
||||
$this->assertCRUD($php);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::writeable
|
||||
*/
|
||||
public function testWriteable() {
|
||||
$php = new FileStorage($this->standardSettings);
|
||||
$this->assertTrue($php->writeable());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::deleteAll
|
||||
*/
|
||||
public function testDeleteAll() {
|
||||
|
||||
// Write out some files.
|
||||
$php = new FileStorage($this->standardSettings);
|
||||
|
||||
$name = $this->randomMachineName() . '/' . $this->randomMachineName() . '.php';
|
||||
|
||||
// Find a global that doesn't exist.
|
||||
do {
|
||||
$random = mt_rand(10000, 100000);
|
||||
} while (isset($GLOBALS[$random]));
|
||||
|
||||
// Write out a PHP file and ensure it's successfully loaded.
|
||||
$code = "<?php\n\$GLOBALS[$random] = TRUE;";
|
||||
$this->assertTrue($php->save($name, $code), 'Saved php file');
|
||||
$php->load($name);
|
||||
$this->assertTrue($GLOBALS[$random], 'File saved correctly with correct value');
|
||||
|
||||
// Make sure directory exists prior to removal.
|
||||
$this->assertTrue(file_exists($this->directory . '/test'), 'File storage directory does not exist.');
|
||||
|
||||
$this->assertTrue($php->deleteAll(), 'Delete all reported success');
|
||||
$this->assertFalse($php->load($name));
|
||||
$this->assertFalse(file_exists($this->directory . '/test'), 'File storage directory does not exist after call to deleteAll()');
|
||||
|
||||
// Should still return TRUE if directory has already been deleted.
|
||||
$this->assertTrue($php->deleteAll(), 'Delete all succeeds with nothing to delete');
|
||||
unset($GLOBALS[$random]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\PhpStorage;
|
||||
|
||||
/**
|
||||
* Tests the MTimeProtectedFastFileStorage implementation.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\Component\PhpStorage\MTimeProtectedFastFileStorage
|
||||
*
|
||||
* @group Drupal
|
||||
* @group PhpStorage
|
||||
*/
|
||||
class MTimeProtectedFastFileStorageTest extends MTimeProtectedFileStorageBase {
|
||||
|
||||
/**
|
||||
* The expected test results for the security test.
|
||||
*
|
||||
* The first iteration does not change the directory mtime so this class will
|
||||
* include the hacked file on the first try but the second test will change
|
||||
* the directory mtime and so on the second try the file will not be included.
|
||||
*/
|
||||
protected $expected = array(TRUE, FALSE);
|
||||
|
||||
/**
|
||||
* The PHP storage class to test.
|
||||
*/
|
||||
protected $storageClass = 'Drupal\Component\PhpStorage\MTimeProtectedFastFileStorage';
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\PhpStorage;
|
||||
|
||||
/**
|
||||
* Base test class for MTime protected storage.
|
||||
*/
|
||||
abstract class MTimeProtectedFileStorageBase extends PhpStorageTestBase {
|
||||
|
||||
/**
|
||||
* The PHP storage class to test.
|
||||
*
|
||||
* This should be overridden by extending classes.
|
||||
*/
|
||||
protected $storageClass;
|
||||
|
||||
/**
|
||||
* The secret string to use for file creation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $secret;
|
||||
|
||||
/**
|
||||
* Test settings to pass to storage instances.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->secret = $this->randomMachineName();
|
||||
|
||||
$this->settings = array(
|
||||
'directory' => $this->directory,
|
||||
'bin' => 'test',
|
||||
'secret' => $this->secret,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests basic load/save/delete operations.
|
||||
*
|
||||
* @covers ::load
|
||||
* @covers ::save
|
||||
* @covers ::delete
|
||||
* @covers ::exists
|
||||
*/
|
||||
public function testCRUD() {
|
||||
$php = new $this->storageClass($this->settings);
|
||||
$this->assertCRUD($php);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the security of the MTimeProtectedFileStorage implementation.
|
||||
*
|
||||
* We test two attacks: first changes the file mtime, then the directory
|
||||
* mtime too.
|
||||
*
|
||||
* We need to delay over 1 second for mtime test.
|
||||
* @medium
|
||||
*/
|
||||
public function testSecurity() {
|
||||
$php = new $this->storageClass($this->settings);
|
||||
$name = 'simpletest.php';
|
||||
$php->save($name, '<?php');
|
||||
$expected_root_directory = $this->directory . '/test';
|
||||
if (substr($name, -4) === '.php') {
|
||||
$expected_directory = $expected_root_directory . '/' . substr($name, 0, -4);
|
||||
}
|
||||
else {
|
||||
$expected_directory = $expected_root_directory . '/' . $name;
|
||||
}
|
||||
$directory_mtime = filemtime($expected_directory);
|
||||
$expected_filename = $expected_directory . '/' . hash_hmac('sha256', $name, $this->secret . $directory_mtime) . '.php';
|
||||
|
||||
// Ensure the file exists and that it and the containing directory have
|
||||
// minimal permissions. fileperms() can return high bits unrelated to
|
||||
// permissions, so mask with 0777.
|
||||
$this->assertTrue(file_exists($expected_filename));
|
||||
$this->assertSame(fileperms($expected_filename) & 0777, 0444);
|
||||
$this->assertSame(fileperms($expected_directory) & 0777, 0777);
|
||||
|
||||
// Ensure the root directory for the bin has a .htaccess file denying web
|
||||
// access.
|
||||
$this->assertSame(file_get_contents($expected_root_directory . '/.htaccess'), call_user_func(array($this->storageClass, 'htaccessLines')));
|
||||
|
||||
// Ensure that if the file is replaced with an untrusted one (due to another
|
||||
// script's file upload vulnerability), it does not get loaded. Since mtime
|
||||
// granularity is 1 second, we cannot prevent an attack that happens within
|
||||
// a second of the initial save().
|
||||
sleep(1);
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$php = new $this->storageClass($this->settings);
|
||||
$GLOBALS['hacked'] = FALSE;
|
||||
$untrusted_code = "<?php\n" . '$GLOBALS["hacked"] = TRUE;';
|
||||
chmod($expected_directory, 0700);
|
||||
chmod($expected_filename, 0700);
|
||||
if ($i) {
|
||||
// Now try to write the file in such a way that the directory mtime
|
||||
// changes and invalidates the hash.
|
||||
file_put_contents($expected_filename . '.tmp', $untrusted_code);
|
||||
rename($expected_filename . '.tmp', $expected_filename);
|
||||
}
|
||||
else {
|
||||
// On the first try do not change the directory mtime but the filemtime
|
||||
// is now larger than the directory mtime.
|
||||
file_put_contents($expected_filename, $untrusted_code);
|
||||
}
|
||||
chmod($expected_filename, 0400);
|
||||
chmod($expected_directory, 0100);
|
||||
$this->assertSame(file_get_contents($expected_filename), $untrusted_code);
|
||||
$this->assertSame($php->exists($name), $this->expected[$i]);
|
||||
$this->assertSame($php->load($name), $this->expected[$i]);
|
||||
$this->assertSame($GLOBALS['hacked'], $this->expected[$i]);
|
||||
}
|
||||
unset($GLOBALS['hacked']);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\PhpStorage;
|
||||
|
||||
/**
|
||||
* Tests the MTimeProtectedFileStorage implementation.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\Component\PhpStorage\MTimeProtectedFileStorage
|
||||
*
|
||||
* @group Drupal
|
||||
* @group PhpStorage
|
||||
*/
|
||||
class MTimeProtectedFileStorageTest extends MTimeProtectedFileStorageBase {
|
||||
|
||||
/**
|
||||
* The expected test results for the security test.
|
||||
*
|
||||
* The default implementation protects against even the filemtime change so
|
||||
* both iterations will return FALSE.
|
||||
*/
|
||||
protected $expected = array(FALSE, FALSE);
|
||||
|
||||
/**
|
||||
* The PHP storage class to test.
|
||||
*/
|
||||
protected $storageClass = 'Drupal\Component\PhpStorage\MTimeProtectedFileStorage';
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\PhpStorage;
|
||||
|
||||
use Drupal\Component\PhpStorage\PhpStorageInterface;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use org\bovigo\vfs\vfsStream;
|
||||
|
||||
/**
|
||||
* Base test for PHP storages.
|
||||
*/
|
||||
abstract class PhpStorageTestBase extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* A unique per test class directory path to test php storage.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $directory;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
vfsStream::setup('exampleDir');
|
||||
$this->directory = vfsStream::url('exampleDir');
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that a PHP storage's load/save/delete operations work.
|
||||
*/
|
||||
public function assertCRUD($php) {
|
||||
$name = $this->randomMachineName() . '/' . $this->randomMachineName() . '.php';
|
||||
|
||||
// Find a global that doesn't exist.
|
||||
do {
|
||||
$random = mt_rand(10000, 100000);
|
||||
} while (isset($GLOBALS[$random]));
|
||||
|
||||
// Write out a PHP file and ensure it's successfully loaded.
|
||||
$code = "<?php\n\$GLOBALS[$random] = TRUE;";
|
||||
$success = $php->save($name, $code);
|
||||
$this->assertTrue($success, 'Saved php file');
|
||||
$php->load($name);
|
||||
$this->assertTrue($GLOBALS[$random], 'File saved correctly with correct value');
|
||||
|
||||
// Run additional asserts.
|
||||
$this->additionalAssertCRUD($php, $name);
|
||||
|
||||
// If the file was successfully loaded, it must also exist, but ensure the
|
||||
// exists() method returns that correctly.
|
||||
$this->assertTrue($php->exists($name), 'Exists works correctly');
|
||||
|
||||
// Delete the file, and then ensure exists() returns FALSE.
|
||||
$this->assertTrue($php->delete($name), 'Delete succeeded');
|
||||
$this->assertFalse($php->exists($name), 'Delete deleted file');
|
||||
|
||||
// Ensure delete() can be called on a non-existing file. It should return
|
||||
// FALSE, but not trigger errors.
|
||||
$this->assertFalse($php->delete($name), 'Delete fails on missing file');
|
||||
unset($GLOBALS[$random]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional asserts to be run.
|
||||
*
|
||||
* @param \Drupal\Component\PhpStorage\PhpStorageInterface $php
|
||||
* The PHP storage object.
|
||||
* @param string $name
|
||||
* The name of an object. It should exist in the storage.
|
||||
*/
|
||||
protected function additionalAssertCRUD(PhpStorageInterface $php, $name) {
|
||||
// By default do not do any additional asserts. This is a way of extending
|
||||
// tests in contrib.
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Plugin\Context;
|
||||
|
||||
use Drupal\Component\Plugin\Context\Context;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Component\Plugin\Context\Context
|
||||
* @group Plugin
|
||||
*/
|
||||
class ContextTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Data provider for testGetContextValue.
|
||||
*/
|
||||
public function providerGetContextValue() {
|
||||
return [
|
||||
['context_value', 'context_value', FALSE, 'data_type'],
|
||||
[NULL, NULL, FALSE, 'data_type'],
|
||||
['will throw exception', NULL, TRUE, 'data_type'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getContextValue
|
||||
* @dataProvider providerGetContextValue
|
||||
*/
|
||||
public function testGetContextValue($expected, $context_value, $is_required, $data_type) {
|
||||
// Mock a Context object.
|
||||
$mock_context = $this->getMockBuilder('Drupal\Component\Plugin\Context\Context')
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(array('getContextDefinition'))
|
||||
->getMock();
|
||||
|
||||
// If the context value exists, getContextValue() behaves like a normal
|
||||
// getter.
|
||||
if ($context_value) {
|
||||
// Set visibility of contextValue.
|
||||
$ref_context_value = new \ReflectionProperty($mock_context, 'contextValue');
|
||||
$ref_context_value->setAccessible(TRUE);
|
||||
// Set contextValue to a testable state.
|
||||
$ref_context_value->setValue($mock_context, $context_value);
|
||||
// Exercise getContextValue().
|
||||
$this->assertEquals($context_value, $mock_context->getContextValue());
|
||||
}
|
||||
// If no context value exists, we have to cover either returning NULL or
|
||||
// throwing an exception if the definition requires it.
|
||||
else {
|
||||
// Create a mock definition.
|
||||
$mock_definition = $this->getMockBuilder('Drupal\Component\Plugin\Context\ContextDefinitionInterface')
|
||||
->setMethods(array('isRequired', 'getDataType'))
|
||||
->getMockForAbstractClass();
|
||||
|
||||
// Set expectation for isRequired().
|
||||
$mock_definition->expects($this->once())
|
||||
->method('isRequired')
|
||||
->willReturn($is_required);
|
||||
|
||||
// Set expectation for getDataType().
|
||||
$mock_definition->expects($this->exactly(
|
||||
$is_required ? 1 : 0
|
||||
))
|
||||
->method('getDataType')
|
||||
->willReturn($data_type);
|
||||
|
||||
// Set expectation for getContextDefinition().
|
||||
$mock_context->expects($this->once())
|
||||
->method('getContextDefinition')
|
||||
->willReturn($mock_definition);
|
||||
|
||||
// Set expectation for exception.
|
||||
if ($is_required) {
|
||||
$this->setExpectedException(
|
||||
'Drupal\Component\Plugin\Exception\ContextException',
|
||||
sprintf("The %s context is required and not present.", $data_type)
|
||||
);
|
||||
}
|
||||
|
||||
// Exercise getContextValue().
|
||||
$this->assertEquals($context_value, $mock_context->getContextValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getContextValue
|
||||
*/
|
||||
public function testDefaultValue() {
|
||||
$mock_definition = $this->getMockBuilder('Drupal\Component\Plugin\Context\ContextDefinitionInterface')
|
||||
->setMethods(array('getDefaultValue'))
|
||||
->getMockForAbstractClass();
|
||||
|
||||
$mock_definition->expects($this->once())
|
||||
->method('getDefaultValue')
|
||||
->willReturn('test');
|
||||
|
||||
$context = new Context($mock_definition);
|
||||
$this->assertEquals('test', $context->getContextValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Plugin;
|
||||
|
||||
use Drupal\Component\Plugin\Definition\PluginDefinitionInterface;
|
||||
use Drupal\Component\Plugin\Factory\DefaultFactory;
|
||||
use Drupal\plugin_test\Plugin\plugin_test\fruit\Cherry;
|
||||
use Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface;
|
||||
use Drupal\plugin_test\Plugin\plugin_test\fruit\Kale;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Component\Plugin\Factory\DefaultFactory
|
||||
* @group Plugin
|
||||
*/
|
||||
class DefaultFactoryTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Tests getPluginClass() with a valid array plugin definition.
|
||||
*
|
||||
* @covers ::getPluginClass
|
||||
*/
|
||||
public function testGetPluginClassWithValidArrayPluginDefinition() {
|
||||
$plugin_class = Cherry::class;
|
||||
$class = DefaultFactory::getPluginClass('cherry', ['class' => $plugin_class]);
|
||||
|
||||
$this->assertEquals($plugin_class, $class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests getPluginClass() with a valid object plugin definition.
|
||||
*
|
||||
* @covers ::getPluginClass
|
||||
*/
|
||||
public function testGetPluginClassWithValidObjectPluginDefinition() {
|
||||
$plugin_class = Cherry::class;
|
||||
$plugin_definition = $this->getMock(PluginDefinitionInterface::class);
|
||||
$plugin_definition->expects($this->atLeastOnce())
|
||||
->method('getClass')
|
||||
->willReturn($plugin_class);
|
||||
$class = DefaultFactory::getPluginClass('cherry', $plugin_definition);
|
||||
|
||||
$this->assertEquals($plugin_class, $class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests getPluginClass() with a missing class definition.
|
||||
*
|
||||
* @covers ::getPluginClass
|
||||
*
|
||||
* @expectedException \Drupal\Component\Plugin\Exception\PluginException
|
||||
* @expectedExceptionMessage The plugin (cherry) did not specify an instance class.
|
||||
*/
|
||||
public function testGetPluginClassWithMissingClassWithArrayPluginDefinition() {
|
||||
DefaultFactory::getPluginClass('cherry', []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests getPluginClass() with a missing class definition.
|
||||
*
|
||||
* @covers ::getPluginClass
|
||||
*
|
||||
* @expectedException \Drupal\Component\Plugin\Exception\PluginException
|
||||
* @expectedExceptionMessage The plugin (cherry) did not specify an instance class.
|
||||
*/
|
||||
public function testGetPluginClassWithMissingClassWithObjectPluginDefinition() {
|
||||
$plugin_definition = $this->getMock(PluginDefinitionInterface::class);
|
||||
DefaultFactory::getPluginClass('cherry', $plugin_definition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests getPluginClass() with a not existing class definition.
|
||||
*
|
||||
* @covers ::getPluginClass
|
||||
*
|
||||
* @expectedException \Drupal\Component\Plugin\Exception\PluginException
|
||||
* @expectedExceptionMessage Plugin (kiwifruit) instance class "\Drupal\plugin_test\Plugin\plugin_test\fruit\Kiwifruit" does not exist.
|
||||
*/
|
||||
public function testGetPluginClassWithNotExistingClassWithArrayPluginDefinition() {
|
||||
DefaultFactory::getPluginClass('kiwifruit', ['class' => '\Drupal\plugin_test\Plugin\plugin_test\fruit\Kiwifruit']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests getPluginClass() with a not existing class definition.
|
||||
*
|
||||
* @covers ::getPluginClass
|
||||
*
|
||||
* @expectedException \Drupal\Component\Plugin\Exception\PluginException
|
||||
*/
|
||||
public function testGetPluginClassWithNotExistingClassWithObjectPluginDefinition() {
|
||||
$plugin_class = '\Drupal\plugin_test\Plugin\plugin_test\fruit\Kiwifruit';
|
||||
$plugin_definition = $this->getMock(PluginDefinitionInterface::class);
|
||||
$plugin_definition->expects($this->atLeastOnce())
|
||||
->method('getClass')
|
||||
->willReturn($plugin_class);
|
||||
DefaultFactory::getPluginClass('kiwifruit', $plugin_definition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests getPluginClass() with a required interface.
|
||||
*
|
||||
* @covers ::getPluginClass
|
||||
*/
|
||||
public function testGetPluginClassWithInterfaceWithArrayPluginDefinition() {
|
||||
$plugin_class = Cherry::class;
|
||||
$class = DefaultFactory::getPluginClass('cherry', ['class' => $plugin_class], FruitInterface::class);
|
||||
|
||||
$this->assertEquals($plugin_class, $class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests getPluginClass() with a required interface.
|
||||
*
|
||||
* @covers ::getPluginClass
|
||||
*/
|
||||
public function testGetPluginClassWithInterfaceWithObjectPluginDefinition() {
|
||||
$plugin_class = Cherry::class;
|
||||
$plugin_definition = $this->getMock(PluginDefinitionInterface::class);
|
||||
$plugin_definition->expects($this->atLeastOnce())
|
||||
->method('getClass')
|
||||
->willReturn($plugin_class);
|
||||
$class = DefaultFactory::getPluginClass('cherry', $plugin_definition, FruitInterface::class);
|
||||
|
||||
$this->assertEquals($plugin_class, $class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests getPluginClass() with a required interface but no implementation.
|
||||
*
|
||||
* @covers ::getPluginClass
|
||||
*
|
||||
* @expectedException \Drupal\Component\Plugin\Exception\PluginException
|
||||
* @expectedExceptionMessage Plugin "cherry" (Drupal\plugin_test\Plugin\plugin_test\fruit\Kale) must implement interface Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface.
|
||||
*/
|
||||
public function testGetPluginClassWithInterfaceAndInvalidClassWithArrayPluginDefinition() {
|
||||
$plugin_class = Kale::class;
|
||||
DefaultFactory::getPluginClass('cherry', ['class' => $plugin_class, 'provider' => 'core'], FruitInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests getPluginClass() with a required interface but no implementation.
|
||||
*
|
||||
* @covers ::getPluginClass
|
||||
*
|
||||
* @expectedException \Drupal\Component\Plugin\Exception\PluginException
|
||||
*/
|
||||
public function testGetPluginClassWithInterfaceAndInvalidClassWithObjectPluginDefinition() {
|
||||
$plugin_class = Kale::class;
|
||||
$plugin_definition = $this->getMock(PluginDefinitionInterface::class);
|
||||
$plugin_definition->expects($this->atLeastOnce())
|
||||
->method('getClass')
|
||||
->willReturn($plugin_class);
|
||||
DefaultFactory::getPluginClass('cherry', $plugin_definition, FruitInterface::class);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Plugin\Discovery;
|
||||
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Component\Plugin\Discovery\DiscoveryCachedTrait
|
||||
* @uses \Drupal\Component\Plugin\Discovery\DiscoveryTrait
|
||||
* @group Plugin
|
||||
*/
|
||||
class DiscoveryCachedTraitTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Data provider for testGetDefinition().
|
||||
*
|
||||
* @return array
|
||||
* - Expected result from getDefinition().
|
||||
* - Cached definitions to be placed into self::$definitions
|
||||
* - Definitions to be returned by getDefinitions().
|
||||
* - Plugin name to query for.
|
||||
*/
|
||||
public function providerGetDefinition() {
|
||||
return array(
|
||||
['definition', [], ['plugin_name' => 'definition'], 'plugin_name'],
|
||||
['definition', ['plugin_name' => 'definition'], [], 'plugin_name'],
|
||||
[NULL, ['plugin_name' => 'definition'], [], 'bad_plugin_name'],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getDefinition
|
||||
* @dataProvider providerGetDefinition
|
||||
*/
|
||||
public function testGetDefinition($expected, $cached_definitions, $get_definitions, $plugin_id) {
|
||||
// Mock a DiscoveryCachedTrait.
|
||||
$trait = $this->getMockForTrait('Drupal\Component\Plugin\Discovery\DiscoveryCachedTrait');
|
||||
$reflection_definitions = new \ReflectionProperty($trait, 'definitions');
|
||||
$reflection_definitions->setAccessible(TRUE);
|
||||
// getDefinition() needs the ::$definitions property to be set in one of two
|
||||
// ways: 1) As existing cached data, or 2) as a side-effect of calling
|
||||
// getDefinitions().
|
||||
// If there are no cached definitions, then we have to fake the side-effect
|
||||
// of getDefinitions().
|
||||
if (count($cached_definitions) < 1) {
|
||||
$trait->expects($this->once())
|
||||
->method('getDefinitions')
|
||||
// Use a callback method, so we can perform the side-effects.
|
||||
->willReturnCallback(function() use ($reflection_definitions, $trait, $get_definitions) {
|
||||
$reflection_definitions->setValue($trait, $get_definitions);
|
||||
return $get_definitions;
|
||||
});
|
||||
}
|
||||
else {
|
||||
// Put $cached_definitions into our mocked ::$definitions.
|
||||
$reflection_definitions->setValue($trait, $cached_definitions);
|
||||
}
|
||||
// Call getDefinition(), with $exception_on_invalid always FALSE.
|
||||
$this->assertSame(
|
||||
$expected,
|
||||
$trait->getDefinition($plugin_id, FALSE)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Plugin\Discovery;
|
||||
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @group Plugin
|
||||
* @coversDefaultClass \Drupal\Component\Plugin\Discovery\DiscoveryTrait
|
||||
*/
|
||||
class DiscoveryTraitTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Data provider for testDoGetDefinition().
|
||||
*
|
||||
* @return array
|
||||
* - Expected plugin definition.
|
||||
* - Plugin definition array, to pass to doGetDefinition().
|
||||
* - Plugin ID to get, passed to doGetDefinition().
|
||||
*/
|
||||
public function providerDoGetDefinition() {
|
||||
return array(
|
||||
['definition', ['plugin_name' => 'definition'], 'plugin_name'],
|
||||
[NULL, ['plugin_name' => 'definition'], 'bad_plugin_name'],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::doGetDefinition
|
||||
* @dataProvider providerDoGetDefinition
|
||||
*/
|
||||
public function testDoGetDefinition($expected, $definitions, $plugin_id) {
|
||||
// Mock the trait.
|
||||
$trait = $this->getMockForTrait('Drupal\Component\Plugin\Discovery\DiscoveryTrait');
|
||||
// Un-protect the method using reflection.
|
||||
$method_ref = new \ReflectionMethod($trait, 'doGetDefinition');
|
||||
$method_ref->setAccessible(TRUE);
|
||||
// Call doGetDefinition, with $exception_on_invalid always FALSE.
|
||||
$this->assertSame(
|
||||
$expected,
|
||||
$method_ref->invoke($trait, $definitions, $plugin_id, FALSE)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testDoGetDefinitionException()
|
||||
*
|
||||
* @return array
|
||||
* - Expected plugin definition.
|
||||
* - Plugin definition array, to pass to doGetDefinition().
|
||||
* - Plugin ID to get, passed to doGetDefinition().
|
||||
*/
|
||||
public function providerDoGetDefinitionException() {
|
||||
return array(
|
||||
[FALSE, ['plugin_name' => 'definition'], 'bad_plugin_name'],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::doGetDefinition
|
||||
* @expectedException \Drupal\Component\Plugin\Exception\PluginNotFoundException
|
||||
* @dataProvider providerDoGetDefinitionException
|
||||
* @uses \Drupal\Component\Plugin\Exception\PluginNotFoundException
|
||||
*/
|
||||
public function testDoGetDefinitionException($expected, $definitions, $plugin_id) {
|
||||
// Mock the trait.
|
||||
$trait = $this->getMockForTrait('Drupal\Component\Plugin\Discovery\DiscoveryTrait');
|
||||
// Un-protect the method using reflection.
|
||||
$method_ref = new \ReflectionMethod($trait, 'doGetDefinition');
|
||||
$method_ref->setAccessible(TRUE);
|
||||
// Call doGetDefinition, with $exception_on_invalid always TRUE.
|
||||
$this->assertSame(
|
||||
$expected,
|
||||
$method_ref->invoke($trait, $definitions, $plugin_id, TRUE)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getDefinition
|
||||
* @dataProvider providerDoGetDefinition
|
||||
*/
|
||||
public function testGetDefinition($expected, $definitions, $plugin_id) {
|
||||
// Since getDefinition is a wrapper around doGetDefinition(), we can re-use
|
||||
// its data provider. We just have to tell abstract method getDefinitions()
|
||||
// to use the $definitions array.
|
||||
$trait = $this->getMockForTrait('Drupal\Component\Plugin\Discovery\DiscoveryTrait');
|
||||
$trait->expects($this->once())
|
||||
->method('getDefinitions')
|
||||
->willReturn($definitions);
|
||||
// Call getDefinition(), with $exception_on_invalid always FALSE.
|
||||
$this->assertSame(
|
||||
$expected,
|
||||
$trait->getDefinition($plugin_id, FALSE)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getDefinition
|
||||
* @expectedException \Drupal\Component\Plugin\Exception\PluginNotFoundException
|
||||
* @dataProvider providerDoGetDefinitionException
|
||||
* @uses \Drupal\Component\Plugin\Exception\PluginNotFoundException
|
||||
*/
|
||||
public function testGetDefinitionException($expected, $definitions, $plugin_id) {
|
||||
// Since getDefinition is a wrapper around doGetDefinition(), we can re-use
|
||||
// its data provider. We just have to tell abstract method getDefinitions()
|
||||
// to use the $definitions array.
|
||||
$trait = $this->getMockForTrait('Drupal\Component\Plugin\Discovery\DiscoveryTrait');
|
||||
$trait->expects($this->once())
|
||||
->method('getDefinitions')
|
||||
->willReturn($definitions);
|
||||
// Call getDefinition(), with $exception_on_invalid always TRUE.
|
||||
$this->assertSame(
|
||||
$expected,
|
||||
$trait->getDefinition($plugin_id, TRUE)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testHasDefinition().
|
||||
*
|
||||
* @return array
|
||||
* - Expected TRUE or FALSE.
|
||||
* - Plugin ID to look for.
|
||||
*/
|
||||
public function providerHasDefinition() {
|
||||
return array(
|
||||
[TRUE, 'valid'],
|
||||
[FALSE, 'not_valid'],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::hasDefinition
|
||||
* @dataProvider providerHasDefinition
|
||||
*/
|
||||
public function testHasDefinition($expected, $plugin_id) {
|
||||
$trait = $this->getMockBuilder('Drupal\Component\Plugin\Discovery\DiscoveryTrait')
|
||||
->setMethods(array('getDefinition'))
|
||||
->getMockForTrait();
|
||||
// Set up our mocked getDefinition() to return TRUE for 'valid' and FALSE
|
||||
// for 'not_valid'.
|
||||
$trait->expects($this->once())
|
||||
->method('getDefinition')
|
||||
->will($this->returnValueMap(array(
|
||||
['valid', FALSE, TRUE],
|
||||
['not_valid', FALSE, FALSE],
|
||||
)));
|
||||
// Call hasDefinition().
|
||||
$this->assertSame(
|
||||
$expected,
|
||||
$trait->hasDefinition($plugin_id)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,229 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Plugin\Discovery;
|
||||
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @group Plugin
|
||||
* @coversDefaultClass \Drupal\Component\Plugin\Discovery\StaticDiscoveryDecorator
|
||||
*/
|
||||
class StaticDiscoveryDecoratorTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Helper method to provide a mocked callback object with expectations.
|
||||
*
|
||||
* If there should be a registered definition, then we have to place a
|
||||
* \Callable in the mock object. The return value of this callback is
|
||||
* never used.
|
||||
*
|
||||
* @return \PHPUnit_Framework_MockObject_MockObject
|
||||
* Mocked object with expectation of registerDefinitionsCallback() being
|
||||
* called once.
|
||||
*/
|
||||
public function getRegisterDefinitionsCallback() {
|
||||
$mock_callable = $this->getMockBuilder('\stdClass')
|
||||
->setMethods(array('registerDefinitionsCallback'))
|
||||
->getMock();
|
||||
// Set expectations for the callback method.
|
||||
$mock_callable->expects($this->once())
|
||||
->method('registerDefinitionsCallback');
|
||||
return $mock_callable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testGetDefinitions().
|
||||
*
|
||||
* @return array
|
||||
* - Expected plugin definition.
|
||||
* - Whether we require the method to register definitions through a
|
||||
* callback.
|
||||
* - Whether to throw an exception if the definition is invalid.
|
||||
* - A plugin definition.
|
||||
* - Base plugin ID.
|
||||
*/
|
||||
public function providerGetDefinition() {
|
||||
return [
|
||||
['is_defined', TRUE, FALSE, ['plugin-definition' => 'is_defined'], 'plugin-definition'],
|
||||
// Make sure we don't call the decorated method if we shouldn't.
|
||||
['is_defined', FALSE, FALSE, ['plugin-definition' => 'is_defined'], 'plugin-definition'],
|
||||
// Return NULL for bad plugin id.
|
||||
[NULL, FALSE, FALSE, ['plugin-definition' => 'is_defined'], 'BAD-plugin-definition'],
|
||||
// Generate an exception.
|
||||
[NULL, FALSE, TRUE, ['plugin-definition' => 'is_defined'], 'BAD-plugin-definition'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getDefinition
|
||||
* @dataProvider providerGetDefinition
|
||||
*/
|
||||
public function testGetDefinition($expected, $has_register_definitions, $exception_on_invalid, $definitions, $base_plugin_id) {
|
||||
// Mock our StaticDiscoveryDecorator.
|
||||
$mock_decorator = $this->getMockBuilder('Drupal\Component\Plugin\Discovery\StaticDiscoveryDecorator')
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(array('registeredDefintionCallback'))
|
||||
->getMock();
|
||||
|
||||
// Set up the ::$registerDefinitions property.
|
||||
$ref_register_definitions = new \ReflectionProperty($mock_decorator, 'registerDefinitions');
|
||||
$ref_register_definitions->setAccessible(TRUE);
|
||||
if ($has_register_definitions) {
|
||||
// Set the callback object on the mocked decorator.
|
||||
$ref_register_definitions->setValue(
|
||||
$mock_decorator,
|
||||
array($this->getRegisterDefinitionsCallback(), 'registerDefinitionsCallback')
|
||||
);
|
||||
}
|
||||
else {
|
||||
// There should be no registerDefinitions callback.
|
||||
$ref_register_definitions->setValue($mock_decorator, NULL);
|
||||
}
|
||||
|
||||
// Set up ::$definitions to an empty array.
|
||||
$ref_definitions = new \ReflectionProperty($mock_decorator, 'definitions');
|
||||
$ref_definitions->setAccessible(TRUE);
|
||||
$ref_definitions->setValue($mock_decorator, array());
|
||||
|
||||
// Mock a decorated object.
|
||||
$mock_decorated = $this->getMockBuilder('Drupal\Component\Plugin\Discovery\DiscoveryInterface')
|
||||
->setMethods(array('getDefinitions'))
|
||||
->getMockForAbstractClass();
|
||||
// Return our definitions from getDefinitions().
|
||||
$mock_decorated->expects($this->once())
|
||||
->method('getDefinitions')
|
||||
->willReturn($definitions);
|
||||
|
||||
// Set up ::$decorated to our mocked decorated object.
|
||||
$ref_decorated = new \ReflectionProperty($mock_decorator, 'decorated');
|
||||
$ref_decorated->setAccessible(TRUE);
|
||||
$ref_decorated->setValue($mock_decorator, $mock_decorated);
|
||||
|
||||
if ($exception_on_invalid) {
|
||||
$this->setExpectedException('Drupal\Component\Plugin\Exception\PluginNotFoundException');
|
||||
}
|
||||
|
||||
// Exercise getDefinition(). It calls parent::getDefinition().
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$mock_decorator->getDefinition($base_plugin_id, $exception_on_invalid)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testGetDefinitions().
|
||||
*
|
||||
* @return array
|
||||
* - bool Whether the test mock has a callback.
|
||||
* - array Plugin definitions.
|
||||
*/
|
||||
public function providerGetDefinitions() {
|
||||
return [
|
||||
[TRUE, ['definition' => 'is_fake']],
|
||||
[FALSE, ['definition' => 'array_of_stuff']],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getDefinitions
|
||||
* @dataProvider providerGetDefinitions
|
||||
*/
|
||||
public function testGetDefinitions($has_register_definitions, $definitions) {
|
||||
// Mock our StaticDiscoveryDecorator.
|
||||
$mock_decorator = $this->getMockBuilder('Drupal\Component\Plugin\Discovery\StaticDiscoveryDecorator')
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(array('registeredDefintionCallback'))
|
||||
->getMock();
|
||||
|
||||
// Set up the ::$registerDefinitions property.
|
||||
$ref_register_definitions = new \ReflectionProperty($mock_decorator, 'registerDefinitions');
|
||||
$ref_register_definitions->setAccessible(TRUE);
|
||||
if ($has_register_definitions) {
|
||||
// Set the callback object on the mocked decorator.
|
||||
$ref_register_definitions->setValue(
|
||||
$mock_decorator,
|
||||
array($this->getRegisterDefinitionsCallback(), 'registerDefinitionsCallback')
|
||||
);
|
||||
}
|
||||
else {
|
||||
// There should be no registerDefinitions callback.
|
||||
$ref_register_definitions->setValue($mock_decorator, NULL);
|
||||
}
|
||||
|
||||
// Set up ::$definitions to an empty array.
|
||||
$ref_definitions = new \ReflectionProperty($mock_decorator, 'definitions');
|
||||
$ref_definitions->setAccessible(TRUE);
|
||||
$ref_definitions->setValue($mock_decorator, array());
|
||||
|
||||
// Mock a decorated object.
|
||||
$mock_decorated = $this->getMockBuilder('Drupal\Component\Plugin\Discovery\DiscoveryInterface')
|
||||
->setMethods(array('getDefinitions'))
|
||||
->getMockForAbstractClass();
|
||||
// Our mocked method will return any arguments sent to it.
|
||||
$mock_decorated->expects($this->once())
|
||||
->method('getDefinitions')
|
||||
->willReturn($definitions);
|
||||
|
||||
// Set up ::$decorated to our mocked decorated object.
|
||||
$ref_decorated = new \ReflectionProperty($mock_decorator, 'decorated');
|
||||
$ref_decorated->setAccessible(TRUE);
|
||||
$ref_decorated->setValue($mock_decorator, $mock_decorated);
|
||||
|
||||
// Exercise getDefinitions(). It calls parent::getDefinitions() but in this
|
||||
// case there will be no side-effects.
|
||||
$this->assertArrayEquals(
|
||||
$definitions,
|
||||
$mock_decorator->getDefinitions()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testCall().
|
||||
*
|
||||
* @return array
|
||||
* - Method name.
|
||||
* - Array of arguments to pass to the method, with the expectation that our
|
||||
* mocked __call() will return them.
|
||||
*/
|
||||
public function providerCall() {
|
||||
return [
|
||||
['complexArguments', ['1', 2.0, 3, ['4' => 'five']]],
|
||||
['noArguments', []],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::__call
|
||||
* @dataProvider providerCall
|
||||
*/
|
||||
public function testCall($method, $args) {
|
||||
// Mock a decorated object.
|
||||
$mock_decorated = $this->getMockBuilder('Drupal\Component\Plugin\Discovery\DiscoveryInterface')
|
||||
->setMethods(array($method))
|
||||
->getMockForAbstractClass();
|
||||
// Our mocked method will return any arguments sent to it.
|
||||
$mock_decorated->expects($this->once())
|
||||
->method($method)
|
||||
->willReturnCallback(
|
||||
function () {
|
||||
return \func_get_args();
|
||||
}
|
||||
);
|
||||
|
||||
// Create a mock decorator.
|
||||
$mock_decorator = $this->getMockBuilder('Drupal\Component\Plugin\Discovery\StaticDiscoveryDecorator')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
// Poke the decorated object into our decorator.
|
||||
$ref_decorated = new \ReflectionProperty($mock_decorator, 'decorated');
|
||||
$ref_decorated->setAccessible(TRUE);
|
||||
$ref_decorated->setValue($mock_decorator, $mock_decorated);
|
||||
|
||||
// Exercise __call.
|
||||
$this->assertArrayEquals(
|
||||
$args,
|
||||
\call_user_func_array(array($mock_decorated, $method), $args)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,216 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\Component\Plugin\Factory\ReflectionFactoryTest.
|
||||
*
|
||||
* Also contains Argument* classes used as data for testing.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\Component\Plugin\Factory;
|
||||
|
||||
use Drupal\Component\Plugin\Factory\ReflectionFactory;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @group Plugin
|
||||
* @coversDefaultClass \Drupal\Component\Plugin\Factory\ReflectionFactory
|
||||
*/
|
||||
class ReflectionFactoryTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Data provider for testGetInstanceArguments.
|
||||
*
|
||||
* The classes used here are defined at the bottom of this file.
|
||||
*
|
||||
* @return array
|
||||
* - Expected output.
|
||||
* - Class to reflect for input to getInstanceArguments().
|
||||
* - $plugin_id parameter to getInstanceArguments().
|
||||
* - $plugin_definition parameter to getInstanceArguments().
|
||||
* - $configuration parameter to getInstanceArguments().
|
||||
*/
|
||||
public function providerGetInstanceArguments() {
|
||||
return [
|
||||
[
|
||||
['arguments_plugin_id'],
|
||||
'Drupal\Tests\Component\Plugin\Factory\ArgumentsPluginId',
|
||||
'arguments_plugin_id',
|
||||
['arguments_plugin_id' => ['class' => 'Drupal\Tests\Component\Plugin\Factory\ArgumentsPluginId']],
|
||||
[],
|
||||
],
|
||||
[
|
||||
[[], ['arguments_many' => ['class' => 'Drupal\Tests\Component\Plugin\Factory\ArgumentsMany']], 'arguments_many', 'default_value', 'what_default'],
|
||||
'Drupal\Tests\Component\Plugin\Factory\ArgumentsMany',
|
||||
'arguments_many',
|
||||
['arguments_many' => ['class' => 'Drupal\Tests\Component\Plugin\Factory\ArgumentsMany']],
|
||||
[],
|
||||
],
|
||||
[
|
||||
// Config array key exists and is set.
|
||||
['thing'],
|
||||
'Drupal\Tests\Component\Plugin\Factory\ArgumentsConfigArrayKey',
|
||||
'arguments_config_array_key',
|
||||
['arguments_config_array_key' => ['class' => 'Drupal\Tests\Component\Plugin\Factory\ArgumentsConfigArrayKey']],
|
||||
['config_name' => 'thing'],
|
||||
],
|
||||
[
|
||||
// Config array key exists and is not set.
|
||||
[NULL],
|
||||
'Drupal\Tests\Component\Plugin\Factory\ArgumentsConfigArrayKey',
|
||||
'arguments_config_array_key',
|
||||
['arguments_config_array_key' => ['class' => 'Drupal\Tests\Component\Plugin\Factory\ArgumentsConfigArrayKey']],
|
||||
['config_name' => NULL],
|
||||
],
|
||||
[
|
||||
// Touch the else clause at the end of the method.
|
||||
[NULL, NULL, NULL, NULL],
|
||||
'Drupal\Tests\Component\Plugin\Factory\ArgumentsAllNull',
|
||||
'arguments_all_null',
|
||||
['arguments_all_null' => ['class' => 'Drupal\Tests\Component\Plugin\Factory\ArgumentsAllNull']],
|
||||
[],
|
||||
],
|
||||
[
|
||||
// A plugin with no constructor.
|
||||
[NULL, NULL, NULL, NULL],
|
||||
'Drupal\Tests\Component\Plugin\Factory\ArgumentsNoConstructor',
|
||||
'arguments_no_constructor',
|
||||
['arguments_no_constructor' => ['class' => 'Drupal\Tests\Component\Plugin\Factory\ArgumentsNoConstructor']],
|
||||
[],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::createInstance
|
||||
* @dataProvider providerGetInstanceArguments
|
||||
*/
|
||||
public function testCreateInstance($expected, $reflector_name, $plugin_id, $plugin_definition, $configuration) {
|
||||
// Create a mock DiscoveryInterface which can return our plugin definition.
|
||||
$mock_discovery = $this->getMockBuilder('Drupal\Component\Plugin\Discovery\DiscoveryInterface')
|
||||
->setMethods(array('getDefinition', 'getDefinitions', 'hasDefinition'))
|
||||
->getMock();
|
||||
$mock_discovery->expects($this->never())->method('getDefinitions');
|
||||
$mock_discovery->expects($this->never())->method('hasDefinition');
|
||||
$mock_discovery->expects($this->once())
|
||||
->method('getDefinition')
|
||||
->willReturn($plugin_definition);
|
||||
|
||||
// Create a stub ReflectionFactory object. We use StubReflectionFactory
|
||||
// because createInstance() has a dependency on a static method.
|
||||
// StubReflectionFactory overrides this static method.
|
||||
$reflection_factory = new StubReflectionFactory($mock_discovery);
|
||||
|
||||
// Finally test that createInstance() returns an object of the class we
|
||||
// want.
|
||||
$this->assertInstanceOf($reflector_name, $reflection_factory->createInstance($plugin_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getInstanceArguments
|
||||
* @dataProvider providerGetInstanceArguments
|
||||
*/
|
||||
public function testGetInstanceArguments($expected, $reflector_name, $plugin_id, $plugin_definition, $configuration) {
|
||||
$reflection_factory = $this->getMockBuilder('Drupal\Component\Plugin\Factory\ReflectionFactory')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$get_instance_arguments_ref = new \ReflectionMethod($reflection_factory, 'getInstanceArguments');
|
||||
$get_instance_arguments_ref->setAccessible(TRUE);
|
||||
|
||||
// Special case for plugin class without a constructor.
|
||||
// getInstanceArguments() throws an exception if there's no constructor.
|
||||
// This is not a documented behavior of getInstanceArguments(), but allows
|
||||
// us to use one data set for this test method as well as
|
||||
// testCreateInstance().
|
||||
if ($plugin_id == 'arguments_no_constructor') {
|
||||
$this->setExpectedException('\ReflectionException');
|
||||
}
|
||||
|
||||
// Finally invoke getInstanceArguments() on our mocked factory.
|
||||
$ref = new \ReflectionClass($reflector_name);
|
||||
$result = $get_instance_arguments_ref->invoke(
|
||||
$reflection_factory, $ref, $plugin_id, $plugin_definition, $configuration);
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Override ReflectionFactory because ::createInstance() calls a static method.
|
||||
*
|
||||
* We have to override getPluginClass so that we can stub out its return value.
|
||||
*/
|
||||
class StubReflectionFactory extends ReflectionFactory {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getPluginClass($plugin_id, $plugin_definition = NULL, $required_interface = NULL) {
|
||||
// Return the class name from the plugin definition.
|
||||
return $plugin_definition[$plugin_id]['class'];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A stub class used by testGetInstanceArguments().
|
||||
*
|
||||
* @see providerGetInstanceArguments()
|
||||
*/
|
||||
class ArgumentsPluginId {
|
||||
|
||||
public function __construct($plugin_id) {
|
||||
// No-op.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A stub class used by testGetInstanceArguments().
|
||||
*
|
||||
* @see providerGetInstanceArguments()
|
||||
*/
|
||||
class ArgumentsMany {
|
||||
|
||||
public function __construct(
|
||||
$configuration, $plugin_definition, $plugin_id, $foo = 'default_value', $what_am_i_doing_here = 'what_default'
|
||||
) {
|
||||
// No-op.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A stub class used by testGetInstanceArguments().
|
||||
*
|
||||
* @see providerGetInstanceArguments()
|
||||
*/
|
||||
class ArgumentsConfigArrayKey {
|
||||
|
||||
public function __construct($config_name) {
|
||||
// No-op.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A stub class used by testGetInstanceArguments().
|
||||
*
|
||||
* @see providerGetInstanceArguments()
|
||||
*/
|
||||
class ArgumentsAllNull {
|
||||
|
||||
public function __construct($charismatic, $demure, $delightful, $electrostatic) {
|
||||
// No-op.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A stub class used by testGetInstanceArguments().
|
||||
*
|
||||
* @see providerGetInstanceArguments()
|
||||
*/
|
||||
class ArgumentsNoConstructor {
|
||||
|
||||
}
|
||||
107
web/core/tests/Drupal/Tests/Component/Plugin/PluginBaseTest.php
Normal file
107
web/core/tests/Drupal/Tests/Component/Plugin/PluginBaseTest.php
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Plugin;
|
||||
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Component\Plugin\PluginBase
|
||||
* @group Plugin
|
||||
*/
|
||||
class PluginBaseTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @dataProvider providerTestGetPluginId
|
||||
* @covers ::getPluginId
|
||||
*/
|
||||
public function testGetPluginId($plugin_id, $expected) {
|
||||
$plugin_base = $this->getMockForAbstractClass('Drupal\Component\Plugin\PluginBase', array(
|
||||
array(),
|
||||
$plugin_id,
|
||||
array(),
|
||||
));
|
||||
|
||||
$this->assertEquals($expected, $plugin_base->getPluginId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns test data for testGetPluginId().
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerTestGetPluginId() {
|
||||
return array(
|
||||
array('base_id', 'base_id'),
|
||||
array('base_id:derivative', 'base_id:derivative'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerTestGetBaseId
|
||||
* @coves ::getBaseId
|
||||
*/
|
||||
public function testGetBaseId($plugin_id, $expected) {
|
||||
/** @var \Drupal\Component\Plugin\PluginBase|\PHPUnit_Framework_MockObject_MockObject $plugin_base */
|
||||
$plugin_base = $this->getMockForAbstractClass('Drupal\Component\Plugin\PluginBase', array(
|
||||
array(),
|
||||
$plugin_id,
|
||||
array(),
|
||||
));
|
||||
|
||||
$this->assertEquals($expected, $plugin_base->getBaseId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns test data for testGetBaseId().
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerTestGetBaseId() {
|
||||
return array(
|
||||
array('base_id', 'base_id'),
|
||||
array('base_id:derivative', 'base_id'),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @dataProvider providerTestGetDerivativeId
|
||||
* @covers ::getDerivativeId
|
||||
*/
|
||||
public function testGetDerivativeId($plugin_id = NULL, $expected = NULL) {
|
||||
/** @var \Drupal\Component\Plugin\PluginBase|\PHPUnit_Framework_MockObject_MockObject $plugin_base */
|
||||
$plugin_base = $this->getMockForAbstractClass('Drupal\Component\Plugin\PluginBase', array(
|
||||
array(),
|
||||
$plugin_id,
|
||||
array(),
|
||||
));
|
||||
|
||||
$this->assertEquals($expected, $plugin_base->getDerivativeId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns test data for testGetDerivativeId().
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerTestGetDerivativeId() {
|
||||
return array(
|
||||
array('base_id', NULL),
|
||||
array('base_id:derivative', 'derivative'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getPluginDefinition
|
||||
*/
|
||||
public function testGetPluginDefinition() {
|
||||
$plugin_base = $this->getMockForAbstractClass('Drupal\Component\Plugin\PluginBase', array(
|
||||
array(),
|
||||
'plugin_id',
|
||||
array('value', array('key' => 'value')),
|
||||
));
|
||||
|
||||
$this->assertEquals(array('value', array('key' => 'value')), $plugin_base->getPluginDefinition());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Plugin;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Component\Plugin\PluginManagerBase
|
||||
* @group Plugin
|
||||
*/
|
||||
class PluginManagerBaseTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* A callback method for mocking FactoryInterface objects.
|
||||
*/
|
||||
public function createInstanceCallback() {
|
||||
$args = func_get_args();
|
||||
$plugin_id = $args[0];
|
||||
$configuration = $args[1];
|
||||
if ('invalid' == $plugin_id) {
|
||||
throw new PluginNotFoundException($plugin_id);
|
||||
}
|
||||
return array(
|
||||
'plugin_id' => $plugin_id,
|
||||
'configuration' => $configuration,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a mocked FactoryInterface object with known properties.
|
||||
*/
|
||||
public function getMockFactoryInterface($expects_count) {
|
||||
$mock_factory = $this->getMockBuilder('Drupal\Component\Plugin\Factory\FactoryInterface')
|
||||
->setMethods(array('createInstance'))
|
||||
->getMockForAbstractClass();
|
||||
$mock_factory->expects($this->exactly($expects_count))
|
||||
->method('createInstance')
|
||||
->willReturnCallback(array($this, 'createInstanceCallback'));
|
||||
return $mock_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests createInstance() with no fallback methods.
|
||||
*
|
||||
* @covers ::createInstance
|
||||
*/
|
||||
public function testCreateInstance() {
|
||||
$manager = $this->getMockBuilder('Drupal\Component\Plugin\PluginManagerBase')
|
||||
->getMockForAbstractClass();
|
||||
// PluginManagerBase::createInstance() looks for a factory object and then
|
||||
// calls createInstance() on it. So we have to mock a factory object.
|
||||
$factory_ref = new \ReflectionProperty($manager, 'factory');
|
||||
$factory_ref->setAccessible(TRUE);
|
||||
$factory_ref->setValue($manager, $this->getMockFactoryInterface(1));
|
||||
|
||||
// Finally the test.
|
||||
$configuration_array = array('config' => 'something');
|
||||
$result = $manager->createInstance('valid', $configuration_array);
|
||||
$this->assertEquals('valid', $result['plugin_id']);
|
||||
$this->assertArrayEquals($configuration_array, $result['configuration']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests createInstance() with a fallback method.
|
||||
*
|
||||
* @covers ::createInstance
|
||||
*/
|
||||
public function testCreateInstanceFallback() {
|
||||
// We use our special stub class which extends PluginManagerBase and also
|
||||
// implements FallbackPluginManagerInterface.
|
||||
$manager = new StubFallbackPluginManager();
|
||||
// Put our stubbed factory on the base object.
|
||||
$factory_ref = new \ReflectionProperty($manager, 'factory');
|
||||
$factory_ref->setAccessible(TRUE);
|
||||
|
||||
// Set up the configuration array.
|
||||
$configuration_array = array('config' => 'something');
|
||||
|
||||
// Test with fallback interface and valid plugin_id.
|
||||
$factory_ref->setValue($manager, $this->getMockFactoryInterface(1));
|
||||
$no_fallback_result = $manager->createInstance('valid', $configuration_array);
|
||||
$this->assertEquals('valid', $no_fallback_result['plugin_id']);
|
||||
$this->assertArrayEquals($configuration_array, $no_fallback_result['configuration']);
|
||||
|
||||
// Test with fallback interface and invalid plugin_id.
|
||||
$factory_ref->setValue($manager, $this->getMockFactoryInterface(2));
|
||||
$fallback_result = $manager->createInstance('invalid', $configuration_array);
|
||||
$this->assertEquals('invalid_fallback', $fallback_result['plugin_id']);
|
||||
$this->assertArrayEquals($configuration_array, $fallback_result['configuration']);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Plugin;
|
||||
|
||||
use Drupal\Component\Plugin\FallbackPluginManagerInterface;
|
||||
use Drupal\Component\Plugin\PluginManagerBase;
|
||||
|
||||
/**
|
||||
* Stubs \Drupal\Component\Plugin\FallbackPluginManagerInterface.
|
||||
*
|
||||
* We have to stub \Drupal\Component\Plugin\FallbackPluginManagerInterface for
|
||||
* \Drupal\Tests\Component\Plugin\PluginManagerBaseTest so that we can
|
||||
* implement ::getFallbackPluginId().
|
||||
*
|
||||
* We do this so we can have it just return the plugin ID passed to it, with
|
||||
* '_fallback' appended.
|
||||
*/
|
||||
class StubFallbackPluginManager extends PluginManagerBase implements FallbackPluginManagerInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFallbackPluginId($plugin_id, array $configuration = array()) {
|
||||
// Minimally implement getFallbackPluginId so that we can test it.
|
||||
return $plugin_id . '_fallback';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,445 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\Component\ProxyBuilder\ProxyBuilderTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\Component\ProxyBuilder;
|
||||
|
||||
use Drupal\Component\ProxyBuilder\ProxyBuilder;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Component\ProxyBuilder\ProxyBuilder
|
||||
* @group proxy_builder
|
||||
*/
|
||||
class ProxyBuilderTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The tested proxy builder.
|
||||
*
|
||||
* @var \Drupal\Component\ProxyBuilder\ProxyBuilder
|
||||
*/
|
||||
protected $proxyBuilder;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->proxyBuilder = new ProxyBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::buildProxyClassName
|
||||
*/
|
||||
public function testBuildProxyClassName() {
|
||||
$class_name = $this->proxyBuilder->buildProxyClassName('Drupal\Tests\Component\ProxyBuilder\TestServiceNoMethod');
|
||||
$this->assertEquals('Drupal\Tests\ProxyClass\Component\ProxyBuilder\TestServiceNoMethod', $class_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::buildProxyClassName
|
||||
*/
|
||||
public function testBuildProxyClassNameForModule() {
|
||||
$class_name = $this->proxyBuilder->buildProxyClassName('Drupal\views_ui\ParamConverter\ViewUIConverter');
|
||||
$this->assertEquals('Drupal\views_ui\ProxyClass\ParamConverter\ViewUIConverter', $class_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::buildProxyNamespace
|
||||
*/
|
||||
public function testBuildProxyNamespace() {
|
||||
$class_name = $this->proxyBuilder->buildProxyNamespace('Drupal\Tests\Component\ProxyBuilder\TestServiceNoMethod');
|
||||
$this->assertEquals('Drupal\Tests\ProxyClass\Component\ProxyBuilder', $class_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the basic methods like the constructor and the lazyLoadItself method.
|
||||
*
|
||||
* @covers ::build
|
||||
* @covers ::buildConstructorMethod
|
||||
* @covers ::buildLazyLoadItselfMethod
|
||||
*/
|
||||
public function testBuildNoMethod() {
|
||||
$class = 'Drupal\Tests\Component\ProxyBuilder\TestServiceNoMethod';
|
||||
|
||||
$result = $this->proxyBuilder->build($class);
|
||||
$this->assertEquals($this->buildExpectedClass($class, ''), $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::buildMethod
|
||||
* @covers ::buildMethodBody
|
||||
*/
|
||||
public function testBuildSimpleMethod() {
|
||||
$class = 'Drupal\Tests\Component\ProxyBuilder\TestServiceSimpleMethod';
|
||||
|
||||
$result = $this->proxyBuilder->build($class);
|
||||
|
||||
$method_body = <<<'EOS'
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function method()
|
||||
{
|
||||
return $this->lazyLoadItself()->method();
|
||||
}
|
||||
|
||||
EOS;
|
||||
$this->assertEquals($this->buildExpectedClass($class, $method_body), $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::buildMethod
|
||||
* @covers ::buildParameter
|
||||
* @covers ::buildMethodBody
|
||||
*/
|
||||
public function testBuildMethodWithParameter() {
|
||||
$class = 'Drupal\Tests\Component\ProxyBuilder\TestServiceMethodWithParameter';
|
||||
|
||||
$result = $this->proxyBuilder->build($class);
|
||||
|
||||
$method_body = <<<'EOS'
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function methodWithParameter($parameter)
|
||||
{
|
||||
return $this->lazyLoadItself()->methodWithParameter($parameter);
|
||||
}
|
||||
|
||||
EOS;
|
||||
$this->assertEquals($this->buildExpectedClass($class, $method_body), $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::buildMethod
|
||||
* @covers ::buildParameter
|
||||
* @covers ::buildMethodBody
|
||||
*/
|
||||
public function testBuildComplexMethod() {
|
||||
$class = 'Drupal\Tests\Component\ProxyBuilder\TestServiceComplexMethod';
|
||||
|
||||
$result = $this->proxyBuilder->build($class);
|
||||
|
||||
// @todo Solve the silly linebreak for array()
|
||||
$method_body = <<<'EOS'
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function complexMethod($parameter, callable $function, \Drupal\Tests\Component\ProxyBuilder\TestServiceNoMethod $test_service = NULL, array &$elements = array (
|
||||
))
|
||||
{
|
||||
return $this->lazyLoadItself()->complexMethod($parameter, $function, $test_service, $elements);
|
||||
}
|
||||
|
||||
EOS;
|
||||
|
||||
$this->assertEquals($this->buildExpectedClass($class, $method_body), $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::buildMethod
|
||||
* @covers ::buildMethodBody
|
||||
*/
|
||||
public function testBuildReturnReference() {
|
||||
$class = 'Drupal\Tests\Component\ProxyBuilder\TestServiceReturnReference';
|
||||
|
||||
$result = $this->proxyBuilder->build($class);
|
||||
|
||||
// @todo Solve the silly linebreak for array()
|
||||
$method_body = <<<'EOS'
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function &returnReference()
|
||||
{
|
||||
return $this->lazyLoadItself()->returnReference();
|
||||
}
|
||||
|
||||
EOS;
|
||||
|
||||
$this->assertEquals($this->buildExpectedClass($class, $method_body), $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::buildMethod
|
||||
* @covers ::buildParameter
|
||||
* @covers ::buildMethodBody
|
||||
*/
|
||||
public function testBuildWithInterface() {
|
||||
$class = 'Drupal\Tests\Component\ProxyBuilder\TestServiceWithInterface';
|
||||
|
||||
$result = $this->proxyBuilder->build($class);
|
||||
|
||||
$method_body = <<<'EOS'
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testMethod($parameter)
|
||||
{
|
||||
return $this->lazyLoadItself()->testMethod($parameter);
|
||||
}
|
||||
|
||||
EOS;
|
||||
|
||||
$interface_string = ' implements \Drupal\Tests\Component\ProxyBuilder\TestInterface';
|
||||
$this->assertEquals($this->buildExpectedClass($class, $method_body, $interface_string), $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::build
|
||||
*/
|
||||
public function testBuildWithNestedInterface() {
|
||||
$class = 'Drupal\Tests\Component\ProxyBuilder\TestServiceWithChildInterfaces';
|
||||
|
||||
$result = $this->proxyBuilder->build($class);
|
||||
$method_body = '';
|
||||
|
||||
$interface_string = ' implements \Drupal\Tests\Component\ProxyBuilder\TestChildInterface';
|
||||
$this->assertEquals($this->buildExpectedClass($class, $method_body, $interface_string), $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::buildMethod
|
||||
* @covers ::buildParameter
|
||||
* @covers ::buildMethodBody
|
||||
*/
|
||||
public function testBuildWithProtectedAndPrivateMethod() {
|
||||
$class = 'Drupal\Tests\Component\ProxyBuilder\TestServiceWithProtectedMethods';
|
||||
|
||||
$result = $this->proxyBuilder->build($class);
|
||||
|
||||
$method_body = <<<'EOS'
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testMethod($parameter)
|
||||
{
|
||||
return $this->lazyLoadItself()->testMethod($parameter);
|
||||
}
|
||||
|
||||
EOS;
|
||||
|
||||
$this->assertEquals($this->buildExpectedClass($class, $method_body), $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::buildMethod
|
||||
* @covers ::buildParameter
|
||||
* @covers ::buildMethodBody
|
||||
*/
|
||||
public function testBuildWithPublicStaticMethod() {
|
||||
$class = 'Drupal\Tests\Component\ProxyBuilder\TestServiceWithPublicStaticMethod';
|
||||
|
||||
$result = $this->proxyBuilder->build($class);
|
||||
|
||||
// Ensure that the static method is not wrapped.
|
||||
$method_body = <<<'EOS'
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function testMethod($parameter)
|
||||
{
|
||||
\Drupal\Tests\Component\ProxyBuilder\TestServiceWithPublicStaticMethod::testMethod($parameter);
|
||||
}
|
||||
|
||||
EOS;
|
||||
|
||||
$this->assertEquals($this->buildExpectedClass($class, $method_body), $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the expected class output.
|
||||
*
|
||||
* @param string $expected_methods_body
|
||||
* The expected body of decorated methods.
|
||||
*
|
||||
* @return string
|
||||
* The code of the entire proxy.
|
||||
*/
|
||||
protected function buildExpectedClass($class, $expected_methods_body, $interface_string = '') {
|
||||
$namespace = ProxyBuilder::buildProxyNamespace($class);
|
||||
$reflection = new \ReflectionClass($class);
|
||||
$proxy_class = $reflection->getShortName();
|
||||
|
||||
$expected_string = <<<'EOS'
|
||||
|
||||
namespace {{ namespace }} {
|
||||
|
||||
/**
|
||||
* Provides a proxy class for \{{ class }}.
|
||||
*
|
||||
* @see \Drupal\Component\ProxyBuilder
|
||||
*/
|
||||
class {{ proxy_class }}{{ interface_string }}
|
||||
{
|
||||
|
||||
/**
|
||||
* The id of the original proxied service.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $drupalProxyOriginalServiceId;
|
||||
|
||||
/**
|
||||
* The real proxied service, after it was lazy loaded.
|
||||
*
|
||||
* @var \{{ class }}
|
||||
*/
|
||||
protected $service;
|
||||
|
||||
/**
|
||||
* The service container.
|
||||
*
|
||||
* @var \Symfony\Component\DependencyInjection\ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* Constructs a ProxyClass Drupal proxy object.
|
||||
*
|
||||
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
|
||||
* The container.
|
||||
* @param string $drupal_proxy_original_service_id
|
||||
* The service ID of the original service.
|
||||
*/
|
||||
public function __construct(\Symfony\Component\DependencyInjection\ContainerInterface $container, $drupal_proxy_original_service_id)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->drupalProxyOriginalServiceId = $drupal_proxy_original_service_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy loads the real service from the container.
|
||||
*
|
||||
* @return object
|
||||
* Returns the constructed real service.
|
||||
*/
|
||||
protected function lazyLoadItself()
|
||||
{
|
||||
if (!isset($this->service)) {
|
||||
$this->service = $this->container->get($this->drupalProxyOriginalServiceId);
|
||||
}
|
||||
|
||||
return $this->service;
|
||||
}
|
||||
{{ expected_methods_body }}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
EOS;
|
||||
|
||||
$expected_methods_body = implode("\n", array_map(function ($value) {
|
||||
if ($value === '') {
|
||||
return $value;
|
||||
}
|
||||
return " $value";
|
||||
}, explode("\n", $expected_methods_body)));
|
||||
|
||||
$expected_string = str_replace('{{ proxy_class }}', $proxy_class, $expected_string);
|
||||
$expected_string = str_replace('{{ namespace }}', $namespace, $expected_string);
|
||||
$expected_string = str_replace('{{ class }}', $class, $expected_string);
|
||||
$expected_string = str_replace('{{ expected_methods_body }}', $expected_methods_body, $expected_string);
|
||||
$expected_string = str_replace('{{ interface_string }}', $interface_string, $expected_string);
|
||||
|
||||
return $expected_string;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TestServiceNoMethod {
|
||||
|
||||
}
|
||||
|
||||
class TestServiceSimpleMethod {
|
||||
|
||||
public function method() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TestServiceMethodWithParameter {
|
||||
|
||||
public function methodWithParameter($parameter) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TestServiceComplexMethod {
|
||||
|
||||
public function complexMethod($parameter, callable $function, TestServiceNoMethod $test_service = NULL, array &$elements = array()) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TestServiceReturnReference {
|
||||
|
||||
public function &returnReference() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface TestInterface {
|
||||
|
||||
public function testMethod($parameter);
|
||||
|
||||
}
|
||||
|
||||
class TestServiceWithInterface implements TestInterface {
|
||||
|
||||
public function testMethod($parameter) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TestServiceWithProtectedMethods {
|
||||
|
||||
public function testMethod($parameter) {
|
||||
|
||||
}
|
||||
|
||||
protected function protectedMethod($parameter) {
|
||||
|
||||
}
|
||||
|
||||
protected function privateMethod($parameter) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TestServiceWithPublicStaticMethod {
|
||||
|
||||
public static function testMethod($parameter) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface TestBaseInterface {
|
||||
|
||||
}
|
||||
|
||||
interface TestChildInterface extends TestBaseInterface {
|
||||
|
||||
}
|
||||
|
||||
class TestServiceWithChildInterfaces implements TestChildInterface {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Render;
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* Tests the TranslatableMarkup class.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\Component\Render\FormattableMarkup
|
||||
* @group utility
|
||||
*/
|
||||
class FormattableMarkupTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The error message of the last error in the error handler.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $lastErrorMessage;
|
||||
|
||||
/**
|
||||
* The error number of the last error in the error handler.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $lastErrorNumber;
|
||||
|
||||
/**
|
||||
* @covers ::__toString
|
||||
* @covers ::jsonSerialize
|
||||
*/
|
||||
public function testToString() {
|
||||
$string = 'Can I please have a @replacement';
|
||||
$formattable_string = new FormattableMarkup($string, ['@replacement' => 'kitten']);
|
||||
$text = (string) $formattable_string;
|
||||
$this->assertEquals('Can I please have a kitten', $text);
|
||||
$text = $formattable_string->jsonSerialize();
|
||||
$this->assertEquals('Can I please have a kitten', $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::count
|
||||
*/
|
||||
public function testCount() {
|
||||
$string = 'Can I please have a @replacement';
|
||||
$formattable_string = new FormattableMarkup($string, ['@replacement' => 'kitten']);
|
||||
$this->assertEquals(strlen($string), $formattable_string->count());
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom error handler that saves the last error.
|
||||
*
|
||||
* We need this custom error handler because we cannot rely on the error to
|
||||
* exception conversion as __toString is never allowed to leak any kind of
|
||||
* exception.
|
||||
*
|
||||
* @param int $error_number
|
||||
* The error number.
|
||||
* @param string $error_message
|
||||
* The error message.
|
||||
*/
|
||||
public function errorHandler($error_number, $error_message) {
|
||||
$this->lastErrorNumber = $error_number;
|
||||
$this->lastErrorMessage = $error_message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::__toString
|
||||
* @dataProvider providerTestUnexpectedPlaceholder
|
||||
*/
|
||||
public function testUnexpectedPlaceholder($string, $arguments, $error_number, $error_message) {
|
||||
// We set a custom error handler because of https://github.com/sebastianbergmann/phpunit/issues/487
|
||||
set_error_handler([$this, 'errorHandler']);
|
||||
// We want this to trigger an error.
|
||||
$markup = new FormattableMarkup($string, $arguments);
|
||||
// Cast it to a string which will generate the errors.
|
||||
$output = (string) $markup;
|
||||
restore_error_handler();
|
||||
// The string should not change.
|
||||
$this->assertEquals($string, $output);
|
||||
$this->assertEquals($error_number, $this->lastErrorNumber);
|
||||
$this->assertEquals($error_message, $this->lastErrorMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for FormattableMarkupTest::testUnexpectedPlaceholder().
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerTestUnexpectedPlaceholder() {
|
||||
return [
|
||||
['Non alpha starting character: ~placeholder', ['~placeholder' => 'replaced'], E_USER_ERROR, 'Invalid placeholder (~placeholder) in string: Non alpha starting character: ~placeholder'],
|
||||
['Alpha starting character: placeholder', ['placeholder' => 'replaced'], E_USER_DEPRECATED, 'Invalid placeholder (placeholder) in string: Alpha starting character: placeholder'],
|
||||
// Ensure that where the placeholder is located in the the string is
|
||||
// irrelevant.
|
||||
['placeholder', ['placeholder' => 'replaced'], E_USER_DEPRECATED, 'Invalid placeholder (placeholder) in string: placeholder'],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Render;
|
||||
|
||||
use Drupal\Component\Render\HtmlEscapedText;
|
||||
use Drupal\Component\Render\MarkupInterface;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* Tests the HtmlEscapedText class.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\Component\Render\HtmlEscapedText
|
||||
* @group utility
|
||||
*/
|
||||
class HtmlEscapedTextTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @covers ::__toString
|
||||
* @covers ::jsonSerialize
|
||||
*
|
||||
* @dataProvider providerToString
|
||||
*/
|
||||
public function testToString($text, $expected, $message) {
|
||||
$escapeable_string = new HtmlEscapedText($text);
|
||||
$this->assertEquals($expected, (string) $escapeable_string, $message);
|
||||
$this->assertEquals($expected, $escapeable_string->jsonSerialize());
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testToString().
|
||||
*
|
||||
* @see testToString()
|
||||
*/
|
||||
function providerToString() {
|
||||
// Checks that invalid multi-byte sequences are escaped.
|
||||
$tests[] = array("Foo\xC0barbaz", 'Foo<6F>barbaz', 'Escapes invalid sequence "Foo\xC0barbaz"');
|
||||
$tests[] = array("\xc2\"", '<27>"', 'Escapes invalid sequence "\xc2\""');
|
||||
$tests[] = array("Fooÿñ", "Fooÿñ", 'Does not escape valid sequence "Fooÿñ"');
|
||||
|
||||
// Checks that special characters are escaped.
|
||||
$script_tag = $this->prophesize(MarkupInterface::class);
|
||||
$script_tag->__toString()->willReturn('<script>');
|
||||
$script_tag = $script_tag->reveal();
|
||||
$tests[] = array($script_tag, '<script>', 'Escapes <script> even inside an object that implements MarkupInterface.');
|
||||
$tests[] = array("<script>", '<script>', 'Escapes <script>');
|
||||
$tests[] = array('<>&"\'', '<>&"'', 'Escapes reserved HTML characters.');
|
||||
$specialchars = $this->prophesize(MarkupInterface::class);
|
||||
$specialchars->__toString()->willReturn('<>&"\'');
|
||||
$specialchars = $specialchars->reveal();
|
||||
$tests[] = array($specialchars, '<>&"'', 'Escapes reserved HTML characters even inside an object that implements MarkupInterface.');
|
||||
|
||||
return $tests;
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::count
|
||||
*/
|
||||
public function testCount() {
|
||||
$string = 'Can I please have a <em>kitten</em>';
|
||||
$escapeable_string = new HtmlEscapedText($string);
|
||||
$this->assertEquals(strlen($string), $escapeable_string->count());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Render;
|
||||
|
||||
use Drupal\Component\Render\PlainTextOutput;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Render\MarkupInterface;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Component\Render\PlainTextOutput
|
||||
* @group Utility
|
||||
*/
|
||||
class PlainTextOutputTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Tests ::renderFromHtml().
|
||||
*
|
||||
* @param $expected
|
||||
* The expected formatted value.
|
||||
* @param $string
|
||||
* A string to be formatted.
|
||||
* @param array $args
|
||||
* (optional) An associative array of replacements to make. Defaults to
|
||||
* none.
|
||||
*
|
||||
* @covers ::renderFromHtml
|
||||
* @dataProvider providerRenderFromHtml
|
||||
*/
|
||||
public function testRenderFromHtml($expected, $string, $args = []) {
|
||||
$markup = SafeMarkup::format($string, $args);
|
||||
$output = PlainTextOutput::renderFromHtml($markup);
|
||||
$this->assertSame($expected, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for ::testRenderFromHtml()
|
||||
*/
|
||||
public function providerRenderFromHtml() {
|
||||
$data = [];
|
||||
|
||||
$data['simple-text'] = ['Giraffes and wombats', 'Giraffes and wombats'];
|
||||
$data['simple-html'] = ['Giraffes and wombats', '<a href="/muh">Giraffes</a> and <strong>wombats</strong>'];
|
||||
$data['html-with-quote'] = ['Giraffes and quote"s', '<a href="/muh">Giraffes</a> and <strong>quote"s</strong>'];
|
||||
|
||||
$expected = 'The <em> tag makes your text look like "this".';
|
||||
$string = 'The <em> tag makes your text look like <em>"this"</em>.';
|
||||
$data['escaped-html-with-quotes'] = [$expected, $string];
|
||||
|
||||
$safe_string = $this->prophesize(MarkupInterface::class);
|
||||
$safe_string->__toString()->willReturn('<em>"this"</em>');
|
||||
$safe_string = $safe_string->reveal();
|
||||
$data['escaped-html-with-quotes-and-placeholders'] = [$expected, 'The @tag tag makes your text look like @result.', ['@tag' => '<em>', '@result' => $safe_string]];
|
||||
|
||||
$safe_string = $this->prophesize(MarkupInterface::class);
|
||||
$safe_string->__toString()->willReturn($string);
|
||||
$safe_string = $safe_string->reveal();
|
||||
$data['safe-string'] = [$expected, $safe_string];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
||||
117
web/core/tests/Drupal/Tests/Component/Serialization/JsonTest.php
Normal file
117
web/core/tests/Drupal/Tests/Component/Serialization/JsonTest.php
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Serialization;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Component\Serialization\Json
|
||||
* @group Serialization
|
||||
*/
|
||||
class JsonTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* A test string with the full ASCII table.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $string;
|
||||
|
||||
/**
|
||||
* An array of unsafe html characters which has to be encoded.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $htmlUnsafe;
|
||||
|
||||
/**
|
||||
* An array of unsafe html characters which are already escaped.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $htmlUnsafeEscaped;
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Setup a string with the full ASCII table.
|
||||
// @todo: Add tests for non-ASCII characters and Unicode.
|
||||
$this->string = '';
|
||||
for ($i = 1; $i < 128; $i++) {
|
||||
$this->string .= chr($i);
|
||||
}
|
||||
|
||||
// Characters that must be escaped.
|
||||
// We check for unescaped " separately.
|
||||
$this->htmlUnsafe = array('<', '>', '\'', '&');
|
||||
// The following are the encoded forms of: < > ' & "
|
||||
$this->htmlUnsafeEscaped = array('\u003C', '\u003E', '\u0027', '\u0026', '\u0022');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests encoding for every ASCII character.
|
||||
*/
|
||||
public function testEncodingAscii() {
|
||||
// Verify there aren't character encoding problems with the source string.
|
||||
$this->assertSame(strlen($this->string), 127, 'A string with the full ASCII table has the correct length.');
|
||||
foreach ($this->htmlUnsafe as $char) {
|
||||
$this->assertTrue(strpos($this->string, $char) > 0, sprintf('A string with the full ASCII table includes %s.', $char));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests encoding length.
|
||||
*/
|
||||
public function testEncodingLength() {
|
||||
// Verify that JSON encoding produces a string with all of the characters.
|
||||
$json = Json::encode($this->string);
|
||||
$this->assertTrue(strlen($json) > strlen($this->string), 'A JSON encoded string is larger than the source string.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests end and start of the encoded string.
|
||||
*/
|
||||
public function testEncodingStartEnd() {
|
||||
$json = Json::encode($this->string);
|
||||
// The first and last characters should be ", and no others.
|
||||
$this->assertTrue($json[0] == '"', 'A JSON encoded string begins with ".');
|
||||
$this->assertTrue($json[strlen($json) - 1] == '"', 'A JSON encoded string ends with ".');
|
||||
$this->assertTrue(substr_count($json, '"') == 2, 'A JSON encoded string contains exactly two ".');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests converting PHP variables to JSON strings and back.
|
||||
*/
|
||||
public function testReversibility() {
|
||||
$json = Json::encode($this->string);
|
||||
// Verify that encoding/decoding is reversible.
|
||||
$json_decoded = Json::decode($json);
|
||||
$this->assertSame($this->string, $json_decoded, 'Encoding a string to JSON and decoding back results in the original string.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the reversibility of structured data
|
||||
*/
|
||||
public function testStructuredReversibility() {
|
||||
// Verify reversibility for structured data. Also verify that necessary
|
||||
// characters are escaped.
|
||||
$source = array(TRUE, FALSE, 0, 1, '0', '1', $this->string, array('key1' => $this->string, 'key2' => array('nested' => TRUE)));
|
||||
$json = Json::encode($source);
|
||||
foreach ($this->htmlUnsafe as $char) {
|
||||
$this->assertTrue(strpos($json, $char) === FALSE, sprintf('A JSON encoded string does not contain %s.', $char));
|
||||
}
|
||||
// Verify that JSON encoding escapes the HTML unsafe characters
|
||||
foreach ($this->htmlUnsafeEscaped as $char) {
|
||||
$this->assertTrue(strpos($json, $char) > 0, sprintf('A JSON encoded string contains %s.', $char));
|
||||
}
|
||||
$json_decoded = Json::decode($json);
|
||||
$this->assertNotSame($source, $json, 'An array encoded in JSON is identical to the source.');
|
||||
$this->assertSame($source, $json_decoded, 'Encoding structured data to JSON and decoding back not results in the original data.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Serialization;
|
||||
|
||||
use Drupal\Component\Serialization\YamlPecl;
|
||||
|
||||
/**
|
||||
* Tests the YamlPecl serialization implementation.
|
||||
*
|
||||
* @group Drupal
|
||||
* @group Serialization
|
||||
* @coversDefaultClass \Drupal\Component\Serialization\YamlPecl
|
||||
* @requires extension yaml
|
||||
*/
|
||||
class YamlPeclTest extends YamlTestBase {
|
||||
|
||||
/**
|
||||
* Tests encoding and decoding basic data structures.
|
||||
*
|
||||
* @covers ::encode
|
||||
* @covers ::decode
|
||||
* @dataProvider providerEncodeDecodeTests
|
||||
*/
|
||||
public function testEncodeDecode($data) {
|
||||
$this->assertEquals($data, YamlPecl::decode(YamlPecl::encode($data)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests decoding YAML node anchors.
|
||||
*
|
||||
* @covers ::decode
|
||||
* @dataProvider providerDecodeTests
|
||||
*/
|
||||
public function testDecode($string, $data) {
|
||||
$this->assertEquals($data, YamlPecl::decode($string));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests our encode settings.
|
||||
*
|
||||
* @covers ::encode
|
||||
*/
|
||||
public function testEncode() {
|
||||
$this->assertEquals('---
|
||||
foo:
|
||||
bar: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sapien ex, venenatis vitae nisi eu, posuere luctus dolor. Nullam convallis
|
||||
...
|
||||
', YamlPecl::encode(['foo' => ['bar' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sapien ex, venenatis vitae nisi eu, posuere luctus dolor. Nullam convallis']]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests YAML boolean callback.
|
||||
*
|
||||
* @param string $string
|
||||
* String value for the YAML boolean.
|
||||
* @param string|bool $expected
|
||||
* The expected return value.
|
||||
*
|
||||
* @covers ::applyBooleanCallbacks
|
||||
* @dataProvider providerBoolTest
|
||||
*/
|
||||
public function testApplyBooleanCallbacks($string, $expected) {
|
||||
$this->assertEquals($expected, YamlPecl::applyBooleanCallbacks($string, 'bool', NULL));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getFileExtension
|
||||
*/
|
||||
public function testGetFileExtension() {
|
||||
$this->assertEquals('yml', YamlPecl::getFileExtension());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that invalid YAML throws an exception.
|
||||
*
|
||||
* @covers ::errorHandler
|
||||
* @expectedException \Drupal\Component\Serialization\Exception\InvalidDataTypeException
|
||||
*/
|
||||
public function testError() {
|
||||
YamlPecl::decode('foo: [ads');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Serialization;
|
||||
|
||||
use Drupal\Component\Serialization\YamlSymfony;
|
||||
|
||||
/**
|
||||
* Tests the YamlSymfony serialization implementation.
|
||||
*
|
||||
* @group Drupal
|
||||
* @group Serialization
|
||||
* @coversDefaultClass \Drupal\Component\Serialization\YamlSymfony
|
||||
*/
|
||||
class YamlSymfonyTest extends YamlTestBase {
|
||||
|
||||
/**
|
||||
* Tests encoding and decoding basic data structures.
|
||||
*
|
||||
* @covers ::encode
|
||||
* @covers ::decode
|
||||
* @dataProvider providerEncodeDecodeTests
|
||||
*/
|
||||
public function testEncodeDecode($data) {
|
||||
$this->assertEquals($data, YamlSymfony::decode(YamlSymfony::encode($data)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests decoding YAML node anchors.
|
||||
*
|
||||
* @covers ::decode
|
||||
* @dataProvider providerDecodeTests
|
||||
*/
|
||||
public function testDecode($string, $data) {
|
||||
$this->assertEquals($data, YamlSymfony::decode($string));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests our encode settings.
|
||||
*
|
||||
* @covers ::encode
|
||||
*/
|
||||
public function testEncode() {
|
||||
$this->assertEquals('foo:
|
||||
bar: \'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sapien ex, venenatis vitae nisi eu, posuere luctus dolor. Nullam convallis\'
|
||||
', YamlSymfony::encode(['foo' => ['bar' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sapien ex, venenatis vitae nisi eu, posuere luctus dolor. Nullam convallis']]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getFileExtension
|
||||
*/
|
||||
public function testGetFileExtension() {
|
||||
$this->assertEquals('yml', YamlSymfony::getFileExtension());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that invalid YAML throws an exception.
|
||||
*
|
||||
* @covers ::decode
|
||||
* @expectedException \Drupal\Component\Serialization\Exception\InvalidDataTypeException
|
||||
*/
|
||||
public function testError() {
|
||||
YamlSymfony::decode('foo: [ads');
|
||||
}
|
||||
|
||||
}
|
||||
133
web/core/tests/Drupal/Tests/Component/Serialization/YamlTest.php
Normal file
133
web/core/tests/Drupal/Tests/Component/Serialization/YamlTest.php
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Serialization;
|
||||
|
||||
use Drupal\Component\Serialization\Exception\InvalidDataTypeException;
|
||||
use Drupal\Component\Serialization\SerializationInterface;
|
||||
use Drupal\Component\Serialization\Yaml;
|
||||
use Drupal\Component\Serialization\YamlPecl;
|
||||
use Drupal\Component\Serialization\YamlSymfony;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Component\Serialization\Yaml
|
||||
* @group Serialization
|
||||
*/
|
||||
class YamlTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @var \PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $mockParser;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->mockParser = $this->getMockBuilder('\stdClass')
|
||||
->setMethods(['encode', 'decode', 'getFileExtension'])
|
||||
->getMock();
|
||||
YamlParserProxy::setMock($this->mockParser);
|
||||
}
|
||||
|
||||
public function tearDown() {
|
||||
YamlParserProxy::setMock(NULL);
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::decode
|
||||
*/
|
||||
public function testDecode() {
|
||||
$this->mockParser
|
||||
->expects($this->once())
|
||||
->method('decode');
|
||||
YamlStub::decode('test');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getFileExtension
|
||||
*/
|
||||
public function testGetFileExtension() {
|
||||
$this->mockParser
|
||||
->expects($this->never())
|
||||
->method('getFileExtension');
|
||||
$this->assertEquals('yml', YamlStub::getFileExtension());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests all YAML files are decoded in the same way with Symfony and PECL.
|
||||
*
|
||||
* This test is a little bit slow but it tests that we do not have any bugs in
|
||||
* our YAML that might not be decoded correctly in any of our implementations.
|
||||
*
|
||||
* @todo This should exist as an integration test not part of our unit tests.
|
||||
* https://www.drupal.org/node/2597730
|
||||
*
|
||||
* @requires extension yaml
|
||||
* @dataProvider providerYamlFilesInCore
|
||||
*/
|
||||
public function testYamlFiles($file) {
|
||||
$data = file_get_contents($file);
|
||||
try {
|
||||
$this->assertEquals(YamlSymfony::decode($data), YamlPecl::decode($data), $file);
|
||||
}
|
||||
catch (InvalidDataTypeException $e) {
|
||||
// Provide file context to the failure so the exception message is useful.
|
||||
$this->fail("Exception thrown parsing $file:\n" . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider that lists all YAML files in core.
|
||||
*/
|
||||
public function providerYamlFilesInCore() {
|
||||
$files = [];
|
||||
$dirs = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(__DIR__ . '/../../../../../', \RecursiveDirectoryIterator::FOLLOW_SYMLINKS));
|
||||
foreach ($dirs as $dir) {
|
||||
$pathname = $dir->getPathname();
|
||||
// Exclude vendor.
|
||||
if ($dir->getExtension() == 'yml' && strpos($pathname, '/../../../../../vendor') === FALSE) {
|
||||
if (strpos($dir->getRealPath(), 'invalid_file') !== FALSE) {
|
||||
// There are some intentionally invalid files provided for testing
|
||||
// library API behaviours, ignore them.
|
||||
continue;
|
||||
}
|
||||
$files[] = [$dir->getRealPath()];
|
||||
}
|
||||
}
|
||||
return $files;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class YamlStub extends Yaml {
|
||||
|
||||
public static function getSerializer() {
|
||||
return '\Drupal\Tests\Component\Serialization\YamlParserProxy';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class YamlParserProxy implements SerializationInterface {
|
||||
|
||||
/**
|
||||
* @var \Drupal\Component\Serialization\SerializationInterface
|
||||
*/
|
||||
protected static $mock;
|
||||
|
||||
public static function setMock($mock) {
|
||||
static::$mock = $mock;
|
||||
}
|
||||
|
||||
public static function encode($data) {
|
||||
return static::$mock->encode($data);
|
||||
}
|
||||
|
||||
public static function decode($raw) {
|
||||
return static::$mock->decode($raw);
|
||||
}
|
||||
|
||||
public static function getFileExtension() {
|
||||
return static::$mock->getFileExtension();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Serialization;
|
||||
|
||||
/**
|
||||
* Provides standard data to validate different YAML implementations.
|
||||
*/
|
||||
abstract class YamlTestBase extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
/**
|
||||
* Some data that should be able to be serialized.
|
||||
*/
|
||||
public function providerEncodeDecodeTests() {
|
||||
return [
|
||||
[
|
||||
'foo' => 'bar',
|
||||
'id' => 'schnitzel',
|
||||
'ponies' => ['nope', 'thanks'],
|
||||
'how' => [
|
||||
'about' => 'if',
|
||||
'i' => 'ask',
|
||||
'nicely',
|
||||
],
|
||||
'the' => [
|
||||
'answer' => [
|
||||
'still' => 'would',
|
||||
'be' => 'Y',
|
||||
],
|
||||
],
|
||||
'how_many_times' => 123,
|
||||
'should_i_ask' => FALSE,
|
||||
1,
|
||||
FALSE,
|
||||
[1, FALSE],
|
||||
[10],
|
||||
[0 => '123456'],
|
||||
],
|
||||
[NULL]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Some data that should be able to be de-serialized.
|
||||
*/
|
||||
public function providerDecodeTests() {
|
||||
$data = [
|
||||
// NULL files.
|
||||
['', NULL],
|
||||
["\n", NULL],
|
||||
["---\n...\n", NULL],
|
||||
|
||||
// Node anchors.
|
||||
[
|
||||
"
|
||||
jquery.ui:
|
||||
version: &jquery_ui 1.10.2
|
||||
|
||||
jquery.ui.accordion:
|
||||
version: *jquery_ui
|
||||
",
|
||||
[
|
||||
'jquery.ui' => [
|
||||
'version' => '1.10.2',
|
||||
],
|
||||
'jquery.ui.accordion' => [
|
||||
'version' => '1.10.2',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
// 1.2 Bool values.
|
||||
foreach ($this->providerBoolTest() as $test) {
|
||||
$data[] = ['bool: ' . $test[0], ['bool' => $test[1]]];
|
||||
}
|
||||
$data = array_merge($data, $this->providerBoolTest());
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests different boolean serialization and de-serialization.
|
||||
*/
|
||||
public function providerBoolTest() {
|
||||
return [
|
||||
['true', TRUE],
|
||||
['TRUE', TRUE],
|
||||
['True', TRUE],
|
||||
['y', 'y'],
|
||||
['Y', 'Y'],
|
||||
['false', FALSE],
|
||||
['FALSE', FALSE],
|
||||
['False', FALSE],
|
||||
['n', 'n'],
|
||||
['N', 'N'],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Transliteration;
|
||||
|
||||
use Drupal\Component\Transliteration\PhpTransliteration;
|
||||
use Drupal\Component\Utility\Random;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use org\bovigo\vfs\vfsStream;
|
||||
|
||||
/**
|
||||
* Tests Transliteration component functionality.
|
||||
*
|
||||
* @group Transliteration
|
||||
*
|
||||
* @coversDefaultClass \Drupal\Component\Transliteration\PhpTransliteration
|
||||
*/
|
||||
class PhpTransliterationTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Tests the PhpTransliteration::removeDiacritics() function.
|
||||
*
|
||||
* @param string $original
|
||||
* The language code to test.
|
||||
* @param string $expected
|
||||
* The expected return from PhpTransliteration::removeDiacritics().
|
||||
*
|
||||
* @dataProvider providerTestPhpTransliterationRemoveDiacritics
|
||||
*/
|
||||
public function testRemoveDiacritics($original, $expected) {
|
||||
$transliterator_class = new PhpTransliteration();
|
||||
$result = $transliterator_class->removeDiacritics($original);
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for self::testRemoveDiacritics().
|
||||
*
|
||||
* @return array
|
||||
* An array of arrays, each containing the parameters for
|
||||
* self::testRemoveDiacritics().
|
||||
*/
|
||||
public function providerTestPhpTransliterationRemoveDiacritics() {
|
||||
return array(
|
||||
// Test all characters in the Unicode range 0x00bf to 0x017f.
|
||||
array('ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ', 'AAAAAAÆCEEEEIIII'),
|
||||
array('ÐÑÒÓÔÕÖרÙÚÛÜÝÞß', 'ÐNOOOOO×OUUUUYÞß'),
|
||||
array('àáâãäåæçèéêëìíîï', 'aaaaaaæceeeeiiii'),
|
||||
array('ðñòóôõö÷øùúûüýþÿ', 'ðnooooo÷ouuuuyþy'),
|
||||
array('ĀāĂ㥹ĆćĈĉĊċČčĎď', 'AaAaAaCcCcCcCcDd'),
|
||||
array('ĐđĒēĔĕĖėĘęĚěĜĝĞğ', 'DdEeEeEeEeEeGgGg'),
|
||||
array('ĠġĢģĤĥĦħĨĩĪīĬĭĮį', 'GgGgHhHhIiIiIiIi'),
|
||||
array('İıIJijĴĵĶķĸĹĺĻļĽľĿ', 'IiIJijJjKkĸLlLlLlL'),
|
||||
array('ŀŁłŃńŅņŇňʼnŊŋŌōŎŏ', 'lLlNnNnNnʼnŊŋOoOo'),
|
||||
array('ŐőŒœŔŕŖŗŘřŚśŜŝŞş', 'OoŒœRrRrRrSsSsSs'),
|
||||
array('ŠšŢţŤťŦŧŨũŪūŬŭŮů', 'SsTtTtTtUuUuUuUu'),
|
||||
array('ŰűŲųŴŵŶŷŸŹźŻżŽž', 'UuUuWwYyYZzZzZz'),
|
||||
|
||||
// Test all characters in the Unicode range 0x01CD to 0x024F.
|
||||
array('ǍǎǏ', 'AaI'),
|
||||
array('ǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟ', 'iOoUuUuUuUuUuǝAa'),
|
||||
array('ǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯ', 'AaǢǣGgGgKkOoOoǮǯ'),
|
||||
array('ǰDZDzdzǴǵǶǷǸǹǺǻǼǽǾǿ', 'jDZDzdzGgǶǷNnAaǼǽOo'),
|
||||
array('ȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏ', 'AaAaEeEeIiIiOoOo'),
|
||||
array('ȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟ', 'RrRrUuUuSsTtȜȝHh'),
|
||||
array('ȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯ', 'ȠȡȢȣZzAaEeOoOoOo'),
|
||||
array('ȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿ', 'OoYylntjȸȹACcLTs'),
|
||||
array('ɀɁɂɃɄɅɆɇɈɉɊɋɌɍɎɏ', 'zɁɂBUɅEeJjQqRrYy'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the PhpTransliteration class.
|
||||
*
|
||||
* @param string $langcode
|
||||
* The language code to test.
|
||||
* @param string $original
|
||||
* The original string.
|
||||
* @param string $expected
|
||||
* The expected return from PhpTransliteration::transliterate().
|
||||
* @param string $unknown_character
|
||||
* (optional) The character to substitute for characters in $string without
|
||||
* transliterated equivalents. Defaults to '?'.
|
||||
* @param int $max_length
|
||||
* (optional) If provided, return at most this many characters, ensuring
|
||||
* that the transliteration does not split in the middle of an input
|
||||
* character's transliteration.
|
||||
*
|
||||
* @dataProvider providerTestPhpTransliteration
|
||||
*/
|
||||
public function testPhpTransliteration($langcode, $original, $expected, $unknown_character = '?', $max_length = NULL) {
|
||||
$transliterator_class = new PhpTransliteration();
|
||||
$actual = $transliterator_class->transliterate($original, $langcode, $unknown_character, $max_length);
|
||||
$this->assertSame($expected, $actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for self::testPhpTransliteration().
|
||||
*
|
||||
* @return array
|
||||
* An array of arrays, each containing the parameters for
|
||||
* self::testPhpTransliteration().
|
||||
*/
|
||||
public function providerTestPhpTransliteration() {
|
||||
$random_generator = new Random();
|
||||
$random = $random_generator->string(10);
|
||||
// Make some strings with two, three, and four-byte characters for testing.
|
||||
// Note that the 3-byte character is overridden by the 'kg' language.
|
||||
$two_byte = 'Ä Ö Ü Å Ø äöüåøhello';
|
||||
// This is a Cyrrillic character that looks something like a u. See
|
||||
// http://www.unicode.org/charts/PDF/U0400.pdf
|
||||
$three_byte = html_entity_decode('ц', ENT_NOQUOTES, 'UTF-8');
|
||||
// This is a Canadian Aboriginal character like a triangle. See
|
||||
// http://www.unicode.org/charts/PDF/U1400.pdf
|
||||
$four_byte = html_entity_decode('ᐑ', ENT_NOQUOTES, 'UTF-8');
|
||||
// These are two Gothic alphabet letters. See
|
||||
// http://wikipedia.org/wiki/Gothic_alphabet
|
||||
// They are not in our tables, but should at least give us '?' (unknown).
|
||||
$five_byte = html_entity_decode('𐌰𐌸', ENT_NOQUOTES, 'UTF-8');
|
||||
|
||||
return array(
|
||||
// Each test case is (language code, input, output).
|
||||
// Test ASCII in English.
|
||||
array('en', $random, $random),
|
||||
// Test ASCII in some other language with no overrides.
|
||||
array('fr', $random, $random),
|
||||
// Test 3 and 4-byte characters in a language without overrides.
|
||||
// Note: if the data tables change, these will need to change too! They
|
||||
// are set up to test that data table loading works, so values come
|
||||
// directly from the data files.
|
||||
array('fr', $three_byte, 'c'),
|
||||
array('fr', $four_byte, 'wii'),
|
||||
// Test 5-byte characters.
|
||||
array('en', $five_byte, '??'),
|
||||
// Test a language with no overrides.
|
||||
array('en', $two_byte, 'A O U A O aouaohello'),
|
||||
// Test language overrides provided by core.
|
||||
array('de', $two_byte, 'Ae Oe Ue A O aeoeueaohello'),
|
||||
array('de', $random, $random),
|
||||
array('dk', $two_byte, 'A O U Aa Oe aouaaoehello'),
|
||||
array('dk', $random, $random),
|
||||
array('kg', $three_byte, 'ts'),
|
||||
// Test strings in some other languages.
|
||||
// Turkish, provided by drupal.org user Kartagis.
|
||||
array('tr', 'Abayı serdiler bize. Söyleyeceğim yüzlerine. Sanırım hepimiz aynı şeyi düşünüyoruz.', 'Abayi serdiler bize. Soyleyecegim yuzlerine. Sanirim hepimiz ayni seyi dusunuyoruz.'),
|
||||
// Illegal/unknown unicode.
|
||||
array('en', chr(0xF8) . chr(0x80) . chr(0x80) . chr(0x80) . chr(0x80), '?'),
|
||||
// Max length.
|
||||
array('de', $two_byte, 'Ae Oe', '?', 5),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the transliteration with max length.
|
||||
*/
|
||||
public function testTransliterationWithMaxLength() {
|
||||
$transliteration = new PhpTransliteration();
|
||||
|
||||
// Test with max length, using German. It should never split up the
|
||||
// transliteration of a single character.
|
||||
$input = 'Ä Ö Ü Å Ø äöüåøhello';
|
||||
$trunc_output = 'Ae Oe Ue A O aeoe';
|
||||
|
||||
$this->assertSame($trunc_output, $transliteration->transliterate($input, 'de', '?', 17), 'Truncating to 17 characters works');
|
||||
$this->assertSame($trunc_output, $transliteration->transliterate($input, 'de', '?', 18), 'Truncating to 18 characters works');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests inclusion is safe.
|
||||
*
|
||||
* @covers ::readLanguageOverrides
|
||||
*/
|
||||
public function testSafeInclude() {
|
||||
// The overrides in the transliteration data directory transliterates 0x82
|
||||
// into "safe" but the overrides one directory higher transliterates the
|
||||
// same character into "security hole". So by using "../index" as the
|
||||
// language code we can test the ../ is stripped from the langcode.
|
||||
vfsStream::setup('transliteration', NULL, [
|
||||
'index.php' => '<?php $overrides = ["../index" => [0x82 => "security hole"]];',
|
||||
'dir' => [
|
||||
'index.php' => '<?php $overrides = ["../index" => [0x82 => "safe"]];',
|
||||
],
|
||||
]);
|
||||
$transliteration = new PhpTransliteration(vfsStream::url('transliteration/dir'));
|
||||
$transliterated = $transliteration->transliterate(chr(0xC2) . chr(0x82), '../index');
|
||||
$this->assertSame($transliterated, 'safe');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,218 @@
|
|||
<?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[] = ['Drupal\Tests\Component\Utility\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 {
|
||||
}
|
||||
|
||||
function test_access_arguments_resolver_access($foo) {
|
||||
}
|
||||
60
web/core/tests/Drupal/Tests/Component/Utility/BytesTest.php
Normal file
60
web/core/tests/Drupal/Tests/Component/Utility/BytesTest.php
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
121
web/core/tests/Drupal/Tests/Component/Utility/ColorTest.php
Normal file
121
web/core/tests/Drupal/Tests/Component/Utility/ColorTest.php
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
<?php
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
150
web/core/tests/Drupal/Tests/Component/Utility/CryptTest.php
Normal file
150
web/core/tests/Drupal/Tests/Component/Utility/CryptTest.php
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
<?php
|
||||
|
||||
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),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
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),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
392
web/core/tests/Drupal/Tests/Component/Utility/HtmlTest.php
Normal file
392
web/core/tests/Drupal/Tests/Component/Utility/HtmlTest.php
Normal file
|
|
@ -0,0 +1,392 @@
|
|||
<?php
|
||||
|
||||
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()),
|
||||
// Verify that passing double underscores as a filter is processed.
|
||||
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 testDecodeEntities()
|
||||
*/
|
||||
public function providerDecodeEntities() {
|
||||
return array(
|
||||
array('Drupal', 'Drupal'),
|
||||
array('<script>', '<script>'),
|
||||
array('<script>', '<script>'),
|
||||
array('<script>', '<script>'),
|
||||
array('&lt;script&gt;', '<script>'),
|
||||
array('"', '"'),
|
||||
array('"', '"'),
|
||||
array('&#34;', '"'),
|
||||
array('"', '"'),
|
||||
array('&quot;', '"'),
|
||||
array("'", "'"),
|
||||
array(''', "'"),
|
||||
array('&#39;', '''),
|
||||
array('©', '©'),
|
||||
array('©', '©'),
|
||||
array('©', '©'),
|
||||
array('→', '→'),
|
||||
array('→', '→'),
|
||||
array('➼', '➼'),
|
||||
array('➼', '➼'),
|
||||
array('€', '€'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Html::escape().
|
||||
*
|
||||
* @dataProvider providerEscape
|
||||
* @covers ::escape
|
||||
*/
|
||||
public function testEscape($expected, $text) {
|
||||
$this->assertEquals($expected, Html::escape($text));
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testEscape().
|
||||
*
|
||||
* @see testEscape()
|
||||
*/
|
||||
public function providerEscape() {
|
||||
return array(
|
||||
array('Drupal', 'Drupal'),
|
||||
array('<script>', '<script>'),
|
||||
array('&lt;script&gt;', '<script>'),
|
||||
array('&#34;', '"'),
|
||||
array('"', '"'),
|
||||
array('&quot;', '"'),
|
||||
array(''', "'"),
|
||||
array('&#039;', '''),
|
||||
array('©', '©'),
|
||||
array('→', '→'),
|
||||
array('➼', '➼'),
|
||||
array('€', '€'),
|
||||
array('Drup<75>al', "Drup\x80al"),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests relationship between escaping and decoding HTML entities.
|
||||
*
|
||||
* @covers ::decodeEntities
|
||||
* @covers ::escape
|
||||
*/
|
||||
public function testDecodeEntitiesAndEscape() {
|
||||
$string = "<em>répété</em>";
|
||||
$escaped = Html::escape($string);
|
||||
$this->assertSame('<em>répét&eacute;</em>', $escaped);
|
||||
$decoded = Html::decodeEntities($escaped);
|
||||
$this->assertSame('<em>répété</em>', $decoded);
|
||||
$decoded = Html::decodeEntities($decoded);
|
||||
$this->assertSame('<em>répété</em>', $decoded);
|
||||
$escaped = Html::escape($decoded);
|
||||
$this->assertSame('<em>répété</em>', $escaped);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Html::serialize().
|
||||
*
|
||||
* Resolves an issue by where an empty DOMDocument object sent to serialization would
|
||||
* cause errors in getElementsByTagName() in the serialization function.
|
||||
*
|
||||
* @covers ::serialize
|
||||
*/
|
||||
public function testSerialize() {
|
||||
$document = new \DOMDocument();
|
||||
$result = Html::serialize($document);
|
||||
$this->assertSame('', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::transformRootRelativeUrlsToAbsolute
|
||||
* @dataProvider providerTestTransformRootRelativeUrlsToAbsolute
|
||||
*/
|
||||
public function testTransformRootRelativeUrlsToAbsolute($html, $scheme_and_host, $expected_html) {
|
||||
$this->assertSame($expected_html ?: $html, Html::transformRootRelativeUrlsToAbsolute($html, $scheme_and_host));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::transformRootRelativeUrlsToAbsolute
|
||||
* @dataProvider providerTestTransformRootRelativeUrlsToAbsoluteAssertion
|
||||
* @expectedException \AssertionError
|
||||
*/
|
||||
public function testTransformRootRelativeUrlsToAbsoluteAssertion($scheme_and_host) {
|
||||
Html::transformRootRelativeUrlsToAbsolute('', $scheme_and_host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides test data for testTransformRootRelativeUrlsToAbsolute().
|
||||
*
|
||||
* @return array
|
||||
* Test data.
|
||||
*/
|
||||
public function providerTestTransformRootRelativeUrlsToAbsolute() {
|
||||
$data = [];
|
||||
|
||||
// One random tag name.
|
||||
$tag_name = strtolower($this->randomMachineName());
|
||||
|
||||
// A site installed either in the root of a domain or a subdirectory.
|
||||
$base_paths = ['/', '/subdir/' . $this->randomMachineName() . '/'];
|
||||
|
||||
foreach ($base_paths as $base_path) {
|
||||
// The only attribute that has more than just a URL as its value, is
|
||||
// 'srcset', so special-case it.
|
||||
$data += [
|
||||
"$tag_name, srcset, $base_path: root-relative" => ["<$tag_name srcset=\"http://example.com{$base_path}already-absolute 200w, {$base_path}root-relative 300w\">root-relative test</$tag_name>", 'http://example.com', "<$tag_name srcset=\"http://example.com{$base_path}already-absolute 200w, http://example.com{$base_path}root-relative 300w\">root-relative test</$tag_name>"],
|
||||
"$tag_name, srcset, $base_path: protocol-relative" => ["<$tag_name srcset=\"http://example.com{$base_path}already-absolute 200w, //example.com{$base_path}protocol-relative 300w\">protocol-relative test</$tag_name>", 'http://example.com', FALSE],
|
||||
"$tag_name, srcset, $base_path: absolute" => ["<$tag_name srcset=\"http://example.com{$base_path}already-absolute 200w, http://example.com{$base_path}absolute 300w\">absolute test</$tag_name>", 'http://example.com', FALSE],
|
||||
];
|
||||
|
||||
foreach (['href', 'poster', 'src', 'cite', 'data', 'action', 'formaction', 'about'] as $attribute) {
|
||||
$data += [
|
||||
"$tag_name, $attribute, $base_path: root-relative" => ["<$tag_name $attribute=\"{$base_path}root-relative\">root-relative test</$tag_name>", 'http://example.com', "<$tag_name $attribute=\"http://example.com{$base_path}root-relative\">root-relative test</$tag_name>"],
|
||||
"$tag_name, $attribute, $base_path: protocol-relative" => ["<$tag_name $attribute=\"//example.com{$base_path}protocol-relative\">protocol-relative test</$tag_name>", 'http://example.com', FALSE],
|
||||
"$tag_name, $attribute, $base_path: absolute" => ["<$tag_name $attribute=\"http://example.com{$base_path}absolute\">absolute test</$tag_name>", 'http://example.com', FALSE],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides test data for testTransformRootRelativeUrlsToAbsoluteAssertion().
|
||||
*
|
||||
* @return array
|
||||
* Test data.
|
||||
*/
|
||||
public function providerTestTransformRootRelativeUrlsToAbsoluteAssertion() {
|
||||
return [
|
||||
'only relative path' => ['llama'],
|
||||
'only root-relative path' => ['/llama'],
|
||||
'host and path' => ['example.com/llama'],
|
||||
'scheme, host and path' => ['http://example.com/llama'],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
158
web/core/tests/Drupal/Tests/Component/Utility/ImageTest.php
Normal file
158
web/core/tests/Drupal/Tests/Component/Utility/ImageTest.php
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,283 @@
|
|||
<?php
|
||||
|
||||
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.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::filter
|
||||
* @dataProvider providerTestFilter
|
||||
*/
|
||||
public function testFilter($array, $callable, $expected) {
|
||||
$this->assertEquals($expected, NestedArray::filter($array, $callable));
|
||||
}
|
||||
|
||||
public function providerTestFilter() {
|
||||
$data = [];
|
||||
$data['1d-array'] = [
|
||||
[0, 1, '', TRUE], NULL, [1 => 1, 3 => TRUE]
|
||||
];
|
||||
$data['1d-array-callable'] = [
|
||||
[0, 1, '', TRUE], function ($element) { return $element === ''; }, [2 => '']
|
||||
];
|
||||
$data['2d-array'] = [
|
||||
[[0, 1, '', TRUE], [0, 1, 2, 3]], NULL, [0 => [1 => 1, 3 => TRUE], 1 => [1 => 1, 2 => 2, 3 => 3]],
|
||||
];
|
||||
$data['2d-array-callable'] = [
|
||||
[[0, 1, '', TRUE], [0, 1, 2, 3]], function ($element) { return is_array($element) || $element === 3; }, [0 => [], 1 => [3 => 3]],
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
||||
158
web/core/tests/Drupal/Tests/Component/Utility/NumberTest.php
Normal file
158
web/core/tests/Drupal/Tests/Component/Utility/NumberTest.php
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
|
||||
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
|
||||
*
|
||||
* @see \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'),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
167
web/core/tests/Drupal/Tests/Component/Utility/RandomTest.php
Normal file
167
web/core/tests/Drupal/Tests/Component/Utility/RandomTest.php
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Utility;
|
||||
|
||||
use Drupal\Component\Utility\Random;
|
||||
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]), 'Generated duplicate random 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]), 'Generated duplicate random 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;
|
||||
}
|
||||
|
||||
}
|
||||
4328
web/core/tests/Drupal/Tests/Component/Utility/RectangleTest.php
Normal file
4328
web/core/tests/Drupal/Tests/Component/Utility/RectangleTest.php
Normal file
File diff suppressed because it is too large
Load diff
187
web/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php
Normal file
187
web/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\Component\Utility\SafeMarkupTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\Component\Utility;
|
||||
|
||||
use Drupal\Component\Render\HtmlEscapedText;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Render\MarkupInterface;
|
||||
use Drupal\Component\Render\MarkupTrait;
|
||||
use Drupal\Component\Utility\UrlHelper;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* Tests marking strings as safe.
|
||||
*
|
||||
* @group Utility
|
||||
* @coversDefaultClass \Drupal\Component\Utility\SafeMarkup
|
||||
*/
|
||||
class SafeMarkupTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function tearDown() {
|
||||
parent::tearDown();
|
||||
|
||||
UrlHelper::setAllowedProtocols(['http', 'https']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests SafeMarkup::isSafe() with different objects.
|
||||
*
|
||||
* @covers ::isSafe
|
||||
*/
|
||||
public function testIsSafe() {
|
||||
$safe_string = $this->getMock('\Drupal\Component\Render\MarkupInterface');
|
||||
$this->assertTrue(SafeMarkup::isSafe($safe_string));
|
||||
$string_object = new SafeMarkupTestString('test');
|
||||
$this->assertFalse(SafeMarkup::isSafe($string_object));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
function testCheckPlain($text, $expected, $message) {
|
||||
$result = SafeMarkup::checkPlain($text);
|
||||
$this->assertTrue($result instanceof HtmlEscapedText);
|
||||
$this->assertEquals($expected, $result, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Drupal\Component\Render\HtmlEscapedText.
|
||||
*
|
||||
* Verifies that the result of SafeMarkup::checkPlain() is the same as using
|
||||
* HtmlEscapedText directly.
|
||||
*
|
||||
* @dataProvider providerCheckPlain
|
||||
*
|
||||
* @param string $text
|
||||
* The text to provide to the HtmlEscapedText constructor.
|
||||
* @param string $expected
|
||||
* The expected output from the function.
|
||||
* @param string $message
|
||||
* The message to provide as output for the test.
|
||||
*/
|
||||
function testHtmlEscapedText($text, $expected, $message) {
|
||||
$result = new HtmlEscapedText($text);
|
||||
$this->assertEquals($expected, $result, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testCheckPlain() and testEscapeString().
|
||||
*
|
||||
* @see testCheckPlain()
|
||||
*/
|
||||
function providerCheckPlain() {
|
||||
// Checks that invalid multi-byte sequences are escaped.
|
||||
$tests[] = array("Foo\xC0barbaz", 'Foo<6F>barbaz', 'Escapes invalid sequence "Foo\xC0barbaz"');
|
||||
$tests[] = array("\xc2\"", '<27>"', 'Escapes invalid sequence "\xc2\""');
|
||||
$tests[] = array("Fooÿñ", "Fooÿñ", 'Does not escape valid sequence "Fooÿñ"');
|
||||
|
||||
// Checks that special characters are escaped.
|
||||
$tests[] = array(SafeMarkupTestMarkup::create("<script>"), '<script>', 'Escapes <script> even inside an object that implements MarkupInterface.');
|
||||
$tests[] = array("<script>", '<script>', 'Escapes <script>');
|
||||
$tests[] = array('<>&"\'', '<>&"'', 'Escapes reserved HTML characters.');
|
||||
$tests[] = array(SafeMarkupTestMarkup::create('<>&"\''), '<>&"'', 'Escapes reserved HTML characters even inside an object that implements MarkupInterface.');
|
||||
|
||||
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.
|
||||
*/
|
||||
public function testFormat($string, array $args, $expected, $message, $expected_is_safe) {
|
||||
UrlHelper::setAllowedProtocols(['http', 'https', 'mailto']);
|
||||
|
||||
$result = SafeMarkup::format($string, $args);
|
||||
$this->assertEquals($expected, (string) $result, $message);
|
||||
$this->assertEquals($expected_is_safe, $result instanceof MarkupInterface, 'SafeMarkup::format correctly sets the result as safe or not safe.');
|
||||
|
||||
foreach ($args as $arg) {
|
||||
$this->assertSame($arg instanceof SafeMarkupTestMarkup, SafeMarkup::isSafe($arg));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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: <script>', 'SafeMarkup::format replaces and escapes string.', TRUE);
|
||||
$tests[] = array('Escaped text: @value', array('@value' => SafeMarkupTestMarkup::create('<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"><script></em>', 'SafeMarkup::format replaces, escapes and themes string.', TRUE);
|
||||
$tests[] = array('Placeholder text: %value', array('%value' => SafeMarkupTestMarkup::create('<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['javascript-protocol-url'] = ['Simple text <a href=":url">giraffe</a>', [':url' => 'javascript://example.com?foo&bar'], 'Simple text <a href="//example.com?foo&bar">giraffe</a>', 'Support for filtering bad protocols', TRUE];
|
||||
$tests['external-url'] = ['Simple text <a href=":url">giraffe</a>', [':url' => 'http://example.com?foo&bar'], 'Simple text <a href="http://example.com?foo&bar">giraffe</a>', 'Support for filtering bad protocols', TRUE];
|
||||
$tests['relative-url'] = ['Simple text <a href=":url">giraffe</a>', [':url' => '/node/1?foo&bar'], 'Simple text <a href="/node/1?foo&bar">giraffe</a>', 'Support for filtering bad protocols', TRUE];
|
||||
$tests['fragment-with-special-chars'] = ['Simple text <a href=":url">giraffe</a>', [':url' => 'http://example.com/#<'], 'Simple text <a href="http://example.com/#&lt;">giraffe</a>', 'Support for filtering bad protocols', TRUE];
|
||||
$tests['mailto-protocol'] = ['Hey giraffe <a href=":url">MUUUH</a>', [':url' => 'mailto:test@example.com'], 'Hey giraffe <a href="mailto:test@example.com">MUUUH</a>', '', TRUE];
|
||||
$tests['js-with-fromCharCode'] = ['Hey giraffe <a href=":url">MUUUH</a>', [':url' => "javascript:alert(String.fromCharCode(88,83,83))"], 'Hey giraffe <a href="alert(String.fromCharCode(88,83,83))">MUUUH</a>', '', TRUE];
|
||||
|
||||
// Test some "URL" values that are not RFC 3986 compliant URLs. The result
|
||||
// of SafeMarkup::format() should still be valid HTML (other than the
|
||||
// value of the "href" attribute not being a valid URL), and not
|
||||
// vulnerable to XSS.
|
||||
$tests['non-url-with-colon'] = ['Hey giraffe <a href=":url">MUUUH</a>', [':url' => "llamas: they are not URLs"], 'Hey giraffe <a href=" they are not URLs">MUUUH</a>', '', TRUE];
|
||||
$tests['non-url-with-html'] = ['Hey giraffe <a href=":url">MUUUH</a>', [':url' => "<span>not a url</span>"], 'Hey giraffe <a href="<span>not a url</span>">MUUUH</a>', '', TRUE];
|
||||
|
||||
// Tests non-standard placeholders that will not replace.
|
||||
$tests['non-standard-placeholder'] = ['Hey hey', ['risky' => "<script>alert('foo');</script>"], 'Hey hey', '', TRUE];
|
||||
return $tests;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SafeMarkupTestString {
|
||||
|
||||
protected $string;
|
||||
|
||||
public function __construct($string) {
|
||||
$this->string = $string;
|
||||
}
|
||||
|
||||
public function __toString() {
|
||||
return $this->string;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks an object's __toString() method as returning markup.
|
||||
*/
|
||||
class SafeMarkupTestMarkup implements MarkupInterface {
|
||||
use MarkupTrait;
|
||||
|
||||
}
|
||||
323
web/core/tests/Drupal/Tests/Component/Utility/SortArrayTest.php
Normal file
323
web/core/tests/Drupal/Tests/Component/Utility/SortArrayTest.php
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
<?php
|
||||
|
||||
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 int $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 int $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 int $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 int $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.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
68
web/core/tests/Drupal/Tests/Component/Utility/TimerTest.php
Normal file
68
web/core/tests/Drupal/Tests/Component/Utility/TimerTest.php
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
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/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.');
|
||||
}
|
||||
|
||||
}
|
||||
570
web/core/tests/Drupal/Tests/Component/Utility/UnicodeTest.php
Normal file
570
web/core/tests/Drupal/Tests/Component/Utility/UnicodeTest.php
Normal file
|
|
@ -0,0 +1,570 @@
|
|||
<?php
|
||||
|
||||
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
|
||||
*/
|
||||
protected 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),
|
||||
array('以呂波耳・ほへとち。リヌルヲ。', 15),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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().
|
||||
*
|
||||
* Invalid UTF-8 examples sourced from http://stackoverflow.com/a/11709412/109119.
|
||||
*
|
||||
* @return array
|
||||
* An array of arrays, each containing the parameters for
|
||||
* self::testValidateUtf8().
|
||||
*/
|
||||
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', '€'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests multibyte strpos.
|
||||
*
|
||||
* @dataProvider providerStrpos
|
||||
* @covers ::strpos
|
||||
*/
|
||||
public function testStrpos($haystack, $needle, $offset, $expected) {
|
||||
// Run through multibyte code path.
|
||||
Unicode::setStatus(Unicode::STATUS_MULTIBYTE);
|
||||
$this->assertEquals($expected, Unicode::strpos($haystack, $needle, $offset));
|
||||
// Run through singlebyte code path.
|
||||
Unicode::setStatus(Unicode::STATUS_SINGLEBYTE);
|
||||
$this->assertEquals($expected, Unicode::strpos($haystack, $needle, $offset));
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testStrpos().
|
||||
*
|
||||
* @see testStrpos()
|
||||
*
|
||||
* @return array
|
||||
* An array containing:
|
||||
* - The haystack string to be searched in.
|
||||
* - The needle string to search for.
|
||||
* - The offset integer to start at.
|
||||
* - The expected integer/FALSE result.
|
||||
*/
|
||||
public function providerStrpos() {
|
||||
return array(
|
||||
array('frànçAIS is über-åwesome', 'frànçAIS is über-åwesome', 0, 0),
|
||||
array('frànçAIS is über-åwesome', 'rànçAIS is über-åwesome', 0, 1),
|
||||
array('frànçAIS is über-åwesome', 'not in string', 0, FALSE),
|
||||
array('frànçAIS is über-åwesome', 'r', 0, 1),
|
||||
array('frànçAIS is über-åwesome', 'nçAIS', 0, 3),
|
||||
array('frànçAIS is über-åwesome', 'nçAIS', 2, 3),
|
||||
array('frànçAIS is über-åwesome', 'nçAIS', 3, 3),
|
||||
array('以呂波耳・ほへとち。リヌルヲ。', '波耳', 0, 2),
|
||||
array('以呂波耳・ほへとち。リヌルヲ。', '波耳', 1, 2),
|
||||
array('以呂波耳・ほへとち。リヌルヲ。', '波耳', 2, 2),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
588
web/core/tests/Drupal/Tests/Component/Utility/UrlHelperTest.php
Normal file
588
web/core/tests/Drupal/Tests/Component/Utility/UrlHelperTest.php
Normal file
|
|
@ -0,0 +1,588 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Utility;
|
||||
|
||||
use Drupal\Component\Utility\UrlHelper;
|
||||
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, $test_url . ' is a valid 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, $test_url . ' is NOT a valid 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, $test_url . ' is a valid 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, $test_url . ' is NOT a valid 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),
|
||||
// Simple external URLs.
|
||||
array('http://example.com', TRUE),
|
||||
array('https://example.com', TRUE),
|
||||
array('http://drupal.org/foo/bar?foo=bar&bar=baz&baz#foo', TRUE),
|
||||
array('//drupal.org', TRUE),
|
||||
// Some browsers ignore or strip leading control characters.
|
||||
array("\x00//www.example.com", TRUE),
|
||||
array("\x08//www.example.com", TRUE),
|
||||
array("\x1F//www.example.com", TRUE),
|
||||
array("\n//www.example.com", TRUE),
|
||||
// JSON supports decoding directly from UTF-8 code points.
|
||||
array(json_decode('"\u00AD"') . "//www.example.com", TRUE),
|
||||
array(json_decode('"\u200E"') . "//www.example.com", TRUE),
|
||||
array(json_decode('"\uE0020"') . "//www.example.com", TRUE),
|
||||
array(json_decode('"\uE000"') . "//www.example.com", TRUE),
|
||||
// Backslashes should be normalized to forward.
|
||||
array('\\\\example.com', TRUE),
|
||||
// Local URLs.
|
||||
array('node', FALSE),
|
||||
array('/system/ajax', FALSE),
|
||||
array('?q=foo:bar', FALSE),
|
||||
array('node/edit:me', FALSE),
|
||||
array('/drupal.org', FALSE),
|
||||
array('<front>', 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);
|
||||
$this->assertEquals($expected, UrlHelper::filterBadProtocol($uri));
|
||||
// Multiple calls to UrlHelper::filterBadProtocol() do not cause double
|
||||
// escaping.
|
||||
$this->assertEquals($expected, UrlHelper::filterBadProtocol(UrlHelper::filterBadProtocol($uri)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for self::testTestFilterBadProtocol().
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function providerTestFilterBadProtocol() {
|
||||
return array(
|
||||
array('javascript://example.com?foo&bar', '//example.com?foo&bar', array('http', 'https')),
|
||||
// Test custom protocols.
|
||||
array('http://example.com?foo&bar', '//example.com?foo&bar', array('https')),
|
||||
// Valid protocol.
|
||||
array('http://example.com?foo&bar', 'http://example.com?foo&bar', array('https', 'http')),
|
||||
// Colon not part of the URL scheme.
|
||||
array('/test:8888?foo&bar', '/test:8888?foo&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'),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
167
web/core/tests/Drupal/Tests/Component/Utility/UserAgentTest.php
Normal file
167
web/core/tests/Drupal/Tests/Component/Utility/UserAgentTest.php
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
<?php
|
||||
|
||||
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'),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
125
web/core/tests/Drupal/Tests/Component/Utility/VariableTest.php
Normal file
125
web/core/tests/Drupal/Tests/Component/Utility/VariableTest.php
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
<?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 '
|
||||
'"\'"',
|
||||
"'",
|
||||
),
|
||||
array(
|
||||
// Quotes with $ symbols.
|
||||
'"\$settings[\'foo\']"',
|
||||
'$settings[\'foo\']',
|
||||
),
|
||||
// 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 {
|
||||
|
||||
}
|
||||
622
web/core/tests/Drupal/Tests/Component/Utility/XssTest.php
Normal file
622
web/core/tests/Drupal/Tests/Component/Utility/XssTest.php
Normal file
|
|
@ -0,0 +1,622 @@
|
|||
<?php
|
||||
|
||||
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's Online",
|
||||
"who's online",
|
||||
'HTML filter -- html entity number',
|
||||
),
|
||||
array(
|
||||
"Who&#039;s Online",
|
||||
"who's online",
|
||||
'HTML filter -- encoded html entity number',
|
||||
),
|
||||
array(
|
||||
"Who&amp;#039; Online",
|
||||
"who&#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="<script defer>alert(0)</script>">',
|
||||
'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=javascript:alert(0)>',
|
||||
'javascript',
|
||||
'HTML scheme clearing evasion -- UTF-8 decimal encoding.',
|
||||
array('img'),
|
||||
),
|
||||
array(
|
||||
'<img src=javascript:alert(0)>',
|
||||
'javascript',
|
||||
'HTML scheme clearing evasion -- long UTF-8 encoding.',
|
||||
array('img'),
|
||||
),
|
||||
array(
|
||||
'<img src=javascript:alert(0)>',
|
||||
'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	ascript:alert(0)">',
|
||||
'script',
|
||||
'HTML scheme clearing evasion -- an encoded, embedded tab.',
|
||||
array('img'),
|
||||
),
|
||||
array(
|
||||
'<img src="jav
ascript:alert(0)">',
|
||||
'script',
|
||||
'HTML scheme clearing evasion -- an encoded, embedded newline.',
|
||||
array('img'),
|
||||
),
|
||||
// With 
 this test would fail, but the entity gets turned into
|
||||
// &#xD;, so it's OK.
|
||||
array(
|
||||
'<img src="jav
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="  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 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(
|
||||
'<a href="https://www.drupal.org/" rel="dc:publisher">Drupal</a>',
|
||||
'<a href="https://www.drupal.org/" rel="dc:publisher">Drupal</a>',
|
||||
'Link tag with rel attribute',
|
||||
array('a')
|
||||
),
|
||||
array(
|
||||
'<span property="dc:subject">Drupal 8: The best release ever.</span>',
|
||||
'<span property="dc:subject">Drupal 8: The best release ever.</span>',
|
||||
'Span tag with property attribute',
|
||||
array('span')
|
||||
),
|
||||
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')
|
||||
),
|
||||
array(
|
||||
'<a data-a2a-url="foo"></a>',
|
||||
'<a data-a2a-url="foo"></a>',
|
||||
'Link tag with numeric data attribute',
|
||||
array('a')
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
||||
97
web/core/tests/Drupal/Tests/Component/Uuid/UuidTest.php
Normal file
97
web/core/tests/Drupal/Tests/Component/Uuid/UuidTest.php
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Component\Uuid;
|
||||
|
||||
use Drupal\Component\Uuid\Uuid;
|
||||
use Drupal\Component\Uuid\UuidInterface;
|
||||
use Drupal\Component\Uuid\Com;
|
||||
use Drupal\Component\Uuid\Pecl;
|
||||
use Drupal\Component\Uuid\Php;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* Tests the handling of Universally Unique Identifiers (UUIDs).
|
||||
*
|
||||
* @group Uuid
|
||||
*/
|
||||
class UuidTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Tests generating valid UUIDs.
|
||||
*
|
||||
* @dataProvider providerUuidInstances
|
||||
*/
|
||||
public function testGenerateUuid(UuidInterface $instance) {
|
||||
$this->assertTrue(Uuid::isValid($instance->generate()), sprintf('UUID generation for %s works.', get_class($instance)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that generated UUIDs are unique.
|
||||
*
|
||||
* @dataProvider providerUuidInstances
|
||||
*/
|
||||
public function testUuidIsUnique(UuidInterface $instance) {
|
||||
$this->assertNotEquals($instance->generate(), $instance->generate(), sprintf('Same UUID was not generated twice with %s.', get_class($instance)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dataprovider for UUID instance tests.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerUuidInstances() {
|
||||
|
||||
$instances = array();
|
||||
$instances[][] = new Php();
|
||||
|
||||
// If valid PECL extensions exists add to list.
|
||||
if (function_exists('uuid_create') && !function_exists('uuid_make')) {
|
||||
$instances[][] = new Pecl();
|
||||
}
|
||||
|
||||
// If we are on Windows add the com implementation as well.
|
||||
if (function_exists('com_create_guid')) {
|
||||
$instances[][] = new Com();
|
||||
}
|
||||
|
||||
return $instances;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests UUID validation.
|
||||
*
|
||||
* @param string $uuid
|
||||
* The uuid to check against.
|
||||
* @param bool $is_valid
|
||||
* Whether the uuid is valid or not.
|
||||
* @param string $message
|
||||
* The message to display on failure.
|
||||
*
|
||||
* @dataProvider providerTestValidation
|
||||
*/
|
||||
public function testValidation($uuid, $is_valid, $message) {
|
||||
$this->assertSame($is_valid, Uuid::isValid($uuid), $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dataprovider for UUID instance tests.
|
||||
*
|
||||
* @return array
|
||||
* An array of arrays containing
|
||||
* - The Uuid to check against.
|
||||
* - (bool) Whether or not the Uuid is valid.
|
||||
* - Failure message.
|
||||
*/
|
||||
public function providerTestValidation() {
|
||||
return array(
|
||||
// These valid UUIDs.
|
||||
array('6ba7b810-9dad-11d1-80b4-00c04fd430c8', TRUE, 'Basic FQDN UUID did not validate'),
|
||||
array('00000000-0000-0000-0000-000000000000', TRUE, 'Minimum UUID did not validate'),
|
||||
array('ffffffff-ffff-ffff-ffff-ffffffffffff', TRUE, 'Maximum UUID did not validate'),
|
||||
// These are invalid UUIDs.
|
||||
array('0ab26e6b-f074-4e44-9da-601205fa0e976', FALSE, 'Invalid format was validated'),
|
||||
array('0ab26e6b-f074-4e44-9daf-1205fa0e9761f', FALSE, 'Invalid length was validated'),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
174
web/core/tests/Drupal/Tests/ComposerIntegrationTest.php
Normal file
174
web/core/tests/Drupal/Tests/ComposerIntegrationTest.php
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests;
|
||||
|
||||
/**
|
||||
* Tests Composer integration.
|
||||
*
|
||||
* @group Composer
|
||||
*/
|
||||
class ComposerIntegrationTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Gets human-readable JSON error messages.
|
||||
*
|
||||
* @return string[]
|
||||
* Keys are JSON_ERROR_* constants.
|
||||
*/
|
||||
protected function getErrorMessages() {
|
||||
$messages = [
|
||||
0 => 'No errors found',
|
||||
JSON_ERROR_DEPTH => 'The maximum stack depth has been exceeded',
|
||||
JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON',
|
||||
JSON_ERROR_CTRL_CHAR => 'Control character error, possibly incorrectly encoded',
|
||||
JSON_ERROR_SYNTAX => 'Syntax error',
|
||||
JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded',
|
||||
];
|
||||
|
||||
if (version_compare(phpversion(), '5.5.0', '>=')) {
|
||||
$messages[JSON_ERROR_RECURSION] = 'One or more recursive references in the value to be encoded';
|
||||
$messages[JSON_ERROR_INF_OR_NAN] = 'One or more NAN or INF values in the value to be encoded';
|
||||
$messages[JSON_ERROR_UNSUPPORTED_TYPE] = 'A value of a type that cannot be encoded was given';
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the paths to the folders that contain the Composer integration.
|
||||
*
|
||||
* @return string[]
|
||||
* The paths.
|
||||
*/
|
||||
protected function getPaths() {
|
||||
return [
|
||||
$this->root,
|
||||
$this->root . '/core',
|
||||
$this->root . '/core/lib/Drupal/Component/Annotation',
|
||||
$this->root . '/core/lib/Drupal/Component/Assertion',
|
||||
$this->root . '/core/lib/Drupal/Component/Bridge',
|
||||
$this->root . '/core/lib/Drupal/Component/Datetime',
|
||||
$this->root . '/core/lib/Drupal/Component/DependencyInjection',
|
||||
$this->root . '/core/lib/Drupal/Component/Diff',
|
||||
$this->root . '/core/lib/Drupal/Component/Discovery',
|
||||
$this->root . '/core/lib/Drupal/Component/EventDispatcher',
|
||||
$this->root . '/core/lib/Drupal/Component/FileCache',
|
||||
$this->root . '/core/lib/Drupal/Component/FileSystem',
|
||||
$this->root . '/core/lib/Drupal/Component/Gettext',
|
||||
$this->root . '/core/lib/Drupal/Component/Graph',
|
||||
$this->root . '/core/lib/Drupal/Component/HttpFoundation',
|
||||
$this->root . '/core/lib/Drupal/Component/PhpStorage',
|
||||
$this->root . '/core/lib/Drupal/Component/Plugin',
|
||||
$this->root . '/core/lib/Drupal/Component/ProxyBuilder',
|
||||
$this->root . '/core/lib/Drupal/Component/Render',
|
||||
$this->root . '/core/lib/Drupal/Component/Serialization',
|
||||
$this->root . '/core/lib/Drupal/Component/Transliteration',
|
||||
$this->root . '/core/lib/Drupal/Component/Utility',
|
||||
$this->root . '/core/lib/Drupal/Component/Uuid',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests composer.json.
|
||||
*/
|
||||
public function testComposerJson() {
|
||||
foreach ($this->getPaths() as $path) {
|
||||
$json = file_get_contents($path . '/composer.json');
|
||||
$result = json_decode($json);
|
||||
$this->assertNotNull($result, $this->getErrorMessages()[json_last_error()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests composer.lock content-hash.
|
||||
*/
|
||||
public function testComposerLockHash() {
|
||||
$content_hash = self::getContentHash(file_get_contents($this->root . '/composer.json'));
|
||||
$lock = json_decode(file_get_contents($this->root . '/composer.lock'), TRUE);
|
||||
$this->assertSame($content_hash, $lock['content-hash']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests core's composer.json replace section.
|
||||
*
|
||||
* Verify that all core modules are also listed in the 'replace' section of
|
||||
* core's composer.json.
|
||||
*/
|
||||
public function testAllModulesReplaced() {
|
||||
// Assemble a path to core modules.
|
||||
$module_path = $this->root . '/core/modules';
|
||||
|
||||
// Grab the 'replace' section of the core composer.json file.
|
||||
$json = json_decode(file_get_contents($this->root . '/core/composer.json'));
|
||||
$composer_replace_packages = (array) $json->replace;
|
||||
|
||||
// Get a list of all the files in the module path.
|
||||
$folders = scandir($module_path);
|
||||
|
||||
// Make sure we only deal with directories that aren't . or ..
|
||||
$module_names = [];
|
||||
$discard = ['.', '..'];
|
||||
foreach ($folders as $file_name) {
|
||||
if ((!in_array($file_name, $discard)) && is_dir($module_path . '/' . $file_name)) {
|
||||
$module_names[] = $file_name;
|
||||
}
|
||||
}
|
||||
|
||||
// Assert that each core module has a corresponding 'replace' in
|
||||
// composer.json.
|
||||
foreach ($module_names as $module_name) {
|
||||
$this->assertArrayHasKey(
|
||||
'drupal/' . $module_name,
|
||||
$composer_replace_packages,
|
||||
'Unable to find ' . $module_name . ' in replace list of composer.json'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// @codingStandardsIgnoreStart
|
||||
/**
|
||||
* The following method is copied from \Composer\Package\Locker.
|
||||
*
|
||||
* @see https://github.com/composer/composer
|
||||
*/
|
||||
/**
|
||||
* Returns the md5 hash of the sorted content of the composer file.
|
||||
*
|
||||
* @param string $composerFileContents The contents of the composer file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function getContentHash($composerFileContents)
|
||||
{
|
||||
$content = json_decode($composerFileContents, true);
|
||||
|
||||
$relevantKeys = array(
|
||||
'name',
|
||||
'version',
|
||||
'require',
|
||||
'require-dev',
|
||||
'conflict',
|
||||
'replace',
|
||||
'provide',
|
||||
'minimum-stability',
|
||||
'prefer-stable',
|
||||
'repositories',
|
||||
'extra',
|
||||
);
|
||||
|
||||
$relevantContent = array();
|
||||
|
||||
foreach (array_intersect($relevantKeys, array_keys($content)) as $key) {
|
||||
$relevantContent[$key] = $content[$key];
|
||||
}
|
||||
if (isset($content['config']['platform'])) {
|
||||
$relevantContent['config']['platform'] = $content['config']['platform'];
|
||||
}
|
||||
|
||||
ksort($relevantContent);
|
||||
|
||||
return md5(json_encode($relevantContent));
|
||||
}
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
}
|
||||
59
web/core/tests/Drupal/Tests/ConfigTestTrait.php
Normal file
59
web/core/tests/Drupal/Tests/ConfigTestTrait.php
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests;
|
||||
|
||||
use Drupal\Core\Config\ConfigImporter;
|
||||
use Drupal\Core\Config\StorageComparer;
|
||||
use Drupal\Core\Config\StorageInterface;
|
||||
|
||||
/**
|
||||
* Provides helper methods to deal with config system objects in tests.
|
||||
*/
|
||||
trait ConfigTestTrait {
|
||||
|
||||
/**
|
||||
* Returns a ConfigImporter object to import test configuration.
|
||||
*
|
||||
* @return \Drupal\Core\Config\ConfigImporter
|
||||
* The config importer object.
|
||||
*/
|
||||
protected function configImporter() {
|
||||
if (!$this->configImporter) {
|
||||
// Set up the ConfigImporter object for testing.
|
||||
$storage_comparer = new StorageComparer(
|
||||
$this->container->get('config.storage.sync'),
|
||||
$this->container->get('config.storage'),
|
||||
$this->container->get('config.manager')
|
||||
);
|
||||
$this->configImporter = new ConfigImporter(
|
||||
$storage_comparer,
|
||||
$this->container->get('event_dispatcher'),
|
||||
$this->container->get('config.manager'),
|
||||
$this->container->get('lock'),
|
||||
$this->container->get('config.typed'),
|
||||
$this->container->get('module_handler'),
|
||||
$this->container->get('module_installer'),
|
||||
$this->container->get('theme_handler'),
|
||||
$this->container->get('string_translation')
|
||||
);
|
||||
}
|
||||
// Always recalculate the changelist when called.
|
||||
return $this->configImporter->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies configuration objects from source storage to target storage.
|
||||
*
|
||||
* @param \Drupal\Core\Config\StorageInterface $source_storage
|
||||
* The source config storage service.
|
||||
* @param \Drupal\Core\Config\StorageInterface $target_storage
|
||||
* The target config storage service.
|
||||
*/
|
||||
protected function copyConfig(StorageInterface $source_storage, StorageInterface $target_storage) {
|
||||
$target_storage->deleteAll();
|
||||
foreach ($source_storage->listAll() as $name) {
|
||||
$target_storage->write($name, $source_storage->read($name));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
572
web/core/tests/Drupal/Tests/Core/Access/AccessManagerTest.php
Normal file
572
web/core/tests/Drupal/Tests/Core/Access/AccessManagerTest.php
Normal file
|
|
@ -0,0 +1,572 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\Core\Access\AccessManagerTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\Core\Access;
|
||||
|
||||
use Drupal\Core\Access\AccessCheckInterface;
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Access\CheckProvider;
|
||||
use Drupal\Core\Cache\Context\CacheContextsManager;
|
||||
use Drupal\Core\Routing\RouteMatch;
|
||||
use Drupal\Core\Access\AccessManager;
|
||||
use Drupal\Core\Access\DefaultAccessCheck;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\router_test\Access\DefinedTestAccessCheck;
|
||||
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\Routing\Exception\RouteNotFoundException;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Core\Access\AccessManager
|
||||
* @group Access
|
||||
*/
|
||||
class AccessManagerTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The dependency injection container.
|
||||
*
|
||||
* @var \Symfony\Component\DependencyInjection\ContainerBuilder
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* The collection of routes, which are tested.
|
||||
*
|
||||
* @var \Symfony\Component\Routing\RouteCollection
|
||||
*/
|
||||
protected $routeCollection;
|
||||
|
||||
/**
|
||||
* The access manager to test.
|
||||
*
|
||||
* @var \Drupal\Core\Access\AccessManager
|
||||
*/
|
||||
protected $accessManager;
|
||||
|
||||
/**
|
||||
* The route provider.
|
||||
*
|
||||
* @var \PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $routeProvider;
|
||||
|
||||
/**
|
||||
* The parameter converter.
|
||||
*
|
||||
* @var \Drupal\Core\ParamConverter\ParamConverterManagerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $paramConverter;
|
||||
|
||||
/**
|
||||
* The mocked account.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $account;
|
||||
|
||||
/**
|
||||
* The access arguments resolver.
|
||||
*
|
||||
* @var \Drupal\Core\Access\AccessArgumentsResolverFactoryInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $argumentsResolverFactory;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Session\AccountInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $currentUser;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Access\CheckProvider
|
||||
*/
|
||||
protected $checkProvider;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->container = new ContainerBuilder();
|
||||
$cache_contexts_manager = $this->prophesize(CacheContextsManager::class)->reveal();
|
||||
$this->container->set('cache_contexts_manager', $cache_contexts_manager);
|
||||
\Drupal::setContainer($this->container);
|
||||
|
||||
$this->routeCollection = new RouteCollection();
|
||||
$this->routeCollection->add('test_route_1', new Route('/test-route-1'));
|
||||
$this->routeCollection->add('test_route_2', new Route('/test-route-2', array(), array('_access' => 'TRUE')));
|
||||
$this->routeCollection->add('test_route_3', new Route('/test-route-3', array(), array('_access' => 'FALSE')));
|
||||
$this->routeCollection->add('test_route_4', new Route('/test-route-4/{value}', array(), array('_access' => 'TRUE')));
|
||||
|
||||
$this->routeProvider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface');
|
||||
$map = array();
|
||||
foreach ($this->routeCollection->all() as $name => $route) {
|
||||
$map[] = array($name, array(), $route);
|
||||
}
|
||||
$map[] = array('test_route_4', array('value' => 'example'), $this->routeCollection->get('test_route_4'));
|
||||
$this->routeProvider->expects($this->any())
|
||||
->method('getRouteByName')
|
||||
->will($this->returnValueMap($map));
|
||||
|
||||
$map = array();
|
||||
$map[] = array('test_route_1', array(), '/test-route-1');
|
||||
$map[] = array('test_route_2', array(), '/test-route-2');
|
||||
$map[] = array('test_route_3', array(), '/test-route-3');
|
||||
$map[] = array('test_route_4', array('value' => 'example'), '/test-route-4/example');
|
||||
|
||||
$this->paramConverter = $this->getMock('Drupal\Core\ParamConverter\ParamConverterManagerInterface');
|
||||
|
||||
$this->account = $this->getMock('Drupal\Core\Session\AccountInterface');
|
||||
$this->currentUser = $this->getMock('Drupal\Core\Session\AccountInterface');
|
||||
$this->argumentsResolverFactory = $this->getMock('Drupal\Core\Access\AccessArgumentsResolverFactoryInterface');
|
||||
$this->checkProvider = new CheckProvider();
|
||||
$this->checkProvider->setContainer($this->container);
|
||||
|
||||
$this->accessManager = new AccessManager($this->routeProvider, $this->paramConverter, $this->argumentsResolverFactory, $this->currentUser, $this->checkProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests \Drupal\Core\Access\AccessManager::setChecks().
|
||||
*/
|
||||
public function testSetChecks() {
|
||||
// Check setChecks without any access checker defined yet.
|
||||
$this->checkProvider->setChecks($this->routeCollection);
|
||||
|
||||
foreach ($this->routeCollection->all() as $route) {
|
||||
$this->assertNull($route->getOption('_access_checks'));
|
||||
}
|
||||
|
||||
$this->setupAccessChecker();
|
||||
|
||||
$this->checkProvider->setChecks($this->routeCollection);
|
||||
|
||||
$this->assertEquals($this->routeCollection->get('test_route_1')->getOption('_access_checks'), NULL);
|
||||
$this->assertEquals($this->routeCollection->get('test_route_2')->getOption('_access_checks'), array('test_access_default'));
|
||||
$this->assertEquals($this->routeCollection->get('test_route_3')->getOption('_access_checks'), array('test_access_default'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests setChecks with a dynamic access checker.
|
||||
*/
|
||||
public function testSetChecksWithDynamicAccessChecker() {
|
||||
// Setup the access manager.
|
||||
$this->accessManager = new AccessManager($this->routeProvider, $this->paramConverter, $this->argumentsResolverFactory, $this->currentUser, $this->checkProvider);
|
||||
|
||||
// Setup the dynamic access checker.
|
||||
$access_check = $this->getMock('Drupal\Tests\Core\Access\TestAccessCheckInterface');
|
||||
$this->container->set('test_access', $access_check);
|
||||
$this->checkProvider->addCheckService('test_access', 'access');
|
||||
|
||||
$route = new Route('/test-path', array(), array('_foo' => '1', '_bar' => '1'));
|
||||
$route2 = new Route('/test-path', array(), array('_foo' => '1', '_bar' => '2'));
|
||||
$collection = new RouteCollection();
|
||||
$collection->add('test_route', $route);
|
||||
$collection->add('test_route2', $route2);
|
||||
|
||||
$access_check->expects($this->exactly(2))
|
||||
->method('applies')
|
||||
->with($this->isInstanceOf('Symfony\Component\Routing\Route'))
|
||||
->will($this->returnCallback(function (Route $route) {
|
||||
return $route->getRequirement('_bar') == 2;
|
||||
}));
|
||||
|
||||
$this->checkProvider->setChecks($collection);
|
||||
$this->assertEmpty($route->getOption('_access_checks'));
|
||||
$this->assertEquals(array('test_access'), $route2->getOption('_access_checks'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests \Drupal\Core\Access\AccessManager::check().
|
||||
*/
|
||||
public function testCheck() {
|
||||
$route_matches = [];
|
||||
|
||||
// Construct route match objects.
|
||||
foreach ($this->routeCollection->all() as $route_name => $route) {
|
||||
$route_matches[$route_name] = new RouteMatch($route_name, $route, [], []);
|
||||
}
|
||||
|
||||
// Check route access without any access checker defined yet.
|
||||
foreach ($route_matches as $route_match) {
|
||||
$this->assertEquals(FALSE, $this->accessManager->check($route_match, $this->account));
|
||||
$this->assertEquals(AccessResult::neutral(), $this->accessManager->check($route_match, $this->account, NULL, TRUE));
|
||||
}
|
||||
|
||||
$this->setupAccessChecker();
|
||||
|
||||
// An access checker got setup, but the routes haven't been setup using
|
||||
// setChecks.
|
||||
foreach ($route_matches as $route_match) {
|
||||
$this->assertEquals(FALSE, $this->accessManager->check($route_match, $this->account));
|
||||
$this->assertEquals(AccessResult::neutral(), $this->accessManager->check($route_match, $this->account, NULL, TRUE));
|
||||
}
|
||||
|
||||
// Now applicable access checks have been saved on each route object.
|
||||
$this->checkProvider->setChecks($this->routeCollection);
|
||||
$this->setupAccessArgumentsResolverFactory();
|
||||
|
||||
$this->assertEquals(FALSE, $this->accessManager->check($route_matches['test_route_1'], $this->account));
|
||||
$this->assertEquals(TRUE, $this->accessManager->check($route_matches['test_route_2'], $this->account));
|
||||
$this->assertEquals(FALSE, $this->accessManager->check($route_matches['test_route_3'], $this->account));
|
||||
$this->assertEquals(TRUE, $this->accessManager->check($route_matches['test_route_4'], $this->account));
|
||||
$this->assertEquals(AccessResult::neutral(), $this->accessManager->check($route_matches['test_route_1'], $this->account, NULL, TRUE));
|
||||
$this->assertEquals(AccessResult::allowed(), $this->accessManager->check($route_matches['test_route_2'], $this->account, NULL, TRUE));
|
||||
$this->assertEquals(AccessResult::forbidden(), $this->accessManager->check($route_matches['test_route_3'], $this->account, NULL, TRUE));
|
||||
$this->assertEquals(AccessResult::allowed(), $this->accessManager->check($route_matches['test_route_4'], $this->account, NULL, TRUE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests \Drupal\Core\Access\AccessManager::check() with no account specified.
|
||||
*
|
||||
* @covers ::check
|
||||
*/
|
||||
public function testCheckWithNullAccount() {
|
||||
$this->setupAccessChecker();
|
||||
$this->checkProvider->setChecks($this->routeCollection);
|
||||
|
||||
$route = $this->routeCollection->get('test_route_2');
|
||||
$route_match = new RouteMatch('test_route_2', $route, [], []);
|
||||
|
||||
// Asserts that the current user is passed to the access arguments resolver
|
||||
// factory.
|
||||
$this->setupAccessArgumentsResolverFactory()
|
||||
->with($route_match, $this->currentUser, NULL);
|
||||
|
||||
$this->assertTrue($this->accessManager->check($route_match));
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for the conjunction test.
|
||||
*
|
||||
* @return array
|
||||
* An array of data for check conjunctions.
|
||||
*
|
||||
* @see \Drupal\Tests\Core\Access\AccessManagerTest::testCheckConjunctions()
|
||||
*/
|
||||
public function providerTestCheckConjunctions() {
|
||||
$access_allow = AccessResult::allowed();
|
||||
$access_deny = AccessResult::neutral();
|
||||
$access_kill = AccessResult::forbidden();
|
||||
|
||||
$access_configurations = array();
|
||||
$access_configurations[] = array(
|
||||
'name' => 'test_route_4',
|
||||
'condition_one' => 'TRUE',
|
||||
'condition_two' => 'FALSE',
|
||||
'expected' => $access_kill,
|
||||
);
|
||||
$access_configurations[] = array(
|
||||
'name' => 'test_route_5',
|
||||
'condition_one' => 'TRUE',
|
||||
'condition_two' => 'NULL',
|
||||
'expected' => $access_deny,
|
||||
);
|
||||
$access_configurations[] = array(
|
||||
'name' => 'test_route_6',
|
||||
'condition_one' => 'FALSE',
|
||||
'condition_two' => 'NULL',
|
||||
'expected' => $access_kill,
|
||||
);
|
||||
$access_configurations[] = array(
|
||||
'name' => 'test_route_7',
|
||||
'condition_one' => 'TRUE',
|
||||
'condition_two' => 'TRUE',
|
||||
'expected' => $access_allow,
|
||||
);
|
||||
$access_configurations[] = array(
|
||||
'name' => 'test_route_8',
|
||||
'condition_one' => 'FALSE',
|
||||
'condition_two' => 'FALSE',
|
||||
'expected' => $access_kill,
|
||||
);
|
||||
$access_configurations[] = array(
|
||||
'name' => 'test_route_9',
|
||||
'condition_one' => 'NULL',
|
||||
'condition_two' => 'NULL',
|
||||
'expected' => $access_deny,
|
||||
);
|
||||
|
||||
return $access_configurations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test \Drupal\Core\Access\AccessManager::check() with conjunctions.
|
||||
*
|
||||
* @dataProvider providerTestCheckConjunctions
|
||||
*/
|
||||
public function testCheckConjunctions($name, $condition_one, $condition_two, $expected_access) {
|
||||
$this->setupAccessChecker();
|
||||
$access_check = new DefinedTestAccessCheck();
|
||||
$this->container->register('test_access_defined', $access_check);
|
||||
$this->checkProvider->addCheckService('test_access_defined', 'access', array('_test_access'));
|
||||
|
||||
$route_collection = new RouteCollection();
|
||||
// Setup a test route for each access configuration.
|
||||
$requirements = array(
|
||||
'_access' => $condition_one,
|
||||
'_test_access' => $condition_two,
|
||||
);
|
||||
$route = new Route($name, array(), $requirements);
|
||||
$route_collection->add($name, $route);
|
||||
|
||||
$this->checkProvider->setChecks($route_collection);
|
||||
$this->setupAccessArgumentsResolverFactory();
|
||||
|
||||
$route_match = new RouteMatch($name, $route, array(), array());
|
||||
$this->assertEquals($expected_access->isAllowed(), $this->accessManager->check($route_match, $this->account));
|
||||
$this->assertEquals($expected_access, $this->accessManager->check($route_match, $this->account, NULL, TRUE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the checkNamedRoute method.
|
||||
*
|
||||
* @see \Drupal\Core\Access\AccessManager::checkNamedRoute()
|
||||
*/
|
||||
public function testCheckNamedRoute() {
|
||||
$this->setupAccessChecker();
|
||||
$this->checkProvider->setChecks($this->routeCollection);
|
||||
$this->setupAccessArgumentsResolverFactory();
|
||||
|
||||
$this->paramConverter->expects($this->at(0))
|
||||
->method('convert')
|
||||
->with(array(RouteObjectInterface::ROUTE_NAME => 'test_route_2', RouteObjectInterface::ROUTE_OBJECT => $this->routeCollection->get('test_route_2')))
|
||||
->will($this->returnValue(array()));
|
||||
$this->paramConverter->expects($this->at(1))
|
||||
->method('convert')
|
||||
->with(array(RouteObjectInterface::ROUTE_NAME => 'test_route_2', RouteObjectInterface::ROUTE_OBJECT => $this->routeCollection->get('test_route_2')))
|
||||
->will($this->returnValue(array()));
|
||||
|
||||
$this->paramConverter->expects($this->at(2))
|
||||
->method('convert')
|
||||
->with(array('value' => 'example', RouteObjectInterface::ROUTE_NAME => 'test_route_4', RouteObjectInterface::ROUTE_OBJECT => $this->routeCollection->get('test_route_4')))
|
||||
->will($this->returnValue(array('value' => 'example')));
|
||||
$this->paramConverter->expects($this->at(3))
|
||||
->method('convert')
|
||||
->with(array('value' => 'example', RouteObjectInterface::ROUTE_NAME => 'test_route_4', RouteObjectInterface::ROUTE_OBJECT => $this->routeCollection->get('test_route_4')))
|
||||
->will($this->returnValue(array('value' => 'example')));
|
||||
|
||||
// Tests the access with routes with parameters without given request.
|
||||
$this->assertEquals(TRUE, $this->accessManager->checkNamedRoute('test_route_2', array(), $this->account));
|
||||
$this->assertEquals(AccessResult::allowed(), $this->accessManager->checkNamedRoute('test_route_2', array(), $this->account, TRUE));
|
||||
$this->assertEquals(TRUE, $this->accessManager->checkNamedRoute('test_route_4', array('value' => 'example'), $this->account));
|
||||
$this->assertEquals(AccessResult::allowed(), $this->accessManager->checkNamedRoute('test_route_4', array('value' => 'example'), $this->account, TRUE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the checkNamedRoute with upcasted values.
|
||||
*
|
||||
* @see \Drupal\Core\Access\AccessManager::checkNamedRoute()
|
||||
*/
|
||||
public function testCheckNamedRouteWithUpcastedValues() {
|
||||
$this->routeCollection = new RouteCollection();
|
||||
$route = new Route('/test-route-1/{value}', array(), array('_test_access' => 'TRUE'));
|
||||
$this->routeCollection->add('test_route_1', $route);
|
||||
|
||||
$this->routeProvider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface');
|
||||
$this->routeProvider->expects($this->any())
|
||||
->method('getRouteByName')
|
||||
->with('test_route_1', array('value' => 'example'))
|
||||
->will($this->returnValue($route));
|
||||
|
||||
$map = array();
|
||||
$map[] = array('test_route_1', array('value' => 'example'), '/test-route-1/example');
|
||||
|
||||
$this->paramConverter = $this->getMock('Drupal\Core\ParamConverter\ParamConverterManagerInterface');
|
||||
$this->paramConverter->expects($this->atLeastOnce())
|
||||
->method('convert')
|
||||
->with(array('value' => 'example', RouteObjectInterface::ROUTE_NAME => 'test_route_1', RouteObjectInterface::ROUTE_OBJECT => $route))
|
||||
->will($this->returnValue(array('value' => 'upcasted_value')));
|
||||
|
||||
$this->setupAccessArgumentsResolverFactory($this->exactly(2))
|
||||
->with($this->callback(function ($route_match) {
|
||||
return $route_match->getParameters()->get('value') == 'upcasted_value';
|
||||
}));
|
||||
|
||||
$this->accessManager = new AccessManager($this->routeProvider, $this->paramConverter, $this->argumentsResolverFactory, $this->currentUser, $this->checkProvider);
|
||||
|
||||
$access_check = $this->getMock('Drupal\Tests\Core\Access\TestAccessCheckInterface');
|
||||
$access_check->expects($this->atLeastOnce())
|
||||
->method('applies')
|
||||
->will($this->returnValue(TRUE));
|
||||
$access_check->expects($this->atLeastOnce())
|
||||
->method('access')
|
||||
->will($this->returnValue(AccessResult::forbidden()));
|
||||
|
||||
$this->container->set('test_access', $access_check);
|
||||
|
||||
$this->checkProvider->addCheckService('test_access', 'access');
|
||||
$this->checkProvider->setChecks($this->routeCollection);
|
||||
|
||||
$this->assertEquals(FALSE, $this->accessManager->checkNamedRoute('test_route_1', array('value' => 'example'), $this->account));
|
||||
$this->assertEquals(AccessResult::forbidden(), $this->accessManager->checkNamedRoute('test_route_1', array('value' => 'example'), $this->account, TRUE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the checkNamedRoute with default values.
|
||||
*
|
||||
* @covers ::checkNamedRoute
|
||||
*/
|
||||
public function testCheckNamedRouteWithDefaultValue() {
|
||||
$this->routeCollection = new RouteCollection();
|
||||
$route = new Route('/test-route-1/{value}', array('value' => 'example'), array('_test_access' => 'TRUE'));
|
||||
$this->routeCollection->add('test_route_1', $route);
|
||||
|
||||
$this->routeProvider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface');
|
||||
$this->routeProvider->expects($this->any())
|
||||
->method('getRouteByName')
|
||||
->with('test_route_1', array())
|
||||
->will($this->returnValue($route));
|
||||
|
||||
$map = array();
|
||||
$map[] = array('test_route_1', array('value' => 'example'), '/test-route-1/example');
|
||||
|
||||
$this->paramConverter = $this->getMock('Drupal\Core\ParamConverter\ParamConverterManagerInterface');
|
||||
$this->paramConverter->expects($this->atLeastOnce())
|
||||
->method('convert')
|
||||
->with(array('value' => 'example', RouteObjectInterface::ROUTE_NAME => 'test_route_1', RouteObjectInterface::ROUTE_OBJECT => $route))
|
||||
->will($this->returnValue(array('value' => 'upcasted_value')));
|
||||
|
||||
$this->setupAccessArgumentsResolverFactory($this->exactly(2))
|
||||
->with($this->callback(function ($route_match) {
|
||||
return $route_match->getParameters()->get('value') == 'upcasted_value';
|
||||
}));
|
||||
|
||||
$this->accessManager = new AccessManager($this->routeProvider, $this->paramConverter, $this->argumentsResolverFactory, $this->currentUser, $this->checkProvider);
|
||||
|
||||
$access_check = $this->getMock('Drupal\Tests\Core\Access\TestAccessCheckInterface');
|
||||
$access_check->expects($this->atLeastOnce())
|
||||
->method('applies')
|
||||
->will($this->returnValue(TRUE));
|
||||
$access_check->expects($this->atLeastOnce())
|
||||
->method('access')
|
||||
->will($this->returnValue(AccessResult::forbidden()));
|
||||
|
||||
$this->container->set('test_access', $access_check);
|
||||
|
||||
$this->checkProvider->addCheckService('test_access', 'access');
|
||||
$this->checkProvider->setChecks($this->routeCollection);
|
||||
|
||||
$this->assertEquals(FALSE, $this->accessManager->checkNamedRoute('test_route_1', array(), $this->account));
|
||||
$this->assertEquals(AccessResult::forbidden(), $this->accessManager->checkNamedRoute('test_route_1', array(), $this->account, TRUE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests checkNamedRoute given an invalid/non existing route name.
|
||||
*/
|
||||
public function testCheckNamedRouteWithNonExistingRoute() {
|
||||
$this->routeProvider->expects($this->any())
|
||||
->method('getRouteByName')
|
||||
->will($this->throwException(new RouteNotFoundException()));
|
||||
|
||||
$this->setupAccessChecker();
|
||||
|
||||
$this->assertEquals(FALSE, $this->accessManager->checkNamedRoute('test_route_1', array(), $this->account), 'A non existing route lead to access.');
|
||||
$this->assertEquals(AccessResult::forbidden()->addCacheTags(['config:core.extension']), $this->accessManager->checkNamedRoute('test_route_1', array(), $this->account, TRUE), 'A non existing route lead to access.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that an access checker throws an exception for not allowed values.
|
||||
*
|
||||
* @dataProvider providerCheckException
|
||||
*
|
||||
* @expectedException \Drupal\Core\Access\AccessException
|
||||
*/
|
||||
public function testCheckException($return_value) {
|
||||
$route_provider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface');
|
||||
|
||||
// Setup a test route for each access configuration.
|
||||
$requirements = array(
|
||||
'_test_incorrect_value' => 'TRUE',
|
||||
);
|
||||
$options = array(
|
||||
'_access_checks' => array(
|
||||
'test_incorrect_value',
|
||||
),
|
||||
);
|
||||
$route = new Route('', array(), $requirements, $options);
|
||||
|
||||
$route_provider->expects($this->any())
|
||||
->method('getRouteByName')
|
||||
->will($this->returnValue($route));
|
||||
|
||||
$this->paramConverter = $this->getMock('Drupal\Core\ParamConverter\ParamConverterManagerInterface');
|
||||
$this->paramConverter->expects($this->any())
|
||||
->method('convert')
|
||||
->will($this->returnValue(array()));
|
||||
|
||||
$this->setupAccessArgumentsResolverFactory();
|
||||
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
// Register a service that will return an incorrect value.
|
||||
$access_check = $this->getMock('Drupal\Tests\Core\Access\TestAccessCheckInterface');
|
||||
$access_check->expects($this->any())
|
||||
->method('access')
|
||||
->will($this->returnValue($return_value));
|
||||
$container->set('test_incorrect_value', $access_check);
|
||||
|
||||
$access_manager = new AccessManager($route_provider, $this->paramConverter, $this->argumentsResolverFactory, $this->currentUser, $this->checkProvider);
|
||||
$this->checkProvider->setContainer($container);
|
||||
$this->checkProvider->addCheckService('test_incorrect_value', 'access');
|
||||
|
||||
$access_manager->checkNamedRoute('test_incorrect_value', array(), $this->account);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testCheckException.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerCheckException() {
|
||||
return array(
|
||||
array(array(1)),
|
||||
array('string'),
|
||||
array(0),
|
||||
array(1),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a default access check service to the container and the access manager.
|
||||
*/
|
||||
protected function setupAccessChecker() {
|
||||
$access_check = new DefaultAccessCheck();
|
||||
$this->container->register('test_access_default', $access_check);
|
||||
$this->checkProvider->addCheckService('test_access_default', 'access', array('_access'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add default expectations to the access arguments resolver factory.
|
||||
*/
|
||||
protected function setupAccessArgumentsResolverFactory($constraint = NULL) {
|
||||
if (!isset($constraint)) {
|
||||
$constraint = $this->any();
|
||||
}
|
||||
return $this->argumentsResolverFactory->expects($constraint)
|
||||
->method('getArgumentsResolver')
|
||||
->will($this->returnCallback(function ($route_match, $account) {
|
||||
$resolver = $this->getMock('Drupal\Component\Utility\ArgumentsResolverInterface');
|
||||
$resolver->expects($this->any())
|
||||
->method('getArguments')
|
||||
->will($this->returnCallback(function ($callable) use ($route_match) {
|
||||
return array($route_match->getRouteObject());
|
||||
}));
|
||||
return $resolver;
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines an interface with a defined access() method for mocking.
|
||||
*/
|
||||
interface TestAccessCheckInterface extends AccessCheckInterface {
|
||||
public function access();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Core\Access;
|
||||
|
||||
use Drupal\Core\Access\AccessResultForbidden;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Core\Access\AccessResultForbidden
|
||||
* @group Access
|
||||
*/
|
||||
class AccessResultForbiddenTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Tests the construction of an AccessResultForbidden object.
|
||||
*
|
||||
* @covers ::__construct
|
||||
* @covers ::getReason
|
||||
*/
|
||||
public function testConstruction() {
|
||||
|
||||
$a = new AccessResultForbidden();
|
||||
$this->assertEquals(NULL, $a->getReason());
|
||||
|
||||
$reason = $this->getRandomGenerator()->string();
|
||||
$b = new AccessResultForbidden($reason);
|
||||
$this->assertEquals($reason, $b->getReason());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test setReason()
|
||||
*
|
||||
* @covers ::setReason
|
||||
*/
|
||||
public function testSetReason() {
|
||||
$a = new AccessResultForbidden();
|
||||
|
||||
$reason = $this->getRandomGenerator()->string();
|
||||
$return = $a->setReason($reason);
|
||||
|
||||
$this->assertSame($reason, $a->getReason());
|
||||
$this->assertSame($a, $return);
|
||||
}
|
||||
|
||||
}
|
||||
986
web/core/tests/Drupal/Tests/Core/Access/AccessResultTest.php
Normal file
986
web/core/tests/Drupal/Tests/Core/Access/AccessResultTest.php
Normal file
|
|
@ -0,0 +1,986 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\Core\Access\AccessResultTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\Core\Access;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Access\AccessResultInterface;
|
||||
use Drupal\Core\Access\AccessResultNeutral;
|
||||
use Drupal\Core\Access\AccessResultReasonInterface;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\CacheableDependencyInterface;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Core\Access\AccessResult
|
||||
* @group Access
|
||||
*/
|
||||
class AccessResultTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The cache contexts manager.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\Context\CacheContextsManager|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $cacheContextsManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->cacheContextsManager = $this->getMockBuilder('Drupal\Core\Cache\Context\CacheContextsManager')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$this->cacheContextsManager->method('assertValidTokens')->willReturn(TRUE);
|
||||
$container = new ContainerBuilder();
|
||||
$container->set('cache_contexts_manager', $this->cacheContextsManager);
|
||||
\Drupal::setContainer($container);
|
||||
}
|
||||
|
||||
protected function assertDefaultCacheability(AccessResult $access) {
|
||||
$this->assertSame([], $access->getCacheContexts());
|
||||
$this->assertSame([], $access->getCacheTags());
|
||||
$this->assertSame(Cache::PERMANENT, $access->getCacheMaxAge());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the construction of an AccessResult object.
|
||||
*
|
||||
* @covers ::neutral
|
||||
*/
|
||||
public function testConstruction() {
|
||||
$verify = function (AccessResult $access) {
|
||||
$this->assertFalse($access->isAllowed());
|
||||
$this->assertFalse($access->isForbidden());
|
||||
$this->assertTrue($access->isNeutral());
|
||||
$this->assertDefaultCacheability($access);
|
||||
};
|
||||
|
||||
// Verify the object when using the constructor.
|
||||
$a = new AccessResultNeutral();
|
||||
$verify($a);
|
||||
|
||||
// Verify the object when using the ::create() convenience method.
|
||||
$b = AccessResult::neutral();
|
||||
$verify($b);
|
||||
|
||||
$this->assertEquals($a, $b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::allowed
|
||||
* @covers ::isAllowed
|
||||
* @covers ::isForbidden
|
||||
* @covers ::isNeutral
|
||||
*/
|
||||
public function testAccessAllowed() {
|
||||
$verify = function (AccessResult $access) {
|
||||
$this->assertTrue($access->isAllowed());
|
||||
$this->assertFalse($access->isForbidden());
|
||||
$this->assertFalse($access->isNeutral());
|
||||
$this->assertDefaultCacheability($access);
|
||||
};
|
||||
|
||||
// Verify the object when using the ::allowed() convenience static method.
|
||||
$b = AccessResult::allowed();
|
||||
$verify($b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::forbidden
|
||||
* @covers ::isAllowed
|
||||
* @covers ::isForbidden
|
||||
* @covers ::isNeutral
|
||||
*/
|
||||
public function testAccessForbidden() {
|
||||
$verify = function (AccessResult $access) {
|
||||
$this->assertFalse($access->isAllowed());
|
||||
$this->assertTrue($access->isForbidden());
|
||||
$this->assertFalse($access->isNeutral());
|
||||
$this->assertDefaultCacheability($access);
|
||||
};
|
||||
|
||||
// Verify the object when using the ::forbidden() convenience static method.
|
||||
$b = AccessResult::forbidden();
|
||||
$verify($b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::forbidden
|
||||
*/
|
||||
public function testAccessForbiddenReason() {
|
||||
$verify = function (AccessResult $access, $reason) {
|
||||
$this->assertInstanceOf(AccessResultReasonInterface::class, $access);
|
||||
$this->assertSame($reason, $access->getReason());
|
||||
};
|
||||
|
||||
$b = AccessResult::forbidden();
|
||||
$verify($b, NULL);
|
||||
|
||||
$reason = $this->getRandomGenerator()->string();
|
||||
$b = AccessResult::forbidden($reason);
|
||||
$verify($b, $reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::allowedIf
|
||||
* @covers ::isAllowed
|
||||
* @covers ::isForbidden
|
||||
* @covers ::isNeutral
|
||||
*/
|
||||
public function testAccessConditionallyAllowed() {
|
||||
$verify = function (AccessResult $access, $allowed) {
|
||||
$this->assertSame($allowed, $access->isAllowed());
|
||||
$this->assertFalse($access->isForbidden());
|
||||
$this->assertSame(!$allowed, $access->isNeutral());
|
||||
$this->assertDefaultCacheability($access);
|
||||
};
|
||||
|
||||
$b1 = AccessResult::allowedIf(TRUE);
|
||||
$verify($b1, TRUE);
|
||||
$b2 = AccessResult::allowedIf(FALSE);
|
||||
$verify($b2, FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::forbiddenIf
|
||||
* @covers ::isAllowed
|
||||
* @covers ::isForbidden
|
||||
* @covers ::isNeutral
|
||||
*/
|
||||
public function testAccessConditionallyForbidden() {
|
||||
$verify = function (AccessResult $access, $forbidden) {
|
||||
$this->assertFalse($access->isAllowed());
|
||||
$this->assertSame($forbidden, $access->isForbidden());
|
||||
$this->assertSame(!$forbidden, $access->isNeutral());
|
||||
$this->assertDefaultCacheability($access);
|
||||
};
|
||||
|
||||
$b1 = AccessResult::forbiddenIf(TRUE);
|
||||
$verify($b1, TRUE);
|
||||
$b2 = AccessResult::forbiddenIf(FALSE);
|
||||
$verify($b2, FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::andIf
|
||||
*/
|
||||
public function testAndIf() {
|
||||
$neutral = AccessResult::neutral();
|
||||
$allowed = AccessResult::allowed();
|
||||
$forbidden = AccessResult::forbidden();
|
||||
$unused_access_result_due_to_lazy_evaluation = $this->getMock('\Drupal\Core\Access\AccessResultInterface');
|
||||
$unused_access_result_due_to_lazy_evaluation->expects($this->never())
|
||||
->method($this->anything());
|
||||
|
||||
// ALLOWED && ALLOWED === ALLOWED.
|
||||
$access = $allowed->andIf($allowed);
|
||||
$this->assertTrue($access->isAllowed());
|
||||
$this->assertFalse($access->isForbidden());
|
||||
$this->assertFalse($access->isNeutral());
|
||||
$this->assertDefaultCacheability($access);
|
||||
|
||||
// ALLOWED && NEUTRAL === NEUTRAL.
|
||||
$access = $allowed->andIf($neutral);
|
||||
$this->assertFalse($access->isAllowed());
|
||||
$this->assertFalse($access->isForbidden());
|
||||
$this->assertTrue($access->isNeutral());
|
||||
$this->assertDefaultCacheability($access);
|
||||
|
||||
// ALLOWED && FORBIDDEN === FORBIDDEN.
|
||||
$access = $allowed->andIf($forbidden);
|
||||
$this->assertFalse($access->isAllowed());
|
||||
$this->assertTrue($access->isForbidden());
|
||||
$this->assertFalse($access->isNeutral());
|
||||
$this->assertDefaultCacheability($access);
|
||||
|
||||
// NEUTRAL && ALLOW == NEUTRAL
|
||||
$access = $neutral->andIf($allowed);
|
||||
$this->assertFalse($access->isAllowed());
|
||||
$this->assertFalse($access->isForbidden());
|
||||
$this->assertTrue($access->isNeutral());
|
||||
$this->assertDefaultCacheability($access);
|
||||
|
||||
// NEUTRAL && NEUTRAL === NEUTRAL.
|
||||
$access = $neutral->andIf($neutral);
|
||||
$this->assertFalse($access->isAllowed());
|
||||
$this->assertFalse($access->isForbidden());
|
||||
$this->assertTrue($access->isNeutral());
|
||||
$this->assertDefaultCacheability($access);
|
||||
|
||||
// NEUTRAL && FORBIDDEN === FORBIDDEN.
|
||||
$access = $neutral->andIf($forbidden);
|
||||
$this->assertFalse($access->isAllowed());
|
||||
$this->assertTrue($access->isForbidden());
|
||||
$this->assertFalse($access->isNeutral());
|
||||
$this->assertDefaultCacheability($access);
|
||||
|
||||
// FORBIDDEN && ALLOWED = FORBIDDEN
|
||||
$access = $forbidden->andif($allowed);
|
||||
$this->assertFalse($access->isAllowed());
|
||||
$this->assertTrue($access->isForbidden());
|
||||
$this->assertFalse($access->isNeutral());
|
||||
$this->assertDefaultCacheability($access);
|
||||
|
||||
// FORBIDDEN && NEUTRAL = FORBIDDEN
|
||||
$access = $forbidden->andif($neutral);
|
||||
$this->assertFalse($access->isAllowed());
|
||||
$this->assertTrue($access->isForbidden());
|
||||
$this->assertFalse($access->isNeutral());
|
||||
$this->assertDefaultCacheability($access);
|
||||
|
||||
// FORBIDDEN && FORBIDDEN = FORBIDDEN
|
||||
$access = $forbidden->andif($forbidden);
|
||||
$this->assertFalse($access->isAllowed());
|
||||
$this->assertTrue($access->isForbidden());
|
||||
$this->assertFalse($access->isNeutral());
|
||||
$this->assertDefaultCacheability($access);
|
||||
|
||||
// FORBIDDEN && * === FORBIDDEN: lazy evaluation verification.
|
||||
$access = $forbidden->andIf($unused_access_result_due_to_lazy_evaluation);
|
||||
$this->assertFalse($access->isAllowed());
|
||||
$this->assertTrue($access->isForbidden());
|
||||
$this->assertFalse($access->isNeutral());
|
||||
$this->assertDefaultCacheability($access);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::orIf
|
||||
*/
|
||||
public function testOrIf() {
|
||||
$neutral = AccessResult::neutral();
|
||||
$allowed = AccessResult::allowed();
|
||||
$forbidden = AccessResult::forbidden();
|
||||
$unused_access_result_due_to_lazy_evaluation = $this->getMock('\Drupal\Core\Access\AccessResultInterface');
|
||||
$unused_access_result_due_to_lazy_evaluation->expects($this->never())
|
||||
->method($this->anything());
|
||||
|
||||
// ALLOWED || ALLOWED === ALLOWED.
|
||||
$access = $allowed->orIf($allowed);
|
||||
$this->assertTrue($access->isAllowed());
|
||||
$this->assertFalse($access->isForbidden());
|
||||
$this->assertFalse($access->isNeutral());
|
||||
$this->assertDefaultCacheability($access);
|
||||
|
||||
// ALLOWED || NEUTRAL === ALLOWED.
|
||||
$access = $allowed->orIf($neutral);
|
||||
$this->assertTrue($access->isAllowed());
|
||||
$this->assertFalse($access->isForbidden());
|
||||
$this->assertFalse($access->isNeutral());
|
||||
$this->assertDefaultCacheability($access);
|
||||
|
||||
// ALLOWED || FORBIDDEN === FORBIDDEN.
|
||||
$access = $allowed->orIf($forbidden);
|
||||
$this->assertFalse($access->isAllowed());
|
||||
$this->assertTrue($access->isForbidden());
|
||||
$this->assertFalse($access->isNeutral());
|
||||
$this->assertDefaultCacheability($access);
|
||||
|
||||
// NEUTRAL || NEUTRAL === NEUTRAL.
|
||||
$access = $neutral->orIf($neutral);
|
||||
$this->assertFalse($access->isAllowed());
|
||||
$this->assertFalse($access->isForbidden());
|
||||
$this->assertTrue($access->isNeutral());
|
||||
$this->assertDefaultCacheability($access);
|
||||
|
||||
// NEUTRAL || ALLOWED === ALLOWED.
|
||||
$access = $neutral->orIf($allowed);
|
||||
$this->assertTrue($access->isAllowed());
|
||||
$this->assertFalse($access->isForbidden());
|
||||
$this->assertFalse($access->isNeutral());
|
||||
$this->assertDefaultCacheability($access);
|
||||
|
||||
// NEUTRAL || FORBIDDEN === FORBIDDEN.
|
||||
$access = $neutral->orIf($forbidden);
|
||||
$this->assertFalse($access->isAllowed());
|
||||
$this->assertTrue($access->isForbidden());
|
||||
$this->assertFalse($access->isNeutral());
|
||||
$this->assertDefaultCacheability($access);
|
||||
|
||||
// FORBIDDEN || ALLOWED === FORBIDDEN.
|
||||
$access = $forbidden->orIf($allowed);
|
||||
$this->assertFalse($access->isAllowed());
|
||||
$this->assertTrue($access->isForbidden());
|
||||
$this->assertFalse($access->isNeutral());
|
||||
$this->assertDefaultCacheability($access);
|
||||
|
||||
// FORBIDDEN || NEUTRAL === FORBIDDEN.
|
||||
$access = $forbidden->orIf($allowed);
|
||||
$this->assertFalse($access->isAllowed());
|
||||
$this->assertTrue($access->isForbidden());
|
||||
$this->assertFalse($access->isNeutral());
|
||||
$this->assertDefaultCacheability($access);
|
||||
|
||||
// FORBIDDEN || FORBIDDEN === FORBIDDEN.
|
||||
$access = $forbidden->orIf($allowed);
|
||||
$this->assertFalse($access->isAllowed());
|
||||
$this->assertTrue($access->isForbidden());
|
||||
$this->assertFalse($access->isNeutral());
|
||||
$this->assertDefaultCacheability($access);
|
||||
|
||||
// FORBIDDEN || * === FORBIDDEN.
|
||||
$access = $forbidden->orIf($unused_access_result_due_to_lazy_evaluation);
|
||||
$this->assertFalse($access->isAllowed());
|
||||
$this->assertTrue($access->isForbidden());
|
||||
$this->assertFalse($access->isNeutral());
|
||||
$this->assertDefaultCacheability($access);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::setCacheMaxAge
|
||||
* @covers ::getCacheMaxAge
|
||||
*/
|
||||
public function testCacheMaxAge() {
|
||||
$this->assertSame(Cache::PERMANENT, AccessResult::neutral()->getCacheMaxAge());
|
||||
$this->assertSame(1337, AccessResult::neutral()->setCacheMaxAge(1337)->getCacheMaxAge());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::addCacheContexts
|
||||
* @covers ::resetCacheContexts
|
||||
* @covers ::getCacheContexts
|
||||
* @covers ::cachePerPermissions
|
||||
* @covers ::cachePerUser
|
||||
* @covers ::allowedIfHasPermission
|
||||
*/
|
||||
public function testCacheContexts() {
|
||||
$verify = function (AccessResult $access, array $contexts) {
|
||||
$this->assertFalse($access->isAllowed());
|
||||
$this->assertFalse($access->isForbidden());
|
||||
$this->assertTrue($access->isNeutral());
|
||||
$this->assertSame(Cache::PERMANENT, $access->getCacheMaxAge());
|
||||
$this->assertSame($contexts, $access->getCacheContexts());
|
||||
$this->assertSame([], $access->getCacheTags());
|
||||
};
|
||||
|
||||
$access = AccessResult::neutral()->addCacheContexts(['foo']);
|
||||
$verify($access, ['foo']);
|
||||
// Verify resetting works.
|
||||
$access->resetCacheContexts();
|
||||
$verify($access, []);
|
||||
// Verify idempotency.
|
||||
$access->addCacheContexts(['foo'])
|
||||
->addCacheContexts(['foo']);
|
||||
$verify($access, ['foo']);
|
||||
// Verify same values in different call order yields the same result.
|
||||
$access->resetCacheContexts()
|
||||
->addCacheContexts(['foo'])
|
||||
->addCacheContexts(['bar']);
|
||||
$verify($access, ['bar', 'foo']);
|
||||
$access->resetCacheContexts()
|
||||
->addCacheContexts(['bar'])
|
||||
->addCacheContexts(['foo']);
|
||||
$verify($access, ['bar', 'foo']);
|
||||
|
||||
// ::cachePerPermissions() convenience method.
|
||||
$contexts = array('user.permissions');
|
||||
$a = AccessResult::neutral()->addCacheContexts($contexts);
|
||||
$verify($a, $contexts);
|
||||
$b = AccessResult::neutral()->cachePerPermissions();
|
||||
$verify($b, $contexts);
|
||||
$this->assertEquals($a, $b);
|
||||
|
||||
// ::cachePerUser() convenience method.
|
||||
$contexts = array('user');
|
||||
$a = AccessResult::neutral()->addCacheContexts($contexts);
|
||||
$verify($a, $contexts);
|
||||
$b = AccessResult::neutral()->cachePerUser();
|
||||
$verify($b, $contexts);
|
||||
$this->assertEquals($a, $b);
|
||||
|
||||
// Both.
|
||||
$contexts = array('user', 'user.permissions');
|
||||
$a = AccessResult::neutral()->addCacheContexts($contexts);
|
||||
$verify($a, $contexts);
|
||||
$b = AccessResult::neutral()->cachePerPermissions()->cachePerUser();
|
||||
$verify($b, $contexts);
|
||||
$c = AccessResult::neutral()->cachePerUser()->cachePerPermissions();
|
||||
$verify($c, $contexts);
|
||||
$this->assertEquals($a, $b);
|
||||
$this->assertEquals($a, $c);
|
||||
|
||||
// ::allowIfHasPermission and ::allowedIfHasPermission convenience methods.
|
||||
$account = $this->getMock('\Drupal\Core\Session\AccountInterface');
|
||||
$account->expects($this->any())
|
||||
->method('hasPermission')
|
||||
->with('may herd llamas')
|
||||
->will($this->returnValue(FALSE));
|
||||
$contexts = array('user.permissions');
|
||||
|
||||
// Verify the object when using the ::allowedIfHasPermission() convenience
|
||||
// static method.
|
||||
$b = AccessResult::allowedIfHasPermission($account, 'may herd llamas');
|
||||
$verify($b, $contexts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::addCacheTags
|
||||
* @covers ::addCacheableDependency
|
||||
* @covers ::getCacheTags
|
||||
* @covers ::resetCacheTags
|
||||
*/
|
||||
public function testCacheTags() {
|
||||
$verify = function (AccessResult $access, array $tags, array $contexts = [], $max_age = Cache::PERMANENT) {
|
||||
$this->assertFalse($access->isAllowed());
|
||||
$this->assertFalse($access->isForbidden());
|
||||
$this->assertTrue($access->isNeutral());
|
||||
$this->assertSame($max_age, $access->getCacheMaxAge());
|
||||
$this->assertSame($contexts, $access->getCacheContexts());
|
||||
$this->assertSame($tags, $access->getCacheTags());
|
||||
};
|
||||
|
||||
$access = AccessResult::neutral()->addCacheTags(['foo:bar']);
|
||||
$verify($access, ['foo:bar']);
|
||||
// Verify resetting works.
|
||||
$access->resetCacheTags();
|
||||
$verify($access, []);
|
||||
// Verify idempotency.
|
||||
$access->addCacheTags(['foo:bar'])
|
||||
->addCacheTags(['foo:bar']);
|
||||
$verify($access, ['foo:bar']);
|
||||
// Verify same values in different call order yields the same result.
|
||||
$access->resetCacheTags()
|
||||
->addCacheTags(['bar:baz'])
|
||||
->addCacheTags(['bar:qux'])
|
||||
->addCacheTags(['foo:bar'])
|
||||
->addCacheTags(['foo:baz']);
|
||||
$verify($access, ['bar:baz', 'bar:qux', 'foo:bar', 'foo:baz']);
|
||||
$access->resetCacheTags()
|
||||
->addCacheTags(['foo:bar'])
|
||||
->addCacheTags(['bar:qux'])
|
||||
->addCacheTags(['foo:baz'])
|
||||
->addCacheTags(['bar:baz']);
|
||||
$verify($access, ['bar:baz', 'bar:qux', 'foo:bar', 'foo:baz']);
|
||||
|
||||
// ::addCacheableDependency() convenience method.
|
||||
$node = $this->getMock('\Drupal\node\NodeInterface');
|
||||
$node->expects($this->any())
|
||||
->method('getCacheTags')
|
||||
->will($this->returnValue(array('node:20011988')));
|
||||
$node->expects($this->any())
|
||||
->method('getCacheMaxAge')
|
||||
->willReturn(600);
|
||||
$node->expects($this->any())
|
||||
->method('getCacheContexts')
|
||||
->willReturn(['user']);
|
||||
$tags = array('node:20011988');
|
||||
$a = AccessResult::neutral()->addCacheTags($tags);
|
||||
$verify($a, $tags);
|
||||
$b = AccessResult::neutral()->addCacheableDependency($node);
|
||||
$verify($b, $tags, ['user'], 600);
|
||||
|
||||
$non_cacheable_dependency = new \stdClass();
|
||||
$non_cacheable = AccessResult::neutral()->addCacheableDependency($non_cacheable_dependency);
|
||||
$verify($non_cacheable, [], [], 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::inheritCacheability
|
||||
*/
|
||||
public function testInheritCacheability() {
|
||||
// andIf(); 1st has defaults, 2nd has custom tags, contexts and max-age.
|
||||
$access = AccessResult::allowed();
|
||||
$other = AccessResult::allowed()->setCacheMaxAge(1500)->cachePerPermissions()->addCacheTags(['node:20011988']);
|
||||
$this->assertTrue($access->inheritCacheability($other) instanceof AccessResult);
|
||||
$this->assertSame(['user.permissions'], $access->getCacheContexts());
|
||||
$this->assertSame(['node:20011988'], $access->getCacheTags());
|
||||
$this->assertSame(1500, $access->getCacheMaxAge());
|
||||
|
||||
// andIf(); 1st has custom tags, max-age, 2nd has custom contexts and max-age.
|
||||
$access = AccessResult::allowed()->cachePerUser()->setCacheMaxAge(43200);
|
||||
$other = AccessResult::forbidden()->addCacheTags(['node:14031991'])->setCacheMaxAge(86400);
|
||||
$this->assertTrue($access->inheritCacheability($other) instanceof AccessResult);
|
||||
$this->assertSame(['user'], $access->getCacheContexts());
|
||||
$this->assertSame(['node:14031991'], $access->getCacheTags());
|
||||
$this->assertSame(43200, $access->getCacheMaxAge());
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a list of access result pairs and operations to test.
|
||||
*
|
||||
* This tests the propagation of cacheability metadata. Rather than testing
|
||||
* every single bit of cacheability metadata, which would lead to a mind-
|
||||
* boggling number of permutations, in this test, we only consider the
|
||||
* permutations of all pairs of the following set:
|
||||
* - Allowed, implements CDI and is cacheable.
|
||||
* - Allowed, implements CDI and is not cacheable.
|
||||
* - Allowed, does not implement CDI (hence not cacheable).
|
||||
* - Forbidden, implements CDI and is cacheable.
|
||||
* - Forbidden, implements CDI and is not cacheable.
|
||||
* - Forbidden, does not implement CDI (hence not cacheable).
|
||||
* - Neutral, implements CDI and is cacheable.
|
||||
* - Neutral, implements CDI and is not cacheable.
|
||||
* - Neutral, does not implement CDI (hence not cacheable).
|
||||
*
|
||||
* (Where "CDI" is CacheableDependencyInterface.)
|
||||
*
|
||||
* This leads to 72 permutations (9!/(9-2)! = 9*8 = 72) per operation. There
|
||||
* are two operations to test (AND and OR), so that leads to a grand total of
|
||||
* 144 permutations, all of which are tested.
|
||||
*
|
||||
* There are two "contagious" patterns:
|
||||
* - Any operation with a forbidden access result yields a forbidden result.
|
||||
* This therefore also applies to the cacheability metadata associated with
|
||||
* a forbidden result. This is the case for bullets 4, 5 and 6 in the set
|
||||
* above.
|
||||
* - Any operation yields an access result object that is of the same class
|
||||
* (implementation) as the first operand. This is because operations are
|
||||
* invoked on the first operand. Therefore, if the first implementation
|
||||
* does not implement CacheableDependencyInterface, then the result won't
|
||||
* either. This is the case for bullets 3, 6 and 9 in the set above.
|
||||
*/
|
||||
public function andOrCacheabilityPropagationProvider() {
|
||||
// ct: cacheable=true, cf: cacheable=false, un: uncacheable.
|
||||
// Note: the test cases that have a "un" access result as the first operand
|
||||
// test UncacheableTestAccessResult, not AccessResult. However, we
|
||||
// definitely want to verify that AccessResult's orIf() and andIf() methods
|
||||
// work correctly when given an AccessResultInterface implementation that
|
||||
// does not implement CacheableDependencyInterface, and we want to test the
|
||||
// full gamut of permutations, so that's not a problem.
|
||||
$allowed_ct = AccessResult::allowed();
|
||||
$allowed_cf = AccessResult::allowed()->setCacheMaxAge(0);
|
||||
$allowed_un = new UncacheableTestAccessResult('ALLOWED');
|
||||
$forbidden_ct = AccessResult::forbidden();
|
||||
$forbidden_cf = AccessResult::forbidden()->setCacheMaxAge(0);
|
||||
$forbidden_un = new UncacheableTestAccessResult('FORBIDDEN');
|
||||
$neutral_ct = AccessResult::neutral();
|
||||
$neutral_cf = AccessResult::neutral()->setCacheMaxAge(0);
|
||||
$neutral_un = new UncacheableTestAccessResult('NEUTRAL');
|
||||
|
||||
// Structure:
|
||||
// - First column: first access result.
|
||||
// - Second column: operator ('OR' or 'AND').
|
||||
// - Third column: second access result.
|
||||
// - Fourth column: whether result implements CacheableDependencyInterface
|
||||
// - Fifth column: whether the result is cacheable (if column 4 is TRUE)
|
||||
return [
|
||||
// Allowed (ct) OR allowed (ct,cf,un).
|
||||
[$allowed_ct, 'OR', $allowed_ct, TRUE, TRUE],
|
||||
[$allowed_ct, 'OR', $allowed_cf, TRUE, TRUE],
|
||||
[$allowed_ct, 'OR', $allowed_un, TRUE, TRUE],
|
||||
// Allowed (cf) OR allowed (ct,cf,un).
|
||||
[$allowed_cf, 'OR', $allowed_ct, TRUE, TRUE],
|
||||
[$allowed_cf, 'OR', $allowed_cf, TRUE, FALSE],
|
||||
[$allowed_cf, 'OR', $allowed_un, TRUE, FALSE],
|
||||
// Allowed (un) OR allowed (ct,cf,un).
|
||||
[$allowed_un, 'OR', $allowed_ct, FALSE, NULL],
|
||||
[$allowed_un, 'OR', $allowed_cf, FALSE, NULL],
|
||||
[$allowed_un, 'OR', $allowed_un, FALSE, NULL],
|
||||
|
||||
// Allowed (ct) OR forbidden (ct,cf,un).
|
||||
[$allowed_ct, 'OR', $forbidden_ct, TRUE, TRUE],
|
||||
[$allowed_ct, 'OR', $forbidden_cf, TRUE, FALSE],
|
||||
[$allowed_ct, 'OR', $forbidden_un, TRUE, FALSE],
|
||||
// Allowed (cf) OR forbidden (ct,cf,un).
|
||||
[$allowed_cf, 'OR', $forbidden_ct, TRUE, TRUE],
|
||||
[$allowed_cf, 'OR', $forbidden_cf, TRUE, FALSE],
|
||||
[$allowed_cf, 'OR', $forbidden_un, TRUE, FALSE],
|
||||
// Allowed (un) OR forbidden (ct,cf,un).
|
||||
[$allowed_un, 'OR', $forbidden_ct, FALSE, NULL],
|
||||
[$allowed_un, 'OR', $forbidden_cf, FALSE, NULL],
|
||||
[$allowed_un, 'OR', $forbidden_un, FALSE, NULL],
|
||||
|
||||
// Allowed (ct) OR neutral (ct,cf,un).
|
||||
[$allowed_ct, 'OR', $neutral_ct, TRUE, TRUE],
|
||||
[$allowed_ct, 'OR', $neutral_cf, TRUE, TRUE],
|
||||
[$allowed_ct, 'OR', $neutral_un, TRUE, TRUE],
|
||||
// Allowed (cf) OR neutral (ct,cf,un).
|
||||
[$allowed_cf, 'OR', $neutral_ct, TRUE, FALSE],
|
||||
[$allowed_cf, 'OR', $neutral_cf, TRUE, FALSE],
|
||||
[$allowed_cf, 'OR', $neutral_un, TRUE, FALSE],
|
||||
// Allowed (un) OR neutral (ct,cf,un).
|
||||
[$allowed_un, 'OR', $neutral_ct, FALSE, NULL],
|
||||
[$allowed_un, 'OR', $neutral_cf, FALSE, NULL],
|
||||
[$allowed_un, 'OR', $neutral_un, FALSE, NULL],
|
||||
|
||||
|
||||
// Forbidden (ct) OR allowed (ct,cf,un).
|
||||
[$forbidden_ct, 'OR', $allowed_ct, TRUE, TRUE],
|
||||
[$forbidden_ct, 'OR', $allowed_cf, TRUE, TRUE],
|
||||
[$forbidden_ct, 'OR', $allowed_un, TRUE, TRUE],
|
||||
// Forbidden (cf) OR allowed (ct,cf,un).
|
||||
[$forbidden_cf, 'OR', $allowed_ct, TRUE, FALSE],
|
||||
[$forbidden_cf, 'OR', $allowed_cf, TRUE, FALSE],
|
||||
[$forbidden_cf, 'OR', $allowed_un, TRUE, FALSE],
|
||||
// Forbidden (un) OR allowed (ct,cf,un).
|
||||
[$forbidden_un, 'OR', $allowed_ct, FALSE, NULL],
|
||||
[$forbidden_un, 'OR', $allowed_cf, FALSE, NULL],
|
||||
[$forbidden_un, 'OR', $allowed_un, FALSE, NULL],
|
||||
|
||||
// Forbidden (ct) OR neutral (ct,cf,un).
|
||||
[$forbidden_ct, 'OR', $neutral_ct, TRUE, TRUE],
|
||||
[$forbidden_ct, 'OR', $neutral_cf, TRUE, TRUE],
|
||||
[$forbidden_ct, 'OR', $neutral_un, TRUE, TRUE],
|
||||
// Forbidden (cf) OR neutral (ct,cf,un).
|
||||
[$forbidden_cf, 'OR', $neutral_ct, TRUE, FALSE],
|
||||
[$forbidden_cf, 'OR', $neutral_cf, TRUE, FALSE],
|
||||
[$forbidden_cf, 'OR', $neutral_un, TRUE, FALSE],
|
||||
// Forbidden (un) OR neutral (ct,cf,un).
|
||||
[$forbidden_un, 'OR', $neutral_ct, FALSE, NULL],
|
||||
[$forbidden_un, 'OR', $neutral_cf, FALSE, NULL],
|
||||
[$forbidden_un, 'OR', $neutral_un, FALSE, NULL],
|
||||
|
||||
// Forbidden (ct) OR forbidden (ct,cf,un).
|
||||
[$forbidden_ct, 'OR', $forbidden_ct, TRUE, TRUE],
|
||||
[$forbidden_ct, 'OR', $forbidden_cf, TRUE, TRUE],
|
||||
[$forbidden_ct, 'OR', $forbidden_un, TRUE, TRUE],
|
||||
// Forbidden (cf) OR forbidden (ct,cf,un).
|
||||
[$forbidden_cf, 'OR', $forbidden_ct, TRUE, TRUE],
|
||||
[$forbidden_cf, 'OR', $forbidden_cf, TRUE, FALSE],
|
||||
[$forbidden_cf, 'OR', $forbidden_un, TRUE, FALSE],
|
||||
// Forbidden (un) OR forbidden (ct,cf,un).
|
||||
[$forbidden_un, 'OR', $forbidden_ct, FALSE, NULL],
|
||||
[$forbidden_un, 'OR', $forbidden_cf, FALSE, NULL],
|
||||
[$forbidden_un, 'OR', $forbidden_un, FALSE, NULL],
|
||||
|
||||
|
||||
// Neutral (ct) OR allowed (ct,cf,un).
|
||||
[$neutral_ct, 'OR', $allowed_ct, TRUE, TRUE],
|
||||
[$neutral_ct, 'OR', $allowed_cf, TRUE, FALSE],
|
||||
[$neutral_ct, 'OR', $allowed_un, TRUE, FALSE],
|
||||
// Neutral (cf) OR allowed (ct,cf,un).
|
||||
[$neutral_cf, 'OR', $allowed_ct, TRUE, TRUE],
|
||||
[$neutral_cf, 'OR', $allowed_cf, TRUE, FALSE],
|
||||
[$neutral_cf, 'OR', $allowed_un, TRUE, FALSE],
|
||||
// Neutral (un) OR allowed (ct,cf,un).
|
||||
[$neutral_un, 'OR', $allowed_ct, FALSE, NULL],
|
||||
[$neutral_un, 'OR', $allowed_cf, FALSE, NULL],
|
||||
[$neutral_un, 'OR', $allowed_un, FALSE, NULL],
|
||||
|
||||
// Neutral (ct) OR neutral (ct,cf,un).
|
||||
[$neutral_ct, 'OR', $neutral_ct, TRUE, TRUE],
|
||||
[$neutral_ct, 'OR', $neutral_cf, TRUE, TRUE],
|
||||
[$neutral_ct, 'OR', $neutral_un, TRUE, TRUE],
|
||||
// Neutral (cf) OR neutral (ct,cf,un).
|
||||
[$neutral_cf, 'OR', $neutral_ct, TRUE, TRUE],
|
||||
[$neutral_cf, 'OR', $neutral_cf, TRUE, FALSE],
|
||||
[$neutral_cf, 'OR', $neutral_un, TRUE, FALSE],
|
||||
// Neutral (un) OR neutral (ct,cf,un).
|
||||
[$neutral_un, 'OR', $neutral_ct, FALSE, NULL],
|
||||
[$neutral_un, 'OR', $neutral_cf, FALSE, NULL],
|
||||
[$neutral_un, 'OR', $neutral_un, FALSE, NULL],
|
||||
|
||||
// Neutral (ct) OR forbidden (ct,cf,un).
|
||||
[$neutral_ct, 'OR', $forbidden_ct, TRUE, TRUE],
|
||||
[$neutral_ct, 'OR', $forbidden_cf, TRUE, FALSE],
|
||||
[$neutral_ct, 'OR', $forbidden_un, TRUE, FALSE],
|
||||
// Neutral (cf) OR forbidden (ct,cf,un).
|
||||
[$neutral_cf, 'OR', $forbidden_ct, TRUE, TRUE],
|
||||
[$neutral_cf, 'OR', $forbidden_cf, TRUE, FALSE],
|
||||
[$neutral_cf, 'OR', $forbidden_un, TRUE, FALSE],
|
||||
// Neutral (un) OR forbidden (ct,cf,un).
|
||||
[$neutral_un, 'OR', $forbidden_ct, FALSE, NULL],
|
||||
[$neutral_un, 'OR', $forbidden_cf, FALSE, NULL],
|
||||
[$neutral_un, 'OR', $forbidden_un, FALSE, NULL],
|
||||
|
||||
|
||||
|
||||
|
||||
// Allowed (ct) AND allowed (ct,cf,un).
|
||||
[$allowed_ct, 'AND', $allowed_ct, TRUE, TRUE],
|
||||
[$allowed_ct, 'AND', $allowed_cf, TRUE, FALSE],
|
||||
[$allowed_ct, 'AND', $allowed_un, TRUE, FALSE],
|
||||
// Allowed (cf) AND allowed (ct,cf,un).
|
||||
[$allowed_cf, 'AND', $allowed_ct, TRUE, FALSE],
|
||||
[$allowed_cf, 'AND', $allowed_cf, TRUE, FALSE],
|
||||
[$allowed_cf, 'AND', $allowed_un, TRUE, FALSE],
|
||||
// Allowed (un) AND allowed (ct,cf,un).
|
||||
[$allowed_un, 'AND', $allowed_ct, FALSE, NULL],
|
||||
[$allowed_un, 'AND', $allowed_cf, FALSE, NULL],
|
||||
[$allowed_un, 'AND', $allowed_un, FALSE, NULL],
|
||||
|
||||
// Allowed (ct) AND forbidden (ct,cf,un).
|
||||
[$allowed_ct, 'AND', $forbidden_ct, TRUE, TRUE],
|
||||
[$allowed_ct, 'AND', $forbidden_cf, TRUE, FALSE],
|
||||
[$allowed_ct, 'AND', $forbidden_un, TRUE, FALSE],
|
||||
// Allowed (cf) AND forbidden (ct,cf,un).
|
||||
[$allowed_cf, 'AND', $forbidden_ct, TRUE, TRUE],
|
||||
[$allowed_cf, 'AND', $forbidden_cf, TRUE, FALSE],
|
||||
[$allowed_cf, 'AND', $forbidden_un, TRUE, FALSE],
|
||||
// Allowed (un) AND forbidden (ct,cf,un).
|
||||
[$allowed_un, 'AND', $forbidden_ct, FALSE, NULL],
|
||||
[$allowed_un, 'AND', $forbidden_cf, FALSE, NULL],
|
||||
[$allowed_un, 'AND', $forbidden_un, FALSE, NULL],
|
||||
|
||||
// Allowed (ct) AND neutral (ct,cf,un).
|
||||
[$allowed_ct, 'AND', $neutral_ct, TRUE, TRUE],
|
||||
[$allowed_ct, 'AND', $neutral_cf, TRUE, FALSE],
|
||||
[$allowed_ct, 'AND', $neutral_un, TRUE, FALSE],
|
||||
// Allowed (cf) AND neutral (ct,cf,un).
|
||||
[$allowed_cf, 'AND', $neutral_ct, TRUE, FALSE],
|
||||
[$allowed_cf, 'AND', $neutral_cf, TRUE, FALSE],
|
||||
[$allowed_cf, 'AND', $neutral_un, TRUE, FALSE],
|
||||
// Allowed (un) AND neutral (ct,cf,un).
|
||||
[$allowed_un, 'AND', $neutral_ct, FALSE, NULL],
|
||||
[$allowed_un, 'AND', $neutral_cf, FALSE, NULL],
|
||||
[$allowed_un, 'AND', $neutral_un, FALSE, NULL],
|
||||
|
||||
|
||||
// Forbidden (ct) AND allowed (ct,cf,un).
|
||||
[$forbidden_ct, 'AND', $allowed_ct, TRUE, TRUE],
|
||||
[$forbidden_ct, 'AND', $allowed_cf, TRUE, TRUE],
|
||||
[$forbidden_ct, 'AND', $allowed_un, TRUE, TRUE],
|
||||
// Forbidden (cf) AND allowed (ct,cf,un).
|
||||
[$forbidden_cf, 'AND', $allowed_ct, TRUE, FALSE],
|
||||
[$forbidden_cf, 'AND', $allowed_cf, TRUE, FALSE],
|
||||
[$forbidden_cf, 'AND', $allowed_un, TRUE, FALSE],
|
||||
// Forbidden (un) AND allowed (ct,cf,un).
|
||||
[$forbidden_un, 'AND', $allowed_ct, FALSE, NULL],
|
||||
[$forbidden_un, 'AND', $allowed_cf, FALSE, NULL],
|
||||
[$forbidden_un, 'AND', $allowed_un, FALSE, NULL],
|
||||
|
||||
// Forbidden (ct) AND neutral (ct,cf,un).
|
||||
[$forbidden_ct, 'AND', $neutral_ct, TRUE, TRUE],
|
||||
[$forbidden_ct, 'AND', $neutral_cf, TRUE, TRUE],
|
||||
[$forbidden_ct, 'AND', $neutral_un, TRUE, TRUE],
|
||||
// Forbidden (cf) AND neutral (ct,cf,un).
|
||||
[$forbidden_cf, 'AND', $neutral_ct, TRUE, FALSE],
|
||||
[$forbidden_cf, 'AND', $neutral_cf, TRUE, FALSE],
|
||||
[$forbidden_cf, 'AND', $neutral_un, TRUE, FALSE],
|
||||
// Forbidden (un) AND neutral (ct,cf,un).
|
||||
[$forbidden_un, 'AND', $neutral_ct, FALSE, NULL],
|
||||
[$forbidden_un, 'AND', $neutral_cf, FALSE, NULL],
|
||||
[$forbidden_un, 'AND', $neutral_un, FALSE, NULL],
|
||||
|
||||
// Forbidden (ct) AND forbidden (ct,cf,un).
|
||||
[$forbidden_ct, 'AND', $forbidden_ct, TRUE, TRUE],
|
||||
[$forbidden_ct, 'AND', $forbidden_cf, TRUE, TRUE],
|
||||
[$forbidden_ct, 'AND', $forbidden_un, TRUE, TRUE],
|
||||
// Forbidden (cf) AND forbidden (ct,cf,un).
|
||||
[$forbidden_cf, 'AND', $forbidden_ct, TRUE, FALSE],
|
||||
[$forbidden_cf, 'AND', $forbidden_cf, TRUE, FALSE],
|
||||
[$forbidden_cf, 'AND', $forbidden_un, TRUE, FALSE],
|
||||
// Forbidden (un) AND forbidden (ct,cf,un).
|
||||
[$forbidden_un, 'AND', $forbidden_ct, FALSE, NULL],
|
||||
[$forbidden_un, 'AND', $forbidden_cf, FALSE, NULL],
|
||||
[$forbidden_un, 'AND', $forbidden_un, FALSE, NULL],
|
||||
|
||||
|
||||
// Neutral (ct) AND allowed (ct,cf,un).
|
||||
[$neutral_ct, 'AND', $allowed_ct, TRUE, TRUE],
|
||||
[$neutral_ct, 'AND', $allowed_cf, TRUE, TRUE],
|
||||
[$neutral_ct, 'AND', $allowed_un, TRUE, TRUE],
|
||||
// Neutral (cf) AND allowed (ct,cf,un).
|
||||
[$neutral_cf, 'AND', $allowed_ct, TRUE, FALSE],
|
||||
[$neutral_cf, 'AND', $allowed_cf, TRUE, FALSE],
|
||||
[$neutral_cf, 'AND', $allowed_un, TRUE, FALSE],
|
||||
// Neutral (un) AND allowed (ct,cf,un).
|
||||
[$neutral_un, 'AND', $allowed_ct, FALSE, NULL],
|
||||
[$neutral_un, 'AND', $allowed_cf, FALSE, NULL],
|
||||
[$neutral_un, 'AND', $allowed_un, FALSE, NULL],
|
||||
|
||||
// Neutral (ct) AND neutral (ct,cf,un).
|
||||
[$neutral_ct, 'AND', $neutral_ct, TRUE, TRUE],
|
||||
[$neutral_ct, 'AND', $neutral_cf, TRUE, TRUE],
|
||||
[$neutral_ct, 'AND', $neutral_un, TRUE, TRUE],
|
||||
// Neutral (cf) AND neutral (ct,cf,un).
|
||||
[$neutral_cf, 'AND', $neutral_ct, TRUE, FALSE],
|
||||
[$neutral_cf, 'AND', $neutral_cf, TRUE, FALSE],
|
||||
[$neutral_cf, 'AND', $neutral_un, TRUE, FALSE],
|
||||
// Neutral (un) AND neutral (ct,cf,un).
|
||||
[$neutral_un, 'AND', $neutral_ct, FALSE, NULL],
|
||||
[$neutral_un, 'AND', $neutral_cf, FALSE, NULL],
|
||||
[$neutral_un, 'AND', $neutral_un, FALSE, NULL],
|
||||
|
||||
// Neutral (ct) AND forbidden (ct,cf,un).
|
||||
[$neutral_ct, 'AND', $forbidden_ct, TRUE, TRUE],
|
||||
[$neutral_ct, 'AND', $forbidden_cf, TRUE, FALSE],
|
||||
[$neutral_ct, 'AND', $forbidden_un, TRUE, FALSE],
|
||||
// Neutral (cf) AND forbidden (ct,cf,un).
|
||||
[$neutral_cf, 'AND', $forbidden_ct, TRUE, TRUE],
|
||||
[$neutral_cf, 'AND', $forbidden_cf, TRUE, FALSE],
|
||||
[$neutral_cf, 'AND', $forbidden_un, TRUE, FALSE],
|
||||
// Neutral (un) AND forbidden (ct,cf,un).
|
||||
[$neutral_un, 'AND', $forbidden_ct, FALSE, NULL],
|
||||
[$neutral_un, 'AND', $forbidden_cf, FALSE, NULL],
|
||||
[$neutral_un, 'AND', $forbidden_un, FALSE, NULL],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::andIf
|
||||
* @covers ::orIf
|
||||
* @covers ::inheritCacheability
|
||||
*
|
||||
* @dataProvider andOrCacheabilityPropagationProvider
|
||||
*/
|
||||
public function testAndOrCacheabilityPropagation(AccessResultInterface $first, $op, AccessResultInterface $second, $implements_cacheable_dependency_interface, $is_cacheable) {
|
||||
if ($op === 'OR') {
|
||||
$result = $first->orIf($second);
|
||||
}
|
||||
elseif ($op === 'AND') {
|
||||
$result = $first->andIf($second);
|
||||
}
|
||||
else {
|
||||
throw new \LogicException('Invalid operator specified');
|
||||
}
|
||||
if ($implements_cacheable_dependency_interface) {
|
||||
$this->assertTrue($result instanceof CacheableDependencyInterface, 'Result is an instance of CacheableDependencyInterface.');
|
||||
if ($result instanceof CacheableDependencyInterface) {
|
||||
$this->assertSame($is_cacheable, $result->getCacheMaxAge() !== 0, 'getCacheMaxAge() matches expectations.');
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->assertFalse($result instanceof CacheableDependencyInterface, 'Result is not an instance of CacheableDependencyInterface.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::orIf
|
||||
*
|
||||
* Tests the special case of ORing non-forbidden access results that are both
|
||||
* cacheable but have different cacheability metadata.
|
||||
* This is only the case for non-forbidden access results; we still abort the
|
||||
* ORing process as soon as a forbidden access result is encountered. This is
|
||||
* tested in ::testOrIf().
|
||||
*/
|
||||
public function testOrIfCacheabilityMerging() {
|
||||
$merge_both_directions = function(AccessResult $a, AccessResult $b) {
|
||||
// A globally cacheable access result.
|
||||
$a->setCacheMaxAge(3600);
|
||||
// Another access result that is cacheable per permissions.
|
||||
$b->setCacheMaxAge(86400)->cachePerPermissions();
|
||||
|
||||
$r1 = $a->orIf($b);
|
||||
$this->assertTrue($r1->getCacheMaxAge() === 3600);
|
||||
$this->assertSame(['user.permissions'], $r1->getCacheContexts());
|
||||
$r2 = $b->orIf($a);
|
||||
$this->assertTrue($r2->getCacheMaxAge() === 3600);
|
||||
$this->assertSame(['user.permissions'], $r2->getCacheContexts());
|
||||
};
|
||||
|
||||
// Merge either direction, get the same result.
|
||||
$merge_both_directions(AccessResult::allowed(), AccessResult::allowed());
|
||||
$merge_both_directions(AccessResult::allowed(), AccessResult::neutral());
|
||||
$merge_both_directions(AccessResult::neutral(), AccessResult::neutral());
|
||||
$merge_both_directions(AccessResult::neutral(), AccessResult::allowed());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests allowedIfHasPermissions().
|
||||
*
|
||||
* @covers ::allowedIfHasPermissions
|
||||
*
|
||||
* @dataProvider providerTestAllowedIfHasPermissions
|
||||
*
|
||||
* @param string[] $permissions
|
||||
* The permissions to check for.
|
||||
* @param string $conjunction
|
||||
* The conjunction to use when checking for permission. 'AND' or 'OR'.
|
||||
* @param \Drupal\Core\Access\AccessResult $expected_access
|
||||
* The expected access check result.
|
||||
*/
|
||||
public function testAllowedIfHasPermissions($permissions, $conjunction, AccessResult $expected_access) {
|
||||
$account = $this->getMock('\Drupal\Core\Session\AccountInterface');
|
||||
$account->expects($this->any())
|
||||
->method('hasPermission')
|
||||
->willReturnMap([
|
||||
['allowed', TRUE],
|
||||
['denied', FALSE],
|
||||
]);
|
||||
|
||||
if ($permissions) {
|
||||
$expected_access->cachePerPermissions();
|
||||
}
|
||||
|
||||
$access_result = AccessResult::allowedIfHasPermissions($account, $permissions, $conjunction);
|
||||
$this->assertEquals($expected_access, $access_result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for the testAllowedIfHasPermissions() method.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerTestAllowedIfHasPermissions() {
|
||||
return [
|
||||
[[], 'AND', AccessResult::allowedIf(FALSE)],
|
||||
[[], 'OR', AccessResult::allowedIf(FALSE)],
|
||||
[['allowed'], 'OR', AccessResult::allowedIf(TRUE)],
|
||||
[['allowed'], 'AND', AccessResult::allowedIf(TRUE)],
|
||||
[['denied'], 'OR', AccessResult::allowedIf(FALSE)],
|
||||
[['denied'], 'AND', AccessResult::allowedIf(FALSE)],
|
||||
[['allowed', 'denied'], 'OR', AccessResult::allowedIf(TRUE)],
|
||||
[['denied', 'allowed'], 'OR', AccessResult::allowedIf(TRUE)],
|
||||
[['allowed', 'denied', 'other'], 'OR', AccessResult::allowedIf(TRUE)],
|
||||
[['allowed', 'denied'], 'AND', AccessResult::allowedIf(FALSE)],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class UncacheableTestAccessResult implements AccessResultInterface {
|
||||
|
||||
/**
|
||||
* The access result value. 'ALLOWED', 'FORBIDDEN' or 'NEUTRAL'.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* Constructs a new UncacheableTestAccessResult object.
|
||||
*/
|
||||
public function __construct($value) {
|
||||
$this->value = $value;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isAllowed() {
|
||||
return $this->value === 'ALLOWED';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isForbidden() {
|
||||
return $this->value === 'FORBIDDEN';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isNeutral() {
|
||||
return $this->value === 'NEUTRAL';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function orIf(AccessResultInterface $other) {
|
||||
if ($this->isForbidden() || $other->isForbidden()) {
|
||||
return new static('FORBIDDEN');
|
||||
}
|
||||
elseif ($this->isAllowed() || $other->isAllowed()) {
|
||||
return new static('ALLOWED');
|
||||
}
|
||||
else {
|
||||
return new static('NEUTRAL');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function andIf(AccessResultInterface $other) {
|
||||
if ($this->isForbidden() || $other->isForbidden()) {
|
||||
return new static('FORBIDDEN');
|
||||
}
|
||||
elseif ($this->isAllowed() && $other->isAllowed()) {
|
||||
return new static('ALLOWED');
|
||||
}
|
||||
else {
|
||||
return new static('NEUTRAL');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Core\Access;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Drupal\Core\Access\CsrfAccessCheck;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Core\Access\CsrfAccessCheck
|
||||
* @group Access
|
||||
*/
|
||||
class CsrfAccessCheckTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The mock CSRF token generator.
|
||||
*
|
||||
* @var \Drupal\Core\Access\CsrfTokenGenerator|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $csrfToken;
|
||||
|
||||
/**
|
||||
* The access checker.
|
||||
*
|
||||
* @var \Drupal\Core\Access\CsrfAccessCheck
|
||||
*/
|
||||
protected $accessCheck;
|
||||
|
||||
/**
|
||||
* The mock route match.
|
||||
*
|
||||
* @var \Drupal\Core\RouteMatch\RouteMatchInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $routeMatch;
|
||||
|
||||
protected function setUp() {
|
||||
$this->csrfToken = $this->getMockBuilder('Drupal\Core\Access\CsrfTokenGenerator')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$this->routeMatch = $this->getMock('Drupal\Core\Routing\RouteMatchInterface');
|
||||
|
||||
$this->accessCheck = new CsrfAccessCheck($this->csrfToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the access() method with a valid token.
|
||||
*/
|
||||
public function testAccessTokenPass() {
|
||||
$this->csrfToken->expects($this->once())
|
||||
->method('validate')
|
||||
->with('test_query', 'test-path/42')
|
||||
->will($this->returnValue(TRUE));
|
||||
|
||||
$this->routeMatch->expects($this->once())
|
||||
->method('getRawParameters')
|
||||
->will($this->returnValue(array('node' => 42)));
|
||||
|
||||
$route = new Route('/test-path/{node}', array(), array('_csrf_token' => 'TRUE'));
|
||||
$request = Request::create('/test-path/42?token=test_query');
|
||||
|
||||
$this->assertEquals(AccessResult::allowed()->setCacheMaxAge(0), $this->accessCheck->access($route, $request, $this->routeMatch));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the access() method with an invalid token.
|
||||
*/
|
||||
public function testAccessTokenFail() {
|
||||
$this->csrfToken->expects($this->once())
|
||||
->method('validate')
|
||||
->with('test_query', 'test-path')
|
||||
->will($this->returnValue(FALSE));
|
||||
|
||||
$this->routeMatch->expects($this->once())
|
||||
->method('getRawParameters')
|
||||
->will($this->returnValue(array()));
|
||||
|
||||
$route = new Route('/test-path', array(), array('_csrf_token' => 'TRUE'));
|
||||
$request = Request::create('/test-path?token=test_query');
|
||||
|
||||
$this->assertEquals(AccessResult::forbidden()->setCacheMaxAge(0), $this->accessCheck->access($route, $request, $this->routeMatch));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Core\Access;
|
||||
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\Core\Access\CsrfTokenGenerator;
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
|
||||
/**
|
||||
* Tests the CsrfTokenGenerator class.
|
||||
*
|
||||
* @group Access
|
||||
* @coversDefaultClass \Drupal\Core\Access\CsrfTokenGenerator
|
||||
*/
|
||||
class CsrfTokenGeneratorTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The CSRF token generator.
|
||||
*
|
||||
* @var \Drupal\Core\Access\CsrfTokenGenerator
|
||||
*/
|
||||
protected $generator;
|
||||
|
||||
/**
|
||||
* The mock private key instance.
|
||||
*
|
||||
* @var \Drupal\Core\PrivateKey|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $privateKey;
|
||||
|
||||
/**
|
||||
* The mock session metadata bag.
|
||||
*
|
||||
* @var \Drupal\Core\Session\MetadataBag|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $sessionMetadata;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->privateKey = $this->getMockBuilder('Drupal\Core\PrivateKey')
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(array('get'))
|
||||
->getMock();
|
||||
|
||||
$this->sessionMetadata = $this->getMockBuilder('Drupal\Core\Session\MetadataBag')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$settings = array(
|
||||
'hash_salt' => $this->randomMachineName(),
|
||||
);
|
||||
|
||||
new Settings($settings);
|
||||
|
||||
$this->generator = new CsrfTokenGenerator($this->privateKey, $this->sessionMetadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up default expectations on the mocks.
|
||||
*/
|
||||
protected function setupDefaultExpectations() {
|
||||
$key = Crypt::randomBytesBase64();
|
||||
$this->privateKey->expects($this->any())
|
||||
->method('get')
|
||||
->will($this->returnValue($key));
|
||||
|
||||
$seed = Crypt::randomBytesBase64();
|
||||
$this->sessionMetadata->expects($this->any())
|
||||
->method('getCsrfTokenSeed')
|
||||
->will($this->returnValue($seed));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests CsrfTokenGenerator::get().
|
||||
*
|
||||
* @covers ::get
|
||||
*/
|
||||
public function testGet() {
|
||||
$this->setupDefaultExpectations();
|
||||
|
||||
$this->assertInternalType('string', $this->generator->get());
|
||||
$this->assertNotSame($this->generator->get(), $this->generator->get($this->randomMachineName()));
|
||||
$this->assertNotSame($this->generator->get($this->randomMachineName()), $this->generator->get($this->randomMachineName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a new token seed is generated upon first use.
|
||||
*
|
||||
* @covers ::get
|
||||
*/
|
||||
public function testGenerateSeedOnGet() {
|
||||
$key = Crypt::randomBytesBase64();
|
||||
$this->privateKey->expects($this->any())
|
||||
->method('get')
|
||||
->will($this->returnValue($key));
|
||||
|
||||
$this->sessionMetadata->expects($this->once())
|
||||
->method('getCsrfTokenSeed')
|
||||
->will($this->returnValue(NULL));
|
||||
|
||||
$this->sessionMetadata->expects($this->once())
|
||||
->method('setCsrfTokenSeed')
|
||||
->with($this->isType('string'));
|
||||
|
||||
$this->assertInternalType('string', $this->generator->get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests CsrfTokenGenerator::validate().
|
||||
*
|
||||
* @covers ::validate
|
||||
*/
|
||||
public function testValidate() {
|
||||
$this->setupDefaultExpectations();
|
||||
|
||||
$token = $this->generator->get();
|
||||
$this->assertTrue($this->generator->validate($token));
|
||||
$this->assertFalse($this->generator->validate($token, 'foo'));
|
||||
|
||||
$token = $this->generator->get('bar');
|
||||
$this->assertTrue($this->generator->validate($token, 'bar'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests CsrfTokenGenerator::validate() with different parameter types.
|
||||
*
|
||||
* @param mixed $token
|
||||
* The token to be validated.
|
||||
* @param mixed $value
|
||||
* (optional) An additional value to base the token on.
|
||||
*
|
||||
* @covers ::validate
|
||||
* @dataProvider providerTestValidateParameterTypes
|
||||
*/
|
||||
public function testValidateParameterTypes($token, $value) {
|
||||
$this->setupDefaultExpectations();
|
||||
|
||||
// The following check might throw PHP fatals and notices, so we disable
|
||||
// error assertions.
|
||||
set_error_handler(function () {return TRUE;});
|
||||
$this->assertFalse($this->generator->validate($token, $value));
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for testValidateParameterTypes.
|
||||
*
|
||||
* @return array
|
||||
* An array of data used by the test.
|
||||
*/
|
||||
public function providerTestValidateParameterTypes() {
|
||||
return array(
|
||||
array(array(), ''),
|
||||
array(TRUE, 'foo'),
|
||||
array(0, 'foo'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests CsrfTokenGenerator::validate() with invalid parameter types.
|
||||
*
|
||||
* @param mixed $token
|
||||
* The token to be validated.
|
||||
* @param mixed $value
|
||||
* (optional) An additional value to base the token on.
|
||||
*
|
||||
* @covers ::validate
|
||||
* @dataProvider providerTestInvalidParameterTypes
|
||||
* @expectedException InvalidArgumentException
|
||||
*/
|
||||
public function testInvalidParameterTypes($token, $value = '') {
|
||||
$this->setupDefaultExpectations();
|
||||
|
||||
$this->generator->validate($token, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for testInvalidParameterTypes.
|
||||
*
|
||||
* @return array
|
||||
* An array of data used by the test.
|
||||
*/
|
||||
public function providerTestInvalidParameterTypes() {
|
||||
return array(
|
||||
array(NULL, new \stdClass()),
|
||||
array(0, array()),
|
||||
array('', array()),
|
||||
array(array(), array()),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the exception thrown when no 'hash_salt' is provided in settings.
|
||||
*
|
||||
* @covers ::get
|
||||
* @expectedException \RuntimeException
|
||||
*/
|
||||
public function testGetWithNoHashSalt() {
|
||||
// Update settings with no hash salt.
|
||||
new Settings(array());
|
||||
$generator = new CsrfTokenGenerator($this->privateKey, $this->sessionMetadata);
|
||||
$generator->get();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\Core\Access\CustomAccessCheckTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\Core\Access;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Access\CustomAccessCheck;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Core\Access\CustomAccessCheck
|
||||
* @group Access
|
||||
*/
|
||||
class CustomAccessCheckTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The access checker to test.
|
||||
*
|
||||
* @var \Drupal\Core\Access\CustomAccessCheck
|
||||
*/
|
||||
protected $accessChecker;
|
||||
|
||||
/**
|
||||
* The mocked controller resolver.
|
||||
*
|
||||
* @var \Drupal\Core\Controller\ControllerResolverInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $controllerResolver;
|
||||
|
||||
/**
|
||||
* The mocked arguments resolver.
|
||||
*
|
||||
* @var \Drupal\Core\Access\AccessArgumentsResolverFactoryInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $argumentsResolverFactory;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->controllerResolver = $this->getMock('Drupal\Core\Controller\ControllerResolverInterface');
|
||||
$this->argumentsResolverFactory = $this->getMock('Drupal\Core\Access\AccessArgumentsResolverFactoryInterface');
|
||||
$this->accessChecker = new CustomAccessCheck($this->controllerResolver, $this->argumentsResolverFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the access method.
|
||||
*/
|
||||
public function testAccess() {
|
||||
$route_match = $this->getMock('Drupal\Core\Routing\RouteMatchInterface');
|
||||
|
||||
$this->controllerResolver->expects($this->at(0))
|
||||
->method('getControllerFromDefinition')
|
||||
->with('\Drupal\Tests\Core\Access\TestController::accessDeny')
|
||||
->will($this->returnValue(array(new TestController(), 'accessDeny')));
|
||||
|
||||
$resolver0 = $this->getMock('Drupal\Component\Utility\ArgumentsResolverInterface');
|
||||
$resolver0->expects($this->once())
|
||||
->method('getArguments')
|
||||
->will($this->returnValue(array()));
|
||||
$this->argumentsResolverFactory->expects($this->at(0))
|
||||
->method('getArgumentsResolver')
|
||||
->will($this->returnValue($resolver0));
|
||||
|
||||
$this->controllerResolver->expects($this->at(1))
|
||||
->method('getControllerFromDefinition')
|
||||
->with('\Drupal\Tests\Core\Access\TestController::accessAllow')
|
||||
->will($this->returnValue(array(new TestController(), 'accessAllow')));
|
||||
|
||||
$resolver1 = $this->getMock('Drupal\Component\Utility\ArgumentsResolverInterface');
|
||||
$resolver1->expects($this->once())
|
||||
->method('getArguments')
|
||||
->will($this->returnValue(array()));
|
||||
$this->argumentsResolverFactory->expects($this->at(1))
|
||||
->method('getArgumentsResolver')
|
||||
->will($this->returnValue($resolver1));
|
||||
|
||||
$this->controllerResolver->expects($this->at(2))
|
||||
->method('getControllerFromDefinition')
|
||||
->with('\Drupal\Tests\Core\Access\TestController::accessParameter')
|
||||
->will($this->returnValue(array(new TestController(), 'accessParameter')));
|
||||
|
||||
$resolver2 = $this->getMock('Drupal\Component\Utility\ArgumentsResolverInterface');
|
||||
$resolver2->expects($this->once())
|
||||
->method('getArguments')
|
||||
->will($this->returnValue(array('parameter' => 'TRUE')));
|
||||
$this->argumentsResolverFactory->expects($this->at(2))
|
||||
->method('getArgumentsResolver')
|
||||
->will($this->returnValue($resolver2));
|
||||
|
||||
$route = new Route('/test-route', array(), array('_custom_access' => '\Drupal\Tests\Core\Access\TestController::accessDeny'));
|
||||
$account = $this->getMock('Drupal\Core\Session\AccountInterface');
|
||||
$this->assertEquals(AccessResult::neutral(), $this->accessChecker->access($route, $route_match, $account));
|
||||
|
||||
$route = new Route('/test-route', array(), array('_custom_access' => '\Drupal\Tests\Core\Access\TestController::accessAllow'));
|
||||
$this->assertEquals(AccessResult::allowed(), $this->accessChecker->access($route, $route_match, $account));
|
||||
|
||||
$route = new Route('/test-route', array('parameter' => 'TRUE'), array('_custom_access' => '\Drupal\Tests\Core\Access\TestController::accessParameter'));
|
||||
$this->assertEquals(AccessResult::allowed(), $this->accessChecker->access($route, $route_match, $account));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TestController {
|
||||
|
||||
public function accessAllow() {
|
||||
return AccessResult::allowed();
|
||||
}
|
||||
|
||||
public function accessDeny() {
|
||||
return AccessResult::neutral();
|
||||
}
|
||||
|
||||
public function accessParameter($parameter) {
|
||||
return AccessResult::allowedIf($parameter == 'TRUE');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Core\Access;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Access\DefaultAccessCheck;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Core\Access\DefaultAccessCheck
|
||||
* @group Access
|
||||
*/
|
||||
class DefaultAccessCheckTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The access checker to test.
|
||||
*
|
||||
* @var \Drupal\Core\Access\DefaultAccessCheck
|
||||
*/
|
||||
protected $accessChecker;
|
||||
|
||||
/**
|
||||
* The mocked account.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $account;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->account = $this->getMock('Drupal\Core\Session\AccountInterface');
|
||||
$this->accessChecker = new DefaultAccessCheck();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the access method.
|
||||
*/
|
||||
public function testAccess() {
|
||||
$request = new Request(array());
|
||||
|
||||
$route = new Route('/test-route', array(), array('_access' => 'NULL'));
|
||||
$this->assertEquals(AccessResult::neutral(), $this->accessChecker->access($route, $request, $this->account));
|
||||
|
||||
$route = new Route('/test-route', array(), array('_access' => 'FALSE'));
|
||||
$this->assertEquals(AccessResult::forbidden(), $this->accessChecker->access($route, $request, $this->account));
|
||||
|
||||
$route = new Route('/test-route', array(), array('_access' => 'TRUE'));
|
||||
$this->assertEquals(AccessResult::allowed(), $this->accessChecker->access($route, $request, $this->account));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Core\Access;
|
||||
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\Core\Access\RouteProcessorCsrf;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Core\Access\RouteProcessorCsrf
|
||||
* @group Access
|
||||
*/
|
||||
class RouteProcessorCsrfTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The mock CSRF token generator.
|
||||
*
|
||||
* @var \Drupal\Core\Access\CsrfTokenGenerator|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $csrfToken;
|
||||
|
||||
/**
|
||||
* The route processor.
|
||||
*
|
||||
* @var \Drupal\Core\Access\RouteProcessorCsrf
|
||||
*/
|
||||
protected $processor;
|
||||
|
||||
protected function setUp() {
|
||||
$this->csrfToken = $this->getMockBuilder('Drupal\Core\Access\CsrfTokenGenerator')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$this->processor = new RouteProcessorCsrf($this->csrfToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the processOutbound() method with no _csrf_token route requirement.
|
||||
*/
|
||||
public function testProcessOutboundNoRequirement() {
|
||||
$this->csrfToken->expects($this->never())
|
||||
->method('get');
|
||||
|
||||
$route = new Route('/test-path');
|
||||
$parameters = array();
|
||||
|
||||
$bubbleable_metadata = new BubbleableMetadata();
|
||||
$this->processor->processOutbound('test', $route, $parameters, $bubbleable_metadata);
|
||||
// No parameters should be added to the parameters array.
|
||||
$this->assertEmpty($parameters);
|
||||
// Cacheability of routes without a _csrf_token route requirement is
|
||||
// unaffected.
|
||||
$this->assertEquals((new BubbleableMetadata()), $bubbleable_metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the processOutbound() method with a _csrf_token route requirement.
|
||||
*/
|
||||
public function testProcessOutbound() {
|
||||
$route = new Route('/test-path', array(), array('_csrf_token' => 'TRUE'));
|
||||
$parameters = array();
|
||||
|
||||
$bubbleable_metadata = new BubbleableMetadata();
|
||||
$this->processor->processOutbound('test', $route, $parameters, $bubbleable_metadata);
|
||||
// 'token' should be added to the parameters array.
|
||||
$this->assertArrayHasKey('token', $parameters);
|
||||
// Bubbleable metadata of routes with a _csrf_token route requirement is a
|
||||
// placeholder.
|
||||
$path = 'test-path';
|
||||
$placeholder = hash('sha1', $path);
|
||||
$placeholder_render_array = [
|
||||
'#lazy_builder' => ['route_processor_csrf:renderPlaceholderCsrfToken', [$path]],
|
||||
];
|
||||
$this->assertSame($parameters['token'], $placeholder);
|
||||
$this->assertEquals((new BubbleableMetadata())->setAttachments(['placeholders' => [$placeholder => $placeholder_render_array]]), $bubbleable_metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the processOutbound() method with a dynamic path and one replacement.
|
||||
*/
|
||||
public function testProcessOutboundDynamicOne() {
|
||||
$route = new Route('/test-path/{slug}', array(), array('_csrf_token' => 'TRUE'));
|
||||
$parameters = array('slug' => 100);
|
||||
|
||||
$bubbleable_metadata = new BubbleableMetadata();
|
||||
$this->processor->processOutbound('test', $route, $parameters, $bubbleable_metadata);
|
||||
// Bubbleable metadata of routes with a _csrf_token route requirement is a
|
||||
// placeholder.
|
||||
$path = 'test-path/100';
|
||||
$placeholder = hash('sha1', $path);
|
||||
$placeholder_render_array = [
|
||||
'#lazy_builder' => ['route_processor_csrf:renderPlaceholderCsrfToken', [$path]],
|
||||
];
|
||||
$this->assertEquals((new BubbleableMetadata())->setAttachments(['placeholders' => [$placeholder => $placeholder_render_array]]), $bubbleable_metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the processOutbound() method with two parameter replacements.
|
||||
*/
|
||||
public function testProcessOutboundDynamicTwo() {
|
||||
$route = new Route('{slug_1}/test-path/{slug_2}', array(), array('_csrf_token' => 'TRUE'));
|
||||
$parameters = array('slug_1' => 100, 'slug_2' => 'test');
|
||||
|
||||
$bubbleable_metadata = new BubbleableMetadata();
|
||||
$this->processor->processOutbound('test', $route, $parameters, $bubbleable_metadata);
|
||||
// Bubbleable metadata of routes with a _csrf_token route requirement is a
|
||||
// placeholder.
|
||||
$path = '100/test-path/test';
|
||||
$placeholder = hash('sha1', $path);
|
||||
$placeholder_render_array = [
|
||||
'#lazy_builder' => ['route_processor_csrf:renderPlaceholderCsrfToken', [$path]],
|
||||
];
|
||||
$this->assertEquals((new BubbleableMetadata())->setAttachments(['placeholders' => [$placeholder => $placeholder_render_array]]), $bubbleable_metadata);
|
||||
}
|
||||
|
||||
}
|
||||
432
web/core/tests/Drupal/Tests/Core/Ajax/AjaxCommandsTest.php
Normal file
432
web/core/tests/Drupal/Tests/Core/Ajax/AjaxCommandsTest.php
Normal file
|
|
@ -0,0 +1,432 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Core\Ajax;
|
||||
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\Core\Ajax\AddCssCommand;
|
||||
use Drupal\Core\Ajax\AfterCommand;
|
||||
use Drupal\Core\Ajax\AlertCommand;
|
||||
use Drupal\Core\Ajax\AppendCommand;
|
||||
use Drupal\Core\Ajax\BeforeCommand;
|
||||
use Drupal\Core\Ajax\ChangedCommand;
|
||||
use Drupal\Core\Ajax\CssCommand;
|
||||
use Drupal\Core\Ajax\DataCommand;
|
||||
use Drupal\Core\Ajax\HtmlCommand;
|
||||
use Drupal\Core\Ajax\InsertCommand;
|
||||
use Drupal\Core\Ajax\InvokeCommand;
|
||||
use Drupal\Core\Ajax\PrependCommand;
|
||||
use Drupal\Core\Ajax\RemoveCommand;
|
||||
use Drupal\Core\Ajax\ReplaceCommand;
|
||||
use Drupal\Core\Ajax\RestripeCommand;
|
||||
use Drupal\Core\Ajax\SettingsCommand;
|
||||
use Drupal\Core\Ajax\CloseDialogCommand;
|
||||
use Drupal\Core\Ajax\CloseModalDialogCommand;
|
||||
use Drupal\Core\Ajax\SetDialogOptionCommand;
|
||||
use Drupal\Core\Ajax\SetDialogTitleCommand;
|
||||
use Drupal\Core\Ajax\RedirectCommand;
|
||||
|
||||
/**
|
||||
* Test coverage for various classes in the \Drupal\Core\Ajax namespace.
|
||||
*
|
||||
* @group Ajax
|
||||
*/
|
||||
class AjaxCommandsTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @covers \Drupal\Core\Ajax\AddCssCommand
|
||||
*/
|
||||
public function testAddCssCommand() {
|
||||
$command = new AddCssCommand('p{ text-decoration:blink; }');
|
||||
|
||||
$expected = array(
|
||||
'command' => 'add_css',
|
||||
'data' => 'p{ text-decoration:blink; }',
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $command->render());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Drupal\Core\Ajax\AfterCommand
|
||||
*/
|
||||
public function testAfterCommand() {
|
||||
$command = new AfterCommand('#page-title', '<p>New Text!</p>', array('my-setting' => 'setting'));
|
||||
|
||||
$expected = array(
|
||||
'command' => 'insert',
|
||||
'method' => 'after',
|
||||
'selector' => '#page-title',
|
||||
'data' => '<p>New Text!</p>',
|
||||
'settings' => array('my-setting' => 'setting'),
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $command->render());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Drupal\Core\Ajax\AlertCommand
|
||||
*/
|
||||
public function testAlertCommand() {
|
||||
$command = new AlertCommand('Set condition 1 throughout the ship!');
|
||||
$expected = array(
|
||||
'command' => 'alert',
|
||||
'text' => 'Set condition 1 throughout the ship!',
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $command->render());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Drupal\Core\Ajax\AppendCommand
|
||||
*/
|
||||
public function testAppendCommand() {
|
||||
$command = new AppendCommand('#page-title', '<p>New Text!</p>', array('my-setting' => 'setting'));
|
||||
|
||||
$expected = array(
|
||||
'command' => 'insert',
|
||||
'method' => 'append',
|
||||
'selector' => '#page-title',
|
||||
'data' => '<p>New Text!</p>',
|
||||
'settings' => array('my-setting' => 'setting'),
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $command->render());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Drupal\Core\Ajax\BeforeCommand
|
||||
*/
|
||||
public function testBeforeCommand() {
|
||||
$command = new BeforeCommand('#page-title', '<p>New Text!</p>', array('my-setting' => 'setting'));
|
||||
|
||||
$expected = array(
|
||||
'command' => 'insert',
|
||||
'method' => 'before',
|
||||
'selector' => '#page-title',
|
||||
'data' => '<p>New Text!</p>',
|
||||
'settings' => array('my-setting' => 'setting'),
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $command->render());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Drupal\Core\Ajax\ChangedCommand
|
||||
*/
|
||||
public function testChangedCommand() {
|
||||
$command = new ChangedCommand('#page-title', '#page-title-changed');
|
||||
|
||||
$expected = array(
|
||||
'command' => 'changed',
|
||||
'selector' => '#page-title',
|
||||
'asterisk' => '#page-title-changed',
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $command->render());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Drupal\Core\Ajax\CssCommand
|
||||
*/
|
||||
public function testCssCommand() {
|
||||
$command = new CssCommand('#page-title', array('text-decoration' => 'blink'));
|
||||
$command->setProperty('font-size', '40px')->setProperty('font-weight', 'bold');
|
||||
|
||||
$expected = array(
|
||||
'command' => 'css',
|
||||
'selector' => '#page-title',
|
||||
'argument' => array(
|
||||
'text-decoration' => 'blink',
|
||||
'font-size' => '40px',
|
||||
'font-weight' => 'bold',
|
||||
),
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $command->render());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Drupal\Core\Ajax\DataCommand
|
||||
*/
|
||||
public function testDataCommand() {
|
||||
$command = new DataCommand('#page-title', 'my-data', array('key' => 'value'));
|
||||
|
||||
$expected = array(
|
||||
'command' => 'data',
|
||||
'selector' => '#page-title',
|
||||
'name' => 'my-data',
|
||||
'value' => array('key' => 'value'),
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $command->render());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Drupal\Core\Ajax\HtmlCommand
|
||||
*/
|
||||
public function testHtmlCommand() {
|
||||
$command = new HtmlCommand('#page-title', '<p>New Text!</p>', array('my-setting' => 'setting'));
|
||||
|
||||
$expected = array(
|
||||
'command' => 'insert',
|
||||
'method' => 'html',
|
||||
'selector' => '#page-title',
|
||||
'data' => '<p>New Text!</p>',
|
||||
'settings' => array('my-setting' => 'setting'),
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $command->render());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Drupal\Core\Ajax\InsertCommand
|
||||
*/
|
||||
public function testInsertCommand() {
|
||||
$command = new InsertCommand('#page-title', '<p>New Text!</p>', array('my-setting' => 'setting'));
|
||||
|
||||
$expected = array(
|
||||
'command' => 'insert',
|
||||
'method' => NULL,
|
||||
'selector' => '#page-title',
|
||||
'data' => '<p>New Text!</p>',
|
||||
'settings' => array('my-setting' => 'setting'),
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $command->render());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Drupal\Core\Ajax\InvokeCommand
|
||||
*/
|
||||
public function testInvokeCommand() {
|
||||
$command = new InvokeCommand('#page-title', 'myMethod', array('var1', 'var2'));
|
||||
|
||||
$expected = array(
|
||||
'command' => 'invoke',
|
||||
'selector' => '#page-title',
|
||||
'method' => 'myMethod',
|
||||
'args' => array('var1', 'var2'),
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $command->render());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Drupal\Core\Ajax\PrependCommand
|
||||
*/
|
||||
public function testPrependCommand() {
|
||||
$command = new PrependCommand('#page-title', '<p>New Text!</p>', array('my-setting' => 'setting'));
|
||||
|
||||
$expected = array(
|
||||
'command' => 'insert',
|
||||
'method' => 'prepend',
|
||||
'selector' => '#page-title',
|
||||
'data' => '<p>New Text!</p>',
|
||||
'settings' => array('my-setting' => 'setting'),
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $command->render());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Drupal\Core\Ajax\RemoveCommand
|
||||
*/
|
||||
public function testRemoveCommand() {
|
||||
$command = new RemoveCommand('#page-title');
|
||||
|
||||
$expected = array(
|
||||
'command' => 'remove',
|
||||
'selector' => '#page-title',
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $command->render());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Drupal\Core\Ajax\ReplaceCommand
|
||||
*/
|
||||
public function testReplaceCommand() {
|
||||
$command = new ReplaceCommand('#page-title', '<p>New Text!</p>', array('my-setting' => 'setting'));
|
||||
|
||||
$expected = array(
|
||||
'command' => 'insert',
|
||||
'method' => 'replaceWith',
|
||||
'selector' => '#page-title',
|
||||
'data' => '<p>New Text!</p>',
|
||||
'settings' => array('my-setting' => 'setting'),
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $command->render());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Drupal\Core\Ajax\RestripeCommand
|
||||
*/
|
||||
public function testRestripeCommand() {
|
||||
$command = new RestripeCommand('#page-title');
|
||||
|
||||
$expected = array(
|
||||
'command' => 'restripe',
|
||||
'selector' => '#page-title',
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $command->render());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Drupal\Core\Ajax\SettingsCommand
|
||||
*/
|
||||
public function testSettingsCommand() {
|
||||
$command = new SettingsCommand(array('key' => 'value'), TRUE);
|
||||
|
||||
$expected = array(
|
||||
'command' => 'settings',
|
||||
'settings' => array('key' => 'value'),
|
||||
'merge' => TRUE,
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $command->render());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Drupal\Core\Ajax\OpenDialogCommand
|
||||
*/
|
||||
public function testOpenDialogCommand() {
|
||||
$command = $this->getMockBuilder('Drupal\Core\Ajax\OpenDialogCommand')
|
||||
->setConstructorArgs(array(
|
||||
'#some-dialog', 'Title', '<p>Text!</p>', array(
|
||||
'url' => FALSE,
|
||||
'width' => 500,
|
||||
),
|
||||
))
|
||||
->setMethods(array('getRenderedContent'))
|
||||
->getMock();
|
||||
|
||||
// This method calls the render service, which isn't available. We want it
|
||||
// to do nothing so we mock it to return a known value.
|
||||
$command->expects($this->once())
|
||||
->method('getRenderedContent')
|
||||
->willReturn('rendered content');
|
||||
|
||||
$expected = array(
|
||||
'command' => 'openDialog',
|
||||
'selector' => '#some-dialog',
|
||||
'settings' => NULL,
|
||||
'data' => 'rendered content',
|
||||
'dialogOptions' => array(
|
||||
'url' => FALSE,
|
||||
'width' => 500,
|
||||
'title' => 'Title',
|
||||
'modal' => FALSE,
|
||||
),
|
||||
);
|
||||
$this->assertEquals($expected, $command->render());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Drupal\Core\Ajax\OpenModalDialogCommand
|
||||
*/
|
||||
public function testOpenModalDialogCommand() {
|
||||
$command = $this->getMockBuilder('Drupal\Core\Ajax\OpenModalDialogCommand')
|
||||
->setConstructorArgs(array(
|
||||
'Title', '<p>Text!</p>', array(
|
||||
'url' => 'example',
|
||||
'width' => 500,
|
||||
),
|
||||
))
|
||||
->setMethods(array('getRenderedContent'))
|
||||
->getMock();
|
||||
|
||||
// This method calls the render service, which isn't available. We want it
|
||||
// to do nothing so we mock it to return a known value.
|
||||
$command->expects($this->once())
|
||||
->method('getRenderedContent')
|
||||
->willReturn('rendered content');
|
||||
|
||||
$expected = array(
|
||||
'command' => 'openDialog',
|
||||
'selector' => '#drupal-modal',
|
||||
'settings' => NULL,
|
||||
'data' => 'rendered content',
|
||||
'dialogOptions' => array(
|
||||
'url' => 'example',
|
||||
'width' => 500,
|
||||
'title' => 'Title',
|
||||
'modal' => TRUE,
|
||||
),
|
||||
);
|
||||
$this->assertEquals($expected, $command->render());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Drupal\Core\Ajax\CloseModalDialogCommand
|
||||
*/
|
||||
public function testCloseModalDialogCommand() {
|
||||
$command = new CloseModalDialogCommand();
|
||||
$expected = array(
|
||||
'command' => 'closeDialog',
|
||||
'selector' => '#drupal-modal',
|
||||
'persist' => FALSE,
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $command->render());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Drupal\Core\Ajax\CloseDialogCommand
|
||||
*/
|
||||
public function testCloseDialogCommand() {
|
||||
$command = new CloseDialogCommand('#some-dialog', TRUE);
|
||||
$expected = array(
|
||||
'command' => 'closeDialog',
|
||||
'selector' => '#some-dialog',
|
||||
'persist' => TRUE,
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $command->render());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Drupal\Core\Ajax\SetDialogOptionCommand
|
||||
*/
|
||||
public function testSetDialogOptionCommand() {
|
||||
$command = new SetDialogOptionCommand('#some-dialog', 'width', '500');
|
||||
$expected = array(
|
||||
'command' => 'setDialogOption',
|
||||
'selector' => '#some-dialog',
|
||||
'optionName' => 'width',
|
||||
'optionValue' => '500',
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $command->render());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Drupal\Core\Ajax\SetDialogTitleCommand
|
||||
*/
|
||||
public function testSetDialogTitleCommand() {
|
||||
$command = new SetDialogTitleCommand('#some-dialog', 'Example');
|
||||
$expected = array(
|
||||
'command' => 'setDialogOption',
|
||||
'selector' => '#some-dialog',
|
||||
'optionName' => 'title',
|
||||
'optionValue' => 'Example',
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $command->render());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Drupal\Core\Ajax\RedirectCommand
|
||||
*/
|
||||
public function testRedirectCommand() {
|
||||
$command = new RedirectCommand('http://example.com');
|
||||
$expected = array(
|
||||
'command' => 'redirect',
|
||||
'url' => 'http://example.com',
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $command->render());
|
||||
}
|
||||
|
||||
}
|
||||
94
web/core/tests/Drupal/Tests/Core/Ajax/AjaxResponseTest.php
Normal file
94
web/core/tests/Drupal/Tests/Core/Ajax/AjaxResponseTest.php
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Core\Ajax;
|
||||
|
||||
use Drupal\Core\Ajax\AjaxResponse;
|
||||
use Drupal\Core\EventSubscriber\AjaxResponseSubscriber;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Core\Ajax\AjaxResponse
|
||||
* @group Ajax
|
||||
*/
|
||||
class AjaxResponseTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The tested ajax response object.
|
||||
*
|
||||
* @var \Drupal\Core\Ajax\AjaxResponse
|
||||
*/
|
||||
protected $ajaxResponse;
|
||||
|
||||
protected function setUp() {
|
||||
$this->ajaxResponse = new AjaxResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the add and getCommands method.
|
||||
*
|
||||
* @see \Drupal\Core\Ajax\AjaxResponse::addCommand()
|
||||
* @see \Drupal\Core\Ajax\AjaxResponse::getCommands()
|
||||
*/
|
||||
public function testCommands() {
|
||||
$command_one = $this->getMock('Drupal\Core\Ajax\CommandInterface');
|
||||
$command_one->expects($this->once())
|
||||
->method('render')
|
||||
->will($this->returnValue(array('command' => 'one')));
|
||||
$command_two = $this->getMock('Drupal\Core\Ajax\CommandInterface');
|
||||
$command_two->expects($this->once())
|
||||
->method('render')
|
||||
->will($this->returnValue(array('command' => 'two')));
|
||||
$command_three = $this->getMock('Drupal\Core\Ajax\CommandInterface');
|
||||
$command_three->expects($this->once())
|
||||
->method('render')
|
||||
->will($this->returnValue(array('command' => 'three')));
|
||||
|
||||
$this->ajaxResponse->addCommand($command_one);
|
||||
$this->ajaxResponse->addCommand($command_two);
|
||||
$this->ajaxResponse->addCommand($command_three, TRUE);
|
||||
|
||||
// Ensure that the added commands are in the right order.
|
||||
$commands =& $this->ajaxResponse->getCommands();
|
||||
$this->assertSame($commands[1], array('command' => 'one'));
|
||||
$this->assertSame($commands[2], array('command' => 'two'));
|
||||
$this->assertSame($commands[0], array('command' => 'three'));
|
||||
|
||||
// Remove one and change one element from commands and ensure the reference
|
||||
// worked as expected.
|
||||
unset($commands[2]);
|
||||
$commands[0]['class'] = 'test-class';
|
||||
|
||||
$commands = $this->ajaxResponse->getCommands();
|
||||
$this->assertSame($commands[1], array('command' => 'one'));
|
||||
$this->assertFalse(isset($commands[2]));
|
||||
$this->assertSame($commands[0], array('command' => 'three', 'class' => 'test-class'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the support for IE specific headers in file uploads.
|
||||
*
|
||||
* @cover ::prepareResponse
|
||||
*/
|
||||
public function testPrepareResponseForIeFormRequestsWithFileUpload() {
|
||||
$request = Request::create('/example', 'POST');
|
||||
$request->headers->set('Accept', 'text/html');
|
||||
$response = new AjaxResponse([]);
|
||||
$response->headers->set('Content-Type', 'application/json; charset=utf-8');
|
||||
|
||||
$ajax_response_attachments_processor = $this->getMock('\Drupal\Core\Render\AttachmentsResponseProcessorInterface');
|
||||
$subscriber = new AjaxResponseSubscriber($ajax_response_attachments_processor);
|
||||
$event = new FilterResponseEvent(
|
||||
$this->getMock('\Symfony\Component\HttpKernel\HttpKernelInterface'),
|
||||
$request,
|
||||
HttpKernelInterface::MASTER_REQUEST,
|
||||
$response
|
||||
);
|
||||
$subscriber->onResponse($event);
|
||||
$this->assertEquals('text/html; charset=utf-8', $response->headers->get('Content-Type'));
|
||||
$this->assertEquals($response->getContent(), '<textarea>[]</textarea>');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Core\Annotation;
|
||||
|
||||
use Drupal\Core\Annotation\PluralTranslation;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Core\Annotation\PluralTranslation
|
||||
* @group Annotation
|
||||
*/
|
||||
class PluralTranslationTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @covers ::get
|
||||
*
|
||||
* @dataProvider providerTestGet
|
||||
*/
|
||||
public function testGet(array $values) {
|
||||
$annotation = new PluralTranslation($values);
|
||||
|
||||
$default_values = [
|
||||
'context' => NULL,
|
||||
];
|
||||
$this->assertEquals($values + $default_values, $annotation->get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data to self::testGet().
|
||||
*/
|
||||
public function providerTestGet() {
|
||||
$data = [];
|
||||
$data[] = [
|
||||
[
|
||||
'singular' => $this->randomMachineName(),
|
||||
'plural' => $this->randomMachineName(),
|
||||
'context' => $this->randomMachineName(),
|
||||
],
|
||||
];
|
||||
$data[] = [
|
||||
[
|
||||
'singular' => $this->randomMachineName(),
|
||||
'plural' => $this->randomMachineName(),
|
||||
],
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerTestMissingData
|
||||
*/
|
||||
public function testMissingData($data) {
|
||||
$this->setExpectedException(\InvalidArgumentException::class);
|
||||
new PluralTranslation($data);
|
||||
}
|
||||
|
||||
public function providerTestMissingData() {
|
||||
$data = [];
|
||||
$data['all-missing'] = [[]];
|
||||
$data['singular-missing'] = [['plural' => 'muh']];
|
||||
$data['plural-missing'] = [['singular' => 'muh']];
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Core\Annotation;
|
||||
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Core\Annotation\Translation
|
||||
* @group Annotation
|
||||
*/
|
||||
class TranslationTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The translation manager used for testing.
|
||||
*
|
||||
* @var \Drupal\Core\StringTranslation\TranslationInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $translationManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
$this->translationManager = $this->getStringTranslationStub();
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::get
|
||||
*
|
||||
* @dataProvider providerTestGet
|
||||
*/
|
||||
public function testGet(array $values, $expected) {
|
||||
$container = new ContainerBuilder();
|
||||
$container->set('string_translation', $this->translationManager);
|
||||
\Drupal::setContainer($container);
|
||||
|
||||
$arguments = isset($values['arguments']) ? $values['arguments'] : array();
|
||||
$options = isset($values['context']) ? array(
|
||||
'context' => $values['context'],
|
||||
) : array();
|
||||
|
||||
$annotation = new Translation($values);
|
||||
|
||||
$this->assertSame($expected, (string) $annotation->get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data to self::testGet().
|
||||
*/
|
||||
public function providerTestGet() {
|
||||
$data = array();
|
||||
$data[] = array(
|
||||
array(
|
||||
'value' => 'Foo',
|
||||
),
|
||||
'Foo'
|
||||
);
|
||||
$random = $this->randomMachineName();
|
||||
$random_html_entity = '&' . $random;
|
||||
$data[] = array(
|
||||
array(
|
||||
'value' => 'Foo @bar @baz %qux',
|
||||
'arguments' => array(
|
||||
'@bar' => $random,
|
||||
'@baz' => $random_html_entity,
|
||||
'%qux' => $random_html_entity,
|
||||
),
|
||||
'context' => $this->randomMachineName(),
|
||||
),
|
||||
'Foo ' . $random . ' &' . $random . ' <em class="placeholder">&' . $random . '</em>',
|
||||
);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Core\Assert;
|
||||
|
||||
use Behat\Mink\Element\DocumentElement;
|
||||
use Behat\Mink\Element\NodeElement;
|
||||
use Behat\Mink\Session;
|
||||
use Drupal\Component\Render\MarkupInterface;
|
||||
use Drupal\FunctionalTests\AssertLegacyTrait;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\Tests\WebAssert;
|
||||
use PHPUnit_Framework_ExpectationFailedException;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\FunctionalTests\AssertLegacyTrait
|
||||
* @group Assert
|
||||
*/
|
||||
class AssertLegacyTraitTest extends UnitTestCase {
|
||||
|
||||
use AssertLegacyTrait;
|
||||
|
||||
/**
|
||||
* The mocked Mink session object used for testing.
|
||||
*
|
||||
* @var \Behat\Mink\Session|\Prophecy\Prophecy\ObjectProphecy
|
||||
*/
|
||||
protected $session;
|
||||
|
||||
/**
|
||||
* The mocked page element used for testing.
|
||||
*
|
||||
* @var Behat\Mink\Element\DocumentElement|\Prophecy\Prophecy\ObjectProphecy
|
||||
*/
|
||||
protected $page;
|
||||
|
||||
/**
|
||||
* The mocked web assert class.
|
||||
*
|
||||
* @var \Drupal\Tests\WebAssert|\Prophecy\Prophecy\ObjectProphecy
|
||||
*/
|
||||
protected $webAssert;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->page = $this->prophesize(DocumentElement::class);
|
||||
$this->session = $this->prophesize(Session::class);
|
||||
$this->session->getPage()->willReturn($this->page->reveal());
|
||||
$this->webAssert = $this->prophesize(WebAssert::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::assertUniqueText
|
||||
*/
|
||||
public function testAssertUniqueText() {
|
||||
$this->page->getText()->willReturn('foo bar bar');
|
||||
$this->assertUniqueText('foo');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::assertUniqueText
|
||||
*/
|
||||
public function testAssertUniqueTextFail() {
|
||||
$this->page->getText()->willReturn('foo bar bar');
|
||||
$this->setExpectedException(PHPUnit_Framework_ExpectationFailedException::class);
|
||||
$this->assertUniqueText('bar');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::assertUniqueText
|
||||
*/
|
||||
public function testAssertUniqueTextUnknown() {
|
||||
$this->page->getText()->willReturn('foo bar bar');
|
||||
$this->setExpectedException(PHPUnit_Framework_ExpectationFailedException::class);
|
||||
$this->assertUniqueText('alice');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::assertUniqueText
|
||||
*/
|
||||
public function testAssertUniqueTextMarkup() {
|
||||
$this->page->getText()->willReturn('foo bar bar');
|
||||
$markupObject = $this->prophesize(MarkupInterface::class);
|
||||
$markupObject->__toString()->willReturn('foo');
|
||||
$this->assertUniqueText($markupObject->reveal());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::assertNoUniqueText
|
||||
*/
|
||||
public function testAssertNoUniqueText() {
|
||||
$this->page->getText()->willReturn('foo bar bar');
|
||||
$this->assertNoUniqueText('bar');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::assertNoUniqueText
|
||||
*/
|
||||
public function testAssertNoUniqueTextFail() {
|
||||
$this->page->getText()->willReturn('foo bar bar');
|
||||
$this->setExpectedException(PHPUnit_Framework_ExpectationFailedException::class);
|
||||
$this->assertNoUniqueText('foo');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::assertNoUniqueText
|
||||
*/
|
||||
public function testAssertNoUniqueTextUnknown() {
|
||||
$this->page->getText()->willReturn('foo bar bar');
|
||||
$this->setExpectedException(PHPUnit_Framework_ExpectationFailedException::class);
|
||||
$this->assertNoUniqueText('alice');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::assertNoUniqueText
|
||||
*/
|
||||
public function testAssertNoUniqueTextMarkup() {
|
||||
$this->page->getText()->willReturn('foo bar bar');
|
||||
$markupObject = $this->prophesize(MarkupInterface::class);
|
||||
$markupObject->__toString()->willReturn('bar');
|
||||
$this->assertNoUniqueText($markupObject->reveal());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::assertOptionSelected
|
||||
*/
|
||||
public function testAssertOptionSelected() {
|
||||
$option_field = $this->prophesize(NodeElement::class);
|
||||
$option_field->hasAttribute('selected')->willReturn(TRUE);
|
||||
|
||||
$this->webAssert
|
||||
->optionExists('myselect', 'two')
|
||||
->willReturn($option_field->reveal());
|
||||
|
||||
$this->assertOptionSelected('myselect', 'two');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::assertOptionSelected
|
||||
*/
|
||||
public function testAssertOptionSelectedFail() {
|
||||
$option_field = $this->prophesize(NodeElement::class);
|
||||
$option_field->hasAttribute('selected')->willReturn(FALSE);
|
||||
|
||||
$this->webAssert
|
||||
->optionExists('myselect', 'two')
|
||||
->willReturn($option_field->reveal());
|
||||
|
||||
$this->setExpectedException(PHPUnit_Framework_ExpectationFailedException::class);
|
||||
$this->assertOptionSelected('myselect', 'two');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a mocked behat session object.
|
||||
*
|
||||
* @return \Behat\Mink\Session
|
||||
* The mocked session.
|
||||
*/
|
||||
protected function getSession() {
|
||||
return $this->session->reveal();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function assertSession($name = NULL) {
|
||||
return $this->webAssert->reveal();
|
||||
}
|
||||
|
||||
}
|
||||
169
web/core/tests/Drupal/Tests/Core/Asset/AssetResolverTest.php
Normal file
169
web/core/tests/Drupal/Tests/Core/Asset/AssetResolverTest.php
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\Core\Asset\AssetResolverTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\Core\Asset;
|
||||
|
||||
use Drupal\Core\Asset\AssetResolver;
|
||||
use Drupal\Core\Asset\AttachedAssets;
|
||||
use Drupal\Core\Asset\AttachedAssetsInterface;
|
||||
use Drupal\Core\Cache\MemoryBackend;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Core\Asset\AssetResolver
|
||||
* @group Asset
|
||||
*/
|
||||
class AssetResolverTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The tested asset resolver service.
|
||||
*
|
||||
* @var \Drupal\Core\Asset\AssetResolver
|
||||
*/
|
||||
protected $assetResolver;
|
||||
|
||||
/**
|
||||
* The mocked library discovery service.
|
||||
*
|
||||
* @var \Drupal\Core\Asset\LibraryDiscoveryInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $libraryDiscovery;
|
||||
|
||||
/**
|
||||
* The mocked library dependency resolver.
|
||||
*
|
||||
* @var \Drupal\Core\Asset\LibraryDependencyResolverInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $libraryDependencyResolver;
|
||||
|
||||
/**
|
||||
* The mocked module handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* The mocked theme manager.
|
||||
*
|
||||
* @var \Drupal\Core\Theme\ThemeManagerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $themeManager;
|
||||
|
||||
/**
|
||||
* The mocked language manager.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* The cache backend to use.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->libraryDiscovery = $this->getMockBuilder('Drupal\Core\Asset\LibraryDiscovery')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->libraryDependencyResolver = $this->getMock('\Drupal\Core\Asset\LibraryDependencyResolverInterface');
|
||||
$this->libraryDependencyResolver->expects($this->any())
|
||||
->method('getLibrariesWithDependencies')
|
||||
->willReturnArgument(0);
|
||||
$this->moduleHandler = $this->getMock('\Drupal\Core\Extension\ModuleHandlerInterface');
|
||||
$this->themeManager = $this->getMock('\Drupal\Core\Theme\ThemeManagerInterface');
|
||||
$active_theme = $this->getMockBuilder('\Drupal\Core\Theme\ActiveTheme')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$active_theme->expects($this->any())
|
||||
->method('getName')
|
||||
->willReturn('bartik');
|
||||
$this->themeManager->expects($this->any())
|
||||
->method('getActiveTheme')
|
||||
->willReturn($active_theme);
|
||||
|
||||
$this->languageManager = $this->getMock('\Drupal\Core\Language\LanguageManagerInterface');
|
||||
$english = $this->getMock('\Drupal\Core\Language\LanguageInterface');
|
||||
$english->expects($this->any())
|
||||
->method('getId')
|
||||
->willReturn('en');
|
||||
$japanese = $this->getMock('\Drupal\Core\Language\LanguageInterface');
|
||||
$japanese->expects($this->any())
|
||||
->method('getId')
|
||||
->willReturn('jp');
|
||||
$this->languageManager = $this->getMock('\Drupal\Core\Language\LanguageManagerInterface');
|
||||
$this->languageManager->expects($this->any())
|
||||
->method('getCurrentLanguage')
|
||||
->will($this->onConsecutiveCalls($english, $english, $japanese, $japanese));
|
||||
$this->cache = new TestMemoryBackend('llama');
|
||||
|
||||
$this->assetResolver = new AssetResolver($this->libraryDiscovery, $this->libraryDependencyResolver, $this->moduleHandler, $this->themeManager, $this->languageManager, $this->cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getCssAssets
|
||||
* @dataProvider providerAttachedAssets
|
||||
*/
|
||||
public function testGetCssAssets(AttachedAssetsInterface $assets_a, AttachedAssetsInterface $assets_b, $expected_cache_item_count) {
|
||||
$this->assetResolver->getCssAssets($assets_a, FALSE);
|
||||
$this->assetResolver->getCssAssets($assets_b, FALSE);
|
||||
$this->assertCount($expected_cache_item_count, $this->cache->getAllCids());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getJsAssets
|
||||
* @dataProvider providerAttachedAssets
|
||||
*/
|
||||
public function testGetJsAssets(AttachedAssetsInterface $assets_a, AttachedAssetsInterface $assets_b, $expected_cache_item_count) {
|
||||
$this->assetResolver->getJsAssets($assets_a, FALSE);
|
||||
$this->assetResolver->getJsAssets($assets_b, FALSE);
|
||||
$this->assertCount($expected_cache_item_count, $this->cache->getAllCids());
|
||||
|
||||
$this->assetResolver->getJsAssets($assets_a, FALSE);
|
||||
$this->assetResolver->getJsAssets($assets_b, FALSE);
|
||||
$this->assertCount($expected_cache_item_count * 2, $this->cache->getAllCids());
|
||||
}
|
||||
|
||||
public function providerAttachedAssets() {
|
||||
$time = time();
|
||||
return [
|
||||
'same libraries, different timestamps' => [
|
||||
(new AttachedAssets())->setAlreadyLoadedLibraries([])->setLibraries(['core/drupal'])->setSettings(['currentTime' => $time]),
|
||||
(new AttachedAssets())->setAlreadyLoadedLibraries([])->setLibraries(['core/drupal'])->setSettings(['currentTime' => $time + 100]),
|
||||
1
|
||||
],
|
||||
'different libraries, same timestamps' => [
|
||||
(new AttachedAssets())->setAlreadyLoadedLibraries([])->setLibraries(['core/drupal'])->setSettings(['currenttime' => $time]),
|
||||
(new AttachedAssets())->setAlreadyLoadedLibraries([])->setLibraries(['core/drupal', 'core/jquery'])->setSettings(['currentTime' => $time]),
|
||||
2
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!defined('CSS_AGGREGATE_DEFAULT')) {
|
||||
define('CSS_AGGREGATE_DEFAULT', 0);
|
||||
}
|
||||
|
||||
if (!defined('JS_DEFAULT')) {
|
||||
define('JS_DEFAULT', 0);
|
||||
}
|
||||
|
||||
class TestMemoryBackend extends MemoryBackend {
|
||||
public function getAllCids() {
|
||||
return array_keys($this->cache);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Core\Asset;
|
||||
|
||||
use Drupal\Core\Asset\CssCollectionGrouper;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* Tests the CSS asset collection grouper.
|
||||
*
|
||||
* @group Asset
|
||||
*/
|
||||
class CssCollectionGrouperUnitTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* A CSS asset grouper.
|
||||
*
|
||||
* @var \Drupal\Core\Asset\CssCollectionGrouper object.
|
||||
*/
|
||||
protected $grouper;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->grouper = new CssCollectionGrouper();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests \Drupal\Core\Asset\CssCollectionGrouper.
|
||||
*/
|
||||
function testGrouper() {
|
||||
$css_assets = array(
|
||||
'system.base.css' => array(
|
||||
'group' => -100,
|
||||
'type' => 'file',
|
||||
'weight' => 0.012,
|
||||
'media' => 'all',
|
||||
'preprocess' => TRUE,
|
||||
'data' => 'core/modules/system/system.base.css',
|
||||
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
|
||||
'basename' => 'system.base.css',
|
||||
),
|
||||
'js.module.css' => array(
|
||||
'group' => -100,
|
||||
'type' => 'file',
|
||||
'weight' => 0.013,
|
||||
'media' => 'all',
|
||||
'preprocess' => TRUE,
|
||||
'data' => 'core/modules/system/js.module.css',
|
||||
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
|
||||
'basename' => 'js.module.css',
|
||||
),
|
||||
'jquery.ui.core.css' => array(
|
||||
'group' => -100,
|
||||
'type' => 'file',
|
||||
'weight' => 0.004,
|
||||
'media' => 'all',
|
||||
'preprocess' => TRUE,
|
||||
'data' => 'core/misc/ui/themes/base/jquery.ui.core.css',
|
||||
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
|
||||
'basename' => 'jquery.ui.core.css',
|
||||
),
|
||||
'field.css' => array(
|
||||
'group' => 0,
|
||||
'type' => 'file',
|
||||
'weight' => 0.011,
|
||||
'media' => 'all',
|
||||
'preprocess' => TRUE,
|
||||
'data' => 'core/modules/field/theme/field.css',
|
||||
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
|
||||
'basename' => 'field.css',
|
||||
),
|
||||
'external.css' => array(
|
||||
'group' => 0,
|
||||
'type' => 'external',
|
||||
'weight' => 0.009,
|
||||
'media' => 'all',
|
||||
'preprocess' => TRUE,
|
||||
'data' => 'http://example.com/external.css',
|
||||
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
|
||||
'basename' => 'external.css',
|
||||
),
|
||||
'elements.css' => array(
|
||||
'group' => 100,
|
||||
'media' => 'all',
|
||||
'type' => 'file',
|
||||
'weight' => 0.001,
|
||||
'preprocess' => TRUE,
|
||||
'data' => 'core/themes/bartik/css/base/elements.css',
|
||||
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
|
||||
'basename' => 'elements.css',
|
||||
),
|
||||
'print.css' => array(
|
||||
'group' => 100,
|
||||
'media' => 'print',
|
||||
'type' => 'file',
|
||||
'weight' => 0.003,
|
||||
'preprocess' => TRUE,
|
||||
'data' => 'core/themes/bartik/css/print.css',
|
||||
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
|
||||
'basename' => 'print.css',
|
||||
),
|
||||
);
|
||||
|
||||
$groups = $this->grouper->group($css_assets);
|
||||
|
||||
$this->assertSame(count($groups), 5, "5 groups created.");
|
||||
|
||||
// Check group 1.
|
||||
$group = $groups[0];
|
||||
$this->assertSame($group['group'], -100);
|
||||
$this->assertSame($group['type'], 'file');
|
||||
$this->assertSame($group['media'], 'all');
|
||||
$this->assertSame($group['preprocess'], TRUE);
|
||||
$this->assertSame(count($group['items']), 3);
|
||||
$this->assertContains($css_assets['system.base.css'], $group['items']);
|
||||
$this->assertContains($css_assets['js.module.css'], $group['items']);
|
||||
|
||||
// Check group 2.
|
||||
$group = $groups[1];
|
||||
$this->assertSame($group['group'], 0);
|
||||
$this->assertSame($group['type'], 'file');
|
||||
$this->assertSame($group['media'], 'all');
|
||||
$this->assertSame($group['preprocess'], TRUE);
|
||||
$this->assertSame(count($group['items']), 1);
|
||||
$this->assertContains($css_assets['field.css'], $group['items']);
|
||||
|
||||
// Check group 3.
|
||||
$group = $groups[2];
|
||||
$this->assertSame($group['group'], 0);
|
||||
$this->assertSame($group['type'], 'external');
|
||||
$this->assertSame($group['media'], 'all');
|
||||
$this->assertSame($group['preprocess'], TRUE);
|
||||
$this->assertSame(count($group['items']), 1);
|
||||
$this->assertContains($css_assets['external.css'], $group['items']);
|
||||
|
||||
// Check group 4.
|
||||
$group = $groups[3];
|
||||
$this->assertSame($group['group'], 100);
|
||||
$this->assertSame($group['type'], 'file');
|
||||
$this->assertSame($group['media'], 'all');
|
||||
$this->assertSame($group['preprocess'], TRUE);
|
||||
$this->assertSame(count($group['items']), 1);
|
||||
$this->assertContains($css_assets['elements.css'], $group['items']);
|
||||
|
||||
// Check group 5.
|
||||
$group = $groups[4];
|
||||
$this->assertSame($group['group'], 100);
|
||||
$this->assertSame($group['type'], 'file');
|
||||
$this->assertSame($group['media'], 'print');
|
||||
$this->assertSame($group['preprocess'], TRUE);
|
||||
$this->assertSame(count($group['items']), 1);
|
||||
$this->assertContains($css_assets['print.css'], $group['items']);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,519 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Core\Asset;
|
||||
|
||||
use Drupal\Core\Asset\CssCollectionRenderer;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* Tests the CSS asset collection renderer.
|
||||
*
|
||||
* @group Asset
|
||||
*/
|
||||
class CssCollectionRendererUnitTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* A CSS asset renderer.
|
||||
*
|
||||
* @var \Drupal\Core\Asset\CssRenderer object.
|
||||
*/
|
||||
protected $renderer;
|
||||
|
||||
/**
|
||||
* A valid file CSS asset group.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fileCssGroup;
|
||||
|
||||
/**
|
||||
* The state mock class.
|
||||
*
|
||||
* @var \Drupal\Core\State\StateInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->state = $this->getMock('Drupal\Core\State\StateInterface');
|
||||
|
||||
$this->renderer = new CssCollectionRenderer($this->state);
|
||||
$this->fileCssGroup = array(
|
||||
'group' => -100,
|
||||
'type' => 'file',
|
||||
'media' => 'all',
|
||||
'preprocess' => TRUE,
|
||||
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
|
||||
'items' => array(
|
||||
0 => array(
|
||||
'group' => -100,
|
||||
'type' => 'file',
|
||||
'weight' => 0.012,
|
||||
'media' => 'all',
|
||||
'preprocess' => TRUE,
|
||||
'data' => 'tests/Drupal/Tests/Core/Asset/foo.css',
|
||||
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
|
||||
'basename' => 'foo.css',
|
||||
),
|
||||
1 => array(
|
||||
'group' => -100,
|
||||
'type' => 'file',
|
||||
'weight' => 0.013,
|
||||
'media' => 'all',
|
||||
'preprocess' => TRUE,
|
||||
'data' => 'tests/Drupal/Tests/Core/Asset/bar.css',
|
||||
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
|
||||
'basename' => 'bar.css',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for the CSS asset rendering test.
|
||||
*
|
||||
* @see testRender
|
||||
*/
|
||||
function providerTestRender() {
|
||||
$create_link_element = function($href, $media = 'all', $browsers = array()) {
|
||||
return array(
|
||||
'#type' => 'html_tag',
|
||||
'#tag' => 'link',
|
||||
'#attributes' => array(
|
||||
'rel' => 'stylesheet',
|
||||
'href' => $href,
|
||||
'media' => $media,
|
||||
),
|
||||
'#browsers' => $browsers,
|
||||
);
|
||||
};
|
||||
$create_style_element = function($value, $media, $browsers = array()) {
|
||||
$style_element = array(
|
||||
'#type' => 'html_tag',
|
||||
'#tag' => 'style',
|
||||
'#value' => $value,
|
||||
'#attributes' => array(
|
||||
'media' => $media
|
||||
),
|
||||
'#browsers' => $browsers,
|
||||
);
|
||||
return $style_element;
|
||||
};
|
||||
|
||||
$create_file_css_asset = function($data, $media = 'all', $preprocess = TRUE) {
|
||||
return array('group' => 0, 'type' => 'file', 'media' => $media, 'preprocess' => $preprocess, 'data' => $data, 'browsers' => array());
|
||||
};
|
||||
|
||||
return array(
|
||||
// Single external CSS asset.
|
||||
0 => array(
|
||||
// CSS assets.
|
||||
array(
|
||||
0 => array('group' => 0, 'type' => 'external', 'media' => 'all', 'preprocess' => TRUE, 'data' => 'http://example.com/popular.js', 'browsers' => array()),
|
||||
),
|
||||
// Render elements.
|
||||
array(
|
||||
0 => $create_link_element('http://example.com/popular.js', 'all'),
|
||||
),
|
||||
),
|
||||
// Single file CSS asset.
|
||||
2 => array(
|
||||
array(
|
||||
0 => array('group' => 0, 'type' => 'file', 'media' => 'all', 'preprocess' => TRUE, 'data' => 'public://css/file-all', 'browsers' => array()),
|
||||
),
|
||||
array(
|
||||
0 => $create_link_element(file_url_transform_relative(file_create_url('public://css/file-all')) . '?0', 'all'),
|
||||
),
|
||||
),
|
||||
// 31 file CSS assets: expect 31 link elements.
|
||||
3 => array(
|
||||
array(
|
||||
0 => $create_file_css_asset('public://css/1.css'),
|
||||
1 => $create_file_css_asset('public://css/2.css'),
|
||||
2 => $create_file_css_asset('public://css/3.css'),
|
||||
3 => $create_file_css_asset('public://css/4.css'),
|
||||
4 => $create_file_css_asset('public://css/5.css'),
|
||||
5 => $create_file_css_asset('public://css/6.css'),
|
||||
6 => $create_file_css_asset('public://css/7.css'),
|
||||
7 => $create_file_css_asset('public://css/8.css'),
|
||||
8 => $create_file_css_asset('public://css/9.css'),
|
||||
9 => $create_file_css_asset('public://css/10.css'),
|
||||
10 => $create_file_css_asset('public://css/11.css'),
|
||||
11 => $create_file_css_asset('public://css/12.css'),
|
||||
12 => $create_file_css_asset('public://css/13.css'),
|
||||
13 => $create_file_css_asset('public://css/14.css'),
|
||||
14 => $create_file_css_asset('public://css/15.css'),
|
||||
15 => $create_file_css_asset('public://css/16.css'),
|
||||
16 => $create_file_css_asset('public://css/17.css'),
|
||||
17 => $create_file_css_asset('public://css/18.css'),
|
||||
18 => $create_file_css_asset('public://css/19.css'),
|
||||
19 => $create_file_css_asset('public://css/20.css'),
|
||||
20 => $create_file_css_asset('public://css/21.css'),
|
||||
21 => $create_file_css_asset('public://css/22.css'),
|
||||
22 => $create_file_css_asset('public://css/23.css'),
|
||||
23 => $create_file_css_asset('public://css/24.css'),
|
||||
24 => $create_file_css_asset('public://css/25.css'),
|
||||
25 => $create_file_css_asset('public://css/26.css'),
|
||||
26 => $create_file_css_asset('public://css/27.css'),
|
||||
27 => $create_file_css_asset('public://css/28.css'),
|
||||
28 => $create_file_css_asset('public://css/29.css'),
|
||||
29 => $create_file_css_asset('public://css/30.css'),
|
||||
30 => $create_file_css_asset('public://css/31.css'),
|
||||
),
|
||||
array(
|
||||
0 => $create_link_element(file_url_transform_relative(file_create_url('public://css/1.css')) . '?0'),
|
||||
1 => $create_link_element(file_url_transform_relative(file_create_url('public://css/2.css')) . '?0'),
|
||||
2 => $create_link_element(file_url_transform_relative(file_create_url('public://css/3.css')) . '?0'),
|
||||
3 => $create_link_element(file_url_transform_relative(file_create_url('public://css/4.css')) . '?0'),
|
||||
4 => $create_link_element(file_url_transform_relative(file_create_url('public://css/5.css')) . '?0'),
|
||||
5 => $create_link_element(file_url_transform_relative(file_create_url('public://css/6.css')) . '?0'),
|
||||
6 => $create_link_element(file_url_transform_relative(file_create_url('public://css/7.css')) . '?0'),
|
||||
7 => $create_link_element(file_url_transform_relative(file_create_url('public://css/8.css')) . '?0'),
|
||||
8 => $create_link_element(file_url_transform_relative(file_create_url('public://css/9.css')) . '?0'),
|
||||
9 => $create_link_element(file_url_transform_relative(file_create_url('public://css/10.css')) . '?0'),
|
||||
10 => $create_link_element(file_url_transform_relative(file_create_url('public://css/11.css')) . '?0'),
|
||||
11 => $create_link_element(file_url_transform_relative(file_create_url('public://css/12.css')) . '?0'),
|
||||
12 => $create_link_element(file_url_transform_relative(file_create_url('public://css/13.css')) . '?0'),
|
||||
13 => $create_link_element(file_url_transform_relative(file_create_url('public://css/14.css')) . '?0'),
|
||||
14 => $create_link_element(file_url_transform_relative(file_create_url('public://css/15.css')) . '?0'),
|
||||
15 => $create_link_element(file_url_transform_relative(file_create_url('public://css/16.css')) . '?0'),
|
||||
16 => $create_link_element(file_url_transform_relative(file_create_url('public://css/17.css')) . '?0'),
|
||||
17 => $create_link_element(file_url_transform_relative(file_create_url('public://css/18.css')) . '?0'),
|
||||
18 => $create_link_element(file_url_transform_relative(file_create_url('public://css/19.css')) . '?0'),
|
||||
19 => $create_link_element(file_url_transform_relative(file_create_url('public://css/20.css')) . '?0'),
|
||||
20 => $create_link_element(file_url_transform_relative(file_create_url('public://css/21.css')) . '?0'),
|
||||
21 => $create_link_element(file_url_transform_relative(file_create_url('public://css/22.css')) . '?0'),
|
||||
22 => $create_link_element(file_url_transform_relative(file_create_url('public://css/23.css')) . '?0'),
|
||||
23 => $create_link_element(file_url_transform_relative(file_create_url('public://css/24.css')) . '?0'),
|
||||
24 => $create_link_element(file_url_transform_relative(file_create_url('public://css/25.css')) . '?0'),
|
||||
25 => $create_link_element(file_url_transform_relative(file_create_url('public://css/26.css')) . '?0'),
|
||||
26 => $create_link_element(file_url_transform_relative(file_create_url('public://css/27.css')) . '?0'),
|
||||
27 => $create_link_element(file_url_transform_relative(file_create_url('public://css/28.css')) . '?0'),
|
||||
28 => $create_link_element(file_url_transform_relative(file_create_url('public://css/29.css')) . '?0'),
|
||||
29 => $create_link_element(file_url_transform_relative(file_create_url('public://css/30.css')) . '?0'),
|
||||
30 => $create_link_element(file_url_transform_relative(file_create_url('public://css/31.css')) . '?0'),
|
||||
),
|
||||
),
|
||||
// 32 file CSS assets with the same properties: expect 2 style elements.
|
||||
4 => array(
|
||||
array(
|
||||
0 => $create_file_css_asset('public://css/1.css'),
|
||||
1 => $create_file_css_asset('public://css/2.css'),
|
||||
2 => $create_file_css_asset('public://css/3.css'),
|
||||
3 => $create_file_css_asset('public://css/4.css'),
|
||||
4 => $create_file_css_asset('public://css/5.css'),
|
||||
5 => $create_file_css_asset('public://css/6.css'),
|
||||
6 => $create_file_css_asset('public://css/7.css'),
|
||||
7 => $create_file_css_asset('public://css/8.css'),
|
||||
8 => $create_file_css_asset('public://css/9.css'),
|
||||
9 => $create_file_css_asset('public://css/10.css'),
|
||||
10 => $create_file_css_asset('public://css/11.css'),
|
||||
11 => $create_file_css_asset('public://css/12.css'),
|
||||
12 => $create_file_css_asset('public://css/13.css'),
|
||||
13 => $create_file_css_asset('public://css/14.css'),
|
||||
14 => $create_file_css_asset('public://css/15.css'),
|
||||
15 => $create_file_css_asset('public://css/16.css'),
|
||||
16 => $create_file_css_asset('public://css/17.css'),
|
||||
17 => $create_file_css_asset('public://css/18.css'),
|
||||
18 => $create_file_css_asset('public://css/19.css'),
|
||||
19 => $create_file_css_asset('public://css/20.css'),
|
||||
20 => $create_file_css_asset('public://css/21.css'),
|
||||
21 => $create_file_css_asset('public://css/22.css'),
|
||||
22 => $create_file_css_asset('public://css/23.css'),
|
||||
23 => $create_file_css_asset('public://css/24.css'),
|
||||
24 => $create_file_css_asset('public://css/25.css'),
|
||||
25 => $create_file_css_asset('public://css/26.css'),
|
||||
26 => $create_file_css_asset('public://css/27.css'),
|
||||
27 => $create_file_css_asset('public://css/28.css'),
|
||||
28 => $create_file_css_asset('public://css/29.css'),
|
||||
29 => $create_file_css_asset('public://css/30.css'),
|
||||
30 => $create_file_css_asset('public://css/31.css'),
|
||||
31 => $create_file_css_asset('public://css/32.css'),
|
||||
),
|
||||
array(
|
||||
0 => $create_style_element('
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/1.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/2.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/3.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/4.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/5.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/6.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/7.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/8.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/9.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/10.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/11.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/12.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/13.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/14.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/15.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/16.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/17.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/18.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/19.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/20.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/21.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/22.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/23.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/24.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/25.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/26.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/27.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/28.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/29.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/30.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/31.css')) . '?0");
|
||||
', 'all'),
|
||||
1 => $create_style_element('
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/32.css')) . '?0");
|
||||
', 'all'),
|
||||
),
|
||||
),
|
||||
// 32 file CSS assets with the same properties, except for the 10th and
|
||||
// 20th files, they have different 'media' properties. Expect 5 style
|
||||
// elements.
|
||||
5 => array(
|
||||
array(
|
||||
0 => $create_file_css_asset('public://css/1.css'),
|
||||
1 => $create_file_css_asset('public://css/2.css'),
|
||||
2 => $create_file_css_asset('public://css/3.css'),
|
||||
3 => $create_file_css_asset('public://css/4.css'),
|
||||
4 => $create_file_css_asset('public://css/5.css'),
|
||||
5 => $create_file_css_asset('public://css/6.css'),
|
||||
6 => $create_file_css_asset('public://css/7.css'),
|
||||
7 => $create_file_css_asset('public://css/8.css'),
|
||||
8 => $create_file_css_asset('public://css/9.css'),
|
||||
9 => $create_file_css_asset('public://css/10.css', 'screen'),
|
||||
10 => $create_file_css_asset('public://css/11.css'),
|
||||
11 => $create_file_css_asset('public://css/12.css'),
|
||||
12 => $create_file_css_asset('public://css/13.css'),
|
||||
13 => $create_file_css_asset('public://css/14.css'),
|
||||
14 => $create_file_css_asset('public://css/15.css'),
|
||||
15 => $create_file_css_asset('public://css/16.css'),
|
||||
16 => $create_file_css_asset('public://css/17.css'),
|
||||
17 => $create_file_css_asset('public://css/18.css'),
|
||||
18 => $create_file_css_asset('public://css/19.css'),
|
||||
19 => $create_file_css_asset('public://css/20.css', 'print'),
|
||||
20 => $create_file_css_asset('public://css/21.css'),
|
||||
21 => $create_file_css_asset('public://css/22.css'),
|
||||
22 => $create_file_css_asset('public://css/23.css'),
|
||||
23 => $create_file_css_asset('public://css/24.css'),
|
||||
24 => $create_file_css_asset('public://css/25.css'),
|
||||
25 => $create_file_css_asset('public://css/26.css'),
|
||||
26 => $create_file_css_asset('public://css/27.css'),
|
||||
27 => $create_file_css_asset('public://css/28.css'),
|
||||
28 => $create_file_css_asset('public://css/29.css'),
|
||||
29 => $create_file_css_asset('public://css/30.css'),
|
||||
30 => $create_file_css_asset('public://css/31.css'),
|
||||
31 => $create_file_css_asset('public://css/32.css'),
|
||||
),
|
||||
array(
|
||||
0 => $create_style_element('
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/1.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/2.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/3.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/4.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/5.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/6.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/7.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/8.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/9.css')) . '?0");
|
||||
', 'all'),
|
||||
1 => $create_style_element('
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/10.css')) . '?0");
|
||||
', 'screen'),
|
||||
2 => $create_style_element('
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/11.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/12.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/13.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/14.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/15.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/16.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/17.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/18.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/19.css')) . '?0");
|
||||
', 'all'),
|
||||
3 => $create_style_element('
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/20.css')) . '?0");
|
||||
', 'print'),
|
||||
4 => $create_style_element('
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/21.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/22.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/23.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/24.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/25.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/26.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/27.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/28.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/29.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/30.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/31.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/32.css')) . '?0");
|
||||
', 'all'),
|
||||
),
|
||||
),
|
||||
// 32 file CSS assets with the same properties, except for the 15th, which
|
||||
// has 'preprocess' = FALSE. Expect 1 link element and 2 style elements.
|
||||
6 => array(
|
||||
array(
|
||||
0 => $create_file_css_asset('public://css/1.css'),
|
||||
1 => $create_file_css_asset('public://css/2.css'),
|
||||
2 => $create_file_css_asset('public://css/3.css'),
|
||||
3 => $create_file_css_asset('public://css/4.css'),
|
||||
4 => $create_file_css_asset('public://css/5.css'),
|
||||
5 => $create_file_css_asset('public://css/6.css'),
|
||||
6 => $create_file_css_asset('public://css/7.css'),
|
||||
7 => $create_file_css_asset('public://css/8.css'),
|
||||
8 => $create_file_css_asset('public://css/9.css'),
|
||||
9 => $create_file_css_asset('public://css/10.css'),
|
||||
10 => $create_file_css_asset('public://css/11.css'),
|
||||
11 => $create_file_css_asset('public://css/12.css'),
|
||||
12 => $create_file_css_asset('public://css/13.css'),
|
||||
13 => $create_file_css_asset('public://css/14.css'),
|
||||
14 => $create_file_css_asset('public://css/15.css', 'all', FALSE),
|
||||
15 => $create_file_css_asset('public://css/16.css'),
|
||||
16 => $create_file_css_asset('public://css/17.css'),
|
||||
17 => $create_file_css_asset('public://css/18.css'),
|
||||
18 => $create_file_css_asset('public://css/19.css'),
|
||||
19 => $create_file_css_asset('public://css/20.css'),
|
||||
20 => $create_file_css_asset('public://css/21.css'),
|
||||
21 => $create_file_css_asset('public://css/22.css'),
|
||||
22 => $create_file_css_asset('public://css/23.css'),
|
||||
23 => $create_file_css_asset('public://css/24.css'),
|
||||
24 => $create_file_css_asset('public://css/25.css'),
|
||||
25 => $create_file_css_asset('public://css/26.css'),
|
||||
26 => $create_file_css_asset('public://css/27.css'),
|
||||
27 => $create_file_css_asset('public://css/28.css'),
|
||||
28 => $create_file_css_asset('public://css/29.css'),
|
||||
29 => $create_file_css_asset('public://css/30.css'),
|
||||
30 => $create_file_css_asset('public://css/31.css'),
|
||||
31 => $create_file_css_asset('public://css/32.css'),
|
||||
),
|
||||
array(
|
||||
0 => $create_style_element('
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/1.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/2.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/3.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/4.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/5.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/6.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/7.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/8.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/9.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/10.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/11.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/12.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/13.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/14.css')) . '?0");
|
||||
', 'all'),
|
||||
1 => $create_link_element(file_url_transform_relative(file_create_url('public://css/15.css')) . '?0'),
|
||||
2 => $create_style_element('
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/16.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/17.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/18.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/19.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/20.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/21.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/22.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/23.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/24.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/25.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/26.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/27.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/28.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/29.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/30.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/31.css')) . '?0");
|
||||
@import url("' . file_url_transform_relative(file_create_url('public://css/32.css')) . '?0");
|
||||
', 'all'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests CSS asset rendering.
|
||||
*
|
||||
* @dataProvider providerTestRender
|
||||
*/
|
||||
function testRender(array $css_assets, array $render_elements) {
|
||||
$this->state->expects($this->once())
|
||||
->method('get')
|
||||
->with('system.css_js_query_string')
|
||||
->will($this->returnValue(NULL));
|
||||
$this->assertSame($render_elements, $this->renderer->render($css_assets));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a CSS asset group with the invalid 'type' => 'internal'.
|
||||
*/
|
||||
function testRenderInvalidType() {
|
||||
$this->state->expects($this->once())
|
||||
->method('get')
|
||||
->with('system.css_js_query_string')
|
||||
->will($this->returnValue(NULL));
|
||||
$this->setExpectedException('Exception', 'Invalid CSS asset type.');
|
||||
|
||||
$css_group = array(
|
||||
'group' => 0,
|
||||
'type' => 'internal',
|
||||
'media' => 'all',
|
||||
'preprocess' => TRUE,
|
||||
'browsers' => array(),
|
||||
'data' => 'http://example.com/popular.js'
|
||||
);
|
||||
$this->renderer->render($css_group);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporary mock for file_create_url(), until that is moved into
|
||||
* Component/Utility.
|
||||
*/
|
||||
if (!function_exists('Drupal\Tests\Core\Asset\file_create_url')) {
|
||||
function file_create_url($uri) {
|
||||
return 'file_create_url:' . $uri;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporary mock of file_url_transform_relative, until that is moved into
|
||||
* Component/Utility.
|
||||
*/
|
||||
if (!function_exists('Drupal\Tests\Core\Asset\file_url_transform_relative')) {
|
||||
function file_url_transform_relative($uri) {
|
||||
return 'file_url_transform_relative:' . $uri;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CssCollectionRenderer uses file_create_url() & file_url_transform_relative(),
|
||||
* which *are* available when using the Simpletest test runner, but not when
|
||||
* using the PHPUnit test runner; hence this hack.
|
||||
*/
|
||||
namespace Drupal\Core\Asset;
|
||||
|
||||
if (!function_exists('Drupal\Core\Asset\file_create_url')) {
|
||||
|
||||
/**
|
||||
* Temporary mock for file_create_url(), until that is moved into
|
||||
* Component/Utility.
|
||||
*/
|
||||
function file_create_url($uri) {
|
||||
return \Drupal\Tests\Core\Asset\file_create_url($uri);
|
||||
}
|
||||
|
||||
}
|
||||
if (!function_exists('Drupal\Core\Asset\file_url_transform_relative')) {
|
||||
|
||||
/**
|
||||
* Temporary mock of file_url_transform_relative, until that is moved into
|
||||
* Component/Utility.
|
||||
*/
|
||||
function file_url_transform_relative($uri) {
|
||||
return \Drupal\Tests\Core\Asset\file_url_transform_relative($uri);
|
||||
}
|
||||
|
||||
}
|
||||
309
web/core/tests/Drupal/Tests/Core/Asset/CssOptimizerUnitTest.php
Normal file
309
web/core/tests/Drupal/Tests/Core/Asset/CssOptimizerUnitTest.php
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Core\Asset;
|
||||
|
||||
use Drupal\Core\Asset\CssOptimizer;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* Tests the CSS asset optimizer.
|
||||
*
|
||||
* @group Asset
|
||||
*/
|
||||
class CssOptimizerUnitTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $backupGlobals = FALSE;
|
||||
|
||||
/**
|
||||
* A CSS asset optimizer.
|
||||
*
|
||||
* @var \Drupal\Core\Asset\CssOptimizer object.
|
||||
*/
|
||||
protected $optimizer;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->optimizer = new CssOptimizer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for the CSS asset optimizing test.
|
||||
*/
|
||||
function providerTestOptimize() {
|
||||
$path = 'core/tests/Drupal/Tests/Core/Asset/css_test_files/';
|
||||
$absolute_path = dirname(__FILE__) . '/css_test_files/';
|
||||
return array(
|
||||
// File. Tests:
|
||||
// - Stripped comments and white-space.
|
||||
// - Retain white-space in selectors. (https://www.drupal.org/node/472820)
|
||||
// - Retain pseudo-selectors. (https://www.drupal.org/node/460448)
|
||||
array(
|
||||
array(
|
||||
'group' => -100,
|
||||
'type' => 'file',
|
||||
'weight' => 0.012,
|
||||
'media' => 'all',
|
||||
'preprocess' => TRUE,
|
||||
'data' => $path . 'css_input_without_import.css',
|
||||
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
|
||||
'basename' => 'css_input_without_import.css',
|
||||
),
|
||||
file_get_contents($absolute_path . 'css_input_without_import.css.optimized.css'),
|
||||
),
|
||||
// File. Tests:
|
||||
// - Proper URLs in imported files. (https://www.drupal.org/node/265719)
|
||||
// - A background image with relative paths, which must be rewritten.
|
||||
// - The rewritten background image path must also be passed through
|
||||
// file_create_url(). (https://www.drupal.org/node/1961340)
|
||||
// - Imported files that are external (protocol-relative URL or not)
|
||||
// should not be expanded. (https://www.drupal.org/node/2014851)
|
||||
array(
|
||||
array(
|
||||
'group' => -100,
|
||||
'type' => 'file',
|
||||
'weight' => 0.013,
|
||||
'media' => 'all',
|
||||
'preprocess' => TRUE,
|
||||
'data' => $path . 'css_input_with_import.css',
|
||||
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
|
||||
'basename' => 'css_input_with_import.css',
|
||||
),
|
||||
str_replace('url(images/icon.png)', 'url(' . file_url_transform_relative(file_create_url($path . 'images/icon.png')) . ')', file_get_contents($absolute_path . 'css_input_with_import.css.optimized.css')),
|
||||
),
|
||||
// File. Tests:
|
||||
// - Retain comment hacks.
|
||||
array(
|
||||
array(
|
||||
'group' => -100,
|
||||
'type' => 'file',
|
||||
'weight' => 0.013,
|
||||
'media' => 'all',
|
||||
'preprocess' => TRUE,
|
||||
'data' => $path . 'comment_hacks.css',
|
||||
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
|
||||
'basename' => 'comment_hacks.css',
|
||||
),
|
||||
file_get_contents($absolute_path . 'comment_hacks.css.optimized.css'),
|
||||
),
|
||||
// File in subfolder. Tests:
|
||||
// - CSS import path is properly interpreted.
|
||||
// (https://www.drupal.org/node/1198904)
|
||||
// - Don't adjust data URIs (https://www.drupal.org/node/2142441)
|
||||
array(
|
||||
array(
|
||||
'group' => -100,
|
||||
'type' => 'file',
|
||||
'weight' => 0.013,
|
||||
'media' => 'all',
|
||||
'preprocess' => TRUE,
|
||||
'data' => $path . 'css_subfolder/css_input_with_import.css',
|
||||
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
|
||||
'basename' => 'css_input_with_import.css',
|
||||
),
|
||||
str_replace('url(../images/icon.png)', 'url(' . file_url_transform_relative(file_create_url($path . 'images/icon.png')) . ')', file_get_contents($absolute_path . 'css_subfolder/css_input_with_import.css.optimized.css')),
|
||||
),
|
||||
// File. Tests:
|
||||
// - Any @charaset declaration at the beginning of a file should be
|
||||
// removed without breaking subsequent CSS.
|
||||
array(
|
||||
array(
|
||||
'group' => -100,
|
||||
'type' => 'file',
|
||||
'weight' => 0.013,
|
||||
'media' => 'all',
|
||||
'preprocess' => TRUE,
|
||||
'data' => $path . 'charset_sameline.css',
|
||||
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
|
||||
'basename' => 'charset_sameline.css',
|
||||
),
|
||||
file_get_contents($absolute_path . 'charset.css.optimized.css'),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'group' => -100,
|
||||
'type' => 'file',
|
||||
'weight' => 0.013,
|
||||
'media' => 'all',
|
||||
'preprocess' => TRUE,
|
||||
'data' => $path . 'charset_newline.css',
|
||||
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
|
||||
'basename' => 'charset_newline.css',
|
||||
),
|
||||
file_get_contents($absolute_path . 'charset.css.optimized.css'),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'group' => -100,
|
||||
'type' => 'file',
|
||||
'weight' => 0.013,
|
||||
'media' => 'all',
|
||||
'preprocess' => TRUE,
|
||||
'data' => $path . 'css_input_with_bom.css',
|
||||
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
|
||||
'basename' => 'css_input_with_bom.css',
|
||||
),
|
||||
'.byte-order-mark-test{content:"☃";}' . "\n",
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'group' => -100,
|
||||
'type' => 'file',
|
||||
'weight' => 0.013,
|
||||
'media' => 'all',
|
||||
'preprocess' => TRUE,
|
||||
'data' => $path . 'css_input_with_charset.css',
|
||||
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
|
||||
'basename' => 'css_input_with_charset.css',
|
||||
),
|
||||
'.charset-test{content:"€";}' . "\n",
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'group' => -100,
|
||||
'type' => 'file',
|
||||
'weight' => 0.013,
|
||||
'media' => 'all',
|
||||
'preprocess' => TRUE,
|
||||
'data' => $path . 'css_input_with_bom_and_charset.css',
|
||||
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
|
||||
'basename' => 'css_input_with_bom_and_charset.css',
|
||||
),
|
||||
'.byte-order-mark-charset-test{content:"☃";}' . "\n",
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'group' => -100,
|
||||
'type' => 'file',
|
||||
'weight' => 0.013,
|
||||
'media' => 'all',
|
||||
'preprocess' => TRUE,
|
||||
'data' => $path . 'css_input_with_utf16_bom.css',
|
||||
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
|
||||
'basename' => 'css_input_with_utf16_bom.css',
|
||||
),
|
||||
'.utf16-byte-order-mark-test{content:"☃";}' . "\n",
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests optimizing a CSS asset group containing 'type' => 'file'.
|
||||
*
|
||||
* @dataProvider providerTestOptimize
|
||||
*/
|
||||
function testOptimize($css_asset, $expected) {
|
||||
global $base_path;
|
||||
$original_base_path = $base_path;
|
||||
$base_path = '/';
|
||||
|
||||
// \Drupal\Core\Asset\CssOptimizer::loadFile() relies on the current working
|
||||
// directory being the one that is used when index.php is the entry point.
|
||||
// Note: PHPUnit automatically restores the original working directory.
|
||||
chdir(realpath(__DIR__ . '/../../../../../../'));
|
||||
|
||||
$this->assertEquals($expected, $this->optimizer->optimize($css_asset), 'Group of file CSS assets optimized correctly.');
|
||||
|
||||
$base_path = $original_base_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a file CSS asset with preprocessing disabled.
|
||||
*/
|
||||
function testTypeFilePreprocessingDisabled() {
|
||||
$this->setExpectedException('Exception', 'Only file CSS assets with preprocessing enabled can be optimized.');
|
||||
|
||||
$css_asset = array(
|
||||
'group' => -100,
|
||||
'type' => 'file',
|
||||
'weight' => 0.012,
|
||||
'media' => 'all',
|
||||
// Preprocessing disabled.
|
||||
'preprocess' => FALSE,
|
||||
'data' => 'tests/Drupal/Tests/Core/Asset/foo.css',
|
||||
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
|
||||
'basename' => 'foo.css',
|
||||
);
|
||||
$this->optimizer->optimize($css_asset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a CSS asset with 'type' => 'external'.
|
||||
*/
|
||||
function testTypeExternal() {
|
||||
$this->setExpectedException('Exception', 'Only file CSS assets can be optimized.');
|
||||
|
||||
$css_asset = array(
|
||||
'group' => -100,
|
||||
// Type external.
|
||||
'type' => 'external',
|
||||
'weight' => 0.012,
|
||||
'media' => 'all',
|
||||
'preprocess' => TRUE,
|
||||
'data' => 'http://example.com/foo.js',
|
||||
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
|
||||
);
|
||||
$this->optimizer->optimize($css_asset);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporary mock for file_create_url(), until that is moved into
|
||||
* Component/Utility.
|
||||
*/
|
||||
if (!function_exists('Drupal\Tests\Core\Asset\file_create_url')) {
|
||||
function file_create_url($uri) {
|
||||
return 'file_create_url:' . $uri;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporary mock of file_url_transform_relative, until that is moved into
|
||||
* Component/Utility.
|
||||
*/
|
||||
if (!function_exists('Drupal\Tests\Core\Asset\file_url_transform_relative')) {
|
||||
function file_url_transform_relative($uri) {
|
||||
return 'file_url_transform_relative:' . $uri;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CssCollectionRenderer uses file_create_url() & file_url_transform_relative(),
|
||||
* which *are* available when using the Simpletest test runner, but not when
|
||||
* using the PHPUnit test runner; hence this hack.
|
||||
*/
|
||||
namespace Drupal\Core\Asset;
|
||||
|
||||
if (!function_exists('Drupal\Core\Asset\file_create_url')) {
|
||||
|
||||
/**
|
||||
* Temporary mock for file_create_url(), until that is moved into
|
||||
* Component/Utility.
|
||||
*/
|
||||
function file_create_url($uri) {
|
||||
return \Drupal\Tests\Core\Asset\file_create_url($uri);
|
||||
}
|
||||
|
||||
}
|
||||
if (!function_exists('Drupal\Core\Asset\file_url_transform_relative')) {
|
||||
|
||||
/**
|
||||
* Temporary mock of file_url_transform_relative, until that is moved into
|
||||
* Component/Utility.
|
||||
*/
|
||||
function file_url_transform_relative($uri) {
|
||||
return \Drupal\Tests\Core\Asset\file_url_transform_relative($uri);
|
||||
}
|
||||
|
||||
}
|
||||
if (!function_exists('Drupal\Core\Asset\file_uri_scheme')) {
|
||||
|
||||
function file_uri_scheme($uri) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
126
web/core/tests/Drupal/Tests/Core/Asset/JsOptimizerUnitTest.php
Normal file
126
web/core/tests/Drupal/Tests/Core/Asset/JsOptimizerUnitTest.php
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Core\Asset;
|
||||
|
||||
use Drupal\Core\Asset\JsOptimizer;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* Tests the JS asset optimizer.
|
||||
*
|
||||
* @group Asset
|
||||
*/
|
||||
class JsOptimizerUnitTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* A JS asset optimizer.
|
||||
*
|
||||
* @var \Drupal\Core\Asset\JsOptimizer object.
|
||||
*/
|
||||
protected $optimizer;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->optimizer = new JsOptimizer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for the JS asset cleaning test.
|
||||
*
|
||||
* @see \Drupal\Core\Asset\JsOptimizer::clean()
|
||||
*
|
||||
* @returns array
|
||||
* An array of test data.
|
||||
*/
|
||||
function providerTestClean() {
|
||||
$path = dirname(__FILE__) . '/js_test_files/';
|
||||
return array(
|
||||
// File. Tests:
|
||||
// - Stripped sourceMappingURL with comment # syntax.
|
||||
0 => array(
|
||||
file_get_contents($path . 'source_mapping_url.min.js'),
|
||||
file_get_contents($path . 'source_mapping_url.min.js.optimized.js'),
|
||||
),
|
||||
// File. Tests:
|
||||
// - Stripped sourceMappingURL with comment @ syntax.
|
||||
1 => array(
|
||||
file_get_contents($path . 'source_mapping_url_old.min.js'),
|
||||
file_get_contents($path . 'source_mapping_url_old.min.js.optimized.js'),
|
||||
),
|
||||
// File. Tests:
|
||||
// - Stripped sourceURL with comment # syntax.
|
||||
2 => array(
|
||||
file_get_contents($path . 'source_url.min.js'),
|
||||
file_get_contents($path . 'source_url.min.js.optimized.js'),
|
||||
),
|
||||
// File. Tests:
|
||||
// - Stripped sourceURL with comment @ syntax.
|
||||
3 => array(
|
||||
file_get_contents($path . 'source_url_old.min.js'),
|
||||
file_get_contents($path . 'source_url_old.min.js.optimized.js'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests cleaning of a JS asset group containing 'type' => 'file'.
|
||||
*
|
||||
* @dataProvider providerTestClean
|
||||
*/
|
||||
function testClean($js_asset, $expected) {
|
||||
$this->assertEquals($expected, $this->optimizer->clean($js_asset));
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for the JS asset optimize test.
|
||||
*
|
||||
* @see \Drupal\Core\Asset\JsOptimizer::optimize()
|
||||
*
|
||||
* @returns array
|
||||
* An array of test data.
|
||||
*/
|
||||
function providerTestOptimize() {
|
||||
$path = dirname(__FILE__) . '/js_test_files/';
|
||||
return array(
|
||||
0 => array(
|
||||
array(
|
||||
'type' => 'file',
|
||||
'preprocess' => TRUE,
|
||||
'data' => $path . 'utf8_bom.js',
|
||||
),
|
||||
file_get_contents($path . 'utf8_bom.js.optimized.js'),
|
||||
),
|
||||
1 => array(
|
||||
array(
|
||||
'type' => 'file',
|
||||
'preprocess' => TRUE,
|
||||
'data' => $path . 'utf16_bom.js',
|
||||
),
|
||||
file_get_contents($path . 'utf16_bom.js.optimized.js'),
|
||||
),
|
||||
2 => array(
|
||||
array(
|
||||
'type' => 'file',
|
||||
'preprocess' => TRUE,
|
||||
'data' => $path . 'latin_9.js',
|
||||
'attributes' => array('charset' => 'ISO-8859-15'),
|
||||
),
|
||||
file_get_contents($path . 'latin_9.js.optimized.js'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests cleaning of a JS asset group containing 'type' => 'file'.
|
||||
*
|
||||
* @dataProvider providerTestOptimize
|
||||
*/
|
||||
function testOptimize($js_asset, $expected) {
|
||||
$this->assertEquals($expected, $this->optimizer->optimize($js_asset));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Core\Asset;
|
||||
|
||||
use Drupal\Core\Asset\LibraryDependencyResolver;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Core\Asset\LibraryDependencyResolver
|
||||
* @group Asset
|
||||
*/
|
||||
class LibraryDependencyResolverTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The tested library dependency resolver.
|
||||
*
|
||||
* @var \Drupal\Core\Asset\LibraryDependencyResolver
|
||||
*/
|
||||
protected $libraryDependencyResolver;
|
||||
|
||||
/**
|
||||
* The mocked library discovery service.
|
||||
*
|
||||
* @var \Drupal\Core\Asset\LibraryDiscoveryInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $libraryDiscovery;
|
||||
|
||||
/**
|
||||
* The mocked module handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* Test library data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $libraryData = array(
|
||||
'no_deps_a' => ['js' => [], 'css' => []],
|
||||
'no_deps_b' => ['js' => [], 'css' => []],
|
||||
'no_deps_c' => ['js' => [], 'css' => []],
|
||||
'deps_a' => ['js' => [], 'css' => [], 'dependencies' => ['test/no_deps_a']],
|
||||
'deps_b' => ['js' => [], 'css' => [], 'dependencies' => ['test/no_deps_a', 'test/no_deps_b']],
|
||||
'deps_c' => ['js' => [], 'css' => [], 'dependencies' => ['test/no_deps_b', 'test/no_deps_a']],
|
||||
'nested_deps_a' => ['js' => [], 'css' => [], 'dependencies' => ['test/deps_a']],
|
||||
'nested_deps_b' => ['js' => [], 'css' => [], 'dependencies' => ['test/nested_deps_a']],
|
||||
'nested_deps_c' => ['js' => [], 'css' => [], 'dependencies' => ['test/nested_deps_b']],
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
$this->libraryDiscovery = $this->getMockBuilder('Drupal\Core\Asset\LibraryDiscovery')
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(['getLibrariesByExtension'])
|
||||
->getMock();
|
||||
$this->libraryDiscovery->expects($this->any())
|
||||
->method('getLibrariesByExtension')
|
||||
->with('test')
|
||||
->will($this->returnValue($this->libraryData));
|
||||
$this->libraryDependencyResolver = new LibraryDependencyResolver($this->libraryDiscovery);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provides test data for ::testGetLibrariesWithDependencies().
|
||||
*/
|
||||
public function providerTestGetLibrariesWithDependencies() {
|
||||
return [
|
||||
// Empty list of libraries.
|
||||
[[], []],
|
||||
// Without dependencies.
|
||||
[['test/no_deps_a'], ['test/no_deps_a']],
|
||||
[['test/no_deps_a', 'test/no_deps_b'], ['test/no_deps_a', 'test/no_deps_b']],
|
||||
[['test/no_deps_b', 'test/no_deps_a'], ['test/no_deps_b', 'test/no_deps_a']],
|
||||
// Single-level (direct) dependencies.
|
||||
[['test/deps_a'], ['test/no_deps_a', 'test/deps_a']],
|
||||
[['test/deps_b'], ['test/no_deps_a', 'test/no_deps_b', 'test/deps_b']],
|
||||
[['test/deps_c'], ['test/no_deps_b', 'test/no_deps_a', 'test/deps_c']],
|
||||
[['test/deps_a', 'test/deps_b'], ['test/no_deps_a', 'test/deps_a', 'test/no_deps_b', 'test/deps_b']],
|
||||
[['test/deps_a', 'test/deps_c'], ['test/no_deps_a', 'test/deps_a', 'test/no_deps_b', 'test/deps_c']],
|
||||
[['test/deps_a', 'test/deps_b', 'test/deps_c'], ['test/no_deps_a', 'test/deps_a', 'test/no_deps_b', 'test/deps_b', 'test/deps_c']],
|
||||
[['test/deps_b', 'test/deps_a'], ['test/no_deps_a', 'test/no_deps_b', 'test/deps_b', 'test/deps_a']],
|
||||
[['test/deps_b', 'test/deps_c'], ['test/no_deps_a', 'test/no_deps_b', 'test/deps_b', 'test/deps_c']],
|
||||
[['test/deps_c', 'test/deps_b'], ['test/no_deps_b', 'test/no_deps_a', 'test/deps_c', 'test/deps_b']],
|
||||
// Multi-level (indirect) dependencies.
|
||||
[['test/nested_deps_a'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a']],
|
||||
[['test/nested_deps_b'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b']],
|
||||
[['test/nested_deps_c'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b', 'test/nested_deps_c']],
|
||||
[['test/nested_deps_a', 'test/nested_deps_b'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b']],
|
||||
[['test/nested_deps_b', 'test/nested_deps_a'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b']],
|
||||
[['test/nested_deps_a', 'test/nested_deps_c'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b', 'test/nested_deps_c']],
|
||||
[['test/nested_deps_b', 'test/nested_deps_c'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b', 'test/nested_deps_c']],
|
||||
[['test/nested_deps_c', 'test/nested_deps_a'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b', 'test/nested_deps_c']],
|
||||
[['test/nested_deps_a', 'test/nested_deps_b', 'test/nested_deps_c'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b', 'test/nested_deps_c']],
|
||||
[['test/nested_deps_a', 'test/nested_deps_c', 'test/nested_deps_b'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b', 'test/nested_deps_c']],
|
||||
[['test/nested_deps_b', 'test/nested_deps_a', 'test/nested_deps_c'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b', 'test/nested_deps_c']],
|
||||
[['test/nested_deps_b', 'test/nested_deps_c', 'test/nested_deps_a'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b', 'test/nested_deps_c']],
|
||||
[['test/nested_deps_c', 'test/nested_deps_a', 'test/nested_deps_b'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b', 'test/nested_deps_c']],
|
||||
[['test/nested_deps_c', 'test/nested_deps_b', 'test/nested_deps_a'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b', 'test/nested_deps_c']],
|
||||
// Complex dependencies, combining the above, with many intersections.
|
||||
[['test/deps_c', 'test/nested_deps_b'], ['test/no_deps_b', 'test/no_deps_a', 'test/deps_c', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b']],
|
||||
[['test/no_deps_a', 'test/deps_c', 'test/nested_deps_b'], ['test/no_deps_a', 'test/no_deps_b', 'test/deps_c', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b']],
|
||||
[['test/nested_deps_b', 'test/deps_c', 'test/no_deps_c'], ['test/no_deps_a', 'test/deps_a', 'test/nested_deps_a', 'test/nested_deps_b', 'test/no_deps_b', 'test/deps_c', 'test/no_deps_c']],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getLibrariesWithDependencies
|
||||
*
|
||||
* @dataProvider providerTestGetLibrariesWithDependencies
|
||||
*/
|
||||
public function testGetLibrariesWithDependencies(array $libraries, array $expected) {
|
||||
$this->assertEquals($expected, $this->libraryDependencyResolver->getLibrariesWithDependencies($libraries));
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides test data for ::testGetMinimalRepresentativeSubset().
|
||||
*/
|
||||
public function providerTestGetMinimalRepresentativeSubset() {
|
||||
return [
|
||||
// Empty list of libraries.
|
||||
[[], []],
|
||||
// Without dependencies.
|
||||
[['test/no_deps_a'], ['test/no_deps_a']],
|
||||
[['test/no_deps_a', 'test/no_deps_b'], ['test/no_deps_a', 'test/no_deps_b']],
|
||||
[['test/no_deps_b', 'test/no_deps_a'], ['test/no_deps_b', 'test/no_deps_a']],
|
||||
// Single-level (direct) dependencies.
|
||||
[['test/deps_a'], ['test/deps_a']],
|
||||
[['test/deps_b'], ['test/deps_b']],
|
||||
[['test/deps_c'], ['test/deps_c']],
|
||||
[['test/deps_a', 'test/deps_b'], ['test/deps_a', 'test/deps_b']],
|
||||
[['test/deps_a', 'test/deps_c'], ['test/deps_a', 'test/deps_c']],
|
||||
[['test/deps_a', 'test/deps_b', 'test/deps_c'], ['test/deps_a', 'test/deps_b', 'test/deps_c']],
|
||||
[['test/deps_b', 'test/deps_a'], ['test/deps_b', 'test/deps_a']],
|
||||
[['test/deps_b', 'test/deps_c'], ['test/deps_b', 'test/deps_c']],
|
||||
[['test/deps_c', 'test/deps_b'], ['test/deps_c', 'test/deps_b']],
|
||||
// Multi-level (indirect) dependencies.
|
||||
[['test/nested_deps_a'], ['test/nested_deps_a']],
|
||||
[['test/nested_deps_b'], ['test/nested_deps_b']],
|
||||
[['test/nested_deps_c'], ['test/nested_deps_c']],
|
||||
[['test/nested_deps_a', 'test/nested_deps_b'], ['test/nested_deps_b']],
|
||||
[['test/nested_deps_b', 'test/nested_deps_a'], ['test/nested_deps_b']],
|
||||
[['test/nested_deps_a', 'test/nested_deps_c'], ['test/nested_deps_c']],
|
||||
[['test/nested_deps_b', 'test/nested_deps_c'], ['test/nested_deps_c']],
|
||||
[['test/nested_deps_c', 'test/nested_deps_a'], ['test/nested_deps_c']],
|
||||
[['test/nested_deps_a', 'test/nested_deps_b', 'test/nested_deps_c'], ['test/nested_deps_c']],
|
||||
[['test/nested_deps_a', 'test/nested_deps_c', 'test/nested_deps_b'], ['test/nested_deps_c']],
|
||||
[['test/nested_deps_b', 'test/nested_deps_a', 'test/nested_deps_c'], ['test/nested_deps_c']],
|
||||
[['test/nested_deps_b', 'test/nested_deps_c', 'test/nested_deps_a'], ['test/nested_deps_c']],
|
||||
[['test/nested_deps_c', 'test/nested_deps_a', 'test/nested_deps_b'], ['test/nested_deps_c']],
|
||||
[['test/nested_deps_c', 'test/nested_deps_b', 'test/nested_deps_a'], ['test/nested_deps_c']],
|
||||
// Complex dependencies, combining the above, with many intersections.
|
||||
[['test/deps_c', 'test/nested_deps_b'], ['test/deps_c', 'test/nested_deps_b']],
|
||||
[['test/no_deps_a', 'test/deps_c', 'test/nested_deps_b'], ['test/deps_c', 'test/nested_deps_b']],
|
||||
[['test/nested_deps_b', 'test/deps_c', 'test/no_deps_c'], ['test/nested_deps_b', 'test/deps_c', 'test/no_deps_c']],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getMinimalRepresentativeSubset
|
||||
*
|
||||
* @dataProvider providerTestGetMinimalRepresentativeSubset
|
||||
*/
|
||||
public function testGetMinimalRepresentativeSubset(array $libraries, array $expected) {
|
||||
$this->assertEquals($expected, $this->libraryDependencyResolver->getMinimalRepresentativeSubset($libraries));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Core\Asset;
|
||||
|
||||
use Drupal\Core\Asset\LibraryDiscoveryCollector;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Core\Asset\LibraryDiscoveryCollector
|
||||
* @group Asset
|
||||
*/
|
||||
class LibraryDiscoveryCollectorTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The mock cache backend.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* The mock lock backend.
|
||||
*
|
||||
* @var \Drupal\Core\Lock\LockBackendInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $lock;
|
||||
|
||||
/**
|
||||
* The mock library discovery parser.
|
||||
*
|
||||
* @var \Drupal\Core\Asset\LibraryDiscoveryParser|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $libraryDiscoveryParser;
|
||||
|
||||
/**
|
||||
* The library discovery collector under test.
|
||||
*
|
||||
* @var \Drupal\Core\Asset\LibraryDiscoveryCollector
|
||||
*/
|
||||
protected $libraryDiscoveryCollector;
|
||||
|
||||
/**
|
||||
* The mocked theme manager.
|
||||
*
|
||||
* @var \Drupal\Core\Theme\ThemeManagerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $themeManager;
|
||||
|
||||
/**
|
||||
* Test library data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $libraryData = array(
|
||||
'test_1' => array(
|
||||
'js' => array(),
|
||||
'css' => array(),
|
||||
),
|
||||
'test_2' => array(
|
||||
'js' => array(),
|
||||
'css' => array(),
|
||||
),
|
||||
);
|
||||
|
||||
protected $activeTheme;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
$this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
|
||||
$this->lock = $this->getMock('Drupal\Core\Lock\LockBackendInterface');
|
||||
$this->themeManager = $this->getMockBuilder('Drupal\Core\Theme\ThemeManagerInterface')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->libraryDiscoveryParser = $this->getMockBuilder('Drupal\Core\Asset\LibraryDiscoveryParser')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the resolve cache miss function.
|
||||
*
|
||||
* @covers ::resolveCacheMiss
|
||||
*/
|
||||
public function testResolveCacheMiss() {
|
||||
$this->activeTheme = $this->getMockBuilder('Drupal\Core\Theme\ActiveTheme')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->themeManager->expects($this->exactly(3))
|
||||
->method('getActiveTheme')
|
||||
->willReturn($this->activeTheme);
|
||||
$this->activeTheme->expects($this->once())
|
||||
->method('getName')
|
||||
->willReturn('kitten_theme');
|
||||
$this->libraryDiscoveryCollector = new LibraryDiscoveryCollector($this->cache, $this->lock, $this->libraryDiscoveryParser, $this->themeManager);
|
||||
|
||||
$this->libraryDiscoveryParser->expects($this->once())
|
||||
->method('buildByExtension')
|
||||
->with('test')
|
||||
->willReturn($this->libraryData);
|
||||
|
||||
$this->assertSame($this->libraryData, $this->libraryDiscoveryCollector->get('test'));
|
||||
$this->assertSame($this->libraryData, $this->libraryDiscoveryCollector->get('test'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the destruct method.
|
||||
*
|
||||
* @covers ::destruct
|
||||
*/
|
||||
public function testDestruct() {
|
||||
$this->activeTheme = $this->getMockBuilder('Drupal\Core\Theme\ActiveTheme')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->themeManager->expects($this->exactly(3))
|
||||
->method('getActiveTheme')
|
||||
->willReturn($this->activeTheme);
|
||||
$this->activeTheme->expects($this->once())
|
||||
->method('getName')
|
||||
->willReturn('kitten_theme');
|
||||
$this->libraryDiscoveryCollector = new LibraryDiscoveryCollector($this->cache, $this->lock, $this->libraryDiscoveryParser, $this->themeManager);
|
||||
|
||||
$this->libraryDiscoveryParser->expects($this->once())
|
||||
->method('buildByExtension')
|
||||
->with('test')
|
||||
->willReturn($this->libraryData);
|
||||
|
||||
$lock_key = 'library_info:kitten_theme:Drupal\Core\Cache\CacheCollector';
|
||||
|
||||
$this->lock->expects($this->once())
|
||||
->method('acquire')
|
||||
->with($lock_key)
|
||||
->will($this->returnValue(TRUE));
|
||||
$this->cache->expects($this->exactly(2))
|
||||
->method('get')
|
||||
->with('library_info:kitten_theme')
|
||||
->willReturn(FALSE);
|
||||
$this->cache->expects($this->once())
|
||||
->method('set')
|
||||
->with('library_info:kitten_theme', array('test' => $this->libraryData), Cache::PERMANENT, ['library_info']);
|
||||
$this->lock->expects($this->once())
|
||||
->method('release')
|
||||
->with($lock_key);
|
||||
|
||||
// This should get data and persist the key.
|
||||
$this->libraryDiscoveryCollector->get('test');
|
||||
$this->libraryDiscoveryCollector->destruct();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,569 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\Core\Asset\LibraryDiscoveryParserTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\Core\Asset;
|
||||
|
||||
use Drupal\Core\Asset\LibraryDiscoveryParser;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Core\Asset\LibraryDiscoveryParser
|
||||
* @group Asset
|
||||
*/
|
||||
class LibraryDiscoveryParserTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The tested library discovery parser service.
|
||||
*
|
||||
* @var \Drupal\Core\Asset\LibraryDiscoveryParser|\Drupal\Tests\Core\Asset\TestLibraryDiscoveryParser
|
||||
*/
|
||||
protected $libraryDiscoveryParser;
|
||||
|
||||
/**
|
||||
* The mocked cache backend.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* The mocked module handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* The mocked theme manager.
|
||||
*
|
||||
* @var \Drupal\Core\Theme\ThemeManagerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $themeManager;
|
||||
|
||||
/**
|
||||
* The mocked lock backend.
|
||||
*
|
||||
* @var \Drupal\Core\Lock\LockBackendInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $lock;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
|
||||
$this->themeManager = $this->getMock('Drupal\Core\Theme\ThemeManagerInterface');
|
||||
$mock_active_theme = $this->getMockBuilder('Drupal\Core\Theme\ActiveTheme')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$mock_active_theme->expects($this->any())
|
||||
->method('getLibrariesOverride')
|
||||
->willReturn([]);
|
||||
$this->themeManager->expects($this->any())
|
||||
->method('getActiveTheme')
|
||||
->willReturn($mock_active_theme);
|
||||
$this->libraryDiscoveryParser = new TestLibraryDiscoveryParser($this->root, $this->moduleHandler, $this->themeManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that basic functionality works for getLibraryByName.
|
||||
*
|
||||
* @covers ::buildByExtension
|
||||
*/
|
||||
public function testBuildByExtensionSimple() {
|
||||
$this->moduleHandler->expects($this->atLeastOnce())
|
||||
->method('moduleExists')
|
||||
->with('example_module')
|
||||
->will($this->returnValue(TRUE));
|
||||
|
||||
$path = __DIR__ . '/library_test_files';
|
||||
$path = substr($path, strlen($this->root) + 1);
|
||||
$this->libraryDiscoveryParser->setPaths('module', 'example_module', $path);
|
||||
|
||||
$libraries = $this->libraryDiscoveryParser->buildByExtension('example_module');
|
||||
$library = $libraries['example'];
|
||||
|
||||
$this->assertCount(0, $library['js']);
|
||||
$this->assertCount(1, $library['css']);
|
||||
$this->assertCount(0, $library['dependencies']);
|
||||
$this->assertEquals($path . '/css/example.css', $library['css'][0]['data']);
|
||||
|
||||
// Ensures that VERSION is replaced by the current core version.
|
||||
$this->assertEquals(\Drupal::VERSION, $library['version']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a theme can be used instead of a module.
|
||||
*
|
||||
* @covers ::buildByExtension
|
||||
*/
|
||||
public function testBuildByExtensionWithTheme() {
|
||||
$this->moduleHandler->expects($this->atLeastOnce())
|
||||
->method('moduleExists')
|
||||
->with('example_theme')
|
||||
->will($this->returnValue(FALSE));
|
||||
|
||||
$path = __DIR__ . '/library_test_files';
|
||||
$path = substr($path, strlen($this->root) + 1);
|
||||
$this->libraryDiscoveryParser->setPaths('theme', 'example_theme', $path);
|
||||
|
||||
$libraries = $this->libraryDiscoveryParser->buildByExtension('example_theme');
|
||||
$library = $libraries['example'];
|
||||
|
||||
$this->assertCount(0, $library['js']);
|
||||
$this->assertCount(1, $library['css']);
|
||||
$this->assertCount(0, $library['dependencies']);
|
||||
$this->assertEquals($path . '/css/example.css', $library['css'][0]['data']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a module with a missing library file results in FALSE.
|
||||
*
|
||||
* @covers ::buildByExtension
|
||||
*/
|
||||
public function testBuildByExtensionWithMissingLibraryFile() {
|
||||
$this->moduleHandler->expects($this->atLeastOnce())
|
||||
->method('moduleExists')
|
||||
->with('example_module')
|
||||
->will($this->returnValue(TRUE));
|
||||
|
||||
$path = __DIR__ . '/library_test_files_not_existing';
|
||||
$path = substr($path, strlen($this->root) + 1);
|
||||
$this->libraryDiscoveryParser->setPaths('module', 'example_module', $path);
|
||||
|
||||
$this->assertSame($this->libraryDiscoveryParser->buildByExtension('example_module'), array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that an exception is thrown when a libraries file couldn't be parsed.
|
||||
*
|
||||
* @expectedException \Drupal\Core\Asset\Exception\InvalidLibraryFileException
|
||||
*
|
||||
* @covers ::buildByExtension
|
||||
*/
|
||||
public function testInvalidLibrariesFile() {
|
||||
$this->moduleHandler->expects($this->atLeastOnce())
|
||||
->method('moduleExists')
|
||||
->with('invalid_file')
|
||||
->will($this->returnValue(TRUE));
|
||||
|
||||
$path = __DIR__ . '/library_test_files';
|
||||
$path = substr($path, strlen($this->root) + 1);
|
||||
$this->libraryDiscoveryParser->setPaths('module', 'invalid_file', $path);
|
||||
|
||||
$this->libraryDiscoveryParser->buildByExtension('invalid_file');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that an exception is thrown when no CSS/JS/setting is specified.
|
||||
*
|
||||
* @expectedException \Drupal\Core\Asset\Exception\IncompleteLibraryDefinitionException
|
||||
* @expectedExceptionMessage Incomplete library definition for definition 'example' in extension 'example_module_missing_information'
|
||||
*
|
||||
* @covers ::buildByExtension
|
||||
*/
|
||||
public function testBuildByExtensionWithMissingInformation() {
|
||||
$this->moduleHandler->expects($this->atLeastOnce())
|
||||
->method('moduleExists')
|
||||
->with('example_module_missing_information')
|
||||
->will($this->returnValue(TRUE));
|
||||
|
||||
$path = __DIR__ . '/library_test_files';
|
||||
$path = substr($path, strlen($this->root) + 1);
|
||||
$this->libraryDiscoveryParser->setPaths('module', 'example_module_missing_information', $path);
|
||||
|
||||
$this->libraryDiscoveryParser->buildByExtension('example_module_missing_information');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the version property, and how it propagates to the contained assets.
|
||||
*
|
||||
* @covers ::buildByExtension
|
||||
*/
|
||||
public function testVersion() {
|
||||
$this->moduleHandler->expects($this->atLeastOnce())
|
||||
->method('moduleExists')
|
||||
->with('versions')
|
||||
->will($this->returnValue(TRUE));
|
||||
|
||||
$path = __DIR__ . '/library_test_files';
|
||||
$path = substr($path, strlen($this->root) + 1);
|
||||
$this->libraryDiscoveryParser->setPaths('module', 'versions', $path);
|
||||
|
||||
$libraries = $this->libraryDiscoveryParser->buildByExtension('versions');
|
||||
|
||||
$this->assertFalse(array_key_exists('version', $libraries['versionless']));
|
||||
$this->assertEquals(-1, $libraries['versionless']['css'][0]['version']);
|
||||
$this->assertEquals(-1, $libraries['versionless']['js'][0]['version']);
|
||||
|
||||
$this->assertEquals('9.8.7.6', $libraries['versioned']['version']);
|
||||
$this->assertEquals('9.8.7.6', $libraries['versioned']['css'][0]['version']);
|
||||
$this->assertEquals('9.8.7.6', $libraries['versioned']['js'][0]['version']);
|
||||
|
||||
$this->assertEquals(\Drupal::VERSION, $libraries['core-versioned']['version']);
|
||||
$this->assertEquals(\Drupal::VERSION, $libraries['core-versioned']['css'][0]['version']);
|
||||
$this->assertEquals(\Drupal::VERSION, $libraries['core-versioned']['js'][0]['version']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests that the version property of external libraries is handled.
|
||||
*
|
||||
* @covers ::buildByExtension
|
||||
*/
|
||||
public function testExternalLibraries() {
|
||||
$this->moduleHandler->expects($this->atLeastOnce())
|
||||
->method('moduleExists')
|
||||
->with('external')
|
||||
->will($this->returnValue(TRUE));
|
||||
|
||||
$path = __DIR__ . '/library_test_files';
|
||||
$path = substr($path, strlen($this->root) + 1);
|
||||
$this->libraryDiscoveryParser->setPaths('module', 'external', $path);
|
||||
|
||||
$libraries = $this->libraryDiscoveryParser->buildByExtension('external');
|
||||
$library = $libraries['example_external'];
|
||||
|
||||
$this->assertEquals('http://example.com/css/example_external.css', $library['css'][0]['data']);
|
||||
$this->assertEquals('http://example.com/example_external.js', $library['js'][0]['data']);
|
||||
$this->assertEquals('3.14', $library['version']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that CSS weights are taken into account properly.
|
||||
*
|
||||
* @covers ::buildByExtension
|
||||
*/
|
||||
public function testDefaultCssWeights() {
|
||||
$this->moduleHandler->expects($this->atLeastOnce())
|
||||
->method('moduleExists')
|
||||
->with('css_weights')
|
||||
->will($this->returnValue(TRUE));
|
||||
|
||||
$path = __DIR__ . '/library_test_files';
|
||||
$path = substr($path, strlen($this->root) + 1);
|
||||
$this->libraryDiscoveryParser->setPaths('module', 'css_weights', $path);
|
||||
|
||||
$libraries = $this->libraryDiscoveryParser->buildByExtension('css_weights');
|
||||
$library = $libraries['example'];
|
||||
$css = $library['css'];
|
||||
$this->assertCount(10, $css);
|
||||
|
||||
// The following default weights are tested:
|
||||
// - CSS_BASE: -200
|
||||
// - CSS_LAYOUT: -100
|
||||
// - CSS_COMPONENT: 0
|
||||
// - CSS_STATE: 100
|
||||
// - CSS_THEME: 200
|
||||
$this->assertEquals(200, $css[0]['weight']);
|
||||
$this->assertEquals(200 + 29, $css[1]['weight']);
|
||||
$this->assertEquals(-200, $css[2]['weight']);
|
||||
$this->assertEquals(-200 + 97, $css[3]['weight']);
|
||||
$this->assertEquals(-100, $css[4]['weight']);
|
||||
$this->assertEquals(-100 + 92, $css[5]['weight']);
|
||||
$this->assertEquals(0, $css[6]['weight']);
|
||||
$this->assertEquals(45, $css[7]['weight']);
|
||||
$this->assertEquals(100, $css[8]['weight']);
|
||||
$this->assertEquals(100 + 8, $css[9]['weight']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that you cannot provide positive weights for JavaScript libraries.
|
||||
*
|
||||
* @expectedException \UnexpectedValueException
|
||||
*
|
||||
* @covers ::buildByExtension
|
||||
*/
|
||||
public function testJsWithPositiveWeight() {
|
||||
$this->moduleHandler->expects($this->atLeastOnce())
|
||||
->method('moduleExists')
|
||||
->with('js_positive_weight')
|
||||
->will($this->returnValue(TRUE));
|
||||
|
||||
$path = __DIR__ . '/library_test_files';
|
||||
$path = substr($path, strlen($this->root) + 1);
|
||||
$this->libraryDiscoveryParser->setPaths('module', 'js_positive_weight', $path);
|
||||
|
||||
$this->libraryDiscoveryParser->buildByExtension('js_positive_weight');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a library with CSS/JavaScript and a setting.
|
||||
*
|
||||
* @covers ::buildByExtension
|
||||
*/
|
||||
public function testLibraryWithCssJsSetting() {
|
||||
$this->moduleHandler->expects($this->atLeastOnce())
|
||||
->method('moduleExists')
|
||||
->with('css_js_settings')
|
||||
->will($this->returnValue(TRUE));
|
||||
|
||||
$path = __DIR__ . '/library_test_files';
|
||||
$path = substr($path, strlen($this->root) + 1);
|
||||
$this->libraryDiscoveryParser->setPaths('module', 'css_js_settings', $path);
|
||||
|
||||
$libraries = $this->libraryDiscoveryParser->buildByExtension('css_js_settings');
|
||||
$library = $libraries['example'];
|
||||
|
||||
// Ensures that the group and type are set automatically.
|
||||
$this->assertEquals(-100, $library['js'][0]['group']);
|
||||
$this->assertEquals('file', $library['js'][0]['type']);
|
||||
$this->assertEquals($path . '/js/example.js', $library['js'][0]['data']);
|
||||
|
||||
$this->assertEquals(0, $library['css'][0]['group']);
|
||||
$this->assertEquals('file', $library['css'][0]['type']);
|
||||
$this->assertEquals($path . '/css/base.css', $library['css'][0]['data']);
|
||||
|
||||
$this->assertEquals(array('key' => 'value'), $library['drupalSettings']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a library with dependencies.
|
||||
*
|
||||
* @covers ::buildByExtension
|
||||
*/
|
||||
public function testLibraryWithDependencies() {
|
||||
$this->moduleHandler->expects($this->atLeastOnce())
|
||||
->method('moduleExists')
|
||||
->with('dependencies')
|
||||
->will($this->returnValue(TRUE));
|
||||
|
||||
$path = __DIR__ . '/library_test_files';
|
||||
$path = substr($path, strlen($this->root) + 1);
|
||||
$this->libraryDiscoveryParser->setPaths('module', 'dependencies', $path);
|
||||
|
||||
$libraries = $this->libraryDiscoveryParser->buildByExtension('dependencies');
|
||||
$library = $libraries['example'];
|
||||
|
||||
$this->assertCount(2, $library['dependencies']);
|
||||
$this->assertEquals('external/example_external', $library['dependencies'][0]);
|
||||
$this->assertEquals('example_module/example', $library['dependencies'][1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a library with a couple of data formats like full URL.
|
||||
*
|
||||
* @covers ::buildByExtension
|
||||
*/
|
||||
public function testLibraryWithDataTypes() {
|
||||
$this->moduleHandler->expects($this->atLeastOnce())
|
||||
->method('moduleExists')
|
||||
->with('data_types')
|
||||
->will($this->returnValue(TRUE));
|
||||
|
||||
$path = __DIR__ . '/library_test_files';
|
||||
$path = substr($path, strlen($this->root) + 1);
|
||||
$this->libraryDiscoveryParser->setPaths('module', 'data_types', $path);
|
||||
|
||||
$this->libraryDiscoveryParser->setFileValidUri('public://test.css', TRUE);
|
||||
$this->libraryDiscoveryParser->setFileValidUri('public://test2.css', FALSE);
|
||||
|
||||
$libraries = $this->libraryDiscoveryParser->buildByExtension('data_types');
|
||||
$library = $libraries['example'];
|
||||
|
||||
$this->assertCount(5, $library['css']);
|
||||
$this->assertEquals('external', $library['css'][0]['type']);
|
||||
$this->assertEquals('http://example.com/test.css', $library['css'][0]['data']);
|
||||
$this->assertEquals('file', $library['css'][1]['type']);
|
||||
$this->assertEquals('tmp/test.css', $library['css'][1]['data']);
|
||||
$this->assertEquals('external', $library['css'][2]['type']);
|
||||
$this->assertEquals('//cdn.com/test.css', $library['css'][2]['data']);
|
||||
$this->assertEquals('file', $library['css'][3]['type']);
|
||||
$this->assertEquals('public://test.css', $library['css'][3]['data']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a library with JavaScript-specific flags.
|
||||
*
|
||||
* @covers ::buildByExtension
|
||||
*/
|
||||
public function testLibraryWithJavaScript() {
|
||||
$this->moduleHandler->expects($this->atLeastOnce())
|
||||
->method('moduleExists')
|
||||
->with('js')
|
||||
->will($this->returnValue(TRUE));
|
||||
|
||||
$path = __DIR__ . '/library_test_files';
|
||||
$path = substr($path, strlen($this->root) + 1);
|
||||
$this->libraryDiscoveryParser->setPaths('module', 'js', $path);
|
||||
|
||||
$libraries = $this->libraryDiscoveryParser->buildByExtension('js');
|
||||
$library = $libraries['example'];
|
||||
|
||||
$this->assertCount(2, $library['js']);
|
||||
$this->assertEquals(FALSE, $library['js'][0]['minified']);
|
||||
$this->assertEquals(TRUE, $library['js'][1]['minified']);
|
||||
}
|
||||
/**
|
||||
* Tests that an exception is thrown when license is missing when 3rd party.
|
||||
*
|
||||
* @expectedException \Drupal\Core\Asset\Exception\LibraryDefinitionMissingLicenseException
|
||||
* @expectedExceptionMessage Missing license information in library definition for definition 'no-license-info-but-remote' extension 'licenses_missing_information': it has a remote, but no license.
|
||||
*
|
||||
* @covers ::buildByExtension
|
||||
*/
|
||||
public function testLibraryThirdPartyWithMissingLicense() {
|
||||
$this->moduleHandler->expects($this->atLeastOnce())
|
||||
->method('moduleExists')
|
||||
->with('licenses_missing_information')
|
||||
->will($this->returnValue(TRUE));
|
||||
|
||||
$path = __DIR__ . '/library_test_files';
|
||||
$path = substr($path, strlen($this->root) + 1);
|
||||
$this->libraryDiscoveryParser->setPaths('module', 'licenses_missing_information', $path);
|
||||
|
||||
$this->libraryDiscoveryParser->buildByExtension('licenses_missing_information');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a library with various licenses, some GPL-compatible, some not.
|
||||
*
|
||||
* @covers ::buildByExtension
|
||||
*/
|
||||
public function testLibraryWithLicenses() {
|
||||
$this->moduleHandler->expects($this->atLeastOnce())
|
||||
->method('moduleExists')
|
||||
->with('licenses')
|
||||
->will($this->returnValue(TRUE));
|
||||
|
||||
$path = __DIR__ . '/library_test_files';
|
||||
$path = substr($path, strlen($this->root) + 1);
|
||||
$this->libraryDiscoveryParser->setPaths('module', 'licenses', $path);
|
||||
|
||||
$libraries = $this->libraryDiscoveryParser->buildByExtension('licenses');
|
||||
|
||||
// For libraries without license info, the default license is applied.
|
||||
$library = $libraries['no-license-info'];
|
||||
$this->assertCount(1, $library['css']);
|
||||
$this->assertCount(1, $library['js']);
|
||||
$this->assertTrue(isset($library['license']));
|
||||
$default_license = array(
|
||||
'name' => 'GNU-GPL-2.0-or-later',
|
||||
'url' => 'https://www.drupal.org/licensing/faq',
|
||||
'gpl-compatible' => TRUE,
|
||||
);
|
||||
$this->assertEquals($library['license'], $default_license);
|
||||
|
||||
// GPL2-licensed libraries.
|
||||
$library = $libraries['gpl2'];
|
||||
$this->assertCount(1, $library['css']);
|
||||
$this->assertCount(1, $library['js']);
|
||||
$expected_license = array(
|
||||
'name' => 'gpl2',
|
||||
'url' => 'https://url-to-gpl2-license',
|
||||
'gpl-compatible' => TRUE,
|
||||
);
|
||||
$this->assertEquals($library['license'], $expected_license);
|
||||
|
||||
// MIT-licensed libraries.
|
||||
$library = $libraries['mit'];
|
||||
$this->assertCount(1, $library['css']);
|
||||
$this->assertCount(1, $library['js']);
|
||||
$expected_license = array(
|
||||
'name' => 'MIT',
|
||||
'url' => 'https://url-to-mit-license',
|
||||
'gpl-compatible' => TRUE,
|
||||
);
|
||||
$this->assertEquals($library['license'], $expected_license);
|
||||
|
||||
// Libraries in the Public Domain.
|
||||
$library = $libraries['public-domain'];
|
||||
$this->assertCount(1, $library['css']);
|
||||
$this->assertCount(1, $library['js']);
|
||||
$expected_license = array(
|
||||
'name' => 'Public Domain',
|
||||
'url' => 'https://url-to-public-domain-license',
|
||||
'gpl-compatible' => TRUE,
|
||||
);
|
||||
$this->assertEquals($library['license'], $expected_license);
|
||||
|
||||
// Apache-licensed libraries.
|
||||
$library = $libraries['apache'];
|
||||
$this->assertCount(1, $library['css']);
|
||||
$this->assertCount(1, $library['js']);
|
||||
$expected_license = array(
|
||||
'name' => 'apache',
|
||||
'url' => 'https://url-to-apache-license',
|
||||
'gpl-compatible' => FALSE,
|
||||
);
|
||||
$this->assertEquals($library['license'], $expected_license);
|
||||
|
||||
// Copyrighted libraries.
|
||||
$library = $libraries['copyright'];
|
||||
$this->assertCount(1, $library['css']);
|
||||
$this->assertCount(1, $library['js']);
|
||||
$expected_license = array(
|
||||
'name' => '© Some company',
|
||||
'gpl-compatible' => FALSE,
|
||||
);
|
||||
$this->assertEquals($library['license'], $expected_license);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the tested class to mock the external dependencies.
|
||||
*/
|
||||
class TestLibraryDiscoveryParser extends LibraryDiscoveryParser {
|
||||
|
||||
protected $paths;
|
||||
|
||||
protected $validUris;
|
||||
|
||||
protected function drupalGetPath($type, $name) {
|
||||
return isset($this->paths[$type][$name]) ? $this->paths[$type][$name] : NULL;
|
||||
}
|
||||
|
||||
public function setPaths($type, $name, $path) {
|
||||
$this->paths[$type][$name] = $path;
|
||||
}
|
||||
|
||||
protected function fileValidUri($source) {
|
||||
return isset($this->validUris[$source]) ? $this->validUris[$source] : FALSE;
|
||||
}
|
||||
|
||||
public function setFileValidUri($source, $valid) {
|
||||
$this->validUris[$source] = $valid;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!defined('CSS_AGGREGATE_DEFAULT')) {
|
||||
define('CSS_AGGREGATE_DEFAULT', 0);
|
||||
}
|
||||
if (!defined('CSS_AGGREGATE_THEME')) {
|
||||
define('CSS_AGGREGATE_THEME', 100);
|
||||
}
|
||||
if (!defined('CSS_BASE')) {
|
||||
define('CSS_BASE', -200);
|
||||
}
|
||||
if (!defined('CSS_LAYOUT')) {
|
||||
define('CSS_LAYOUT', -100);
|
||||
}
|
||||
if (!defined('CSS_COMPONENT')) {
|
||||
define('CSS_COMPONENT', 0);
|
||||
}
|
||||
if (!defined('CSS_STATE')) {
|
||||
define('CSS_STATE', 100);
|
||||
}
|
||||
if (!defined('CSS_THEME')) {
|
||||
define('CSS_THEME', 200);
|
||||
}
|
||||
if (!defined('JS_SETTING')) {
|
||||
define('JS_SETTING', -200);
|
||||
}
|
||||
if (!defined('JS_LIBRARY')) {
|
||||
define('JS_LIBRARY', -100);
|
||||
}
|
||||
if (!defined('JS_DEFAULT')) {
|
||||
define('JS_DEFAULT', 0);
|
||||
}
|
||||
if (!defined('JS_THEME')) {
|
||||
define('JS_THEME', 100);
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Core\Asset;
|
||||
|
||||
use Drupal\Core\Asset\LibraryDiscovery;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Core\Asset\LibraryDiscovery
|
||||
* @group Asset
|
||||
*/
|
||||
class LibraryDiscoveryTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The tested library discovery service.
|
||||
*
|
||||
* @var \Drupal\Core\Asset\LibraryDiscovery
|
||||
*/
|
||||
protected $libraryDiscovery;
|
||||
|
||||
/**
|
||||
* The mocked library discovery cache collector.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheCollectorInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $libraryDiscoveryCollector;
|
||||
|
||||
/**
|
||||
* The cache tags invalidator.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $cacheTagsInvalidator;
|
||||
|
||||
/**
|
||||
* Test library data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $libraryData = [
|
||||
'test_1' => [
|
||||
'js' => [],
|
||||
'css' => [
|
||||
'foo.css' => [],
|
||||
],
|
||||
],
|
||||
'test_2' => [
|
||||
'js' => [
|
||||
'bar.js' => [],
|
||||
],
|
||||
'css' => [],
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->cacheTagsInvalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidatorInterface');
|
||||
$this->libraryDiscoveryCollector = $this->getMockBuilder('Drupal\Core\Asset\LibraryDiscoveryCollector')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->libraryDiscovery = new LibraryDiscovery($this->libraryDiscoveryCollector, $this->cacheTagsInvalidator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getLibrariesByExtension
|
||||
*/
|
||||
public function testGetLibrariesByExtension() {
|
||||
$this->libraryDiscoveryCollector->expects($this->once())
|
||||
->method('get')
|
||||
->with('test')
|
||||
->willReturn($this->libraryData);
|
||||
|
||||
$this->libraryDiscovery->getLibrariesbyExtension('test');
|
||||
// Verify that subsequent calls don't trigger hook_library_info_alter()
|
||||
// and hook_js_settings_alter() invocations, nor do they talk to the
|
||||
// collector again. This ensures that the alterations made by
|
||||
// hook_library_info_alter() and hook_js_settings_alter() implementations
|
||||
// are statically cached, as desired.
|
||||
$this->libraryDiscovery->getLibraryByName('test', 'test_1');
|
||||
$this->libraryDiscovery->getLibrariesbyExtension('test');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
@charset "UTF-8";html{font-family:"sans-serif";}
|
||||
|
|
@ -0,0 +1 @@
|
|||
html{font-family:"sans-serif";}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
@charset "UTF-8";
|
||||
html{font-family:"sans-serif";}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue