diff --git a/.htaccess b/.htaccess
index fd7bd29..856fd32 100644
--- a/.htaccess
+++ b/.htaccess
@@ -39,6 +39,14 @@ AddEncoding gzip svgz
php_value mbstring.http_input pass
php_value mbstring.http_output pass
php_flag mbstring.encoding_translation off
+
+ # Assertions.
+ # By default PHP has these turned on. Production sites should turn these off.
+ # While assertions can be turned off at run time, we need to have this setting
+ # in place immediately to catch an assertions thrown before the settings file
+ # can be loaded.
+ php_value assert.active 1
+ # To enable them, change 0 to 1. Recommended for dev sites.
# Requires mod_expires to be enabled.
diff --git a/core/lib/Drupal/Component/Fault/Assertion.php b/core/lib/Drupal/Component/Fault/Assertion.php
new file mode 100644
index 0000000..4939b96
--- /dev/null
+++ b/core/lib/Drupal/Component/Fault/Assertion.php
@@ -0,0 +1,121 @@
+errorLocation['line']
+ . ' in file '
+ . $this->errorLocation['file']
+ . ' -- asserted: '
+ . $this->code
+ . ' -- comment: ' . $this->message;
+ }
+
+ /**
+ * Implements BaseFaultHandler::verboseResponse.
+ */
+ protected function verboseResponse() {
+ $this->printHtmlStart(); ?>
+
The system has encountered an error.
+HTML;
+ $this->printHtmlEnd();
+ }
+
+ /**
+ * Print the start of an HTML response.
+ */
+ protected function printHtmlStart() {
+ echo <<
+
+
+
+
+HTML;
+
+ }
+
+ /**
+ * Send the conclusion of the output.
+ */
+ protected function printHtmlEnd() {
+ echo '
';
+ }
+
+ /**
+ * Send the correct response headers for a fault response.
+ */
+ protected function sendHeaders($type) {
+ if (headers_sent()) {
+ return;
+ }
+
+ header('Content-Type: ' . $type);
+
+ // Somewhat redundant - browsers shouldn't cache 500 class responses anyway.
+ header("Cache-Control: no-cache, must-revalidate");
+ header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");
+ header($_SERVER["SERVER_PROTOCOL"] . " 503 Service Unavailable");
+ }
+
+}
diff --git a/core/lib/Drupal/Component/Fault/FaultException.php b/core/lib/Drupal/Component/Fault/FaultException.php
new file mode 100644
index 0000000..dd8538b
--- /dev/null
+++ b/core/lib/Drupal/Component/Fault/FaultException.php
@@ -0,0 +1,12 @@
+resolve();
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Cache/ApcuBackend.php b/core/lib/Drupal/Core/Cache/ApcuBackend.php
index 9bac79e..c8b763d 100644
--- a/core/lib/Drupal/Core/Cache/ApcuBackend.php
+++ b/core/lib/Drupal/Core/Cache/ApcuBackend.php
@@ -166,7 +166,7 @@ protected function prepareItem($cache, $allow_invalid) {
* {@inheritdoc}
*/
public function set($cid, $data, $expire = CacheBackendInterface::CACHE_PERMANENT, array $tags = array()) {
- Cache::validateTags($tags);
+ assert('count($tags) === 0 || \\Drupal\\Component\\Fault\\Assertion::collectionOfStrings($tags)', 'One or more invalid Cache Tags in passed array');
$tags = array_unique($tags);
$cache = new \stdClass();
$cache->cid = $cid;
diff --git a/core/lib/Drupal/Core/Cache/Cache.php b/core/lib/Drupal/Core/Cache/Cache.php
index d149592..d8898a5 100644
--- a/core/lib/Drupal/Core/Cache/Cache.php
+++ b/core/lib/Drupal/Core/Cache/Cache.php
@@ -37,7 +37,7 @@ public static function mergeContexts() {
$cache_contexts = array_merge($cache_contexts, $contexts);
}
$cache_contexts = array_unique($cache_contexts);
- \Drupal::service('cache_contexts_manager')->validateTokens($cache_contexts);
+ assert('\\Drupal::service(\'cache_contexts_manager\')->assertValidTokens($cache_contexts)', 'One or more invalid tokens passed.');
sort($cache_contexts);
return $cache_contexts;
}
@@ -66,7 +66,7 @@ public static function mergeTags() {
$cache_tags = array_merge($cache_tags, $tags);
}
$cache_tags = array_unique($cache_tags);
- static::validateTags($cache_tags);
+ assert('count($cache_tags) === 0 || \\Drupal\\Component\\Fault\\Assertion::collectionOfStrings($cache_tags)', 'One or more invalid Cache Tags in passed array');
sort($cache_tags);
return $cache_tags;
}
@@ -110,6 +110,8 @@ public static function mergeMaxAges() {
* An array of cache tags.
*
* @throws \LogicException
+ *
+ * @deprecated use assert('count($tags) === 0 || \\Drupal\\Component\\Fault\\Assertions::collectionOfStrings($tags)');
*/
public static function validateTags(array $tags) {
if (empty($tags)) {
diff --git a/core/lib/Drupal/Core/Cache/CacheCollector.php b/core/lib/Drupal/Core/Cache/CacheCollector.php
index a6d8ab5..c2add52 100644
--- a/core/lib/Drupal/Core/Cache/CacheCollector.php
+++ b/core/lib/Drupal/Core/Cache/CacheCollector.php
@@ -115,7 +115,7 @@
* (optional) The tags to specify for the cache item.
*/
public function __construct($cid, CacheBackendInterface $cache, LockBackendInterface $lock, array $tags = array()) {
- Cache::validateTags($tags);
+ assert('count($tags) === 0 || \\Drupal\\Component\\Fault\\Assertion::collectionOfStrings($tags)', 'One or more invalid Cache Tags in passed array');
$this->cid = $cid;
$this->cache = $cache;
$this->tags = $tags;
diff --git a/core/lib/Drupal/Core/Cache/CacheContextsManager.php b/core/lib/Drupal/Core/Cache/CacheContextsManager.php
index 67857cb..6e74617 100644
--- a/core/lib/Drupal/Core/Cache/CacheContextsManager.php
+++ b/core/lib/Drupal/Core/Cache/CacheContextsManager.php
@@ -271,4 +271,16 @@ public function validateTokens(array $context_tokens = []) {
}
}
+ /**
+ * Wrapper for the above for use with the assert statement.
+ */
+ public function assertValidTokens(array $context_tokens = []) {
+ try {
+ $this->validateTokens($context_tokens);
+ } catch ( \LogicException $e) {
+ return FALSE;
+ }
+ return TRUE;
+ }
+
}
diff --git a/core/lib/Drupal/Core/Cache/CacheTagsInvalidator.php b/core/lib/Drupal/Core/Cache/CacheTagsInvalidator.php
index 64a8eb0..f76c4fa 100644
--- a/core/lib/Drupal/Core/Cache/CacheTagsInvalidator.php
+++ b/core/lib/Drupal/Core/Cache/CacheTagsInvalidator.php
@@ -28,7 +28,7 @@ class CacheTagsInvalidator implements CacheTagsInvalidatorInterface {
*/
public function invalidateTags(array $tags) {
// Validate the tags.
- Cache::validateTags($tags);
+ assert('count($tags) === 0 || \\Drupal\\Component\\Fault\\Assertion::collectionOfStrings($tags)', 'One or more invalid Cache Tags in passed array');
// Notify all added cache tags invalidators.
foreach ($this->invalidators as $invalidator) {
diff --git a/core/lib/Drupal/Core/Cache/DatabaseBackend.php b/core/lib/Drupal/Core/Cache/DatabaseBackend.php
index 81e83bb..ce12687 100644
--- a/core/lib/Drupal/Core/Cache/DatabaseBackend.php
+++ b/core/lib/Drupal/Core/Cache/DatabaseBackend.php
@@ -150,7 +150,7 @@ protected function prepareItem($cache, $allow_invalid) {
* Implements Drupal\Core\Cache\CacheBackendInterface::set().
*/
public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array()) {
- Cache::validateTags($tags);
+ assert('count($tags) === 0 || \\Drupal\\Component\\Fault\\Assertion::collectionOfStrings($tags)', 'One or more invalid Cache Tags in passed array');
$tags = array_unique($tags);
// Sort the cache tags so that they are stored consistently in the database.
sort($tags);
@@ -210,7 +210,7 @@ public function setMultiple(array $items) {
'tags' => array(),
);
- Cache::validateTags($item['tags']);
+ assert('count($item[\'tags\']) === 0 || \\Drupal\\Component\\Fault\\Assertion::collectionOfStrings($item[\'tags\'])', 'One or more invalid Cache Tags in passed array');
$item['tags'] = array_unique($item['tags']);
// Sort the cache tags so that they are stored consistently in the DB.
sort($item['tags']);
diff --git a/core/lib/Drupal/Core/Cache/MemoryBackend.php b/core/lib/Drupal/Core/Cache/MemoryBackend.php
index 63b56c2..801e695 100644
--- a/core/lib/Drupal/Core/Cache/MemoryBackend.php
+++ b/core/lib/Drupal/Core/Cache/MemoryBackend.php
@@ -107,7 +107,7 @@ protected function prepareItem($cache, $allow_invalid) {
* Implements Drupal\Core\Cache\CacheBackendInterface::set().
*/
public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array()) {
- Cache::validateTags($tags);
+ assert('count($tags) === 0 || \\Drupal\\Component\\Fault\\Assertion::collectionOfStrings($tags)', 'One or more invalid Cache Tags in passed array');
$tags = array_unique($tags);
// Sort the cache tags so that they are stored consistently in the database.
sort($tags);
diff --git a/core/lib/Drupal/Core/Cache/PhpBackend.php b/core/lib/Drupal/Core/Cache/PhpBackend.php
index 761e394..03a2290 100644
--- a/core/lib/Drupal/Core/Cache/PhpBackend.php
+++ b/core/lib/Drupal/Core/Cache/PhpBackend.php
@@ -148,7 +148,7 @@ protected function prepareItem($cache, $allow_invalid) {
* {@inheritdoc}
*/
public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array()) {
- Cache::validateTags($tags);
+ assert('count($tags) === 0 || \\Drupal\\Component\\Fault\\Assertion::collectionOfStrings($tags)', 'One or more invalid Cache Tags in passed array');
$item = (object) array(
'cid' => $cid,
'data' => $data,
diff --git a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
index 297f971..a932f5c 100644
--- a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
+++ b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
@@ -132,7 +132,7 @@ public function __construct($subdir, \Traversable $namespaces, ModuleHandlerInte
* definitions should be cleared along with other, related cache entries.
*/
public function setCacheBackend(CacheBackendInterface $cache_backend, $cache_key, array $cache_tags = array()) {
- Cache::validateTags($cache_tags);
+ assert('count($cache_tags) === 0 || \\Drupal\\Component\\Fault\\Assertion::collectionOfStrings($cache_tags)', 'One or more invalid Cache Tags in passed array');
$this->cacheBackend = $cache_backend;
$this->cacheKey = $cache_key;
$this->cacheTags = $cache_tags;
diff --git a/core/modules/simpletest/src/AssertionTestingTrait.php b/core/modules/simpletest/src/AssertionTestingTrait.php
new file mode 100644
index 0000000..003e17d
--- /dev/null
+++ b/core/modules/simpletest/src/AssertionTestingTrait.php
@@ -0,0 +1,51 @@
+startAssertionHandling();
+ * parent::setUp();
+ * }
+ * @endcode
+ *
+ * @code
+ * public function tearDown() {
+ * $this->stopAssertionHandling();
+ * $this->assertAssertionNotRaised();
+ * parent::setUp();
+ * }
+ * @endcode
+ */
+trait AssertionTestingTrait {
+ use BaseAssertionTestingTrait;
+ /**
+ * {@inheritdoc}
+ */
+ protected function assertAssertionsRaised() {
+ $this->assertIdentical(func_get_args(), $this->assertionsRaised, 'Expected Assertions Raised.');
+ $this->assertionsRaised = [];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function assertAssertionNotRaised() {
+ $this->assertTrue(count($this->assertionsRaised) === 0, 'No Assertions Raised');
+ }
+
+}
diff --git a/core/modules/system/src/Tests/Cache/GenericCacheBackendUnitTestBase.php b/core/modules/system/src/Tests/Cache/GenericCacheBackendUnitTestBase.php
index cc0d9df..006812b 100644
--- a/core/modules/system/src/Tests/Cache/GenericCacheBackendUnitTestBase.php
+++ b/core/modules/system/src/Tests/Cache/GenericCacheBackendUnitTestBase.php
@@ -10,6 +10,8 @@
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\simpletest\KernelTestBase;
+use Drupal\simpletest\AssertionTestingTrait;
+use Drupal\Tests\AssertionException;
/**
* Tests any cache backend.
@@ -22,6 +24,7 @@
* For a full working implementation.
*/
abstract class GenericCacheBackendUnitTestBase extends KernelTestBase {
+ use AssertionTestingTrait;
/**
* Array of objects implementing Drupal\Core\Cache\CacheBackendInterface.
@@ -107,6 +110,7 @@ protected function getCacheBackend($bin = null) {
}
protected function setUp() {
+ $this->startAssertionHandling();
$this->cachebackends = array();
$this->defaultValue = $this->randomMachineName(10);
@@ -116,6 +120,9 @@ protected function setUp() {
}
protected function tearDown() {
+ $this->stopAssertionHandling();
+ $this->assertAssertionNotRaised();
+
// Destruct the registered backend, each test will get a fresh instance,
// properly emptying it here ensure that on persistent data backends they
// will come up empty the next test.
@@ -219,12 +226,14 @@ public function testSetGet() {
$this->assertFalse($backend->get('test8'));
// Calling ::set() with invalid cache tags.
+ $this->dieOnRaise = TRUE;
try {
$backend->set('exception_test', 'value', Cache::PERMANENT, ['node' => [3, 5, 7]]);
- $this->fail('::set() was called with invalid cache tags, no exception was thrown.');
+ $this->fail('::set() was called with invalid cache tags, no assertion raised.');
}
- catch (\LogicException $e) {
- $this->pass('::set() was called with invalid cache tags, an exception was thrown.');
+ catch ( AssertionException $e) {
+ $this->pass('::set() was called with invalid cache tags, an assertion was raised.');
+ $this->dieOnRaise = FALSE;
}
}
@@ -413,6 +422,7 @@ public function testSetMultiple() {
$this->assertEqual($cached['cid_5']->data, $items['cid_5']['data'], 'New cache item set correctly.');
// Calling ::setMultiple() with invalid cache tags.
+ $this->dieOnRaise = TRUE;
try {
$items = [
'exception_test_1' => array('data' => 1, 'tags' => []),
@@ -420,10 +430,11 @@ public function testSetMultiple() {
'exception_test_3' => array('data' => 3, 'tags' => ['node' => [3, 5, 7]]),
];
$backend->setMultiple($items);
- $this->fail('::setMultiple() was called with invalid cache tags, no exception was thrown.');
+ $this->fail('::setMultiple() was called with invalid cache tags, no assertion was raised.');
}
- catch (\LogicException $e) {
- $this->pass('::setMultiple() was called with invalid cache tags, an exception was thrown.');
+ catch (AssertionException $e) {
+ $this->pass('::setMultiple() was called with invalid cache tags, an assertion was raised.');
+ $this->dieOnRaise = FALSE;
}
}
diff --git a/core/tests/Drupal/Tests/AssertionException.php b/core/tests/Drupal/Tests/AssertionException.php
new file mode 100644
index 0000000..837f11c
--- /dev/null
+++ b/core/tests/Drupal/Tests/AssertionException.php
@@ -0,0 +1,24 @@
+startAssertionHandling();
+ * parent::setUp();
+ * }
+ * @endcode
+ */
+trait AssertionTestingTrait {
+ use BaseAssertionTestingTrait;
+ /**
+ * {@inheritdoc}
+ *
+ * Drupal Unit test has no tear down and since this trait will most
+ * frequently be applied to its children we just go ahead and define this
+ * method. Reminder - if you need to define this method in your test you'll
+ * need to alias this function when you bind in the trait.
+ */
+ protected function tearDown() {
+ $this->stopAssertionHandling();
+ $this->assertEmpty(
+ $this->assertionsRaised,
+ 'Unaccounted for assert fails found at test conclusion: ' . implode(', ', $this->assertionsRaised)
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function assertAssertionsRaised() {
+ $this->assertEquals(func_get_args(), $this->assertionsRaised);
+ $this->assertionsRaised = [];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function assertAssertionNotRaised() {
+ $this->assertEmpty($this->assertionsRaised);
+ }
+
+}
diff --git a/core/tests/Drupal/Tests/BaseAssertionTestingTrait.php b/core/tests/Drupal/Tests/BaseAssertionTestingTrait.php
new file mode 100644
index 0000000..10f20d9
--- /dev/null
+++ b/core/tests/Drupal/Tests/BaseAssertionTestingTrait.php
@@ -0,0 +1,128 @@
+dieOnRaise) {
+ throw new AssertionException($message);
+ }
+
+ // Otherwise we log the assertion as thrown and let the code continue.
+ $this->assertionsRaised[] = $message;
+
+ // Inform PHP we've successfully completed our handling of the assert fail.
+ return TRUE;
+ }
+
+ /**
+ * Start assertion handling for the test.
+ */
+ protected function startAssertionHandling() {
+ assert_options(ASSERT_WARNING, FALSE);
+ assert_options(ASSERT_BAIL, FALSE);
+ assert_options(ASSERT_CALLBACK, [$this, 'assertCallbackHandle']);
+ $this->assertionsRaised = [];
+ return FALSE;
+ }
+
+ /**
+ * Suspend assertion handling.
+ */
+ protected function suspendAssertionHandling() {
+ assert_options(ASSERT_WARNING, TRUE);
+ assert_options(ASSERT_CALLBACK, NULL);
+ $this->assertionsRaised = [];
+ }
+
+ /**
+ * Cease handling assertions and clear the way for the next test.
+ *
+ * Call this from tearDown()
+ */
+ protected function stopAssertionHandling() {
+ // If an error was expected and indeed thrown there will be no chance to
+ // clear out the assertion log before we reach this function. In order for
+ // those tests to work set a flag.
+ if ($this->thereWillBeErrors) {
+ $this->assertionsRaised = [];
+ $this->thereWillBeErrors = FALSE;
+ }
+
+ $this->suspendAssertionHandling();
+ $this->dieOnRaise = FALSE;
+ }
+
+ /**
+ * Check if the assertions specified where raised.
+ *
+ * This function can be overloaded. Assertions should be passed in the order
+ * they are expected to occur. After being accounted for the assertion count
+ * is reset.
+ */
+ abstract protected function assertAssertionsRaised();
+
+ /**
+ * Insure no assertions where thrown.
+ *
+ * Called during teardown, but you may wish to call it at other times.
+ */
+ abstract protected function assertAssertionNotRaised();
+
+}
diff --git a/core/tests/Drupal/Tests/Component/Fault/AssertionHandlerTest.php b/core/tests/Drupal/Tests/Component/Fault/AssertionHandlerTest.php
new file mode 100644
index 0000000..78fc25f
--- /dev/null
+++ b/core/tests/Drupal/Tests/Component/Fault/AssertionHandlerTest.php
@@ -0,0 +1,65 @@
+startAssertionHandling();
+ parent::setUp();
+ }
+
+ /**
+ * Test the assert handler.
+ *
+ * The json response mode is used since it's output is easiest to check.
+ */
+ public function testAssertHandling() {
+ // Flag to the AssertionHandler that it is to spit out HTML anyway, leave
+ // the buffers alone, and not log anything.
+ $_SERVER['DRUPAL_FAULT_COMPONENT_IN_TEST_MODE'] = TRUE;
+
+ // JSON Response.
+ $_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest';
+
+ $response = new AssertionHandler(__FILE__, 42, 'FALSE', 'node://66 test2', [
+ [
+ 'file' => __FILE__,
+ 'line' => 42,
+ 'function' => 'assert',
+ 'class' => 'Moo',
+ ],
+ [
+ 'file' => 'check',
+ 'line' => 12,
+ 'function' => 'clear',
+ 'class' => 'Woo'
+ ]
+ ]);
+ $this->assertAssertionsRaised('This class can only be used by other classes in the Fault Component');
+
+ ob_start();
+ $response->resolve();
+ $this->assertEquals('Assert Failure line 42 in file /core/tests/Drupal/Tests/Component/Fault/AssertionHandlerTest.php -- asserted: FALSE -- comment: test2',
+ ob_get_clean()
+ );
+ }
+
+}
diff --git a/core/tests/Drupal/Tests/Component/Fault/AssertionTest.php b/core/tests/Drupal/Tests/Component/Fault/AssertionTest.php
new file mode 100644
index 0000000..db765ae
--- /dev/null
+++ b/core/tests/Drupal/Tests/Component/Fault/AssertionTest.php
@@ -0,0 +1,244 @@
+ 'foo'],
+ ['function' => 'moo']
+ ]);
+ }
+
+ /**
+ * Test no Caller exists situation.
+ *
+ * @expectedException \Drupal\Component\Fault\FaultException
+ *
+ * @expectedExceptionMessage No caller exists.
+ */
+ public function testValidCallerExceptionNotGlobal() {
+ Assertion::validCaller('', [
+ ['function' => 'foo'],
+ ['function' => 'assert']
+ ]);
+ }
+
+ /**
+ * Test the analysis of stackframes.
+ */
+ public function testValidCallerAnalysis() {
+ // Call in same class.
+ $this->assertTrue(
+ Assertion::validCaller('', [
+ ['function' => 'foo'],
+ ['function' => 'assert'],
+ ['function' => 'callee', 'class' => 'A\\B\\C'],
+ ['function' => 'caller', 'class' => 'A\\B\\C']
+ ])
+ );
+
+ // Call in same namespace.
+ $this->assertTrue(
+ Assertion::validCaller('', [
+ ['function' => 'foo'],
+ ['function' => 'assert'],
+ ['function' => 'callee', 'class' => 'A\\B\\C'],
+ ['function' => 'caller', 'class' => 'A\\B\\D']
+ ])
+ );
+
+ // Call in child namespace.
+ $this->assertTrue(
+ Assertion::validCaller('', [
+ ['function' => 'foo'],
+ ['function' => 'assert'],
+ ['function' => 'callee', 'class' => 'A\\B\\C'],
+ ['function' => 'caller', 'class' => 'A\\B\\D\\E']
+ ])
+ );
+
+ // Call in parent namespace.
+ $this->assertFalse(
+ Assertion::validCaller('', [
+ ['function' => 'foo'],
+ ['function' => 'assert'],
+ ['function' => 'callee', 'class' => 'A\\B\\C'],
+ ['function' => 'caller', 'class' => 'A\\D']
+ ])
+ );
+
+ // Call from global namespace.
+ $this->assertFalse(
+ Assertion::validCaller('', [
+ ['function' => 'foo'],
+ ['function' => 'assert'],
+ ['function' => 'callee', 'class' => 'A\\B\\C'],
+ ['function' => 'caller', 'class' => 'D']
+ ])
+ );
+
+ // A child class will have to be in the same namepace to function.
+ $this->assertTrue(
+ Assertion::validCaller('', [
+ ['function' => 'foo'],
+ ['function' => 'assert'],
+ ['function' => 'callee', 'class' => 'A\\B\\C'],
+ ['function' => 'callee', 'class' => 'A\\B\\D'],
+ ['function' => 'callee', 'class' => 'A\\B\\E'],
+ ['function' => 'caller', 'class' => 'A\\B\\F']
+ ])
+ );
+
+ // Or in a child namespace. The scope governance is that of the class
+ // making the assertion.
+ $this->assertTrue(
+ Assertion::validCaller('', [
+ ['function' => 'foo'],
+ ['function' => 'assert'],
+ ['function' => 'callee', 'class' => 'A\\B\\C'],
+ ['function' => 'callee', 'class' => 'A\\B\\D\\E'],
+ ['function' => 'callee', 'class' => 'A\\B\\F\\G\\E'],
+ ['function' => 'caller', 'class' => 'A\\B\\F\\G']
+ ])
+ );
+
+ // An extender class from a foreign namespace allows calls from the original
+ // namespace.
+ $this->assertTrue(
+ Assertion::validCaller('', [
+ ['function' => 'foo'],
+ ['function' => 'assert'],
+ ['function' => 'callee', 'class' => 'A\\B\\C'],
+ ['function' => 'callee', 'class' => 'A\\D\\E'],
+ ['function' => 'caller', 'class' => 'A\\B\\F\\G']
+ ])
+ );
+
+ // But will not allow calls from a new namespace.
+ $this->assertFalse(
+ Assertion::validCaller('', [
+ ['function' => 'foo'],
+ ['function' => 'assert'],
+ ['function' => 'callee', 'class' => 'A\\B\\C'],
+ ['function' => 'callee', 'class' => 'A\\D\\E'],
+ ['function' => 'caller', 'class' => 'A\\F\\G']
+ ])
+ );
+
+ // Test scope argument.
+ $this->assertTrue(
+ Assertion::validCaller('A\\F', [
+ ['function' => 'foo'],
+ ['function' => 'assert'],
+ ['function' => 'callee', 'class' => 'A\\B\\C'],
+ ['function' => 'callee', 'class' => 'A\\D\\E'],
+ ['function' => 'caller', 'class' => 'A\\F\\G']
+ ])
+ );
+ }
+
+ /**
+ * Test the collectionOf method.
+ */
+ public function testCollectionOf() {
+ // We don't need a test mock - the internal ArrayObject will work just fine.
+ $this->assertTrue(
+ Assertion::collectionOf([
+ new ArrayObject(),
+ new ArrayObject()
+ ], 'ArrayObject')
+ );
+
+ $this->assertFalse(
+ Assertion::collectionOf([
+ new ArrayObject(),
+ []
+ ], 'ArrayObject')
+ );
+
+ $this->assertTrue(
+ Assertion::collectionOf(new ArrayObject([
+ new ArrayObject(),
+ new ArrayObject()
+ ]), 'ArrayObject')
+ );
+
+ $this->assertFalse(
+ Assertion::collectionOf(new ArrayObject([
+ new ArrayObject(),
+ []
+ ]), 'ArrayObject')
+ );
+
+ // Non traversables fail.
+ $this->assertFalse(
+ Assertion::collectionOf('string', 'ArrayObject')
+ );
+
+ $this->assertFalse(
+ Assertion::collectionOf(new \stdClass(), 'ArrayObject')
+ );
+
+ // Empty collections fail.
+ $this->assertFalse(
+ Assertion::collectionOf([], 'ArrayObject')
+ );
+
+ }
+
+ /**
+ * Test CollectionOfStrings method.
+ */
+ public function testCollectionOfStrings() {
+ $this->assertTrue(Assertion::collectionOfStrings([
+ 'foo',
+ 'boo',
+ new ToStringMock('doo')
+ ]));
+
+ $this->assertTrue(Assertion::collectionOfStrings(new ArrayObject([
+ 'foo',
+ 'boo',
+ new ToStringMock('doo')
+ ])));
+
+ $this->assertFalse(Assertion::collectionOfStrings([
+ 'foo',
+ 1,
+ new ToStringMock('doo')
+ ]));
+
+ $this->assertFalse(Assertion::collectionOfStrings(new ArrayObject([
+ 'foo',
+ 1,
+ new ToStringMock('doo')
+ ])));
+ }
+
+}
diff --git a/core/tests/Drupal/Tests/Component/Fault/FaultSetupTest.php b/core/tests/Drupal/Tests/Component/Fault/FaultSetupTest.php
new file mode 100644
index 0000000..be39e46
--- /dev/null
+++ b/core/tests/Drupal/Tests/Component/Fault/FaultSetupTest.php
@@ -0,0 +1,44 @@
+assertEquals(['Drupal\\Component\\Fault\\FaultSetup', 'handleAssert'], assert_options(ASSERT_CALLBACK));
+ $this->assertEquals(1, assert_options(ASSERT_BAIL));
+
+ // Reset and disable assertions momentarily.
+ assert_options(ASSERT_CALLBACK, NULL);
+ assert_options(ASSERT_ACTIVE, 0);
+
+ // Now test to see if the method does nothing as it should when assert
+ // active = 0
+ FaultSetup::start();
+ $this->assertEquals(0, assert_options(ASSERT_ACTIVE));
+ $this->assertEquals(NULL, assert_options(ASSERT_CALLBACK));
+
+ // Restore test environment defaults.
+ assert_options(ASSERT_ACTIVE, 1);
+ assert_options(ASSERT_BAIL, 0);
+ }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Cache/CacheTagsInvalidatorTest.php b/core/tests/Drupal/Tests/Core/Cache/CacheTagsInvalidatorTest.php
index 27c70e5..f650412 100644
--- a/core/tests/Drupal/Tests/Core/Cache/CacheTagsInvalidatorTest.php
+++ b/core/tests/Drupal/Tests/Core/Cache/CacheTagsInvalidatorTest.php
@@ -19,17 +19,6 @@ class CacheTagsInvalidatorTest extends UnitTestCase {
/**
* @covers ::invalidateTags
- *
- * @expectedException \LogicException
- * @expectedExceptionMessage Cache tags must be strings, array given.
- */
- public function testInvalidateTagsWithInvalidTags() {
- $cache_tags_invalidator = new CacheTagsInvalidator();
- $cache_tags_invalidator->invalidateTags(['node' => [2, 3, 5, 8, 13]]);
- }
-
- /**
- * @covers ::invalidateTags
* @covers ::addInvalidator
*/
public function testInvalidateTags() {
diff --git a/core/tests/Drupal/Tests/Core/Cache/CacheTest.php b/core/tests/Drupal/Tests/Core/Cache/CacheTest.php
index c2e61a1..2cdc030 100644
--- a/core/tests/Drupal/Tests/Core/Cache/CacheTest.php
+++ b/core/tests/Drupal/Tests/Core/Cache/CacheTest.php
@@ -18,48 +18,6 @@
class CacheTest extends UnitTestCase {
/**
- * Provides a list of cache tags arrays.
- *
- * @return array
- */
- public function validateTagsProvider() {
- return [
- [[], FALSE],
- [['foo'], FALSE],
- [['foo', 'bar'], FALSE],
- [['foo', 'bar', 'llama:2001988', 'baz', 'llama:14031991'], FALSE],
- // Invalid.
- [[FALSE], 'Cache tags must be strings, boolean given.'],
- [[TRUE], 'Cache tags must be strings, boolean given.'],
- [['foo', FALSE], 'Cache tags must be strings, boolean given.'],
- [[NULL], 'Cache tags must be strings, NULL given.'],
- [['foo', NULL], 'Cache tags must be strings, NULL given.'],
- [[1337], 'Cache tags must be strings, integer given.'],
- [['foo', 1337], 'Cache tags must be strings, integer given.'],
- [[3.14], 'Cache tags must be strings, double given.'],
- [['foo', 3.14], 'Cache tags must be strings, double given.'],
- [[[]], 'Cache tags must be strings, array given.'],
- [['foo', []], 'Cache tags must be strings, array given.'],
- [['foo', ['bar']], 'Cache tags must be strings, array given.'],
- [[new \stdClass()], 'Cache tags must be strings, object given.'],
- [['foo', new \stdClass()], 'Cache tags must be strings, object given.'],
- ];
- }
-
- /**
- * @covers ::validateTags
- *
- * @dataProvider validateTagsProvider
- */
- public function testValidateTags(array $tags, $expected_exception_message) {
- if ($expected_exception_message !== FALSE) {
- $this->setExpectedException('LogicException', $expected_exception_message);
- }
- // If it doesn't throw an exception, validateTags() returns NULL.
- $this->assertNull(Cache::validateTags($tags));
- }
-
- /**
* Provides a list of pairs of cache tags arrays to be merged.
*
* @return array
diff --git a/core/tests/Drupal/Tests/Core/Cache/CacheableMetadataTest.php b/core/tests/Drupal/Tests/Core/Cache/CacheableMetadataTest.php
index ccbb7d6..4d55851 100644
--- a/core/tests/Drupal/Tests/Core/Cache/CacheableMetadataTest.php
+++ b/core/tests/Drupal/Tests/Core/Cache/CacheableMetadataTest.php
@@ -35,6 +35,7 @@ public function testMerge(CacheableMetadata $a, CacheableMetadata $b, CacheableM
$cache_contexts_manager = $this->getMockBuilder('Drupal\Core\Cache\CacheContextsManager')
->disableOriginalConstructor()
->getMock();
+ $cache_contexts_manager->method('assertValidTokens')->willReturn(TRUE);
$container = new ContainerBuilder();
$container->set('cache_contexts_manager', $cache_contexts_manager);
\Drupal::setContainer($container);
diff --git a/core/tests/Drupal/Tests/Core/Render/BubbleableMetadataTest.php b/core/tests/Drupal/Tests/Core/Render/BubbleableMetadataTest.php
index c6a217a..9e22b21 100644
--- a/core/tests/Drupal/Tests/Core/Render/BubbleableMetadataTest.php
+++ b/core/tests/Drupal/Tests/Core/Render/BubbleableMetadataTest.php
@@ -56,6 +56,7 @@ public function testMerge(BubbleableMetadata $a, CacheableMetadata $b, Bubbleabl
$cache_contexts_manager = $this->getMockBuilder('Drupal\Core\Cache\CacheContextsManager')
->disableOriginalConstructor()
->getMock();
+ $cache_contexts_manager->method('assertValidTokens')->willReturn(TRUE);
$container = new ContainerBuilder();
$container->set('cache_contexts_manager', $cache_contexts_manager);
$container->set('renderer', $renderer);
diff --git a/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php b/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php
index 903adce..aa2b57d 100644
--- a/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php
+++ b/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php
@@ -104,6 +104,7 @@ protected function setUp() {
$this->cacheContextsManager = $this->getMockBuilder('Drupal\Core\Cache\CacheContextsManager')
->disableOriginalConstructor()
->getMock();
+ $this->cacheContextsManager->method('assertValidTokens')->willReturn(TRUE);
$this->cacheContextsManager->expects($this->any())
->method('convertTokensToKeys')
->willReturnCallback(function($context_tokens) {
diff --git a/core/tests/Drupal/Tests/ToStringMock.php b/core/tests/Drupal/Tests/ToStringMock.php
new file mode 100644
index 0000000..d1089c1
--- /dev/null
+++ b/core/tests/Drupal/Tests/ToStringMock.php
@@ -0,0 +1,33 @@
+string = strval($string);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function __toString() {
+ return $this->string;
+ }
+
+}
diff --git a/index.php b/index.php
index 53392f9..3379865 100644
--- a/index.php
+++ b/index.php
@@ -10,11 +10,13 @@
use Drupal\Core\DrupalKernel;
use Drupal\Core\Site\Settings;
+use Drupal\Component\Fault\FaultSetup;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$autoloader = require_once 'autoload.php';
+FaultSetup::start();
try {
$request = Request::createFromGlobals();