Update to Drupal 8.2.0. For more information, see https://www.drupal.org/project/drupal/releases/8.2.0
This commit is contained in:
parent
2f563ab520
commit
f1c8716f57
1732 changed files with 52334 additions and 11780 deletions
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\FunctionalJavascriptTests\Ajax;
|
||||
|
||||
use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
|
||||
|
||||
/**
|
||||
* Tests that AJAX responses use the current theme.
|
||||
*
|
||||
* @group Ajax
|
||||
*/
|
||||
class AjaxThemeTest extends JavascriptTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['ajax_test'];
|
||||
|
||||
public function testAjaxWithAdminRoute() {
|
||||
\Drupal::service('theme_installer')->install(['stable', 'seven']);
|
||||
$theme_config = \Drupal::configFactory()->getEditable('system.theme');
|
||||
$theme_config->set('admin', 'seven');
|
||||
$theme_config->set('default', 'stable');
|
||||
$theme_config->save();
|
||||
|
||||
$account = $this->drupalCreateUser(['view the administration theme']);
|
||||
$this->drupalLogin($account);
|
||||
|
||||
// First visit the site directly via the URL. This should render it in the
|
||||
// admin theme.
|
||||
$this->drupalGet('admin/ajax-test/theme');
|
||||
$assert = $this->assertSession();
|
||||
$assert->pageTextContains('Current theme: seven');
|
||||
|
||||
// Now click the modal, which should also use the admin theme.
|
||||
$this->drupalGet('ajax-test/dialog');
|
||||
$assert->pageTextNotContains('Current theme: stable');
|
||||
$this->clickLink('Link 8 (ajax)');
|
||||
$assert->assertWaitOnAjaxRequest();
|
||||
|
||||
$assert->pageTextContains('Current theme: stable');
|
||||
$assert->pageTextNotContains('Current theme: seven');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\FunctionalJavascriptTests\Core\Session;
|
||||
|
||||
use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
|
||||
use Drupal\menu_link_content\Entity\MenuLinkContent;
|
||||
|
||||
/**
|
||||
* Tests that sessions don't expire.
|
||||
*
|
||||
* @group session
|
||||
*/
|
||||
class SessionTest extends JavascriptTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['menu_link_content', 'block'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$account = $this->drupalCreateUser();
|
||||
$this->drupalLogin($account);
|
||||
|
||||
$menu_link_content = MenuLinkContent::create([
|
||||
'title' => 'Link to front page',
|
||||
'menu_name' => 'tools',
|
||||
'link' => ['uri' => 'route:<front>'],
|
||||
]);
|
||||
$menu_link_content->save();
|
||||
|
||||
$this->drupalPlaceBlock('system_menu_block:tools');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the session doesn't expire.
|
||||
*
|
||||
* Makes sure that drupal_valid_test_ua() works for multiple requests
|
||||
* performed by the Mink browser. The SIMPLETEST_USER_AGENT cookie must always
|
||||
* be valid.
|
||||
*/
|
||||
public function testSessionExpiration() {
|
||||
// Visit the front page and click the link back to the front page a large
|
||||
// number of times.
|
||||
$this->drupalGet('<front>');
|
||||
|
||||
$session_assert = $this->assertSession();
|
||||
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
for ($i = 0; $i < 25; $i++) {
|
||||
$page->clickLink('Link to front page');
|
||||
$session_assert->statusCodeEquals(200);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -38,7 +38,7 @@ abstract class JavascriptTestBase extends BrowserTestBase {
|
|||
catch (DeadClient $e) {
|
||||
$this->markTestSkipped('PhantomJS is either not installed or not running. Start it via phantomjs --ssl-protocol=any --ignore-ssl-errors=true vendor/jcalderonzumba/gastonjs/src/Client/main.js 8510 1024 768&');
|
||||
}
|
||||
catch (Exception $e) {
|
||||
catch (\Exception $e) {
|
||||
$this->markTestSkipped('An unexpected error occurred while starting Mink: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace Drupal\FunctionalTests;
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\Component\Utility\Xss;
|
||||
use Drupal\KernelTests\AssertLegacyTrait as BaseAssertLegacyTrait;
|
||||
|
||||
/**
|
||||
|
@ -53,10 +55,17 @@ trait AssertLegacyTrait {
|
|||
* Plain text to look for.
|
||||
*
|
||||
* @deprecated Scheduled for removal in Drupal 9.0.0.
|
||||
* Use $this->assertSession()->pageTextContains() or
|
||||
* $this->assertSession()->responseContains() instead.
|
||||
* Use instead:
|
||||
* - $this->assertSession()->responseContains() for non-HTML responses,
|
||||
* like XML or Json.
|
||||
* - $this->assertSession()->pageTextContains() for HTML responses. Unlike
|
||||
* the deprecated assertText(), the passed text should be HTML decoded,
|
||||
* exactly as a human sees it in the browser.
|
||||
*/
|
||||
protected function assertText($text) {
|
||||
// Cast MarkupInterface to string.
|
||||
$text = (string) $text;
|
||||
|
||||
$content_type = $this->getSession()->getResponseHeader('Content-type');
|
||||
// In case of a Non-HTML response (example: XML) check the original
|
||||
// response.
|
||||
|
@ -64,7 +73,7 @@ trait AssertLegacyTrait {
|
|||
$this->assertSession()->responseContains($text);
|
||||
}
|
||||
else {
|
||||
$this->assertSession()->pageTextContains($text);
|
||||
$this->assertTextHelper($text, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,10 +87,17 @@ trait AssertLegacyTrait {
|
|||
* Plain text to look for.
|
||||
*
|
||||
* @deprecated Scheduled for removal in Drupal 9.0.0.
|
||||
* Use $this->assertSession()->pageTextNotContains() or
|
||||
* $this->assertSession()->responseNotContains() instead.
|
||||
* Use instead:
|
||||
* - $this->assertSession()->responseNotContains() for non-HTML responses,
|
||||
* like XML or Json.
|
||||
* - $this->assertSession()->pageTextNotContains() for HTML responses.
|
||||
* Unlike the deprecated assertNoText(), the passed text should be HTML
|
||||
* decoded, exactly as a human sees it in the browser.
|
||||
*/
|
||||
protected function assertNoText($text) {
|
||||
// Cast MarkupInterface to string.
|
||||
$text = (string) $text;
|
||||
|
||||
$content_type = $this->getSession()->getResponseHeader('Content-type');
|
||||
// In case of a Non-HTML response (example: XML) check the original
|
||||
// response.
|
||||
|
@ -89,10 +105,40 @@ trait AssertLegacyTrait {
|
|||
$this->assertSession()->responseNotContains($text);
|
||||
}
|
||||
else {
|
||||
$this->assertSession()->pageTextNotContains($text);
|
||||
$this->assertTextHelper($text);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for assertText and assertNoText.
|
||||
*
|
||||
* @param string $text
|
||||
* Plain text to look for.
|
||||
* @param bool $not_exists
|
||||
* (optional) TRUE if this text should not exist, FALSE if it should.
|
||||
* Defaults to TRUE.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE on pass, FALSE on fail.
|
||||
*/
|
||||
protected function assertTextHelper($text, $not_exists = TRUE) {
|
||||
$args = ['@text' => $text];
|
||||
$message = $not_exists ? new FormattableMarkup('"@text" not found', $args) : new FormattableMarkup('"@text" found', $args);
|
||||
|
||||
$raw_content = $this->getSession()->getPage()->getContent();
|
||||
// Trying to simulate what the user sees, given that it removes all text
|
||||
// inside the head tags, removes inline Javascript, fix all HTML entities,
|
||||
// removes dangerous protocols and filtering out all HTML tags, as they are
|
||||
// not visible in a normal browser.
|
||||
$raw_content = preg_replace('@<head>(.+?)</head>@si', '', $raw_content);
|
||||
$page_text = Xss::filter($raw_content, []);
|
||||
|
||||
$actual = $not_exists == (strpos($page_text, (string) $text) === FALSE);
|
||||
$this->assertTrue($actual, $message);
|
||||
|
||||
return $actual;
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes if the text is found ONLY ONCE on the text version of the page.
|
||||
*
|
||||
|
|
116
core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php
Normal file
116
core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php
Normal file
|
@ -0,0 +1,116 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\FunctionalTests;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests BrowserTestBase functionality.
|
||||
*
|
||||
* @group browsertestbase
|
||||
*/
|
||||
class BrowserTestBaseTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('test_page_test', 'form_test', 'system_test');
|
||||
|
||||
/**
|
||||
* Tests basic page test.
|
||||
*/
|
||||
public function testGoTo() {
|
||||
$account = $this->drupalCreateUser();
|
||||
$this->drupalLogin($account);
|
||||
|
||||
// Visit a Drupal page that requires login.
|
||||
$this->drupalGet('test-page');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
// Test page contains some text.
|
||||
$this->assertSession()->pageTextContains('Test page text.');
|
||||
|
||||
// Check that returned plain text is correct.
|
||||
$text = $this->getTextContent();
|
||||
$this->assertContains('Test page text.', $text);
|
||||
$this->assertNotContains('</html>', $text);
|
||||
|
||||
// Response includes cache tags that we can assert.
|
||||
$this->assertSession()->responseHeaderEquals('X-Drupal-Cache-Tags', 'rendered');
|
||||
|
||||
// Test that we can read the JS settings.
|
||||
$js_settings = $this->getDrupalSettings();
|
||||
$this->assertSame('azAZ09();.,\\\/-_{}', $js_settings['test-setting']);
|
||||
|
||||
// Test drupalGet with a url object.
|
||||
$url = Url::fromRoute('test_page_test.render_title');
|
||||
$this->drupalGet($url);
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
// Test page contains some text.
|
||||
$this->assertSession()->pageTextContains('Hello Drupal');
|
||||
|
||||
// Test that setting headers with drupalGet() works.
|
||||
$this->drupalGet('system-test/header', array(), array(
|
||||
'Test-Header' => 'header value',
|
||||
));
|
||||
$returned_header = $this->getSession()->getResponseHeader('Test-Header');
|
||||
$this->assertSame('header value', $returned_header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests basic form functionality.
|
||||
*/
|
||||
public function testForm() {
|
||||
// Ensure the proper response code for a _form route.
|
||||
$this->drupalGet('form-test/object-builder');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
// Ensure the form and text field exist.
|
||||
$this->assertSession()->elementExists('css', 'form#form-test-form-test-object');
|
||||
$this->assertSession()->fieldExists('bananas');
|
||||
|
||||
$edit = ['bananas' => 'green'];
|
||||
$this->submitForm($edit, 'Save', 'form-test-form-test-object');
|
||||
|
||||
$config_factory = $this->container->get('config.factory');
|
||||
$value = $config_factory->get('form_test.object')->get('bananas');
|
||||
$this->assertSame('green', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests clickLink() functionality.
|
||||
*/
|
||||
public function testClickLink() {
|
||||
$this->drupalGet('test-page');
|
||||
$this->clickLink('Visually identical test links');
|
||||
$this->assertContains('user/login', $this->getSession()->getCurrentUrl());
|
||||
$this->drupalGet('test-page');
|
||||
$this->clickLink('Visually identical test links', 0);
|
||||
$this->assertContains('user/login', $this->getSession()->getCurrentUrl());
|
||||
$this->drupalGet('test-page');
|
||||
$this->clickLink('Visually identical test links', 1);
|
||||
$this->assertContains('user/register', $this->getSession()->getCurrentUrl());
|
||||
}
|
||||
|
||||
public function testError() {
|
||||
$this->setExpectedException('\Exception', 'User notice: foo');
|
||||
$this->drupalGet('test-error');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests legacy asserts.
|
||||
*/
|
||||
public function testLegacyAsserts() {
|
||||
$this->drupalGet('test-encoded');
|
||||
$dangerous = 'Bad html <script>alert(123);</script>';
|
||||
$sanitized = Html::escape($dangerous);
|
||||
$this->assertNoText($dangerous);
|
||||
$this->assertText($sanitized);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\FunctionalTests\Core\Config;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Tests\Traits\Core\Config\SchemaConfigListenerTestTrait;
|
||||
|
||||
/**
|
||||
* Tests the functionality of ConfigSchemaChecker in KernelTestBase tests.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class SchemaConfigListenerTest extends BrowserTestBase {
|
||||
|
||||
use SchemaConfigListenerTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('config_test');
|
||||
|
||||
}
|
|
@ -83,7 +83,6 @@ trait AssertConfigTrait {
|
|||
break;
|
||||
default:
|
||||
throw new \Exception($config_name . ': ' . var_export($op, TRUE));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,21 @@ class DefaultConfigTest extends KernelTestBase {
|
|||
*/
|
||||
public static $modules = ['system', 'user'];
|
||||
|
||||
/**
|
||||
* The following config entries are changed on module install.
|
||||
*
|
||||
* Compare them does not make sense.
|
||||
*
|
||||
* @todo Figure out why simpletest.settings is not installed.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $skippedConfig = [
|
||||
'locale.settings' => ['path: '],
|
||||
'syslog.settings' => ['facility: '],
|
||||
'simpletest.settings' => TRUE,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -49,20 +64,6 @@ class DefaultConfigTest extends KernelTestBase {
|
|||
* @dataProvider providerTestModuleConfig
|
||||
*/
|
||||
public function testModuleConfig($module) {
|
||||
/** @var \Drupal\Core\Extension\ModuleInstallerInterface $module_installer */
|
||||
$module_installer = $this->container->get('module_installer');
|
||||
/** @var \Drupal\Core\Config\StorageInterface $active_config_storage */
|
||||
$active_config_storage = $this->container->get('config.storage');
|
||||
/** @var \Drupal\Core\Config\ConfigManagerInterface $config_manager */
|
||||
$config_manager = $this->container->get('config.manager');
|
||||
|
||||
// @todo https://www.drupal.org/node/2308745 Rest has an implicit dependency
|
||||
// on the Node module remove once solved.
|
||||
if (in_array($module, ['rest', 'hal'])) {
|
||||
$module_installer->install(['node']);
|
||||
}
|
||||
$module_installer->install([$module]);
|
||||
|
||||
// System and user are required in order to be able to install some of the
|
||||
// other modules. Therefore they are put into static::$modules, which though
|
||||
// doesn't install config files, so import those config files explicitly.
|
||||
|
@ -73,21 +74,71 @@ class DefaultConfigTest extends KernelTestBase {
|
|||
break;
|
||||
}
|
||||
|
||||
$default_install_path = drupal_get_path('module', $module) . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY;
|
||||
$module_config_storage = new FileStorage($default_install_path, StorageInterface::DEFAULT_COLLECTION);
|
||||
$module_path = drupal_get_path('module', $module) . '/';
|
||||
|
||||
// The following config entries are changed on module install, so compare
|
||||
// them doesn't make sense.
|
||||
$skipped_config = [];
|
||||
$skipped_config['locale.settings'][] = 'path: ';
|
||||
$skipped_config['syslog.settings'][] = 'facility: ';
|
||||
// @todo Figure out why simpletest.settings is not installed.
|
||||
$skipped_config['simpletest.settings'] = TRUE;
|
||||
/** @var \Drupal\Core\Extension\ModuleInstallerInterface $module_installer */
|
||||
$module_installer = $this->container->get('module_installer');
|
||||
|
||||
// Compare the installed config with the one in the module directory.
|
||||
foreach ($module_config_storage->listAll() as $config_name) {
|
||||
$result = $config_manager->diff($module_config_storage, $active_config_storage, $config_name);
|
||||
$this->assertConfigDiff($result, $config_name, $skipped_config);
|
||||
// @todo https://www.drupal.org/node/2308745 Rest has an implicit dependency
|
||||
// on the Node module remove once solved.
|
||||
if (in_array($module, ['rest', 'hal'])) {
|
||||
$module_installer->install(['node']);
|
||||
}
|
||||
|
||||
// Work out any additional modules and themes that need installing to create
|
||||
// and optional config.
|
||||
$optional_config_storage = new FileStorage($module_path . InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION);
|
||||
$modules_to_install = [$module];
|
||||
$themes_to_install = [];
|
||||
foreach ($optional_config_storage->listAll() as $config_name) {
|
||||
$data = $optional_config_storage->read($config_name);
|
||||
if (isset($data['dependencies']['module'])) {
|
||||
$modules_to_install = array_merge($modules_to_install, $data['dependencies']['module']);
|
||||
}
|
||||
if (isset($data['dependencies']['theme'])) {
|
||||
$themes_to_install = array_merge($themes_to_install, $data['dependencies']['theme']);
|
||||
}
|
||||
}
|
||||
$module_installer->install(array_unique($modules_to_install));
|
||||
$this->container->get('theme_installer')->install($themes_to_install);
|
||||
|
||||
// Test configuration in the module's config/install directory.
|
||||
$module_config_storage = new FileStorage($module_path . InstallStorage::CONFIG_INSTALL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION);
|
||||
$this->doTestsOnConfigStorage($module_config_storage);
|
||||
|
||||
// Test configuration in the module's config/optional directory.
|
||||
$this->doTestsOnConfigStorage($optional_config_storage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that default config matches the installed config.
|
||||
*
|
||||
* @param \Drupal\Core\Config\StorageInterface $default_config_storage
|
||||
* The default config storage to test.
|
||||
*/
|
||||
protected function doTestsOnConfigStorage(StorageInterface $default_config_storage) {
|
||||
/** @var \Drupal\Core\Config\ConfigManagerInterface $config_manager */
|
||||
$config_manager = $this->container->get('config.manager');
|
||||
|
||||
// Just connect directly to the config table so we don't need to worry about
|
||||
// the cache layer.
|
||||
$active_config_storage = $this->container->get('config.storage');
|
||||
|
||||
foreach ($default_config_storage->listAll() as $config_name) {
|
||||
if ($active_config_storage->exists($config_name)) {
|
||||
// If it is a config entity re-save it. This ensures that any
|
||||
// recalculation of dependencies does not cause config change.
|
||||
if ($entity_type = $config_manager->getEntityTypeIdByName($config_name)) {
|
||||
$entity_storage = $config_manager
|
||||
->getEntityManager()
|
||||
->getStorage($entity_type);
|
||||
$id = $entity_storage->getIDFromConfigName($config_name, $entity_storage->getEntityType()
|
||||
->getConfigPrefix());
|
||||
$entity_storage->load($id)->calculateDependencies()->save();
|
||||
}
|
||||
$result = $config_manager->diff($default_config_storage, $active_config_storage, $config_name);
|
||||
$this->assertConfigDiff($result, $config_name, static::$skippedConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
480
core/tests/Drupal/KernelTests/Core/Asset/AttachedAssetsTest.php
Normal file
480
core/tests/Drupal/KernelTests/Core/Asset/AttachedAssetsTest.php
Normal file
|
@ -0,0 +1,480 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Asset;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Asset\AttachedAssets;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests #attached assets: attached asset libraries and JavaScript settings.
|
||||
*
|
||||
* i.e. tests:
|
||||
*
|
||||
* @code
|
||||
* $build['#attached']['library'] = …
|
||||
* $build['#attached']['drupalSettings'] = …
|
||||
* @endcode
|
||||
*
|
||||
* @group Common
|
||||
* @group Asset
|
||||
*/
|
||||
class AttachedAssetsTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* The asset resolver service.
|
||||
*
|
||||
* @var \Drupal\Core\Asset\AssetResolverInterface
|
||||
*/
|
||||
protected $assetResolver;
|
||||
|
||||
/**
|
||||
* The renderer service.
|
||||
*
|
||||
* @var \Drupal\Core\Render\RendererInterface
|
||||
*/
|
||||
protected $renderer;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('language', 'simpletest', 'common_test', 'system');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->container->get('router.builder')->rebuild();
|
||||
|
||||
$this->assetResolver = $this->container->get('asset.resolver');
|
||||
$this->renderer = $this->container->get('renderer');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that default CSS and JavaScript is empty.
|
||||
*/
|
||||
function testDefault() {
|
||||
$assets = new AttachedAssets();
|
||||
$this->assertEqual(array(), $this->assetResolver->getCssAssets($assets, FALSE), 'Default CSS is empty.');
|
||||
list($js_assets_header, $js_assets_footer) = $this->assetResolver->getJsAssets($assets, FALSE);
|
||||
$this->assertEqual(array(), $js_assets_header, 'Default header JavaScript is empty.');
|
||||
$this->assertEqual(array(), $js_assets_footer, 'Default footer JavaScript is empty.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests non-existing libraries.
|
||||
*/
|
||||
function testLibraryUnknown() {
|
||||
$build['#attached']['library'][] = 'core/unknown';
|
||||
$assets = AttachedAssets::createFromRenderArray($build);
|
||||
|
||||
$this->assertIdentical([], $this->assetResolver->getJsAssets($assets, FALSE)[0], 'Unknown library was not added to the page.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests adding a CSS and a JavaScript file.
|
||||
*/
|
||||
function testAddFiles() {
|
||||
$build['#attached']['library'][] = 'common_test/files';
|
||||
$assets = AttachedAssets::createFromRenderArray($build);
|
||||
|
||||
$css = $this->assetResolver->getCssAssets($assets, FALSE);
|
||||
$js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
|
||||
$this->assertTrue(array_key_exists('core/modules/system/tests/modules/common_test/bar.css', $css), 'CSS files are correctly added.');
|
||||
$this->assertTrue(array_key_exists('core/modules/system/tests/modules/common_test/foo.js', $js), 'JavaScript files are correctly added.');
|
||||
|
||||
$css_render_array = \Drupal::service('asset.css.collection_renderer')->render($css);
|
||||
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
|
||||
$rendered_css = $this->renderer->renderPlain($css_render_array);
|
||||
$rendered_js = $this->renderer->renderPlain($js_render_array);
|
||||
$query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0';
|
||||
$this->assertNotIdentical(strpos($rendered_css, '<link rel="stylesheet" href="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/bar.css')) . '?' . $query_string . '" media="all" />'), FALSE, 'Rendering an external CSS file.');
|
||||
$this->assertNotIdentical(strpos($rendered_js, '<script src="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/foo.js')) . '?' . $query_string . '"></script>'), FALSE, 'Rendering an external JavaScript file.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests adding JavaScript settings.
|
||||
*/
|
||||
function testAddJsSettings() {
|
||||
// Add a file in order to test default settings.
|
||||
$build['#attached']['library'][] = 'core/drupalSettings';
|
||||
$assets = AttachedAssets::createFromRenderArray($build);
|
||||
|
||||
$this->assertEqual([], $assets->getSettings(), 'JavaScript settings on $assets are empty.');
|
||||
$javascript = $this->assetResolver->getJsAssets($assets, FALSE)[1];
|
||||
$this->assertTrue(array_key_exists('currentPath', $javascript['drupalSettings']['data']['path']), 'The current path JavaScript setting is set correctly.');
|
||||
$this->assertTrue(array_key_exists('currentPath', $assets->getSettings()['path']), 'JavaScript settings on $assets are resolved after retrieving JavaScript assets, and are equal to the returned JavaScript settings.');
|
||||
|
||||
$assets->setSettings(['drupal' => 'rocks', 'dries' => 280342800]);
|
||||
$javascript = $this->assetResolver->getJsAssets($assets, FALSE)[1];
|
||||
$this->assertEqual(280342800, $javascript['drupalSettings']['data']['dries'], 'JavaScript setting is set correctly.');
|
||||
$this->assertEqual('rocks', $javascript['drupalSettings']['data']['drupal'], 'The other JavaScript setting is set correctly.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests adding external CSS and JavaScript files.
|
||||
*/
|
||||
function testAddExternalFiles() {
|
||||
$build['#attached']['library'][] = 'common_test/external';
|
||||
$assets = AttachedAssets::createFromRenderArray($build);
|
||||
|
||||
$css = $this->assetResolver->getCssAssets($assets, FALSE);
|
||||
$js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
|
||||
$this->assertTrue(array_key_exists('http://example.com/stylesheet.css', $css), 'External CSS files are correctly added.');
|
||||
$this->assertTrue(array_key_exists('http://example.com/script.js', $js), 'External JavaScript files are correctly added.');
|
||||
|
||||
$css_render_array = \Drupal::service('asset.css.collection_renderer')->render($css);
|
||||
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
|
||||
$rendered_css = $this->renderer->renderPlain($css_render_array);
|
||||
$rendered_js = $this->renderer->renderPlain($js_render_array);
|
||||
$this->assertNotIdentical(strpos($rendered_css, '<link rel="stylesheet" href="http://example.com/stylesheet.css" media="all" />'), FALSE, 'Rendering an external CSS file.');
|
||||
$this->assertNotIdentical(strpos($rendered_js, '<script src="http://example.com/script.js"></script>'), FALSE, 'Rendering an external JavaScript file.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests adding JavaScript files with additional attributes.
|
||||
*/
|
||||
function testAttributes() {
|
||||
$build['#attached']['library'][] = 'common_test/js-attributes';
|
||||
$assets = AttachedAssets::createFromRenderArray($build);
|
||||
|
||||
$js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
|
||||
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
|
||||
$rendered_js = $this->renderer->renderPlain($js_render_array);
|
||||
$expected_1 = '<script src="http://example.com/deferred-external.js" foo="bar" defer></script>';
|
||||
$expected_2 = '<script src="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/deferred-internal.js')) . '?v=1" defer bar="foo"></script>';
|
||||
$this->assertNotIdentical(strpos($rendered_js, $expected_1), FALSE, 'Rendered external JavaScript with correct defer and random attributes.');
|
||||
$this->assertNotIdentical(strpos($rendered_js, $expected_2), FALSE, 'Rendered internal JavaScript with correct defer and random attributes.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that attributes are maintained when JS aggregation is enabled.
|
||||
*/
|
||||
function testAggregatedAttributes() {
|
||||
$build['#attached']['library'][] = 'common_test/js-attributes';
|
||||
$assets = AttachedAssets::createFromRenderArray($build);
|
||||
|
||||
$js = $this->assetResolver->getJsAssets($assets, TRUE)[1];
|
||||
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
|
||||
$rendered_js = $this->renderer->renderPlain($js_render_array);
|
||||
$expected_1 = '<script src="http://example.com/deferred-external.js" foo="bar" defer></script>';
|
||||
$expected_2 = '<script src="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/deferred-internal.js')) . '?v=1" defer bar="foo"></script>';
|
||||
$this->assertNotIdentical(strpos($rendered_js, $expected_1), FALSE, 'Rendered external JavaScript with correct defer and random attributes.');
|
||||
$this->assertNotIdentical(strpos($rendered_js, $expected_2), FALSE, 'Rendered internal JavaScript with correct defer and random attributes.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Integration test for CSS/JS aggregation.
|
||||
*/
|
||||
function testAggregation() {
|
||||
$build['#attached']['library'][] = 'core/drupal.timezone';
|
||||
$build['#attached']['library'][] = 'core/drupal.vertical-tabs';
|
||||
$assets = AttachedAssets::createFromRenderArray($build);
|
||||
|
||||
$this->assertEqual(1, count($this->assetResolver->getCssAssets($assets, TRUE)), 'There is a sole aggregated CSS asset.');
|
||||
|
||||
list($header_js, $footer_js) = $this->assetResolver->getJsAssets($assets, TRUE);
|
||||
$this->assertEqual([], \Drupal::service('asset.js.collection_renderer')->render($header_js), 'There are 0 JavaScript assets in the header.');
|
||||
$rendered_footer_js = \Drupal::service('asset.js.collection_renderer')->render($footer_js);
|
||||
$this->assertEqual(2, count($rendered_footer_js), 'There are 2 JavaScript assets in the footer.');
|
||||
$this->assertEqual('drupal-settings-json', $rendered_footer_js[0]['#attributes']['data-drupal-selector'], 'The first of the two JavaScript assets in the footer has drupal settings.');
|
||||
$this->assertEqual(0, strpos($rendered_footer_js[1]['#attributes']['src'], base_path()), 'The second of the two JavaScript assets in the footer has the sole aggregated JavaScript asset.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests JavaScript settings.
|
||||
*/
|
||||
function testSettings() {
|
||||
$build = array();
|
||||
$build['#attached']['library'][] = 'core/drupalSettings';
|
||||
// Nonsensical value to verify if it's possible to override path settings.
|
||||
$build['#attached']['drupalSettings']['path']['pathPrefix'] = 'yarhar';
|
||||
$assets = AttachedAssets::createFromRenderArray($build);
|
||||
|
||||
$js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
|
||||
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
|
||||
// Cast to string since this returns a \Drupal\Core\Render\Markup object.
|
||||
$rendered_js = (string) $this->renderer->renderPlain($js_render_array);
|
||||
|
||||
// Parse the generated drupalSettings <script> back to a PHP representation.
|
||||
$startToken = '{';
|
||||
$endToken = '}';
|
||||
$start = strpos($rendered_js, $startToken);
|
||||
$end = strrpos($rendered_js, $endToken);
|
||||
// Convert to a string, as $renderer_js is a \Drupal\Core\Render\Markup
|
||||
// object.
|
||||
$json = Unicode::substr($rendered_js, $start, $end - $start + 1);
|
||||
$parsed_settings = Json::decode($json);
|
||||
|
||||
// Test whether the settings for core/drupalSettings are available.
|
||||
$this->assertTrue(isset($parsed_settings['path']['baseUrl']), 'drupalSettings.path.baseUrl is present.');
|
||||
$this->assertIdentical($parsed_settings['path']['pathPrefix'], 'yarhar', 'drupalSettings.path.pathPrefix is present and has the correct (overridden) value.');
|
||||
$this->assertIdentical($parsed_settings['path']['currentPath'], '', 'drupalSettings.path.currentPath is present and has the correct value.');
|
||||
$this->assertIdentical($parsed_settings['path']['currentPathIsAdmin'], FALSE, 'drupalSettings.path.currentPathIsAdmin is present and has the correct value.');
|
||||
$this->assertIdentical($parsed_settings['path']['isFront'], FALSE, 'drupalSettings.path.isFront is present and has the correct value.');
|
||||
$this->assertIdentical($parsed_settings['path']['currentLanguage'], 'en', 'drupalSettings.path.currentLanguage is present and has the correct value.');
|
||||
|
||||
// Tests whether altering JavaScript settings via hook_js_settings_alter()
|
||||
// is working as expected.
|
||||
// @see common_test_js_settings_alter()
|
||||
$this->assertIdentical($parsed_settings['pluralDelimiter'], '☃');
|
||||
$this->assertIdentical($parsed_settings['foo'], 'bar');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests JS assets depending on the 'core/<head>' virtual library.
|
||||
*/
|
||||
function testHeaderHTML() {
|
||||
$build['#attached']['library'][] = 'common_test/js-header';
|
||||
$assets = AttachedAssets::createFromRenderArray($build);
|
||||
|
||||
$js = $this->assetResolver->getJsAssets($assets, FALSE)[0];
|
||||
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
|
||||
$rendered_js = $this->renderer->renderPlain($js_render_array);
|
||||
$query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0';
|
||||
$this->assertNotIdentical(strpos($rendered_js, '<script src="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/header.js')) . '?' . $query_string . '"></script>'), FALSE, 'The JS asset in common_test/js-header appears in the header.');
|
||||
$this->assertNotIdentical(strpos($rendered_js, '<script src="' . file_url_transform_relative(file_create_url('core/misc/drupal.js'))), FALSE, 'The JS asset of the direct dependency (core/drupal) of common_test/js-header appears in the header.');
|
||||
$this->assertNotIdentical(strpos($rendered_js, '<script src="' . file_url_transform_relative(file_create_url('core/assets/vendor/domready/ready.min.js'))), FALSE, 'The JS asset of the indirect dependency (core/domready) of common_test/js-header appears in the header.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that for assets with cache = FALSE, Drupal sets preprocess = FALSE.
|
||||
*/
|
||||
function testNoCache() {
|
||||
$build['#attached']['library'][] = 'common_test/no-cache';
|
||||
$assets = AttachedAssets::createFromRenderArray($build);
|
||||
|
||||
$js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
|
||||
$this->assertFalse($js['core/modules/system/tests/modules/common_test/nocache.js']['preprocess'], 'Setting cache to FALSE sets preprocess to FALSE when adding JavaScript.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests adding JavaScript within conditional comments.
|
||||
*
|
||||
* @see \Drupal\Core\Render\Element\HtmlTag::preRenderConditionalComments()
|
||||
*/
|
||||
function testBrowserConditionalComments() {
|
||||
$default_query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0';
|
||||
|
||||
$build['#attached']['library'][] = 'common_test/browsers';
|
||||
$assets = AttachedAssets::createFromRenderArray($build);
|
||||
|
||||
$js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
|
||||
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
|
||||
$rendered_js = $this->renderer->renderPlain($js_render_array);
|
||||
$expected_1 = "<!--[if lte IE 8]>\n" . '<script src="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/old-ie.js')) . '?' . $default_query_string . '"></script>' . "\n<![endif]-->";
|
||||
$expected_2 = "<!--[if !IE]><!-->\n" . '<script src="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/no-ie.js')) . '?' . $default_query_string . '"></script>' . "\n<!--<![endif]-->";
|
||||
|
||||
$this->assertNotIdentical(strpos($rendered_js, $expected_1), FALSE, 'Rendered JavaScript within downlevel-hidden conditional comments.');
|
||||
$this->assertNotIdentical(strpos($rendered_js, $expected_2), FALSE, 'Rendered JavaScript within downlevel-revealed conditional comments.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests JavaScript versioning.
|
||||
*/
|
||||
function testVersionQueryString() {
|
||||
$build['#attached']['library'][] = 'core/backbone';
|
||||
$build['#attached']['library'][] = 'core/domready';
|
||||
$assets = AttachedAssets::createFromRenderArray($build);
|
||||
|
||||
$js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
|
||||
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
|
||||
|
||||
$rendered_js = $this->renderer->renderPlain($js_render_array);
|
||||
$this->assertTrue(strpos($rendered_js, 'core/assets/vendor/backbone/backbone-min.js?v=1.2.3') > 0 && strpos($rendered_js, 'core/assets/vendor/domready/ready.min.js?v=1.0.8') > 0, 'JavaScript version identifiers correctly appended to URLs');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests JavaScript and CSS asset ordering.
|
||||
*/
|
||||
function testRenderOrder() {
|
||||
$build['#attached']['library'][] = 'common_test/order';
|
||||
$assets = AttachedAssets::createFromRenderArray($build);
|
||||
|
||||
// Construct the expected result from the regex.
|
||||
$expected_order_js = [
|
||||
"-8_1",
|
||||
"-8_2",
|
||||
"-8_3",
|
||||
"-8_4",
|
||||
"-5_1", // The external script.
|
||||
"-3_1",
|
||||
"-3_2",
|
||||
"0_1",
|
||||
"0_2",
|
||||
"0_3",
|
||||
];
|
||||
|
||||
// Retrieve the rendered JavaScript and test against the regex.
|
||||
$js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
|
||||
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
|
||||
$rendered_js = $this->renderer->renderPlain($js_render_array);
|
||||
$matches = array();
|
||||
if (preg_match_all('/weight_([-0-9]+_[0-9]+)/', $rendered_js, $matches)) {
|
||||
$result = $matches[1];
|
||||
}
|
||||
else {
|
||||
$result = array();
|
||||
}
|
||||
$this->assertIdentical($result, $expected_order_js, 'JavaScript is added in the expected weight order.');
|
||||
|
||||
// Construct the expected result from the regex.
|
||||
$expected_order_css = [
|
||||
// Base.
|
||||
'base_weight_-101_1',
|
||||
'base_weight_-8_1',
|
||||
'layout_weight_-101_1',
|
||||
'base_weight_0_1',
|
||||
'base_weight_0_2',
|
||||
// Layout.
|
||||
'layout_weight_-8_1',
|
||||
'component_weight_-101_1',
|
||||
'layout_weight_0_1',
|
||||
'layout_weight_0_2',
|
||||
// Component.
|
||||
'component_weight_-8_1',
|
||||
'state_weight_-101_1',
|
||||
'component_weight_0_1',
|
||||
'component_weight_0_2',
|
||||
// State.
|
||||
'state_weight_-8_1',
|
||||
'theme_weight_-101_1',
|
||||
'state_weight_0_1',
|
||||
'state_weight_0_2',
|
||||
// Theme.
|
||||
'theme_weight_-8_1',
|
||||
'theme_weight_0_1',
|
||||
'theme_weight_0_2',
|
||||
];
|
||||
|
||||
// Retrieve the rendered CSS and test against the regex.
|
||||
$css = $this->assetResolver->getCssAssets($assets, FALSE);
|
||||
$css_render_array = \Drupal::service('asset.css.collection_renderer')->render($css);
|
||||
$rendered_css = $this->renderer->renderPlain($css_render_array);
|
||||
$matches = array();
|
||||
if (preg_match_all('/([a-z]+)_weight_([-0-9]+_[0-9]+)/', $rendered_css, $matches)) {
|
||||
$result = $matches[0];
|
||||
}
|
||||
else {
|
||||
$result = array();
|
||||
}
|
||||
$this->assertIdentical($result, $expected_order_css, 'CSS is added in the expected weight order.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests rendering the JavaScript with a file's weight above jQuery's.
|
||||
*/
|
||||
function testRenderDifferentWeight() {
|
||||
// If a library contains assets A and B, and A is listed first, then B can
|
||||
// still make itself appear first by defining a lower weight.
|
||||
$build['#attached']['library'][] = 'core/jquery';
|
||||
$build['#attached']['library'][] = 'common_test/weight';
|
||||
$assets = AttachedAssets::createFromRenderArray($build);
|
||||
|
||||
$js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
|
||||
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
|
||||
$rendered_js = $this->renderer->renderPlain($js_render_array);
|
||||
$this->assertTrue(strpos($rendered_js, 'lighter.css') < strpos($rendered_js, 'first.js'), 'Lighter CSS assets are rendered first.');
|
||||
$this->assertTrue(strpos($rendered_js, 'lighter.js') < strpos($rendered_js, 'first.js'), 'Lighter JavaScript assets are rendered first.');
|
||||
$this->assertTrue(strpos($rendered_js, 'before-jquery.js') < strpos($rendered_js, 'core/assets/vendor/jquery/jquery.min.js'), 'Rendering a JavaScript file above jQuery.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests altering a JavaScript's weight via hook_js_alter().
|
||||
*
|
||||
* @see simpletest_js_alter()
|
||||
*/
|
||||
function testAlter() {
|
||||
// Add both tableselect.js and simpletest.js.
|
||||
$build['#attached']['library'][] = 'core/drupal.tableselect';
|
||||
$build['#attached']['library'][] = 'simpletest/drupal.simpletest';
|
||||
$assets = AttachedAssets::createFromRenderArray($build);
|
||||
|
||||
// Render the JavaScript, testing if simpletest.js was altered to be before
|
||||
// tableselect.js. See simpletest_js_alter() to see where this alteration
|
||||
// takes place.
|
||||
$js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
|
||||
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
|
||||
$rendered_js = $this->renderer->renderPlain($js_render_array);
|
||||
$this->assertTrue(strpos($rendered_js, 'simpletest.js') < strpos($rendered_js, 'core/misc/tableselect.js'), 'Altering JavaScript weight through the alter hook.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a JavaScript library to the page and alters it.
|
||||
*
|
||||
* @see common_test_library_info_alter()
|
||||
*/
|
||||
function testLibraryAlter() {
|
||||
// Verify that common_test altered the title of Farbtastic.
|
||||
/** @var \Drupal\Core\Asset\LibraryDiscoveryInterface $library_discovery */
|
||||
$library_discovery = \Drupal::service('library.discovery');
|
||||
$library = $library_discovery->getLibraryByName('core', 'jquery.farbtastic');
|
||||
$this->assertEqual($library['version'], '0.0', 'Registered libraries were altered.');
|
||||
|
||||
// common_test_library_info_alter() also added a dependency on jQuery Form.
|
||||
$build['#attached']['library'][] = 'core/jquery.farbtastic';
|
||||
$assets = AttachedAssets::createFromRenderArray($build);
|
||||
$js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
|
||||
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
|
||||
$rendered_js = $this->renderer->renderPlain($js_render_array);
|
||||
$this->assertTrue(strpos($rendered_js, 'core/assets/vendor/jquery-form/jquery.form.min.js'), 'Altered library dependencies are added to the page.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically defines an asset library and alters it.
|
||||
*/
|
||||
function testDynamicLibrary() {
|
||||
/** @var \Drupal\Core\Asset\LibraryDiscoveryInterface $library_discovery */
|
||||
$library_discovery = \Drupal::service('library.discovery');
|
||||
// Retrieve a dynamic library definition.
|
||||
// @see common_test_library_info_build()
|
||||
\Drupal::state()->set('common_test.library_info_build_test', TRUE);
|
||||
$library_discovery->clearCachedDefinitions();
|
||||
$dynamic_library = $library_discovery->getLibraryByName('common_test', 'dynamic_library');
|
||||
$this->assertTrue(is_array($dynamic_library));
|
||||
if ($this->assertTrue(isset($dynamic_library['version']))) {
|
||||
$this->assertIdentical('1.0', $dynamic_library['version']);
|
||||
}
|
||||
// Make sure the dynamic library definition could be altered.
|
||||
// @see common_test_library_info_alter()
|
||||
if ($this->assertTrue(isset($dynamic_library['dependencies']))) {
|
||||
$this->assertIdentical(['core/jquery'], $dynamic_library['dependencies']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that multiple modules can implement libraries with the same name.
|
||||
*
|
||||
* @see common_test.library.yml
|
||||
*/
|
||||
function testLibraryNameConflicts() {
|
||||
/** @var \Drupal\Core\Asset\LibraryDiscoveryInterface $library_discovery */
|
||||
$library_discovery = \Drupal::service('library.discovery');
|
||||
$farbtastic = $library_discovery->getLibraryByName('common_test', 'jquery.farbtastic');
|
||||
$this->assertEqual($farbtastic['version'], '0.1', 'Alternative libraries can be added to the page.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests JavaScript files that have querystrings attached get added right.
|
||||
*/
|
||||
function testAddJsFileWithQueryString() {
|
||||
$build['#attached']['library'][] = 'common_test/querystring';
|
||||
$assets = AttachedAssets::createFromRenderArray($build);
|
||||
|
||||
$css = $this->assetResolver->getCssAssets($assets, FALSE);
|
||||
$js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
|
||||
$this->assertTrue(array_key_exists('core/modules/system/tests/modules/common_test/querystring.css?arg1=value1&arg2=value2', $css), 'CSS file with query string is correctly added.');
|
||||
$this->assertTrue(array_key_exists('core/modules/system/tests/modules/common_test/querystring.js?arg1=value1&arg2=value2', $js), 'JavaScript file with query string is correctly added.');
|
||||
|
||||
$css_render_array = \Drupal::service('asset.css.collection_renderer')->render($css);
|
||||
$rendered_css = $this->renderer->renderPlain($css_render_array);
|
||||
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
|
||||
$rendered_js = $this->renderer->renderPlain($js_render_array);
|
||||
$query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0';
|
||||
$this->assertNotIdentical(strpos($rendered_css, '<link rel="stylesheet" href="' . str_replace('&', '&', file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/querystring.css?arg1=value1&arg2=value2'))) . '&' . $query_string . '" media="all" />'), FALSE, 'CSS file with query string gets version query string correctly appended..');
|
||||
$this->assertNotIdentical(strpos($rendered_js, '<script src="' . str_replace('&', '&', file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/querystring.js?arg1=value1&arg2=value2'))) . '&' . $query_string . '"></script>'), FALSE, 'JavaScript file with query string gets version query string correctly appended.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,296 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Asset;
|
||||
|
||||
use Drupal\Core\Asset\Exception\InvalidLibrariesExtendSpecificationException;
|
||||
use Drupal\Core\Asset\Exception\InvalidLibrariesOverrideSpecificationException;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the library discovery and library discovery parser.
|
||||
*
|
||||
* @group Render
|
||||
*/
|
||||
class LibraryDiscoveryIntegrationTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* The library discovery service.
|
||||
*
|
||||
* @var \Drupal\Core\Asset\LibraryDiscoveryInterface
|
||||
*/
|
||||
protected $libraryDiscovery;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->container->get('theme_installer')->install(['test_theme', 'classy']);
|
||||
$this->libraryDiscovery = $this->container->get('library.discovery');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that hook_library_info is invoked and the cache is cleared.
|
||||
*/
|
||||
public function testHookLibraryInfoByTheme() {
|
||||
// Activate test_theme and verify that the library 'kitten' is added using
|
||||
// hook_library_info_alter().
|
||||
$this->activateTheme('test_theme');
|
||||
$this->assertTrue($this->libraryDiscovery->getLibraryByName('test_theme', 'kitten'));
|
||||
|
||||
// Now make classy the active theme and assert that library is not added.
|
||||
$this->activateTheme('classy');
|
||||
$this->assertFalse($this->libraryDiscovery->getLibraryByName('test_theme', 'kitten'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that libraries-override are applied to library definitions.
|
||||
*/
|
||||
public function testLibrariesOverride() {
|
||||
// Assert some classy libraries that will be overridden or removed.
|
||||
$this->activateTheme('classy');
|
||||
$this->assertAssetInLibrary('core/themes/classy/css/components/button.css', 'classy', 'base', 'css');
|
||||
$this->assertAssetInLibrary('core/themes/classy/css/components/collapse-processed.css', 'classy', 'base', 'css');
|
||||
$this->assertAssetInLibrary('core/themes/classy/css/components/container-inline.css', 'classy', 'base', 'css');
|
||||
$this->assertAssetInLibrary('core/themes/classy/css/components/details.css', 'classy', 'base', 'css');
|
||||
$this->assertAssetInLibrary('core/themes/classy/css/components/dialog.css', 'classy', 'dialog', 'css');
|
||||
|
||||
// Confirmatory assert on core library to be removed.
|
||||
$this->assertTrue($this->libraryDiscovery->getLibraryByName('core', 'drupal.progress'), 'Confirmatory test on "core/drupal.progress"');
|
||||
|
||||
// Activate test theme that defines libraries overrides.
|
||||
$this->activateTheme('test_theme');
|
||||
|
||||
// Assert that entire library was correctly overridden.
|
||||
$this->assertEqual($this->libraryDiscovery->getLibraryByName('core', 'drupal.collapse'), $this->libraryDiscovery->getLibraryByName('test_theme', 'collapse'), 'Entire library correctly overridden.');
|
||||
|
||||
// Assert that classy library assets were correctly overridden or removed.
|
||||
$this->assertNoAssetInLibrary('core/themes/classy/css/components/button.css', 'classy', 'base', 'css');
|
||||
$this->assertNoAssetInLibrary('core/themes/classy/css/components/collapse-processed.css', 'classy', 'base', 'css');
|
||||
$this->assertNoAssetInLibrary('core/themes/classy/css/components/container-inline.css', 'classy', 'base', 'css');
|
||||
$this->assertNoAssetInLibrary('core/themes/classy/css/components/details.css', 'classy', 'base', 'css');
|
||||
$this->assertNoAssetInLibrary('core/themes/classy/css/components/dialog.css', 'classy', 'dialog', 'css');
|
||||
|
||||
$this->assertAssetInLibrary('core/modules/system/tests/themes/test_theme/css/my-button.css', 'classy', 'base', 'css');
|
||||
$this->assertAssetInLibrary('core/modules/system/tests/themes/test_theme/css/my-collapse-processed.css', 'classy', 'base', 'css');
|
||||
$this->assertAssetInLibrary('themes/my_theme/css/my-container-inline.css', 'classy', 'base', 'css');
|
||||
$this->assertAssetInLibrary('themes/my_theme/css/my-details.css', 'classy', 'base', 'css');
|
||||
|
||||
// Assert that entire library was correctly removed.
|
||||
$this->assertFalse($this->libraryDiscovery->getLibraryByName('core', 'drupal.progress'), 'Entire library correctly removed.');
|
||||
|
||||
// Assert that overridden library asset still retains attributes.
|
||||
$library = $this->libraryDiscovery->getLibraryByName('core', 'jquery');
|
||||
foreach ($library['js'] as $definition) {
|
||||
if ($definition['data'] == 'core/modules/system/tests/themes/test_theme/js/collapse.js') {
|
||||
$this->assertTrue($definition['minified'] && $definition['weight'] == -20, 'Previous attributes retained');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests libraries-override on drupalSettings.
|
||||
*/
|
||||
public function testLibrariesOverrideDrupalSettings() {
|
||||
// Activate test theme that attempts to override drupalSettings.
|
||||
$this->activateTheme('test_theme_libraries_override_with_drupal_settings');
|
||||
|
||||
// Assert that drupalSettings cannot be overridden and throws an exception.
|
||||
try {
|
||||
$this->libraryDiscovery->getLibraryByName('core', 'drupal.ajax');
|
||||
$this->fail('Throw Exception when trying to override drupalSettings');
|
||||
}
|
||||
catch (InvalidLibrariesOverrideSpecificationException $e) {
|
||||
$expected_message = 'drupalSettings may not be overridden in libraries-override. Trying to override core/drupal.ajax/drupalSettings. Use hook_library_info_alter() instead.';
|
||||
$this->assertEqual($e->getMessage(), $expected_message, 'Throw Exception when trying to override drupalSettings');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests libraries-override on malformed assets.
|
||||
*/
|
||||
public function testLibrariesOverrideMalformedAsset() {
|
||||
// Activate test theme that overrides with a malformed asset.
|
||||
$this->activateTheme('test_theme_libraries_override_with_invalid_asset');
|
||||
|
||||
// Assert that improperly formed asset "specs" throw an exception.
|
||||
try {
|
||||
$this->libraryDiscovery->getLibraryByName('core', 'drupal.dialog');
|
||||
$this->fail('Throw Exception when specifying invalid override');
|
||||
}
|
||||
catch (InvalidLibrariesOverrideSpecificationException $e) {
|
||||
$expected_message = 'Library asset core/drupal.dialog/css is not correctly specified. It should be in the form "extension/library_name/sub_key/path/to/asset.js".';
|
||||
$this->assertEqual($e->getMessage(), $expected_message, 'Throw Exception when specifying invalid override');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests library assets with other ways for specifying paths.
|
||||
*/
|
||||
public function testLibrariesOverrideOtherAssetLibraryNames() {
|
||||
// Activate a test theme that defines libraries overrides on other types of
|
||||
// assets.
|
||||
$this->activateTheme('test_theme');
|
||||
|
||||
// Assert Drupal-relative paths.
|
||||
$this->assertAssetInLibrary('themes/my_theme/css/dropbutton.css', 'core', 'drupal.dropbutton', 'css');
|
||||
|
||||
// Assert stream wrapper paths.
|
||||
$this->assertAssetInLibrary('public://my_css/vertical-tabs.css', 'core', 'drupal.vertical-tabs', 'css');
|
||||
|
||||
// Assert a protocol-relative URI.
|
||||
$this->assertAssetInLibrary('//my-server/my_theme/css/jquery_ui.css', 'core', 'jquery.ui', 'css');
|
||||
|
||||
// Assert an absolute URI.
|
||||
$this->assertAssetInLibrary('http://example.com/my_theme/css/farbtastic.css', 'core', 'jquery.farbtastic', 'css');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that base theme libraries-override still apply in sub themes.
|
||||
*/
|
||||
public function testBaseThemeLibrariesOverrideInSubTheme() {
|
||||
// Activate a test theme that has subthemes.
|
||||
$this->activateTheme('test_subtheme');
|
||||
|
||||
// Assert that libraries-override specified in the base theme still applies
|
||||
// in the sub theme.
|
||||
$this->assertNoAssetInLibrary('core/misc/dialog/dialog.js', 'core', 'drupal.dialog', 'js');
|
||||
$this->assertAssetInLibrary('core/modules/system/tests/themes/test_basetheme/css/farbtastic.css', 'core', 'jquery.farbtastic', 'css');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests libraries-extend.
|
||||
*/
|
||||
public function testLibrariesExtend() {
|
||||
// Activate classy themes and verify the libraries are not extended.
|
||||
$this->activateTheme('classy');
|
||||
$this->assertNoAssetInLibrary('core/modules/system/tests/themes/test_theme_libraries_extend/css/extend_1.css', 'classy', 'book-navigation', 'css');
|
||||
$this->assertNoAssetInLibrary('core/modules/system/tests/themes/test_theme_libraries_extend/js/extend_1.js', 'classy', 'book-navigation', 'js');
|
||||
$this->assertNoAssetInLibrary('core/modules/system/tests/themes/test_theme_libraries_extend/css/extend_2.css', 'classy', 'book-navigation', 'css');
|
||||
|
||||
// Activate the theme that extends the book-navigation library in classy.
|
||||
$this->activateTheme('test_theme_libraries_extend');
|
||||
$this->assertAssetInLibrary('core/modules/system/tests/themes/test_theme_libraries_extend/css/extend_1.css', 'classy', 'book-navigation', 'css');
|
||||
$this->assertAssetInLibrary('core/modules/system/tests/themes/test_theme_libraries_extend/js/extend_1.js', 'classy', 'book-navigation', 'js');
|
||||
$this->assertAssetInLibrary('core/modules/system/tests/themes/test_theme_libraries_extend/css/extend_2.css', 'classy', 'book-navigation', 'css');
|
||||
|
||||
// Activate a sub theme and confirm that it inherits the library assets
|
||||
// extended in the base theme as well as its own.
|
||||
$this->assertNoAssetInLibrary('core/modules/system/tests/themes/test_basetheme/css/base-libraries-extend.css', 'classy', 'base', 'css');
|
||||
$this->assertNoAssetInLibrary('core/modules/system/tests/themes/test_subtheme/css/sub-libraries-extend.css', 'classy', 'base', 'css');
|
||||
$this->activateTheme('test_subtheme');
|
||||
$this->assertAssetInLibrary('core/modules/system/tests/themes/test_basetheme/css/base-libraries-extend.css', 'classy', 'base', 'css');
|
||||
$this->assertAssetInLibrary('core/modules/system/tests/themes/test_subtheme/css/sub-libraries-extend.css', 'classy', 'base', 'css');
|
||||
|
||||
// Activate test theme that extends with a non-existent library. An
|
||||
// exception should be thrown.
|
||||
$this->activateTheme('test_theme_libraries_extend');
|
||||
try {
|
||||
$this->libraryDiscovery->getLibraryByName('core', 'drupal.dialog');
|
||||
$this->fail('Throw Exception when specifying non-existent libraries-extend.');
|
||||
}
|
||||
catch (InvalidLibrariesExtendSpecificationException $e) {
|
||||
$expected_message = 'The specified library "test_theme_libraries_extend/non_existent_library" does not exist.';
|
||||
$this->assertEqual($e->getMessage(), $expected_message, 'Throw Exception when specifying non-existent libraries-extend.');
|
||||
}
|
||||
|
||||
// Also, test non-string libraries-extend. An exception should be thrown.
|
||||
$this->container->get('theme_installer')->install(['test_theme']);
|
||||
try {
|
||||
$this->libraryDiscovery->getLibraryByName('test_theme', 'collapse');
|
||||
$this->fail('Throw Exception when specifying non-string libraries-extend.');
|
||||
}
|
||||
catch (InvalidLibrariesExtendSpecificationException $e) {
|
||||
$expected_message = 'The libraries-extend specification for each library must be a list of strings.';
|
||||
$this->assertEqual($e->getMessage(), $expected_message, 'Throw Exception when specifying non-string libraries-extend.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates a specified theme.
|
||||
*
|
||||
* Installs the theme if not already installed and makes it the active theme.
|
||||
*
|
||||
* @param string $theme_name
|
||||
* The name of the theme to be activated.
|
||||
*/
|
||||
protected function activateTheme($theme_name) {
|
||||
$this->container->get('theme_installer')->install([$theme_name]);
|
||||
|
||||
/** @var \Drupal\Core\Theme\ThemeInitializationInterface $theme_initializer */
|
||||
$theme_initializer = $this->container->get('theme.initialization');
|
||||
|
||||
/** @var \Drupal\Core\Theme\ThemeManagerInterface $theme_manager */
|
||||
$theme_manager = $this->container->get('theme.manager');
|
||||
|
||||
$theme_manager->setActiveTheme($theme_initializer->getActiveThemeByName($theme_name));
|
||||
|
||||
$this->libraryDiscovery->clearCachedDefinitions();
|
||||
|
||||
// Assert message.
|
||||
$this->pass(sprintf('Activated theme "%s"', $theme_name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the specified asset is in the given library.
|
||||
*
|
||||
* @param string $asset
|
||||
* The asset file with the path for the file.
|
||||
* @param string $extension
|
||||
* The extension in which the $library is defined.
|
||||
* @param string $library_name
|
||||
* Name of the library.
|
||||
* @param mixed $sub_key
|
||||
* The library sub key where the given asset is defined.
|
||||
* @param string $message
|
||||
* (optional) A message to display with the assertion.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the specified asset is found in the library.
|
||||
*/
|
||||
protected function assertAssetInLibrary($asset, $extension, $library_name, $sub_key, $message = NULL) {
|
||||
if (!isset($message)) {
|
||||
$message = sprintf('Asset %s found in library "%s/%s"', $asset, $extension, $library_name);
|
||||
}
|
||||
$library = $this->libraryDiscovery->getLibraryByName($extension, $library_name);
|
||||
foreach ($library[$sub_key] as $definition) {
|
||||
if ($asset == $definition['data']) {
|
||||
return $this->pass($message);
|
||||
}
|
||||
}
|
||||
return $this->fail($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the specified asset is not in the given library.
|
||||
*
|
||||
* @param string $asset
|
||||
* The asset file with the path for the file.
|
||||
* @param string $extension
|
||||
* The extension in which the $library_name is defined.
|
||||
* @param string $library_name
|
||||
* Name of the library.
|
||||
* @param mixed $sub_key
|
||||
* The library sub key where the given asset is defined.
|
||||
* @param string $message
|
||||
* (optional) A message to display with the assertion.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the specified asset is not found in the library.
|
||||
*/
|
||||
protected function assertNoAssetInLibrary($asset, $extension, $library_name, $sub_key, $message = NULL) {
|
||||
if (!isset($message)) {
|
||||
$message = sprintf('Asset %s not found in library "%s/%s"', $asset, $extension, $library_name);
|
||||
}
|
||||
$library = $this->libraryDiscovery->getLibraryByName($extension, $library_name);
|
||||
foreach ($library[$sub_key] as $definition) {
|
||||
if ($asset == $definition['data']) {
|
||||
return $this->fail($message);
|
||||
}
|
||||
}
|
||||
return $this->pass($message);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Asset;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests that the asset files for all core libraries exist.
|
||||
*
|
||||
* This test also changes the active theme to each core theme to verify
|
||||
* the libraries after theme-level libraries-override and libraries-extend are
|
||||
* applied.
|
||||
*
|
||||
* @group Asset
|
||||
*/
|
||||
class ResolvedLibraryDefinitionsFilesMatchTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* The theme handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ThemeHandlerInterface
|
||||
*/
|
||||
protected $themeHandler;
|
||||
|
||||
/**
|
||||
* The theme initialization.
|
||||
*
|
||||
* @var \Drupal\Core\Theme\ThemeInitializationInterface
|
||||
*/
|
||||
protected $themeInitialization;
|
||||
|
||||
/**
|
||||
* The theme manager.
|
||||
*
|
||||
* @var \Drupal\Core\Theme\ThemeManagerInterface
|
||||
*/
|
||||
protected $themeManager;
|
||||
|
||||
/**
|
||||
* The library discovery service.
|
||||
*
|
||||
* @var \Drupal\Core\Asset\LibraryDiscoveryInterface
|
||||
*/
|
||||
protected $libraryDiscovery;
|
||||
|
||||
/**
|
||||
* A list of all core modules.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $allModules;
|
||||
|
||||
/**
|
||||
* A list of all core themes.
|
||||
*
|
||||
* We hardcode this because test themes don't use a 'package' or 'hidden' key
|
||||
* so we don't have a good way of filtering to only get "real" themes.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $allThemes = [
|
||||
'bartik',
|
||||
'classy',
|
||||
'seven',
|
||||
'stable',
|
||||
'stark',
|
||||
];
|
||||
|
||||
/**
|
||||
* A list of libraries to skip checking, in the format extension/library_name.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $librariesToSkip = [
|
||||
// Locale has a "dummy" library that does not actually exist.
|
||||
'locale/translations',
|
||||
];
|
||||
|
||||
/**
|
||||
* A list of all paths that have been checked.
|
||||
*
|
||||
* @var array[]
|
||||
*/
|
||||
protected $pathsChecked;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['system'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Install all core themes.
|
||||
sort($this->allThemes);
|
||||
$this->container->get('theme_installer')->install($this->allThemes);
|
||||
|
||||
// Enable all core modules.
|
||||
$all_modules = system_rebuild_module_data();
|
||||
$all_modules = array_filter($all_modules, function ($module) {
|
||||
// Filter contrib, hidden, already enabled modules and modules in the
|
||||
// Testing package.
|
||||
if ($module->origin !== 'core' || !empty($module->info['hidden']) || $module->status == TRUE || $module->info['package'] == 'Testing') {
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
});
|
||||
$this->allModules = array_keys($all_modules);
|
||||
$this->allModules[] = 'system';
|
||||
sort($this->allModules);
|
||||
$this->container->get('module_installer')->install($this->allModules);
|
||||
|
||||
$this->themeHandler = $this->container->get('theme_handler');
|
||||
$this->themeInitialization = $this->container->get('theme.initialization');
|
||||
$this->themeManager = $this->container->get('theme.manager');
|
||||
$this->libraryDiscovery = $this->container->get('library.discovery');
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that all core module and theme library files exist.
|
||||
*/
|
||||
public function testCoreLibraryCompleteness() {
|
||||
// First verify all libraries with no active theme.
|
||||
$this->verifyLibraryFilesExist($this->getAllLibraries());
|
||||
|
||||
// Then verify all libraries for each core theme. This may seem like
|
||||
// overkill but themes can override and extend other extensions' libraries
|
||||
// and these changes are only applied for the active theme.
|
||||
foreach ($this->allThemes as $theme) {
|
||||
$this->themeManager->setActiveTheme($this->themeInitialization->getActiveThemeByName($theme));
|
||||
$this->libraryDiscovery->clearCachedDefinitions();
|
||||
|
||||
$this->verifyLibraryFilesExist($this->getAllLibraries());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that all the library files exist.
|
||||
*
|
||||
* @param array[] $library_definitions
|
||||
* An array of library definitions, keyed by extension, then by library, and
|
||||
* so on.
|
||||
*/
|
||||
protected function verifyLibraryFilesExist($library_definitions) {
|
||||
$root = \Drupal::root();
|
||||
foreach ($library_definitions as $extension => $libraries) {
|
||||
foreach ($libraries as $library_name => $library) {
|
||||
if (in_array("$extension/$library_name", $this->librariesToSkip)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check that all the assets exist.
|
||||
foreach (['css', 'js'] as $asset_type) {
|
||||
foreach ($library[$asset_type] as $asset) {
|
||||
$file = $asset['data'];
|
||||
$path = $root . '/' . $file;
|
||||
// Only check and assert each file path once.
|
||||
if (!isset($this->pathsChecked[$path])) {
|
||||
$this->assertTrue(is_file($path), "$file file referenced from the $extension/$library_name library exists.");
|
||||
$this->pathsChecked[$path] = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all libraries for core and all installed modules.
|
||||
*
|
||||
* @return \Drupal\Core\Extension\Extension[]
|
||||
*/
|
||||
protected function getAllLibraries() {
|
||||
$modules = \Drupal::moduleHandler()->getModuleList();
|
||||
$extensions = $modules;
|
||||
$module_list = array_keys($modules);
|
||||
sort($module_list);
|
||||
$this->assertEqual($this->allModules, $module_list, 'All core modules are installed.');
|
||||
|
||||
$themes = $this->themeHandler->listInfo();
|
||||
$extensions += $themes;
|
||||
$theme_list = array_keys($themes);
|
||||
sort($theme_list);
|
||||
$this->assertEqual($this->allThemes, $theme_list, 'All core themes are installed.');
|
||||
|
||||
$libraries['core'] = $this->libraryDiscovery->getLibrariesByExtension('core');
|
||||
|
||||
$root = \Drupal::root();
|
||||
foreach ($extensions as $extension_name => $extension) {
|
||||
$library_file = $extension->getPath() . '/' . $extension_name . '.libraries.yml';
|
||||
if (is_file($root . '/' . $library_file)) {
|
||||
$libraries[$extension_name] = $this->libraryDiscovery->getLibrariesByExtension($extension_name);
|
||||
}
|
||||
}
|
||||
return $libraries;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Block;
|
||||
|
||||
use Drupal\block_test\PluginForm\EmptyBlockForm;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests that blocks can have multiple forms.
|
||||
*
|
||||
* @group block
|
||||
*/
|
||||
class MultipleBlockFormTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['system', 'block', 'block_test'];
|
||||
|
||||
/**
|
||||
* Tests that blocks can have multiple forms.
|
||||
*/
|
||||
public function testMultipleForms() {
|
||||
$configuration = ['label' => 'A very cool block'];
|
||||
$block = \Drupal::service('plugin.manager.block')->createInstance('test_multiple_forms_block', $configuration);
|
||||
|
||||
$form_object1 = \Drupal::service('plugin_form.factory')->createInstance($block, 'configure');
|
||||
$form_object2 = \Drupal::service('plugin_form.factory')->createInstance($block, 'secondary');
|
||||
|
||||
// Assert that the block itself is used for the default form.
|
||||
$this->assertSame($block, $form_object1);
|
||||
|
||||
// Ensure that EmptyBlockForm is used and the plugin is set.
|
||||
$this->assertInstanceOf(EmptyBlockForm::class, $form_object2);
|
||||
$this->assertAttributeEquals($block, 'plugin', $form_object2);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Bootstrap;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests that drupal_get_filename() works correctly.
|
||||
*
|
||||
* @group Bootstrap
|
||||
*/
|
||||
class GetFilenameTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['system'];
|
||||
|
||||
/**
|
||||
* Tests that drupal_get_filename() works when the file is not in database.
|
||||
*/
|
||||
function testDrupalGetFilename() {
|
||||
// drupal_get_profile() is using obtaining the profile from state if the
|
||||
// install_state global is not set.
|
||||
global $install_state;
|
||||
$install_state['parameters']['profile'] = 'testing';
|
||||
|
||||
// Rebuild system.module.files state data.
|
||||
// @todo Remove as part of https://www.drupal.org/node/2186491
|
||||
drupal_static_reset('system_rebuild_module_data');
|
||||
system_rebuild_module_data();
|
||||
|
||||
// Retrieving the location of a module.
|
||||
$this->assertIdentical(drupal_get_filename('module', 'system'), 'core/modules/system/system.info.yml');
|
||||
|
||||
// Retrieving the location of a theme.
|
||||
\Drupal::service('theme_handler')->install(array('stark'));
|
||||
$this->assertIdentical(drupal_get_filename('theme', 'stark'), 'core/themes/stark/stark.info.yml');
|
||||
|
||||
// Retrieving the location of a theme engine.
|
||||
$this->assertIdentical(drupal_get_filename('theme_engine', 'twig'), 'core/themes/engines/twig/twig.info.yml');
|
||||
|
||||
// Retrieving the location of a profile. Profiles are a special case with
|
||||
// a fixed location and naming.
|
||||
$this->assertIdentical(drupal_get_filename('profile', 'testing'), 'core/profiles/testing/testing.info.yml');
|
||||
|
||||
|
||||
// Generate a non-existing module name.
|
||||
$non_existing_module = uniqid("", TRUE);
|
||||
|
||||
// Set a custom error handler so we can ignore the file not found error.
|
||||
set_error_handler(function($severity, $message, $file, $line) {
|
||||
// Skip error handling if this is a "file not found" error.
|
||||
if (strstr($message, 'is missing from the file system:')) {
|
||||
\Drupal::state()->set('get_filename_test_triggered_error', TRUE);
|
||||
return;
|
||||
}
|
||||
throw new \ErrorException($message, 0, $severity, $file, $line);
|
||||
});
|
||||
$this->assertNull(drupal_get_filename('module', $non_existing_module), 'Searching for an item that does not exist returns NULL.');
|
||||
$this->assertTrue(\Drupal::state()->get('get_filename_test_triggered_error'), 'Searching for an item that does not exist triggers an error.');
|
||||
// Restore the original error handler.
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Bootstrap;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests that drupal_static() and drupal_static_reset() work.
|
||||
*
|
||||
* @group Bootstrap
|
||||
*/
|
||||
class ResettableStaticTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Tests drupal_static() function.
|
||||
*
|
||||
* Tests that a variable reference returned by drupal_static() gets reset when
|
||||
* drupal_static_reset() is called.
|
||||
*/
|
||||
function testDrupalStatic() {
|
||||
$name = __CLASS__ . '_' . __METHOD__;
|
||||
$var = &drupal_static($name, 'foo');
|
||||
$this->assertEqual($var, 'foo', 'Variable returned by drupal_static() was set to its default.');
|
||||
|
||||
// Call the specific reset and the global reset each twice to ensure that
|
||||
// multiple resets can be issued without odd side effects.
|
||||
$var = 'bar';
|
||||
drupal_static_reset($name);
|
||||
$this->assertEqual($var, 'foo', 'Variable was reset after first invocation of name-specific reset.');
|
||||
$var = 'bar';
|
||||
drupal_static_reset($name);
|
||||
$this->assertEqual($var, 'foo', 'Variable was reset after second invocation of name-specific reset.');
|
||||
$var = 'bar';
|
||||
drupal_static_reset();
|
||||
$this->assertEqual($var, 'foo', 'Variable was reset after first invocation of global reset.');
|
||||
$var = 'bar';
|
||||
drupal_static_reset();
|
||||
$this->assertEqual($var, 'foo', 'Variable was reset after second invocation of global reset.');
|
||||
}
|
||||
|
||||
}
|
207
core/tests/Drupal/KernelTests/Core/Cache/ApcuBackendTest.php
Normal file
207
core/tests/Drupal/KernelTests/Core/Cache/ApcuBackendTest.php
Normal file
|
@ -0,0 +1,207 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Cache;
|
||||
|
||||
use Drupal\Core\Cache\Apcu4Backend;
|
||||
use Drupal\Core\Cache\ApcuBackend;
|
||||
|
||||
/**
|
||||
* Tests the APCu cache backend.
|
||||
*
|
||||
* @group Cache
|
||||
* @requires extension apcu
|
||||
*/
|
||||
class ApcuBackendTest extends GenericCacheBackendUnitTestBase {
|
||||
|
||||
/**
|
||||
* Get a list of failed requirements.
|
||||
*
|
||||
* This specifically bypasses checkRequirements because it fails tests. PHP 7
|
||||
* does not have APCu and simpletest does not have a explicit "skip"
|
||||
* functionality so to emulate it we override all test methods and explicitly
|
||||
* pass when requirements are not met.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getRequirements() {
|
||||
$requirements = [];
|
||||
if (!extension_loaded('apcu')) {
|
||||
$requirements[] = 'APCu extension not found.';
|
||||
}
|
||||
else {
|
||||
if (PHP_SAPI === 'cli' && !ini_get('apc.enable_cli')) {
|
||||
$requirements[] = 'apc.enable_cli must be enabled to run this test.';
|
||||
}
|
||||
}
|
||||
return $requirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if requirements fail.
|
||||
*
|
||||
* If the requirements fail the test method should return immediately instead
|
||||
* of running any tests. Messages will be output to display why the test was
|
||||
* skipped.
|
||||
*/
|
||||
protected function requirementsFail() {
|
||||
$requirements = $this->getRequirements();
|
||||
if (!empty($requirements)) {
|
||||
foreach ($requirements as $message) {
|
||||
$this->pass($message);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createCacheBackend($bin) {
|
||||
if (version_compare(phpversion('apcu'), '5.0.0', '>=')) {
|
||||
return new ApcuBackend($bin, $this->databasePrefix, \Drupal::service('cache_tags.invalidator.checksum'));
|
||||
}
|
||||
else {
|
||||
return new Apcu4Backend($bin, $this->databasePrefix, \Drupal::service('cache_tags.invalidator.checksum'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function tearDown() {
|
||||
foreach ($this->cachebackends as $bin => $cachebackend) {
|
||||
$this->cachebackends[$bin]->removeBin();
|
||||
}
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testSetGet() {
|
||||
if ($this->requirementsFail()) {
|
||||
return;
|
||||
}
|
||||
parent::testSetGet();
|
||||
|
||||
// Make sure entries are permanent (i.e. no TTL).
|
||||
$backend = $this->getCacheBackend($this->getTestBin());
|
||||
$key = $backend->getApcuKey('TEST8');
|
||||
|
||||
if (class_exists('\APCUIterator')) {
|
||||
$iterator = new \APCUIterator('/^' . $key . '/');
|
||||
}
|
||||
else {
|
||||
$iterator = new \APCIterator('user', '/^' . $key . '/');
|
||||
}
|
||||
|
||||
foreach ($iterator as $item) {
|
||||
$this->assertEqual(0, $item['ttl']);
|
||||
$found = TRUE;
|
||||
}
|
||||
$this->assertTrue($found);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testDelete() {
|
||||
if ($this->requirementsFail()) {
|
||||
return;
|
||||
}
|
||||
parent::testDelete();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testValueTypeIsKept() {
|
||||
if ($this->requirementsFail()) {
|
||||
return;
|
||||
}
|
||||
parent::testValueTypeIsKept();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testGetMultiple() {
|
||||
if ($this->requirementsFail()) {
|
||||
return;
|
||||
}
|
||||
parent::testGetMultiple();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testSetMultiple() {
|
||||
if ($this->requirementsFail()) {
|
||||
return;
|
||||
}
|
||||
parent::testSetMultiple();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testDeleteMultiple() {
|
||||
if ($this->requirementsFail()) {
|
||||
return;
|
||||
}
|
||||
parent::testDeleteMultiple();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testDeleteAll() {
|
||||
if ($this->requirementsFail()) {
|
||||
return;
|
||||
}
|
||||
parent::testDeleteAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testInvalidate() {
|
||||
if ($this->requirementsFail()) {
|
||||
return;
|
||||
}
|
||||
parent::testInvalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testInvalidateTags() {
|
||||
if ($this->requirementsFail()) {
|
||||
return;
|
||||
}
|
||||
parent::testInvalidateTags();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testInvalidateAll() {
|
||||
if ($this->requirementsFail()) {
|
||||
return;
|
||||
}
|
||||
parent::testInvalidateAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testRemoveBin() {
|
||||
if ($this->requirementsFail()) {
|
||||
return;
|
||||
}
|
||||
parent::testRemoveBin();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Cache;
|
||||
|
||||
use Drupal\Core\Cache\BackendChain;
|
||||
use Drupal\Core\Cache\MemoryBackend;
|
||||
|
||||
/**
|
||||
* Unit test of the backend chain using the generic cache unit test base.
|
||||
*
|
||||
* @group Cache
|
||||
*/
|
||||
class BackendChainTest extends GenericCacheBackendUnitTestBase {
|
||||
|
||||
protected function createCacheBackend($bin) {
|
||||
$chain = new BackendChain($bin);
|
||||
|
||||
// We need to create some various backends in the chain.
|
||||
$chain
|
||||
->appendBackend(new MemoryBackend('foo'))
|
||||
->prependBackend(new MemoryBackend('bar'))
|
||||
->appendBackend(new MemoryBackend('baz'));
|
||||
|
||||
\Drupal::service('cache_tags.invalidator')->addInvalidator($chain);
|
||||
|
||||
return $chain;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Cache;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\simpletest\UserCreationTrait;
|
||||
use Drupal\user\Entity\Role;
|
||||
|
||||
/**
|
||||
* Tests the cache context optimization.
|
||||
*
|
||||
* @group Render
|
||||
*/
|
||||
class CacheContextOptimizationTest extends KernelTestBase {
|
||||
|
||||
use UserCreationTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public static $modules = ['user', 'system'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('user');
|
||||
$this->installConfig(['user']);
|
||||
$this->installSchema('system', ['sequences']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that 'user.permissions' cache context is able to define cache tags.
|
||||
*/
|
||||
public function testUserPermissionCacheContextOptimization() {
|
||||
$user1 = $this->createUser();
|
||||
$this->assertEqual($user1->id(), 1);
|
||||
|
||||
$authenticated_user = $this->createUser(['administer permissions']);
|
||||
$role = $authenticated_user->getRoles()[1];
|
||||
|
||||
$test_element = [
|
||||
'#cache' => [
|
||||
'keys' => ['test'],
|
||||
'contexts' => ['user', 'user.permissions'],
|
||||
],
|
||||
];
|
||||
\Drupal::service('account_switcher')->switchTo($authenticated_user);
|
||||
$element = $test_element;
|
||||
$element['#markup'] = 'content for authenticated users';
|
||||
$output = \Drupal::service('renderer')->renderRoot($element);
|
||||
$this->assertEqual($output, 'content for authenticated users');
|
||||
|
||||
// Verify that the render caching is working so that other tests can be
|
||||
// trusted.
|
||||
$element = $test_element;
|
||||
$element['#markup'] = 'this should not be visible';
|
||||
$output = \Drupal::service('renderer')->renderRoot($element);
|
||||
$this->assertEqual($output, 'content for authenticated users');
|
||||
|
||||
// Even though the cache contexts have been optimized to only include 'user'
|
||||
// cache context, the element should have been changed because
|
||||
// 'user.permissions' cache context defined a cache tags for permission
|
||||
// changes, which should have bubbled up for the element when it was
|
||||
// optimized away.
|
||||
Role::load($role)
|
||||
->revokePermission('administer permissions')
|
||||
->save();
|
||||
$element = $test_element;
|
||||
$element['#markup'] = 'this should be visible';
|
||||
$output = \Drupal::service('renderer')->renderRoot($element);
|
||||
$this->assertEqual($output, 'this should be visible');
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that 'user.roles' still works when it is optimized away.
|
||||
*/
|
||||
public function testUserRolesCacheContextOptimization() {
|
||||
$root_user = $this->createUser();
|
||||
$this->assertEqual($root_user->id(), 1);
|
||||
|
||||
$authenticated_user = $this->createUser(['administer permissions']);
|
||||
$role = $authenticated_user->getRoles()[1];
|
||||
|
||||
$test_element = [
|
||||
'#cache' => [
|
||||
'keys' => ['test'],
|
||||
'contexts' => ['user', 'user.roles'],
|
||||
],
|
||||
];
|
||||
\Drupal::service('account_switcher')->switchTo($authenticated_user);
|
||||
$element = $test_element;
|
||||
$element['#markup'] = 'content for authenticated users';
|
||||
$output = \Drupal::service('renderer')->renderRoot($element);
|
||||
$this->assertEqual($output, 'content for authenticated users');
|
||||
|
||||
// Verify that the render caching is working so that other tests can be
|
||||
// trusted.
|
||||
$element = $test_element;
|
||||
$element['#markup'] = 'this should not be visible';
|
||||
$output = \Drupal::service('renderer')->renderRoot($element);
|
||||
$this->assertEqual($output, 'content for authenticated users');
|
||||
|
||||
// Even though the cache contexts have been optimized to only include 'user'
|
||||
// cache context, the element should have been changed because 'user.roles'
|
||||
// cache context defined a cache tag for user entity changes, which should
|
||||
// have bubbled up for the element when it was optimized away.
|
||||
$authenticated_user->removeRole($role);
|
||||
$authenticated_user->save();
|
||||
$element = $test_element;
|
||||
$element['#markup'] = 'this should be visible';
|
||||
$output = \Drupal::service('renderer')->renderRoot($element);
|
||||
$this->assertEqual($output, 'this should be visible');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Cache;
|
||||
|
||||
use Drupal\Core\Cache\ChainedFastBackend;
|
||||
use Drupal\Core\Cache\DatabaseBackend;
|
||||
use Drupal\Core\Cache\PhpBackend;
|
||||
|
||||
/**
|
||||
* Unit test of the fast chained backend using the generic cache unit test base.
|
||||
*
|
||||
* @group Cache
|
||||
*/
|
||||
class ChainedFastBackendTest extends GenericCacheBackendUnitTestBase {
|
||||
|
||||
/**
|
||||
* Creates a new instance of ChainedFastBackend.
|
||||
*
|
||||
* @return \Drupal\Core\Cache\ChainedFastBackend
|
||||
* A new ChainedFastBackend object.
|
||||
*/
|
||||
protected function createCacheBackend($bin) {
|
||||
$consistent_backend = new DatabaseBackend(\Drupal::service('database'), \Drupal::service('cache_tags.invalidator.checksum'), $bin);
|
||||
$fast_backend = new PhpBackend($bin, \Drupal::service('cache_tags.invalidator.checksum'));
|
||||
$backend = new ChainedFastBackend($consistent_backend, $fast_backend, $bin);
|
||||
// Explicitly register the cache bin as it can not work through the
|
||||
// cache bin list in the container.
|
||||
\Drupal::service('cache_tags.invalidator')->addInvalidator($backend);
|
||||
return $backend;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Cache;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* Tests DatabaseBackend cache tag implementation.
|
||||
*
|
||||
* @group Cache
|
||||
*/
|
||||
class DatabaseBackendTagTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function register(ContainerBuilder $container) {
|
||||
parent::register($container);
|
||||
// Change container to database cache backends.
|
||||
$container
|
||||
->register('cache_factory', 'Drupal\Core\Cache\CacheFactory')
|
||||
->addArgument(new Reference('settings'))
|
||||
->addMethodCall('setContainer', array(new Reference('service_container')));
|
||||
}
|
||||
|
||||
public function testTagInvalidations() {
|
||||
// Create cache entry in multiple bins.
|
||||
$tags = array('test_tag:1', 'test_tag:2', 'test_tag:3');
|
||||
$bins = array('data', 'bootstrap', 'render');
|
||||
foreach ($bins as $bin) {
|
||||
$bin = \Drupal::cache($bin);
|
||||
$bin->set('test', 'value', Cache::PERMANENT, $tags);
|
||||
$this->assertTrue($bin->get('test'), 'Cache item was set in bin.');
|
||||
}
|
||||
|
||||
$invalidations_before = intval(db_select('cachetags')->fields('cachetags', array('invalidations'))->condition('tag', 'test_tag:2')->execute()->fetchField());
|
||||
Cache::invalidateTags(array('test_tag:2'));
|
||||
|
||||
// Test that cache entry has been invalidated in multiple bins.
|
||||
foreach ($bins as $bin) {
|
||||
$bin = \Drupal::cache($bin);
|
||||
$this->assertFalse($bin->get('test'), 'Tag invalidation affected item in bin.');
|
||||
}
|
||||
|
||||
// Test that only one tag invalidation has occurred.
|
||||
$invalidations_after = intval(db_select('cachetags')->fields('cachetags', array('invalidations'))->condition('tag', 'test_tag:2')->execute()->fetchField());
|
||||
$this->assertEqual($invalidations_after, $invalidations_before + 1, 'Only one addition cache tag invalidation has occurred after invalidating a tag used in multiple bins.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Cache;
|
||||
|
||||
use Drupal\Core\Cache\DatabaseBackend;
|
||||
|
||||
/**
|
||||
* Unit test of the database backend using the generic cache unit test base.
|
||||
*
|
||||
* @group Cache
|
||||
*/
|
||||
class DatabaseBackendTest extends GenericCacheBackendUnitTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system');
|
||||
|
||||
/**
|
||||
* Creates a new instance of DatabaseBackend.
|
||||
*
|
||||
* @return
|
||||
* A new DatabaseBackend object.
|
||||
*/
|
||||
protected function createCacheBackend($bin) {
|
||||
return new DatabaseBackend($this->container->get('database'), $this->container->get('cache_tags.invalidator.checksum'), $bin);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testSetGet() {
|
||||
parent::testSetGet();
|
||||
$backend = $this->getCacheBackend();
|
||||
|
||||
// Set up a cache ID that is not ASCII and longer than 255 characters so we
|
||||
// can test cache ID normalization.
|
||||
$cid_long = str_repeat('愛€', 500);
|
||||
$cached_value_long = $this->randomMachineName();
|
||||
$backend->set($cid_long, $cached_value_long);
|
||||
$this->assertIdentical($cached_value_long, $backend->get($cid_long)->data, "Backend contains the correct value for long, non-ASCII cache id.");
|
||||
|
||||
$cid_short = '愛1€';
|
||||
$cached_value_short = $this->randomMachineName();
|
||||
$backend->set($cid_short, $cached_value_short);
|
||||
$this->assertIdentical($cached_value_short, $backend->get($cid_short)->data, "Backend contains the correct value for short, non-ASCII cache id.");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,620 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Cache;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests any cache backend.
|
||||
*
|
||||
* Full generic unit test suite for any cache backend. In order to use it for a
|
||||
* cache backend implementation, extend this class and override the
|
||||
* createBackendInstance() method to return an object.
|
||||
*
|
||||
* @see DatabaseBackendUnitTestCase
|
||||
* For a full working implementation.
|
||||
*/
|
||||
abstract class GenericCacheBackendUnitTestBase extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Array of objects implementing Drupal\Core\Cache\CacheBackendInterface.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $cachebackends;
|
||||
|
||||
/**
|
||||
* Cache bin to use for testing.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $testBin;
|
||||
|
||||
/**
|
||||
* Random value to use in tests.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $defaultValue;
|
||||
|
||||
/**
|
||||
* Gets the testing bin.
|
||||
*
|
||||
* Override this method if you want to work on a different bin than the
|
||||
* default one.
|
||||
*
|
||||
* @return string
|
||||
* Bin name.
|
||||
*/
|
||||
protected function getTestBin() {
|
||||
if (!isset($this->testBin)) {
|
||||
$this->testBin = 'page';
|
||||
}
|
||||
return $this->testBin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cache backend to test.
|
||||
*
|
||||
* Override this method to test a CacheBackend.
|
||||
*
|
||||
* @param string $bin
|
||||
* Bin name to use for this backend instance.
|
||||
*
|
||||
* @return \Drupal\Core\Cache\CacheBackendInterface
|
||||
* Cache backend to test.
|
||||
*/
|
||||
protected abstract function createCacheBackend($bin);
|
||||
|
||||
/**
|
||||
* Allows specific implementation to change the environment before a test run.
|
||||
*/
|
||||
public function setUpCacheBackend() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows alteration of environment after a test run but before tear down.
|
||||
*
|
||||
* Used before the real tear down because the tear down will change things
|
||||
* such as the database prefix.
|
||||
*/
|
||||
public function tearDownCacheBackend() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a backend to test; this will get a shared instance set in the object.
|
||||
*
|
||||
* @return \Drupal\Core\Cache\CacheBackendInterface
|
||||
* Cache backend to test.
|
||||
*/
|
||||
protected function getCacheBackend($bin = NULL) {
|
||||
if (!isset($bin)) {
|
||||
$bin = $this->getTestBin();
|
||||
}
|
||||
if (!isset($this->cachebackends[$bin])) {
|
||||
$this->cachebackends[$bin] = $this->createCacheBackend($bin);
|
||||
// Ensure the backend is empty.
|
||||
$this->cachebackends[$bin]->deleteAll();
|
||||
}
|
||||
return $this->cachebackends[$bin];
|
||||
}
|
||||
|
||||
protected function setUp() {
|
||||
$this->cachebackends = array();
|
||||
$this->defaultValue = $this->randomMachineName(10);
|
||||
|
||||
parent::setUp();
|
||||
|
||||
$this->setUpCacheBackend();
|
||||
}
|
||||
|
||||
protected function tearDown() {
|
||||
// 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.
|
||||
foreach ($this->cachebackends as $bin => $cachebackend) {
|
||||
$this->cachebackends[$bin]->deleteAll();
|
||||
}
|
||||
unset($this->cachebackends);
|
||||
|
||||
$this->tearDownCacheBackend();
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the get and set methods of Drupal\Core\Cache\CacheBackendInterface.
|
||||
*/
|
||||
public function testSetGet() {
|
||||
$backend = $this->getCacheBackend();
|
||||
|
||||
$this->assertIdentical(FALSE, $backend->get('test1'), "Backend does not contain data for cache id test1.");
|
||||
$with_backslash = array('foo' => '\Drupal\foo\Bar');
|
||||
$backend->set('test1', $with_backslash);
|
||||
$cached = $backend->get('test1');
|
||||
$this->assert(is_object($cached), "Backend returned an object for cache id test1.");
|
||||
$this->assertIdentical($with_backslash, $cached->data);
|
||||
$this->assertTrue($cached->valid, 'Item is marked as valid.');
|
||||
// We need to round because microtime may be rounded up in the backend.
|
||||
$this->assertTrue($cached->created >= REQUEST_TIME && $cached->created <= round(microtime(TRUE), 3), 'Created time is correct.');
|
||||
$this->assertEqual($cached->expire, Cache::PERMANENT, 'Expire time is correct.');
|
||||
|
||||
$this->assertIdentical(FALSE, $backend->get('test2'), "Backend does not contain data for cache id test2.");
|
||||
$backend->set('test2', array('value' => 3), REQUEST_TIME + 3);
|
||||
$cached = $backend->get('test2');
|
||||
$this->assert(is_object($cached), "Backend returned an object for cache id test2.");
|
||||
$this->assertIdentical(array('value' => 3), $cached->data);
|
||||
$this->assertTrue($cached->valid, 'Item is marked as valid.');
|
||||
$this->assertTrue($cached->created >= REQUEST_TIME && $cached->created <= round(microtime(TRUE), 3), 'Created time is correct.');
|
||||
$this->assertEqual($cached->expire, REQUEST_TIME + 3, 'Expire time is correct.');
|
||||
|
||||
$backend->set('test3', 'foobar', REQUEST_TIME - 3);
|
||||
$this->assertFalse($backend->get('test3'), 'Invalid item not returned.');
|
||||
$cached = $backend->get('test3', TRUE);
|
||||
$this->assert(is_object($cached), 'Backend returned an object for cache id test3.');
|
||||
$this->assertFalse($cached->valid, 'Item is marked as valid.');
|
||||
$this->assertTrue($cached->created >= REQUEST_TIME && $cached->created <= round(microtime(TRUE), 3), 'Created time is correct.');
|
||||
$this->assertEqual($cached->expire, REQUEST_TIME - 3, 'Expire time is correct.');
|
||||
|
||||
$this->assertIdentical(FALSE, $backend->get('test4'), "Backend does not contain data for cache id test4.");
|
||||
$with_eof = array('foo' => "\nEOF\ndata");
|
||||
$backend->set('test4', $with_eof);
|
||||
$cached = $backend->get('test4');
|
||||
$this->assert(is_object($cached), "Backend returned an object for cache id test4.");
|
||||
$this->assertIdentical($with_eof, $cached->data);
|
||||
$this->assertTrue($cached->valid, 'Item is marked as valid.');
|
||||
$this->assertTrue($cached->created >= REQUEST_TIME && $cached->created <= round(microtime(TRUE), 3), 'Created time is correct.');
|
||||
$this->assertEqual($cached->expire, Cache::PERMANENT, 'Expire time is correct.');
|
||||
|
||||
$this->assertIdentical(FALSE, $backend->get('test5'), "Backend does not contain data for cache id test5.");
|
||||
$with_eof_and_semicolon = array('foo' => "\nEOF;\ndata");
|
||||
$backend->set('test5', $with_eof_and_semicolon);
|
||||
$cached = $backend->get('test5');
|
||||
$this->assert(is_object($cached), "Backend returned an object for cache id test5.");
|
||||
$this->assertIdentical($with_eof_and_semicolon, $cached->data);
|
||||
$this->assertTrue($cached->valid, 'Item is marked as valid.');
|
||||
$this->assertTrue($cached->created >= REQUEST_TIME && $cached->created <= round(microtime(TRUE), 3), 'Created time is correct.');
|
||||
$this->assertEqual($cached->expire, Cache::PERMANENT, 'Expire time is correct.');
|
||||
|
||||
$with_variable = array('foo' => '$bar');
|
||||
$backend->set('test6', $with_variable);
|
||||
$cached = $backend->get('test6');
|
||||
$this->assert(is_object($cached), "Backend returned an object for cache id test6.");
|
||||
$this->assertIdentical($with_variable, $cached->data);
|
||||
|
||||
// Make sure that a cached object is not affected by changing the original.
|
||||
$data = new \stdClass();
|
||||
$data->value = 1;
|
||||
$data->obj = new \stdClass();
|
||||
$data->obj->value = 2;
|
||||
$backend->set('test7', $data);
|
||||
$expected_data = clone $data;
|
||||
// Add a property to the original. It should not appear in the cached data.
|
||||
$data->this_should_not_be_in_the_cache = TRUE;
|
||||
$cached = $backend->get('test7');
|
||||
$this->assert(is_object($cached), "Backend returned an object for cache id test7.");
|
||||
$this->assertEqual($expected_data, $cached->data);
|
||||
$this->assertFalse(isset($cached->data->this_should_not_be_in_the_cache));
|
||||
// Add a property to the cache data. It should not appear when we fetch
|
||||
// the data from cache again.
|
||||
$cached->data->this_should_not_be_in_the_cache = TRUE;
|
||||
$fresh_cached = $backend->get('test7');
|
||||
$this->assertFalse(isset($fresh_cached->data->this_should_not_be_in_the_cache));
|
||||
|
||||
// Check with a long key.
|
||||
$cid = str_repeat('a', 300);
|
||||
$backend->set($cid, 'test');
|
||||
$this->assertEqual('test', $backend->get($cid)->data);
|
||||
|
||||
// Check that the cache key is case sensitive.
|
||||
$backend->set('TEST8', 'value');
|
||||
$this->assertEqual('value', $backend->get('TEST8')->data);
|
||||
$this->assertFalse($backend->get('test8'));
|
||||
|
||||
// Calling ::set() with invalid cache tags. This should fail an assertion.
|
||||
try {
|
||||
$backend->set('assertion_test', 'value', Cache::PERMANENT, ['node' => [3, 5, 7]]);
|
||||
$this->fail('::set() was called with invalid cache tags, runtime assertion did not fail.');
|
||||
}
|
||||
catch (\AssertionError $e) {
|
||||
$this->pass('::set() was called with invalid cache tags, runtime assertion failed.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Drupal\Core\Cache\CacheBackendInterface::delete().
|
||||
*/
|
||||
public function testDelete() {
|
||||
$backend = $this->getCacheBackend();
|
||||
|
||||
$this->assertIdentical(FALSE, $backend->get('test1'), "Backend does not contain data for cache id test1.");
|
||||
$backend->set('test1', 7);
|
||||
$this->assert(is_object($backend->get('test1')), "Backend returned an object for cache id test1.");
|
||||
|
||||
$this->assertIdentical(FALSE, $backend->get('test2'), "Backend does not contain data for cache id test2.");
|
||||
$backend->set('test2', 3);
|
||||
$this->assert(is_object($backend->get('test2')), "Backend returned an object for cache id %cid.");
|
||||
|
||||
$backend->delete('test1');
|
||||
$this->assertIdentical(FALSE, $backend->get('test1'), "Backend does not contain data for cache id test1 after deletion.");
|
||||
|
||||
$this->assert(is_object($backend->get('test2')), "Backend still has an object for cache id test2.");
|
||||
|
||||
$backend->delete('test2');
|
||||
$this->assertIdentical(FALSE, $backend->get('test2'), "Backend does not contain data for cache id test2 after deletion.");
|
||||
|
||||
$long_cid = str_repeat('a', 300);
|
||||
$backend->set($long_cid, 'test');
|
||||
$backend->delete($long_cid);
|
||||
$this->assertIdentical(FALSE, $backend->get($long_cid), "Backend does not contain data for long cache id after deletion.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests data type preservation.
|
||||
*/
|
||||
public function testValueTypeIsKept() {
|
||||
$backend = $this->getCacheBackend();
|
||||
|
||||
$variables = array(
|
||||
'test1' => 1,
|
||||
'test2' => '0',
|
||||
'test3' => '',
|
||||
'test4' => 12.64,
|
||||
'test5' => FALSE,
|
||||
'test6' => array(1, 2, 3),
|
||||
);
|
||||
|
||||
// Create cache entries.
|
||||
foreach ($variables as $cid => $data) {
|
||||
$backend->set($cid, $data);
|
||||
}
|
||||
|
||||
// Retrieve and test cache objects.
|
||||
foreach ($variables as $cid => $value) {
|
||||
$object = $backend->get($cid);
|
||||
$this->assert(is_object($object), sprintf("Backend returned an object for cache id %s.", $cid));
|
||||
$this->assertIdentical($value, $object->data, sprintf("Data of cached id %s kept is identical in type and value", $cid));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Drupal\Core\Cache\CacheBackendInterface::getMultiple().
|
||||
*/
|
||||
public function testGetMultiple() {
|
||||
$backend = $this->getCacheBackend();
|
||||
|
||||
// Set numerous testing keys.
|
||||
$long_cid = str_repeat('a', 300);
|
||||
$backend->set('test1', 1);
|
||||
$backend->set('test2', 3);
|
||||
$backend->set('test3', 5);
|
||||
$backend->set('test4', 7);
|
||||
$backend->set('test5', 11);
|
||||
$backend->set('test6', 13);
|
||||
$backend->set('test7', 17);
|
||||
$backend->set($long_cid, 300);
|
||||
|
||||
// Mismatch order for harder testing.
|
||||
$reference = array(
|
||||
'test3',
|
||||
'test7',
|
||||
'test21', // Cid does not exist.
|
||||
'test6',
|
||||
'test19', // Cid does not exist until added before second getMultiple().
|
||||
'test2',
|
||||
);
|
||||
|
||||
$cids = $reference;
|
||||
$ret = $backend->getMultiple($cids);
|
||||
// Test return - ensure it contains existing cache ids.
|
||||
$this->assert(isset($ret['test2']), "Existing cache id test2 is set.");
|
||||
$this->assert(isset($ret['test3']), "Existing cache id test3 is set.");
|
||||
$this->assert(isset($ret['test6']), "Existing cache id test6 is set.");
|
||||
$this->assert(isset($ret['test7']), "Existing cache id test7 is set.");
|
||||
// Test return - ensure that objects has expected properties.
|
||||
$this->assertTrue($ret['test2']->valid, 'Item is marked as valid.');
|
||||
$this->assertTrue($ret['test2']->created >= REQUEST_TIME && $ret['test2']->created <= round(microtime(TRUE), 3), 'Created time is correct.');
|
||||
$this->assertEqual($ret['test2']->expire, Cache::PERMANENT, 'Expire time is correct.');
|
||||
// Test return - ensure it does not contain nonexistent cache ids.
|
||||
$this->assertFalse(isset($ret['test19']), "Nonexistent cache id test19 is not set.");
|
||||
$this->assertFalse(isset($ret['test21']), "Nonexistent cache id test21 is not set.");
|
||||
// Test values.
|
||||
$this->assertIdentical($ret['test2']->data, 3, "Existing cache id test2 has the correct value.");
|
||||
$this->assertIdentical($ret['test3']->data, 5, "Existing cache id test3 has the correct value.");
|
||||
$this->assertIdentical($ret['test6']->data, 13, "Existing cache id test6 has the correct value.");
|
||||
$this->assertIdentical($ret['test7']->data, 17, "Existing cache id test7 has the correct value.");
|
||||
// Test $cids array - ensure it contains cache id's that do not exist.
|
||||
$this->assert(in_array('test19', $cids), "Nonexistent cache id test19 is in cids array.");
|
||||
$this->assert(in_array('test21', $cids), "Nonexistent cache id test21 is in cids array.");
|
||||
// Test $cids array - ensure it does not contain cache id's that exist.
|
||||
$this->assertFalse(in_array('test2', $cids), "Existing cache id test2 is not in cids array.");
|
||||
$this->assertFalse(in_array('test3', $cids), "Existing cache id test3 is not in cids array.");
|
||||
$this->assertFalse(in_array('test6', $cids), "Existing cache id test6 is not in cids array.");
|
||||
$this->assertFalse(in_array('test7', $cids), "Existing cache id test7 is not in cids array.");
|
||||
|
||||
// Test a second time after deleting and setting new keys which ensures that
|
||||
// if the backend uses statics it does not cause unexpected results.
|
||||
$backend->delete('test3');
|
||||
$backend->delete('test6');
|
||||
$backend->set('test19', 57);
|
||||
|
||||
$cids = $reference;
|
||||
$ret = $backend->getMultiple($cids);
|
||||
// Test return - ensure it contains existing cache ids.
|
||||
$this->assert(isset($ret['test2']), "Existing cache id test2 is set");
|
||||
$this->assert(isset($ret['test7']), "Existing cache id test7 is set");
|
||||
$this->assert(isset($ret['test19']), "Added cache id test19 is set");
|
||||
// Test return - ensure it does not contain nonexistent cache ids.
|
||||
$this->assertFalse(isset($ret['test3']), "Deleted cache id test3 is not set");
|
||||
$this->assertFalse(isset($ret['test6']), "Deleted cache id test6 is not set");
|
||||
$this->assertFalse(isset($ret['test21']), "Nonexistent cache id test21 is not set");
|
||||
// Test values.
|
||||
$this->assertIdentical($ret['test2']->data, 3, "Existing cache id test2 has the correct value.");
|
||||
$this->assertIdentical($ret['test7']->data, 17, "Existing cache id test7 has the correct value.");
|
||||
$this->assertIdentical($ret['test19']->data, 57, "Added cache id test19 has the correct value.");
|
||||
// Test $cids array - ensure it contains cache id's that do not exist.
|
||||
$this->assert(in_array('test3', $cids), "Deleted cache id test3 is in cids array.");
|
||||
$this->assert(in_array('test6', $cids), "Deleted cache id test6 is in cids array.");
|
||||
$this->assert(in_array('test21', $cids), "Nonexistent cache id test21 is in cids array.");
|
||||
// Test $cids array - ensure it does not contain cache id's that exist.
|
||||
$this->assertFalse(in_array('test2', $cids), "Existing cache id test2 is not in cids array.");
|
||||
$this->assertFalse(in_array('test7', $cids), "Existing cache id test7 is not in cids array.");
|
||||
$this->assertFalse(in_array('test19', $cids), "Added cache id test19 is not in cids array.");
|
||||
|
||||
// Test with a long $cid and non-numeric array key.
|
||||
$cids = array('key:key' => $long_cid);
|
||||
$return = $backend->getMultiple($cids);
|
||||
$this->assertEqual(300, $return[$long_cid]->data);
|
||||
$this->assertTrue(empty($cids));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests \Drupal\Core\Cache\CacheBackendInterface::setMultiple().
|
||||
*/
|
||||
public function testSetMultiple() {
|
||||
$backend = $this->getCacheBackend();
|
||||
|
||||
$future_expiration = REQUEST_TIME + 100;
|
||||
|
||||
// Set multiple testing keys.
|
||||
$backend->set('cid_1', 'Some other value');
|
||||
$items = array(
|
||||
'cid_1' => array('data' => 1),
|
||||
'cid_2' => array('data' => 2),
|
||||
'cid_3' => array('data' => array(1, 2)),
|
||||
'cid_4' => array('data' => 1, 'expire' => $future_expiration),
|
||||
'cid_5' => array('data' => 1, 'tags' => array('test:a', 'test:b')),
|
||||
);
|
||||
$backend->setMultiple($items);
|
||||
$cids = array_keys($items);
|
||||
$cached = $backend->getMultiple($cids);
|
||||
|
||||
$this->assertEqual($cached['cid_1']->data, $items['cid_1']['data'], 'Over-written cache item set correctly.');
|
||||
$this->assertTrue($cached['cid_1']->valid, 'Item is marked as valid.');
|
||||
$this->assertTrue($cached['cid_1']->created >= REQUEST_TIME && $cached['cid_1']->created <= round(microtime(TRUE), 3), 'Created time is correct.');
|
||||
$this->assertEqual($cached['cid_1']->expire, CacheBackendInterface::CACHE_PERMANENT, 'Cache expiration defaults to permanent.');
|
||||
|
||||
$this->assertEqual($cached['cid_2']->data, $items['cid_2']['data'], 'New cache item set correctly.');
|
||||
$this->assertEqual($cached['cid_2']->expire, CacheBackendInterface::CACHE_PERMANENT, 'Cache expiration defaults to permanent.');
|
||||
|
||||
$this->assertEqual($cached['cid_3']->data, $items['cid_3']['data'], 'New cache item with serialized data set correctly.');
|
||||
$this->assertEqual($cached['cid_3']->expire, CacheBackendInterface::CACHE_PERMANENT, 'Cache expiration defaults to permanent.');
|
||||
|
||||
$this->assertEqual($cached['cid_4']->data, $items['cid_4']['data'], 'New cache item set correctly.');
|
||||
$this->assertEqual($cached['cid_4']->expire, $future_expiration, 'Cache expiration has been correctly set.');
|
||||
|
||||
$this->assertEqual($cached['cid_5']->data, $items['cid_5']['data'], 'New cache item set correctly.');
|
||||
|
||||
// Calling ::setMultiple() with invalid cache tags. This should fail an
|
||||
// assertion.
|
||||
try {
|
||||
$items = [
|
||||
'exception_test_1' => array('data' => 1, 'tags' => []),
|
||||
'exception_test_2' => array('data' => 2, 'tags' => ['valid']),
|
||||
'exception_test_3' => array('data' => 3, 'tags' => ['node' => [3, 5, 7]]),
|
||||
];
|
||||
$backend->setMultiple($items);
|
||||
$this->fail('::setMultiple() was called with invalid cache tags, runtime assertion did not fail.');
|
||||
}
|
||||
catch (\AssertionError $e) {
|
||||
$this->pass('::setMultiple() was called with invalid cache tags, runtime assertion failed.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Drupal\Core\Cache\CacheBackendInterface::delete() and
|
||||
* Drupal\Core\Cache\CacheBackendInterface::deleteMultiple().
|
||||
*/
|
||||
public function testDeleteMultiple() {
|
||||
$backend = $this->getCacheBackend();
|
||||
|
||||
// Set numerous testing keys.
|
||||
$backend->set('test1', 1);
|
||||
$backend->set('test2', 3);
|
||||
$backend->set('test3', 5);
|
||||
$backend->set('test4', 7);
|
||||
$backend->set('test5', 11);
|
||||
$backend->set('test6', 13);
|
||||
$backend->set('test7', 17);
|
||||
|
||||
$backend->delete('test1');
|
||||
$backend->delete('test23'); // Nonexistent key should not cause an error.
|
||||
$backend->deleteMultiple(array(
|
||||
'test3',
|
||||
'test5',
|
||||
'test7',
|
||||
'test19', // Nonexistent key should not cause an error.
|
||||
'test21', // Nonexistent key should not cause an error.
|
||||
));
|
||||
|
||||
// Test if expected keys have been deleted.
|
||||
$this->assertIdentical(FALSE, $backend->get('test1'), "Cache id test1 deleted.");
|
||||
$this->assertIdentical(FALSE, $backend->get('test3'), "Cache id test3 deleted.");
|
||||
$this->assertIdentical(FALSE, $backend->get('test5'), "Cache id test5 deleted.");
|
||||
$this->assertIdentical(FALSE, $backend->get('test7'), "Cache id test7 deleted.");
|
||||
|
||||
// Test if expected keys exist.
|
||||
$this->assertNotIdentical(FALSE, $backend->get('test2'), "Cache id test2 exists.");
|
||||
$this->assertNotIdentical(FALSE, $backend->get('test4'), "Cache id test4 exists.");
|
||||
$this->assertNotIdentical(FALSE, $backend->get('test6'), "Cache id test6 exists.");
|
||||
|
||||
// Test if that expected keys do not exist.
|
||||
$this->assertIdentical(FALSE, $backend->get('test19'), "Cache id test19 does not exist.");
|
||||
$this->assertIdentical(FALSE, $backend->get('test21'), "Cache id test21 does not exist.");
|
||||
|
||||
// Calling deleteMultiple() with an empty array should not cause an error.
|
||||
$this->assertFalse($backend->deleteMultiple(array()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Drupal\Core\Cache\CacheBackendInterface::deleteAll().
|
||||
*/
|
||||
public function testDeleteAll() {
|
||||
$backend_a = $this->getCacheBackend();
|
||||
$backend_b = $this->getCacheBackend('bootstrap');
|
||||
|
||||
// Set both expiring and permanent keys.
|
||||
$backend_a->set('test1', 1, Cache::PERMANENT);
|
||||
$backend_a->set('test2', 3, time() + 1000);
|
||||
$backend_b->set('test3', 4, Cache::PERMANENT);
|
||||
|
||||
$backend_a->deleteAll();
|
||||
|
||||
$this->assertFalse($backend_a->get('test1'), 'First key has been deleted.');
|
||||
$this->assertFalse($backend_a->get('test2'), 'Second key has been deleted.');
|
||||
$this->assertTrue($backend_b->get('test3'), 'Item in other bin is preserved.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Drupal\Core\Cache\CacheBackendInterface::invalidate() and
|
||||
* Drupal\Core\Cache\CacheBackendInterface::invalidateMultiple().
|
||||
*/
|
||||
function testInvalidate() {
|
||||
$backend = $this->getCacheBackend();
|
||||
$backend->set('test1', 1);
|
||||
$backend->set('test2', 2);
|
||||
$backend->set('test3', 2);
|
||||
$backend->set('test4', 2);
|
||||
|
||||
$reference = array('test1', 'test2', 'test3', 'test4');
|
||||
|
||||
$cids = $reference;
|
||||
$ret = $backend->getMultiple($cids);
|
||||
$this->assertEqual(count($ret), 4, 'Four items returned.');
|
||||
|
||||
$backend->invalidate('test1');
|
||||
$backend->invalidateMultiple(array('test2', 'test3'));
|
||||
|
||||
$cids = $reference;
|
||||
$ret = $backend->getMultiple($cids);
|
||||
$this->assertEqual(count($ret), 1, 'Only one item element returned.');
|
||||
|
||||
$cids = $reference;
|
||||
$ret = $backend->getMultiple($cids, TRUE);
|
||||
$this->assertEqual(count($ret), 4, 'Four items returned.');
|
||||
|
||||
// Calling invalidateMultiple() with an empty array should not cause an
|
||||
// error.
|
||||
$this->assertFalse($backend->invalidateMultiple(array()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Drupal\Core\Cache\CacheBackendInterface::invalidateTags().
|
||||
*/
|
||||
function testInvalidateTags() {
|
||||
$backend = $this->getCacheBackend();
|
||||
|
||||
// Create two cache entries with the same tag and tag value.
|
||||
$backend->set('test_cid_invalidate1', $this->defaultValue, Cache::PERMANENT, array('test_tag:2'));
|
||||
$backend->set('test_cid_invalidate2', $this->defaultValue, Cache::PERMANENT, array('test_tag:2'));
|
||||
$this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Two cache items were created.');
|
||||
|
||||
// Invalidate test_tag of value 1. This should invalidate both entries.
|
||||
Cache::invalidateTags(array('test_tag:2'));
|
||||
$this->assertFalse($backend->get('test_cid_invalidate1') || $backend->get('test_cid_invalidate2'), 'Two cache items invalidated after invalidating a cache tag.');
|
||||
$this->assertTrue($backend->get('test_cid_invalidate1', TRUE) && $backend->get('test_cid_invalidate2', TRUE), 'Cache items not deleted after invalidating a cache tag.');
|
||||
|
||||
// Create two cache entries with the same tag and an array tag value.
|
||||
$backend->set('test_cid_invalidate1', $this->defaultValue, Cache::PERMANENT, array('test_tag:1'));
|
||||
$backend->set('test_cid_invalidate2', $this->defaultValue, Cache::PERMANENT, array('test_tag:1'));
|
||||
$this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Two cache items were created.');
|
||||
|
||||
// Invalidate test_tag of value 1. This should invalidate both entries.
|
||||
Cache::invalidateTags(array('test_tag:1'));
|
||||
$this->assertFalse($backend->get('test_cid_invalidate1') || $backend->get('test_cid_invalidate2'), 'Two caches removed after invalidating a cache tag.');
|
||||
$this->assertTrue($backend->get('test_cid_invalidate1', TRUE) && $backend->get('test_cid_invalidate2', TRUE), 'Cache items not deleted after invalidating a cache tag.');
|
||||
|
||||
// Create three cache entries with a mix of tags and tag values.
|
||||
$backend->set('test_cid_invalidate1', $this->defaultValue, Cache::PERMANENT, array('test_tag:1'));
|
||||
$backend->set('test_cid_invalidate2', $this->defaultValue, Cache::PERMANENT, array('test_tag:2'));
|
||||
$backend->set('test_cid_invalidate3', $this->defaultValue, Cache::PERMANENT, array('test_tag_foo:3'));
|
||||
$this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2') && $backend->get('test_cid_invalidate3'), 'Three cached items were created.');
|
||||
Cache::invalidateTags(array('test_tag_foo:3'));
|
||||
$this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Cache items not matching the tag were not invalidated.');
|
||||
$this->assertFalse($backend->get('test_cid_invalidated3'), 'Cached item matching the tag was removed.');
|
||||
|
||||
// Create cache entry in multiple bins. Two cache entries
|
||||
// (test_cid_invalidate1 and test_cid_invalidate2) still exist from previous
|
||||
// tests.
|
||||
$tags = array('test_tag:1', 'test_tag:2', 'test_tag:3');
|
||||
$bins = array('path', 'bootstrap', 'page');
|
||||
foreach ($bins as $bin) {
|
||||
$this->getCacheBackend($bin)->set('test', $this->defaultValue, Cache::PERMANENT, $tags);
|
||||
$this->assertTrue($this->getCacheBackend($bin)->get('test'), 'Cache item was set in bin.');
|
||||
}
|
||||
|
||||
Cache::invalidateTags(array('test_tag:2'));
|
||||
|
||||
// Test that the cache entry has been invalidated in multiple bins.
|
||||
foreach ($bins as $bin) {
|
||||
$this->assertFalse($this->getCacheBackend($bin)->get('test'), 'Tag invalidation affected item in bin.');
|
||||
}
|
||||
// Test that the cache entry with a matching tag has been invalidated.
|
||||
$this->assertFalse($this->getCacheBackend($bin)->get('test_cid_invalidate2'), 'Cache items matching tag were invalidated.');
|
||||
// Test that the cache entry with without a matching tag still exists.
|
||||
$this->assertTrue($this->getCacheBackend($bin)->get('test_cid_invalidate1'), 'Cache items not matching tag were not invalidated.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Drupal\Core\Cache\CacheBackendInterface::invalidateAll().
|
||||
*/
|
||||
public function testInvalidateAll() {
|
||||
$backend_a = $this->getCacheBackend();
|
||||
$backend_b = $this->getCacheBackend('bootstrap');
|
||||
|
||||
// Set both expiring and permanent keys.
|
||||
$backend_a->set('test1', 1, Cache::PERMANENT);
|
||||
$backend_a->set('test2', 3, time() + 1000);
|
||||
$backend_b->set('test3', 4, Cache::PERMANENT);
|
||||
|
||||
$backend_a->invalidateAll();
|
||||
|
||||
$this->assertFalse($backend_a->get('test1'), 'First key has been invalidated.');
|
||||
$this->assertFalse($backend_a->get('test2'), 'Second key has been invalidated.');
|
||||
$this->assertTrue($backend_b->get('test3'), 'Item in other bin is preserved.');
|
||||
$this->assertTrue($backend_a->get('test1', TRUE), 'First key has not been deleted.');
|
||||
$this->assertTrue($backend_a->get('test2', TRUE), 'Second key has not been deleted.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Drupal\Core\Cache\CacheBackendInterface::removeBin().
|
||||
*/
|
||||
public function testRemoveBin() {
|
||||
$backend_a = $this->getCacheBackend();
|
||||
$backend_b = $this->getCacheBackend('bootstrap');
|
||||
|
||||
// Set both expiring and permanent keys.
|
||||
$backend_a->set('test1', 1, Cache::PERMANENT);
|
||||
$backend_a->set('test2', 3, time() + 1000);
|
||||
$backend_b->set('test3', 4, Cache::PERMANENT);
|
||||
|
||||
$backend_a->removeBin();
|
||||
|
||||
$this->assertFalse($backend_a->get('test1'), 'First key has been deleted.');
|
||||
$this->assertFalse($backend_a->get('test2', TRUE), 'Second key has been deleted.');
|
||||
$this->assertTrue($backend_b->get('test3'), 'Item in other bin is preserved.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Cache;
|
||||
|
||||
use Drupal\Core\Cache\MemoryBackend;
|
||||
|
||||
/**
|
||||
* Unit test of the memory cache backend using the generic cache unit test base.
|
||||
*
|
||||
* @group Cache
|
||||
*/
|
||||
class MemoryBackendTest extends GenericCacheBackendUnitTestBase {
|
||||
|
||||
/**
|
||||
* Creates a new instance of MemoryBackend.
|
||||
*
|
||||
* @return
|
||||
* A new MemoryBackend object.
|
||||
*/
|
||||
protected function createCacheBackend($bin) {
|
||||
$backend = new MemoryBackend($bin);
|
||||
\Drupal::service('cache_tags.invalidator')->addInvalidator($backend);
|
||||
return $backend;
|
||||
}
|
||||
|
||||
}
|
25
core/tests/Drupal/KernelTests/Core/Cache/PhpBackendTest.php
Normal file
25
core/tests/Drupal/KernelTests/Core/Cache/PhpBackendTest.php
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Cache;
|
||||
|
||||
use Drupal\Core\Cache\PhpBackend;
|
||||
|
||||
/**
|
||||
* Unit test of the PHP cache backend using the generic cache unit test base.
|
||||
*
|
||||
* @group Cache
|
||||
*/
|
||||
class PhpBackendTest extends GenericCacheBackendUnitTestBase {
|
||||
|
||||
/**
|
||||
* Creates a new instance of MemoryBackend.
|
||||
*
|
||||
* @return
|
||||
* A new MemoryBackend object.
|
||||
*/
|
||||
protected function createCacheBackend($bin) {
|
||||
$backend = new PhpBackend($bin, \Drupal::service('cache_tags.invalidator.checksum'));
|
||||
return $backend;
|
||||
}
|
||||
|
||||
}
|
277
core/tests/Drupal/KernelTests/Core/Command/DbDumpTest.php
Normal file
277
core/tests/Drupal/KernelTests/Core/Command/DbDumpTest.php
Normal file
|
@ -0,0 +1,277 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Command;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Command\DbDumpApplication;
|
||||
use Drupal\Core\Config\DatabaseStorage;
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* Tests for the database dump commands.
|
||||
*
|
||||
* @group Update
|
||||
*/
|
||||
class DbDumpTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['system', 'config', 'dblog', 'menu_link_content', 'link', 'block_content', 'file', 'user'];
|
||||
|
||||
/**
|
||||
* Test data to write into config.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* Flag to skip these tests, which are database-backend dependent (MySQL).
|
||||
*
|
||||
* @see \Drupal\Core\Command\DbDumpCommand
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $skipTests = FALSE;
|
||||
|
||||
/**
|
||||
* An array of original table schemas.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $originalTableSchemas = [];
|
||||
|
||||
/**
|
||||
* An array of original table indexes (including primary and unique keys).
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $originalTableIndexes = [];
|
||||
|
||||
/**
|
||||
* Tables that should be part of the exported script.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $tables;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Register a database cache backend rather than memory-based.
|
||||
*/
|
||||
public function register(ContainerBuilder $container) {
|
||||
parent::register($container);
|
||||
$container->register('cache_factory', 'Drupal\Core\Cache\DatabaseBackendFactory')
|
||||
->addArgument(new Reference('database'))
|
||||
->addArgument(new Reference('cache_tags.invalidator.checksum'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Determine what database backend is running, and set the skip flag.
|
||||
$this->skipTests = Database::getConnection()->databaseType() !== 'mysql';
|
||||
|
||||
// Create some schemas so our export contains tables.
|
||||
$this->installSchema('system', [
|
||||
'key_value_expire',
|
||||
'sessions',
|
||||
]);
|
||||
$this->installSchema('dblog', ['watchdog']);
|
||||
$this->installEntitySchema('block_content');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('file');
|
||||
$this->installEntitySchema('menu_link_content');
|
||||
$this->installSchema('system', 'sequences');
|
||||
|
||||
// Place some sample config to test for in the export.
|
||||
$this->data = [
|
||||
'foo' => $this->randomMachineName(),
|
||||
'bar' => $this->randomMachineName(),
|
||||
];
|
||||
$storage = new DatabaseStorage(Database::getConnection(), 'config');
|
||||
$storage->write('test_config', $this->data);
|
||||
|
||||
// Create user account with some potential syntax issues.
|
||||
$account = User::create(['mail' => 'q\'uote$dollar@example.com', 'name' => '$dollar']);
|
||||
$account->save();
|
||||
|
||||
// Create url_alias (this will create 'url_alias').
|
||||
$this->container->get('path.alias_storage')->save('/user/' . $account->id(), '/user/example');
|
||||
|
||||
// Create a cache table (this will create 'cache_discovery').
|
||||
\Drupal::cache('discovery')->set('test', $this->data);
|
||||
|
||||
// These are all the tables that should now be in place.
|
||||
$this->tables = [
|
||||
'block_content',
|
||||
'block_content_field_data',
|
||||
'block_content_field_revision',
|
||||
'block_content_revision',
|
||||
'cachetags',
|
||||
'config',
|
||||
'cache_bootstrap',
|
||||
'cache_config',
|
||||
'cache_data',
|
||||
'cache_discovery',
|
||||
'cache_entity',
|
||||
'file_managed',
|
||||
'key_value_expire',
|
||||
'menu_link_content',
|
||||
'menu_link_content_data',
|
||||
'sequences',
|
||||
'sessions',
|
||||
'url_alias',
|
||||
'user__roles',
|
||||
'users',
|
||||
'users_field_data',
|
||||
'watchdog',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the command directly.
|
||||
*/
|
||||
public function testDbDumpCommand() {
|
||||
if ($this->skipTests) {
|
||||
$this->pass("Skipping test since the DbDumpCommand is currently only compatible with MySql");
|
||||
return;
|
||||
}
|
||||
|
||||
$application = new DbDumpApplication();
|
||||
$command = $application->find('dump-database-d8-mysql');
|
||||
$command_tester = new CommandTester($command);
|
||||
$command_tester->execute([]);
|
||||
|
||||
// Tables that are schema-only should not have data exported.
|
||||
$pattern = preg_quote("\$connection->insert('sessions')");
|
||||
$this->assertFalse(preg_match('/' . $pattern . '/', $command_tester->getDisplay()), 'Tables defined as schema-only do not have data exported to the script.');
|
||||
|
||||
// Table data is exported.
|
||||
$pattern = preg_quote("\$connection->insert('config')");
|
||||
$this->assertTrue(preg_match('/' . $pattern . '/', $command_tester->getDisplay()), 'Table data is properly exported to the script.');
|
||||
|
||||
// The test data are in the dump (serialized).
|
||||
$pattern = preg_quote(serialize($this->data));
|
||||
$this->assertTrue(preg_match('/' . $pattern . '/', $command_tester->getDisplay()), 'Generated data is found in the exported script.');
|
||||
|
||||
// Check that the user account name and email address was properly escaped.
|
||||
$pattern = preg_quote('"q\'uote\$dollar@example.com"');
|
||||
$this->assertTrue(preg_match('/' . $pattern . '/', $command_tester->getDisplay()), 'The user account email address was properly escaped in the exported script.');
|
||||
$pattern = preg_quote('\'$dollar\'');
|
||||
$this->assertTrue(preg_match('/' . $pattern . '/', $command_tester->getDisplay()), 'The user account name was properly escaped in the exported script.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test loading the script back into the database.
|
||||
*/
|
||||
public function testScriptLoad() {
|
||||
if ($this->skipTests) {
|
||||
$this->pass("Skipping test since the DbDumpCommand is currently only compatible with MySql");
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate the script.
|
||||
$application = new DbDumpApplication();
|
||||
$command = $application->find('dump-database-d8-mysql');
|
||||
$command_tester = new CommandTester($command);
|
||||
$command_tester->execute([]);
|
||||
$script = $command_tester->getDisplay();
|
||||
|
||||
// Store original schemas and drop tables to avoid errors.
|
||||
foreach ($this->tables as $table) {
|
||||
$this->originalTableSchemas[$table] = $this->getTableSchema($table);
|
||||
$this->originalTableIndexes[$table] = $this->getTableIndexes($table);
|
||||
Database::getConnection()->schema()->dropTable($table);
|
||||
}
|
||||
|
||||
// This will load the data.
|
||||
$file = sys_get_temp_dir() . '/' . $this->randomMachineName();
|
||||
file_put_contents($file, $script);
|
||||
require_once $file;
|
||||
|
||||
// The tables should now exist and the schemas should match the originals.
|
||||
foreach ($this->tables as $table) {
|
||||
$this->assertTrue(Database::getConnection()
|
||||
->schema()
|
||||
->tableExists($table), SafeMarkup::format('Table @table created by the database script.', ['@table' => $table]));
|
||||
$this->assertIdentical($this->originalTableSchemas[$table], $this->getTableSchema($table), SafeMarkup::format('The schema for @table was properly restored.', ['@table' => $table]));
|
||||
$this->assertIdentical($this->originalTableIndexes[$table], $this->getTableIndexes($table), SafeMarkup::format('The indexes for @table were properly restored.', ['@table' => $table]));
|
||||
}
|
||||
|
||||
// Ensure the test config has been replaced.
|
||||
$config = unserialize(db_query("SELECT data FROM {config} WHERE name = 'test_config'")->fetchField());
|
||||
$this->assertIdentical($config, $this->data, 'Script has properly restored the config table data.');
|
||||
|
||||
// Ensure the cache data was not exported.
|
||||
$this->assertFalse(\Drupal::cache('discovery')
|
||||
->get('test'), 'Cache data was not exported to the script.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get a simplified schema for a given table.
|
||||
*
|
||||
* @param string $table
|
||||
*
|
||||
* @return array
|
||||
* Array keyed by field name, with the values being the field type.
|
||||
*/
|
||||
protected function getTableSchema($table) {
|
||||
// Verify the field type on the data column in the cache table.
|
||||
// @todo this is MySQL specific.
|
||||
$query = db_query("SHOW COLUMNS FROM {" . $table . "}");
|
||||
$definition = [];
|
||||
while ($row = $query->fetchAssoc()) {
|
||||
$definition[$row['Field']] = $row['Type'];
|
||||
}
|
||||
return $definition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns indexes for a given table.
|
||||
*
|
||||
* @param string $table
|
||||
* The table to find indexes for.
|
||||
*
|
||||
* @return array
|
||||
* The 'primary key', 'unique keys', and 'indexes' portion of the Drupal
|
||||
* table schema.
|
||||
*/
|
||||
protected function getTableIndexes($table) {
|
||||
$query = db_query("SHOW INDEX FROM {" . $table . "}");
|
||||
$definition = [];
|
||||
while ($row = $query->fetchAssoc()) {
|
||||
$index_name = $row['Key_name'];
|
||||
$column = $row['Column_name'];
|
||||
// Key the arrays by the index sequence for proper ordering (start at 0).
|
||||
$order = $row['Seq_in_index'] - 1;
|
||||
|
||||
// If specified, add length to the index.
|
||||
if ($row['Sub_part']) {
|
||||
$column = [$column, $row['Sub_part']];
|
||||
}
|
||||
|
||||
if ($index_name === 'PRIMARY') {
|
||||
$definition['primary key'][$order] = $column;
|
||||
}
|
||||
elseif ($row['Non_unique'] == 0) {
|
||||
$definition['unique keys'][$index_name][$order] = $column;
|
||||
}
|
||||
else {
|
||||
$definition['indexes'][$index_name][$order] = $column;
|
||||
}
|
||||
}
|
||||
return $definition;
|
||||
}
|
||||
|
||||
}
|
69
core/tests/Drupal/KernelTests/Core/Common/SizeTest.php
Normal file
69
core/tests/Drupal/KernelTests/Core/Common/SizeTest.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Common;
|
||||
|
||||
use Drupal\Component\Utility\Bytes;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Parse a predefined amount of bytes and compare the output with the expected
|
||||
* value.
|
||||
*
|
||||
* @group Common
|
||||
*/
|
||||
class SizeTest extends KernelTestBase {
|
||||
protected $exactTestCases;
|
||||
protected $roundedTestCases;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$kb = Bytes::KILOBYTE;
|
||||
$this->exactTestCases = array(
|
||||
'1 byte' => 1,
|
||||
'1 KB' => $kb,
|
||||
'1 MB' => $kb * $kb,
|
||||
'1 GB' => $kb * $kb * $kb,
|
||||
'1 TB' => $kb * $kb * $kb * $kb,
|
||||
'1 PB' => $kb * $kb * $kb * $kb * $kb,
|
||||
'1 EB' => $kb * $kb * $kb * $kb * $kb * $kb,
|
||||
'1 ZB' => $kb * $kb * $kb * $kb * $kb * $kb * $kb,
|
||||
'1 YB' => $kb * $kb * $kb * $kb * $kb * $kb * $kb * $kb,
|
||||
);
|
||||
$this->roundedTestCases = array(
|
||||
'2 bytes' => 2,
|
||||
'1 MB' => ($kb * $kb) - 1, // rounded to 1 MB (not 1000 or 1024 kilobyte!)
|
||||
round(3623651 / ($this->exactTestCases['1 MB']), 2) . ' MB' => 3623651, // megabytes
|
||||
round(67234178751368124 / ($this->exactTestCases['1 PB']), 2) . ' PB' => 67234178751368124, // petabytes
|
||||
round(235346823821125814962843827 / ($this->exactTestCases['1 YB']), 2) . ' YB' => 235346823821125814962843827, // yottabytes
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that format_size() returns the expected string.
|
||||
*/
|
||||
function testCommonFormatSize() {
|
||||
foreach (array($this->exactTestCases, $this->roundedTestCases) as $test_cases) {
|
||||
foreach ($test_cases as $expected => $input) {
|
||||
$this->assertEqual(
|
||||
($result = format_size($input, NULL)),
|
||||
$expected,
|
||||
$expected . ' == ' . $result . ' (' . $input . ' bytes)'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cross-tests Bytes::toInt() and format_size().
|
||||
*/
|
||||
function testCommonParseSizeFormatSize() {
|
||||
foreach ($this->exactTestCases as $size) {
|
||||
$this->assertEqual(
|
||||
$size,
|
||||
($parsed_size = Bytes::toInt($string = format_size($size, NULL))),
|
||||
$size . ' == ' . $parsed_size . ' (' . $string . ')'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
57
core/tests/Drupal/KernelTests/Core/Common/XssUnitTest.php
Normal file
57
core/tests/Drupal/KernelTests/Core/Common/XssUnitTest.php
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Common;
|
||||
|
||||
use Drupal\Component\Utility\UrlHelper;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Confirm that \Drupal\Component\Utility\Xss::filter() and check_url() work
|
||||
* correctly, including invalid multi-byte sequences.
|
||||
*
|
||||
* @group Common
|
||||
*/
|
||||
class XssUnitTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('filter', 'system');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installConfig(array('system'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests t() functionality.
|
||||
*/
|
||||
function testT() {
|
||||
$text = t('Simple text');
|
||||
$this->assertEqual($text, 'Simple text', 't leaves simple text alone.');
|
||||
$text = t('Escaped text: @value', array('@value' => '<script>'));
|
||||
$this->assertEqual($text, 'Escaped text: <script>', 't replaces and escapes string.');
|
||||
$text = t('Placeholder text: %value', array('%value' => '<script>'));
|
||||
$this->assertEqual($text, 'Placeholder text: <em class="placeholder"><script></em>', 't replaces, escapes and themes string.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that harmful protocols are stripped.
|
||||
*/
|
||||
function testBadProtocolStripping() {
|
||||
// Ensure that check_url() strips out harmful protocols, and encodes for
|
||||
// HTML.
|
||||
// Ensure \Drupal\Component\Utility\UrlHelper::stripDangerousProtocols() can
|
||||
// be used to return a plain-text string stripped of harmful protocols.
|
||||
$url = 'javascript:http://www.example.com/?x=1&y=2';
|
||||
$expected_plain = 'http://www.example.com/?x=1&y=2';
|
||||
$expected_html = 'http://www.example.com/?x=1&y=2';
|
||||
$this->assertIdentical(check_url($url), $expected_html, 'check_url() filters a URL and encodes it for HTML.');
|
||||
$this->assertIdentical(UrlHelper::filterBadProtocol($url), $expected_html, '\Drupal\Component\Utility\UrlHelper::filterBadProtocol() filters a URL and encodes it for HTML.');
|
||||
$this->assertIdentical(UrlHelper::stripDangerousProtocols($url), $expected_plain, '\Drupal\Component\Utility\UrlHelper::stripDangerousProtocols() filters a URL and returns plain text.');
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -21,6 +21,7 @@ class CacheabilityMetadataConfigOverrideTest extends KernelTestBase {
|
|||
'config',
|
||||
'config_override_test',
|
||||
'system',
|
||||
'user'
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
@ -44,9 +44,9 @@ class ConfigDiffTest extends KernelTestBase {
|
|||
// Verify that the diff reflects a change.
|
||||
$diff = \Drupal::service('config.manager')->diff($active, $sync, $config_name);
|
||||
$edits = $diff->getEdits();
|
||||
$this->assertEqual($edits[0]->type, 'change', 'The first item in the diff is a change.');
|
||||
$this->assertEqual($edits[0]->orig[0], $change_key . ': ' . $original_data[$change_key], format_string("The active value for key '%change_key' is '%original_data'.", array('%change_key' => $change_key, '%original_data' => $original_data[$change_key])));
|
||||
$this->assertEqual($edits[0]->closing[0], $change_key . ': ' . $change_data, format_string("The sync value for key '%change_key' is '%change_data'.", array('%change_key' => $change_key, '%change_data' => $change_data)));
|
||||
$this->assertYamlEdit($edits, $change_key, 'change',
|
||||
[$change_key . ': ' . $original_data[$change_key]],
|
||||
[$change_key . ': ' . $change_data]);
|
||||
|
||||
// Reset data back to original, and remove a key
|
||||
$sync_data = $original_data;
|
||||
|
@ -56,10 +56,11 @@ class ConfigDiffTest extends KernelTestBase {
|
|||
// Verify that the diff reflects a removed key.
|
||||
$diff = \Drupal::service('config.manager')->diff($active, $sync, $config_name);
|
||||
$edits = $diff->getEdits();
|
||||
$this->assertEqual($edits[0]->type, 'copy', 'The first item in the diff is a copy.');
|
||||
$this->assertEqual($edits[1]->type, 'delete', 'The second item in the diff is a delete.');
|
||||
$this->assertEqual($edits[1]->orig[0], $remove_key . ': ' . $original_data[$remove_key], format_string("The active value for key '%remove_key' is '%original_data'.", array('%remove_key' => $remove_key, '%original_data' => $original_data[$remove_key])));
|
||||
$this->assertFalse($edits[1]->closing, format_string("The key '%remove_key' does not exist in sync.", array('%remove_key' => $remove_key)));
|
||||
$this->assertYamlEdit($edits, $change_key, 'copy');
|
||||
$this->assertYamlEdit($edits, $remove_key, 'delete',
|
||||
[$remove_key . ': ' . $original_data[$remove_key]],
|
||||
FALSE
|
||||
);
|
||||
|
||||
// Reset data back to original and add a key
|
||||
$sync_data = $original_data;
|
||||
|
@ -69,10 +70,8 @@ class ConfigDiffTest extends KernelTestBase {
|
|||
// Verify that the diff reflects an added key.
|
||||
$diff = \Drupal::service('config.manager')->diff($active, $sync, $config_name);
|
||||
$edits = $diff->getEdits();
|
||||
$this->assertEqual($edits[0]->type, 'copy', 'The first item in the diff is a copy.');
|
||||
$this->assertEqual($edits[1]->type, 'add', 'The second item in the diff is an add.');
|
||||
$this->assertFalse($edits[1]->orig, format_string("The key '%add_key' does not exist in active.", array('%add_key' => $add_key)));
|
||||
$this->assertEqual($edits[1]->closing[0], $add_key . ': ' . $add_data, format_string("The sync value for key '%add_key' is '%add_data'.", array('%add_key' => $add_key, '%add_data' => $add_data)));
|
||||
$this->assertYamlEdit($edits, $change_key, 'copy');
|
||||
$this->assertYamlEdit($edits, $add_key, 'add', FALSE, [$add_key . ': ' . $add_data]);
|
||||
|
||||
// Test diffing a renamed config entity.
|
||||
$test_entity_id = $this->randomMachineName();
|
||||
|
@ -97,10 +96,11 @@ class ConfigDiffTest extends KernelTestBase {
|
|||
|
||||
$diff = \Drupal::service('config.manager')->diff($active, $sync, 'config_test.dynamic.' . $new_test_entity_id, $config_name);
|
||||
$edits = $diff->getEdits();
|
||||
$this->assertEqual($edits[0]->type, 'copy', 'The first item in the diff is a copy.');
|
||||
$this->assertEqual($edits[1]->type, 'change', 'The second item in the diff is a change.');
|
||||
$this->assertEqual($edits[1]->orig, array('id: ' . $new_test_entity_id));
|
||||
$this->assertEqual($edits[1]->closing, array('id: ' . $test_entity_id));
|
||||
$this->assertYamlEdit($edits, 'uuid', 'copy');
|
||||
$this->assertYamlEdit($edits, 'id', 'change',
|
||||
['id: ' . $new_test_entity_id],
|
||||
['id: ' . $test_entity_id]);
|
||||
$this->assertYamlEdit($edits, 'label', 'copy');
|
||||
$this->assertEqual($edits[2]->type, 'copy', 'The third item in the diff is a copy.');
|
||||
$this->assertEqual(count($edits), 3, 'There are three items in the diff.');
|
||||
}
|
||||
|
@ -133,10 +133,56 @@ class ConfigDiffTest extends KernelTestBase {
|
|||
// Test that the differences are detected when diffing the collection.
|
||||
$diff = \Drupal::service('config.manager')->diff($active, $sync, $config_name, NULL, 'test');
|
||||
$edits = $diff->getEdits();
|
||||
$this->assertEqual($edits[0]->type, 'change', 'The second item in the diff is a copy.');
|
||||
$this->assertEqual($edits[0]->orig, array('foo: bar'));
|
||||
$this->assertEqual($edits[0]->closing, array('foo: baz'));
|
||||
$this->assertEqual($edits[1]->type, 'copy', 'The second item in the diff is a copy.');
|
||||
$this->assertYamlEdit($edits, 'foo', 'change', ['foo: bar'], ['foo: baz']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to test that an edit is found in a diff'd YAML file.
|
||||
*
|
||||
* @param array $edits
|
||||
* A list of edits.
|
||||
* @param string $field
|
||||
* The field key that is being asserted.
|
||||
* @param string $type
|
||||
* The type of edit that is being asserted.
|
||||
* @param mixed $orig
|
||||
* (optional) The original value of of the edit. If not supplied, assertion
|
||||
* is skipped.
|
||||
* @param mixed $closing
|
||||
* (optional) The closing value of of the edit. If not supplied, assertion
|
||||
* is skipped.
|
||||
*/
|
||||
protected function assertYamlEdit(array $edits, $field, $type, $orig = NULL, $closing = NULL) {
|
||||
$match = FALSE;
|
||||
foreach ($edits as $edit) {
|
||||
// Choose which section to search for the field.
|
||||
$haystack = $type == 'add' ? $edit->closing : $edit->orig;
|
||||
// Look through each line and try and find the key.
|
||||
if (is_array($haystack)) {
|
||||
foreach ($haystack as $item) {
|
||||
if (strpos($item, $field . ':') === 0) {
|
||||
$match = TRUE;
|
||||
// Assert that the edit is of the type specified.
|
||||
$this->assertEqual($edit->type, $type, "The $field item in the diff is a $type");
|
||||
// If an original value was given, assert that it matches.
|
||||
if (isset($orig)) {
|
||||
$this->assertIdentical($edit->orig, $orig, "The original value for key '$field' is correct.");
|
||||
}
|
||||
// If a closing value was given, assert that it matches.
|
||||
if (isset($closing)) {
|
||||
$this->assertIdentical($edit->closing, $closing, "The closing value for key '$field' is correct.");
|
||||
}
|
||||
// Break out of the search entirely.
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't match anything, fail.
|
||||
if (!$match) {
|
||||
$this->fail("$field edit was not matched");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -50,8 +50,10 @@ class ConfigEntityStaticCacheTest extends KernelTestBase {
|
|||
* Tests that the static cache is working.
|
||||
*/
|
||||
public function testCacheHit() {
|
||||
$entity_1 = entity_load($this->entityTypeId, $this->entityId);
|
||||
$entity_2 = entity_load($this->entityTypeId, $this->entityId);
|
||||
$storage = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityTypeId);
|
||||
$entity_1 = $storage->load($this->entityId);
|
||||
$entity_2 = $storage->load($this->entityId);
|
||||
// config_entity_static_cache_test_config_test_load() sets _loadStamp to a
|
||||
// random string. If they match, it means $entity_2 was retrieved from the
|
||||
// static cache rather than going through a separate load sequence.
|
||||
|
@ -62,19 +64,21 @@ class ConfigEntityStaticCacheTest extends KernelTestBase {
|
|||
* Tests that the static cache is reset on entity save and delete.
|
||||
*/
|
||||
public function testReset() {
|
||||
$entity = entity_load($this->entityTypeId, $this->entityId);
|
||||
$storage = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityTypeId);
|
||||
$entity = $storage->load($this->entityId);
|
||||
|
||||
// Ensure loading after a save retrieves the updated entity rather than an
|
||||
// obsolete cached one.
|
||||
$entity->label = 'New label';
|
||||
$entity->save();
|
||||
$entity = entity_load($this->entityTypeId, $this->entityId);
|
||||
$entity = $storage->load($this->entityId);
|
||||
$this->assertIdentical($entity->label, 'New label');
|
||||
|
||||
// Ensure loading after a delete retrieves NULL rather than an obsolete
|
||||
// cached one.
|
||||
$entity->delete();
|
||||
$this->assertNull(entity_load($this->entityTypeId, $this->entityId));
|
||||
$this->assertNull($storage->load($this->entityId));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -27,11 +27,10 @@ class ConfigEntityStorageTest extends KernelTestBase {
|
|||
$entity_type = 'config_test';
|
||||
$id = 'test_1';
|
||||
// Load the original configuration entity.
|
||||
$this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type)
|
||||
->create(array('id' => $id))
|
||||
->save();
|
||||
$entity = entity_load($entity_type, $id);
|
||||
$storage = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type);
|
||||
$storage->create(['id' => $id])->save();
|
||||
$entity = $storage->load($id);
|
||||
|
||||
$original_properties = $entity->toArray();
|
||||
|
||||
|
|
|
@ -199,8 +199,8 @@ class ConfigInstallTest extends KernelTestBase {
|
|||
}
|
||||
catch (UnmetDependenciesException $e) {
|
||||
$this->assertEqual($e->getExtension(), 'config_install_dependency_test');
|
||||
$this->assertEqual($e->getConfigObjects(), ['config_test.dynamic.other_module_test_with_dependency']);
|
||||
$this->assertEqual($e->getMessage(), 'Configuration objects (config_test.dynamic.other_module_test_with_dependency) provided by config_install_dependency_test have unmet dependencies');
|
||||
$this->assertEqual($e->getConfigObjects(), ['config_other_module_config_test.weird_simple_config', 'config_test.dynamic.other_module_test_with_dependency']);
|
||||
$this->assertEqual($e->getMessage(), 'Configuration objects (config_other_module_config_test.weird_simple_config, config_test.dynamic.other_module_test_with_dependency) provided by config_install_dependency_test have unmet dependencies');
|
||||
}
|
||||
$this->installModules(['config_other_module_config_test']);
|
||||
$this->installModules(['config_install_dependency_test']);
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
namespace Drupal\KernelTests\Core\Config;
|
||||
|
||||
use Drupal\Core\Config\Schema\SchemaIncompleteException;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\Tests\Traits\Core\Config\SchemaConfigListenerTestTrait;
|
||||
|
||||
/**
|
||||
* Tests the functionality of ConfigSchemaChecker in KernelTestBase tests.
|
||||
|
@ -12,50 +12,22 @@ use Drupal\KernelTests\KernelTestBase;
|
|||
*/
|
||||
class SchemaConfigListenerTest extends KernelTestBase {
|
||||
|
||||
use SchemaConfigListenerTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('config_test');
|
||||
|
||||
/**
|
||||
* Tests \Drupal\Core\Config\Testing\ConfigSchemaChecker.
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testConfigSchemaChecker() {
|
||||
// Test a non-existing schema.
|
||||
$message = 'Expected SchemaIncompleteException thrown';
|
||||
try {
|
||||
$this->config('config_schema_test.schemaless')->set('foo', 'bar')->save();
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (SchemaIncompleteException $e) {
|
||||
$this->pass($message);
|
||||
$this->assertEqual('No schema for config_schema_test.schemaless', $e->getMessage());
|
||||
}
|
||||
|
||||
// Test a valid schema.
|
||||
$message = 'Unexpected SchemaIncompleteException thrown';
|
||||
$config = $this->config('config_test.types')->set('int', 10);
|
||||
try {
|
||||
$config->save();
|
||||
$this->pass($message);
|
||||
}
|
||||
catch (SchemaIncompleteException $e) {
|
||||
$this->fail($message);
|
||||
}
|
||||
|
||||
// Test an invalid schema.
|
||||
$message = 'Expected SchemaIncompleteException thrown';
|
||||
$config = $this->config('config_test.types')
|
||||
->set('foo', 'bar')
|
||||
->set('array', 1);
|
||||
try {
|
||||
$config->save();
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (SchemaIncompleteException $e) {
|
||||
$this->pass($message);
|
||||
$this->assertEqual('Schema errors for config_test.types with the following errors: config_test.types:foo missing schema, config_test.types:array variable type is integer but applied schema class is Drupal\Core\Config\Schema\Sequence', $e->getMessage());
|
||||
}
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Install configuration provided by the module so that the order of the
|
||||
// config keys is the same as
|
||||
// \Drupal\FunctionalTests\Core\Config\SchemaConfigListenerTest.
|
||||
$this->installConfig(['config_test']);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
namespace Drupal\KernelTests\Core\Config\Storage;
|
||||
|
||||
use Drupal\Component\Serialization\Yaml;
|
||||
use Drupal\Core\Config\FileStorage;
|
||||
use Drupal\Core\Config\UnsupportedDataTypeConfigException;
|
||||
use Drupal\Core\Serialization\Yaml;
|
||||
use Drupal\Core\StreamWrapper\PublicStream;
|
||||
|
||||
/**
|
||||
|
|
|
@ -36,20 +36,20 @@ abstract class DatabaseTestBase extends KernelTestBase {
|
|||
*/
|
||||
function ensureSampleDataNull() {
|
||||
db_insert('test_null')
|
||||
->fields(array('name', 'age'))
|
||||
->values(array(
|
||||
->fields(array('name', 'age'))
|
||||
->values(array(
|
||||
'name' => 'Kermit',
|
||||
'age' => 25,
|
||||
))
|
||||
->values(array(
|
||||
->values(array(
|
||||
'name' => 'Fozzie',
|
||||
'age' => NULL,
|
||||
))
|
||||
->values(array(
|
||||
->values(array(
|
||||
'name' => 'Gonzo',
|
||||
'age' => 27,
|
||||
))
|
||||
->execute();
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -126,4 +126,13 @@ class LoggingTest extends DatabaseTestBase {
|
|||
$this->assertEqual(count($queries2), 1, 'Correct number of queries recorded for second connection.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that getLog with a wrong key return an empty array.
|
||||
*/
|
||||
function testGetLoggingWrongKey() {
|
||||
$result = Database::getLog('wrong');
|
||||
|
||||
$this->assertEqual($result, [], 'The function getLog with a wrong key returns an empty array.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -505,6 +505,7 @@ class SchemaTest extends KernelTestBase {
|
|||
array('not null' => FALSE, 'default' => 7),
|
||||
array('not null' => TRUE, 'initial' => 1),
|
||||
array('not null' => TRUE, 'initial' => 1, 'default' => 7),
|
||||
array('not null' => TRUE, 'initial_from_field' => 'serial_column'),
|
||||
);
|
||||
|
||||
foreach ($variations as $variation) {
|
||||
|
@ -532,6 +533,7 @@ class SchemaTest extends KernelTestBase {
|
|||
array('not null' => FALSE, 'default' => 7),
|
||||
array('not null' => TRUE, 'initial' => 1),
|
||||
array('not null' => TRUE, 'initial' => 1, 'default' => 7),
|
||||
array('not null' => TRUE, 'initial_from_field' => 'serial_column'),
|
||||
);
|
||||
|
||||
foreach ($variations as $variation) {
|
||||
|
@ -620,6 +622,19 @@ class SchemaTest extends KernelTestBase {
|
|||
$this->assertEqual($count, 0, 'Initial values filled out.');
|
||||
}
|
||||
|
||||
// Check that the initial value from another field has been registered.
|
||||
if (isset($field_spec['initial_from_field'])) {
|
||||
// There should be no row with a value different than
|
||||
// $field_spec['initial_from_field'].
|
||||
$count = db_select($table_name)
|
||||
->fields($table_name, array('serial_column'))
|
||||
->where($table_name . '.' . $field_spec['initial_from_field'] . ' <> ' . $table_name . '.' . $field_name)
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
$this->assertEqual($count, 0, 'Initial values from another field filled out.');
|
||||
}
|
||||
|
||||
// Check that the default value has been registered.
|
||||
if (isset($field_spec['default'])) {
|
||||
// Try inserting a row, and check the resulting value of the new column.
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\DrupalKernel;
|
||||
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests site-specific service overrides.
|
||||
*
|
||||
* @group DrupalKernel
|
||||
*/
|
||||
class DrupalKernelSiteTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Tests services.yml in site directory.
|
||||
*/
|
||||
public function testServicesYml() {
|
||||
$container_yamls = Settings::get('container_yamls');
|
||||
$container_yamls[] = $this->siteDirectory . '/services.yml';
|
||||
$this->setSetting('container_yamls', $container_yamls);
|
||||
$this->assertFalse($this->container->has('site.service.yml'));
|
||||
// A service provider class always has precedence over services.yml files.
|
||||
// KernelTestBase::buildContainer() swaps out many services with in-memory
|
||||
// implementations already, so those cannot be tested.
|
||||
$this->assertIdentical(get_class($this->container->get('cache.backend.database')), 'Drupal\Core\Cache\DatabaseBackendFactory');
|
||||
|
||||
$class = __CLASS__;
|
||||
$doc = <<<EOD
|
||||
services:
|
||||
# Add a new service.
|
||||
site.service.yml:
|
||||
class: $class
|
||||
# Swap out a core service.
|
||||
cache.backend.database:
|
||||
class: Drupal\Core\Cache\MemoryBackendFactory
|
||||
EOD;
|
||||
file_put_contents($this->siteDirectory . '/services.yml', $doc);
|
||||
|
||||
// Rebuild the container.
|
||||
$this->container->get('kernel')->rebuildContainer();
|
||||
|
||||
$this->assertTrue($this->container->has('site.service.yml'));
|
||||
$this->assertIdentical(get_class($this->container->get('cache.backend.database')), 'Drupal\Core\Cache\MemoryBackendFactory');
|
||||
}
|
||||
|
||||
}
|
|
@ -133,6 +133,11 @@ class DrupalKernelTest extends KernelTestBase {
|
|||
'pathname' => drupal_get_filename('module', 'service_provider_test'),
|
||||
'filename' => NULL,
|
||||
));
|
||||
|
||||
// Check that the container itself is not among the persist IDs because it
|
||||
// does not make sense to persist the container itself.
|
||||
$persist_ids = $container->getParameter('persist_ids');
|
||||
$this->assertIdentical(FALSE, array_search('service_container', $persist_ids));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\DrupalKernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Tests that services are correctly destructed.
|
||||
*
|
||||
* @group DrupalKernel
|
||||
*/
|
||||
class ServiceDestructionTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Verifies that services are destructed when used.
|
||||
*/
|
||||
public function testDestructionUsed() {
|
||||
// Enable the test module to add it to the container.
|
||||
$this->enableModules(array('service_provider_test'));
|
||||
|
||||
$request = $this->container->get('request_stack')->getCurrentRequest();
|
||||
$kernel = $this->container->get('kernel');
|
||||
$kernel->preHandle($request);
|
||||
|
||||
// The service has not been destructed yet.
|
||||
$this->assertNull(\Drupal::state()->get('service_provider_test.destructed'));
|
||||
|
||||
// Call the class and then terminate the kernel
|
||||
$this->container->get('service_provider_test_class');
|
||||
|
||||
$response = new Response();
|
||||
$kernel->terminate($request, $response);
|
||||
$this->assertTrue(\Drupal::state()->get('service_provider_test.destructed'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that services are not unnecessarily destructed when not used.
|
||||
*/
|
||||
public function testDestructionUnused() {
|
||||
// Enable the test module to add it to the container.
|
||||
$this->enableModules(array('service_provider_test'));
|
||||
|
||||
$request = $this->container->get('request_stack')->getCurrentRequest();
|
||||
$kernel = $this->container->get('kernel');
|
||||
$kernel->preHandle($request);
|
||||
|
||||
// The service has not been destructed yet.
|
||||
$this->assertNull(\Drupal::state()->get('service_provider_test.destructed'));
|
||||
|
||||
// Terminate the kernel. The test class has not been called, so it should not
|
||||
// be destructed.
|
||||
$response = new Response();
|
||||
$kernel->terminate($request, $response);
|
||||
$this->assertNull(\Drupal::state()->get('service_provider_test.destructed'));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Element;
|
||||
|
||||
use Drupal\Core\Form\FormInterface;
|
||||
use Drupal\Core\Form\FormState;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element\PathElement;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\user\Entity\Role;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Tests PathElement validation and conversion functionality.
|
||||
*
|
||||
* @group Form
|
||||
*/
|
||||
class PathElementFormTest extends KernelTestBase implements FormInterface {
|
||||
|
||||
/**
|
||||
* User for testing.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $testUser;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system', 'user');
|
||||
|
||||
/**
|
||||
* Sets up the test.
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installSchema('system', ['sequences', 'key_value_expire']);
|
||||
$this->installEntitySchema('user');
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
/** @var \Drupal\user\RoleInterface $role */
|
||||
$role = Role::create(array(
|
||||
'id' => 'admin',
|
||||
'label' => 'admin',
|
||||
));
|
||||
$role->grantPermission('link to any page');
|
||||
$role->save();
|
||||
$this->testUser = User::create(array(
|
||||
'name' => 'foobar',
|
||||
'mail' => 'foobar@example.com',
|
||||
));
|
||||
$this->testUser->addRole($role->id());
|
||||
$this->testUser->save();
|
||||
\Drupal::service('current_user')->setAccount($this->testUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'test_path_element';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
// A required validated path.
|
||||
$form['required_validate'] = array(
|
||||
'#type' => 'path',
|
||||
'#required' => TRUE,
|
||||
'#title' => 'required_validate',
|
||||
'#convert_path' => PathElement::CONVERT_NONE,
|
||||
);
|
||||
|
||||
// A non validated required path.
|
||||
$form['required_non_validate'] = array(
|
||||
'#type' => 'path',
|
||||
'#required' => TRUE,
|
||||
'#title' => 'required_non_validate',
|
||||
'#convert_path' => PathElement::CONVERT_NONE,
|
||||
'#validate_path' => FALSE,
|
||||
);
|
||||
|
||||
// A non required validated path.
|
||||
$form['optional_validate'] = array(
|
||||
'#type' => 'path',
|
||||
'#required' => FALSE,
|
||||
'#title' => 'optional_validate',
|
||||
'#convert_path' => PathElement::CONVERT_NONE,
|
||||
);
|
||||
|
||||
// A non required converted path.
|
||||
$form['optional_validate'] = array(
|
||||
'#type' => 'path',
|
||||
'#required' => FALSE,
|
||||
'#title' => 'optional_validate',
|
||||
'#convert_path' => PathElement::CONVERT_ROUTE,
|
||||
);
|
||||
|
||||
// A converted required validated path.
|
||||
$form['required_validate_route'] = array(
|
||||
'#type' => 'path',
|
||||
'#required' => TRUE,
|
||||
'#title' => 'required_validate_route',
|
||||
);
|
||||
|
||||
// A converted required validated path.
|
||||
$form['required_validate_url'] = array(
|
||||
'#type' => 'path',
|
||||
'#required' => TRUE,
|
||||
'#title' => 'required_validate_url',
|
||||
'#convert_path' => PathElement::CONVERT_URL,
|
||||
);
|
||||
|
||||
$form['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Submit'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {}
|
||||
|
||||
/**
|
||||
* Form validation handler.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {}
|
||||
|
||||
/**
|
||||
* Tests that default handlers are added even if custom are specified.
|
||||
*/
|
||||
public function testPathElement() {
|
||||
$form_state = (new FormState())
|
||||
->setValues([
|
||||
'required_validate' => 'user/' . $this->testUser->id(),
|
||||
'required_non_validate' => 'magic-ponies',
|
||||
'required_validate_route' => 'user/' . $this->testUser->id(),
|
||||
'required_validate_url' => 'user/' . $this->testUser->id(),
|
||||
]);
|
||||
$form_builder = $this->container->get('form_builder');
|
||||
$form_builder->submitForm($this, $form_state);
|
||||
|
||||
// Valid form state.
|
||||
$this->assertEqual(count($form_state->getErrors()), 0);
|
||||
$this->assertEqual($form_state->getValue('required_validate_route'), array(
|
||||
'route_name' => 'entity.user.canonical',
|
||||
'route_parameters' => array(
|
||||
'user' => $this->testUser->id(),
|
||||
),
|
||||
));
|
||||
/** @var \Drupal\Core\Url $url */
|
||||
$url = $form_state->getValue('required_validate_url');
|
||||
$this->assertTrue($url instanceof Url);
|
||||
$this->assertEqual($url->getRouteName(), 'entity.user.canonical');
|
||||
$this->assertEqual($url->getRouteParameters(), array(
|
||||
'user' => $this->testUser->id(),
|
||||
));
|
||||
|
||||
// Test #required.
|
||||
$form_state = (new FormState())
|
||||
->setValues([
|
||||
'required_non_validate' => 'magic-ponies',
|
||||
'required_validate_route' => 'user/' . $this->testUser->id(),
|
||||
'required_validate_url' => 'user/' . $this->testUser->id(),
|
||||
]);
|
||||
$form_builder->submitForm($this, $form_state);
|
||||
$errors = $form_state->getErrors();
|
||||
// Should be missing 'required_validate' field.
|
||||
$this->assertEqual(count($errors), 1);
|
||||
$this->assertEqual($errors, array('required_validate' => t('@name field is required.', array('@name' => 'required_validate'))));
|
||||
|
||||
// Test invalid parameters.
|
||||
$form_state = (new FormState())
|
||||
->setValues([
|
||||
'required_validate' => 'user/74',
|
||||
'required_non_validate' => 'magic-ponies',
|
||||
'required_validate_route' => 'user/74',
|
||||
'required_validate_url' => 'user/74',
|
||||
]);
|
||||
$form_builder = $this->container->get('form_builder');
|
||||
$form_builder->submitForm($this, $form_state);
|
||||
|
||||
// Valid form state.
|
||||
$errors = $form_state->getErrors();
|
||||
$this->assertEqual(count($errors), 3);
|
||||
$this->assertEqual($errors, array(
|
||||
'required_validate' => t('This path does not exist or you do not have permission to link to %path.', array('%path' => 'user/74')),
|
||||
'required_validate_route' => t('This path does not exist or you do not have permission to link to %path.', array('%path' => 'user/74')),
|
||||
'required_validate_url' => t('This path does not exist or you do not have permission to link to %path.', array('%path' => 'user/74')),
|
||||
));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
|
||||
/**
|
||||
* Tests correct field method invocation order.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class ContentEntityFieldMethodInvocationOrderTest extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['language', 'system', 'entity_test'];
|
||||
|
||||
/**
|
||||
* The EntityTest entity type storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\ContentEntityStorageInterface
|
||||
*/
|
||||
protected $entityTestFieldMethodsStorage;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Enable an additional language.
|
||||
ConfigurableLanguage::createFromLangcode('de')->save();
|
||||
ConfigurableLanguage::createFromLangcode('fr')->save();
|
||||
|
||||
$this->installEntitySchema('entity_test_field_methods');
|
||||
|
||||
$this->entityTestFieldMethodsStorage = $this->entityManager->getStorage('entity_test_field_methods');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests correct field method invocation order.
|
||||
*/
|
||||
public function testFieldMethodInvocationOrder() {
|
||||
|
||||
// Create a test entity.
|
||||
$entity = $this->entityTestFieldMethodsStorage->create([
|
||||
'name' => $this->randomString(),
|
||||
'langcode' => 'de',
|
||||
]);
|
||||
$entity->save();
|
||||
|
||||
$entity->addTranslation('fr')
|
||||
->save();
|
||||
|
||||
// Reset the current value of the test field.
|
||||
foreach (['de', 'fr'] as $langcode) {
|
||||
$entity->getTranslation($langcode)->test_invocation_order->value = 0;
|
||||
}
|
||||
$entity->getTranslation('de')
|
||||
->save();
|
||||
$this->assertTrue($entity->getTranslation('fr')->test_invocation_order->value > $entity->getTranslation('de')->test_invocation_order->value, 'The field presave method has been invoked in the correct entity translation order.');
|
||||
|
||||
// Reset the current value of the test field.
|
||||
foreach (['de', 'fr'] as $langcode) {
|
||||
$entity->getTranslation($langcode)->test_invocation_order->value = 0;
|
||||
}
|
||||
$entity->getTranslation('fr')
|
||||
->save();
|
||||
$this->assertTrue($entity->getTranslation('de')->test_invocation_order->value > $entity->getTranslation('fr')->test_invocation_order->value, 'The field presave method has been invoked in the correct entity translation order.');
|
||||
}
|
||||
|
||||
}
|
|
@ -60,32 +60,36 @@ class EntityApiTest extends EntityKernelTestBase {
|
|||
->create(array('name' => 'test', 'user_id' => NULL));
|
||||
$entity->save();
|
||||
|
||||
$entities = array_values(entity_load_multiple_by_properties($entity_type, array('name' => 'test')));
|
||||
/** @var \Drupal\Core\Entity\EntityStorageInterface $storage */
|
||||
$storage = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type);
|
||||
|
||||
$entities = array_values($storage->loadByProperties(['name' => 'test']));
|
||||
$this->assertEqual($entities[0]->name->value, 'test', format_string('%entity_type: Created and loaded entity', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($entities[1]->name->value, 'test', format_string('%entity_type: Created and loaded entity', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Test loading a single entity.
|
||||
$loaded_entity = entity_load($entity_type, $entity->id());
|
||||
$loaded_entity = $storage->load($entity->id());
|
||||
$this->assertEqual($loaded_entity->id(), $entity->id(), format_string('%entity_type: Loaded a single entity by id.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Test deleting an entity.
|
||||
$entities = array_values(entity_load_multiple_by_properties($entity_type, array('name' => 'test2')));
|
||||
$entities = array_values($storage->loadByProperties(['name' => 'test2']));
|
||||
$entities[0]->delete();
|
||||
$entities = array_values(entity_load_multiple_by_properties($entity_type, array('name' => 'test2')));
|
||||
$entities = array_values($storage->loadByProperties(['name' => 'test2']));
|
||||
$this->assertEqual($entities, array(), format_string('%entity_type: Entity deleted.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Test updating an entity.
|
||||
$entities = array_values(entity_load_multiple_by_properties($entity_type, array('name' => 'test')));
|
||||
$entities = array_values($storage->loadByProperties(['name' => 'test']));
|
||||
$entities[0]->name->value = 'test3';
|
||||
$entities[0]->save();
|
||||
$entity = entity_load($entity_type, $entities[0]->id());
|
||||
$entity = $storage->load($entities[0]->id());
|
||||
$this->assertEqual($entity->name->value, 'test3', format_string('%entity_type: Entity updated.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Try deleting multiple test entities by deleting all.
|
||||
$ids = array_keys(entity_load_multiple($entity_type));
|
||||
$ids = array_keys($storage->loadMultiple());
|
||||
entity_delete_multiple($entity_type, $ids);
|
||||
|
||||
$all = entity_load_multiple($entity_type);
|
||||
$all = $storage->loadMultiple();
|
||||
$this->assertTrue(empty($all), format_string('%entity_type: Deleted all entities.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Verify that all data got deleted.
|
||||
|
@ -110,7 +114,7 @@ class EntityApiTest extends EntityKernelTestBase {
|
|||
$controller->delete($entities);
|
||||
|
||||
// Verify that entities got deleted.
|
||||
$all = entity_load_multiple($entity_type);
|
||||
$all = $storage->loadMultiple();
|
||||
$this->assertTrue(empty($all), format_string('%entity_type: Deleted all entities.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Verify that all data got deleted from the tables.
|
||||
|
|
|
@ -377,7 +377,9 @@ class EntityFieldTest extends EntityKernelTestBase {
|
|||
$entity->save();
|
||||
$this->assertTrue((bool) $entity->id(), format_string('%entity_type: Entity has received an id.', array('%entity_type' => $entity_type)));
|
||||
|
||||
$entity = entity_load($entity_type, $entity->id());
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type)
|
||||
->load($entity->id());
|
||||
$this->assertTrue((bool) $entity->id(), format_string('%entity_type: Entity loaded.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Access the name field.
|
||||
|
@ -744,7 +746,9 @@ class EntityFieldTest extends EntityKernelTestBase {
|
|||
|
||||
// Save and load entity and make sure it still works.
|
||||
$entity->save();
|
||||
$entity = entity_load($entity_type, $entity->id());
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type)
|
||||
->load($entity->id());
|
||||
$this->assertEqual($entity->field_test_text->processed, $target, format_string('%entity_type: Text is processed with the default filter.', array('%entity_type' => $entity_type)));
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
use Drupal\entity_test\Entity\EntityTestMulRev;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
|
||||
/**
|
||||
* Tests translating a non-revisionable field.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class EntityNonRevisionableTranslatableFieldTest extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('entity_test', 'language', 'content_translation');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('entity_test_mulrev');
|
||||
$this->installEntitySchema('configurable_language');
|
||||
|
||||
ConfigurableLanguage::createFromLangcode('es')->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests translating a non-revisionable field.
|
||||
*/
|
||||
function testTranslatingNonRevisionableField() {
|
||||
/** @var \Drupal\Core\Entity\ContentEntityBase $entity */
|
||||
$entity = EntityTestMulRev::create();
|
||||
$entity->set('non_rev_field', 'Hello');
|
||||
$entity->save();
|
||||
|
||||
$translation = $entity->addTranslation('es');
|
||||
$translation->set('non_rev_field', 'Hola');
|
||||
$translation->save();
|
||||
|
||||
$reloaded = EntityTestMulRev::load($entity->id());
|
||||
$this->assertEquals('Hello', $reloaded->getTranslation('en')->get('non_rev_field')->value);
|
||||
|
||||
$this->assertEquals('Hola', $reloaded->getTranslation('es')->get('non_rev_field')->value);
|
||||
}
|
||||
|
||||
}
|
|
@ -247,7 +247,7 @@ class EntityQueryTest extends EntityKernelTestBase {
|
|||
->condition("$greetings.value", 'merhaba')
|
||||
->sort('id')
|
||||
->execute();
|
||||
$entities = entity_load_multiple('entity_test_mulrev', $ids);
|
||||
$entities = EntityTestMulRev::loadMultiple($ids);
|
||||
$first_entity = reset($entities);
|
||||
$old_name = $first_entity->name->value;
|
||||
foreach ($entities as $entity) {
|
||||
|
@ -481,7 +481,7 @@ class EntityQueryTest extends EntityKernelTestBase {
|
|||
->exists("$field_name.color")
|
||||
->count()
|
||||
->execute();
|
||||
$this->assertFalse($count);
|
||||
$this->assertFalse($count);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -164,11 +164,11 @@ class EntityTranslationTest extends EntityLanguageTestBase {
|
|||
|
||||
// Create a language neutral entity and check that properties are stored
|
||||
// as language neutral.
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type)
|
||||
->create(array('name' => $name, 'user_id' => $uid, $langcode_key => LanguageInterface::LANGCODE_NOT_SPECIFIED));
|
||||
$storage = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type);
|
||||
$entity = $storage->create(['name' => $name, 'user_id' => $uid, $langcode_key => LanguageInterface::LANGCODE_NOT_SPECIFIED]);
|
||||
$entity->save();
|
||||
$entity = entity_load($entity_type, $entity->id());
|
||||
$entity = $storage->load($entity->id());
|
||||
$default_langcode = $entity->language()->getId();
|
||||
$this->assertEqual($default_langcode, LanguageInterface::LANGCODE_NOT_SPECIFIED, format_string('%entity_type: Entity created as language neutral.', array('%entity_type' => $entity_type)));
|
||||
$field = $entity->getTranslation(LanguageInterface::LANGCODE_DEFAULT)->get('name');
|
||||
|
@ -192,7 +192,7 @@ class EntityTranslationTest extends EntityLanguageTestBase {
|
|||
->getStorage($entity_type)
|
||||
->create(array('name' => $name, 'user_id' => $uid, $langcode_key => $langcode));
|
||||
$entity->save();
|
||||
$entity = entity_load($entity_type, $entity->id());
|
||||
$entity = $storage->load($entity->id());
|
||||
$default_langcode = $entity->language()->getId();
|
||||
$this->assertEqual($default_langcode, $langcode, format_string('%entity_type: Entity created as language specific.', array('%entity_type' => $entity_type)));
|
||||
$field = $entity->getTranslation($langcode)->get('name');
|
||||
|
@ -224,7 +224,7 @@ class EntityTranslationTest extends EntityLanguageTestBase {
|
|||
$entity->save();
|
||||
|
||||
// Check that property translation were correctly stored.
|
||||
$entity = entity_load($entity_type, $entity->id());
|
||||
$entity = $storage->load($entity->id());
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$args = array(
|
||||
'%entity_type' => $entity_type,
|
||||
|
@ -242,33 +242,34 @@ class EntityTranslationTest extends EntityLanguageTestBase {
|
|||
// Create an additional entity with only the uid set. The uid for the
|
||||
// original language is the same of one used for a translation.
|
||||
$langcode = $this->langcodes[1];
|
||||
$this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type)
|
||||
->create(array(
|
||||
/** @var \Drupal\Core\Entity\EntityStorageInterface $storage */
|
||||
$storage = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type);
|
||||
$storage->create([
|
||||
'user_id' => $properties[$langcode]['user_id'],
|
||||
'name' => 'some name',
|
||||
$langcode_key => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
))
|
||||
])
|
||||
->save();
|
||||
|
||||
$entities = entity_load_multiple($entity_type);
|
||||
$entities = $storage->loadMultiple();
|
||||
$this->assertEqual(count($entities), 3, format_string('%entity_type: Three entities were created.', array('%entity_type' => $entity_type)));
|
||||
$entities = entity_load_multiple($entity_type, array($translated_id));
|
||||
$entities = $storage->loadMultiple([$translated_id]);
|
||||
$this->assertEqual(count($entities), 1, format_string('%entity_type: One entity correctly loaded by id.', array('%entity_type' => $entity_type)));
|
||||
$entities = entity_load_multiple_by_properties($entity_type, array('name' => $name));
|
||||
$entities = $storage->loadByProperties(['name' => $name]);
|
||||
$this->assertEqual(count($entities), 2, format_string('%entity_type: Two entities correctly loaded by name.', array('%entity_type' => $entity_type)));
|
||||
// @todo The default language condition should go away in favor of an
|
||||
// explicit parameter.
|
||||
$entities = entity_load_multiple_by_properties($entity_type, array('name' => $properties[$langcode]['name'][0], $default_langcode_key => 0));
|
||||
$entities = $storage->loadByProperties(['name' => $properties[$langcode]['name'][0], $default_langcode_key => 0]);
|
||||
$this->assertEqual(count($entities), 1, format_string('%entity_type: One entity correctly loaded by name translation.', array('%entity_type' => $entity_type)));
|
||||
$entities = entity_load_multiple_by_properties($entity_type, array($langcode_key => $default_langcode, 'name' => $name));
|
||||
$entities = $storage->loadByProperties([$langcode_key => $default_langcode, 'name' => $name]);
|
||||
$this->assertEqual(count($entities), 1, format_string('%entity_type: One entity correctly loaded by name and language.', array('%entity_type' => $entity_type)));
|
||||
|
||||
$entities = entity_load_multiple_by_properties($entity_type, array($langcode_key => $langcode, 'name' => $properties[$langcode]['name'][0]));
|
||||
$entities = $storage->loadByProperties([$langcode_key => $langcode, 'name' => $properties[$langcode]['name'][0]]);
|
||||
$this->assertEqual(count($entities), 0, format_string('%entity_type: No entity loaded by name translation specifying the translation language.', array('%entity_type' => $entity_type)));
|
||||
$entities = entity_load_multiple_by_properties($entity_type, array($langcode_key => $langcode, 'name' => $properties[$langcode]['name'][0], $default_langcode_key => 0));
|
||||
$entities = $storage->loadByProperties([$langcode_key => $langcode, 'name' => $properties[$langcode]['name'][0], $default_langcode_key => 0]);
|
||||
$this->assertEqual(count($entities), 1, format_string('%entity_type: One entity loaded by name translation and language specifying to look for translations.', array('%entity_type' => $entity_type)));
|
||||
$entities = entity_load_multiple_by_properties($entity_type, array('user_id' => $properties[$langcode]['user_id'][0], $default_langcode_key => NULL));
|
||||
$entities = $storage->loadByProperties(['user_id' => $properties[$langcode]['user_id'][0], $default_langcode_key => NULL]);
|
||||
$this->assertEqual(count($entities), 2, format_string('%entity_type: Two entities loaded by uid without caring about property translatability.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Test property conditions and orders with multiple languages in the same
|
||||
|
@ -284,7 +285,8 @@ class EntityTranslationTest extends EntityLanguageTestBase {
|
|||
$this->assertEqual(count($result), 1, format_string('%entity_type: One entity loaded by name and uid using different language meta conditions.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Test mixed property and field conditions.
|
||||
$entity = entity_load($entity_type, reset($result), TRUE);
|
||||
$storage->resetCache($result);
|
||||
$entity = $storage->load(reset($result));
|
||||
$field_value = $this->randomString();
|
||||
$entity->getTranslation($langcode)->set($this->fieldName, array(array('value' => $field_value)));
|
||||
$entity->save();
|
||||
|
|
|
@ -65,7 +65,11 @@ class EntityUUIDTest extends EntityKernelTestBase {
|
|||
$this->assertIdentical($entity->uuid(), $uuid);
|
||||
|
||||
// Verify that the UUID is retained upon loading.
|
||||
$entity_loaded = entity_load($entity_type, $entity->id(), TRUE);
|
||||
/** @var \Drupal\Core\Entity\EntityStorageInterface $storage */
|
||||
$storage = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type);
|
||||
$storage->resetCache([$entity->id()]);
|
||||
$entity_loaded = $storage->load($entity->id());
|
||||
$this->assertIdentical($entity_loaded->uuid(), $uuid);
|
||||
|
||||
// Verify that \Drupal::entityManager()->loadEntityByUuid() loads the same entity.
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\EventSubscriber;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\EventSubscriber\ReplicaDatabaseIgnoreSubscriber;
|
||||
use Drupal\Core\DrupalKernel;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
|
||||
/**
|
||||
* Tests that ReplicaDatabaseIgnoreSubscriber functions correctly.
|
||||
*
|
||||
* @group system
|
||||
*/
|
||||
class IgnoreReplicaSubscriberTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Tests \Drupal\Core\EventSubscriber\ReplicaDatabaseIgnoreSubscriber::checkReplicaServer().
|
||||
*/
|
||||
function testSystemInitIgnoresSecondaries() {
|
||||
// Clone the master credentials to a replica connection.
|
||||
// Note this will result in two independent connection objects that happen
|
||||
// to point to the same place.
|
||||
$connection_info = Database::getConnectionInfo('default');
|
||||
Database::addConnectionInfo('default', 'replica', $connection_info['default']);
|
||||
|
||||
db_ignore_replica();
|
||||
$class_loader = require \Drupal::root() . '/autoload.php';
|
||||
$kernel = new DrupalKernel('testing', $class_loader, FALSE);
|
||||
$event = new GetResponseEvent($kernel, Request::create('http://example.com'), HttpKernelInterface::MASTER_REQUEST);
|
||||
$subscriber = new ReplicaDatabaseIgnoreSubscriber();
|
||||
$subscriber->checkReplicaServer($event);
|
||||
|
||||
$db1 = Database::getConnection('default', 'default');
|
||||
$db2 = Database::getConnection('replica', 'default');
|
||||
|
||||
$this->assertIdentical($db1, $db2, 'System Init ignores secondaries when requested.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Extension;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests hook_module_implements_alter().
|
||||
*
|
||||
* @group Module
|
||||
*/
|
||||
class ModuleImplementsAlterTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['system'];
|
||||
|
||||
/**
|
||||
* Tests hook_module_implements_alter() adding an implementation.
|
||||
*
|
||||
* @see \Drupal\Core\Extension\ModuleHandler::buildImplementationInfo()
|
||||
* @see module_test_module_implements_alter()
|
||||
*/
|
||||
function testModuleImplementsAlter() {
|
||||
|
||||
// Get an instance of the module handler, to observe how it is going to be
|
||||
// replaced.
|
||||
$module_handler = \Drupal::moduleHandler();
|
||||
|
||||
$this->assertTrue($module_handler === \Drupal::moduleHandler(),
|
||||
'Module handler instance is still the same.');
|
||||
|
||||
// Install the module_test module.
|
||||
\Drupal::service('module_installer')->install(array('module_test'));
|
||||
|
||||
// Assert that the \Drupal::moduleHandler() instance has been replaced.
|
||||
$this->assertFalse($module_handler === \Drupal::moduleHandler(),
|
||||
'The \Drupal::moduleHandler() instance has been replaced during \Drupal::moduleHandler()->install().');
|
||||
|
||||
// Assert that module_test.module is now included.
|
||||
$this->assertTrue(function_exists('module_test_modules_installed'),
|
||||
'The file module_test.module was successfully included.');
|
||||
|
||||
$this->assertTrue(array_key_exists('module_test', \Drupal::moduleHandler()->getModuleList()),
|
||||
'module_test is in the module list.');
|
||||
|
||||
$this->assertTrue(in_array('module_test', \Drupal::moduleHandler()->getImplementations('modules_installed')),
|
||||
'module_test implements hook_modules_installed().');
|
||||
|
||||
$this->assertTrue(in_array('module_test', \Drupal::moduleHandler()->getImplementations('module_implements_alter')),
|
||||
'module_test implements hook_module_implements_alter().');
|
||||
|
||||
// Assert that module_test.implementations.inc is not included yet.
|
||||
$this->assertFalse(function_exists('module_test_altered_test_hook'),
|
||||
'The file module_test.implementations.inc is not included yet.');
|
||||
|
||||
// Trigger hook discovery for hook_altered_test_hook().
|
||||
// Assert that module_test_module_implements_alter(*, 'altered_test_hook')
|
||||
// has added an implementation.
|
||||
$this->assertTrue(in_array('module_test', \Drupal::moduleHandler()->getImplementations('altered_test_hook')),
|
||||
'module_test implements hook_altered_test_hook().');
|
||||
|
||||
// Assert that module_test.implementations.inc was included as part of the process.
|
||||
$this->assertTrue(function_exists('module_test_altered_test_hook'),
|
||||
'The file module_test.implementations.inc was included.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests what happens if hook_module_implements_alter() adds a non-existing
|
||||
* function to the implementations.
|
||||
*
|
||||
* @see \Drupal\Core\Extension\ModuleHandler::buildImplementationInfo()
|
||||
* @see module_test_module_implements_alter()
|
||||
*/
|
||||
function testModuleImplementsAlterNonExistingImplementation() {
|
||||
|
||||
// Install the module_test module.
|
||||
\Drupal::service('module_installer')->install(array('module_test'));
|
||||
|
||||
try {
|
||||
// Trigger hook discovery.
|
||||
\Drupal::moduleHandler()->getImplementations('unimplemented_test_hook');
|
||||
$this->fail('An exception should be thrown for the non-existing implementation.');
|
||||
}
|
||||
catch (\RuntimeException $e) {
|
||||
$this->pass('An exception should be thrown for the non-existing implementation.');
|
||||
$this->assertEqual($e->getMessage(), 'An invalid implementation module_test_unimplemented_test_hook was added by hook_module_implements_alter()');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
164
core/tests/Drupal/KernelTests/Core/File/DirectoryTest.php
Normal file
164
core/tests/Drupal/KernelTests/Core/File/DirectoryTest.php
Normal file
|
@ -0,0 +1,164 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\File;
|
||||
|
||||
use Drupal\Component\PhpStorage\FileStorage;
|
||||
|
||||
/**
|
||||
* Tests operations dealing with directories.
|
||||
*
|
||||
* @group File
|
||||
*/
|
||||
class DirectoryTest extends FileTestBase {
|
||||
|
||||
/**
|
||||
* Test local directory handling functions.
|
||||
*/
|
||||
function testFileCheckLocalDirectoryHandling() {
|
||||
$site_path = $this->container->get('site.path');
|
||||
$directory = $site_path . '/files';
|
||||
|
||||
// Check a new recursively created local directory for correct file system
|
||||
// permissions.
|
||||
$parent = $this->randomMachineName();
|
||||
$child = $this->randomMachineName();
|
||||
|
||||
// Files directory already exists.
|
||||
$this->assertTrue(is_dir($directory), t('Files directory already exists.'), 'File');
|
||||
// Make files directory writable only.
|
||||
$old_mode = fileperms($directory);
|
||||
|
||||
// Create the directories.
|
||||
$parent_path = $directory . DIRECTORY_SEPARATOR . $parent;
|
||||
$child_path = $parent_path . DIRECTORY_SEPARATOR . $child;
|
||||
$this->assertTrue(drupal_mkdir($child_path, 0775, TRUE), t('No error reported when creating new local directories.'), 'File');
|
||||
|
||||
// Ensure new directories also exist.
|
||||
$this->assertTrue(is_dir($parent_path), t('New parent directory actually exists.'), 'File');
|
||||
$this->assertTrue(is_dir($child_path), t('New child directory actually exists.'), 'File');
|
||||
|
||||
// Check that new directory permissions were set properly.
|
||||
$this->assertDirectoryPermissions($parent_path, 0775);
|
||||
$this->assertDirectoryPermissions($child_path, 0775);
|
||||
|
||||
// Check that existing directory permissions were not modified.
|
||||
$this->assertDirectoryPermissions($directory, $old_mode);
|
||||
|
||||
// Check creating a directory using an absolute path.
|
||||
$absolute_path = drupal_realpath($directory) . DIRECTORY_SEPARATOR . $this->randomMachineName() . DIRECTORY_SEPARATOR . $this->randomMachineName();
|
||||
$this->assertTrue(drupal_mkdir($absolute_path, 0775, TRUE), 'No error reported when creating new absolute directories.', 'File');
|
||||
$this->assertDirectoryPermissions($absolute_path, 0775);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test directory handling functions.
|
||||
*/
|
||||
function testFileCheckDirectoryHandling() {
|
||||
// A directory to operate on.
|
||||
$directory = file_default_scheme() . '://' . $this->randomMachineName() . '/' . $this->randomMachineName();
|
||||
$this->assertFalse(is_dir($directory), 'Directory does not exist prior to testing.');
|
||||
|
||||
// Non-existent directory.
|
||||
$this->assertFalse(file_prepare_directory($directory, 0), 'Error reported for non-existing directory.', 'File');
|
||||
|
||||
// Make a directory.
|
||||
$this->assertTrue(file_prepare_directory($directory, FILE_CREATE_DIRECTORY), 'No error reported when creating a new directory.', 'File');
|
||||
|
||||
// Make sure directory actually exists.
|
||||
$this->assertTrue(is_dir($directory), 'Directory actually exists.', 'File');
|
||||
|
||||
if (substr(PHP_OS, 0, 3) != 'WIN') {
|
||||
// PHP on Windows doesn't support any kind of useful read-only mode for
|
||||
// directories. When executing a chmod() on a directory, PHP only sets the
|
||||
// read-only flag, which doesn't prevent files to actually be written
|
||||
// in the directory on any recent version of Windows.
|
||||
|
||||
// Make directory read only.
|
||||
@drupal_chmod($directory, 0444);
|
||||
$this->assertFalse(file_prepare_directory($directory, 0), 'Error reported for a non-writeable directory.', 'File');
|
||||
|
||||
// Test directory permission modification.
|
||||
$this->setSetting('file_chmod_directory', 0777);
|
||||
$this->assertTrue(file_prepare_directory($directory, FILE_MODIFY_PERMISSIONS), 'No error reported when making directory writeable.', 'File');
|
||||
}
|
||||
|
||||
// Test that the directory has the correct permissions.
|
||||
$this->assertDirectoryPermissions($directory, 0777, 'file_chmod_directory setting is respected.');
|
||||
|
||||
// Remove .htaccess file to then test that it gets re-created.
|
||||
@drupal_unlink(file_default_scheme() . '://.htaccess');
|
||||
$this->assertFalse(is_file(file_default_scheme() . '://.htaccess'), 'Successfully removed the .htaccess file in the files directory.', 'File');
|
||||
file_ensure_htaccess();
|
||||
$this->assertTrue(is_file(file_default_scheme() . '://.htaccess'), 'Successfully re-created the .htaccess file in the files directory.', 'File');
|
||||
// Verify contents of .htaccess file.
|
||||
$file = file_get_contents(file_default_scheme() . '://.htaccess');
|
||||
$this->assertEqual($file, FileStorage::htaccessLines(FALSE), 'The .htaccess file contains the proper content.', 'File');
|
||||
}
|
||||
|
||||
/**
|
||||
* This will take a directory and path, and find a valid filepath that is not
|
||||
* taken by another file.
|
||||
*/
|
||||
function testFileCreateNewFilepath() {
|
||||
// First we test against an imaginary file that does not exist in a
|
||||
// directory.
|
||||
$basename = 'xyz.txt';
|
||||
$directory = 'core/misc';
|
||||
$original = $directory . '/' . $basename;
|
||||
$path = file_create_filename($basename, $directory);
|
||||
$this->assertEqual($path, $original, format_string('New filepath %new equals %original.', array('%new' => $path, '%original' => $original)), 'File');
|
||||
|
||||
// Then we test against a file that already exists within that directory.
|
||||
$basename = 'druplicon.png';
|
||||
$original = $directory . '/' . $basename;
|
||||
$expected = $directory . '/druplicon_0.png';
|
||||
$path = file_create_filename($basename, $directory);
|
||||
$this->assertEqual($path, $expected, format_string('Creating a new filepath from %original equals %new (expected %expected).', array('%new' => $path, '%original' => $original, '%expected' => $expected)), 'File');
|
||||
|
||||
// @TODO: Finally we copy a file into a directory several times, to ensure a properly iterating filename suffix.
|
||||
}
|
||||
|
||||
/**
|
||||
* This will test the filepath for a destination based on passed flags and
|
||||
* whether or not the file exists.
|
||||
*
|
||||
* If a file exists, file_destination($destination, $replace) will either
|
||||
* return:
|
||||
* - the existing filepath, if $replace is FILE_EXISTS_REPLACE
|
||||
* - a new filepath if FILE_EXISTS_RENAME
|
||||
* - an error (returning FALSE) if FILE_EXISTS_ERROR.
|
||||
* If the file doesn't currently exist, then it will simply return the
|
||||
* filepath.
|
||||
*/
|
||||
function testFileDestination() {
|
||||
// First test for non-existent file.
|
||||
$destination = 'core/misc/xyz.txt';
|
||||
$path = file_destination($destination, FILE_EXISTS_REPLACE);
|
||||
$this->assertEqual($path, $destination, 'Non-existing filepath destination is correct with FILE_EXISTS_REPLACE.', 'File');
|
||||
$path = file_destination($destination, FILE_EXISTS_RENAME);
|
||||
$this->assertEqual($path, $destination, 'Non-existing filepath destination is correct with FILE_EXISTS_RENAME.', 'File');
|
||||
$path = file_destination($destination, FILE_EXISTS_ERROR);
|
||||
$this->assertEqual($path, $destination, 'Non-existing filepath destination is correct with FILE_EXISTS_ERROR.', 'File');
|
||||
|
||||
$destination = 'core/misc/druplicon.png';
|
||||
$path = file_destination($destination, FILE_EXISTS_REPLACE);
|
||||
$this->assertEqual($path, $destination, 'Existing filepath destination remains the same with FILE_EXISTS_REPLACE.', 'File');
|
||||
$path = file_destination($destination, FILE_EXISTS_RENAME);
|
||||
$this->assertNotEqual($path, $destination, 'A new filepath destination is created when filepath destination already exists with FILE_EXISTS_RENAME.', 'File');
|
||||
$path = file_destination($destination, FILE_EXISTS_ERROR);
|
||||
$this->assertEqual($path, FALSE, 'An error is returned when filepath destination already exists with FILE_EXISTS_ERROR.', 'File');
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the file_directory_temp() function always returns a value.
|
||||
*/
|
||||
function testFileDirectoryTemp() {
|
||||
// Start with an empty variable to ensure we have a clean slate.
|
||||
$config = $this->config('system.file');
|
||||
$config->set('path.temporary', '')->save();
|
||||
$tmp_directory = file_directory_temp();
|
||||
$this->assertEqual(empty($tmp_directory), FALSE, 'file_directory_temp() returned a non-empty value.');
|
||||
$this->assertEqual($config->get('path.temporary'), $tmp_directory);
|
||||
}
|
||||
|
||||
}
|
202
core/tests/Drupal/KernelTests/Core/File/FileTestBase.php
Normal file
202
core/tests/Drupal/KernelTests/Core/File/FileTestBase.php
Normal file
|
@ -0,0 +1,202 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\File;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Base class for file tests that adds some additional file specific
|
||||
* assertions and helper functions.
|
||||
*/
|
||||
abstract class FileTestBase extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system');
|
||||
|
||||
/**
|
||||
* A stream wrapper scheme to register for the test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $scheme;
|
||||
|
||||
/**
|
||||
* A fully-qualified stream wrapper class name to register for the test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $classname;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
\Drupal::configFactory()->getEditable('system.file')->set('default_scheme', 'public')->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function register(ContainerBuilder $container) {
|
||||
parent::register($container);
|
||||
|
||||
$container->register('stream_wrapper.private', 'Drupal\Core\StreamWrapper\PrivateStream')
|
||||
->addTag('stream_wrapper', ['scheme' => 'private']);
|
||||
|
||||
if (isset($this->scheme)) {
|
||||
$container->register('stream_wrapper.' . $this->scheme, $this->classname)
|
||||
->addTag('stream_wrapper', ['scheme' => $this->scheme]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpFilesystem() {
|
||||
$public_file_directory = $this->siteDirectory . '/files';
|
||||
|
||||
require_once 'core/includes/file.inc';
|
||||
|
||||
mkdir($this->siteDirectory, 0775);
|
||||
mkdir($this->siteDirectory . '/files', 0775);
|
||||
mkdir($this->siteDirectory . '/files/config/' . CONFIG_SYNC_DIRECTORY, 0775, TRUE);
|
||||
|
||||
$this->setSetting('file_public_path', $public_file_directory);
|
||||
|
||||
$GLOBALS['config_directories'] = array(
|
||||
CONFIG_SYNC_DIRECTORY => $this->siteDirectory . '/files/config/sync',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to test the permissions of a file.
|
||||
*
|
||||
* @param $filepath
|
||||
* String file path.
|
||||
* @param $expected_mode
|
||||
* Octal integer like 0664 or 0777.
|
||||
* @param $message
|
||||
* Optional message.
|
||||
*/
|
||||
function assertFilePermissions($filepath, $expected_mode, $message = NULL) {
|
||||
// Clear out PHP's file stat cache to be sure we see the current value.
|
||||
clearstatcache(TRUE, $filepath);
|
||||
|
||||
// Mask out all but the last three octets.
|
||||
$actual_mode = fileperms($filepath) & 0777;
|
||||
|
||||
// PHP on Windows has limited support for file permissions. Usually each of
|
||||
// "user", "group" and "other" use one octal digit (3 bits) to represent the
|
||||
// read/write/execute bits. On Windows, chmod() ignores the "group" and
|
||||
// "other" bits, and fileperms() returns the "user" bits in all three
|
||||
// positions. $expected_mode is updated to reflect this.
|
||||
if (substr(PHP_OS, 0, 3) == 'WIN') {
|
||||
// Reset the "group" and "other" bits.
|
||||
$expected_mode = $expected_mode & 0700;
|
||||
// Shift the "user" bits to the "group" and "other" positions also.
|
||||
$expected_mode = $expected_mode | $expected_mode >> 3 | $expected_mode >> 6;
|
||||
}
|
||||
|
||||
if (!isset($message)) {
|
||||
$message = t('Expected file permission to be %expected, actually were %actual.', array('%actual' => decoct($actual_mode), '%expected' => decoct($expected_mode)));
|
||||
}
|
||||
$this->assertEqual($actual_mode, $expected_mode, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to test the permissions of a directory.
|
||||
*
|
||||
* @param $directory
|
||||
* String directory path.
|
||||
* @param $expected_mode
|
||||
* Octal integer like 0664 or 0777.
|
||||
* @param $message
|
||||
* Optional message.
|
||||
*/
|
||||
function assertDirectoryPermissions($directory, $expected_mode, $message = NULL) {
|
||||
// Clear out PHP's file stat cache to be sure we see the current value.
|
||||
clearstatcache(TRUE, $directory);
|
||||
|
||||
// Mask out all but the last three octets.
|
||||
$actual_mode = fileperms($directory) & 0777;
|
||||
$expected_mode = $expected_mode & 0777;
|
||||
|
||||
// PHP on Windows has limited support for file permissions. Usually each of
|
||||
// "user", "group" and "other" use one octal digit (3 bits) to represent the
|
||||
// read/write/execute bits. On Windows, chmod() ignores the "group" and
|
||||
// "other" bits, and fileperms() returns the "user" bits in all three
|
||||
// positions. $expected_mode is updated to reflect this.
|
||||
if (substr(PHP_OS, 0, 3) == 'WIN') {
|
||||
// Reset the "group" and "other" bits.
|
||||
$expected_mode = $expected_mode & 0700;
|
||||
// Shift the "user" bits to the "group" and "other" positions also.
|
||||
$expected_mode = $expected_mode | $expected_mode >> 3 | $expected_mode >> 6;
|
||||
}
|
||||
|
||||
if (!isset($message)) {
|
||||
$message = t('Expected directory permission to be %expected, actually were %actual.', array('%actual' => decoct($actual_mode), '%expected' => decoct($expected_mode)));
|
||||
}
|
||||
$this->assertEqual($actual_mode, $expected_mode, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a directory and assert it exists.
|
||||
*
|
||||
* @param $path
|
||||
* Optional string with a directory path. If none is provided, a random
|
||||
* name in the site's files directory will be used.
|
||||
* @return
|
||||
* The path to the directory.
|
||||
*/
|
||||
function createDirectory($path = NULL) {
|
||||
// A directory to operate on.
|
||||
if (!isset($path)) {
|
||||
$path = file_default_scheme() . '://' . $this->randomMachineName();
|
||||
}
|
||||
$this->assertTrue(drupal_mkdir($path) && is_dir($path), 'Directory was created successfully.');
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a file and return the URI of it.
|
||||
*
|
||||
* @param $filepath
|
||||
* Optional string specifying the file path. If none is provided then a
|
||||
* randomly named file will be created in the site's files directory.
|
||||
* @param $contents
|
||||
* Optional contents to save into the file. If a NULL value is provided an
|
||||
* arbitrary string will be used.
|
||||
* @param $scheme
|
||||
* Optional string indicating the stream scheme to use. Drupal core includes
|
||||
* public, private, and temporary. The public wrapper is the default.
|
||||
* @return
|
||||
* File URI.
|
||||
*/
|
||||
function createUri($filepath = NULL, $contents = NULL, $scheme = NULL) {
|
||||
if (!isset($filepath)) {
|
||||
// Prefix with non-latin characters to ensure that all file-related
|
||||
// tests work with international filenames.
|
||||
$filepath = 'Файл для тестирования ' . $this->randomMachineName();
|
||||
}
|
||||
if (!isset($scheme)) {
|
||||
$scheme = file_default_scheme();
|
||||
}
|
||||
$filepath = $scheme . '://' . $filepath;
|
||||
|
||||
if (!isset($contents)) {
|
||||
$contents = "file_put_contents() doesn't seem to appreciate empty strings so let's put in some data.";
|
||||
}
|
||||
|
||||
file_put_contents($filepath, $contents);
|
||||
$this->assertTrue(is_file($filepath), t('The test file exists on the disk.'), 'Create test file');
|
||||
return $filepath;
|
||||
}
|
||||
|
||||
}
|
92
core/tests/Drupal/KernelTests/Core/File/HtaccessTest.php
Normal file
92
core/tests/Drupal/KernelTests/Core/File/HtaccessTest.php
Normal file
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\File;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests .htaccess file saving.
|
||||
*
|
||||
* @group File
|
||||
*/
|
||||
class HtaccessTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Tests file_save_htaccess().
|
||||
*/
|
||||
function testHtaccessSave() {
|
||||
// Prepare test directories.
|
||||
$public = Settings::get('file_public_path') . '/test/public';
|
||||
$private = Settings::get('file_public_path') . '/test/private';
|
||||
$stream = 'public://test/stream';
|
||||
|
||||
// Verify that file_save_htaccess() returns FALSE if .htaccess cannot be
|
||||
// written.
|
||||
// Note: We cannot test the condition of a directory lacking write
|
||||
// permissions, since at least on Windows file_save_htaccess() succeeds
|
||||
// even when changing directory permissions to 0000.
|
||||
$this->assertFalse(file_save_htaccess($public, FALSE));
|
||||
|
||||
// Create public .htaccess file.
|
||||
mkdir($public, 0777, TRUE);
|
||||
$this->assertTrue(file_save_htaccess($public, FALSE));
|
||||
$content = file_get_contents($public . '/.htaccess');
|
||||
$this->assertTrue(strpos($content, "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006") !== FALSE);
|
||||
$this->assertFalse(strpos($content, "Require all denied") !== FALSE);
|
||||
$this->assertFalse(strpos($content, "Deny from all") !== FALSE);
|
||||
$this->assertTrue(strpos($content, "Options -Indexes -ExecCGI -Includes -MultiViews") !== FALSE);
|
||||
$this->assertTrue(strpos($content, "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2013_003") !== FALSE);
|
||||
$this->assertFilePermissions($public . '/.htaccess', 0444);
|
||||
|
||||
$this->assertTrue(file_save_htaccess($public, FALSE));
|
||||
|
||||
// Create private .htaccess file.
|
||||
mkdir($private, 0777, TRUE);
|
||||
$this->assertTrue(file_save_htaccess($private));
|
||||
$content = file_get_contents($private . '/.htaccess');
|
||||
$this->assertTrue(strpos($content, "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006") !== FALSE);
|
||||
$this->assertTrue(strpos($content, "Require all denied") !== FALSE);
|
||||
$this->assertTrue(strpos($content, "Deny from all") !== FALSE);
|
||||
$this->assertTrue(strpos($content, "Options -Indexes -ExecCGI -Includes -MultiViews") !== FALSE);
|
||||
$this->assertTrue(strpos($content, "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2013_003") !== FALSE);
|
||||
$this->assertFilePermissions($private . '/.htaccess', 0444);
|
||||
|
||||
$this->assertTrue(file_save_htaccess($private));
|
||||
|
||||
// Create an .htaccess file using a stream URI.
|
||||
mkdir($stream, 0777, TRUE);
|
||||
$this->assertTrue(file_save_htaccess($stream));
|
||||
$content = file_get_contents($stream . '/.htaccess');
|
||||
$this->assertTrue(strpos($content, "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006") !== FALSE);
|
||||
$this->assertTrue(strpos($content, "Require all denied") !== FALSE);
|
||||
$this->assertTrue(strpos($content, "Deny from all") !== FALSE);
|
||||
$this->assertTrue(strpos($content, "Options -Indexes -ExecCGI -Includes -MultiViews") !== FALSE);
|
||||
$this->assertTrue(strpos($content, "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2013_003") !== FALSE);
|
||||
$this->assertFilePermissions($stream . '/.htaccess', 0444);
|
||||
|
||||
$this->assertTrue(file_save_htaccess($stream));
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts expected file permissions for a given file.
|
||||
*
|
||||
* @param string $uri
|
||||
* The URI of the file to check.
|
||||
* @param int $expected
|
||||
* The expected file permissions; e.g., 0444.
|
||||
*
|
||||
* @return bool
|
||||
* Whether the actual file permissions match the expected.
|
||||
*/
|
||||
protected function assertFilePermissions($uri, $expected) {
|
||||
$actual = fileperms($uri) & 0777;
|
||||
return $this->assertIdentical($actual, $expected, SafeMarkup::format('@uri file permissions @actual are identical to @expected.', array(
|
||||
'@uri' => $uri,
|
||||
'@actual' => 0 . decoct($actual),
|
||||
'@expected' => 0 . decoct($expected),
|
||||
)));
|
||||
}
|
||||
|
||||
}
|
92
core/tests/Drupal/KernelTests/Core/File/MimeTypeTest.php
Normal file
92
core/tests/Drupal/KernelTests/Core/File/MimeTypeTest.php
Normal file
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\File;
|
||||
|
||||
/**
|
||||
* Tests filename mimetype detection.
|
||||
*
|
||||
* @group File
|
||||
*/
|
||||
class MimeTypeTest extends FileTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('file_test');
|
||||
|
||||
/**
|
||||
* Test mapping of mimetypes from filenames.
|
||||
*/
|
||||
public function testFileMimeTypeDetection() {
|
||||
$prefixes = ['public://', 'private://', 'temporary://', 'dummy-remote://'];
|
||||
|
||||
$test_case = array(
|
||||
'test.jar' => 'application/java-archive',
|
||||
'test.jpeg' => 'image/jpeg',
|
||||
'test.JPEG' => 'image/jpeg',
|
||||
'test.jpg' => 'image/jpeg',
|
||||
'test.jar.jpg' => 'image/jpeg',
|
||||
'test.jpg.jar' => 'application/java-archive',
|
||||
'test.pcf.Z' => 'application/x-font',
|
||||
'pcf.z' => 'application/octet-stream',
|
||||
'jar' => 'application/octet-stream',
|
||||
'some.junk' => 'application/octet-stream',
|
||||
'foo.file_test_1' => 'madeup/file_test_1',
|
||||
'foo.file_test_2' => 'madeup/file_test_2',
|
||||
'foo.doc' => 'madeup/doc',
|
||||
'test.ogg' => 'audio/ogg',
|
||||
);
|
||||
|
||||
$guesser = $this->container->get('file.mime_type.guesser');
|
||||
// Test using default mappings.
|
||||
foreach ($test_case as $input => $expected) {
|
||||
// Test stream [URI].
|
||||
foreach ($prefixes as $prefix) {
|
||||
$output = $guesser->guess($prefix . $input);
|
||||
$this->assertIdentical($output, $expected, format_string('Mimetype for %input is %output (expected: %expected).', array('%input' => $prefix . $input, '%output' => $output, '%expected' => $expected)));
|
||||
}
|
||||
|
||||
// Test normal path equivalent
|
||||
$output = $guesser->guess($input);
|
||||
$this->assertIdentical($output, $expected, format_string('Mimetype (using default mappings) for %input is %output (expected: %expected).', array('%input' => $input, '%output' => $output, '%expected' => $expected)));
|
||||
}
|
||||
|
||||
// Now test the extension gusser by passing in a custom mapping.
|
||||
$mapping = array(
|
||||
'mimetypes' => array(
|
||||
0 => 'application/java-archive',
|
||||
1 => 'image/jpeg',
|
||||
),
|
||||
'extensions' => array(
|
||||
'jar' => 0,
|
||||
'jpg' => 1,
|
||||
)
|
||||
);
|
||||
|
||||
$test_case = array(
|
||||
'test.jar' => 'application/java-archive',
|
||||
'test.jpeg' => 'application/octet-stream',
|
||||
'test.jpg' => 'image/jpeg',
|
||||
'test.jar.jpg' => 'image/jpeg',
|
||||
'test.jpg.jar' => 'application/java-archive',
|
||||
'test.pcf.z' => 'application/octet-stream',
|
||||
'pcf.z' => 'application/octet-stream',
|
||||
'jar' => 'application/octet-stream',
|
||||
'some.junk' => 'application/octet-stream',
|
||||
'foo.file_test_1' => 'application/octet-stream',
|
||||
'foo.file_test_2' => 'application/octet-stream',
|
||||
'foo.doc' => 'application/octet-stream',
|
||||
'test.ogg' => 'application/octet-stream',
|
||||
);
|
||||
$extension_guesser = $this->container->get('file.mime_type.guesser.extension');
|
||||
$extension_guesser->setMapping($mapping);
|
||||
|
||||
foreach ($test_case as $input => $expected) {
|
||||
$output = $extension_guesser->guess($input);
|
||||
$this->assertIdentical($output, $expected, format_string('Mimetype (using passed-in mappings) for %input is %output (expected: %expected).', array('%input' => $input, '%output' => $output, '%expected' => $expected)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
87
core/tests/Drupal/KernelTests/Core/File/NameMungingTest.php
Normal file
87
core/tests/Drupal/KernelTests/Core/File/NameMungingTest.php
Normal file
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\File;
|
||||
|
||||
/**
|
||||
* Tests filename munging and unmunging.
|
||||
*
|
||||
* @group File
|
||||
*/
|
||||
class NameMungingTest extends FileTestBase {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $badExtension;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $nameWithUcExt;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->badExtension = 'php';
|
||||
$this->name = $this->randomMachineName() . '.' . $this->badExtension . '.txt';
|
||||
$this->nameWithUcExt = $this->randomMachineName() . '.' . strtoupper($this->badExtension) . '.txt';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a file and munge/unmunge the name.
|
||||
*/
|
||||
function testMunging() {
|
||||
// Disable insecure uploads.
|
||||
$this->config('system.file')->set('allow_insecure_uploads', 0)->save();
|
||||
$munged_name = file_munge_filename($this->name, '', TRUE);
|
||||
$messages = drupal_get_messages();
|
||||
$this->assertTrue(in_array(strtr('For security reasons, your upload has been renamed to <em class="placeholder">%filename</em>.', array('%filename' => $munged_name)), $messages['status']), 'Alert properly set when a file is renamed.');
|
||||
$this->assertNotEqual($munged_name, $this->name, format_string('The new filename (%munged) has been modified from the original (%original)', array('%munged' => $munged_name, '%original' => $this->name)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests munging with a null byte in the filename.
|
||||
*/
|
||||
function testMungeNullByte() {
|
||||
$prefix = $this->randomMachineName();
|
||||
$filename = $prefix . '.' . $this->badExtension . "\0.txt";
|
||||
$this->assertEqual(file_munge_filename($filename, ''), $prefix . '.' . $this->badExtension . '_.txt', 'A filename with a null byte is correctly munged to remove the null byte.');
|
||||
}
|
||||
|
||||
/**
|
||||
* If the system.file.allow_insecure_uploads setting evaluates to true, the file should
|
||||
* come out untouched, no matter how evil the filename.
|
||||
*/
|
||||
function testMungeIgnoreInsecure() {
|
||||
$this->config('system.file')->set('allow_insecure_uploads', 1)->save();
|
||||
$munged_name = file_munge_filename($this->name, '');
|
||||
$this->assertIdentical($munged_name, $this->name, format_string('The original filename (%original) matches the munged filename (%munged) when insecure uploads are enabled.', array('%munged' => $munged_name, '%original' => $this->name)));
|
||||
}
|
||||
|
||||
/**
|
||||
* White listed extensions are ignored by file_munge_filename().
|
||||
*/
|
||||
function testMungeIgnoreWhitelisted() {
|
||||
// Declare our extension as whitelisted. The declared extensions should
|
||||
// be case insensitive so test using one with a different case.
|
||||
$munged_name = file_munge_filename($this->nameWithUcExt, $this->badExtension);
|
||||
$this->assertIdentical($munged_name, $this->nameWithUcExt, format_string('The new filename (%munged) matches the original (%original) once the extension has been whitelisted.', array('%munged' => $munged_name, '%original' => $this->nameWithUcExt)));
|
||||
// The allowed extensions should also be normalized.
|
||||
$munged_name = file_munge_filename($this->name, strtoupper($this->badExtension));
|
||||
$this->assertIdentical($munged_name, $this->name, format_string('The new filename (%munged) matches the original (%original) also when the whitelisted extension is in uppercase.', array('%munged' => $munged_name, '%original' => $this->name)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that unmunge gets your name back.
|
||||
*/
|
||||
function testUnMunge() {
|
||||
$munged_name = file_munge_filename($this->name, '', FALSE);
|
||||
$unmunged_name = file_unmunge_filename($munged_name);
|
||||
$this->assertIdentical($unmunged_name, $this->name, format_string('The unmunged (%unmunged) filename matches the original (%original)', array('%unmunged' => $unmunged_name, '%original' => $this->name)));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\File;
|
||||
|
||||
use Drupal\Core\StreamWrapper\StreamWrapperInterface;
|
||||
use Drupal\file_test\StreamWrapper\DummyReadOnlyStreamWrapper;
|
||||
|
||||
/**
|
||||
* Tests the read-only stream wrapper write functions.
|
||||
*
|
||||
* @group File
|
||||
*/
|
||||
class ReadOnlyStreamWrapperTest extends FileTestBase {
|
||||
|
||||
/**
|
||||
* A stream wrapper scheme to register for the test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $scheme = 'dummy-readonly';
|
||||
|
||||
/**
|
||||
* A fully-qualified stream wrapper class name to register for the test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $classname = 'Drupal\file_test\StreamWrapper\DummyReadOnlyStreamWrapper';
|
||||
|
||||
/**
|
||||
* Test read-only specific behavior.
|
||||
*/
|
||||
function testReadOnlyBehavior() {
|
||||
$type = DummyReadOnlyStreamWrapper::getType();
|
||||
// Checks that the stream wrapper type is not declared as writable.
|
||||
$this->assertSame(0, $type & StreamWrapperInterface::WRITE);
|
||||
// Checks that the stream wrapper type is declared as local.
|
||||
$this->assertSame(1, $type & StreamWrapperInterface::LOCAL);
|
||||
|
||||
// Generate a test file
|
||||
$filename = $this->randomMachineName();
|
||||
$site_path = $this->container->get('site.path');
|
||||
$filepath = $site_path . '/files/' . $filename;
|
||||
file_put_contents($filepath, $filename);
|
||||
|
||||
// Generate a read-only stream wrapper instance
|
||||
$uri = $this->scheme . '://' . $filename;
|
||||
\Drupal::service('stream_wrapper_manager')->getViaScheme($this->scheme);
|
||||
|
||||
// Attempt to open a file in read/write mode
|
||||
$handle = @fopen($uri, 'r+');
|
||||
$this->assertFalse($handle, 'Unable to open a file for reading and writing with the read-only stream wrapper.');
|
||||
// Attempt to open a file in binary read mode
|
||||
$handle = fopen($uri, 'rb');
|
||||
$this->assertTrue($handle, 'Able to open a file for reading in binary mode with the read-only stream wrapper.');
|
||||
$this->assertTrue(fclose($handle), 'Able to close file opened in binary mode using the read_only stream wrapper.');
|
||||
// Attempt to open a file in text read mode
|
||||
$handle = fopen($uri, 'rt');
|
||||
$this->assertTrue($handle, 'Able to open a file for reading in text mode with the read-only stream wrapper.');
|
||||
$this->assertTrue(fclose($handle), 'Able to close file opened in text mode using the read_only stream wrapper.');
|
||||
// Attempt to open a file in read mode
|
||||
$handle = fopen($uri, 'r');
|
||||
$this->assertTrue($handle, 'Able to open a file for reading with the read-only stream wrapper.');
|
||||
// Attempt to change file permissions
|
||||
$this->assertFalse(@chmod($uri, 0777), 'Unable to change file permissions when using read-only stream wrapper.');
|
||||
// Attempt to acquire an exclusive lock for writing
|
||||
$this->assertFalse(@flock($handle, LOCK_EX | LOCK_NB), 'Unable to acquire an exclusive lock using the read-only stream wrapper.');
|
||||
// Attempt to obtain a shared lock
|
||||
$this->assertTrue(flock($handle, LOCK_SH | LOCK_NB), 'Able to acquire a shared lock using the read-only stream wrapper.');
|
||||
// Attempt to release a shared lock
|
||||
$this->assertTrue(flock($handle, LOCK_UN | LOCK_NB), 'Able to release a shared lock using the read-only stream wrapper.');
|
||||
// Attempt to truncate the file
|
||||
$this->assertFalse(@ftruncate($handle, 0), 'Unable to truncate using the read-only stream wrapper.');
|
||||
// Attempt to write to the file
|
||||
$this->assertFalse(@fwrite($handle, $this->randomMachineName()), 'Unable to write to file using the read-only stream wrapper.');
|
||||
// Attempt to flush output to the file
|
||||
$this->assertFalse(@fflush($handle), 'Unable to flush output to file using the read-only stream wrapper.');
|
||||
// Attempt to close the stream. (Suppress errors, as fclose triggers fflush.)
|
||||
$this->assertTrue(fclose($handle), 'Able to close file using the read_only stream wrapper.');
|
||||
// Test the rename() function
|
||||
$this->assertFalse(@rename($uri, $this->scheme . '://newname.txt'), 'Unable to rename files using the read-only stream wrapper.');
|
||||
// Test the unlink() function
|
||||
$this->assertTrue(@drupal_unlink($uri), 'Able to unlink file using read-only stream wrapper.');
|
||||
$this->assertTrue(file_exists($filepath), 'Unlink File was not actually deleted.');
|
||||
|
||||
// Test the mkdir() function by attempting to create a directory.
|
||||
$dirname = $this->randomMachineName();
|
||||
$dir = $site_path . '/files/' . $dirname;
|
||||
$readonlydir = $this->scheme . '://' . $dirname;
|
||||
$this->assertFalse(@drupal_mkdir($readonlydir, 0775, 0), 'Unable to create directory with read-only stream wrapper.');
|
||||
// Create a temporary directory for testing purposes
|
||||
$this->assertTrue(drupal_mkdir($dir), 'Test directory created.');
|
||||
// Test the rmdir() function by attempting to remove the directory.
|
||||
$this->assertFalse(@drupal_rmdir($readonlydir), 'Unable to delete directory with read-only stream wrapper.');
|
||||
// Remove the temporary directory.
|
||||
drupal_rmdir($dir);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\File;
|
||||
|
||||
/**
|
||||
* Tests operations dealing with directories.
|
||||
*
|
||||
* @group File
|
||||
*/
|
||||
class RemoteFileDirectoryTest extends DirectoryTest {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('file_test');
|
||||
|
||||
/**
|
||||
* A stream wrapper scheme to register for the test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $scheme = 'dummy-remote';
|
||||
|
||||
/**
|
||||
* A fully-qualified stream wrapper class name to register for the test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $classname = 'Drupal\file_test\StreamWrapper\DummyRemoteStreamWrapper';
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->config('system.file')->set('default_scheme', 'dummy-remote')->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\File;
|
||||
|
||||
/**
|
||||
* Tests the file_scan_directory() function.
|
||||
*
|
||||
* @group File
|
||||
*/
|
||||
class RemoteFileScanDirectoryTest extends ScanDirectoryTest {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('file_test');
|
||||
|
||||
/**
|
||||
* A stream wrapper scheme to register for the test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $scheme = 'dummy-remote';
|
||||
|
||||
/**
|
||||
* A fully-qualified stream wrapper class name to register for the test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $classname = 'Drupal\file_test\StreamWrapper\DummyRemoteStreamWrapper';
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->config('system.file')->set('default_scheme', 'dummy-remote')->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\File;
|
||||
|
||||
/**
|
||||
* Tests the unmanaged file copy function.
|
||||
*
|
||||
* @group File
|
||||
*/
|
||||
class RemoteFileUnmanagedCopyTest extends UnmanagedCopyTest {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('file_test');
|
||||
|
||||
/**
|
||||
* A stream wrapper scheme to register for the test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $scheme = 'dummy-remote';
|
||||
|
||||
/**
|
||||
* A fully-qualified stream wrapper class name to register for the test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $classname = 'Drupal\file_test\StreamWrapper\DummyRemoteStreamWrapper';
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->config('system.file')->set('default_scheme', 'dummy-remote')->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\File;
|
||||
|
||||
/**
|
||||
* Tests the unmanaged file delete recursive function.
|
||||
*
|
||||
* @group File
|
||||
*/
|
||||
class RemoteFileUnmanagedDeleteRecursiveTest extends UnmanagedDeleteRecursiveTest {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('file_test');
|
||||
|
||||
/**
|
||||
* A stream wrapper scheme to register for the test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $scheme = 'dummy-remote';
|
||||
|
||||
/**
|
||||
* A fully-qualified stream wrapper class name to register for the test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $classname = 'Drupal\file_test\StreamWrapper\DummyRemoteStreamWrapper';
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->config('system.file')->set('default_scheme', 'dummy-remote')->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\File;
|
||||
|
||||
/**
|
||||
* Tests the unmanaged file delete function.
|
||||
*
|
||||
* @group File
|
||||
*/
|
||||
class RemoteFileUnmanagedDeleteTest extends UnmanagedDeleteTest {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('file_test');
|
||||
|
||||
/**
|
||||
* A stream wrapper scheme to register for the test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $scheme = 'dummy-remote';
|
||||
|
||||
/**
|
||||
* A fully-qualified stream wrapper class name to register for the test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $classname = 'Drupal\file_test\StreamWrapper\DummyRemoteStreamWrapper';
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->config('system.file')->set('default_scheme', 'dummy-remote')->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\File;
|
||||
|
||||
/**
|
||||
* Tests the unmanaged file move function.
|
||||
*
|
||||
* @group File
|
||||
*/
|
||||
class RemoteFileUnmanagedMoveTest extends UnmanagedMoveTest {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('file_test');
|
||||
|
||||
/**
|
||||
* A stream wrapper scheme to register for the test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $scheme = 'dummy-remote';
|
||||
|
||||
/**
|
||||
* A fully-qualified stream wrapper class name to register for the test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $classname = 'Drupal\file_test\StreamWrapper\DummyRemoteStreamWrapper';
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->config('system.file')->set('default_scheme', 'dummy-remote')->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\File;
|
||||
|
||||
/**
|
||||
* Tests the unmanaged file save data function.
|
||||
*
|
||||
* @group File
|
||||
*/
|
||||
class RemoteFileUnmanagedSaveDataTest extends UnmanagedSaveDataTest {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('file_test');
|
||||
|
||||
/**
|
||||
* A stream wrapper scheme to register for the test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $scheme = 'dummy-remote';
|
||||
|
||||
/**
|
||||
* A fully-qualified stream wrapper class name to register for the test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $classname = 'Drupal\file_test\StreamWrapper\DummyRemoteStreamWrapper';
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->config('system.file')->set('default_scheme', 'dummy-remote')->save();
|
||||
}
|
||||
|
||||
}
|
166
core/tests/Drupal/KernelTests/Core/File/ScanDirectoryTest.php
Normal file
166
core/tests/Drupal/KernelTests/Core/File/ScanDirectoryTest.php
Normal file
|
@ -0,0 +1,166 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\File;
|
||||
|
||||
/**
|
||||
* Tests the file_scan_directory() function.
|
||||
*
|
||||
* @group File
|
||||
*/
|
||||
class ScanDirectoryTest extends FileTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('file_test');
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Hardcode the location of the simpletest files as it is already known
|
||||
// and shouldn't change, and we don't yet have a way to retrieve their
|
||||
// location from drupal_get_filename() in a cached way.
|
||||
// @todo Remove as part of https://www.drupal.org/node/2186491
|
||||
$this->path = 'core/modules/simpletest/files';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the format of the returned values.
|
||||
*/
|
||||
function testReturn() {
|
||||
// Grab a listing of all the JavaScript files and check that they're
|
||||
// passed to the callback.
|
||||
$all_files = file_scan_directory($this->path, '/^javascript-/');
|
||||
ksort($all_files);
|
||||
$this->assertEqual(2, count($all_files), 'Found two, expected javascript files.');
|
||||
|
||||
// Check the first file.
|
||||
$file = reset($all_files);
|
||||
$this->assertEqual(key($all_files), $file->uri, 'Correct array key was used for the first returned file.');
|
||||
$this->assertEqual($file->uri, $this->path . '/javascript-1.txt', 'First file name was set correctly.');
|
||||
$this->assertEqual($file->filename, 'javascript-1.txt', 'First basename was set correctly');
|
||||
$this->assertEqual($file->name, 'javascript-1', 'First name was set correctly.');
|
||||
|
||||
// Check the second file.
|
||||
$file = next($all_files);
|
||||
$this->assertEqual(key($all_files), $file->uri, 'Correct array key was used for the second returned file.');
|
||||
$this->assertEqual($file->uri, $this->path . '/javascript-2.script', 'Second file name was set correctly.');
|
||||
$this->assertEqual($file->filename, 'javascript-2.script', 'Second basename was set correctly');
|
||||
$this->assertEqual($file->name, 'javascript-2', 'Second name was set correctly.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the callback function is called correctly.
|
||||
*/
|
||||
function testOptionCallback() {
|
||||
|
||||
// When nothing is matched nothing should be passed to the callback.
|
||||
$all_files = file_scan_directory($this->path, '/^NONEXISTINGFILENAME/', array('callback' => 'file_test_file_scan_callback'));
|
||||
$this->assertEqual(0, count($all_files), 'No files were found.');
|
||||
$results = file_test_file_scan_callback();
|
||||
file_test_file_scan_callback_reset();
|
||||
$this->assertEqual(0, count($results), 'No files were passed to the callback.');
|
||||
|
||||
// Grab a listing of all the JavaScript files and check that they're
|
||||
// passed to the callback.
|
||||
$all_files = file_scan_directory($this->path, '/^javascript-/', array('callback' => 'file_test_file_scan_callback'));
|
||||
$this->assertEqual(2, count($all_files), 'Found two, expected javascript files.');
|
||||
$results = file_test_file_scan_callback();
|
||||
file_test_file_scan_callback_reset();
|
||||
$this->assertEqual(2, count($results), 'Files were passed to the callback.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the no-mask parameter is honored.
|
||||
*/
|
||||
function testOptionNoMask() {
|
||||
// Grab a listing of all the JavaScript files.
|
||||
$all_files = file_scan_directory($this->path, '/^javascript-/');
|
||||
$this->assertEqual(2, count($all_files), 'Found two, expected javascript files.');
|
||||
|
||||
// Now use the nomask parameter to filter out the .script file.
|
||||
$filtered_files = file_scan_directory($this->path, '/^javascript-/', array('nomask' => '/.script$/'));
|
||||
$this->assertEqual(1, count($filtered_files), 'Filtered correctly.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that key parameter sets the return value's key.
|
||||
*/
|
||||
function testOptionKey() {
|
||||
// "filename", for the path starting with $dir.
|
||||
$expected = array($this->path . '/javascript-1.txt', $this->path . '/javascript-2.script');
|
||||
$actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'filepath')));
|
||||
sort($actual);
|
||||
$this->assertEqual($expected, $actual, 'Returned the correct values for the filename key.');
|
||||
|
||||
// "basename", for the basename of the file.
|
||||
$expected = array('javascript-1.txt', 'javascript-2.script');
|
||||
$actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'filename')));
|
||||
sort($actual);
|
||||
$this->assertEqual($expected, $actual, 'Returned the correct values for the basename key.');
|
||||
|
||||
// "name" for the name of the file without an extension.
|
||||
$expected = array('javascript-1', 'javascript-2');
|
||||
$actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'name')));
|
||||
sort($actual);
|
||||
$this->assertEqual($expected, $actual, 'Returned the correct values for the name key.');
|
||||
|
||||
// Invalid option that should default back to "filename".
|
||||
$expected = array($this->path . '/javascript-1.txt', $this->path . '/javascript-2.script');
|
||||
$actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'INVALID')));
|
||||
sort($actual);
|
||||
$this->assertEqual($expected, $actual, 'An invalid key defaulted back to the default.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the recurse option descends into subdirectories.
|
||||
*/
|
||||
function testOptionRecurse() {
|
||||
$files = file_scan_directory($this->path . '/..', '/^javascript-/', array('recurse' => FALSE));
|
||||
$this->assertTrue(empty($files), "Without recursion couldn't find javascript files.");
|
||||
|
||||
$files = file_scan_directory($this->path . '/..', '/^javascript-/', array('recurse' => TRUE));
|
||||
$this->assertEqual(2, count($files), 'With recursion we found the expected javascript files.');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check that the min_depth options lets us ignore files in the starting
|
||||
* directory.
|
||||
*/
|
||||
function testOptionMinDepth() {
|
||||
$files = file_scan_directory($this->path, '/^javascript-/', array('min_depth' => 0));
|
||||
$this->assertEqual(2, count($files), 'No minimum-depth gets files in current directory.');
|
||||
|
||||
$files = file_scan_directory($this->path, '/^javascript-/', array('min_depth' => 1));
|
||||
$this->assertTrue(empty($files), 'Minimum-depth of 1 successfully excludes files from current directory.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests file_scan_directory() obeys 'file_scan_ignore_directories' setting.
|
||||
*/
|
||||
public function testIgnoreDirectories() {
|
||||
$files = file_scan_directory('core/modules/system/tests/fixtures/IgnoreDirectories', '/\.txt$/');
|
||||
$this->assertCount(2, $files, '2 text files found when not ignoring directories.');
|
||||
|
||||
$this->setSetting('file_scan_ignore_directories', ['frontend_framework']);
|
||||
$files = file_scan_directory('core/modules/system/tests/fixtures/IgnoreDirectories', '/\.txt$/');
|
||||
$this->assertCount(1, $files, '1 text files found when ignoring directories called "frontend_framework".');
|
||||
|
||||
// Ensure that the directories in file_scan_ignore_directories are escaped
|
||||
// using preg_quote.
|
||||
$this->setSetting('file_scan_ignore_directories', ['frontend.*']);
|
||||
$files = file_scan_directory('core/modules/system/tests/fixtures/IgnoreDirectories', '/\.txt$/');
|
||||
$this->assertCount(2, $files, '2 text files found when ignoring a directory that is not there.');
|
||||
|
||||
$files = file_scan_directory('core/modules/system/tests/fixtures/IgnoreDirectories', '/\.txt$/', ['nomask' => '/^something_thing_else$/']);
|
||||
$this->assertCount(2, $files, '2 text files found when an "nomask" option is passed in.');
|
||||
}
|
||||
|
||||
}
|
147
core/tests/Drupal/KernelTests/Core/File/StreamWrapperTest.php
Normal file
147
core/tests/Drupal/KernelTests/Core/File/StreamWrapperTest.php
Normal file
|
@ -0,0 +1,147 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\File;
|
||||
|
||||
use Drupal\Core\DrupalKernel;
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Drupal\Core\StreamWrapper\PublicStream;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Tests stream wrapper functions.
|
||||
*
|
||||
* @group File
|
||||
*/
|
||||
class StreamWrapperTest extends FileTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('file_test');
|
||||
|
||||
/**
|
||||
* A stream wrapper scheme to register for the test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $scheme = 'dummy';
|
||||
|
||||
/**
|
||||
* A fully-qualified stream wrapper class name to register for the test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $classname = 'Drupal\file_test\StreamWrapper\DummyStreamWrapper';
|
||||
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Add file_private_path setting.
|
||||
$request = Request::create('/');;
|
||||
$site_path = DrupalKernel::findSitePath($request);
|
||||
$this->setSetting('file_private_path', $site_path . '/private');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the getClassName() function.
|
||||
*/
|
||||
function testGetClassName() {
|
||||
// Check the dummy scheme.
|
||||
$this->assertEqual($this->classname, \Drupal::service('stream_wrapper_manager')->getClass($this->scheme), 'Got correct class name for dummy scheme.');
|
||||
// Check core's scheme.
|
||||
$this->assertEqual('Drupal\Core\StreamWrapper\PublicStream', \Drupal::service('stream_wrapper_manager')->getClass('public'), 'Got correct class name for public scheme.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the getViaScheme() method.
|
||||
*/
|
||||
function testGetInstanceByScheme() {
|
||||
$instance = \Drupal::service('stream_wrapper_manager')->getViaScheme($this->scheme);
|
||||
$this->assertEqual($this->classname, get_class($instance), 'Got correct class type for dummy scheme.');
|
||||
|
||||
$instance = \Drupal::service('stream_wrapper_manager')->getViaScheme('public');
|
||||
$this->assertEqual('Drupal\Core\StreamWrapper\PublicStream', get_class($instance), 'Got correct class type for public scheme.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the getViaUri() and getViaScheme() methods and target functions.
|
||||
*/
|
||||
function testUriFunctions() {
|
||||
$config = $this->config('system.file');
|
||||
|
||||
$instance = \Drupal::service('stream_wrapper_manager')->getViaUri($this->scheme . '://foo');
|
||||
$this->assertEqual($this->classname, get_class($instance), 'Got correct class type for dummy URI.');
|
||||
|
||||
$instance = \Drupal::service('stream_wrapper_manager')->getViaUri('public://foo');
|
||||
$this->assertEqual('Drupal\Core\StreamWrapper\PublicStream', get_class($instance), 'Got correct class type for public URI.');
|
||||
|
||||
// Test file_uri_target().
|
||||
$this->assertEqual(file_uri_target('public://foo/bar.txt'), 'foo/bar.txt', 'Got a valid stream target from public://foo/bar.txt.');
|
||||
$this->assertEqual(file_uri_target('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='), 'image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==', t('Got a valid stream target from a data URI.'));
|
||||
$this->assertFalse(file_uri_target('foo/bar.txt'), 'foo/bar.txt is not a valid stream.');
|
||||
$this->assertFalse(file_uri_target('public://'), 'public:// has no target.');
|
||||
$this->assertFalse(file_uri_target('data:'), 'data: has no target.');
|
||||
|
||||
// Test file_build_uri() and
|
||||
// Drupal\Core\StreamWrapper\LocalStream::getDirectoryPath().
|
||||
$this->assertEqual(file_build_uri('foo/bar.txt'), 'public://foo/bar.txt', 'Expected scheme was added.');
|
||||
$this->assertEqual(\Drupal::service('stream_wrapper_manager')->getViaScheme('public')->getDirectoryPath(), PublicStream::basePath(), 'Expected default directory path was returned.');
|
||||
$this->assertEqual(\Drupal::service('stream_wrapper_manager')->getViaScheme('temporary')->getDirectoryPath(), $config->get('path.temporary'), 'Expected temporary directory path was returned.');
|
||||
$config->set('default_scheme', 'private')->save();
|
||||
$this->assertEqual(file_build_uri('foo/bar.txt'), 'private://foo/bar.txt', 'Got a valid URI from foo/bar.txt.');
|
||||
|
||||
// Test file_create_url()
|
||||
// TemporaryStream::getExternalUrl() uses Url::fromRoute(), which needs
|
||||
// route information to work.
|
||||
$this->container->get('router.builder')->rebuild();
|
||||
$this->assertTrue(strpos(file_create_url('temporary://test.txt'), 'system/temporary?file=test.txt'), 'Temporary external URL correctly built.');
|
||||
$this->assertTrue(strpos(file_create_url('public://test.txt'), Settings::get('file_public_path') . '/test.txt'), 'Public external URL correctly built.');
|
||||
$this->assertTrue(strpos(file_create_url('private://test.txt'), 'system/files/test.txt'), 'Private external URL correctly built.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test some file handle functions.
|
||||
*/
|
||||
function testFileFunctions() {
|
||||
$filename = 'public://' . $this->randomMachineName();
|
||||
file_put_contents($filename, str_repeat('d', 1000));
|
||||
|
||||
// Open for rw and place pointer at beginning of file so select will return.
|
||||
$handle = fopen($filename, 'c+');
|
||||
$this->assertTrue($handle, 'Able to open a file for appending, reading and writing.');
|
||||
|
||||
// Attempt to change options on the file stream: should all fail.
|
||||
$this->assertFalse(@stream_set_blocking($handle, 0), 'Unable to set to non blocking using a local stream wrapper.');
|
||||
$this->assertFalse(@stream_set_blocking($handle, 1), 'Unable to set to blocking using a local stream wrapper.');
|
||||
$this->assertFalse(@stream_set_timeout($handle, 1), 'Unable to set read time out using a local stream wrapper.');
|
||||
$this->assertEqual(-1 /*EOF*/, @stream_set_write_buffer($handle, 512), 'Unable to set write buffer using a local stream wrapper.');
|
||||
|
||||
// This will test stream_cast().
|
||||
$read = array($handle);
|
||||
$write = NULL;
|
||||
$except = NULL;
|
||||
$this->assertEqual(1, stream_select($read, $write, $except, 0), 'Able to cast a stream via stream_select.');
|
||||
|
||||
// This will test stream_truncate().
|
||||
$this->assertEqual(1, ftruncate($handle, 0), 'Able to truncate a stream via ftruncate().');
|
||||
fclose($handle);
|
||||
$this->assertEqual(0, filesize($filename), 'Able to truncate a stream.');
|
||||
|
||||
// Cleanup.
|
||||
unlink($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the scheme functions.
|
||||
*/
|
||||
function testGetValidStreamScheme() {
|
||||
$this->assertEqual('foo', file_uri_scheme('foo://pork//chops'), 'Got the correct scheme from foo://asdf');
|
||||
$this->assertEqual('data', file_uri_scheme('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='), 'Got the correct scheme from a data URI.');
|
||||
$this->assertFalse(file_uri_scheme('foo/bar.txt'), 'foo/bar.txt is not a valid stream.');
|
||||
$this->assertTrue(file_stream_wrapper_valid_scheme(file_uri_scheme('public://asdf')), 'Got a valid stream scheme from public://asdf');
|
||||
$this->assertFalse(file_stream_wrapper_valid_scheme(file_uri_scheme('foo://asdf')), 'Did not get a valid stream scheme from foo://asdf');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\File;
|
||||
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Drupal\Core\File\FileSystem;
|
||||
|
||||
/**
|
||||
* Tests the unmanaged file copy function.
|
||||
*
|
||||
* @group File
|
||||
*/
|
||||
class UnmanagedCopyTest extends FileTestBase {
|
||||
/**
|
||||
* Copy a normal file.
|
||||
*/
|
||||
function testNormal() {
|
||||
// Create a file for testing
|
||||
$uri = $this->createUri();
|
||||
|
||||
// Copying to a new name.
|
||||
$desired_filepath = 'public://' . $this->randomMachineName();
|
||||
$new_filepath = file_unmanaged_copy($uri, $desired_filepath, FILE_EXISTS_ERROR);
|
||||
$this->assertTrue($new_filepath, 'Copy was successful.');
|
||||
$this->assertEqual($new_filepath, $desired_filepath, 'Returned expected filepath.');
|
||||
$this->assertTrue(file_exists($uri), 'Original file remains.');
|
||||
$this->assertTrue(file_exists($new_filepath), 'New file exists.');
|
||||
$this->assertFilePermissions($new_filepath, Settings::get('file_chmod_file', FileSystem::CHMOD_FILE));
|
||||
|
||||
// Copying with rename.
|
||||
$desired_filepath = 'public://' . $this->randomMachineName();
|
||||
$this->assertTrue(file_put_contents($desired_filepath, ' '), 'Created a file so a rename will have to happen.');
|
||||
$newer_filepath = file_unmanaged_copy($uri, $desired_filepath, FILE_EXISTS_RENAME);
|
||||
$this->assertTrue($newer_filepath, 'Copy was successful.');
|
||||
$this->assertNotEqual($newer_filepath, $desired_filepath, 'Returned expected filepath.');
|
||||
$this->assertTrue(file_exists($uri), 'Original file remains.');
|
||||
$this->assertTrue(file_exists($newer_filepath), 'New file exists.');
|
||||
$this->assertFilePermissions($newer_filepath, Settings::get('file_chmod_file', FileSystem::CHMOD_FILE));
|
||||
|
||||
// TODO: test copying to a directory (rather than full directory/file path)
|
||||
// TODO: test copying normal files using normal paths (rather than only streams)
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a non-existent file.
|
||||
*/
|
||||
function testNonExistent() {
|
||||
// Copy non-existent file
|
||||
$desired_filepath = $this->randomMachineName();
|
||||
$this->assertFalse(file_exists($desired_filepath), "Randomly named file doesn't exists.");
|
||||
$new_filepath = file_unmanaged_copy($desired_filepath, $this->randomMachineName());
|
||||
$this->assertFalse($new_filepath, 'Copying a missing file fails.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a file onto itself.
|
||||
*/
|
||||
function testOverwriteSelf() {
|
||||
// Create a file for testing
|
||||
$uri = $this->createUri();
|
||||
|
||||
// Copy the file onto itself with renaming works.
|
||||
$new_filepath = file_unmanaged_copy($uri, $uri, FILE_EXISTS_RENAME);
|
||||
$this->assertTrue($new_filepath, 'Copying onto itself with renaming works.');
|
||||
$this->assertNotEqual($new_filepath, $uri, 'Copied file has a new name.');
|
||||
$this->assertTrue(file_exists($uri), 'Original file exists after copying onto itself.');
|
||||
$this->assertTrue(file_exists($new_filepath), 'Copied file exists after copying onto itself.');
|
||||
$this->assertFilePermissions($new_filepath, Settings::get('file_chmod_file', FileSystem::CHMOD_FILE));
|
||||
|
||||
// Copy the file onto itself without renaming fails.
|
||||
$new_filepath = file_unmanaged_copy($uri, $uri, FILE_EXISTS_ERROR);
|
||||
$this->assertFalse($new_filepath, 'Copying onto itself without renaming fails.');
|
||||
$this->assertTrue(file_exists($uri), 'File exists after copying onto itself.');
|
||||
|
||||
// Copy the file into same directory without renaming fails.
|
||||
$new_filepath = file_unmanaged_copy($uri, drupal_dirname($uri), FILE_EXISTS_ERROR);
|
||||
$this->assertFalse($new_filepath, 'Copying onto itself fails.');
|
||||
$this->assertTrue(file_exists($uri), 'File exists after copying onto itself.');
|
||||
|
||||
// Copy the file into same directory with renaming works.
|
||||
$new_filepath = file_unmanaged_copy($uri, drupal_dirname($uri), FILE_EXISTS_RENAME);
|
||||
$this->assertTrue($new_filepath, 'Copying into same directory works.');
|
||||
$this->assertNotEqual($new_filepath, $uri, 'Copied file has a new name.');
|
||||
$this->assertTrue(file_exists($uri), 'Original file exists after copying onto itself.');
|
||||
$this->assertTrue(file_exists($new_filepath), 'Copied file exists after copying onto itself.');
|
||||
$this->assertFilePermissions($new_filepath, Settings::get('file_chmod_file', FileSystem::CHMOD_FILE));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\File;
|
||||
|
||||
/**
|
||||
* Tests the unmanaged file delete recursive function.
|
||||
*
|
||||
* @group File
|
||||
*/
|
||||
class UnmanagedDeleteRecursiveTest extends FileTestBase {
|
||||
/**
|
||||
* Delete a normal file.
|
||||
*/
|
||||
function testSingleFile() {
|
||||
// Create a file for testing
|
||||
$filepath = file_default_scheme() . '://' . $this->randomMachineName();
|
||||
file_put_contents($filepath, '');
|
||||
|
||||
// Delete the file.
|
||||
$this->assertTrue(file_unmanaged_delete_recursive($filepath), 'Function reported success.');
|
||||
$this->assertFalse(file_exists($filepath), 'Test file has been deleted.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Try deleting an empty directory.
|
||||
*/
|
||||
function testEmptyDirectory() {
|
||||
// A directory to operate on.
|
||||
$directory = $this->createDirectory();
|
||||
|
||||
// Delete the directory.
|
||||
$this->assertTrue(file_unmanaged_delete_recursive($directory), 'Function reported success.');
|
||||
$this->assertFalse(file_exists($directory), 'Directory has been deleted.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Try deleting a directory with some files.
|
||||
*/
|
||||
function testDirectory() {
|
||||
// A directory to operate on.
|
||||
$directory = $this->createDirectory();
|
||||
$filepathA = $directory . '/A';
|
||||
$filepathB = $directory . '/B';
|
||||
file_put_contents($filepathA, '');
|
||||
file_put_contents($filepathB, '');
|
||||
|
||||
// Delete the directory.
|
||||
$this->assertTrue(file_unmanaged_delete_recursive($directory), 'Function reported success.');
|
||||
$this->assertFalse(file_exists($filepathA), 'Test file A has been deleted.');
|
||||
$this->assertFalse(file_exists($filepathB), 'Test file B has been deleted.');
|
||||
$this->assertFalse(file_exists($directory), 'Directory has been deleted.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Try deleting subdirectories with some files.
|
||||
*/
|
||||
function testSubDirectory() {
|
||||
// A directory to operate on.
|
||||
$directory = $this->createDirectory();
|
||||
$subdirectory = $this->createDirectory($directory . '/sub');
|
||||
$filepathA = $directory . '/A';
|
||||
$filepathB = $subdirectory . '/B';
|
||||
file_put_contents($filepathA, '');
|
||||
file_put_contents($filepathB, '');
|
||||
|
||||
// Delete the directory.
|
||||
$this->assertTrue(file_unmanaged_delete_recursive($directory), 'Function reported success.');
|
||||
$this->assertFalse(file_exists($filepathA), 'Test file A has been deleted.');
|
||||
$this->assertFalse(file_exists($filepathB), 'Test file B has been deleted.');
|
||||
$this->assertFalse(file_exists($subdirectory), 'Subdirectory has been deleted.');
|
||||
$this->assertFalse(file_exists($directory), 'Directory has been deleted.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\File;
|
||||
|
||||
/**
|
||||
* Tests the unmanaged file delete function.
|
||||
*
|
||||
* @group File
|
||||
*/
|
||||
class UnmanagedDeleteTest extends FileTestBase {
|
||||
/**
|
||||
* Delete a normal file.
|
||||
*/
|
||||
function testNormal() {
|
||||
// Create a file for testing
|
||||
$uri = $this->createUri();
|
||||
|
||||
// Delete a regular file
|
||||
$this->assertTrue(file_unmanaged_delete($uri), 'Deleted worked.');
|
||||
$this->assertFalse(file_exists($uri), 'Test file has actually been deleted.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Try deleting a missing file.
|
||||
*/
|
||||
function testMissing() {
|
||||
// Try to delete a non-existing file
|
||||
$this->assertTrue(file_unmanaged_delete(file_default_scheme() . '/' . $this->randomMachineName()), 'Returns true when deleting a non-existent file.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Try deleting a directory.
|
||||
*/
|
||||
function testDirectory() {
|
||||
// A directory to operate on.
|
||||
$directory = $this->createDirectory();
|
||||
|
||||
// Try to delete a directory
|
||||
$this->assertFalse(file_unmanaged_delete($directory), 'Could not delete the delete directory.');
|
||||
$this->assertTrue(file_exists($directory), 'Directory has not been deleted.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\File;
|
||||
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Drupal\Core\File\FileSystem;
|
||||
|
||||
/**
|
||||
* Tests the unmanaged file move function.
|
||||
*
|
||||
* @group File
|
||||
*/
|
||||
class UnmanagedMoveTest extends FileTestBase {
|
||||
/**
|
||||
* Move a normal file.
|
||||
*/
|
||||
function testNormal() {
|
||||
// Create a file for testing
|
||||
$uri = $this->createUri();
|
||||
|
||||
// Moving to a new name.
|
||||
$desired_filepath = 'public://' . $this->randomMachineName();
|
||||
$new_filepath = file_unmanaged_move($uri, $desired_filepath, FILE_EXISTS_ERROR);
|
||||
$this->assertTrue($new_filepath, 'Move was successful.');
|
||||
$this->assertEqual($new_filepath, $desired_filepath, 'Returned expected filepath.');
|
||||
$this->assertTrue(file_exists($new_filepath), 'File exists at the new location.');
|
||||
$this->assertFalse(file_exists($uri), 'No file remains at the old location.');
|
||||
$this->assertFilePermissions($new_filepath, Settings::get('file_chmod_file', FileSystem::CHMOD_FILE));
|
||||
|
||||
// Moving with rename.
|
||||
$desired_filepath = 'public://' . $this->randomMachineName();
|
||||
$this->assertTrue(file_exists($new_filepath), 'File exists before moving.');
|
||||
$this->assertTrue(file_put_contents($desired_filepath, ' '), 'Created a file so a rename will have to happen.');
|
||||
$newer_filepath = file_unmanaged_move($new_filepath, $desired_filepath, FILE_EXISTS_RENAME);
|
||||
$this->assertTrue($newer_filepath, 'Move was successful.');
|
||||
$this->assertNotEqual($newer_filepath, $desired_filepath, 'Returned expected filepath.');
|
||||
$this->assertTrue(file_exists($newer_filepath), 'File exists at the new location.');
|
||||
$this->assertFalse(file_exists($new_filepath), 'No file remains at the old location.');
|
||||
$this->assertFilePermissions($newer_filepath, Settings::get('file_chmod_file', FileSystem::CHMOD_FILE));
|
||||
|
||||
// TODO: test moving to a directory (rather than full directory/file path)
|
||||
// TODO: test creating and moving normal files (rather than streams)
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to move a missing file.
|
||||
*/
|
||||
function testMissing() {
|
||||
// Move non-existent file.
|
||||
$new_filepath = file_unmanaged_move($this->randomMachineName(), $this->randomMachineName());
|
||||
$this->assertFalse($new_filepath, 'Moving a missing file fails.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to move a file onto itself.
|
||||
*/
|
||||
function testOverwriteSelf() {
|
||||
// Create a file for testing.
|
||||
$uri = $this->createUri();
|
||||
|
||||
// Move the file onto itself without renaming shouldn't make changes.
|
||||
$new_filepath = file_unmanaged_move($uri, $uri, FILE_EXISTS_REPLACE);
|
||||
$this->assertFalse($new_filepath, 'Moving onto itself without renaming fails.');
|
||||
$this->assertTrue(file_exists($uri), 'File exists after moving onto itself.');
|
||||
|
||||
// Move the file onto itself with renaming will result in a new filename.
|
||||
$new_filepath = file_unmanaged_move($uri, $uri, FILE_EXISTS_RENAME);
|
||||
$this->assertTrue($new_filepath, 'Moving onto itself with renaming works.');
|
||||
$this->assertFalse(file_exists($uri), 'Original file has been removed.');
|
||||
$this->assertTrue(file_exists($new_filepath), 'File exists after moving onto itself.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\File;
|
||||
|
||||
/**
|
||||
* Tests the file_unmanaged_save_data() function.
|
||||
*
|
||||
* @group File
|
||||
*/
|
||||
class UnmanagedSaveDataTest extends FileTestBase {
|
||||
/**
|
||||
* Test the file_unmanaged_save_data() function.
|
||||
*/
|
||||
function testFileSaveData() {
|
||||
$contents = $this->randomMachineName(8);
|
||||
$this->setSetting('file_chmod_file', 0777);
|
||||
|
||||
// No filename.
|
||||
$filepath = file_unmanaged_save_data($contents);
|
||||
$this->assertTrue($filepath, 'Unnamed file saved correctly.');
|
||||
$this->assertEqual(file_uri_scheme($filepath), file_default_scheme(), "File was placed in Drupal's files directory.");
|
||||
$this->assertEqual($contents, file_get_contents($filepath), 'Contents of the file are correct.');
|
||||
|
||||
// Provide a filename.
|
||||
$filepath = file_unmanaged_save_data($contents, 'public://asdf.txt', FILE_EXISTS_REPLACE);
|
||||
$this->assertTrue($filepath, 'Unnamed file saved correctly.');
|
||||
$this->assertEqual('asdf.txt', drupal_basename($filepath), 'File was named correctly.');
|
||||
$this->assertEqual($contents, file_get_contents($filepath), 'Contents of the file are correct.');
|
||||
$this->assertFilePermissions($filepath, 0777);
|
||||
}
|
||||
|
||||
}
|
118
core/tests/Drupal/KernelTests/Core/File/UrlRewritingTest.php
Normal file
118
core/tests/Drupal/KernelTests/Core/File/UrlRewritingTest.php
Normal file
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\File;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Tests for file URL rewriting.
|
||||
*
|
||||
* @group File
|
||||
*/
|
||||
class UrlRewritingTest extends FileTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('file_test');
|
||||
|
||||
/**
|
||||
* Tests the rewriting of shipped file URLs by hook_file_url_alter().
|
||||
*/
|
||||
function testShippedFileURL() {
|
||||
// Test generating a URL to a shipped file (i.e. a file that is part of
|
||||
// Drupal core, a module or a theme, for example a JavaScript file).
|
||||
|
||||
// Test alteration of file URLs to use a CDN.
|
||||
\Drupal::state()->set('file_test.hook_file_url_alter', 'cdn');
|
||||
$filepath = 'core/assets/vendor/jquery/jquery.min.js';
|
||||
$url = file_create_url($filepath);
|
||||
$this->assertEqual(FILE_URL_TEST_CDN_1 . '/' . $filepath, $url, 'Correctly generated a CDN URL for a shipped file.');
|
||||
$filepath = 'core/misc/favicon.ico';
|
||||
$url = file_create_url($filepath);
|
||||
$this->assertEqual(FILE_URL_TEST_CDN_2 . '/' . $filepath, $url, 'Correctly generated a CDN URL for a shipped file.');
|
||||
|
||||
// Test alteration of file URLs to use root-relative URLs.
|
||||
\Drupal::state()->set('file_test.hook_file_url_alter', 'root-relative');
|
||||
$filepath = 'core/assets/vendor/jquery/jquery.min.js';
|
||||
$url = file_create_url($filepath);
|
||||
$this->assertEqual(base_path() . '/' . $filepath, $url, 'Correctly generated a root-relative URL for a shipped file.');
|
||||
$filepath = 'core/misc/favicon.ico';
|
||||
$url = file_create_url($filepath);
|
||||
$this->assertEqual(base_path() . '/' . $filepath, $url, 'Correctly generated a root-relative URL for a shipped file.');
|
||||
|
||||
// Test alteration of file URLs to use protocol-relative URLs.
|
||||
\Drupal::state()->set('file_test.hook_file_url_alter', 'protocol-relative');
|
||||
$filepath = 'core/assets/vendor/jquery/jquery.min.js';
|
||||
$url = file_create_url($filepath);
|
||||
$this->assertEqual('/' . base_path() . '/' . $filepath, $url, 'Correctly generated a protocol-relative URL for a shipped file.');
|
||||
$filepath = 'core/misc/favicon.ico';
|
||||
$url = file_create_url($filepath);
|
||||
$this->assertEqual('/' . base_path() . '/' . $filepath, $url, 'Correctly generated a protocol-relative URL for a shipped file.');
|
||||
|
||||
// Test alteration of file URLs with query strings and/or fragment.
|
||||
\Drupal::state()->delete('file_test.hook_file_url_alter');
|
||||
$filepath = 'core/misc/favicon.ico';
|
||||
$url = file_create_url($filepath . '?foo');
|
||||
$this->assertEqual($GLOBALS['base_url'] . '/' . $filepath . '?foo=', $url, 'Correctly generated URL. The query string is present.');
|
||||
$url = file_create_url($filepath . '?foo=bar');
|
||||
$this->assertEqual($GLOBALS['base_url'] . '/' . $filepath . '?foo=bar', $url, 'Correctly generated URL. The query string is present.');
|
||||
$url = file_create_url($filepath . '#v1.2');
|
||||
$this->assertEqual($GLOBALS['base_url'] . '/' . $filepath . '#v1.2', $url, 'Correctly generated URL. The fragment is present.');
|
||||
$url = file_create_url($filepath . '?foo=bar#v1.2');
|
||||
$this->assertEqual($GLOBALS['base_url'] . '/' . $filepath . '?foo=bar#v1.2', $url, 'Correctly generated URL. The query string amd fragment is present.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the rewriting of public managed file URLs by hook_file_url_alter().
|
||||
*/
|
||||
function testPublicManagedFileURL() {
|
||||
// Test generating a URL to a managed file.
|
||||
|
||||
// Test alteration of file URLs to use a CDN.
|
||||
\Drupal::state()->set('file_test.hook_file_url_alter', 'cdn');
|
||||
$uri = $this->createUri();
|
||||
$url = file_create_url($uri);
|
||||
$public_directory_path = \Drupal::service('stream_wrapper_manager')->getViaScheme('public')->getDirectoryPath();
|
||||
$this->assertEqual(FILE_URL_TEST_CDN_2 . '/' . $public_directory_path . '/' . drupal_basename($uri), $url, 'Correctly generated a CDN URL for a created file.');
|
||||
|
||||
// Test alteration of file URLs to use root-relative URLs.
|
||||
\Drupal::state()->set('file_test.hook_file_url_alter', 'root-relative');
|
||||
$uri = $this->createUri();
|
||||
$url = file_create_url($uri);
|
||||
$this->assertEqual(base_path() . '/' . $public_directory_path . '/' . drupal_basename($uri), $url, 'Correctly generated a root-relative URL for a created file.');
|
||||
|
||||
// Test alteration of file URLs to use a protocol-relative URLs.
|
||||
\Drupal::state()->set('file_test.hook_file_url_alter', 'protocol-relative');
|
||||
$uri = $this->createUri();
|
||||
$url = file_create_url($uri);
|
||||
$this->assertEqual('/' . base_path() . '/' . $public_directory_path . '/' . drupal_basename($uri), $url, 'Correctly generated a protocol-relative URL for a created file.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test file_url_transform_relative().
|
||||
*/
|
||||
function testRelativeFileURL() {
|
||||
// Disable file_test.module's hook_file_url_alter() implementation.
|
||||
\Drupal::state()->set('file_test.hook_file_url_alter', NULL);
|
||||
|
||||
// Create a mock Request for file_url_transform_relative().
|
||||
$request = Request::create($GLOBALS['base_url']);
|
||||
$this->container->get('request_stack')->push($request);
|
||||
\Drupal::setContainer($this->container);
|
||||
|
||||
// Shipped file.
|
||||
$filepath = 'core/assets/vendor/jquery/jquery.min.js';
|
||||
$url = file_create_url($filepath);
|
||||
$this->assertIdentical(base_path() . $filepath, file_url_transform_relative($url));
|
||||
|
||||
// Managed file.
|
||||
$uri = $this->createUri();
|
||||
$url = file_create_url($uri);
|
||||
$public_directory_path = \Drupal::service('stream_wrapper_manager')->getViaScheme('public')->getDirectoryPath();
|
||||
$this->assertIdentical(base_path() . $public_directory_path . '/' . rawurlencode(drupal_basename($uri)), file_url_transform_relative($url));
|
||||
}
|
||||
|
||||
}
|
106
core/tests/Drupal/KernelTests/Core/Form/ExternalFormUrlTest.php
Normal file
106
core/tests/Drupal/KernelTests/Core/Form/ExternalFormUrlTest.php
Normal file
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Form;
|
||||
|
||||
use Drupal\Core\Form\FormInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Ensures that form actions can't be tricked into sending to external URLs.
|
||||
*
|
||||
* @group system
|
||||
*/
|
||||
class ExternalFormUrlTest extends KernelTestBase implements FormInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['user', 'system'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'external_form_url_test';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$form['something'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => 'What do you think?',
|
||||
];
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installSchema('system', ['key_value_expire', 'sequences']);
|
||||
$this->installEntitySchema('user');
|
||||
|
||||
$test_user = User::create([
|
||||
'name' => 'foobar',
|
||||
'mail' => 'foobar@example.com',
|
||||
]);
|
||||
$test_user->save();
|
||||
\Drupal::service('current_user')->setAccount($test_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests form behaviour.
|
||||
*/
|
||||
public function testActionUrlBehavior() {
|
||||
// Create a new request which has a request uri with multiple leading
|
||||
// slashes and make it the master request.
|
||||
$request_stack = \Drupal::service('request_stack');
|
||||
/** @var \Symfony\Component\HttpFoundation\RequestStack $original_request */
|
||||
$original_request = $request_stack->pop();
|
||||
// Just request some more so there is no request left.
|
||||
$request_stack->pop();
|
||||
$request_stack->pop();
|
||||
$request = Request::create($original_request->getSchemeAndHttpHost() . '//example.org');
|
||||
$request_stack->push($request);
|
||||
|
||||
$form = \Drupal::formBuilder()->getForm($this);
|
||||
$markup = \Drupal::service('renderer')->renderRoot($form);
|
||||
|
||||
$this->setRawContent($markup);
|
||||
$elements = $this->xpath('//form/@action');
|
||||
$action = (string) $elements[0];
|
||||
$this->assertEqual($original_request->getSchemeAndHttpHost() . '//example.org', $action);
|
||||
|
||||
// Create a new request which has a request uri with a single leading slash
|
||||
// and make it the master request.
|
||||
$request_stack = \Drupal::service('request_stack');
|
||||
$original_request = $request_stack->pop();
|
||||
$request = Request::create($original_request->getSchemeAndHttpHost() . '/example.org');
|
||||
$request_stack->push($request);
|
||||
|
||||
$form = \Drupal::formBuilder()->getForm($this);
|
||||
$markup = \Drupal::service('renderer')->renderRoot($form);
|
||||
|
||||
$this->setRawContent($markup);
|
||||
$elements = $this->xpath('//form/@action');
|
||||
$action = (string) $elements[0];
|
||||
$this->assertEqual('/example.org', $action);
|
||||
}
|
||||
|
||||
}
|
104
core/tests/Drupal/KernelTests/Core/Form/FormCacheTest.php
Normal file
104
core/tests/Drupal/KernelTests/Core/Form/FormCacheTest.php
Normal file
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Form;
|
||||
|
||||
use Drupal\Core\Form\FormState;
|
||||
use Drupal\Core\Session\AnonymousUserSession;
|
||||
use Drupal\Core\Session\UserSession;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests \Drupal::formBuilder()->setCache() and
|
||||
* \Drupal::formBuilder()->getCache().
|
||||
*
|
||||
* @group Form
|
||||
*/
|
||||
class FormCacheTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system', 'user');
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $formBuildId;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $form;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $formState;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installSchema('system', array('key_value_expire'));
|
||||
|
||||
$this->formBuildId = $this->randomMachineName();
|
||||
$this->form = array(
|
||||
'#property' => $this->randomMachineName(),
|
||||
);
|
||||
$this->formState = new FormState();
|
||||
$this->formState->set('example', $this->randomMachineName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the form cache with a logged-in user.
|
||||
*/
|
||||
function testCacheToken() {
|
||||
\Drupal::currentUser()->setAccount(new UserSession(array('uid' => 1)));
|
||||
\Drupal::formBuilder()->setCache($this->formBuildId, $this->form, $this->formState);
|
||||
|
||||
$cached_form_state = new FormState();
|
||||
$cached_form = \Drupal::formBuilder()->getCache($this->formBuildId, $cached_form_state);
|
||||
$this->assertEqual($this->form['#property'], $cached_form['#property']);
|
||||
$this->assertTrue(!empty($cached_form['#cache_token']), 'Form has a cache token');
|
||||
$this->assertEqual($this->formState->get('example'), $cached_form_state->get('example'));
|
||||
|
||||
// Test that the form cache isn't loaded when the session/token has changed.
|
||||
// Change the private key. (We cannot change the session ID because this
|
||||
// will break the parent site test runner batch.)
|
||||
\Drupal::state()->set('system.private_key', 'invalid');
|
||||
$cached_form_state = new FormState();
|
||||
$cached_form = \Drupal::formBuilder()->getCache($this->formBuildId, $cached_form_state);
|
||||
$this->assertFalse($cached_form, 'No form returned from cache');
|
||||
$cached_form_state_example = $cached_form_state->get('example');
|
||||
$this->assertTrue(empty($cached_form_state_example));
|
||||
|
||||
// Test that loading the cache with a different form_id fails.
|
||||
$wrong_form_build_id = $this->randomMachineName(9);
|
||||
$cached_form_state = new FormState();
|
||||
$this->assertFalse(\Drupal::formBuilder()->getCache($wrong_form_build_id, $cached_form_state), 'No form returned from cache');
|
||||
$cached_form_state_example = $cached_form_state->get('example');
|
||||
$this->assertTrue(empty($cached_form_state_example), 'Cached form state was not loaded');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the form cache without a logged-in user.
|
||||
*/
|
||||
function testNoCacheToken() {
|
||||
// Switch to a anonymous user account.
|
||||
$account_switcher = \Drupal::service('account_switcher');
|
||||
$account_switcher->switchTo(new AnonymousUserSession());
|
||||
|
||||
$this->formState->set('example', $this->randomMachineName());
|
||||
\Drupal::formBuilder()->setCache($this->formBuildId, $this->form, $this->formState);
|
||||
|
||||
$cached_form_state = new FormState();
|
||||
$cached_form = \Drupal::formBuilder()->getCache($this->formBuildId, $cached_form_state);
|
||||
$this->assertEqual($this->form['#property'], $cached_form['#property']);
|
||||
$this->assertTrue(empty($cached_form['#cache_token']), 'Form has no cache token');
|
||||
$this->assertEqual($this->formState->get('example'), $cached_form_state->get('example'));
|
||||
|
||||
// Restore user account.
|
||||
$account_switcher->switchBack();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Form;
|
||||
|
||||
use Drupal\Core\Form\FormInterface;
|
||||
use Drupal\Core\Form\FormState;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests automatically added form handlers.
|
||||
*
|
||||
* @group Form
|
||||
*/
|
||||
class FormDefaultHandlersTest extends KernelTestBase implements FormInterface {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installSchema('system', ['key_value_expire']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'test_form_handlers';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$form['#validate'][] = '::customValidateForm';
|
||||
$form['#submit'][] = '::customSubmitForm';
|
||||
$form['submit'] = array('#type' => 'submit', '#value' => 'Save');
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function customValidateForm(array &$form, FormStateInterface $form_state) {
|
||||
$test_handlers = $form_state->get('test_handlers');
|
||||
$test_handlers['validate'][] = __FUNCTION__;
|
||||
$form_state->set('test_handlers', $test_handlers);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
$test_handlers = $form_state->get('test_handlers');
|
||||
$test_handlers['validate'][] = __FUNCTION__;
|
||||
$form_state->set('test_handlers', $test_handlers);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function customSubmitForm(array &$form, FormStateInterface $form_state) {
|
||||
$test_handlers = $form_state->get('test_handlers');
|
||||
$test_handlers['submit'][] = __FUNCTION__;
|
||||
$form_state->set('test_handlers', $test_handlers);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$test_handlers = $form_state->get('test_handlers');
|
||||
$test_handlers['submit'][] = __FUNCTION__;
|
||||
$form_state->set('test_handlers', $test_handlers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that default handlers are added even if custom are specified.
|
||||
*/
|
||||
function testDefaultAndCustomHandlers() {
|
||||
$form_state = new FormState();
|
||||
$form_builder = $this->container->get('form_builder');
|
||||
$form_builder->submitForm($this, $form_state);
|
||||
|
||||
$handlers = $form_state->get('test_handlers');
|
||||
|
||||
$this->assertIdentical(count($handlers['validate']), 2);
|
||||
$this->assertIdentical($handlers['validate'][0], 'customValidateForm');
|
||||
$this->assertIdentical($handlers['validate'][1], 'validateForm');
|
||||
|
||||
$this->assertIdentical(count($handlers['submit']), 2);
|
||||
$this->assertIdentical($handlers['submit'][0], 'customSubmitForm');
|
||||
$this->assertIdentical($handlers['submit'][1], 'submitForm');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Form;
|
||||
|
||||
use Drupal\Core\Form\FormInterface;
|
||||
use Drupal\Core\Form\FormState;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests detection of triggering_element for programmed form submissions.
|
||||
*
|
||||
* @group Form
|
||||
*/
|
||||
class TriggeringElementProgrammedTest extends KernelTestBase implements FormInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'triggering_element_programmed_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$form['one'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => 'One',
|
||||
'#required' => TRUE,
|
||||
);
|
||||
$form['two'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => 'Two',
|
||||
'#required' => TRUE,
|
||||
);
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$user_input = $form_state->getUserInput();
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => 'Save',
|
||||
'#limit_validation_errors' => array(
|
||||
array($user_input['section']),
|
||||
),
|
||||
// Required for #limit_validation_errors.
|
||||
'#submit' => array(array($this, 'submitForm')),
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
// Verify that the only submit button was recognized as triggering_element.
|
||||
$this->assertEqual($form['actions']['submit']['#array_parents'], $form_state->getTriggeringElement()['#array_parents']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that #limit_validation_errors of the only submit button takes effect.
|
||||
*/
|
||||
function testLimitValidationErrors() {
|
||||
// Programmatically submit the form.
|
||||
$form_state = new FormState();
|
||||
$form_state->setValue('section', 'one');
|
||||
$form_builder = $this->container->get('form_builder');
|
||||
$form_builder->submitForm($this, $form_state);
|
||||
|
||||
// Verify that only the specified section was validated.
|
||||
$errors = $form_state->getErrors();
|
||||
$this->assertTrue(isset($errors['one']), "Section 'one' was validated.");
|
||||
$this->assertFalse(isset($errors['two']), "Section 'two' was not validated.");
|
||||
|
||||
// Verify that there are only values for the specified section.
|
||||
$this->assertTrue($form_state->hasValue('one'), "Values for section 'one' found.");
|
||||
$this->assertFalse($form_state->hasValue('two'), "Values for section 'two' not found.");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\HttpKernel;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\DependencyInjection\ServiceModifierInterface;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Tests CORS provided by Drupal.
|
||||
*
|
||||
* @see sites/default/default.services.yml
|
||||
* @see \Asm89\Stack\Cors
|
||||
* @see \Asm89\Stack\CorsService
|
||||
*
|
||||
* @group Http
|
||||
*/
|
||||
class CorsIntegrationTest extends KernelTestBase implements ServiceModifierInterface {
|
||||
|
||||
/**
|
||||
* The cors container configuration.
|
||||
*
|
||||
* @var null|array
|
||||
*/
|
||||
protected $corsConfig = NULL;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['system', 'test_page_test'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installSchema('system', 'router');
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
}
|
||||
|
||||
public function testCrossSiteRequest() {
|
||||
|
||||
// Test default parameters.
|
||||
$cors_config = $this->container->getParameter('cors.config');
|
||||
$this->assertSame(FALSE, $cors_config['enabled']);
|
||||
$this->assertSame([], $cors_config['allowedHeaders']);
|
||||
$this->assertSame([], $cors_config['allowedMethods']);
|
||||
$this->assertSame(['*'], $cors_config['allowedOrigins']);
|
||||
|
||||
$this->assertSame(FALSE, $cors_config['exposedHeaders']);
|
||||
$this->assertSame(FALSE, $cors_config['maxAge']);
|
||||
$this->assertSame(FALSE, $cors_config['supportsCredentials']);
|
||||
|
||||
// Configure the CORS stack to allow a specific set of origins, but don't
|
||||
// specify an origin header.
|
||||
$request = Request::create('/test-page');
|
||||
$request->headers->set('Origin', '');
|
||||
$cors_config['enabled'] = TRUE;
|
||||
$cors_config['allowedOrigins'] = ['http://example.com'];
|
||||
|
||||
$this->corsConfig = $cors_config;
|
||||
$this->container->get('kernel')->rebuildContainer();
|
||||
|
||||
/** @var \Symfony\Component\HttpFoundation\Response $response */
|
||||
$response = $this->container->get('http_kernel')->handle($request);
|
||||
$this->assertEquals(Response::HTTP_FORBIDDEN, $response->getStatusCode());
|
||||
$this->assertEquals('Not allowed.', $response->getContent());
|
||||
|
||||
// Specify a valid origin.
|
||||
$request->headers->set('Origin', 'http://example.com');
|
||||
$response = $this->container->get('http_kernel')->handle($request);
|
||||
$this->assertEquals(Response::HTTP_OK, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function alter(ContainerBuilder $container) {
|
||||
if (isset($this->corsConfig)) {
|
||||
$container->setParameter('cors.config', $this->corsConfig);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\HttpKernel;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
|
||||
/**
|
||||
* Tests the stacked kernel functionality.
|
||||
*
|
||||
* @group Routing
|
||||
*/
|
||||
class StackKernelIntegrationTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('httpkernel_test', 'system');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a request.
|
||||
*/
|
||||
public function testRequest() {
|
||||
$request = Request::create((new Url('httpkernel_test.empty'))->toString());
|
||||
/** @var \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel */
|
||||
$http_kernel = \Drupal::service('http_kernel');
|
||||
$http_kernel->handle($request, HttpKernelInterface::MASTER_REQUEST, FALSE);
|
||||
|
||||
$this->assertEqual($request->attributes->get('_hello'), 'world');
|
||||
$this->assertEqual($request->attributes->get('_previous_optional_argument'), 'test_argument');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that late middlewares are automatically flagged lazy.
|
||||
*/
|
||||
public function testLazyLateMiddlewares() {
|
||||
$this->assertFalse($this->container->getDefinition('http_middleware.reverse_proxy')->isLazy(), 'lazy flag on http_middleware.reverse_proxy definition is not set');
|
||||
$this->assertFalse($this->container->getDefinition('http_middleware.kernel_pre_handle')->isLazy(), 'lazy flag on http_middleware.kernel_pre_handle definition is not set');
|
||||
$this->assertFalse($this->container->getDefinition('http_middleware.session')->isLazy(), 'lazy flag on http_middleware.session definition is not set');
|
||||
$this->assertFalse($this->container->getDefinition('http_kernel.basic')->isLazy(), 'lazy flag on http_kernel.basic definition is not set');
|
||||
|
||||
\Drupal::service('module_installer')->install(['page_cache']);
|
||||
$this->container = \Drupal::service('kernel')->rebuildContainer();
|
||||
|
||||
$this->assertFalse($this->container->getDefinition('http_middleware.reverse_proxy')->isLazy(), 'lazy flag on http_middleware.reverse_proxy definition is not set');
|
||||
$this->assertFalse($this->container->getDefinition('http_middleware.page_cache')->isLazy(), 'lazy flag on http_middleware.page_cache definition is not set');
|
||||
$this->assertTrue($this->container->getDefinition('http_middleware.kernel_pre_handle')->isLazy(), 'lazy flag on http_middleware.kernel_pre_handle definition is automatically set if page_cache is enabled.');
|
||||
$this->assertTrue($this->container->getDefinition('http_middleware.session')->isLazy(), 'lazy flag on http_middleware.session definition is automatically set if page_cache is enabled.');
|
||||
$this->assertTrue($this->container->getDefinition('http_kernel.basic')->isLazy(), 'lazy flag on http_kernel.basic definition is automatically set if page_cache is enabled.');
|
||||
}
|
||||
|
||||
}
|
529
core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php
Normal file
529
core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php
Normal file
|
@ -0,0 +1,529 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Image;
|
||||
|
||||
use Drupal\Core\Image\ImageInterface;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests that core image manipulations work properly: scale, resize, rotate,
|
||||
* crop, scale and crop, and desaturate.
|
||||
*
|
||||
* @group Image
|
||||
*/
|
||||
class ToolkitGdTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* The image factory service.
|
||||
*
|
||||
* @var \Drupal\Core\Image\ImageFactory
|
||||
*/
|
||||
protected $imageFactory;
|
||||
|
||||
// Colors that are used in testing.
|
||||
protected $black = array(0, 0, 0, 0);
|
||||
protected $red = array(255, 0, 0, 0);
|
||||
protected $green = array(0, 255, 0, 0);
|
||||
protected $blue = array(0, 0, 255, 0);
|
||||
protected $yellow = array(255, 255, 0, 0);
|
||||
protected $white = array(255, 255, 255, 0);
|
||||
protected $transparent = array(0, 0, 0, 127);
|
||||
// Used as rotate background colors.
|
||||
protected $fuchsia = array(255, 0, 255, 0);
|
||||
protected $rotateTransparent = array(255, 255, 255, 127);
|
||||
|
||||
protected $width = 40;
|
||||
protected $height = 20;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system', 'simpletest');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Set the image factory service.
|
||||
$this->imageFactory = $this->container->get('image.factory');
|
||||
}
|
||||
|
||||
protected function checkRequirements() {
|
||||
// GD2 support is available.
|
||||
if (!function_exists('imagegd2')) {
|
||||
return array(
|
||||
'Image manipulations for the GD toolkit cannot run because the GD toolkit is not available.',
|
||||
);
|
||||
}
|
||||
return parent::checkRequirements();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to compare two colors by RGBa.
|
||||
*/
|
||||
function colorsAreEqual($color_a, $color_b) {
|
||||
// Fully transparent pixels are equal, regardless of RGB.
|
||||
if ($color_a[3] == 127 && $color_b[3] == 127) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
foreach ($color_a as $key => $value) {
|
||||
if ($color_b[$key] != $value) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for finding a pixel's RGBa values.
|
||||
*/
|
||||
function getPixelColor(ImageInterface $image, $x, $y) {
|
||||
$toolkit = $image->getToolkit();
|
||||
$color_index = imagecolorat($toolkit->getResource(), $x, $y);
|
||||
|
||||
$transparent_index = imagecolortransparent($toolkit->getResource());
|
||||
if ($color_index == $transparent_index) {
|
||||
return array(0, 0, 0, 127);
|
||||
}
|
||||
|
||||
return array_values(imagecolorsforindex($toolkit->getResource(), $color_index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Since PHP can't visually check that our images have been manipulated
|
||||
* properly, build a list of expected color values for each of the corners and
|
||||
* the expected height and widths for the final images.
|
||||
*/
|
||||
function testManipulations() {
|
||||
|
||||
// Test that the image factory is set to use the GD toolkit.
|
||||
$this->assertEqual($this->imageFactory->getToolkitId(), 'gd', 'The image factory is set to use the \'gd\' image toolkit.');
|
||||
|
||||
// Test the list of supported extensions.
|
||||
$expected_extensions = ['png', 'gif', 'jpeg', 'jpg', 'jpe'];
|
||||
$supported_extensions = $this->imageFactory->getSupportedExtensions();
|
||||
$this->assertEqual($expected_extensions, array_intersect($expected_extensions, $supported_extensions));
|
||||
|
||||
// Test that the supported extensions map to correct internal GD image
|
||||
// types.
|
||||
$expected_image_types = [
|
||||
'png' => IMAGETYPE_PNG,
|
||||
'gif' => IMAGETYPE_GIF,
|
||||
'jpeg' => IMAGETYPE_JPEG,
|
||||
'jpg' => IMAGETYPE_JPEG,
|
||||
'jpe' => IMAGETYPE_JPEG
|
||||
];
|
||||
$image = $this->imageFactory->get();
|
||||
foreach ($expected_image_types as $extension => $expected_image_type) {
|
||||
$image_type = $image->getToolkit()->extensionToImageType($extension);
|
||||
$this->assertSame($expected_image_type, $image_type);
|
||||
}
|
||||
|
||||
// Typically the corner colors will be unchanged. These colors are in the
|
||||
// order of top-left, top-right, bottom-right, bottom-left.
|
||||
$default_corners = array($this->red, $this->green, $this->blue, $this->transparent);
|
||||
|
||||
// A list of files that will be tested.
|
||||
$files = array(
|
||||
'image-test.png',
|
||||
'image-test.gif',
|
||||
'image-test-no-transparency.gif',
|
||||
'image-test.jpg',
|
||||
);
|
||||
|
||||
// Setup a list of tests to perform on each type.
|
||||
$operations = array(
|
||||
'resize' => array(
|
||||
'function' => 'resize',
|
||||
'arguments' => array('width' => 20, 'height' => 10),
|
||||
'width' => 20,
|
||||
'height' => 10,
|
||||
'corners' => $default_corners,
|
||||
),
|
||||
'scale_x' => array(
|
||||
'function' => 'scale',
|
||||
'arguments' => array('width' => 20),
|
||||
'width' => 20,
|
||||
'height' => 10,
|
||||
'corners' => $default_corners,
|
||||
),
|
||||
'scale_y' => array(
|
||||
'function' => 'scale',
|
||||
'arguments' => array('height' => 10),
|
||||
'width' => 20,
|
||||
'height' => 10,
|
||||
'corners' => $default_corners,
|
||||
),
|
||||
'upscale_x' => array(
|
||||
'function' => 'scale',
|
||||
'arguments' => array('width' => 80, 'upscale' => TRUE),
|
||||
'width' => 80,
|
||||
'height' => 40,
|
||||
'corners' => $default_corners,
|
||||
),
|
||||
'upscale_y' => array(
|
||||
'function' => 'scale',
|
||||
'arguments' => array('height' => 40, 'upscale' => TRUE),
|
||||
'width' => 80,
|
||||
'height' => 40,
|
||||
'corners' => $default_corners,
|
||||
),
|
||||
'crop' => array(
|
||||
'function' => 'crop',
|
||||
'arguments' => array('x' => 12, 'y' => 4, 'width' => 16, 'height' => 12),
|
||||
'width' => 16,
|
||||
'height' => 12,
|
||||
'corners' => array_fill(0, 4, $this->white),
|
||||
),
|
||||
'scale_and_crop' => array(
|
||||
'function' => 'scale_and_crop',
|
||||
'arguments' => array('width' => 10, 'height' => 8),
|
||||
'width' => 10,
|
||||
'height' => 8,
|
||||
'corners' => array_fill(0, 4, $this->black),
|
||||
),
|
||||
'convert_jpg' => array(
|
||||
'function' => 'convert',
|
||||
'width' => 40,
|
||||
'height' => 20,
|
||||
'arguments' => array('extension' => 'jpeg'),
|
||||
'corners' => $default_corners,
|
||||
),
|
||||
'convert_gif' => array(
|
||||
'function' => 'convert',
|
||||
'width' => 40,
|
||||
'height' => 20,
|
||||
'arguments' => array('extension' => 'gif'),
|
||||
'corners' => $default_corners,
|
||||
),
|
||||
'convert_png' => array(
|
||||
'function' => 'convert',
|
||||
'width' => 40,
|
||||
'height' => 20,
|
||||
'arguments' => array('extension' => 'png'),
|
||||
'corners' => $default_corners,
|
||||
),
|
||||
);
|
||||
|
||||
// Systems using non-bundled GD2 don't have imagerotate. Test if available.
|
||||
if (function_exists('imagerotate')) {
|
||||
$operations += array(
|
||||
'rotate_5' => array(
|
||||
'function' => 'rotate',
|
||||
'arguments' => array('degrees' => 5, 'background' => '#FF00FF'), // Fuchsia background.
|
||||
'width' => 41,
|
||||
'height' => 23,
|
||||
'corners' => array_fill(0, 4, $this->fuchsia),
|
||||
),
|
||||
'rotate_90' => array(
|
||||
'function' => 'rotate',
|
||||
'arguments' => array('degrees' => 90, 'background' => '#FF00FF'), // Fuchsia background.
|
||||
'width' => 20,
|
||||
'height' => 40,
|
||||
'corners' => array($this->transparent, $this->red, $this->green, $this->blue),
|
||||
),
|
||||
'rotate_transparent_5' => array(
|
||||
'function' => 'rotate',
|
||||
'arguments' => array('degrees' => 5),
|
||||
'width' => 41,
|
||||
'height' => 23,
|
||||
'corners' => array_fill(0, 4, $this->rotateTransparent),
|
||||
),
|
||||
'rotate_transparent_90' => array(
|
||||
'function' => 'rotate',
|
||||
'arguments' => array('degrees' => 90),
|
||||
'width' => 20,
|
||||
'height' => 40,
|
||||
'corners' => array($this->transparent, $this->red, $this->green, $this->blue),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Systems using non-bundled GD2 don't have imagefilter. Test if available.
|
||||
if (function_exists('imagefilter')) {
|
||||
$operations += array(
|
||||
'desaturate' => array(
|
||||
'function' => 'desaturate',
|
||||
'arguments' => array(),
|
||||
'height' => 20,
|
||||
'width' => 40,
|
||||
// Grayscale corners are a bit funky. Each of the corners are a shade of
|
||||
// gray. The values of these were determined simply by looking at the
|
||||
// final image to see what desaturated colors end up being.
|
||||
'corners' => array(
|
||||
array_fill(0, 3, 76) + array(3 => 0),
|
||||
array_fill(0, 3, 149) + array(3 => 0),
|
||||
array_fill(0, 3, 29) + array(3 => 0),
|
||||
array_fill(0, 3, 225) + array(3 => 127)
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Prepare a directory for test file results.
|
||||
$directory = Settings::get('file_public_path') . '/imagetest';
|
||||
file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
|
||||
|
||||
foreach ($files as $file) {
|
||||
foreach ($operations as $op => $values) {
|
||||
// Load up a fresh image.
|
||||
$image = $this->imageFactory->get(drupal_get_path('module', 'simpletest') . '/files/' . $file);
|
||||
$toolkit = $image->getToolkit();
|
||||
if (!$image->isValid()) {
|
||||
$this->fail(SafeMarkup::format('Could not load image %file.', array('%file' => $file)));
|
||||
continue 2;
|
||||
}
|
||||
$image_original_type = $image->getToolkit()->getType();
|
||||
|
||||
// All images should be converted to truecolor when loaded.
|
||||
$image_truecolor = imageistruecolor($toolkit->getResource());
|
||||
$this->assertTrue($image_truecolor, SafeMarkup::format('Image %file after load is a truecolor image.', array('%file' => $file)));
|
||||
|
||||
// Store the original GD resource.
|
||||
$old_res = $toolkit->getResource();
|
||||
|
||||
// Perform our operation.
|
||||
$image->apply($values['function'], $values['arguments']);
|
||||
|
||||
// If the operation replaced the resource, check that the old one has
|
||||
// been destroyed.
|
||||
$new_res = $toolkit->getResource();
|
||||
if ($new_res !== $old_res) {
|
||||
$this->assertFalse(is_resource($old_res), SafeMarkup::format("'%operation' destroyed the original resource.", ['%operation' => $values['function']]));
|
||||
}
|
||||
|
||||
// To keep from flooding the test with assert values, make a general
|
||||
// value for whether each group of values fail.
|
||||
$correct_dimensions_real = TRUE;
|
||||
$correct_dimensions_object = TRUE;
|
||||
|
||||
if (imagesy($toolkit->getResource()) != $values['height'] || imagesx($toolkit->getResource()) != $values['width']) {
|
||||
$correct_dimensions_real = FALSE;
|
||||
}
|
||||
|
||||
// Check that the image object has an accurate record of the dimensions.
|
||||
if ($image->getWidth() != $values['width'] || $image->getHeight() != $values['height']) {
|
||||
$correct_dimensions_object = FALSE;
|
||||
}
|
||||
|
||||
$file_path = $directory . '/' . $op . image_type_to_extension($image->getToolkit()->getType());
|
||||
$image->save($file_path);
|
||||
|
||||
$this->assertTrue($correct_dimensions_real, SafeMarkup::format('Image %file after %action action has proper dimensions.', array('%file' => $file, '%action' => $op)));
|
||||
$this->assertTrue($correct_dimensions_object, SafeMarkup::format('Image %file object after %action action is reporting the proper height and width values.', array('%file' => $file, '%action' => $op)));
|
||||
|
||||
// JPEG colors will always be messed up due to compression. So we skip
|
||||
// these tests if the original or the result is in jpeg format.
|
||||
if ($image->getToolkit()->getType() != IMAGETYPE_JPEG && $image_original_type != IMAGETYPE_JPEG) {
|
||||
// Now check each of the corners to ensure color correctness.
|
||||
foreach ($values['corners'] as $key => $corner) {
|
||||
// The test gif that does not have transparency color set is a
|
||||
// special case.
|
||||
if ($file === 'image-test-no-transparency.gif') {
|
||||
if ($op == 'desaturate') {
|
||||
// For desaturating, keep the expected color from the test
|
||||
// data, but set alpha channel to fully opaque.
|
||||
$corner[3] = 0;
|
||||
}
|
||||
elseif ($corner === $this->transparent) {
|
||||
// Set expected pixel to yellow where the others have
|
||||
// transparent.
|
||||
$corner = $this->yellow;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the location of the corner.
|
||||
switch ($key) {
|
||||
case 0:
|
||||
$x = 0;
|
||||
$y = 0;
|
||||
break;
|
||||
case 1:
|
||||
$x = $image->getWidth() - 1;
|
||||
$y = 0;
|
||||
break;
|
||||
case 2:
|
||||
$x = $image->getWidth() - 1;
|
||||
$y = $image->getHeight() - 1;
|
||||
break;
|
||||
case 3:
|
||||
$x = 0;
|
||||
$y = $image->getHeight() - 1;
|
||||
break;
|
||||
}
|
||||
$color = $this->getPixelColor($image, $x, $y);
|
||||
// We also skip the color test for transparency for gif <-> png
|
||||
// conversion. The convert operation cannot handle that correctly.
|
||||
if ($image->getToolkit()->getType() == $image_original_type || $corner != $this->transparent) {
|
||||
$correct_colors = $this->colorsAreEqual($color, $corner);
|
||||
$this->assertTrue($correct_colors, SafeMarkup::format('Image %file object after %action action has the correct color placement at corner %corner.',
|
||||
array('%file' => $file, '%action' => $op, '%corner' => $key)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that saved image reloads without raising PHP errors.
|
||||
$image_reloaded = $this->imageFactory->get($file_path);
|
||||
$resource = $image_reloaded->getToolkit()->getResource();
|
||||
}
|
||||
}
|
||||
|
||||
// Test creation of image from scratch, and saving to storage.
|
||||
foreach (array(IMAGETYPE_PNG, IMAGETYPE_GIF, IMAGETYPE_JPEG) as $type) {
|
||||
$image = $this->imageFactory->get();
|
||||
$image->createNew(50, 20, image_type_to_extension($type, FALSE), '#ffff00');
|
||||
$file = 'from_null' . image_type_to_extension($type);
|
||||
$file_path = $directory . '/' . $file ;
|
||||
$this->assertEqual(50, $image->getWidth(), SafeMarkup::format('Image file %file has the correct width.', array('%file' => $file)));
|
||||
$this->assertEqual(20, $image->getHeight(), SafeMarkup::format('Image file %file has the correct height.', array('%file' => $file)));
|
||||
$this->assertEqual(image_type_to_mime_type($type), $image->getMimeType(), SafeMarkup::format('Image file %file has the correct MIME type.', array('%file' => $file)));
|
||||
$this->assertTrue($image->save($file_path), SafeMarkup::format('Image %file created anew from a null image was saved.', array('%file' => $file)));
|
||||
|
||||
// Reload saved image.
|
||||
$image_reloaded = $this->imageFactory->get($file_path);
|
||||
if (!$image_reloaded->isValid()) {
|
||||
$this->fail(SafeMarkup::format('Could not load image %file.', array('%file' => $file)));
|
||||
continue;
|
||||
}
|
||||
$this->assertEqual(50, $image_reloaded->getWidth(), SafeMarkup::format('Image file %file has the correct width.', array('%file' => $file)));
|
||||
$this->assertEqual(20, $image_reloaded->getHeight(), SafeMarkup::format('Image file %file has the correct height.', array('%file' => $file)));
|
||||
$this->assertEqual(image_type_to_mime_type($type), $image_reloaded->getMimeType(), SafeMarkup::format('Image file %file has the correct MIME type.', array('%file' => $file)));
|
||||
if ($image_reloaded->getToolkit()->getType() == IMAGETYPE_GIF) {
|
||||
$this->assertEqual('#ffff00', $image_reloaded->getToolkit()->getTransparentColor(), SafeMarkup::format('Image file %file has the correct transparent color channel set.', array('%file' => $file)));
|
||||
}
|
||||
else {
|
||||
$this->assertEqual(NULL, $image_reloaded->getToolkit()->getTransparentColor(), SafeMarkup::format('Image file %file has no color channel set.', array('%file' => $file)));
|
||||
}
|
||||
}
|
||||
|
||||
// Test failures of the 'create_new' operation.
|
||||
$image = $this->imageFactory->get();
|
||||
$image->createNew(-50, 20);
|
||||
$this->assertFalse($image->isValid(), 'CreateNew with negative width fails.');
|
||||
$image->createNew(50, 20, 'foo');
|
||||
$this->assertFalse($image->isValid(), 'CreateNew with invalid extension fails.');
|
||||
$image->createNew(50, 20, 'gif', '#foo');
|
||||
$this->assertFalse($image->isValid(), 'CreateNew with invalid color hex string fails.');
|
||||
$image->createNew(50, 20, 'gif', '#ff0000');
|
||||
$this->assertTrue($image->isValid(), 'CreateNew with valid arguments validates the Image.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that GD resources are freed from memory.
|
||||
*/
|
||||
public function testResourceDestruction() {
|
||||
// Test that an Image object going out of scope releases its GD resource.
|
||||
$image = $this->imageFactory->get(drupal_get_path('module', 'simpletest') . '/files/image-test.png');
|
||||
$res = $image->getToolkit()->getResource();
|
||||
$this->assertTrue(is_resource($res), 'Successfully loaded image resource.');
|
||||
$image = NULL;
|
||||
$this->assertFalse(is_resource($res), 'Image resource was destroyed after losing scope.');
|
||||
|
||||
// Test that 'create_new' operation does not leave orphaned GD resources.
|
||||
$image = $this->imageFactory->get(drupal_get_path('module', 'simpletest') . '/files/image-test.png');
|
||||
$old_res = $image->getToolkit()->getResource();
|
||||
// Check if resource has been created successfully.
|
||||
$this->assertTrue(is_resource($old_res));
|
||||
$image->createNew(20, 20);
|
||||
$new_res = $image->getToolkit()->getResource();
|
||||
// Check if the original resource has been destroyed.
|
||||
$this->assertFalse(is_resource($old_res));
|
||||
// Check if a new resource has been created successfully.
|
||||
$this->assertTrue(is_resource($new_res));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for GIF images with transparency.
|
||||
*/
|
||||
function testGifTransparentImages() {
|
||||
// Prepare a directory for test file results.
|
||||
$directory = Settings::get('file_public_path') . '/imagetest';
|
||||
file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
|
||||
|
||||
// Test loading an indexed GIF image with transparent color set.
|
||||
// Color at top-right pixel should be fully transparent.
|
||||
$file = 'image-test-transparent-indexed.gif';
|
||||
$image = $this->imageFactory->get(drupal_get_path('module', 'simpletest') . '/files/' . $file);
|
||||
$resource = $image->getToolkit()->getResource();
|
||||
$color_index = imagecolorat($resource, $image->getWidth() - 1, 0);
|
||||
$color = array_values(imagecolorsforindex($resource, $color_index));
|
||||
$this->assertEqual($this->rotateTransparent, $color, "Image {$file} after load has full transparent color at corner 1.");
|
||||
|
||||
// Test deliberately creating a GIF image with no transparent color set.
|
||||
// Color at top-right pixel should be fully transparent while in memory,
|
||||
// fully opaque after flushing image to file.
|
||||
$file = 'image-test-no-transparent-color-set.gif';
|
||||
$file_path = $directory . '/' . $file ;
|
||||
// Create image.
|
||||
$image = $this->imageFactory->get();
|
||||
$image->createNew(50, 20, 'gif', NULL);
|
||||
$resource = $image->getToolkit()->getResource();
|
||||
$color_index = imagecolorat($resource, $image->getWidth() - 1, 0);
|
||||
$color = array_values(imagecolorsforindex($resource, $color_index));
|
||||
$this->assertEqual($this->rotateTransparent, $color, "New GIF image with no transparent color set after creation has full transparent color at corner 1.");
|
||||
// Save image.
|
||||
$this->assertTrue($image->save($file_path), "New GIF image {$file} was saved.");
|
||||
// Reload image.
|
||||
$image_reloaded = $this->imageFactory->get($file_path);
|
||||
$resource = $image_reloaded->getToolkit()->getResource();
|
||||
$color_index = imagecolorat($resource, $image_reloaded->getWidth() - 1, 0);
|
||||
$color = array_values(imagecolorsforindex($resource, $color_index));
|
||||
// Check explicitly for alpha == 0 as the rest of the color has been
|
||||
// compressed and may have slight difference from full white.
|
||||
$this->assertEqual(0, $color[3], "New GIF image {$file} after reload has no transparent color at corner 1.");
|
||||
|
||||
// Test loading an image whose transparent color index is out of range.
|
||||
// This image was generated by taking an initial image with a palette size
|
||||
// of 6 colors, and setting the transparent color index to 6 (one higher
|
||||
// than the largest allowed index), as follows:
|
||||
// @code
|
||||
// $image = imagecreatefromgif('core/modules/simpletest/files/image-test.gif');
|
||||
// imagecolortransparent($image, 6);
|
||||
// imagegif($image, 'core/modules/simpletest/files/image-test-transparent-out-of-range.gif');
|
||||
// @endcode
|
||||
// This allows us to test that an image with an out-of-range color index
|
||||
// can be loaded correctly.
|
||||
$file = 'image-test-transparent-out-of-range.gif';
|
||||
$image = $this->imageFactory->get(drupal_get_path('module', 'simpletest') . '/files/' . $file);
|
||||
$toolkit = $image->getToolkit();
|
||||
|
||||
if (!$image->isValid()) {
|
||||
$this->fail(SafeMarkup::format('Could not load image %file.', array('%file' => $file)));
|
||||
}
|
||||
else {
|
||||
// All images should be converted to truecolor when loaded.
|
||||
$image_truecolor = imageistruecolor($toolkit->getResource());
|
||||
$this->assertTrue($image_truecolor, SafeMarkup::format('Image %file after load is a truecolor image.', array('%file' => $file)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests calling a missing image operation plugin.
|
||||
*/
|
||||
function testMissingOperation() {
|
||||
|
||||
// Test that the image factory is set to use the GD toolkit.
|
||||
$this->assertEqual($this->imageFactory->getToolkitId(), 'gd', 'The image factory is set to use the \'gd\' image toolkit.');
|
||||
|
||||
// An image file that will be tested.
|
||||
$file = 'image-test.png';
|
||||
|
||||
// Load up a fresh image.
|
||||
$image = $this->imageFactory->get(drupal_get_path('module', 'simpletest') . '/files/' . $file);
|
||||
if (!$image->isValid()) {
|
||||
$this->fail(SafeMarkup::format('Could not load image %file.', array('%file' => $file)));
|
||||
}
|
||||
|
||||
// Try perform a missing toolkit operation.
|
||||
$this->assertFalse($image->apply('missing_op', array()), 'Calling a missing image toolkit operation plugin fails.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Installer;
|
||||
|
||||
use Drupal\Core\StringTranslation\Translator\FileTranslation;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests for installer language support.
|
||||
*
|
||||
* @group Installer
|
||||
*/
|
||||
class InstallerLanguageTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Tests that the installer can find translation files.
|
||||
*/
|
||||
function testInstallerTranslationFiles() {
|
||||
// Different translation files would be found depending on which language
|
||||
// we are looking for.
|
||||
$expected_translation_files = array(
|
||||
NULL => array('drupal-8.0.0-beta2.hu.po', 'drupal-8.0.0.de.po'),
|
||||
'de' => array('drupal-8.0.0.de.po'),
|
||||
'hu' => array('drupal-8.0.0-beta2.hu.po'),
|
||||
'it' => array(),
|
||||
);
|
||||
|
||||
// Hardcode the simpletest module location as we don't yet know where it is.
|
||||
// @todo Remove as part of https://www.drupal.org/node/2186491
|
||||
$file_translation = new FileTranslation('core/modules/simpletest/files/translations');
|
||||
foreach ($expected_translation_files as $langcode => $files_expected) {
|
||||
$files_found = $file_translation->findTranslationFiles($langcode);
|
||||
$this->assertTrue(count($files_found) == count($files_expected), format_string('@count installer languages found.', array('@count' => count($files_expected))));
|
||||
foreach ($files_found as $file) {
|
||||
$this->assertTrue(in_array($file->filename, $files_expected), format_string('@file found.', array('@file' => $file->filename)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests profile info caching in non-English languages.
|
||||
*/
|
||||
function testInstallerTranslationCache() {
|
||||
require_once 'core/includes/install.inc';
|
||||
|
||||
// Prime the drupal_get_filename() static cache with the location of the
|
||||
// testing profile as it is not the currently active profile and we don't
|
||||
// yet have any cached way to retrieve its location.
|
||||
// @todo Remove as part of https://www.drupal.org/node/2186491
|
||||
drupal_get_filename('profile', 'testing', 'core/profiles/testing/testing.info.yml');
|
||||
|
||||
$info_en = install_profile_info('testing', 'en');
|
||||
$info_nl = install_profile_info('testing', 'nl');
|
||||
|
||||
$this->assertFalse(in_array('locale', $info_en['dependencies']), 'Locale is not set when installing in English.');
|
||||
$this->assertTrue(in_array('locale', $info_nl['dependencies']), 'Locale is set when installing in Dutch.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\KeyValueStore;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\KeyValueStore\KeyValueFactory;
|
||||
|
||||
/**
|
||||
* Tests the key-value database storage.
|
||||
*
|
||||
* @group KeyValueStore
|
||||
*/
|
||||
class DatabaseStorageExpirableTest extends StorageTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->factory = 'keyvalue.expirable';
|
||||
$this->installSchema('system', array('key_value_expire'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function register(ContainerBuilder $container) {
|
||||
parent::register($container);
|
||||
|
||||
$parameter[KeyValueFactory::DEFAULT_SETTING] = 'keyvalue.expirable.database';
|
||||
$container->setParameter('factory.keyvalue.expirable', $parameter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests CRUD functionality with expiration.
|
||||
*/
|
||||
public function testCRUDWithExpiration() {
|
||||
$stores = $this->createStorage();
|
||||
|
||||
// Verify that an item can be stored with setWithExpire().
|
||||
// Use a random expiration in each test.
|
||||
$stores[0]->setWithExpire('foo', $this->objects[0], rand(500, 100000));
|
||||
$this->assertIdenticalObject($this->objects[0], $stores[0]->get('foo'));
|
||||
// Verify that the other collection is not affected.
|
||||
$this->assertFalse($stores[1]->get('foo'));
|
||||
|
||||
// Verify that an item can be updated with setWithExpire().
|
||||
$stores[0]->setWithExpire('foo', $this->objects[1], rand(500, 100000));
|
||||
$this->assertIdenticalObject($this->objects[1], $stores[0]->get('foo'));
|
||||
// Verify that the other collection is still not affected.
|
||||
$this->assertFalse($stores[1]->get('foo'));
|
||||
|
||||
// Verify that the expirable data key is unique.
|
||||
$stores[1]->setWithExpire('foo', $this->objects[2], rand(500, 100000));
|
||||
$this->assertIdenticalObject($this->objects[1], $stores[0]->get('foo'));
|
||||
$this->assertIdenticalObject($this->objects[2], $stores[1]->get('foo'));
|
||||
|
||||
// Verify that multiple items can be stored with setMultipleWithExpire().
|
||||
$values = array(
|
||||
'foo' => $this->objects[3],
|
||||
'bar' => $this->objects[4],
|
||||
);
|
||||
$stores[0]->setMultipleWithExpire($values, rand(500, 100000));
|
||||
$result = $stores[0]->getMultiple(array('foo', 'bar'));
|
||||
foreach ($values as $j => $value) {
|
||||
$this->assertIdenticalObject($value, $result[$j]);
|
||||
}
|
||||
|
||||
// Verify that the other collection was not affected.
|
||||
$this->assertIdenticalObject($stores[1]->get('foo'), $this->objects[2]);
|
||||
$this->assertFalse($stores[1]->get('bar'));
|
||||
|
||||
// Verify that all items in a collection can be retrieved.
|
||||
// Ensure that an item with the same name exists in the other collection.
|
||||
$stores[1]->set('foo', $this->objects[5]);
|
||||
$result = $stores[0]->getAll();
|
||||
// Not using assertIdentical(), since the order is not defined for getAll().
|
||||
$this->assertEqual(count($result), count($values));
|
||||
foreach ($result as $key => $value) {
|
||||
$this->assertEqual($values[$key], $value);
|
||||
}
|
||||
// Verify that all items in the other collection are different.
|
||||
$result = $stores[1]->getAll();
|
||||
$this->assertEqual($result, array('foo' => $this->objects[5]));
|
||||
|
||||
// Verify that multiple items can be deleted.
|
||||
$stores[0]->deleteMultiple(array_keys($values));
|
||||
$this->assertFalse($stores[0]->get('foo'));
|
||||
$this->assertFalse($stores[0]->get('bar'));
|
||||
$this->assertFalse($stores[0]->getMultiple(array('foo', 'bar')));
|
||||
// Verify that the item in the other collection still exists.
|
||||
$this->assertIdenticalObject($this->objects[5], $stores[1]->get('foo'));
|
||||
|
||||
// Test that setWithExpireIfNotExists() succeeds only the first time.
|
||||
$key = $this->randomMachineName();
|
||||
for ($i = 0; $i <= 1; $i++) {
|
||||
// setWithExpireIfNotExists() should be TRUE the first time (when $i is
|
||||
// 0) and FALSE the second time (when $i is 1).
|
||||
$this->assertEqual(!$i, $stores[0]->setWithExpireIfNotExists($key, $this->objects[$i], rand(500, 100000)));
|
||||
$this->assertIdenticalObject($this->objects[0], $stores[0]->get($key));
|
||||
// Verify that the other collection is not affected.
|
||||
$this->assertFalse($stores[1]->get($key));
|
||||
}
|
||||
|
||||
// Remove the item and try to set it again.
|
||||
$stores[0]->delete($key);
|
||||
$stores[0]->setWithExpireIfNotExists($key, $this->objects[1], rand(500, 100000));
|
||||
// This time it should succeed.
|
||||
$this->assertIdenticalObject($this->objects[1], $stores[0]->get($key));
|
||||
// Verify that the other collection is still not affected.
|
||||
$this->assertFalse($stores[1]->get($key));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests data expiration.
|
||||
*/
|
||||
public function testExpiration() {
|
||||
$stores = $this->createStorage();
|
||||
$day = 604800;
|
||||
|
||||
// Set an item to expire in the past and another without an expiration.
|
||||
$stores[0]->setWithExpire('yesterday', 'all my troubles seemed so far away', -1 * $day);
|
||||
$stores[0]->set('troubles', 'here to stay');
|
||||
|
||||
// Only the non-expired item should be returned.
|
||||
$this->assertFalse($stores[0]->has('yesterday'));
|
||||
$this->assertFalse($stores[0]->get('yesterday'));
|
||||
$this->assertTrue($stores[0]->has('troubles'));
|
||||
$this->assertIdentical($stores[0]->get('troubles'), 'here to stay');
|
||||
$this->assertIdentical(count($stores[0]->getMultiple(array('yesterday', 'troubles'))), 1);
|
||||
|
||||
// Store items set to expire in the past in various ways.
|
||||
$stores[0]->setWithExpire($this->randomMachineName(), $this->objects[0], -7 * $day);
|
||||
$stores[0]->setWithExpireIfNotExists($this->randomMachineName(), $this->objects[1], -5 * $day);
|
||||
$stores[0]->setMultipleWithExpire(
|
||||
array(
|
||||
$this->randomMachineName() => $this->objects[2],
|
||||
$this->randomMachineName() => $this->objects[3],
|
||||
),
|
||||
-3 * $day
|
||||
);
|
||||
$stores[0]->setWithExpireIfNotExists('yesterday', "you'd forgiven me", -1 * $day);
|
||||
$stores[0]->setWithExpire('still', "'til we say we're sorry", 2 * $day);
|
||||
|
||||
// Ensure only non-expired items are retrieved.
|
||||
$all = $stores[0]->getAll();
|
||||
$this->assertIdentical(count($all), 2);
|
||||
foreach (array('troubles', 'still') as $key) {
|
||||
$this->assertTrue(!empty($all[$key]));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\KeyValueStore;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\KeyValueStore\KeyValueFactory;
|
||||
|
||||
/**
|
||||
* Tests the key-value database storage.
|
||||
*
|
||||
* @group KeyValueStore
|
||||
*/
|
||||
class DatabaseStorageTest extends StorageTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installSchema('system', array('key_value'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function register(ContainerBuilder $container) {
|
||||
parent::register($container);
|
||||
|
||||
$parameter[KeyValueFactory::DEFAULT_SETTING] = 'keyvalue.database';
|
||||
$container->setParameter('factory.keyvalue', $parameter);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\KeyValueStore;
|
||||
|
||||
use Drupal\Component\Serialization\PhpSerialize;
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\KeyValueStore\DatabaseStorageExpirable;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests garbage collection for the expirable key-value database storage.
|
||||
*
|
||||
* @group KeyValueStore
|
||||
*/
|
||||
class GarbageCollectionTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// These additional tables are necessary due to the call to system_cron().
|
||||
$this->installSchema('system', array('key_value_expire'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests garbage collection.
|
||||
*/
|
||||
public function testGarbageCollection() {
|
||||
$collection = $this->randomMachineName();
|
||||
$store = new DatabaseStorageExpirable($collection, new PhpSerialize(), Database::getConnection());
|
||||
|
||||
// Insert some items and confirm that they're set.
|
||||
for ($i = 0; $i <= 3; $i++) {
|
||||
$store->setWithExpire('key_' . $i, $this->randomObject(), rand(500, 100000));
|
||||
}
|
||||
$this->assertIdentical(sizeof($store->getAll()), 4, 'Four items were written to the storage.');
|
||||
|
||||
// Manually expire the data.
|
||||
for ($i = 0; $i <= 3; $i++) {
|
||||
db_merge('key_value_expire')
|
||||
->keys(array(
|
||||
'name' => 'key_' . $i,
|
||||
'collection' => $collection,
|
||||
))
|
||||
->fields(array(
|
||||
'expire' => REQUEST_TIME - 1,
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
|
||||
|
||||
// Perform a new set operation and then trigger garbage collection.
|
||||
$store->setWithExpire('autumn', 'winter', rand(500, 1000000));
|
||||
system_cron();
|
||||
|
||||
// Query the database and confirm that the stale records were deleted.
|
||||
$result = db_query(
|
||||
'SELECT name, value FROM {key_value_expire} WHERE collection = :collection',
|
||||
array(
|
||||
':collection' => $collection,
|
||||
))->fetchAll();
|
||||
$this->assertIdentical(count($result), 1, 'Only one item remains after garbage collection');
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\KeyValueStore;
|
||||
|
||||
use Drupal\Core\Entity\EntityMalformedException;
|
||||
use Drupal\Core\Entity\EntityStorageException;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\entity_test\Entity\EntityTestLabel;
|
||||
|
||||
/**
|
||||
* Tests KeyValueEntityStorage for content entities.
|
||||
*
|
||||
* @group KeyValueStore
|
||||
*/
|
||||
class KeyValueContentEntityStorageTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('user', 'entity_test', 'keyvalue_test');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('user');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests CRUD operations.
|
||||
*/
|
||||
function testCRUD() {
|
||||
$default_langcode = \Drupal::languageManager()->getDefaultLanguage()->getId();
|
||||
// Verify default properties on a newly created empty entity.
|
||||
$empty = EntityTestLabel::create();
|
||||
$this->assertIdentical($empty->id->value, NULL);
|
||||
$this->assertIdentical($empty->name->value, NULL);
|
||||
$this->assertTrue($empty->uuid->value);
|
||||
$this->assertIdentical($empty->langcode->value, $default_langcode);
|
||||
|
||||
// Verify ConfigEntity properties/methods on the newly created empty entity.
|
||||
$this->assertIdentical($empty->isNew(), TRUE);
|
||||
$this->assertIdentical($empty->bundle(), 'entity_test_label');
|
||||
$this->assertIdentical($empty->id(), NULL);
|
||||
$this->assertTrue($empty->uuid());
|
||||
$this->assertIdentical($empty->label(), NULL);
|
||||
|
||||
// Verify Entity properties/methods on the newly created empty entity.
|
||||
$this->assertIdentical($empty->getEntityTypeId(), 'entity_test_label');
|
||||
// The URI can only be checked after saving.
|
||||
try {
|
||||
$empty->urlInfo();
|
||||
$this->fail('EntityMalformedException was thrown.');
|
||||
}
|
||||
catch (EntityMalformedException $e) {
|
||||
$this->pass('EntityMalformedException was thrown.');
|
||||
}
|
||||
|
||||
// Verify that an empty entity cannot be saved.
|
||||
try {
|
||||
$empty->save();
|
||||
$this->fail('EntityMalformedException was thrown.');
|
||||
}
|
||||
catch (EntityMalformedException $e) {
|
||||
$this->pass('EntityMalformedException was thrown.');
|
||||
}
|
||||
|
||||
// Verify that an entity with an empty ID string is considered empty, too.
|
||||
$empty_id = EntityTestLabel::create(array(
|
||||
'id' => '',
|
||||
));
|
||||
$this->assertIdentical($empty_id->isNew(), TRUE);
|
||||
try {
|
||||
$empty_id->save();
|
||||
$this->fail('EntityMalformedException was thrown.');
|
||||
}
|
||||
catch (EntityMalformedException $e) {
|
||||
$this->pass('EntityMalformedException was thrown.');
|
||||
}
|
||||
|
||||
// Verify properties on a newly created entity.
|
||||
$entity_test = EntityTestLabel::create($expected = array(
|
||||
'id' => $this->randomMachineName(),
|
||||
'name' => $this->randomString(),
|
||||
));
|
||||
$this->assertIdentical($entity_test->id->value, $expected['id']);
|
||||
$this->assertTrue($entity_test->uuid->value);
|
||||
$this->assertNotEqual($entity_test->uuid->value, $empty->uuid->value);
|
||||
$this->assertIdentical($entity_test->name->value, $expected['name']);
|
||||
$this->assertIdentical($entity_test->langcode->value, $default_langcode);
|
||||
|
||||
// Verify methods on the newly created entity.
|
||||
$this->assertIdentical($entity_test->isNew(), TRUE);
|
||||
$this->assertIdentical($entity_test->id(), $expected['id']);
|
||||
$this->assertTrue($entity_test->uuid());
|
||||
$expected['uuid'] = $entity_test->uuid();
|
||||
$this->assertIdentical($entity_test->label(), $expected['name']);
|
||||
|
||||
// Verify that the entity can be saved.
|
||||
try {
|
||||
$status = $entity_test->save();
|
||||
$this->pass('EntityMalformedException was not thrown.');
|
||||
}
|
||||
catch (EntityMalformedException $e) {
|
||||
$this->fail('EntityMalformedException was not thrown.');
|
||||
}
|
||||
|
||||
// Verify that the correct status is returned and properties did not change.
|
||||
$this->assertIdentical($status, SAVED_NEW);
|
||||
$this->assertIdentical($entity_test->id(), $expected['id']);
|
||||
$this->assertIdentical($entity_test->uuid(), $expected['uuid']);
|
||||
$this->assertIdentical($entity_test->label(), $expected['name']);
|
||||
$this->assertIdentical($entity_test->isNew(), FALSE);
|
||||
|
||||
// Save again, and verify correct status and properties again.
|
||||
$status = $entity_test->save();
|
||||
$this->assertIdentical($status, SAVED_UPDATED);
|
||||
$this->assertIdentical($entity_test->id(), $expected['id']);
|
||||
$this->assertIdentical($entity_test->uuid(), $expected['uuid']);
|
||||
$this->assertIdentical($entity_test->label(), $expected['name']);
|
||||
$this->assertIdentical($entity_test->isNew(), FALSE);
|
||||
|
||||
// Ensure that creating an entity with the same id as an existing one is not
|
||||
// possible.
|
||||
$same_id = EntityTestLabel::create(array(
|
||||
'id' => $entity_test->id(),
|
||||
));
|
||||
$this->assertIdentical($same_id->isNew(), TRUE);
|
||||
try {
|
||||
$same_id->save();
|
||||
$this->fail('Not possible to overwrite an entity entity.');
|
||||
}
|
||||
catch (EntityStorageException $e) {
|
||||
$this->pass('Not possible to overwrite an entity entity.');
|
||||
}
|
||||
|
||||
// Verify that renaming the ID returns correct status and properties.
|
||||
$ids = array($expected['id'], 'second_' . $this->randomMachineName(4), 'third_' . $this->randomMachineName(4));
|
||||
for ($i = 1; $i < 3; $i++) {
|
||||
$old_id = $ids[$i - 1];
|
||||
$new_id = $ids[$i];
|
||||
// Before renaming, everything should point to the current ID.
|
||||
$this->assertIdentical($entity_test->id(), $old_id);
|
||||
|
||||
// Rename.
|
||||
$entity_test->id = $new_id;
|
||||
$this->assertIdentical($entity_test->id(), $new_id);
|
||||
$status = $entity_test->save();
|
||||
$this->assertIdentical($status, SAVED_UPDATED);
|
||||
$this->assertIdentical($entity_test->isNew(), FALSE);
|
||||
|
||||
// Verify that originalID points to new ID directly after renaming.
|
||||
$this->assertIdentical($entity_test->id(), $new_id);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\KeyValueStore;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\KeyValueStore\KeyValueFactory;
|
||||
|
||||
/**
|
||||
* Tests the key-value memory storage.
|
||||
*
|
||||
* @group KeyValueStore
|
||||
*/
|
||||
class MemoryStorageTest extends StorageTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function register(ContainerBuilder $container) {
|
||||
parent::register($container);
|
||||
|
||||
$container->register('keyvalue.memory', 'Drupal\Core\KeyValueStore\KeyValueMemoryFactory');
|
||||
$parameter[KeyValueFactory::DEFAULT_SETTING] = 'keyvalue.memory';
|
||||
$container->setParameter('factory.keyvalue', $parameter);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,215 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\KeyValueStore;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Base class for testing key-value storages.
|
||||
*/
|
||||
abstract class StorageTestBase extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* An array of random stdClass objects.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $objects = array();
|
||||
|
||||
/**
|
||||
* An array of data collection labels.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $collections = array();
|
||||
|
||||
/**
|
||||
* Whether we are using an expirable key/value store.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $factory = 'keyvalue';
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Define two data collections,
|
||||
$this->collections = array(0 => 'zero', 1 => 'one');
|
||||
|
||||
// Create several objects for testing.
|
||||
for ($i = 0; $i <= 5; $i++) {
|
||||
$this->objects[$i] = $this->randomObject();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests CRUD operations.
|
||||
*/
|
||||
public function testCRUD() {
|
||||
$stores = $this->createStorage();
|
||||
// Verify that each store returns its own collection name.
|
||||
$this->assertIdentical($stores[0]->getCollectionName(), $this->collections[0]);
|
||||
$this->assertIdentical($stores[1]->getCollectionName(), $this->collections[1]);
|
||||
|
||||
// Verify that an item can be stored.
|
||||
$stores[0]->set('foo', $this->objects[0]);
|
||||
$this->assertTrue($stores[0]->has('foo'));
|
||||
$this->assertIdenticalObject($this->objects[0], $stores[0]->get('foo'));
|
||||
// Verify that the other collection is not affected.
|
||||
$this->assertFalse($stores[1]->has('foo'));
|
||||
$this->assertFalse($stores[1]->get('foo'));
|
||||
|
||||
// Verify that an item can be updated.
|
||||
$stores[0]->set('foo', $this->objects[1]);
|
||||
$this->assertIdenticalObject($this->objects[1], $stores[0]->get('foo'));
|
||||
// Verify that the other collection is still not affected.
|
||||
$this->assertFalse($stores[1]->get('foo'));
|
||||
|
||||
// Verify that a collection/name pair is unique.
|
||||
$stores[1]->set('foo', $this->objects[2]);
|
||||
$this->assertIdenticalObject($this->objects[1], $stores[0]->get('foo'));
|
||||
$this->assertIdenticalObject($this->objects[2], $stores[1]->get('foo'));
|
||||
|
||||
// Verify that an item can be deleted.
|
||||
$stores[0]->delete('foo');
|
||||
$this->assertFalse($stores[0]->has('foo'));
|
||||
$this->assertFalse($stores[0]->get('foo'));
|
||||
|
||||
// Verify that the other collection is not affected.
|
||||
$this->assertTrue($stores[1]->has('foo'));
|
||||
$this->assertIdenticalObject($this->objects[2], $stores[1]->get('foo'));
|
||||
$stores[1]->delete('foo');
|
||||
$this->assertFalse($stores[1]->get('foo'));
|
||||
|
||||
// Verify that multiple items can be stored.
|
||||
$values = array(
|
||||
'foo' => $this->objects[3],
|
||||
'bar' => $this->objects[4],
|
||||
);
|
||||
$stores[0]->setMultiple($values);
|
||||
|
||||
// Verify that multiple items can be retrieved.
|
||||
$result = $stores[0]->getMultiple(array('foo', 'bar'));
|
||||
foreach ($values as $j => $value) {
|
||||
$this->assertIdenticalObject($value, $result[$j]);
|
||||
}
|
||||
|
||||
// Verify that the other collection was not affected.
|
||||
$this->assertFalse($stores[1]->get('foo'));
|
||||
$this->assertFalse($stores[1]->get('bar'));
|
||||
|
||||
// Verify that all items in a collection can be retrieved.
|
||||
// Ensure that an item with the same name exists in the other collection.
|
||||
$stores[1]->set('foo', $this->objects[5]);
|
||||
$result = $stores[0]->getAll();
|
||||
// Not using assertIdentical(), since the order is not defined for getAll().
|
||||
$this->assertEqual(count($result), count($values));
|
||||
foreach ($result as $key => $value) {
|
||||
$this->assertEqual($values[$key], $value);
|
||||
}
|
||||
// Verify that all items in the other collection are different.
|
||||
$result = $stores[1]->getAll();
|
||||
$this->assertEqual($result, array('foo' => $this->objects[5]));
|
||||
|
||||
// Verify that multiple items can be deleted.
|
||||
$stores[0]->deleteMultiple(array_keys($values));
|
||||
$this->assertFalse($stores[0]->get('foo'));
|
||||
$this->assertFalse($stores[0]->get('bar'));
|
||||
$this->assertFalse($stores[0]->getMultiple(array('foo', 'bar')));
|
||||
// Verify that deleting no items does not cause an error.
|
||||
$stores[0]->deleteMultiple(array());
|
||||
// Verify that the item in the other collection still exists.
|
||||
$this->assertIdenticalObject($this->objects[5], $stores[1]->get('foo'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests expected behavior for non-existing keys.
|
||||
*/
|
||||
public function testNonExistingKeys() {
|
||||
|
||||
$stores = $this->createStorage();
|
||||
|
||||
// Verify that a non-existing key returns NULL as value.
|
||||
$this->assertNull($stores[0]->get('foo'));
|
||||
|
||||
// Verify that a non-existing key with a default returns the default.
|
||||
$this->assertIdentical($stores[0]->get('foo', 'bar'), 'bar');
|
||||
|
||||
// Verify that a FALSE value can be stored.
|
||||
$stores[0]->set('foo', FALSE);
|
||||
$this->assertIdentical($stores[0]->get('foo'), FALSE);
|
||||
|
||||
// Verify that a deleted key returns NULL as value.
|
||||
$stores[0]->delete('foo');
|
||||
$this->assertNull($stores[0]->get('foo'));
|
||||
|
||||
// Verify that a non-existing key is not returned when getting multiple keys.
|
||||
$stores[0]->set('bar', 'baz');
|
||||
$values = $stores[0]->getMultiple(array('foo', 'bar'));
|
||||
$this->assertFalse(isset($values['foo']), "Key 'foo' not found.");
|
||||
$this->assertIdentical($values['bar'], 'baz');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the setIfNotExists() method.
|
||||
*/
|
||||
public function testSetIfNotExists() {
|
||||
$stores = $this->createStorage();
|
||||
|
||||
$key = $this->randomMachineName();
|
||||
// Test that setIfNotExists() succeeds only the first time.
|
||||
for ($i = 0; $i <= 1; $i++) {
|
||||
// setIfNotExists() should be TRUE the first time (when $i is 0) and
|
||||
// FALSE the second time (when $i is 1).
|
||||
$this->assertEqual(!$i, $stores[0]->setIfNotExists($key, $this->objects[$i]));
|
||||
$this->assertIdenticalObject($this->objects[0], $stores[0]->get($key));
|
||||
// Verify that the other collection is not affected.
|
||||
$this->assertFalse($stores[1]->get($key));
|
||||
}
|
||||
|
||||
// Remove the item and try to set it again.
|
||||
$stores[0]->delete($key);
|
||||
$stores[0]->setIfNotExists($key, $this->objects[1]);
|
||||
// This time it should succeed.
|
||||
$this->assertIdenticalObject($this->objects[1], $stores[0]->get($key));
|
||||
// Verify that the other collection is still not affected.
|
||||
$this->assertFalse($stores[1]->get($key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the rename operation.
|
||||
*/
|
||||
public function testRename() {
|
||||
$stores = $this->createStorage();
|
||||
$store = $stores[0];
|
||||
|
||||
$store->set('old', 'thing');
|
||||
$this->assertIdentical($store->get('old'), 'thing');
|
||||
$store->rename('old', 'new');
|
||||
$this->assertIdentical($store->get('new'), 'thing');
|
||||
$this->assertNull($store->get('old'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates storage objects for each collection defined for this class.
|
||||
*
|
||||
* Storing the storage objects in a class member variable causes a fatal
|
||||
* exception in DatabaseStorageExpirableTest, because in that situation
|
||||
* garbage collection is not triggered until the test class itself is
|
||||
* destructed, after tearDown() has deleted the database tables. Instead,
|
||||
* create the storage objects locally in each test using this method.
|
||||
*
|
||||
* @see \Drupal\system\Tests\KeyValueStore\DatabaseStorageExpirable
|
||||
* @see \Drupal\Core\KeyValueStore\DatabaseStorageExpirable::garbageCollection()
|
||||
*/
|
||||
protected function createStorage() {
|
||||
$stores = array();
|
||||
foreach ($this->collections as $i => $collection) {
|
||||
$stores[$i] = $this->container->get($this->factory)->get($collection);
|
||||
}
|
||||
|
||||
return $stores;
|
||||
}
|
||||
|
||||
}
|
71
core/tests/Drupal/KernelTests/Core/Lock/LockTest.php
Normal file
71
core/tests/Drupal/KernelTests/Core/Lock/LockTest.php
Normal file
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Lock;
|
||||
|
||||
use Drupal\Core\Lock\DatabaseLockBackend;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the Database lock backend.
|
||||
*
|
||||
* @group Lock
|
||||
*/
|
||||
class LockTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Database lock backend to test.
|
||||
*
|
||||
* @var \Drupal\Core\Lock\DatabaseLockBackend
|
||||
*/
|
||||
protected $lock;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->lock = new DatabaseLockBackend($this->container->get('database'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests backend release functionality.
|
||||
*/
|
||||
public function testBackendLockRelease() {
|
||||
$success = $this->lock->acquire('lock_a');
|
||||
$this->assertTrue($success, 'Could acquire first lock.');
|
||||
|
||||
// This function is not part of the backend, but the default database
|
||||
// backend implement it, we can here use it safely.
|
||||
$is_free = $this->lock->lockMayBeAvailable('lock_a');
|
||||
$this->assertFalse($is_free, 'First lock is unavailable.');
|
||||
|
||||
$this->lock->release('lock_a');
|
||||
$is_free = $this->lock->lockMayBeAvailable('lock_a');
|
||||
$this->assertTrue($is_free, 'First lock has been released.');
|
||||
|
||||
$success = $this->lock->acquire('lock_b');
|
||||
$this->assertTrue($success, 'Could acquire second lock.');
|
||||
|
||||
$success = $this->lock->acquire('lock_b');
|
||||
$this->assertTrue($success, 'Could acquire second lock a second time within the same request.');
|
||||
|
||||
$this->lock->release('lock_b');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests backend release functionality.
|
||||
*/
|
||||
public function testBackendLockReleaseAll() {
|
||||
$success = $this->lock->acquire('lock_a');
|
||||
$this->assertTrue($success, 'Could acquire first lock.');
|
||||
|
||||
$success = $this->lock->acquire('lock_b');
|
||||
$this->assertTrue($success, 'Could acquire second lock.');
|
||||
|
||||
$this->lock->releaseAll();
|
||||
|
||||
$is_free = $this->lock->lockMayBeAvailable('lock_a');
|
||||
$this->assertTrue($is_free, 'First lock has been released.');
|
||||
|
||||
$is_free = $this->lock->lockMayBeAvailable('lock_b');
|
||||
$this->assertTrue($is_free, 'Second lock has been released.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Menu;
|
||||
|
||||
use Drupal\Core\Menu\MenuTreeParameters;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests integration of static menu links.
|
||||
*
|
||||
* @group Menu
|
||||
*/
|
||||
class MenuLinkDefaultIntegrationTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array(
|
||||
'menu_test',
|
||||
);
|
||||
|
||||
/**
|
||||
* Tests moving a static menu link without a specified menu to the root.
|
||||
*/
|
||||
public function testMoveToRoot() {
|
||||
/** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */
|
||||
$menu_link_manager = \Drupal::service('plugin.manager.menu.link');
|
||||
$menu_link_manager->rebuild();
|
||||
|
||||
$menu_link = $menu_link_manager->getDefinition('menu_test.child');
|
||||
$this->assertEqual($menu_link['parent'], 'menu_test.parent');
|
||||
$this->assertEqual($menu_link['menu_name'], 'test');
|
||||
|
||||
$tree = \Drupal::menuTree()->load('test', new MenuTreeParameters());
|
||||
$this->assertEqual(count($tree), 1);
|
||||
$this->assertEqual($tree['menu_test.parent']->link->getPluginId(), 'menu_test.parent');
|
||||
$this->assertEqual($tree['menu_test.parent']->subtree['menu_test.child']->link->getPluginId(), 'menu_test.child');
|
||||
|
||||
// Ensure that the menu name is not forgotten.
|
||||
$menu_link_manager->updateDefinition('menu_test.child', array('parent' => ''));
|
||||
$menu_link = $menu_link_manager->getDefinition('menu_test.child');
|
||||
|
||||
$this->assertEqual($menu_link['parent'], '');
|
||||
$this->assertEqual($menu_link['menu_name'], 'test');
|
||||
|
||||
$tree = \Drupal::menuTree()->load('test', new MenuTreeParameters());
|
||||
$this->assertEqual(count($tree), 2);
|
||||
$this->assertEqual($tree['menu_test.parent']->link->getPluginId(), 'menu_test.parent');
|
||||
$this->assertEqual($tree['menu_test.child']->link->getPluginId(), 'menu_test.child');
|
||||
|
||||
$this->assertTrue(TRUE);
|
||||
}
|
||||
|
||||
}
|
133
core/tests/Drupal/KernelTests/Core/Menu/MenuLinkTreeTest.php
Normal file
133
core/tests/Drupal/KernelTests/Core/Menu/MenuLinkTreeTest.php
Normal file
|
@ -0,0 +1,133 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Menu;
|
||||
|
||||
use Drupal\Core\Menu\MenuLinkTreeElement;
|
||||
use Drupal\Core\Menu\MenuTreeParameters;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\Tests\Core\Menu\MenuLinkMock;
|
||||
|
||||
/**
|
||||
* Tests the menu link tree.
|
||||
*
|
||||
* @group Menu
|
||||
*
|
||||
* @see \Drupal\Core\Menu\MenuLinkTree
|
||||
*/
|
||||
class MenuLinkTreeTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* The tested menu link tree.
|
||||
*
|
||||
* @var \Drupal\Core\Menu\MenuLinkTree
|
||||
*/
|
||||
protected $linkTree;
|
||||
|
||||
/**
|
||||
* The menu link plugin manager.
|
||||
*
|
||||
* @var \Drupal\Core\Menu\MenuLinkManagerInterface $menuLinkManager
|
||||
*/
|
||||
protected $menuLinkManager;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array(
|
||||
'system',
|
||||
'menu_test',
|
||||
'menu_link_content',
|
||||
'field',
|
||||
'link',
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
$this->installEntitySchema('menu_link_content');
|
||||
|
||||
$this->linkTree = $this->container->get('menu.link_tree');
|
||||
$this->menuLinkManager = $this->container->get('plugin.manager.menu.link');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests deleting all the links in a menu.
|
||||
*/
|
||||
public function testDeleteLinksInMenu() {
|
||||
\Drupal::entityManager()->getStorage('menu')->create(array('id' => 'menu1'))->save();
|
||||
\Drupal::entityManager()->getStorage('menu')->create(array('id' => 'menu2'))->save();
|
||||
|
||||
\Drupal::entityManager()->getStorage('menu_link_content')->create(array('link' => ['uri' => 'internal:/menu_name_test'], 'menu_name' => 'menu1', 'bundle' => 'menu_link_content'))->save();
|
||||
\Drupal::entityManager()->getStorage('menu_link_content')->create(array('link' => ['uri' => 'internal:/menu_name_test'], 'menu_name' => 'menu1', 'bundle' => 'menu_link_content'))->save();
|
||||
\Drupal::entityManager()->getStorage('menu_link_content')->create(array('link' => ['uri' => 'internal:/menu_name_test'], 'menu_name' => 'menu2', 'bundle' => 'menu_link_content'))->save();
|
||||
|
||||
$output = $this->linkTree->load('menu1', new MenuTreeParameters());
|
||||
$this->assertEqual(count($output), 2);
|
||||
$output = $this->linkTree->load('menu2', new MenuTreeParameters());
|
||||
$this->assertEqual(count($output), 1);
|
||||
|
||||
$this->menuLinkManager->deleteLinksInMenu('menu1');
|
||||
|
||||
$output = $this->linkTree->load('menu1', new MenuTreeParameters());
|
||||
$this->assertEqual(count($output), 0);
|
||||
|
||||
$output = $this->linkTree->load('menu2', new MenuTreeParameters());
|
||||
$this->assertEqual(count($output), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests creating links with an expected tree structure.
|
||||
*/
|
||||
public function testCreateLinksInMenu() {
|
||||
// This creates a tree with the following structure:
|
||||
// - 1
|
||||
// - 2
|
||||
// - 3
|
||||
// - 4
|
||||
// - 5
|
||||
// - 7
|
||||
// - 6
|
||||
// - 8
|
||||
// With link 6 being the only external link.
|
||||
|
||||
$links = array(
|
||||
1 => MenuLinkMock::create(array('id' => 'test.example1', 'route_name' => 'example1', 'title' => 'foo', 'parent' => '')),
|
||||
2 => MenuLinkMock::create(array('id' => 'test.example2', 'route_name' => 'example2', 'title' => 'bar', 'parent' => 'test.example1', 'route_parameters' => array('foo' => 'bar'))),
|
||||
3 => MenuLinkMock::create(array('id' => 'test.example3', 'route_name' => 'example3', 'title' => 'baz', 'parent' => 'test.example2', 'route_parameters' => array('baz' => 'qux'))),
|
||||
4 => MenuLinkMock::create(array('id' => 'test.example4', 'route_name' => 'example4', 'title' => 'qux', 'parent' => 'test.example3')),
|
||||
5 => MenuLinkMock::create(array('id' => 'test.example5', 'route_name' => 'example5', 'title' => 'foofoo', 'parent' => '')),
|
||||
6 => MenuLinkMock::create(array('id' => 'test.example6', 'route_name' => '', 'url' => 'https://www.drupal.org/', 'title' => 'barbar', 'parent' => '')),
|
||||
7 => MenuLinkMock::create(array('id' => 'test.example7', 'route_name' => 'example7', 'title' => 'bazbaz', 'parent' => '')),
|
||||
8 => MenuLinkMock::create(array('id' => 'test.example8', 'route_name' => 'example8', 'title' => 'quxqux', 'parent' => '')),
|
||||
);
|
||||
foreach ($links as $instance) {
|
||||
$this->menuLinkManager->addDefinition($instance->getPluginId(), $instance->getPluginDefinition());
|
||||
}
|
||||
$parameters = new MenuTreeParameters();
|
||||
$tree = $this->linkTree->load('mock', $parameters);
|
||||
|
||||
$count = function(array $tree) {
|
||||
$sum = function ($carry, MenuLinkTreeElement $item) {
|
||||
return $carry + $item->count();
|
||||
};
|
||||
return array_reduce($tree, $sum);
|
||||
};
|
||||
|
||||
$this->assertEqual($count($tree), 8);
|
||||
$parameters = new MenuTreeParameters();
|
||||
$parameters->setRoot('test.example2');
|
||||
$tree = $this->linkTree->load($instance->getMenuName(), $parameters);
|
||||
$top_link = reset($tree);
|
||||
$this->assertEqual(count($top_link->subtree), 1);
|
||||
$child = reset($top_link->subtree);
|
||||
$this->assertEqual($child->link->getPluginId(), $links[3]->getPluginId());
|
||||
$height = $this->linkTree->getSubtreeHeight('test.example2');
|
||||
$this->assertEqual($height, 3);
|
||||
}
|
||||
|
||||
}
|
453
core/tests/Drupal/KernelTests/Core/Menu/MenuTreeStorageTest.php
Normal file
453
core/tests/Drupal/KernelTests/Core/Menu/MenuTreeStorageTest.php
Normal file
|
@ -0,0 +1,453 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Menu;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\PluginException;
|
||||
use Drupal\Core\Menu\MenuTreeParameters;
|
||||
use Drupal\Core\Menu\MenuTreeStorage;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the menu tree storage.
|
||||
*
|
||||
* @group Menu
|
||||
*
|
||||
* @see \Drupal\Core\Menu\MenuTreeStorage
|
||||
*/
|
||||
class MenuTreeStorageTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* The tested tree storage.
|
||||
*
|
||||
* @var \Drupal\Core\Menu\MenuTreeStorage
|
||||
*/
|
||||
protected $treeStorage;
|
||||
|
||||
/**
|
||||
* The database connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->treeStorage = new MenuTreeStorage($this->container->get('database'), $this->container->get('cache.menu'), $this->container->get('cache_tags.invalidator'), 'menu_tree');
|
||||
$this->connection = $this->container->get('database');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the tree storage when no tree was built yet.
|
||||
*/
|
||||
public function testBasicMethods() {
|
||||
$this->doTestEmptyStorage();
|
||||
$this->doTestTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that there are no menu links by default.
|
||||
*/
|
||||
protected function doTestEmptyStorage() {
|
||||
$this->assertEqual(0, $this->treeStorage->countMenuLinks());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that table gets created on the fly.
|
||||
*/
|
||||
protected function doTestTable() {
|
||||
// Test that we can create a tree storage with an arbitrary table name and
|
||||
// that selecting from the storage creates the table.
|
||||
$tree_storage = new MenuTreeStorage($this->container->get('database'), $this->container->get('cache.menu'), $this->container->get('cache_tags.invalidator'), 'test_menu_tree');
|
||||
$this->assertFalse($this->connection->schema()->tableExists('test_menu_tree'), 'Test table is not yet created');
|
||||
$tree_storage->countMenuLinks();
|
||||
$this->assertTrue($this->connection->schema()->tableExists('test_menu_tree'), 'Test table was created');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests with a simple linear hierarchy.
|
||||
*/
|
||||
public function testSimpleHierarchy() {
|
||||
// Add some links with parent on the previous one and test some values.
|
||||
// <tools>
|
||||
// - test1
|
||||
// -- test2
|
||||
// --- test3
|
||||
$this->addMenuLink('test1', '');
|
||||
$this->assertMenuLink('test1', array('has_children' => 0, 'depth' => 1));
|
||||
|
||||
$this->addMenuLink('test2', 'test1');
|
||||
$this->assertMenuLink('test1', array('has_children' => 1, 'depth' => 1), array(), array('test2'));
|
||||
$this->assertMenuLink('test2', array('has_children' => 0, 'depth' => 2), array('test1'));
|
||||
|
||||
$this->addMenuLink('test3', 'test2');
|
||||
$this->assertMenuLink('test1', array('has_children' => 1, 'depth' => 1), array(), array('test2', 'test3'));
|
||||
$this->assertMenuLink('test2', array('has_children' => 1, 'depth' => 2), array('test1'), array('test3'));
|
||||
$this->assertMenuLink('test3', array('has_children' => 0, 'depth' => 3), array('test2', 'test1'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the tree with moving links inside the hierarchy.
|
||||
*/
|
||||
public function testMenuLinkMoving() {
|
||||
// Before the move.
|
||||
// <tools>
|
||||
// - test1
|
||||
// -- test2
|
||||
// --- test3
|
||||
// - test4
|
||||
// -- test5
|
||||
// --- test6
|
||||
|
||||
$this->addMenuLink('test1', '');
|
||||
$this->addMenuLink('test2', 'test1');
|
||||
$this->addMenuLink('test3', 'test2');
|
||||
$this->addMenuLink('test4', '');
|
||||
$this->addMenuLink('test5', 'test4');
|
||||
$this->addMenuLink('test6', 'test5');
|
||||
|
||||
$this->assertMenuLink('test1', array('has_children' => 1, 'depth' => 1), array(), array('test2', 'test3'));
|
||||
$this->assertMenuLink('test2', array('has_children' => 1, 'depth' => 2), array('test1'), array('test3'));
|
||||
$this->assertMenuLink('test4', array('has_children' => 1, 'depth' => 1), array(), array('test5', 'test6'));
|
||||
$this->assertMenuLink('test5', array('has_children' => 1, 'depth' => 2), array('test4'), array('test6'));
|
||||
$this->assertMenuLink('test6', array('has_children' => 0, 'depth' => 3), array('test5', 'test4'));
|
||||
|
||||
$this->moveMenuLink('test2', 'test5');
|
||||
// After the 1st move.
|
||||
// <tools>
|
||||
// - test1
|
||||
// - test4
|
||||
// -- test5
|
||||
// --- test2
|
||||
// ---- test3
|
||||
// --- test6
|
||||
|
||||
$this->assertMenuLink('test1', array('has_children' => 0, 'depth' => 1));
|
||||
$this->assertMenuLink('test2', array('has_children' => 1, 'depth' => 3), array('test5', 'test4'), array('test3'));
|
||||
$this->assertMenuLink('test3', array('has_children' => 0, 'depth' => 4), array('test2', 'test5', 'test4'));
|
||||
$this->assertMenuLink('test4', array('has_children' => 1, 'depth' => 1), array(), array('test5', 'test2', 'test3', 'test6'));
|
||||
$this->assertMenuLink('test5', array('has_children' => 1, 'depth' => 2), array('test4'), array('test2', 'test3', 'test6'));
|
||||
$this->assertMenuLink('test6', array('has_children' => 0, 'depth' => 3), array('test5', 'test4'));
|
||||
|
||||
$this->moveMenuLink('test4', 'test1');
|
||||
$this->moveMenuLink('test3', 'test1');
|
||||
// After the next 2 moves.
|
||||
// <tools>
|
||||
// - test1
|
||||
// -- test3
|
||||
// -- test4
|
||||
// --- test5
|
||||
// ---- test2
|
||||
// ---- test6
|
||||
|
||||
$this->assertMenuLink('test1', array('has_children' => 1, 'depth' => 1), array(), array('test4', 'test5', 'test2', 'test3', 'test6'));
|
||||
$this->assertMenuLink('test2', array('has_children' => 0, 'depth' => 4), array('test5', 'test4', 'test1'));
|
||||
$this->assertMenuLink('test3', array('has_children' => 0, 'depth' => 2), array('test1'));
|
||||
$this->assertMenuLink('test4', array('has_children' => 1, 'depth' => 2), array('test1'), array('test2', 'test5', 'test6'));
|
||||
$this->assertMenuLink('test5', array('has_children' => 1, 'depth' => 3), array('test4', 'test1'), array('test2', 'test6'));
|
||||
$this->assertMenuLink('test6', array('has_children' => 0, 'depth' => 4), array('test5', 'test4', 'test1'));
|
||||
|
||||
// Deleting a link in the middle should re-attach child links to the parent.
|
||||
$this->treeStorage->delete('test4');
|
||||
// After the delete.
|
||||
// <tools>
|
||||
// - test1
|
||||
// -- test3
|
||||
// -- test5
|
||||
// --- test2
|
||||
// --- test6
|
||||
$this->assertMenuLink('test1', array('has_children' => 1, 'depth' => 1), array(), array('test5', 'test2', 'test3', 'test6'));
|
||||
$this->assertMenuLink('test2', array('has_children' => 0, 'depth' => 3), array('test5', 'test1'));
|
||||
$this->assertMenuLink('test3', array('has_children' => 0, 'depth' => 2), array('test1'));
|
||||
$this->assertFalse($this->treeStorage->load('test4'));
|
||||
$this->assertMenuLink('test5', array('has_children' => 1, 'depth' => 2), array('test1'), array('test2', 'test6'));
|
||||
$this->assertMenuLink('test6', array('has_children' => 0, 'depth' => 3), array('test5', 'test1'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests with disabled child links.
|
||||
*/
|
||||
public function testMenuDisabledChildLinks() {
|
||||
// Add some links with parent on the previous one and test some values.
|
||||
// <tools>
|
||||
// - test1
|
||||
// -- test2 (disabled)
|
||||
|
||||
$this->addMenuLink('test1', '');
|
||||
$this->assertMenuLink('test1', array('has_children' => 0, 'depth' => 1));
|
||||
|
||||
$this->addMenuLink('test2', 'test1', '<front>', array(), 'tools', array('enabled' => 0));
|
||||
// The 1st link does not have any visible children, so has_children is 0.
|
||||
$this->assertMenuLink('test1', array('has_children' => 0, 'depth' => 1));
|
||||
$this->assertMenuLink('test2', array('has_children' => 0, 'depth' => 2, 'enabled' => 0), array('test1'));
|
||||
|
||||
// Add more links with parent on the previous one.
|
||||
// <footer>
|
||||
// - footerA
|
||||
// ===============
|
||||
// <tools>
|
||||
// - test1
|
||||
// -- test2 (disabled)
|
||||
// --- test3
|
||||
// ---- test4
|
||||
// ----- test5
|
||||
// ------ test6
|
||||
// ------- test7
|
||||
// -------- test8
|
||||
// --------- test9
|
||||
$this->addMenuLink('footerA', '', '<front>', array(), 'footer');
|
||||
$visible_children = array();
|
||||
for ($i = 3; $i <= $this->treeStorage->maxDepth(); $i++) {
|
||||
$parent = $i - 1;
|
||||
$this->addMenuLink("test$i", "test$parent");
|
||||
$visible_children[] = "test$i";
|
||||
}
|
||||
// The 1st link does not have any visible children, so has_children is still
|
||||
// 0. However, it has visible links below it that will be found.
|
||||
$this->assertMenuLink('test1', array('has_children' => 0, 'depth' => 1), array(), $visible_children);
|
||||
// This should fail since test9 would end up at greater than max depth.
|
||||
try {
|
||||
$this->moveMenuLink('test1', 'footerA');
|
||||
$this->fail('Exception was not thrown');
|
||||
}
|
||||
catch (PluginException $e) {
|
||||
$this->pass($e->getMessage());
|
||||
}
|
||||
// The opposite move should work, and change the has_children flag.
|
||||
$this->moveMenuLink('footerA', 'test1');
|
||||
$visible_children[] = 'footerA';
|
||||
$this->assertMenuLink('test1', array('has_children' => 1, 'depth' => 1), array(), $visible_children);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the loadTreeData method.
|
||||
*/
|
||||
public function testLoadTree() {
|
||||
$this->addMenuLink('test1', '');
|
||||
$this->addMenuLink('test2', 'test1');
|
||||
$this->addMenuLink('test3', 'test2');
|
||||
$this->addMenuLink('test4');
|
||||
$this->addMenuLink('test5', 'test4');
|
||||
|
||||
$data = $this->treeStorage->loadTreeData('tools', new MenuTreeParameters());
|
||||
$tree = $data['tree'];
|
||||
$this->assertEqual(count($tree['test1']['subtree']), 1);
|
||||
$this->assertEqual(count($tree['test1']['subtree']['test2']['subtree']), 1);
|
||||
$this->assertEqual(count($tree['test1']['subtree']['test2']['subtree']['test3']['subtree']), 0);
|
||||
$this->assertEqual(count($tree['test4']['subtree']), 1);
|
||||
$this->assertEqual(count($tree['test4']['subtree']['test5']['subtree']), 0);
|
||||
|
||||
$parameters = new MenuTreeParameters();
|
||||
$parameters->setActiveTrail(array('test4', 'test5'));
|
||||
$data = $this->treeStorage->loadTreeData('tools', $parameters);
|
||||
$tree = $data['tree'];
|
||||
$this->assertEqual(count($tree['test1']['subtree']), 1);
|
||||
$this->assertFalse($tree['test1']['in_active_trail']);
|
||||
$this->assertEqual(count($tree['test1']['subtree']['test2']['subtree']), 1);
|
||||
$this->assertFalse($tree['test1']['subtree']['test2']['in_active_trail']);
|
||||
$this->assertEqual(count($tree['test1']['subtree']['test2']['subtree']['test3']['subtree']), 0);
|
||||
$this->assertFalse($tree['test1']['subtree']['test2']['subtree']['test3']['in_active_trail']);
|
||||
$this->assertEqual(count($tree['test4']['subtree']), 1);
|
||||
$this->assertTrue($tree['test4']['in_active_trail']);
|
||||
$this->assertEqual(count($tree['test4']['subtree']['test5']['subtree']), 0);
|
||||
$this->assertTrue($tree['test4']['subtree']['test5']['in_active_trail']);
|
||||
|
||||
// Add some conditions to ensure that conditions work as expected.
|
||||
$parameters = new MenuTreeParameters();
|
||||
$parameters->addCondition('parent', 'test1');
|
||||
$data = $this->treeStorage->loadTreeData('tools', $parameters);
|
||||
$this->assertEqual(count($data['tree']), 1);
|
||||
$this->assertEqual($data['tree']['test2']['definition']['id'], 'test2');
|
||||
$this->assertEqual($data['tree']['test2']['subtree'], []);
|
||||
|
||||
// Test for only enabled links.
|
||||
$link = $this->treeStorage->load('test3');
|
||||
$link['enabled'] = FALSE;
|
||||
$this->treeStorage->save($link);
|
||||
$link = $this->treeStorage->load('test4');
|
||||
$link['enabled'] = FALSE;
|
||||
$this->treeStorage->save($link);
|
||||
$link = $this->treeStorage->load('test5');
|
||||
$link['enabled'] = FALSE;
|
||||
$this->treeStorage->save($link);
|
||||
|
||||
$parameters = new MenuTreeParameters();
|
||||
$parameters->onlyEnabledLinks();
|
||||
$data = $this->treeStorage->loadTreeData('tools', $parameters);
|
||||
$this->assertEqual(count($data['tree']), 1);
|
||||
$this->assertEqual($data['tree']['test1']['definition']['id'], 'test1');
|
||||
$this->assertEqual(count($data['tree']['test1']['subtree']), 1);
|
||||
$this->assertEqual($data['tree']['test1']['subtree']['test2']['definition']['id'], 'test2');
|
||||
$this->assertEqual($data['tree']['test1']['subtree']['test2']['subtree'], []);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests finding the subtree height with content menu links.
|
||||
*/
|
||||
public function testSubtreeHeight() {
|
||||
// root
|
||||
// - child1
|
||||
// -- child2
|
||||
// --- child3
|
||||
// ---- child4
|
||||
$this->addMenuLink('root');
|
||||
$this->addMenuLink('child1', 'root');
|
||||
$this->addMenuLink('child2', 'child1');
|
||||
$this->addMenuLink('child3', 'child2');
|
||||
$this->addMenuLink('child4', 'child3');
|
||||
|
||||
$this->assertEqual($this->treeStorage->getSubtreeHeight('root'), 5);
|
||||
$this->assertEqual($this->treeStorage->getSubtreeHeight('child1'), 4);
|
||||
$this->assertEqual($this->treeStorage->getSubtreeHeight('child2'), 3);
|
||||
$this->assertEqual($this->treeStorage->getSubtreeHeight('child3'), 2);
|
||||
$this->assertEqual($this->treeStorage->getSubtreeHeight('child4'), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure hierarchy persists after a menu rebuild.
|
||||
*/
|
||||
public function testMenuRebuild() {
|
||||
// root
|
||||
// - child1
|
||||
// -- child2
|
||||
// --- child3
|
||||
// ---- child4
|
||||
$this->addMenuLink('root');
|
||||
$this->addMenuLink('child1', 'root');
|
||||
$this->addMenuLink('child2', 'child1');
|
||||
$this->addMenuLink('child3', 'child2');
|
||||
$this->addMenuLink('child4', 'child3');
|
||||
|
||||
$this->assertEqual($this->treeStorage->getSubtreeHeight('root'), 5);
|
||||
$this->assertEqual($this->treeStorage->getSubtreeHeight('child1'), 4);
|
||||
$this->assertEqual($this->treeStorage->getSubtreeHeight('child2'), 3);
|
||||
$this->assertEqual($this->treeStorage->getSubtreeHeight('child3'), 2);
|
||||
$this->assertEqual($this->treeStorage->getSubtreeHeight('child4'), 1);
|
||||
|
||||
// Intentionally leave child3 out to mimic static or external links.
|
||||
$definitions = $this->treeStorage->loadMultiple(['root', 'child1', 'child2', 'child4']);
|
||||
$this->treeStorage->rebuild($definitions);
|
||||
$this->assertEqual($this->treeStorage->getSubtreeHeight('root'), 5);
|
||||
$this->assertEqual($this->treeStorage->getSubtreeHeight('child1'), 4);
|
||||
$this->assertEqual($this->treeStorage->getSubtreeHeight('child2'), 3);
|
||||
$this->assertEqual($this->treeStorage->getSubtreeHeight('child3'), 2);
|
||||
$this->assertEqual($this->treeStorage->getSubtreeHeight('child4'), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests MenuTreeStorage::loadByProperties().
|
||||
*/
|
||||
public function testLoadByProperties() {
|
||||
$tests = array(
|
||||
array('foo' => 'bar'),
|
||||
array(0 => 'wrong'),
|
||||
);
|
||||
$message = 'An invalid property name throws an exception.';
|
||||
foreach ($tests as $properties) {
|
||||
try {
|
||||
$this->treeStorage->loadByProperties($properties);
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (\InvalidArgumentException $e) {
|
||||
$this->assertTrue(preg_match('/^An invalid property name, .+ was specified. Allowed property names are:/', $e->getMessage()), 'Found expected exception message.');
|
||||
$this->pass($message);
|
||||
}
|
||||
}
|
||||
$this->addMenuLink('test_link.1', '', 'test', array(), 'menu1');
|
||||
$properties = array('menu_name' => 'menu1');
|
||||
$links = $this->treeStorage->loadByProperties($properties);
|
||||
$this->assertEqual('menu1', $links['test_link.1']['menu_name']);
|
||||
$this->assertEqual('test', $links['test_link.1']['route_name']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a link with the given ID and supply defaults.
|
||||
*/
|
||||
protected function addMenuLink($id, $parent = '', $route_name = 'test', $route_parameters = array(), $menu_name = 'tools', $extra = array()) {
|
||||
$link = array(
|
||||
'id' => $id,
|
||||
'menu_name' => $menu_name,
|
||||
'route_name' => $route_name,
|
||||
'route_parameters' => $route_parameters,
|
||||
'title' => 'test',
|
||||
'parent' => $parent,
|
||||
'options' => array(),
|
||||
'metadata' => array(),
|
||||
) + $extra;
|
||||
$this->treeStorage->save($link);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the link with the given ID so it's under a new parent.
|
||||
*
|
||||
* @param string $id
|
||||
* The ID of the menu link to move.
|
||||
* @param string $new_parent
|
||||
* The ID of the new parent link.
|
||||
*/
|
||||
protected function moveMenuLink($id, $new_parent) {
|
||||
$menu_link = $this->treeStorage->load($id);
|
||||
$menu_link['parent'] = $new_parent;
|
||||
$this->treeStorage->save($menu_link);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a link's stored representation matches the expected values.
|
||||
*
|
||||
* @param string $id
|
||||
* The ID of the menu link to test
|
||||
* @param array $expected_properties
|
||||
* A keyed array of column names and values like has_children and depth.
|
||||
* @param array $parents
|
||||
* An ordered array of the IDs of the menu links that are the parents.
|
||||
* @param array $children
|
||||
* Array of child IDs that are visible (enabled == 1).
|
||||
*/
|
||||
protected function assertMenuLink($id, array $expected_properties, array $parents = array(), array $children = array()) {
|
||||
$query = $this->connection->select('menu_tree');
|
||||
$query->fields('menu_tree');
|
||||
$query->condition('id', $id);
|
||||
foreach ($expected_properties as $field => $value) {
|
||||
$query->condition($field, $value);
|
||||
}
|
||||
$all = $query->execute()->fetchAll(\PDO::FETCH_ASSOC);
|
||||
$this->assertEqual(count($all), 1, "Found link $id matching all the expected properties");
|
||||
$raw = reset($all);
|
||||
|
||||
// Put the current link onto the front.
|
||||
array_unshift($parents, $raw['id']);
|
||||
|
||||
$query = $this->connection->select('menu_tree');
|
||||
$query->fields('menu_tree', array('id', 'mlid'));
|
||||
$query->condition('id', $parents, 'IN');
|
||||
$found_parents = $query->execute()->fetchAllKeyed(0, 1);
|
||||
|
||||
$this->assertEqual(count($parents), count($found_parents), 'Found expected number of parents');
|
||||
$this->assertEqual($raw['depth'], count($found_parents), 'Number of parents is the same as the depth');
|
||||
|
||||
$materialized_path = $this->treeStorage->getRootPathIds($id);
|
||||
$this->assertEqual(array_values($materialized_path), array_values($parents), 'Parents match the materialized path');
|
||||
// Check that the selected mlid values of the parents are in the correct
|
||||
// column, including the link's own.
|
||||
for ($i = $raw['depth']; $i >= 1; $i--) {
|
||||
$parent_id = array_shift($parents);
|
||||
$this->assertEqual($raw["p$i"], $found_parents[$parent_id], "mlid of parent matches at column p$i");
|
||||
}
|
||||
for ($i = $raw['depth'] + 1; $i <= $this->treeStorage->maxDepth(); $i++) {
|
||||
$this->assertEqual($raw["p$i"], 0, "parent is 0 at column p$i greater than depth");
|
||||
}
|
||||
if ($parents) {
|
||||
$this->assertEqual($raw['parent'], end($parents), 'Ensure that the parent field is set properly');
|
||||
}
|
||||
$found_children = array_keys($this->treeStorage->loadAllChildren($id));
|
||||
// We need both these checks since the 2nd will pass if there are extra
|
||||
// IDs loaded in $found_children.
|
||||
$this->assertEqual(count($children), count($found_children), "Found expected number of children for $id");
|
||||
$this->assertEqual(array_intersect($children, $found_children), $children, 'Child IDs match');
|
||||
}
|
||||
|
||||
}
|
219
core/tests/Drupal/KernelTests/Core/Path/AliasTest.php
Normal file
219
core/tests/Drupal/KernelTests/Core/Path/AliasTest.php
Normal file
|
@ -0,0 +1,219 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Path;
|
||||
|
||||
use Drupal\Core\Cache\MemoryCounterBackend;
|
||||
use Drupal\Core\Path\AliasStorage;
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Path\AliasManager;
|
||||
use Drupal\Core\Path\AliasWhitelist;
|
||||
|
||||
/**
|
||||
* Tests path alias CRUD and lookup functionality.
|
||||
*
|
||||
* @group Path
|
||||
*/
|
||||
class AliasTest extends PathUnitTestBase {
|
||||
|
||||
function testCRUD() {
|
||||
//Prepare database table.
|
||||
$connection = Database::getConnection();
|
||||
$this->fixtures->createTables($connection);
|
||||
|
||||
//Create Path object.
|
||||
$aliasStorage = new AliasStorage($connection, $this->container->get('module_handler'));
|
||||
|
||||
$aliases = $this->fixtures->sampleUrlAliases();
|
||||
|
||||
//Create a few aliases
|
||||
foreach ($aliases as $idx => $alias) {
|
||||
$aliasStorage->save($alias['source'], $alias['alias'], $alias['langcode']);
|
||||
|
||||
$result = $connection->query('SELECT * FROM {url_alias} WHERE source = :source AND alias= :alias AND langcode = :langcode', array(':source' => $alias['source'], ':alias' => $alias['alias'], ':langcode' => $alias['langcode']));
|
||||
$rows = $result->fetchAll();
|
||||
|
||||
$this->assertEqual(count($rows), 1, format_string('Created an entry for %alias.', array('%alias' => $alias['alias'])));
|
||||
|
||||
//Cache the pid for further tests.
|
||||
$aliases[$idx]['pid'] = $rows[0]->pid;
|
||||
}
|
||||
|
||||
//Load a few aliases
|
||||
foreach ($aliases as $alias) {
|
||||
$pid = $alias['pid'];
|
||||
$loadedAlias = $aliasStorage->load(array('pid' => $pid));
|
||||
$this->assertEqual($loadedAlias, $alias, format_string('Loaded the expected path with pid %pid.', array('%pid' => $pid)));
|
||||
}
|
||||
|
||||
// Load alias by source path.
|
||||
$loadedAlias = $aliasStorage->load(array('source' => '/node/1'));
|
||||
$this->assertEqual($loadedAlias['alias'], '/alias_for_node_1_und', 'The last created alias loaded by default.');
|
||||
|
||||
//Update a few aliases
|
||||
foreach ($aliases as $alias) {
|
||||
$fields = $aliasStorage->save($alias['source'], $alias['alias'] . '_updated', $alias['langcode'], $alias['pid']);
|
||||
|
||||
$this->assertEqual($alias['alias'], $fields['original']['alias']);
|
||||
|
||||
$result = $connection->query('SELECT pid FROM {url_alias} WHERE source = :source AND alias= :alias AND langcode = :langcode', array(':source' => $alias['source'], ':alias' => $alias['alias'] . '_updated', ':langcode' => $alias['langcode']));
|
||||
$pid = $result->fetchField();
|
||||
|
||||
$this->assertEqual($pid, $alias['pid'], format_string('Updated entry for pid %pid.', array('%pid' => $pid)));
|
||||
}
|
||||
|
||||
//Delete a few aliases
|
||||
foreach ($aliases as $alias) {
|
||||
$pid = $alias['pid'];
|
||||
$aliasStorage->delete(array('pid' => $pid));
|
||||
|
||||
$result = $connection->query('SELECT * FROM {url_alias} WHERE pid = :pid', array(':pid' => $pid));
|
||||
$rows = $result->fetchAll();
|
||||
|
||||
$this->assertEqual(count($rows), 0, format_string('Deleted entry with pid %pid.', array('%pid' => $pid)));
|
||||
}
|
||||
}
|
||||
|
||||
function testLookupPath() {
|
||||
//Prepare database table.
|
||||
$connection = Database::getConnection();
|
||||
$this->fixtures->createTables($connection);
|
||||
|
||||
//Create AliasManager and Path object.
|
||||
$aliasManager = $this->container->get('path.alias_manager');
|
||||
$aliasStorage = new AliasStorage($connection, $this->container->get('module_handler'));
|
||||
|
||||
// Test the situation where the source is the same for multiple aliases.
|
||||
// Start with a language-neutral alias, which we will override.
|
||||
$path = array(
|
||||
'source' => "/user/1",
|
||||
'alias' => '/foo',
|
||||
);
|
||||
|
||||
$aliasStorage->save($path['source'], $path['alias']);
|
||||
$this->assertEqual($aliasManager->getAliasByPath($path['source']), $path['alias'], 'Basic alias lookup works.');
|
||||
$this->assertEqual($aliasManager->getPathByAlias($path['alias']), $path['source'], 'Basic source lookup works.');
|
||||
|
||||
// Create a language specific alias for the default language (English).
|
||||
$path = array(
|
||||
'source' => "/user/1",
|
||||
'alias' => "/users/Dries",
|
||||
'langcode' => 'en',
|
||||
);
|
||||
$aliasStorage->save($path['source'], $path['alias'], $path['langcode']);
|
||||
// Hook that clears cache is not executed with unit tests.
|
||||
\Drupal::service('path.alias_manager')->cacheClear();
|
||||
$this->assertEqual($aliasManager->getAliasByPath($path['source']), $path['alias'], 'English alias overrides language-neutral alias.');
|
||||
$this->assertEqual($aliasManager->getPathByAlias($path['alias']), $path['source'], 'English source overrides language-neutral source.');
|
||||
|
||||
// Create a language-neutral alias for the same path, again.
|
||||
$path = array(
|
||||
'source' => "/user/1",
|
||||
'alias' => '/bar',
|
||||
);
|
||||
$aliasStorage->save($path['source'], $path['alias']);
|
||||
$this->assertEqual($aliasManager->getAliasByPath($path['source']), "/users/Dries", 'English alias still returned after entering a language-neutral alias.');
|
||||
|
||||
// Create a language-specific (xx-lolspeak) alias for the same path.
|
||||
$path = array(
|
||||
'source' => "/user/1",
|
||||
'alias' => '/LOL',
|
||||
'langcode' => 'xx-lolspeak',
|
||||
);
|
||||
$aliasStorage->save($path['source'], $path['alias'], $path['langcode']);
|
||||
$this->assertEqual($aliasManager->getAliasByPath($path['source']), "/users/Dries", 'English alias still returned after entering a LOLspeak alias.');
|
||||
// The LOLspeak alias should be returned if we really want LOLspeak.
|
||||
$this->assertEqual($aliasManager->getAliasByPath($path['source'], 'xx-lolspeak'), '/LOL', 'LOLspeak alias returned if we specify xx-lolspeak to the alias manager.');
|
||||
|
||||
// Create a new alias for this path in English, which should override the
|
||||
// previous alias for "user/1".
|
||||
$path = array(
|
||||
'source' => "/user/1",
|
||||
'alias' => '/users/my-new-path',
|
||||
'langcode' => 'en',
|
||||
);
|
||||
$aliasStorage->save($path['source'], $path['alias'], $path['langcode']);
|
||||
// Hook that clears cache is not executed with unit tests.
|
||||
$aliasManager->cacheClear();
|
||||
$this->assertEqual($aliasManager->getAliasByPath($path['source']), $path['alias'], 'Recently created English alias returned.');
|
||||
$this->assertEqual($aliasManager->getPathByAlias($path['alias']), $path['source'], 'Recently created English source returned.');
|
||||
|
||||
// Remove the English aliases, which should cause a fallback to the most
|
||||
// recently created language-neutral alias, 'bar'.
|
||||
$aliasStorage->delete(array('langcode' => 'en'));
|
||||
// Hook that clears cache is not executed with unit tests.
|
||||
$aliasManager->cacheClear();
|
||||
$this->assertEqual($aliasManager->getAliasByPath($path['source']), '/bar', 'Path lookup falls back to recently created language-neutral alias.');
|
||||
|
||||
// Test the situation where the alias and language are the same, but
|
||||
// the source differs. The newer alias record should be returned.
|
||||
$aliasStorage->save('/user/2', '/bar');
|
||||
// Hook that clears cache is not executed with unit tests.
|
||||
$aliasManager->cacheClear();
|
||||
$this->assertEqual($aliasManager->getPathByAlias('/bar'), '/user/2', 'Newer alias record is returned when comparing two LanguageInterface::LANGCODE_NOT_SPECIFIED paths with the same alias.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the alias whitelist.
|
||||
*/
|
||||
function testWhitelist() {
|
||||
// Prepare database table.
|
||||
$connection = Database::getConnection();
|
||||
$this->fixtures->createTables($connection);
|
||||
|
||||
$memoryCounterBackend = new MemoryCounterBackend('default');
|
||||
|
||||
// Create AliasManager and Path object.
|
||||
$aliasStorage = new AliasStorage($connection, $this->container->get('module_handler'));
|
||||
$whitelist = new AliasWhitelist('path_alias_whitelist', $memoryCounterBackend, $this->container->get('lock'), $this->container->get('state'), $aliasStorage);
|
||||
$aliasManager = new AliasManager($aliasStorage, $whitelist, $this->container->get('language_manager'), $memoryCounterBackend);
|
||||
|
||||
// No alias for user and admin yet, so should be NULL.
|
||||
$this->assertNull($whitelist->get('user'));
|
||||
$this->assertNull($whitelist->get('admin'));
|
||||
|
||||
// Non-existing path roots should be NULL too. Use a length of 7 to avoid
|
||||
// possible conflict with random aliases below.
|
||||
$this->assertNull($whitelist->get($this->randomMachineName()));
|
||||
|
||||
// Add an alias for user/1, user should get whitelisted now.
|
||||
$aliasStorage->save('/user/1', '/' . $this->randomMachineName());
|
||||
$aliasManager->cacheClear();
|
||||
$this->assertTrue($whitelist->get('user'));
|
||||
$this->assertNull($whitelist->get('admin'));
|
||||
$this->assertNull($whitelist->get($this->randomMachineName()));
|
||||
|
||||
// Add an alias for admin, both should get whitelisted now.
|
||||
$aliasStorage->save('/admin/something', '/' . $this->randomMachineName());
|
||||
$aliasManager->cacheClear();
|
||||
$this->assertTrue($whitelist->get('user'));
|
||||
$this->assertTrue($whitelist->get('admin'));
|
||||
$this->assertNull($whitelist->get($this->randomMachineName()));
|
||||
|
||||
// Remove the user alias again, whitelist entry should be removed.
|
||||
$aliasStorage->delete(array('source' => '/user/1'));
|
||||
$aliasManager->cacheClear();
|
||||
$this->assertNull($whitelist->get('user'));
|
||||
$this->assertTrue($whitelist->get('admin'));
|
||||
$this->assertNull($whitelist->get($this->randomMachineName()));
|
||||
|
||||
// Destruct the whitelist so that the caches are written.
|
||||
$whitelist->destruct();
|
||||
$this->assertEqual($memoryCounterBackend->getCounter('set', 'path_alias_whitelist'), 1);
|
||||
$memoryCounterBackend->resetCounter();
|
||||
|
||||
// Re-initialize the whitelist using the same cache backend, should load
|
||||
// from cache.
|
||||
$whitelist = new AliasWhitelist('path_alias_whitelist', $memoryCounterBackend, $this->container->get('lock'), $this->container->get('state'), $aliasStorage);
|
||||
$this->assertNull($whitelist->get('user'));
|
||||
$this->assertTrue($whitelist->get('admin'));
|
||||
$this->assertNull($whitelist->get($this->randomMachineName()));
|
||||
$this->assertEqual($memoryCounterBackend->getCounter('get', 'path_alias_whitelist'), 1);
|
||||
$this->assertEqual($memoryCounterBackend->getCounter('set', 'path_alias_whitelist'), 0);
|
||||
|
||||
// Destruct the whitelist, should not attempt to write the cache again.
|
||||
$whitelist->destruct();
|
||||
$this->assertEqual($memoryCounterBackend->getCounter('get', 'path_alias_whitelist'), 1);
|
||||
$this->assertEqual($memoryCounterBackend->getCounter('set', 'path_alias_whitelist'), 0);
|
||||
}
|
||||
|
||||
}
|
33
core/tests/Drupal/KernelTests/Core/Path/PathUnitTestBase.php
Normal file
33
core/tests/Drupal/KernelTests/Core/Path/PathUnitTestBase.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Path;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\system\Tests\Path\UrlAliasFixtures;
|
||||
|
||||
/**
|
||||
* Base class for Path/URL alias integration tests.
|
||||
*/
|
||||
abstract class PathUnitTestBase extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* @var \Drupal\system\Tests\Path\UrlAliasFixtures
|
||||
*/
|
||||
protected $fixtures;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->fixtures = new UrlAliasFixtures();
|
||||
// The alias whitelist expects that the menu path roots are set by a
|
||||
// menu router rebuild.
|
||||
\Drupal::state()->set('router.path_roots', array('user', 'admin'));
|
||||
}
|
||||
|
||||
protected function tearDown() {
|
||||
$this->fixtures->dropTables(Database::getConnection());
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Plugin\Condition;
|
||||
|
||||
use Drupal\Core\Plugin\Context\Context;
|
||||
use Drupal\Core\Plugin\Context\ContextDefinition;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Tests a condition that requires two users.
|
||||
*
|
||||
* @group condition_test
|
||||
*/
|
||||
class ConditionTestDualUserTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* An anonymous user for testing purposes.
|
||||
*
|
||||
* @var \Drupal\user\Entity\User
|
||||
*/
|
||||
protected $anonymous;
|
||||
|
||||
/**
|
||||
* An authenticated user for testing purposes.
|
||||
*
|
||||
* @var \Drupal\user\Entity\User
|
||||
*/
|
||||
protected $authenticated;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['system', 'user', 'condition_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installSchema('system', 'sequences');
|
||||
$this->installEntitySchema('user');
|
||||
|
||||
$this->anonymous = User::create(['uid' => 0]);
|
||||
$this->authenticated = User::create(['uid' => 1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the dual user condition.
|
||||
*/
|
||||
public function testConditions() {
|
||||
$this->doTestIdenticalUser();
|
||||
$this->doTestDifferentUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests with both contexts mapped to the same user.
|
||||
*/
|
||||
protected function doTestIdenticalUser() {
|
||||
/** @var \Drupal\Core\Condition\ConditionPluginBase $condition */
|
||||
$condition = \Drupal::service('plugin.manager.condition')
|
||||
->createInstance('condition_test_dual_user')
|
||||
// Map the anonymous user to both contexts.
|
||||
->setContextMapping([
|
||||
'user1' => 'anonymous',
|
||||
'user2' => 'anonymous',
|
||||
]);
|
||||
$definition = new ContextDefinition('entity:user');
|
||||
$contexts['anonymous'] = new Context($definition, $this->anonymous);
|
||||
\Drupal::service('context.handler')->applyContextMapping($condition, $contexts);
|
||||
$this->assertTrue($condition->execute());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests with each context mapped to different users.
|
||||
*/
|
||||
protected function doTestDifferentUser() {
|
||||
/** @var \Drupal\Core\Condition\ConditionPluginBase $condition */
|
||||
$condition = \Drupal::service('plugin.manager.condition')
|
||||
->createInstance('condition_test_dual_user')
|
||||
->setContextMapping([
|
||||
'user1' => 'anonymous',
|
||||
'user2' => 'authenticated',
|
||||
]);
|
||||
$definition = new ContextDefinition('entity:user');
|
||||
$contexts['anonymous'] = new Context($definition, $this->anonymous);
|
||||
$contexts['authenticated'] = new Context($definition, $this->authenticated);
|
||||
\Drupal::service('context.handler')->applyContextMapping($condition, $contexts);
|
||||
$this->assertFalse($condition->execute());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Plugin\Condition;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the CurrentThemeCondition plugin.
|
||||
*
|
||||
* @group Condition
|
||||
*/
|
||||
class CurrentThemeConditionTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('system', 'theme_test');
|
||||
|
||||
/**
|
||||
* Tests the current theme condition.
|
||||
*/
|
||||
public function testCurrentTheme() {
|
||||
\Drupal::service('theme_handler')->install(array('test_theme'));
|
||||
|
||||
$manager = \Drupal::service('plugin.manager.condition');
|
||||
/** @var $condition \Drupal\Core\Condition\ConditionInterface */
|
||||
$condition = $manager->createInstance('current_theme');
|
||||
$condition->setConfiguration(array('theme' => 'test_theme'));
|
||||
/** @var $condition_negated \Drupal\Core\Condition\ConditionInterface */
|
||||
$condition_negated = $manager->createInstance('current_theme');
|
||||
$condition_negated->setConfiguration(array('theme' => 'test_theme', 'negate' => TRUE));
|
||||
|
||||
$this->assertEqual($condition->summary(), SafeMarkup::format('The current theme is @theme', array('@theme' => 'test_theme')));
|
||||
$this->assertEqual($condition_negated->summary(), SafeMarkup::format('The current theme is not @theme', array('@theme' => 'test_theme')));
|
||||
|
||||
// The expected theme has not been set up yet.
|
||||
$this->assertFalse($condition->execute());
|
||||
$this->assertTrue($condition_negated->execute());
|
||||
|
||||
// Set the expected theme to be used.
|
||||
\Drupal::service('theme_handler')->setDefault('test_theme');
|
||||
\Drupal::theme()->resetActiveTheme();
|
||||
|
||||
$this->assertTrue($condition->execute());
|
||||
$this->assertFalse($condition_negated->execute());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Plugin\Condition;
|
||||
|
||||
use Drupal\Core\Plugin\Context\Context;
|
||||
use Drupal\Core\Plugin\Context\ContextDefinition;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
|
||||
/**
|
||||
* Tests a condition with optional context.
|
||||
*
|
||||
* @group condition_test
|
||||
*/
|
||||
class OptionalContextConditionTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['system', 'user', 'condition_test', 'node'];
|
||||
|
||||
/**
|
||||
* Tests with both contexts mapped to the same user.
|
||||
*/
|
||||
public function testContextMissing() {
|
||||
/** @var \Drupal\Core\Condition\ConditionPluginBase $condition */
|
||||
$condition = \Drupal::service('plugin.manager.condition')
|
||||
->createInstance('condition_test_optional_context')
|
||||
->setContextMapping([
|
||||
'node' => 'node',
|
||||
]);
|
||||
\Drupal::service('context.handler')->applyContextMapping($condition, []);
|
||||
$this->assertTrue($condition->execute());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests with both contexts mapped to the same user.
|
||||
*/
|
||||
public function testContextNoValue() {
|
||||
/** @var \Drupal\Core\Condition\ConditionPluginBase $condition */
|
||||
$condition = \Drupal::service('plugin.manager.condition')
|
||||
->createInstance('condition_test_optional_context')
|
||||
->setContextMapping([
|
||||
'node' => 'node',
|
||||
]);
|
||||
$definition = new ContextDefinition('entity:node');
|
||||
$contexts['node'] = (new Context($definition));
|
||||
\Drupal::service('context.handler')->applyContextMapping($condition, $contexts);
|
||||
$this->assertTrue($condition->execute());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests with both contexts mapped to the same user.
|
||||
*/
|
||||
public function testContextAvailable() {
|
||||
NodeType::create(['type' => 'example', 'name' => 'Example'])->save();
|
||||
/** @var \Drupal\Core\Condition\ConditionPluginBase $condition */
|
||||
$condition = \Drupal::service('plugin.manager.condition')
|
||||
->createInstance('condition_test_optional_context')
|
||||
->setContextMapping([
|
||||
'node' => 'node',
|
||||
]);
|
||||
$definition = new ContextDefinition('entity:node');
|
||||
$node = Node::create(['type' => 'example']);
|
||||
$contexts['node'] = new Context($definition, $node);
|
||||
\Drupal::service('context.handler')->applyContextMapping($condition, $contexts);
|
||||
$this->assertFalse($condition->execute());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Plugin\Condition;
|
||||
|
||||
use Drupal\Core\Path\CurrentPathStack;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\system\Tests\Routing\MockAliasManager;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
/**
|
||||
* Tests that the Request Path Condition, provided by the system module, is
|
||||
* working properly.
|
||||
*
|
||||
* @group Plugin
|
||||
*/
|
||||
class RequestPathTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* The condition plugin manager under test.
|
||||
*
|
||||
* @var \Drupal\Core\Condition\ConditionManager
|
||||
*/
|
||||
protected $pluginManager;
|
||||
|
||||
/**
|
||||
* The path alias manager used for testing.
|
||||
*
|
||||
* @var \Drupal\system\Tests\Routing\MockAliasManager
|
||||
*/
|
||||
protected $aliasManager;
|
||||
|
||||
/**
|
||||
* The request stack used for testing.
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\RequestStack
|
||||
*/
|
||||
protected $requestStack;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system', 'user', 'field', 'path');
|
||||
|
||||
/**
|
||||
* The current path.
|
||||
*
|
||||
* @var \Drupal\Core\Path\CurrentPathStack|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $currentPath;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installSchema('system', array('sequences'));
|
||||
|
||||
$this->pluginManager = $this->container->get('plugin.manager.condition');
|
||||
|
||||
// Set a mock alias manager in the container.
|
||||
$this->aliasManager = new MockAliasManager();
|
||||
$this->container->set('path.alias_manager', $this->aliasManager);
|
||||
|
||||
// Set the test request stack in the container.
|
||||
$this->requestStack = new RequestStack();
|
||||
$this->container->set('request_stack', $this->requestStack);
|
||||
|
||||
$this->currentPath = new CurrentPathStack($this->requestStack);
|
||||
$this->container->set('path.current', $this->currentPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the request path condition.
|
||||
*/
|
||||
public function testConditions() {
|
||||
|
||||
// Get the request path condition and test and configure it to check against
|
||||
// different patterns and requests.
|
||||
|
||||
$pages = "/my/pass/page\r\n/my/pass/page2\r\n/foo";
|
||||
|
||||
$request = Request::create('/my/pass/page2');
|
||||
$this->requestStack->push($request);
|
||||
|
||||
/* @var \Drupal\system\Plugin\Condition\RequestPath $condition */
|
||||
$condition = $this->pluginManager->createInstance('request_path');
|
||||
$condition->setConfig('pages', $pages);
|
||||
|
||||
$this->aliasManager->addAlias('/my/pass/page2', '/my/pass/page2');
|
||||
|
||||
$this->assertTrue($condition->execute(), 'The request path matches a standard path');
|
||||
$this->assertEqual($condition->summary(), 'Return true on the following pages: /my/pass/page, /my/pass/page2, /foo', 'The condition summary matches for a standard path');
|
||||
|
||||
// Test an aliased path.
|
||||
$this->currentPath->setPath('/my/aliased/page', $request);
|
||||
$this->requestStack->pop();
|
||||
$this->requestStack->push($request);
|
||||
|
||||
$this->aliasManager->addAlias('/my/aliased/page', '/my/pass/page');
|
||||
|
||||
$this->assertTrue($condition->execute(), 'The request path matches an aliased path');
|
||||
$this->assertEqual($condition->summary(), 'Return true on the following pages: /my/pass/page, /my/pass/page2, /foo', 'The condition summary matches for an aliased path');
|
||||
|
||||
// Test a wildcard path.
|
||||
$this->aliasManager->addAlias('/my/pass/page3', '/my/pass/page3');
|
||||
$this->currentPath->setPath('/my/pass/page3', $request);
|
||||
$this->requestStack->pop();
|
||||
$this->requestStack->push($request);
|
||||
|
||||
$condition->setConfig('pages', '/my/pass/*');
|
||||
|
||||
$this->assertTrue($condition->evaluate(), 'The system_path my/pass/page3 passes for wildcard paths.');
|
||||
$this->assertEqual($condition->summary(), 'Return true on the following pages: /my/pass/*', 'The condition summary matches for a wildcard path');
|
||||
|
||||
// Test a missing path.
|
||||
$this->requestStack->pop();
|
||||
$this->requestStack->push($request);
|
||||
$this->currentPath->setPath('/my/fail/page4', $request);
|
||||
|
||||
$condition->setConfig('pages', '/my/pass/*');
|
||||
|
||||
$this->aliasManager->addAlias('/my/fail/page4', '/my/fail/page4');
|
||||
|
||||
$this->assertFalse($condition->evaluate(), 'The system_path /my/pass/page4 fails for a missing path.');
|
||||
|
||||
// Test a path of '/'.
|
||||
$this->aliasManager->addAlias('/', '/my/pass/page3');
|
||||
$this->currentPath->setPath('/', $request);
|
||||
$this->requestStack->pop();
|
||||
$this->requestStack->push($request);
|
||||
|
||||
$this->assertTrue($condition->evaluate(), 'The system_path my/pass/page3 passes for wildcard paths.');
|
||||
$this->assertEqual($condition->summary(), 'Return true on the following pages: /my/pass/*', 'The condition summary matches for a wildcard path');
|
||||
|
||||
}
|
||||
|
||||
}
|
106
core/tests/Drupal/KernelTests/Core/Plugin/ContextPluginTest.php
Normal file
106
core/tests/Drupal/KernelTests/Core/Plugin/ContextPluginTest.php
Normal file
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Plugin;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\ContextException;
|
||||
use Drupal\Core\Plugin\Context\ContextDefinition;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\plugin_test\Plugin\MockBlockManager;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Tests that contexts are properly set and working within plugins.
|
||||
*
|
||||
* @group Plugin
|
||||
*/
|
||||
class ContextPluginTest extends KernelTestBase {
|
||||
|
||||
public static $modules = array('system', 'user', 'node', 'field', 'filter', 'text');
|
||||
|
||||
/**
|
||||
* Tests basic context definition and value getters and setters.
|
||||
*/
|
||||
function testContext() {
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('node');
|
||||
$this->installEntitySchema('node_type');
|
||||
$type = NodeType::create(['type' => 'page', 'name' => 'Page']);
|
||||
$type->save();
|
||||
|
||||
$name = $this->randomMachineName();
|
||||
$manager = new MockBlockManager();
|
||||
$plugin = $manager->createInstance('user_name');
|
||||
// Create a node, add it as context, catch the exception.
|
||||
$node = Node::create(['type' => 'page', 'title' => $name]);
|
||||
|
||||
// Try to get context that is missing its definition.
|
||||
try {
|
||||
$plugin->getContextDefinition('not_exists');
|
||||
$this->fail('The user context should not yet be set.');
|
||||
}
|
||||
catch (ContextException $e) {
|
||||
$this->assertEqual($e->getMessage(), 'The not_exists context is not a valid context.');
|
||||
}
|
||||
|
||||
// Test the getContextDefinitions() method.
|
||||
$user_context_definition = ContextDefinition::create('entity:user')->setLabel(t('User'));
|
||||
$this->assertEqual($plugin->getContextDefinitions()['user']->getLabel(), $user_context_definition->getLabel());
|
||||
|
||||
// Test the getContextDefinition() method for a valid context.
|
||||
$this->assertEqual($plugin->getContextDefinition('user')->getLabel(), $user_context_definition->getLabel());
|
||||
|
||||
// Try to get a context with valid definition.
|
||||
$this->assertNotNull($plugin->getContext('user'), 'Succeeded to get a context with a valid definition.');
|
||||
|
||||
// Try to get a value of a valid context, while this value has not been set.
|
||||
try {
|
||||
$plugin->getContextValue('user');
|
||||
}
|
||||
catch (ContextException $e) {
|
||||
$this->assertIdentical("The 'entity:user' context is required and not present.", $e->getMessage(), 'Requesting a non-set value of a required context should throw a context exception.');
|
||||
}
|
||||
|
||||
// Try to pass the wrong class type as a context value.
|
||||
$plugin->setContextValue('user', $node);
|
||||
$violations = $plugin->validateContexts();
|
||||
$this->assertTrue(!empty($violations), 'The provided context value does not pass validation.');
|
||||
|
||||
// Set an appropriate context value and check to make sure its methods work
|
||||
// as expected.
|
||||
$user = User::create(['name' => $name]);
|
||||
$plugin->setContextValue('user', $user);
|
||||
|
||||
$this->assertEqual($plugin->getContextValue('user')->getUsername(), $user->getUsername());
|
||||
$this->assertEqual($user->label(), $plugin->getTitle());
|
||||
|
||||
// Test Optional context handling.
|
||||
$plugin = $manager->createInstance('user_name_optional');
|
||||
$this->assertNull($plugin->getContextValue('user'), 'Requesting a non-set value of a valid context should return NULL.');
|
||||
|
||||
// Test Complex compound context handling.
|
||||
$complex_plugin = $manager->createInstance('complex_context');
|
||||
$complex_plugin->setContextValue('user', $user);
|
||||
|
||||
// With only the user context set, try to get the context values.
|
||||
$values = $complex_plugin->getContextValues();
|
||||
$this->assertNull($values['node'], 'The node context is not yet set.');
|
||||
$this->assertNotNull($values['user'], 'The user context is set');
|
||||
|
||||
$complex_plugin->setContextValue('node', $node);
|
||||
$context_wrappers = $complex_plugin->getContexts();
|
||||
// Make sure what came out of the wrappers is good.
|
||||
$this->assertEqual($context_wrappers['user']->getContextValue()->label(), $user->label());
|
||||
$this->assertEqual($context_wrappers['node']->getContextValue()->label(), $node->label());
|
||||
|
||||
// Make sure what comes out of the context values is good.
|
||||
$contexts = $complex_plugin->getContextValues();
|
||||
$this->assertEqual($contexts['user']->label(), $user->label());
|
||||
$this->assertEqual($contexts['node']->label(), $node->label());
|
||||
|
||||
// Test the title method for the complex context plugin.
|
||||
$this->assertEqual($user->label() . ' -- ' . $node->label(), $complex_plugin->getTitle());
|
||||
}
|
||||
|
||||
}
|
32
core/tests/Drupal/KernelTests/Core/Plugin/DerivativeTest.php
Normal file
32
core/tests/Drupal/KernelTests/Core/Plugin/DerivativeTest.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Plugin;
|
||||
|
||||
/**
|
||||
* Tests that derivative plugins are correctly discovered.
|
||||
*
|
||||
* @group Plugin
|
||||
*/
|
||||
class DerivativeTest extends PluginTestBase {
|
||||
|
||||
/**
|
||||
* Tests getDefinitions() and getDefinition() with a derivativeDecorator.
|
||||
*/
|
||||
function testDerivativeDecorator() {
|
||||
// Ensure that getDefinitions() returns the expected definitions.
|
||||
$this->assertEqual($this->mockBlockManager->getDefinitions(), $this->mockBlockExpectedDefinitions);
|
||||
|
||||
// Ensure that getDefinition() returns the expected definition.
|
||||
foreach ($this->mockBlockExpectedDefinitions as $id => $definition) {
|
||||
$this->assertEqual($this->mockBlockManager->getDefinition($id), $definition);
|
||||
}
|
||||
|
||||
// Ensure that NULL is returned as the definition of a non-existing base
|
||||
// plugin, a non-existing derivative plugin, or a base plugin that may not
|
||||
// be used without deriving.
|
||||
$this->assertIdentical($this->mockBlockManager->getDefinition('non_existing', FALSE), NULL, 'NULL returned as the definition of a non-existing base plugin.');
|
||||
$this->assertIdentical($this->mockBlockManager->getDefinition('menu:non_existing', FALSE), NULL, 'NULL returned as the definition of a non-existing derivative plugin.');
|
||||
$this->assertIdentical($this->mockBlockManager->getDefinition('menu', FALSE), NULL, 'NULL returned as the definition of a base plugin that may not be used without deriving.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Plugin\Discovery;
|
||||
|
||||
use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
|
||||
|
||||
/**
|
||||
* Tests that plugins are correctly discovered using annotated classes.
|
||||
*
|
||||
* @group Plugin
|
||||
*/
|
||||
class AnnotatedClassDiscoveryTest extends DiscoveryTestBase {
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->expectedDefinitions = array(
|
||||
'apple' => array(
|
||||
'id' => 'apple',
|
||||
'label' => 'Apple',
|
||||
'color' => 'green',
|
||||
'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Apple',
|
||||
'provider' => 'plugin_test',
|
||||
),
|
||||
'banana' => array(
|
||||
'id' => 'banana',
|
||||
'label' => 'Banana',
|
||||
'color' => 'yellow',
|
||||
'uses' => array(
|
||||
'bread' => t('Banana bread'),
|
||||
'loaf' => array(
|
||||
'singular' => '@count loaf',
|
||||
'plural' => '@count loaves',
|
||||
'context' => NULL,
|
||||
),
|
||||
),
|
||||
'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Banana',
|
||||
'provider' => 'plugin_test',
|
||||
),
|
||||
'cherry' => array(
|
||||
'id' => 'cherry',
|
||||
'label' => 'Cherry',
|
||||
'color' => 'red',
|
||||
'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Cherry',
|
||||
'provider' => 'plugin_test',
|
||||
),
|
||||
'kale' => array(
|
||||
'id' => 'kale',
|
||||
'label' => 'Kale',
|
||||
'color' => 'green',
|
||||
'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Kale',
|
||||
'provider' => 'plugin_test',
|
||||
),
|
||||
'orange' => array(
|
||||
'id' => 'orange',
|
||||
'label' => 'Orange',
|
||||
'color' => 'orange',
|
||||
'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Orange',
|
||||
'provider' => 'plugin_test',
|
||||
),
|
||||
'big_apple' => array(
|
||||
'id' => 'big_apple',
|
||||
'label' => 'Big Apple',
|
||||
'color' => 'green',
|
||||
'class' => 'Drupal\plugin_test_extended\Plugin\plugin_test\fruit\BigApple',
|
||||
'provider' => 'plugin_test_extended',
|
||||
),
|
||||
'extending_non_installed_class' => array(
|
||||
'id' => 'extending_non_installed_class',
|
||||
'label' => 'A plugin whose class is extending from a non-installed module class',
|
||||
'color' => 'pink',
|
||||
'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\ExtendingNonInstalledClass',
|
||||
'provider' => 'plugin_test',
|
||||
),
|
||||
);
|
||||
|
||||
$base_directory = \Drupal::root() . '/core/modules/system/tests/modules/plugin_test/src';
|
||||
$base_directory2 = \Drupal::root() . '/core/modules/system/tests/modules/plugin_test_extended/src';
|
||||
$namespaces = new \ArrayObject(array('Drupal\plugin_test' => $base_directory, 'Drupal\plugin_test_extended' => $base_directory2));
|
||||
|
||||
$annotation_namespaces = ['Drupal\plugin_test\Plugin\Annotation', 'Drupal\plugin_test_extended\Plugin\Annotation'];
|
||||
$this->discovery = new AnnotatedClassDiscovery('Plugin/plugin_test/fruit', $namespaces, 'Drupal\Component\Annotation\Plugin', $annotation_namespaces);
|
||||
$this->emptyDiscovery = new AnnotatedClassDiscovery('Plugin/non_existing_module/non_existing_plugin_type', $namespaces);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Plugin\Discovery;
|
||||
|
||||
use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
|
||||
|
||||
/**
|
||||
* Tests that a custom annotation class is used.
|
||||
*
|
||||
* @group Plugin
|
||||
* @see \Drupal\plugin_test\Plugin\Annotation\PluginExample
|
||||
*/
|
||||
class CustomAnnotationClassDiscoveryTest extends DiscoveryTestBase {
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->expectedDefinitions = array(
|
||||
'example_1' => array(
|
||||
'id' => 'example_1',
|
||||
'custom' => 'John',
|
||||
'class' => 'Drupal\plugin_test\Plugin\plugin_test\custom_annotation\Example1',
|
||||
'provider' => 'plugin_test',
|
||||
),
|
||||
'example_2' => array(
|
||||
'id' => 'example_2',
|
||||
'custom' => 'Paul',
|
||||
'class' => 'Drupal\plugin_test\Plugin\plugin_test\custom_annotation\Example2',
|
||||
'provider' => 'plugin_test',
|
||||
),
|
||||
);
|
||||
|
||||
$base_directory = \Drupal::root() . '/core/modules/system/tests/modules/plugin_test/src';
|
||||
$root_namespaces = new \ArrayObject(array('Drupal\plugin_test' => $base_directory));
|
||||
|
||||
$this->discovery = new AnnotatedClassDiscovery('Plugin/plugin_test/custom_annotation', $root_namespaces, 'Drupal\plugin_test\Plugin\Annotation\PluginExample');
|
||||
$this->emptyDiscovery = new AnnotatedClassDiscovery('Plugin/non_existing_module/non_existing_plugin_type', $root_namespaces, 'Drupal\plugin_test\Plugin\Annotation\PluginExample');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Plugin\Discovery;
|
||||
|
||||
use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
|
||||
|
||||
/**
|
||||
* Tests that plugins in a custom directory are correctly discovered using
|
||||
* annotated classes.
|
||||
*
|
||||
* @group Plugin
|
||||
*/
|
||||
class CustomDirectoryAnnotatedClassDiscoveryTest extends DiscoveryTestBase {
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->expectedDefinitions = array(
|
||||
'custom_example_1' => array(
|
||||
'id' => 'custom_example_1',
|
||||
'custom' => 'Tim',
|
||||
'class' => 'Drupal\plugin_test\CustomDirectoryExample1',
|
||||
'provider' => 'plugin_test',
|
||||
),
|
||||
'custom_example_2' => array(
|
||||
'id' => 'custom_example_2',
|
||||
'custom' => 'Meghan',
|
||||
'class' => 'Drupal\plugin_test\CustomDirectoryExample2',
|
||||
'provider' => 'plugin_test',
|
||||
),
|
||||
'apple' => array(
|
||||
'id' => 'apple',
|
||||
'label' => 'Apple',
|
||||
'color' => 'green',
|
||||
'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Apple',
|
||||
'provider' => 'plugin_test',
|
||||
),
|
||||
'banana' => array(
|
||||
'id' => 'banana',
|
||||
'label' => 'Banana',
|
||||
'color' => 'yellow',
|
||||
'uses' => array(
|
||||
'bread' => t('Banana bread'),
|
||||
'loaf' => array(
|
||||
'singular' => '@count loaf',
|
||||
'plural' => '@count loaves',
|
||||
'context' => NULL,
|
||||
),
|
||||
),
|
||||
'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Banana',
|
||||
'provider' => 'plugin_test',
|
||||
),
|
||||
'cherry' => array(
|
||||
'id' => 'cherry',
|
||||
'label' => 'Cherry',
|
||||
'color' => 'red',
|
||||
'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Cherry',
|
||||
'provider' => 'plugin_test',
|
||||
),
|
||||
'kale' => array(
|
||||
'id' => 'kale',
|
||||
'label' => 'Kale',
|
||||
'color' => 'green',
|
||||
'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Kale',
|
||||
'provider' => 'plugin_test',
|
||||
),
|
||||
'orange' => array(
|
||||
'id' => 'orange',
|
||||
'label' => 'Orange',
|
||||
'color' => 'orange',
|
||||
'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Orange',
|
||||
'provider' => 'plugin_test',
|
||||
),
|
||||
'extending_non_installed_class' => array(
|
||||
'id' => 'extending_non_installed_class',
|
||||
'label' => 'A plugin whose class is extending from a non-installed module class',
|
||||
'color' => 'pink',
|
||||
'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\ExtendingNonInstalledClass',
|
||||
'provider' => 'plugin_test',
|
||||
),
|
||||
);
|
||||
|
||||
$base_directory = \Drupal::root() . '/core/modules/system/tests/modules/plugin_test/src';
|
||||
$namespaces = new \ArrayObject(array('Drupal\plugin_test' => $base_directory));
|
||||
|
||||
$this->discovery = new AnnotatedClassDiscovery('', $namespaces);
|
||||
$empty_namespaces = new \ArrayObject();
|
||||
$this->emptyDiscovery = new AnnotatedClassDiscovery('', $empty_namespaces);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Plugin\Discovery;
|
||||
|
||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Base class for plugin discovery tests.
|
||||
*/
|
||||
abstract class DiscoveryTestBase extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* The discovery component to test.
|
||||
*
|
||||
* @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface
|
||||
*/
|
||||
protected $discovery;
|
||||
|
||||
/**
|
||||
* The plugin definitions the discovery component is expected to discover.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $expectedDefinitions;
|
||||
|
||||
/**
|
||||
* An empty discovery component.
|
||||
*
|
||||
* This will be tested to ensure that the case where no plugin information is
|
||||
* found, is handled correctly.
|
||||
*
|
||||
* @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface
|
||||
*/
|
||||
protected $emptyDiscovery;
|
||||
|
||||
/**
|
||||
* Tests getDefinitions() and getDefinition().
|
||||
*/
|
||||
function testDiscoveryInterface() {
|
||||
// Ensure that getDefinitions() returns the expected definitions.
|
||||
// For the arrays to be identical (instead of only equal), they must be
|
||||
// sorted equally, which seems unnecessary here.
|
||||
// The discovered definitions may contain circular references; use a custom
|
||||
// assertion message to prevent var_export() from getting called.
|
||||
$this->assertEqual($this->discovery->getDefinitions(), $this->expectedDefinitions, 'Expected definitions found.');
|
||||
|
||||
// Ensure that getDefinition() returns the expected definition.
|
||||
foreach ($this->expectedDefinitions as $id => $definition) {
|
||||
$this->assertDefinitionIdentical($this->discovery->getDefinition($id), $definition);
|
||||
}
|
||||
|
||||
// Ensure that an empty array is returned if no plugin definitions are found.
|
||||
$this->assertIdentical($this->emptyDiscovery->getDefinitions(), array(), 'array() returned if no plugin definitions are found.');
|
||||
|
||||
// Ensure that NULL is returned as the definition of a non-existing plugin.
|
||||
$this->assertIdentical($this->emptyDiscovery->getDefinition('non_existing', FALSE), NULL, 'NULL returned as the definition of a non-existing plugin.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts a definition against an expected definition.
|
||||
*
|
||||
* Converts any instances of \Drupal\Core\Annotation\Translation to a string.
|
||||
*
|
||||
* @param array $definition
|
||||
* The definition to test.
|
||||
* @param array $expected_definition
|
||||
* The expected definition to test against.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the assertion succeeded, FALSE otherwise.
|
||||
*/
|
||||
protected function assertDefinitionIdentical(array $definition, array $expected_definition) {
|
||||
$func = function (&$item){
|
||||
if ($item instanceof TranslatableMarkup) {
|
||||
$item = (string) $item;
|
||||
}
|
||||
};
|
||||
array_walk_recursive($definition, $func);
|
||||
array_walk_recursive($expected_definition, $func);
|
||||
return $this->assertIdentical($definition, $expected_definition);
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue