Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176
This commit is contained in:
commit
9921556621
13277 changed files with 1459781 additions and 0 deletions
7
core/modules/config/config.info.yml
Normal file
7
core/modules/config/config.info.yml
Normal file
|
@ -0,0 +1,7 @@
|
|||
name: 'Configuration Manager'
|
||||
type: module
|
||||
description: 'Allows administrators to manage configuration changes.'
|
||||
package: Core
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
configure: config.sync
|
5
core/modules/config/config.links.menu.yml
Normal file
5
core/modules/config/config.links.menu.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
config.sync:
|
||||
title: 'Configuration management'
|
||||
description: 'Import, export, or synchronize your site configuration.'
|
||||
route_name: config.sync
|
||||
parent: system.admin_config_development
|
34
core/modules/config/config.links.task.yml
Normal file
34
core/modules/config/config.links.task.yml
Normal file
|
@ -0,0 +1,34 @@
|
|||
config.sync:
|
||||
route_name: config.sync
|
||||
base_route: config.sync
|
||||
title: 'Synchronize'
|
||||
|
||||
config.full:
|
||||
route_name: config.import_full
|
||||
title: 'Full Import/Export'
|
||||
base_route: config.sync
|
||||
|
||||
config.single:
|
||||
route_name: config.import_single
|
||||
title: 'Single Import/Export'
|
||||
base_route: config.sync
|
||||
|
||||
config.export_full:
|
||||
route_name: config.export_full
|
||||
title: Export
|
||||
parent_id: config.full
|
||||
|
||||
config.import_full:
|
||||
route_name: config.import_full
|
||||
title: Import
|
||||
parent_id: config.full
|
||||
|
||||
config.export_single:
|
||||
route_name: config.export_single
|
||||
title: Export
|
||||
parent_id: config.single
|
||||
|
||||
config.import_single:
|
||||
route_name: config.import_single
|
||||
title: Import
|
||||
parent_id: config.single
|
46
core/modules/config/config.module
Normal file
46
core/modules/config/config.module
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Allows site administrators to modify configuration.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
function config_help($route_name, RouteMatchInterface $route_match) {
|
||||
switch ($route_name) {
|
||||
case 'help.page.config':
|
||||
$output = '';
|
||||
$output .= '<h3>' . t('About') . '</h3>';
|
||||
$output .= '<p>' . t('The Configuration manager module provides a user interface for importing and exporting configuration changes; i.e., for staging configuration data between multiple instances of this web site. For more information, see the online handbook entry for <a href="!url">Configuration manager module</a>', array(
|
||||
'!url' => 'https://www.drupal.org/documentation/administer/config',
|
||||
)) . '</p>';
|
||||
return $output;
|
||||
|
||||
case 'config.sync':
|
||||
$output = '';
|
||||
$output .= '<p>' . t('Import configuration that is placed in your staging directory. All changes, deletions, renames, and additions are listed below.') . '</p>';
|
||||
return $output;
|
||||
|
||||
case 'config.import_full':
|
||||
$output = '';
|
||||
$output .= '<p>' . t('After uploading a configuration archive, you will be able to examine the changes and import them.') . '</p>';
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_file_download().
|
||||
*/
|
||||
function config_file_download($uri) {
|
||||
$scheme = file_uri_scheme($uri);
|
||||
$target = file_uri_target($uri);
|
||||
if ($scheme == 'temporary' && $target == 'config.tar.gz') {
|
||||
return array(
|
||||
'Content-disposition' => 'attachment; filename="config.tar.gz"',
|
||||
);
|
||||
}
|
||||
}
|
9
core/modules/config/config.permissions.yml
Normal file
9
core/modules/config/config.permissions.yml
Normal file
|
@ -0,0 +1,9 @@
|
|||
synchronize configuration:
|
||||
title: 'Synchronize configuration'
|
||||
restrict access: true
|
||||
export configuration:
|
||||
title: 'Export configuration'
|
||||
restrict access: true
|
||||
import configuration:
|
||||
title: 'Import configuration'
|
||||
restrict access: true
|
64
core/modules/config/config.routing.yml
Normal file
64
core/modules/config/config.routing.yml
Normal file
|
@ -0,0 +1,64 @@
|
|||
config.sync:
|
||||
path: '/admin/config/development/configuration'
|
||||
defaults:
|
||||
_form: '\Drupal\config\Form\ConfigSync'
|
||||
_title: 'Synchronize'
|
||||
requirements:
|
||||
_permission: 'synchronize configuration'
|
||||
|
||||
config.diff:
|
||||
path: '/admin/config/development/configuration/sync/diff/{source_name}/{target_name}'
|
||||
defaults:
|
||||
_controller: '\Drupal\config\Controller\ConfigController::diff'
|
||||
target_name: NULL
|
||||
requirements:
|
||||
_permission: 'synchronize configuration'
|
||||
|
||||
config.diff_collection:
|
||||
path: '/admin/config/development/configuration/sync/diff_collection/{collection}/{source_name}/{target_name}'
|
||||
defaults:
|
||||
_controller: '\Drupal\config\Controller\ConfigController::diff'
|
||||
target_name: NULL
|
||||
requirements:
|
||||
_permission: 'synchronize configuration'
|
||||
|
||||
config.export_download:
|
||||
path: '/admin/config/development/configuration/full/export-download'
|
||||
defaults:
|
||||
_controller: '\Drupal\config\Controller\ConfigController::downloadExport'
|
||||
requirements:
|
||||
_permission: 'export configuration'
|
||||
|
||||
config.export_full:
|
||||
path: '/admin/config/development/configuration/full/export'
|
||||
defaults:
|
||||
_form: '\Drupal\config\Form\ConfigExportForm'
|
||||
_title: 'Export'
|
||||
requirements:
|
||||
_permission: 'export configuration'
|
||||
|
||||
config.import_full:
|
||||
path: '/admin/config/development/configuration/full/import'
|
||||
defaults:
|
||||
_form: '\Drupal\config\Form\ConfigImportForm'
|
||||
_title: 'Import'
|
||||
requirements:
|
||||
_permission: 'import configuration'
|
||||
|
||||
config.import_single:
|
||||
path: '/admin/config/development/configuration/single/import'
|
||||
defaults:
|
||||
_title: 'Single import'
|
||||
_form: '\Drupal\config\Form\ConfigSingleImportForm'
|
||||
requirements:
|
||||
_permission: 'import configuration'
|
||||
|
||||
config.export_single:
|
||||
path: '/admin/config/development/configuration/single/export/{config_type}/{config_name}'
|
||||
defaults:
|
||||
_title: 'Single export'
|
||||
_form: '\Drupal\config\Form\ConfigSingleExportForm'
|
||||
config_type: NULL
|
||||
config_name: NULL
|
||||
requirements:
|
||||
_permission: 'export configuration'
|
5
core/modules/config/config.services.yml
Normal file
5
core/modules/config/config.services.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
services:
|
||||
config.config_subscriber:
|
||||
class: Drupal\config\ConfigSubscriber
|
||||
tags:
|
||||
- { name: event_subscriber }
|
42
core/modules/config/src/ConfigSubscriber.php
Normal file
42
core/modules/config/src/ConfigSubscriber.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\ConfigSubscriber.
|
||||
*/
|
||||
|
||||
namespace Drupal\config;
|
||||
|
||||
use Drupal\Core\Config\ConfigEvents;
|
||||
use Drupal\Core\Config\ConfigImporterEvent;
|
||||
use Drupal\Core\Config\ConfigImporterException;
|
||||
use Drupal\Core\Config\ConfigImportValidateEventSubscriberBase;
|
||||
|
||||
|
||||
/**
|
||||
* Config subscriber.
|
||||
*/
|
||||
class ConfigSubscriber extends ConfigImportValidateEventSubscriberBase {
|
||||
|
||||
/**
|
||||
* Checks that the Configuration module is not being uninstalled.
|
||||
*
|
||||
* @param ConfigImporterEvent $event
|
||||
* The config import event.
|
||||
*/
|
||||
public function onConfigImporterValidate(ConfigImporterEvent $event) {
|
||||
$importer = $event->getConfigImporter();
|
||||
$core_extension = $importer->getStorageComparer()->getSourceStorage()->read('core.extension');
|
||||
if (!isset($core_extension['module']['config'])) {
|
||||
$importer->logError($this->t('Can not uninstall the Configuration module as part of a configuration synchronization through the user interface.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
static function getSubscribedEvents() {
|
||||
$events[ConfigEvents::IMPORT_VALIDATE][] = array('onConfigImporterValidate', 20);
|
||||
return $events;
|
||||
}
|
||||
}
|
165
core/modules/config/src/Controller/ConfigController.php
Normal file
165
core/modules/config/src/Controller/ConfigController.php
Normal file
|
@ -0,0 +1,165 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Controller\ConfigController.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Controller;
|
||||
|
||||
use Drupal\Core\Archiver\ArchiveTar;
|
||||
use Drupal\Component\Serialization\Yaml;
|
||||
use Drupal\Core\Config\ConfigManagerInterface;
|
||||
use Drupal\Core\Config\StorageInterface;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Diff\DiffFormatter;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\system\FileDownloadController;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Returns responses for config module routes.
|
||||
*/
|
||||
class ConfigController implements ContainerInjectionInterface {
|
||||
|
||||
/**
|
||||
* The target storage.
|
||||
*
|
||||
* @var \Drupal\Core\Config\StorageInterface
|
||||
*/
|
||||
protected $targetStorage;
|
||||
|
||||
/**
|
||||
* The source storage.
|
||||
*
|
||||
* @var \Drupal\Core\Config\StorageInterface
|
||||
*/
|
||||
protected $sourceStorage;
|
||||
|
||||
/**
|
||||
* The configuration manager.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigManagerInterface
|
||||
*/
|
||||
protected $configManager;
|
||||
|
||||
/**
|
||||
* The file download controller.
|
||||
*
|
||||
* @var \Drupal\system\FileDownloadController
|
||||
*/
|
||||
protected $fileDownloadController;
|
||||
|
||||
/**
|
||||
* The diff formatter.
|
||||
*
|
||||
* @var \Drupal\Core\Diff\DiffFormatter
|
||||
*/
|
||||
protected $diffFormatter;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('config.storage'),
|
||||
$container->get('config.storage.staging'),
|
||||
$container->get('config.manager'),
|
||||
new FileDownloadController(),
|
||||
$container->get('diff.formatter')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a ConfigController object.
|
||||
*
|
||||
* @param \Drupal\Core\Config\StorageInterface $target_storage
|
||||
* The target storage.
|
||||
* @param \Drupal\Core\Config\StorageInterface $source_storage
|
||||
* The source storage
|
||||
* @param \Drupal\system\FileDownloadController $file_download_controller
|
||||
* The file download controller.
|
||||
*/
|
||||
public function __construct(StorageInterface $target_storage, StorageInterface $source_storage, ConfigManagerInterface $config_manager, FileDownloadController $file_download_controller, DiffFormatter $diff_formatter) {
|
||||
$this->targetStorage = $target_storage;
|
||||
$this->sourceStorage = $source_storage;
|
||||
$this->configManager = $config_manager;
|
||||
$this->fileDownloadController = $file_download_controller;
|
||||
$this->diffFormatter = $diff_formatter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads a tarball of the site configuration.
|
||||
*/
|
||||
public function downloadExport() {
|
||||
file_unmanaged_delete(file_directory_temp() . '/config.tar.gz');
|
||||
|
||||
$archiver = new ArchiveTar(file_directory_temp() . '/config.tar.gz', 'gz');
|
||||
// Get raw configuration data without overrides.
|
||||
foreach ($this->configManager->getConfigFactory()->listAll() as $name) {
|
||||
$archiver->addString("$name.yml", Yaml::encode($this->configManager->getConfigFactory()->get($name)->getRawData()));
|
||||
}
|
||||
// Get all override data from the remaining collections.
|
||||
foreach ($this->targetStorage->getAllCollectionNames() as $collection) {
|
||||
$collection_storage = $this->targetStorage->createCollection($collection);
|
||||
foreach ($collection_storage->listAll() as $name) {
|
||||
$archiver->addString(str_replace('.', '/', $collection) . "/$name.yml", Yaml::encode($collection_storage->read($name)));
|
||||
}
|
||||
}
|
||||
|
||||
$request = new Request(array('file' => 'config.tar.gz'));
|
||||
return $this->fileDownloadController->download($request, 'temporary');
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows diff of specified configuration file.
|
||||
*
|
||||
* @param string $source_name
|
||||
* The name of the configuration file.
|
||||
* @param string $target_name
|
||||
* (optional) The name of the target configuration file if different from
|
||||
* the $source_name.
|
||||
* @param string $collection
|
||||
* (optional) The configuration collection name. Defaults to the default
|
||||
* collection.
|
||||
*
|
||||
* @return string
|
||||
* Table showing a two-way diff between the active and staged configuration.
|
||||
*/
|
||||
public function diff($source_name, $target_name = NULL, $collection = NULL) {
|
||||
if (!isset($collection)) {
|
||||
$collection = StorageInterface::DEFAULT_COLLECTION;
|
||||
}
|
||||
$diff = $this->configManager->diff($this->targetStorage, $this->sourceStorage, $source_name, $target_name, $collection);
|
||||
$this->diffFormatter->show_header = FALSE;
|
||||
|
||||
$build = array();
|
||||
|
||||
$build['#title'] = t('View changes of @config_file', array('@config_file' => $source_name));
|
||||
// Add the CSS for the inline diff.
|
||||
$build['#attached']['library'][] = 'system/diff';
|
||||
|
||||
$build['diff'] = array(
|
||||
'#type' => 'table',
|
||||
'#header' => array(
|
||||
array('data' => t('Old'), 'colspan' => '2'),
|
||||
array('data' => t('New'), 'colspan' => '2'),
|
||||
),
|
||||
'#rows' => $this->diffFormatter->format($diff),
|
||||
);
|
||||
|
||||
$build['back'] = array(
|
||||
'#type' => 'link',
|
||||
'#attributes' => array(
|
||||
'class' => array(
|
||||
'dialog-cancel',
|
||||
),
|
||||
),
|
||||
'#title' => "Back to 'Synchronize configuration' page.",
|
||||
'#url' => Url::fromRoute('config.sync'),
|
||||
);
|
||||
|
||||
return $build;
|
||||
}
|
||||
}
|
46
core/modules/config/src/Form/ConfigExportForm.php
Normal file
46
core/modules/config/src/Form/ConfigExportForm.php
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Form\ConfigExportForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Form;
|
||||
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Defines the configuration export form.
|
||||
*/
|
||||
class ConfigExportForm extends FormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'config_export_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$form['description'] = array(
|
||||
'#markup' => '<p>' . $this->t('Use the export button below to download your site configuration.') . '</p>',
|
||||
);
|
||||
$form['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Export'),
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$form_state->setRedirect('config.export_download');
|
||||
}
|
||||
|
||||
}
|
108
core/modules/config/src/Form/ConfigImportForm.php
Normal file
108
core/modules/config/src/Form/ConfigImportForm.php
Normal file
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Form\ConfigImportForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Form;
|
||||
|
||||
use Drupal\Core\Archiver\ArchiveTar;
|
||||
use Drupal\Core\Config\StorageInterface;
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines the configuration import form.
|
||||
*/
|
||||
class ConfigImportForm extends FormBase {
|
||||
|
||||
/**
|
||||
* The configuration storage.
|
||||
*
|
||||
* @var \Drupal\Core\Config\StorageInterface
|
||||
*/
|
||||
protected $configStorage;
|
||||
|
||||
/**
|
||||
* Constructs a new ConfigImportForm.
|
||||
*
|
||||
* @param \Drupal\Core\Config\StorageInterface $config_storage
|
||||
* The configuration storage.
|
||||
*/
|
||||
public function __construct(StorageInterface $config_storage) {
|
||||
$this->configStorage = $config_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('config.storage.staging')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'config_import_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$form['import_tarball'] = array(
|
||||
'#type' => 'file',
|
||||
'#title' => $this->t('Configuration archive'),
|
||||
'#description' => $this->t('Allowed types: @extensions.', array('@extensions' => 'tar.gz tgz tar.bz2')),
|
||||
);
|
||||
|
||||
$form['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Upload'),
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
$file_upload = $this->getRequest()->files->get('files[import_tarball]', NULL, TRUE);
|
||||
if ($file_upload && $file_upload->isValid()) {
|
||||
$form_state->setValue('import_tarball', $file_upload->getRealPath());
|
||||
}
|
||||
else {
|
||||
$form_state->setErrorByName('import_tarball', $this->t('The file could not be uploaded.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
if ($path = $form_state->getValue('import_tarball')) {
|
||||
$this->configStorage->deleteAll();
|
||||
try {
|
||||
$archiver = new ArchiveTar($path, 'gz');
|
||||
$files = array();
|
||||
foreach ($archiver->listContent() as $file) {
|
||||
$files[] = $file['filename'];
|
||||
}
|
||||
$archiver->extractList($files, config_get_config_directory(CONFIG_STAGING_DIRECTORY));
|
||||
drupal_set_message($this->t('Your configuration files were successfully uploaded and are ready for import.'));
|
||||
$form_state->setRedirect('config.sync');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
drupal_set_message($this->t('Could not extract the contents of the tar file. The error message is <em>@message</em>', array('@message' => $e->getMessage())), 'error');
|
||||
}
|
||||
drupal_unlink($path);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
206
core/modules/config/src/Form/ConfigSingleExportForm.php
Normal file
206
core/modules/config/src/Form/ConfigSingleExportForm.php
Normal file
|
@ -0,0 +1,206 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Form\ConfigSingleExportForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Form;
|
||||
|
||||
use Drupal\Component\Serialization\Yaml;
|
||||
use Drupal\Core\Config\StorageInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormState;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a form for exporting a single configuration file.
|
||||
*/
|
||||
class ConfigSingleExportForm extends FormBase {
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* The config storage.
|
||||
*
|
||||
* @var \Drupal\Core\Config\StorageInterface
|
||||
*/
|
||||
protected $configStorage;
|
||||
|
||||
/**
|
||||
* Tracks the valid config entity type definitions.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeInterface[]
|
||||
*/
|
||||
protected $definitions = array();
|
||||
|
||||
/**
|
||||
* Constructs a new ConfigSingleImportForm.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
* @param \Drupal\Core\Config\StorageInterface $config_storage
|
||||
* The config storage.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager, StorageInterface $config_storage) {
|
||||
$this->entityManager = $entity_manager;
|
||||
$this->configStorage = $config_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.manager'),
|
||||
$container->get('config.storage')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'config_single_export_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, $config_type = NULL, $config_name = NULL) {
|
||||
foreach ($this->entityManager->getDefinitions() as $entity_type => $definition) {
|
||||
if ($definition->isSubclassOf('Drupal\Core\Config\Entity\ConfigEntityInterface')) {
|
||||
$this->definitions[$entity_type] = $definition;
|
||||
}
|
||||
}
|
||||
$entity_types = array_map(function (EntityTypeInterface $definition) {
|
||||
return $definition->getLabel();
|
||||
}, $this->definitions);
|
||||
// Sort the entity types by label, then add the simple config to the top.
|
||||
uasort($entity_types, 'strnatcasecmp');
|
||||
$config_types = array(
|
||||
'system.simple' => $this->t('Simple configuration'),
|
||||
) + $entity_types;
|
||||
$form['config_type'] = array(
|
||||
'#title' => $this->t('Configuration type'),
|
||||
'#type' => 'select',
|
||||
'#options' => $config_types,
|
||||
'#default_value' => $config_type,
|
||||
'#ajax' => array(
|
||||
'callback' => '::updateConfigurationType',
|
||||
'wrapper' => 'edit-config-type-wrapper',
|
||||
),
|
||||
);
|
||||
$default_type = $form_state->getValue('config_type', $config_type);
|
||||
$form['config_name'] = array(
|
||||
'#title' => $this->t('Configuration name'),
|
||||
'#type' => 'select',
|
||||
'#options' => $this->findConfiguration($default_type),
|
||||
'#default_value' => $config_name,
|
||||
'#required' => TRUE,
|
||||
'#prefix' => '<div id="edit-config-type-wrapper">',
|
||||
'#suffix' => '</div>',
|
||||
'#ajax' => array(
|
||||
'callback' => '::updateExport',
|
||||
'wrapper' => 'edit-export-wrapper',
|
||||
),
|
||||
);
|
||||
|
||||
$form['export'] = array(
|
||||
'#title' => $this->t('Here is your configuration:'),
|
||||
'#type' => 'textarea',
|
||||
'#rows' => 24,
|
||||
'#required' => TRUE,
|
||||
'#prefix' => '<div id="edit-export-wrapper">',
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
if ($config_type && $config_name) {
|
||||
$fake_form_state = (new FormState())->setValues([
|
||||
'config_type' => $config_type,
|
||||
'config_name' => $config_name,
|
||||
]);
|
||||
$form['export'] = $this->updateExport($form, $fake_form_state);
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles switching the configuration type selector.
|
||||
*/
|
||||
public function updateConfigurationType($form, FormStateInterface $form_state) {
|
||||
$form['config_name']['#options'] = $this->findConfiguration($form_state->getValue('config_type'));
|
||||
return $form['config_name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles switching the export textarea.
|
||||
*/
|
||||
public function updateExport($form, FormStateInterface $form_state) {
|
||||
// Determine the full config name for the selected config entity.
|
||||
if ($form_state->getValue('config_type') !== 'system.simple') {
|
||||
$definition = $this->entityManager->getDefinition($form_state->getValue('config_type'));
|
||||
$name = $definition->getConfigPrefix() . '.' . $form_state->getValue('config_name');
|
||||
}
|
||||
// The config name is used directly for simple configuration.
|
||||
else {
|
||||
$name = $form_state->getValue('config_name');
|
||||
}
|
||||
// Read the raw data for this config name, encode it, and display it.
|
||||
$form['export']['#value'] = Yaml::encode($this->configStorage->read($name));
|
||||
$form['export']['#description'] = $this->t('Filename: %name', array('%name' => $name . '.yml'));
|
||||
return $form['export'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles switching the configuration type selector.
|
||||
*/
|
||||
protected function findConfiguration($config_type) {
|
||||
$names = array(
|
||||
'' => $this->t('- Select -'),
|
||||
);
|
||||
// For a given entity type, load all entities.
|
||||
if ($config_type && $config_type !== 'system.simple') {
|
||||
$entity_storage = $this->entityManager->getStorage($config_type);
|
||||
foreach ($entity_storage->loadMultiple() as $entity) {
|
||||
$entity_id = $entity->id();
|
||||
$label = $entity->label() ?: $entity_id;
|
||||
$names[$entity_id] = $label;
|
||||
}
|
||||
}
|
||||
// Handle simple configuration.
|
||||
else {
|
||||
// Gather the config entity prefixes.
|
||||
$config_prefixes = array_map(function (EntityTypeInterface $definition) {
|
||||
return $definition->getConfigPrefix() . '.';
|
||||
}, $this->definitions);
|
||||
|
||||
// Find all config, and then filter our anything matching a config prefix.
|
||||
$names = $this->configStorage->listAll();
|
||||
$names = array_combine($names, $names);
|
||||
foreach ($names as $config_name) {
|
||||
foreach ($config_prefixes as $config_prefix) {
|
||||
if (strpos($config_name, $config_prefix) === 0) {
|
||||
unset($names[$config_name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $names;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
// Nothing to submit.
|
||||
}
|
||||
|
||||
}
|
268
core/modules/config/src/Form/ConfigSingleImportForm.php
Normal file
268
core/modules/config/src/Form/ConfigSingleImportForm.php
Normal file
|
@ -0,0 +1,268 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Form\ConfigSingleImportForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Form;
|
||||
|
||||
use Drupal\Component\Serialization\Yaml;
|
||||
use Drupal\Core\Config\StorageInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Form\ConfirmFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a form for importing a single configuration file.
|
||||
*/
|
||||
class ConfigSingleImportForm extends ConfirmFormBase {
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* The config storage.
|
||||
*
|
||||
* @var \Drupal\Core\Config\StorageInterface
|
||||
*/
|
||||
protected $configStorage;
|
||||
|
||||
/**
|
||||
* If the config exists, this is that object. Otherwise, FALSE.
|
||||
*
|
||||
* @var \Drupal\Core\Config\Config|\Drupal\Core\Config\Entity\ConfigEntityInterface|bool
|
||||
*/
|
||||
protected $configExists = FALSE;
|
||||
|
||||
/**
|
||||
* The submitted data needing to be confirmed.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data = array();
|
||||
|
||||
/**
|
||||
* Constructs a new ConfigSingleImportForm.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
* @param \Drupal\Core\Config\StorageInterface $config_storage
|
||||
* The config storage.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager, StorageInterface $config_storage) {
|
||||
$this->entityManager = $entity_manager;
|
||||
$this->configStorage = $config_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.manager'),
|
||||
$container->get('config.storage')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'config_single_import_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
return new Url('config.import_single');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {
|
||||
if ($this->data['config_type'] === 'system.simple') {
|
||||
$name = $this->data['config_name'];
|
||||
$type = $this->t('simple configuration');
|
||||
}
|
||||
else {
|
||||
$definition = $this->entityManager->getDefinition($this->data['config_type']);
|
||||
$name = $this->data['import'][$definition->getKey('id')];
|
||||
$type = $definition->getLowercaseLabel();
|
||||
}
|
||||
|
||||
$args = array(
|
||||
'%name' => $name,
|
||||
'@type' => strtolower($type),
|
||||
);
|
||||
if ($this->configExists) {
|
||||
$question = $this->t('Are you sure you want to update the %name @type?', $args);
|
||||
}
|
||||
else {
|
||||
$question = $this->t('Are you sure you want to create a new %name @type?', $args);
|
||||
}
|
||||
return $question;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
// When this is the confirmation step fall through to the confirmation form.
|
||||
if ($this->data) {
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
$entity_types = array();
|
||||
foreach ($this->entityManager->getDefinitions() as $entity_type => $definition) {
|
||||
if ($definition->isSubclassOf('Drupal\Core\Config\Entity\ConfigEntityInterface')) {
|
||||
$entity_types[$entity_type] = $definition->getLabel();
|
||||
}
|
||||
}
|
||||
// Sort the entity types by label, then add the simple config to the top.
|
||||
uasort($entity_types, 'strnatcasecmp');
|
||||
$config_types = array(
|
||||
'system.simple' => $this->t('Simple configuration'),
|
||||
) + $entity_types;
|
||||
$form['config_type'] = array(
|
||||
'#title' => $this->t('Configuration type'),
|
||||
'#type' => 'select',
|
||||
'#options' => $config_types,
|
||||
'#required' => TRUE,
|
||||
);
|
||||
$form['config_name'] = array(
|
||||
'#title' => $this->t('Configuration name'),
|
||||
'#description' => $this->t('Enter the name of the configuration file without the <em>.yml</em> extension. (e.g. <em>system.site</em>)'),
|
||||
'#type' => 'textfield',
|
||||
'#states' => array(
|
||||
'required' => array(
|
||||
':input[name="config_type"]' => array('value' => 'system.simple'),
|
||||
),
|
||||
'visible' => array(
|
||||
':input[name="config_type"]' => array('value' => 'system.simple'),
|
||||
),
|
||||
),
|
||||
);
|
||||
$form['import'] = array(
|
||||
'#title' => $this->t('Paste your configuration here'),
|
||||
'#type' => 'textarea',
|
||||
'#rows' => 24,
|
||||
'#required' => TRUE,
|
||||
);
|
||||
$form['advanced'] = array(
|
||||
'#type' => 'details',
|
||||
'#title' => $this->t('Advanced'),
|
||||
);
|
||||
$form['advanced']['custom_entity_id'] = array(
|
||||
'#title' => $this->t('Custom Entity ID'),
|
||||
'#type' => 'textfield',
|
||||
'#description' => $this->t('Specify a custom entity ID. This will override the entity ID in the configuration above.'),
|
||||
);
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Import'),
|
||||
'#button_type' => 'primary',
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
// The confirmation step needs no additional validation.
|
||||
if ($this->data) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Decode the submitted import.
|
||||
$data = Yaml::decode($form_state->getValue('import'));
|
||||
|
||||
// Validate for config entities.
|
||||
if ($form_state->getValue('config_type') !== 'system.simple') {
|
||||
$definition = $this->entityManager->getDefinition($form_state->getValue('config_type'));
|
||||
$id_key = $definition->getKey('id');
|
||||
|
||||
// If a custom entity ID is specified, override the value in the
|
||||
// configuration data being imported.
|
||||
if (!$form_state->isValueEmpty('custom_entity_id')) {
|
||||
$data[$id_key] = $form_state->getValue('custom_entity_id');
|
||||
}
|
||||
|
||||
$entity_storage = $this->entityManager->getStorage($form_state->getValue('config_type'));
|
||||
// If an entity ID was not specified, set an error.
|
||||
if (!isset($data[$id_key])) {
|
||||
$form_state->setErrorByName('import', $this->t('Missing ID key "@id_key" for this @entity_type import.', array('@id_key' => $id_key, '@entity_type' => $definition->getLabel())));
|
||||
return;
|
||||
}
|
||||
// If there is an existing entity, ensure matching ID and UUID.
|
||||
if ($entity = $entity_storage->load($data[$id_key])) {
|
||||
$this->configExists = $entity;
|
||||
if (!isset($data['uuid'])) {
|
||||
$form_state->setErrorByName('import', $this->t('An entity with this machine name already exists but the import did not specify a UUID.'));
|
||||
return;
|
||||
}
|
||||
if ($data['uuid'] !== $entity->uuid()) {
|
||||
$form_state->setErrorByName('import', $this->t('An entity with this machine name already exists but the UUID does not match.'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
// If there is no entity with a matching ID, check for a UUID match.
|
||||
elseif (isset($data['uuid']) && $entity_storage->loadByProperties(array('uuid' => $data['uuid']))) {
|
||||
$form_state->setErrorByName('import', $this->t('An entity with this UUID already exists but the machine name does not match.'));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$config = $this->config($form_state->getValue('config_name'));
|
||||
$this->configExists = !$config->isNew() ? $config : FALSE;
|
||||
}
|
||||
|
||||
// Store the decoded version of the submitted import.
|
||||
$form_state->setValueForElement($form['import'], $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
// If this form has not yet been confirmed, store the values and rebuild.
|
||||
if (!$this->data) {
|
||||
$form_state->setRebuild();
|
||||
$this->data = $form_state->getValues();
|
||||
return;
|
||||
}
|
||||
|
||||
// If a simple configuration file was added, set the data and save.
|
||||
if ($this->data['config_type'] === 'system.simple') {
|
||||
$this->configFactory()->getEditable($this->data['config_name'])->setData($this->data['import'])->save();
|
||||
drupal_set_message($this->t('The %name configuration was imported.', array('%name' => $this->data['config_name'])));
|
||||
}
|
||||
// For a config entity, create an entity and save it.
|
||||
else {
|
||||
try {
|
||||
$entity_storage = $this->entityManager->getStorage($this->data['config_type']);
|
||||
if ($this->configExists) {
|
||||
$entity = $entity_storage->updateFromStorageRecord($this->configExists, $this->data['import']);
|
||||
}
|
||||
else {
|
||||
$entity = $entity_storage->createFromStorageRecord($this->data['import']);
|
||||
}
|
||||
$entity->save();
|
||||
drupal_set_message($this->t('The @entity_type %label was imported.', array('@entity_type' => $entity->getEntityTypeId(), '%label' => $entity->label())));
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
drupal_set_message($e->getMessage(), 'error');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
418
core/modules/config/src/Form/ConfigSync.php
Normal file
418
core/modules/config/src/Form/ConfigSync.php
Normal file
|
@ -0,0 +1,418 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Form\ConfigSync.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Form;
|
||||
|
||||
use Drupal\Component\Uuid\UuidInterface;
|
||||
use Drupal\Core\Config\ConfigImporterException;
|
||||
use Drupal\Core\Config\ConfigImporter;
|
||||
use Drupal\Core\Config\TypedConfigManagerInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Extension\ModuleInstallerInterface;
|
||||
use Drupal\Core\Extension\ThemeHandlerInterface;
|
||||
use Drupal\Core\Config\ConfigManagerInterface;
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Config\StorageInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Lock\LockBackendInterface;
|
||||
use Drupal\Core\Config\StorageComparer;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Construct the storage changes in a configuration synchronization form.
|
||||
*/
|
||||
class ConfigSync extends FormBase {
|
||||
|
||||
/**
|
||||
* The database lock object.
|
||||
*
|
||||
* @var \Drupal\Core\Lock\LockBackendInterface
|
||||
*/
|
||||
protected $lock;
|
||||
|
||||
/**
|
||||
* The staging configuration object.
|
||||
*
|
||||
* @var \Drupal\Core\Config\StorageInterface
|
||||
*/
|
||||
protected $stagingStorage;
|
||||
|
||||
/**
|
||||
* The active configuration object.
|
||||
*
|
||||
* @var \Drupal\Core\Config\StorageInterface
|
||||
*/
|
||||
protected $activeStorage;
|
||||
|
||||
/**
|
||||
* The snapshot configuration object.
|
||||
*
|
||||
* @var \Drupal\Core\Config\StorageInterface
|
||||
*/
|
||||
protected $snapshotStorage;
|
||||
|
||||
/**
|
||||
* Event dispatcher.
|
||||
*
|
||||
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
|
||||
*/
|
||||
protected $eventDispatcher;
|
||||
|
||||
/**
|
||||
* The configuration manager.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigManagerInterface;
|
||||
*/
|
||||
protected $configManager;
|
||||
|
||||
/**
|
||||
* The typed config manager.
|
||||
*
|
||||
* @var \Drupal\Core\Config\TypedConfigManagerInterface
|
||||
*/
|
||||
protected $typedConfigManager;
|
||||
|
||||
/**
|
||||
* The module handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* The theme handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ThemeHandlerInterface
|
||||
*/
|
||||
protected $themeHandler;
|
||||
|
||||
/**
|
||||
* The module installer.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleInstallerInterface
|
||||
*/
|
||||
protected $moduleInstaller;
|
||||
|
||||
/**
|
||||
* The renderer.
|
||||
*
|
||||
* @var \Drupal\Core\Render\RendererInterface
|
||||
*/
|
||||
protected $renderer;
|
||||
|
||||
/**
|
||||
* Constructs the object.
|
||||
*
|
||||
* @param \Drupal\Core\Config\StorageInterface $staging_storage
|
||||
* The source storage.
|
||||
* @param \Drupal\Core\Config\StorageInterface $active_storage
|
||||
* The target storage.
|
||||
* @param \Drupal\Core\Config\StorageInterface $snapshot_storage
|
||||
* The snapshot storage.
|
||||
* @param \Drupal\Core\Lock\LockBackendInterface $lock
|
||||
* The lock object.
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
|
||||
* Event dispatcher.
|
||||
* @param \Drupal\Core\Config\ConfigManagerInterface $config_manager
|
||||
* Configuration manager.
|
||||
* @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config
|
||||
* The typed configuration manager.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler.
|
||||
* @param \Drupal\Core\Extension\ModuleInstallerInterface $module_installer
|
||||
* The module installer.
|
||||
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
|
||||
* The theme handler.
|
||||
* @param \Drupal\Core\Render\RendererInterface
|
||||
* The renderer.
|
||||
*/
|
||||
public function __construct(StorageInterface $staging_storage, StorageInterface $active_storage, StorageInterface $snapshot_storage, LockBackendInterface $lock, EventDispatcherInterface $event_dispatcher, ConfigManagerInterface $config_manager, TypedConfigManagerInterface $typed_config, ModuleHandlerInterface $module_handler, ModuleInstallerInterface $module_installer, ThemeHandlerInterface $theme_handler, RendererInterface $renderer) {
|
||||
$this->stagingStorage = $staging_storage;
|
||||
$this->activeStorage = $active_storage;
|
||||
$this->snapshotStorage = $snapshot_storage;
|
||||
$this->lock = $lock;
|
||||
$this->eventDispatcher = $event_dispatcher;
|
||||
$this->configManager = $config_manager;
|
||||
$this->typedConfigManager = $typed_config;
|
||||
$this->moduleHandler = $module_handler;
|
||||
$this->moduleInstaller = $module_installer;
|
||||
$this->themeHandler = $theme_handler;
|
||||
$this->renderer = $renderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('config.storage.staging'),
|
||||
$container->get('config.storage'),
|
||||
$container->get('config.storage.snapshot'),
|
||||
$container->get('lock.persistent'),
|
||||
$container->get('event_dispatcher'),
|
||||
$container->get('config.manager'),
|
||||
$container->get('config.typed'),
|
||||
$container->get('module_handler'),
|
||||
$container->get('module_installer'),
|
||||
$container->get('theme_handler'),
|
||||
$container->get('renderer')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'config_admin_import_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Import all'),
|
||||
);
|
||||
$source_list = $this->stagingStorage->listAll();
|
||||
$storage_comparer = new StorageComparer($this->stagingStorage, $this->activeStorage, $this->configManager);
|
||||
if (empty($source_list) || !$storage_comparer->createChangelist()->hasChanges()) {
|
||||
$form['no_changes'] = array(
|
||||
'#type' => 'table',
|
||||
'#header' => array('Name', 'Operations'),
|
||||
'#rows' => array(),
|
||||
'#empty' => $this->t('There are no configuration changes to import.'),
|
||||
);
|
||||
$form['actions']['#access'] = FALSE;
|
||||
return $form;
|
||||
}
|
||||
elseif (!$storage_comparer->validateSiteUuid()) {
|
||||
drupal_set_message($this->t('The staged configuration cannot be imported, because it originates from a different site than this site. You can only synchronize configuration between cloned instances of this site.'), 'error');
|
||||
$form['actions']['#access'] = FALSE;
|
||||
return $form;
|
||||
}
|
||||
// A list of changes will be displayed, so check if the user should be
|
||||
// warned of potential losses to configuration.
|
||||
if ($this->snapshotStorage->exists('core.extension')) {
|
||||
$snapshot_comparer = new StorageComparer($this->activeStorage, $this->snapshotStorage, $this->configManager);
|
||||
if (!$form_state->getUserInput() && $snapshot_comparer->createChangelist()->hasChanges()) {
|
||||
$change_list = array();
|
||||
foreach ($snapshot_comparer->getAllCollectionNames() as $collection) {
|
||||
foreach ($snapshot_comparer->getChangelist(NULL, $collection) as $config_names) {
|
||||
if (empty($config_names)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($config_names as $config_name) {
|
||||
$change_list[] = $config_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
sort($change_list);
|
||||
$change_list_render = array(
|
||||
'#theme' => 'item_list',
|
||||
'#items' => $change_list,
|
||||
);
|
||||
$change_list_html = $this->renderer->renderPlain($change_list_render);
|
||||
drupal_set_message($this->t('The following items in your active configuration have changes since the last import that may be lost on the next import. !changes', array('!changes' => $change_list_html)), 'warning');
|
||||
}
|
||||
}
|
||||
|
||||
// Store the comparer for use in the submit.
|
||||
$form_state->set('storage_comparer', $storage_comparer);
|
||||
|
||||
// Add the AJAX library to the form for dialog support.
|
||||
$form['#attached']['library'][] = 'core/drupal.ajax';
|
||||
|
||||
foreach ($storage_comparer->getAllCollectionNames() as $collection) {
|
||||
if ($collection != StorageInterface::DEFAULT_COLLECTION) {
|
||||
$form[$collection]['collection_heading'] = array(
|
||||
'#type' => 'html_tag',
|
||||
'#tag' => 'h2',
|
||||
'#value' => $this->t('!collection configuration collection', array('!collection' => $collection)),
|
||||
);
|
||||
}
|
||||
foreach ($storage_comparer->getChangelist(NULL, $collection) as $config_change_type => $config_names) {
|
||||
if (empty($config_names)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// @todo A table caption would be more appropriate, but does not have the
|
||||
// visual importance of a heading.
|
||||
$form[$collection][$config_change_type]['heading'] = array(
|
||||
'#type' => 'html_tag',
|
||||
'#tag' => 'h3',
|
||||
);
|
||||
switch ($config_change_type) {
|
||||
case 'create':
|
||||
$form[$collection][$config_change_type]['heading']['#value'] = $this->formatPlural(count($config_names), '@count new', '@count new');
|
||||
break;
|
||||
|
||||
case 'update':
|
||||
$form[$collection][$config_change_type]['heading']['#value'] = $this->formatPlural(count($config_names), '@count changed', '@count changed');
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
$form[$collection][$config_change_type]['heading']['#value'] = $this->formatPlural(count($config_names), '@count removed', '@count removed');
|
||||
break;
|
||||
|
||||
case 'rename':
|
||||
$form[$collection][$config_change_type]['heading']['#value'] = $this->formatPlural(count($config_names), '@count renamed', '@count renamed');
|
||||
break;
|
||||
}
|
||||
$form[$collection][$config_change_type]['list'] = array(
|
||||
'#type' => 'table',
|
||||
'#header' => array('Name', 'Operations'),
|
||||
);
|
||||
|
||||
foreach ($config_names as $config_name) {
|
||||
if ($config_change_type == 'rename') {
|
||||
$names = $storage_comparer->extractRenameNames($config_name);
|
||||
$route_options = array('source_name' => $names['old_name'], 'target_name' => $names['new_name']);
|
||||
$config_name = $this->t('!source_name to !target_name', array('!source_name' => $names['old_name'], '!target_name' => $names['new_name']));
|
||||
}
|
||||
else {
|
||||
$route_options = array('source_name' => $config_name);
|
||||
}
|
||||
if ($collection != StorageInterface::DEFAULT_COLLECTION) {
|
||||
$route_name = 'config.diff_collection';
|
||||
$route_options['collection'] = $collection;
|
||||
}
|
||||
else {
|
||||
$route_name = 'config.diff';
|
||||
}
|
||||
$links['view_diff'] = array(
|
||||
'title' => $this->t('View differences'),
|
||||
'url' => Url::fromRoute($route_name, $route_options),
|
||||
'attributes' => array(
|
||||
'class' => array('use-ajax'),
|
||||
'data-dialog-type' => 'modal',
|
||||
'data-dialog-options' => json_encode(array(
|
||||
'width' => 700
|
||||
)),
|
||||
),
|
||||
);
|
||||
$form[$collection][$config_change_type]['list']['#rows'][] = array(
|
||||
'name' => $config_name,
|
||||
'operations' => array(
|
||||
'data' => array(
|
||||
'#type' => 'operations',
|
||||
'#links' => $links,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$config_importer = new ConfigImporter(
|
||||
$form_state->get('storage_comparer'),
|
||||
$this->eventDispatcher,
|
||||
$this->configManager,
|
||||
$this->lock,
|
||||
$this->typedConfigManager,
|
||||
$this->moduleHandler,
|
||||
$this->moduleInstaller,
|
||||
$this->themeHandler,
|
||||
$this->getStringTranslation()
|
||||
);
|
||||
if ($config_importer->alreadyImporting()) {
|
||||
drupal_set_message($this->t('Another request may be synchronizing configuration already.'));
|
||||
}
|
||||
else{
|
||||
try {
|
||||
$sync_steps = $config_importer->initialize();
|
||||
$batch = array(
|
||||
'operations' => array(),
|
||||
'finished' => array(get_class($this), 'finishBatch'),
|
||||
'title' => t('Synchronizing configuration'),
|
||||
'init_message' => t('Starting configuration synchronization.'),
|
||||
'progress_message' => t('Completed @current step of @total.'),
|
||||
'error_message' => t('Configuration synchronization has encountered an error.'),
|
||||
'file' => drupal_get_path('module', 'config') . '/config.admin.inc',
|
||||
);
|
||||
foreach ($sync_steps as $sync_step) {
|
||||
$batch['operations'][] = array(array(get_class($this), 'processBatch'), array($config_importer, $sync_step));
|
||||
}
|
||||
|
||||
batch_set($batch);
|
||||
}
|
||||
catch (ConfigImporterException $e) {
|
||||
// There are validation errors.
|
||||
drupal_set_message($this->t('The configuration cannot be imported because it failed validation for the following reasons:'), 'error');
|
||||
foreach ($config_importer->getErrors() as $message) {
|
||||
drupal_set_message($message, 'error');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the config import batch and persists the importer.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigImporter $config_importer
|
||||
* The batch config importer object to persist.
|
||||
* @param string $sync_step
|
||||
* The synchronization step to do.
|
||||
* @param array $context
|
||||
* The batch context.
|
||||
*/
|
||||
public static function processBatch(ConfigImporter $config_importer, $sync_step, &$context) {
|
||||
if (!isset($context['sandbox']['config_importer'])) {
|
||||
$context['sandbox']['config_importer'] = $config_importer;
|
||||
}
|
||||
|
||||
$config_importer = $context['sandbox']['config_importer'];
|
||||
$config_importer->doSyncStep($sync_step, $context);
|
||||
if ($errors = $config_importer->getErrors()) {
|
||||
if (!isset($context['results']['errors'])) {
|
||||
$context['results']['errors'] = array();
|
||||
}
|
||||
$context['results']['errors'] += $errors;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish batch.
|
||||
*
|
||||
* This function is a static function to avoid serializing the ConfigSync
|
||||
* object unnecessarily.
|
||||
*/
|
||||
public static function finishBatch($success, $results, $operations) {
|
||||
if ($success) {
|
||||
if (!empty($results['errors'])) {
|
||||
foreach ($results['errors'] as $error) {
|
||||
drupal_set_message($error, 'error');
|
||||
\Drupal::logger('config_sync')->error($error);
|
||||
}
|
||||
drupal_set_message(\Drupal::translation()->translate('The configuration was imported with errors.'), 'warning');
|
||||
}
|
||||
else {
|
||||
drupal_set_message(\Drupal::translation()->translate('The configuration was imported successfully.'));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// An error occurred.
|
||||
// $operations contains the operations that remained unprocessed.
|
||||
$error_operation = reset($operations);
|
||||
$message = \Drupal::translation()->translate('An error occurred while processing %error_operation with arguments: @arguments', array('%error_operation' => $error_operation[0], '@arguments' => print_r($error_operation[1], TRUE)));
|
||||
drupal_set_message($message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\AssertConfigEntityImportTrait.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
|
||||
/**
|
||||
* Provides test assertions for testing config entity synchronization.
|
||||
*
|
||||
* Can be used by test classes that extend \Drupal\simpletest\WebTestBase or
|
||||
* \Drupal\simpletest\KernelTestBase.
|
||||
*/
|
||||
trait AssertConfigEntityImportTrait {
|
||||
|
||||
/**
|
||||
* Asserts that a config entity can be imported without changing it.
|
||||
*
|
||||
* @param \Drupal\Core\Config\Entity\ConfigEntityInterface $entity
|
||||
* The config entity to test importing.
|
||||
*/
|
||||
public function assertConfigEntityImport(ConfigEntityInterface $entity) {
|
||||
// Save original config information.
|
||||
$entity_uuid = $entity->uuid();
|
||||
$entity_type_id = $entity->getEntityTypeId();
|
||||
$original_data = $entity->toArray();
|
||||
// Copy everything to staging.
|
||||
$this->copyConfig(\Drupal::service('config.storage'), \Drupal::service('config.storage.staging'));
|
||||
// Delete the configuration from active. Don't worry about side effects of
|
||||
// deleting config like fields cleaning up field storages. The coming import
|
||||
// should recreate everything as necessary.
|
||||
$entity->delete();
|
||||
$this->configImporter()->reset()->import();
|
||||
$imported_entity = \Drupal::entityManager()->loadEntityByUuid($entity_type_id, $entity_uuid);
|
||||
$this->assertIdentical($original_data, $imported_entity->toArray());
|
||||
}
|
||||
|
||||
}
|
316
core/modules/config/src/Tests/ConfigCRUDTest.php
Normal file
316
core/modules/config/src/Tests/ConfigCRUDTest.php
Normal file
|
@ -0,0 +1,316 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigCRUDTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Config\ConfigNameException;
|
||||
use Drupal\Core\Config\ConfigValueException;
|
||||
use Drupal\Core\Config\InstallStorage;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
use Drupal\Core\Config\FileStorage;
|
||||
use Drupal\Core\Config\DatabaseStorage;
|
||||
use Drupal\Core\Config\UnsupportedDataTypeConfigException;
|
||||
|
||||
/**
|
||||
* Tests CRUD operations on configuration objects.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigCRUDTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Exempt from strict schema checking.
|
||||
*
|
||||
* @see \Drupal\Core\Config\Testing\ConfigSchemaChecker
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $strictConfigSchema = FALSE;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system');
|
||||
|
||||
/**
|
||||
* Tests CRUD operations.
|
||||
*/
|
||||
function testCRUD() {
|
||||
$storage = $this->container->get('config.storage');
|
||||
$config_factory = $this->container->get('config.factory');
|
||||
$name = 'config_test.crud';
|
||||
|
||||
$config = $this->config($name);
|
||||
$this->assertIdentical($config->isNew(), TRUE);
|
||||
|
||||
// Create a new configuration object.
|
||||
$config->set('value', 'initial');
|
||||
$config->save();
|
||||
$this->assertIdentical($config->isNew(), FALSE);
|
||||
|
||||
// Verify the active configuration contains the saved value.
|
||||
$actual_data = $storage->read($name);
|
||||
$this->assertIdentical($actual_data, array('value' => 'initial'));
|
||||
|
||||
// Update the configuration object instance.
|
||||
$config->set('value', 'instance-update');
|
||||
$config->save();
|
||||
$this->assertIdentical($config->isNew(), FALSE);
|
||||
|
||||
// Verify the active configuration contains the updated value.
|
||||
$actual_data = $storage->read($name);
|
||||
$this->assertIdentical($actual_data, array('value' => 'instance-update'));
|
||||
|
||||
// Verify a call to $this->config() immediately returns the updated value.
|
||||
$new_config = $this->config($name);
|
||||
$this->assertIdentical($new_config->get(), $config->get());
|
||||
$this->assertIdentical($config->isNew(), FALSE);
|
||||
|
||||
// Pollute the config factory static cache.
|
||||
$config_factory->getEditable($name);
|
||||
|
||||
// Delete the configuration object.
|
||||
$config->delete();
|
||||
|
||||
// Verify the configuration object is empty.
|
||||
$this->assertIdentical($config->get(), array());
|
||||
$this->assertIdentical($config->isNew(), TRUE);
|
||||
|
||||
// Verify that all copies of the configuration has been removed from the
|
||||
// static cache.
|
||||
$this->assertIdentical($config_factory->getEditable($name)->isNew(), TRUE);
|
||||
|
||||
// Verify the active configuration contains no value.
|
||||
$actual_data = $storage->read($name);
|
||||
$this->assertIdentical($actual_data, FALSE);
|
||||
|
||||
// Verify $this->config() returns no data.
|
||||
$new_config = $this->config($name);
|
||||
$this->assertIdentical($new_config->get(), $config->get());
|
||||
$this->assertIdentical($config->isNew(), TRUE);
|
||||
|
||||
// Re-create the configuration object.
|
||||
$config->set('value', 're-created');
|
||||
$config->save();
|
||||
$this->assertIdentical($config->isNew(), FALSE);
|
||||
|
||||
// Verify the active configuration contains the updated value.
|
||||
$actual_data = $storage->read($name);
|
||||
$this->assertIdentical($actual_data, array('value' => 're-created'));
|
||||
|
||||
// Verify a call to $this->config() immediately returns the updated value.
|
||||
$new_config = $this->config($name);
|
||||
$this->assertIdentical($new_config->get(), $config->get());
|
||||
$this->assertIdentical($config->isNew(), FALSE);
|
||||
|
||||
// Rename the configuration object.
|
||||
$new_name = 'config_test.crud_rename';
|
||||
$this->container->get('config.factory')->rename($name, $new_name);
|
||||
$renamed_config = $this->config($new_name);
|
||||
$this->assertIdentical($renamed_config->get(), $config->get());
|
||||
$this->assertIdentical($renamed_config->isNew(), FALSE);
|
||||
|
||||
// Ensure that the old configuration object is removed from both the cache
|
||||
// and the configuration storage.
|
||||
$config = $this->config($name);
|
||||
$this->assertIdentical($config->get(), array());
|
||||
$this->assertIdentical($config->isNew(), TRUE);
|
||||
|
||||
// Test renaming when config.factory does not have the object in its static
|
||||
// cache.
|
||||
$name = 'config_test.crud_rename';
|
||||
// Pollute the non-overrides static cache.
|
||||
$config_factory->getEditable($name);
|
||||
// Pollute the overrides static cache.
|
||||
$config = $config_factory->get($name);
|
||||
// Rename and ensure that happened properly.
|
||||
$new_name = 'config_test.crud_rename_no_cache';
|
||||
$config_factory->rename($name, $new_name);
|
||||
$renamed_config = $config_factory->get($new_name);
|
||||
$this->assertIdentical($renamed_config->get(), $config->get());
|
||||
$this->assertIdentical($renamed_config->isNew(), FALSE);
|
||||
// Ensure the overrides static cache has been cleared.
|
||||
$this->assertIdentical($config_factory->get($name)->isNew(), TRUE);
|
||||
// Ensure the non-overrides static cache has been cleared.
|
||||
$this->assertIdentical($config_factory->getEditable($name)->isNew(), TRUE);
|
||||
|
||||
// Merge data into the configuration object.
|
||||
$new_config = $this->config($new_name);
|
||||
$expected_values = array(
|
||||
'value' => 'herp',
|
||||
'404' => 'derp',
|
||||
);
|
||||
$new_config->merge($expected_values);
|
||||
$new_config->save();
|
||||
$this->assertIdentical($new_config->get('value'), $expected_values['value']);
|
||||
$this->assertIdentical($new_config->get('404'), $expected_values['404']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the validation of configuration object names.
|
||||
*/
|
||||
function testNameValidation() {
|
||||
// Verify that an object name without namespace causes an exception.
|
||||
$name = 'nonamespace';
|
||||
$message = 'Expected ConfigNameException was thrown for a name without a namespace.';
|
||||
try {
|
||||
$this->config($name)->save();
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (ConfigNameException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
|
||||
// Verify that a name longer than the maximum length causes an exception.
|
||||
$name = 'config_test.herman_melville.moby_dick_or_the_whale.harper_1851.now_small_fowls_flew_screaming_over_the_yet_yawning_gulf_a_sullen_white_surf_beat_against_its_steep_sides_then_all_collapsed_and_the_great_shroud_of_the_sea_rolled_on_as_it_rolled_five_thousand_years_ago';
|
||||
$message = 'Expected ConfigNameException was thrown for a name longer than Config::MAX_NAME_LENGTH.';
|
||||
try {
|
||||
$this->config($name)->save();
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (ConfigNameException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
|
||||
// Verify that disallowed characters in the name cause an exception.
|
||||
$characters = $test_characters = array(':', '?', '*', '<', '>', '"', '\'', '/', '\\');
|
||||
foreach ($test_characters as $i => $c) {
|
||||
try {
|
||||
$name = 'namespace.object' . $c;
|
||||
$config = $this->config($name);
|
||||
$config->save();
|
||||
}
|
||||
catch (ConfigNameException $e) {
|
||||
unset($test_characters[$i]);
|
||||
}
|
||||
}
|
||||
$this->assertTrue(empty($test_characters), format_string('Expected ConfigNameException was thrown for all invalid name characters: @characters', array(
|
||||
'@characters' => implode(' ', $characters),
|
||||
)));
|
||||
|
||||
// Verify that a valid config object name can be saved.
|
||||
$name = 'namespace.object';
|
||||
$message = 'ConfigNameException was not thrown for a valid object name.';
|
||||
try {
|
||||
$config = $this->config($name);
|
||||
$config->save();
|
||||
$this->pass($message);
|
||||
}
|
||||
catch (ConfigNameException $e) {
|
||||
$this->fail($message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the validation of configuration object values.
|
||||
*/
|
||||
function testValueValidation() {
|
||||
// Verify that setData() will catch dotted keys.
|
||||
$message = 'Expected ConfigValueException was thrown from setData() for value with dotted keys.';
|
||||
try {
|
||||
$this->config('namespace.object')->setData(array('key.value' => 12))->save();
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (ConfigValueException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
|
||||
// Verify that set() will catch dotted keys.
|
||||
$message = 'Expected ConfigValueException was thrown from set() for value with dotted keys.';
|
||||
try {
|
||||
$this->config('namespace.object')->set('foo', array('key.value' => 12))->save();
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (ConfigValueException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests data type handling.
|
||||
*/
|
||||
public function testDataTypes() {
|
||||
\Drupal::service('module_installer')->install(array('config_test'));
|
||||
$storage = new DatabaseStorage($this->container->get('database'), 'config');
|
||||
$name = 'config_test.types';
|
||||
$config = $this->config($name);
|
||||
$original_content = file_get_contents(drupal_get_path('module', 'config_test') . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY . "/$name.yml");
|
||||
$this->verbose('<pre>' . $original_content . "\n" . var_export($storage->read($name), TRUE));
|
||||
|
||||
// Verify variable data types are intact.
|
||||
$data = array(
|
||||
'array' => array(),
|
||||
'boolean' => TRUE,
|
||||
'exp' => 1.2e+34,
|
||||
'float' => 3.14159,
|
||||
'float_as_integer' => (float) 1,
|
||||
'hex' => 0xC,
|
||||
'int' => 99,
|
||||
'octal' => 0775,
|
||||
'string' => 'string',
|
||||
'string_int' => '1',
|
||||
);
|
||||
$this->assertIdentical($config->get(), $data);
|
||||
|
||||
// Re-set each key using Config::set().
|
||||
foreach($data as $key => $value) {
|
||||
$config->set($key, $value);
|
||||
}
|
||||
$config->save();
|
||||
$this->assertIdentical($config->get(), $data);
|
||||
// Assert the data against the file storage.
|
||||
$this->assertIdentical($storage->read($name), $data);
|
||||
$this->verbose('<pre>' . $name . var_export($storage->read($name), TRUE));
|
||||
|
||||
// Set data using config::setData().
|
||||
$config->setData($data)->save();
|
||||
$this->assertIdentical($config->get(), $data);
|
||||
$this->assertIdentical($storage->read($name), $data);
|
||||
|
||||
// Test that schema type enforcement can be overridden by trusting the data.
|
||||
$this->assertIdentical(99, $config->get('int'));
|
||||
$config->set('int', '99')->save(TRUE);
|
||||
$this->assertIdentical('99', $config->get('int'));
|
||||
// Test that re-saving without testing the data enforces the schema type.
|
||||
$config->save();
|
||||
$this->assertIdentical($data, $config->get());
|
||||
|
||||
// Test that setting an unsupported type for a config object with a schema
|
||||
// fails.
|
||||
try {
|
||||
$config->set('stream', fopen(__FILE__, 'r'))->save();
|
||||
$this->fail('No Exception thrown upon saving invalid data type.');
|
||||
}
|
||||
catch (UnsupportedDataTypeConfigException $e) {
|
||||
$this->pass(SafeMarkup::format('%class thrown upon saving invalid data type.', array(
|
||||
'%class' => get_class($e),
|
||||
)));
|
||||
}
|
||||
|
||||
// Test that setting an unsupported type for a config object with no schema
|
||||
// also fails.
|
||||
$typed_config_manager = $this->container->get('config.typed');
|
||||
$config_name = 'config_test.no_schema';
|
||||
$config = $this->config($config_name);
|
||||
$this->assertFalse($typed_config_manager->hasConfigSchema($config_name));
|
||||
|
||||
try {
|
||||
$config->set('stream', fopen(__FILE__, 'r'))->save();
|
||||
$this->fail('No Exception thrown upon saving invalid data type.');
|
||||
}
|
||||
catch (UnsupportedDataTypeConfigException $e) {
|
||||
$this->pass(SafeMarkup::format('%class thrown upon saving invalid data type.', array(
|
||||
'%class' => get_class($e),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
480
core/modules/config/src/Tests/ConfigDependencyTest.php
Normal file
480
core/modules/config/src/Tests/ConfigDependencyTest.php
Normal file
|
@ -0,0 +1,480 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigDependencyTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\system\Tests\Entity\EntityUnitTestBase;
|
||||
|
||||
/**
|
||||
* Tests for configuration dependencies.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigDependencyTest extends EntityUnitTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* The entity_test module is enabled to provide content entity types.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_test', 'entity_test', 'user');
|
||||
|
||||
/**
|
||||
* Tests that calculating dependencies for system module.
|
||||
*/
|
||||
public function testNonEntity() {
|
||||
$this->installConfig(array('system'));
|
||||
$config_manager = \Drupal::service('config.manager');
|
||||
$dependents = $config_manager->findConfigEntityDependents('module', array('system'));
|
||||
$this->assertTrue(isset($dependents['system.site']), 'Simple configuration system.site has a UUID key even though it is not a configuration entity and therefore is found when looking for dependencies of the System module.');
|
||||
// Ensure that calling
|
||||
// \Drupal\Core\Config\ConfigManager::findConfigEntityDependentsAsEntities()
|
||||
// does not try to load system.site as an entity.
|
||||
$config_manager->findConfigEntityDependentsAsEntities('module', array('system'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests creating dependencies on configuration entities.
|
||||
*/
|
||||
public function testDependencyManagement() {
|
||||
/** @var \Drupal\Core\Config\ConfigManagerInterface $config_manager */
|
||||
$config_manager = \Drupal::service('config.manager');
|
||||
$storage = $this->container->get('entity.manager')->getStorage('config_test');
|
||||
// Test dependencies between modules.
|
||||
$entity1 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity1',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'module' => array('node')
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$entity1->save();
|
||||
|
||||
$dependents = $config_manager->findConfigEntityDependents('module', array('node'));
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 has a dependency on the Node module.');
|
||||
$dependents = $config_manager->findConfigEntityDependents('module', array('config_test'));
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 has a dependency on the config_test module.');
|
||||
$dependents = $config_manager->findConfigEntityDependents('module', array('views'));
|
||||
$this->assertFalse(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 does not have a dependency on the Views module.');
|
||||
// Ensure that the provider of the config entity is not actually written to
|
||||
// the dependencies array.
|
||||
$raw_config = $this->config('config_test.dynamic.entity1');
|
||||
$this->assertTrue(array_search('node', $raw_config->get('dependencies.module')) !== FALSE, 'Node module is written to the dependencies array as this has to be explicit.');
|
||||
|
||||
// Create additional entities to test dependencies on config entities.
|
||||
$entity2 = $storage->create(array('id' => 'entity2', 'dependencies' => array('enforced' => array('config' => array($entity1->getConfigDependencyName())))));
|
||||
$entity2->save();
|
||||
$entity3 = $storage->create(array('id' => 'entity3', 'dependencies' => array('enforced' => array('config' => array($entity2->getConfigDependencyName())))));
|
||||
$entity3->save();
|
||||
$entity4 = $storage->create(array('id' => 'entity4', 'dependencies' => array('enforced' => array('config' => array($entity3->getConfigDependencyName())))));
|
||||
$entity4->save();
|
||||
|
||||
// Test getting $entity1's dependencies as configuration dependency objects.
|
||||
$dependents = $config_manager->findConfigEntityDependents('config', array($entity1->getConfigDependencyName()));
|
||||
$this->assertFalse(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 does not have a dependency on itself.');
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity2']), 'config_test.dynamic.entity2 has a dependency on config_test.dynamic.entity1.');
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity3']), 'config_test.dynamic.entity3 has a dependency on config_test.dynamic.entity1.');
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity4']), 'config_test.dynamic.entity4 has a dependency on config_test.dynamic.entity1.');
|
||||
|
||||
// Test getting $entity2's dependencies as entities.
|
||||
$dependents = $config_manager->findConfigEntityDependentsAsEntities('config', array($entity2->getConfigDependencyName()));
|
||||
$dependent_ids = $this->getDependentIds($dependents);
|
||||
$this->assertFalse(in_array('config_test:entity1', $dependent_ids), 'config_test.dynamic.entity1 does not have a dependency on config_test.dynamic.entity1.');
|
||||
$this->assertFalse(in_array('config_test:entity2', $dependent_ids), 'config_test.dynamic.entity2 does not have a dependency on itself.');
|
||||
$this->assertTrue(in_array('config_test:entity3', $dependent_ids), 'config_test.dynamic.entity3 has a dependency on config_test.dynamic.entity2.');
|
||||
$this->assertTrue(in_array('config_test:entity4', $dependent_ids), 'config_test.dynamic.entity4 has a dependency on config_test.dynamic.entity2.');
|
||||
|
||||
// Test getting node module's dependencies as configuration dependency
|
||||
// objects.
|
||||
$dependents = $config_manager->findConfigEntityDependents('module', array('node'));
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 has a dependency on the Node module.');
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity2']), 'config_test.dynamic.entity2 has a dependency on the Node module.');
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity3']), 'config_test.dynamic.entity3 has a dependency on the Node module.');
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity4']), 'config_test.dynamic.entity4 has a dependency on the Node module.');
|
||||
|
||||
// Test getting node module's dependencies as configuration dependency
|
||||
// objects after making $entity3 also dependent on node module but $entity1
|
||||
// no longer depend on node module.
|
||||
$entity1->setEnforcedDependencies([])->save();
|
||||
$entity3->setEnforcedDependencies(['module' => ['node'], 'config' => [$entity2->getConfigDependencyName()]])->save();
|
||||
$dependents = $config_manager->findConfigEntityDependents('module', array('node'));
|
||||
$this->assertFalse(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 does not have a dependency on the Node module.');
|
||||
$this->assertFalse(isset($dependents['config_test.dynamic.entity2']), 'config_test.dynamic.entity2 does not have a dependency on the Node module.');
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity3']), 'config_test.dynamic.entity3 has a dependency on the Node module.');
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity4']), 'config_test.dynamic.entity4 has a dependency on the Node module.');
|
||||
|
||||
// Test dependency on a content entity.
|
||||
$entity_test = entity_create('entity_test', array(
|
||||
'name' => $this->randomString(),
|
||||
'type' => 'entity_test',
|
||||
));
|
||||
$entity_test->save();
|
||||
$entity2->setEnforcedDependencies(['config' => [$entity1->getConfigDependencyName()], 'content' => [$entity_test->getConfigDependencyName()]])->save();;
|
||||
$dependents = $config_manager->findConfigEntityDependents('content', array($entity_test->getConfigDependencyName()));
|
||||
$this->assertFalse(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 does not have a dependency on the content entity.');
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity2']), 'config_test.dynamic.entity2 has a dependency on the content entity.');
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity3']), 'config_test.dynamic.entity3 has a dependency on the content entity (via entity2).');
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity4']), 'config_test.dynamic.entity4 has a dependency on the content entity (via entity3).');
|
||||
|
||||
// Create a configuration entity of a different type with the same ID as one
|
||||
// of the entities already created.
|
||||
$alt_storage = $this->container->get('entity.manager')->getStorage('config_query_test');
|
||||
$alt_storage->create(array('id' => 'entity1', 'dependencies' => array('enforced' => array('config' => array($entity1->getConfigDependencyName())))))->save();
|
||||
$alt_storage->create(array('id' => 'entity2', 'dependencies' => array('enforced' => array('module' => array('views')))))->save();
|
||||
|
||||
$dependents = $config_manager->findConfigEntityDependentsAsEntities('config', array($entity1->getConfigDependencyName()));
|
||||
$dependent_ids = $this->getDependentIds($dependents);
|
||||
$this->assertFalse(in_array('config_test:entity1', $dependent_ids), 'config_test.dynamic.entity1 does not have a dependency on itself.');
|
||||
$this->assertTrue(in_array('config_test:entity2', $dependent_ids), 'config_test.dynamic.entity2 has a dependency on config_test.dynamic.entity1.');
|
||||
$this->assertTrue(in_array('config_test:entity3', $dependent_ids), 'config_test.dynamic.entity3 has a dependency on config_test.dynamic.entity1.');
|
||||
$this->assertTrue(in_array('config_test:entity4', $dependent_ids), 'config_test.dynamic.entity4 has a dependency on config_test.dynamic.entity1.');
|
||||
$this->assertTrue(in_array('config_query_test:entity1', $dependent_ids), 'config_query_test.dynamic.entity1 has a dependency on config_test.dynamic.entity1.');
|
||||
$this->assertFalse(in_array('config_query_test:entity2', $dependent_ids), 'config_query_test.dynamic.entity2 does not have a dependency on config_test.dynamic.entity1.');
|
||||
|
||||
$dependents = $config_manager->findConfigEntityDependentsAsEntities('module', array('node', 'views'));
|
||||
$dependent_ids = $this->getDependentIds($dependents);
|
||||
$this->assertFalse(in_array('config_test:entity1', $dependent_ids), 'config_test.dynamic.entity1 does not have a dependency on Views or Node.');
|
||||
$this->assertFalse(in_array('config_test:entity2', $dependent_ids), 'config_test.dynamic.entity2 does not have a dependency on Views or Node.');
|
||||
$this->assertTrue(in_array('config_test:entity3', $dependent_ids), 'config_test.dynamic.entity3 has a dependency on Views or Node.');
|
||||
$this->assertTrue(in_array('config_test:entity4', $dependent_ids), 'config_test.dynamic.entity4 has a dependency on Views or Node.');
|
||||
$this->assertFalse(in_array('config_query_test:entity1', $dependent_ids), 'config_test.query.entity1 does not have a dependency on Views or Node.');
|
||||
$this->assertTrue(in_array('config_query_test:entity2', $dependent_ids), 'config_test.query.entity2 has a dependency on Views or Node.');
|
||||
|
||||
$dependents = $config_manager->findConfigEntityDependentsAsEntities('module', array('config_test'));
|
||||
$dependent_ids = $this->getDependentIds($dependents);
|
||||
$this->assertTrue(in_array('config_test:entity1', $dependent_ids), 'config_test.dynamic.entity1 has a dependency on config_test module.');
|
||||
$this->assertTrue(in_array('config_test:entity2', $dependent_ids), 'config_test.dynamic.entity2 has a dependency on config_test module.');
|
||||
$this->assertTrue(in_array('config_test:entity3', $dependent_ids), 'config_test.dynamic.entity3 has a dependency on config_test module.');
|
||||
$this->assertTrue(in_array('config_test:entity4', $dependent_ids), 'config_test.dynamic.entity4 has a dependency on config_test module.');
|
||||
$this->assertTrue(in_array('config_query_test:entity1', $dependent_ids), 'config_test.query.entity1 has a dependency on config_test module.');
|
||||
$this->assertTrue(in_array('config_query_test:entity2', $dependent_ids), 'config_test.query.entity2 has a dependency on config_test module.');
|
||||
|
||||
// Test the ability to find missing content dependencies.
|
||||
$missing_dependencies = $config_manager->findMissingContentDependencies();
|
||||
$this->assertEqual([], $missing_dependencies);
|
||||
|
||||
$expected = [$entity_test->uuid() => [
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => $entity_test->bundle(),
|
||||
'uuid' => $entity_test->uuid(),
|
||||
]];
|
||||
// Delete the content entity so that is it now missing.
|
||||
$entity_test->delete();
|
||||
$missing_dependencies = $config_manager->findMissingContentDependencies();
|
||||
$this->assertEqual($expected, $missing_dependencies);
|
||||
|
||||
// Add a fake missing dependency to ensure multiple missing dependencies
|
||||
// work.
|
||||
$entity1->setEnforcedDependencies(['content' => [$entity_test->getConfigDependencyName(), 'entity_test:bundle:uuid']])->save();;
|
||||
$expected['uuid'] = [
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'bundle',
|
||||
'uuid' => 'uuid',
|
||||
];
|
||||
$missing_dependencies = $config_manager->findMissingContentDependencies();
|
||||
$this->assertEqual($expected, $missing_dependencies);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests ConfigManager::uninstall() and config entity dependency management.
|
||||
*/
|
||||
public function testConfigEntityUninstall() {
|
||||
/** @var \Drupal\Core\Config\ConfigManagerInterface $config_manager */
|
||||
$config_manager = \Drupal::service('config.manager');
|
||||
/** @var \Drupal\Core\Config\Entity\ConfigEntityStorage $storage */
|
||||
$storage = $this->container->get('entity.manager')->getStorage('config_test');
|
||||
// Test dependencies between modules.
|
||||
$entity1 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity1',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'module' => array('node', 'config_test')
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
$entity1->save();
|
||||
$entity2 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity2',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'config' => array($entity1->getConfigDependencyName()),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
$entity2->save();
|
||||
// Perform a module rebuild so we can know where the node module is located
|
||||
// and uninstall it.
|
||||
// @todo Remove as part of https://www.drupal.org/node/2186491
|
||||
system_rebuild_module_data();
|
||||
// Test that doing a config uninstall of the node module deletes entity2
|
||||
// since it is dependent on entity1 which is dependent on the node module.
|
||||
$config_manager->uninstall('module', 'node');
|
||||
$this->assertFalse($storage->load('entity1'), 'Entity 1 deleted');
|
||||
$this->assertFalse($storage->load('entity2'), 'Entity 2 deleted');
|
||||
|
||||
// Set a more complicated test where dependencies will be fixed.
|
||||
\Drupal::state()->set('config_test.fix_dependencies', array($entity1->getConfigDependencyName()));
|
||||
|
||||
// Entity1 will be deleted because it depends on node.
|
||||
$entity1 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity1',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'module' => array('node', 'config_test')
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
$entity1->save();
|
||||
|
||||
// Entity2 has a dependency on Entity1 but it can be fixed because
|
||||
// \Drupal\config_test\Entity::onDependencyRemoval() will remove the
|
||||
// dependency before config entities are deleted.
|
||||
$entity2 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity2',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'config' => array($entity1->getConfigDependencyName()),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
$entity2->save();
|
||||
|
||||
// Entity3 will be unchanged because it is dependent on Entity2 which can
|
||||
// be fixed.
|
||||
$entity3 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity3',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'config' => array($entity2->getConfigDependencyName()),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
$entity3->save();
|
||||
|
||||
// Entity4's config dependency will be fixed but it will still be deleted
|
||||
// because it also depends on the node module.
|
||||
$entity4 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity4',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'config' => array($entity1->getConfigDependencyName()),
|
||||
'module' => array('node', 'config_test')
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
$entity4->save();
|
||||
|
||||
// Do a dry run using
|
||||
// \Drupal\Core\Config\ConfigManager::getConfigEntitiesToChangeOnDependencyRemoval().
|
||||
$config_entities = $config_manager->getConfigEntitiesToChangeOnDependencyRemoval('module', ['node']);
|
||||
$this->assertEqual($entity1->uuid(), $config_entities['delete'][0]->uuid(), 'Entity 1 will be deleted.');
|
||||
$this->assertEqual($entity2->uuid(), reset($config_entities['update'])->uuid(), 'Entity 2 will be updated.');
|
||||
$this->assertEqual($entity3->uuid(), reset($config_entities['unchanged'])->uuid(), 'Entity 3 is not changed.');
|
||||
$this->assertEqual($entity4->uuid(), $config_entities['delete'][1]->uuid(), 'Entity 4 will be deleted.');
|
||||
|
||||
// Perform the uninstall.
|
||||
$config_manager->uninstall('module', 'node');
|
||||
|
||||
// Test that expected actions have been performed.
|
||||
$this->assertFalse($storage->load('entity1'), 'Entity 1 deleted');
|
||||
$entity2 = $storage->load('entity2');
|
||||
$this->assertTrue($entity2, 'Entity 2 not deleted');
|
||||
$this->assertEqual($entity2->calculateDependencies()['config'], array(), 'Entity 2 dependencies updated to remove dependency on Entity1.');
|
||||
$entity3 = $storage->load('entity3');
|
||||
$this->assertTrue($entity3, 'Entity 3 not deleted');
|
||||
$this->assertEqual($entity3->calculateDependencies()['config'], [$entity2->getConfigDependencyName()], 'Entity 3 still depends on Entity 2.');
|
||||
$this->assertFalse($storage->load('entity4'), 'Entity 4 deleted');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests deleting a configuration entity and dependency management.
|
||||
*/
|
||||
public function testConfigEntityDelete() {
|
||||
/** @var \Drupal\Core\Config\ConfigManagerInterface $config_manager */
|
||||
$config_manager = \Drupal::service('config.manager');
|
||||
/** @var \Drupal\Core\Config\Entity\ConfigEntityStorage $storage */
|
||||
$storage = $this->container->get('entity.manager')->getStorage('config_test');
|
||||
// Test dependencies between configuration entities.
|
||||
$entity1 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity1'
|
||||
)
|
||||
);
|
||||
$entity1->save();
|
||||
$entity2 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity2',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'config' => array($entity1->getConfigDependencyName()),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
$entity2->save();
|
||||
|
||||
// Do a dry run using
|
||||
// \Drupal\Core\Config\ConfigManager::getConfigEntitiesToChangeOnDependencyRemoval().
|
||||
$config_entities = $config_manager->getConfigEntitiesToChangeOnDependencyRemoval('config', [$entity1->getConfigDependencyName()]);
|
||||
$this->assertEqual($entity2->uuid(), reset($config_entities['delete'])->uuid(), 'Entity 2 will be deleted.');
|
||||
$this->assertTrue(empty($config_entities['update']), 'No dependent configuration entities will be updated.');
|
||||
$this->assertTrue(empty($config_entities['unchanged']), 'No dependent configuration entities will be unchanged.');
|
||||
|
||||
// Test that doing a delete of entity1 deletes entity2 since it is dependent
|
||||
// on entity1.
|
||||
$entity1->delete();
|
||||
$this->assertFalse($storage->load('entity1'), 'Entity 1 deleted');
|
||||
$this->assertFalse($storage->load('entity2'), 'Entity 2 deleted');
|
||||
|
||||
// Set a more complicated test where dependencies will be fixed.
|
||||
\Drupal::state()->set('config_test.fix_dependencies', array($entity1->getConfigDependencyName()));
|
||||
|
||||
// Entity1 will be deleted by the test.
|
||||
$entity1 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity1',
|
||||
)
|
||||
);
|
||||
$entity1->save();
|
||||
|
||||
// Entity2 has a dependency on Entity1 but it can be fixed because
|
||||
// \Drupal\config_test\Entity::onDependencyRemoval() will remove the
|
||||
// dependency before config entities are deleted.
|
||||
$entity2 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity2',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'config' => array($entity1->getConfigDependencyName()),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
$entity2->save();
|
||||
|
||||
// Entity3 will be unchanged because it is dependent on Entity2 which can
|
||||
// be fixed.
|
||||
$entity3 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity3',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'config' => array($entity2->getConfigDependencyName()),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
$entity3->save();
|
||||
|
||||
// Do a dry run using
|
||||
// \Drupal\Core\Config\ConfigManager::getConfigEntitiesToChangeOnDependencyRemoval().
|
||||
$config_entities = $config_manager->getConfigEntitiesToChangeOnDependencyRemoval('config', [$entity1->getConfigDependencyName()]);
|
||||
$this->assertTrue(empty($config_entities['delete']), 'No dependent configuration entities will be deleted.');
|
||||
$this->assertEqual($entity2->uuid(), reset($config_entities['update'])->uuid(), 'Entity 2 will be updated.');
|
||||
$this->assertEqual($entity3->uuid(), reset($config_entities['unchanged'])->uuid(), 'Entity 3 is not changed.');
|
||||
|
||||
// Perform the uninstall.
|
||||
$entity1->delete();
|
||||
|
||||
// Test that expected actions have been performed.
|
||||
$this->assertFalse($storage->load('entity1'), 'Entity 1 deleted');
|
||||
$entity2 = $storage->load('entity2');
|
||||
$this->assertTrue($entity2, 'Entity 2 not deleted');
|
||||
$this->assertEqual($entity2->calculateDependencies()['config'], array(), 'Entity 2 dependencies updated to remove dependency on Entity1.');
|
||||
$entity3 = $storage->load('entity3');
|
||||
$this->assertTrue($entity3, 'Entity 3 not deleted');
|
||||
$this->assertEqual($entity3->calculateDependencies()['config'], [$entity2->getConfigDependencyName()], 'Entity 3 still depends on Entity 2.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests getConfigEntitiesToChangeOnDependencyRemoval() with content entities.
|
||||
*
|
||||
* At the moment there is no runtime code that calculates configuration
|
||||
* dependencies on content entity delete because this calculation is expensive
|
||||
* and all content dependencies are soft. This test ensures that the code
|
||||
* works for content entities.
|
||||
*
|
||||
* @see \Drupal\Core\Config\ConfigManager::getConfigEntitiesToChangeOnDependencyRemoval()
|
||||
*/
|
||||
public function testContentEntityDelete() {
|
||||
$this->installEntitySchema('entity_test');
|
||||
/** @var \Drupal\Core\Config\ConfigManagerInterface $config_manager */
|
||||
$config_manager = \Drupal::service('config.manager');
|
||||
|
||||
$content_entity = EntityTest::create();
|
||||
$content_entity->save();
|
||||
/** @var \Drupal\Core\Config\Entity\ConfigEntityStorage $storage */
|
||||
$storage = $this->container->get('entity.manager')->getStorage('config_test');
|
||||
$entity1 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity1',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'content' => array($content_entity->getConfigDependencyName())
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
$entity1->save();
|
||||
$entity2 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity2',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'config' => array($entity1->getConfigDependencyName())
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
$entity2->save();
|
||||
|
||||
// Create a configuration entity that is not in the dependency chain.
|
||||
$entity3 = $storage->create(array('id' => 'entity3'));
|
||||
$entity3->save();
|
||||
|
||||
$config_entities = $config_manager->getConfigEntitiesToChangeOnDependencyRemoval('content', [$content_entity->getConfigDependencyName()]);
|
||||
$this->assertEqual($entity1->uuid(), $config_entities['delete'][0]->uuid(), 'Entity 1 will be deleted.');
|
||||
$this->assertEqual($entity2->uuid(), $config_entities['delete'][1]->uuid(), 'Entity 2 will be deleted.');
|
||||
$this->assertTrue(empty($config_entities['update']), 'No dependencies of the content entity will be updated.');
|
||||
$this->assertTrue(empty($config_entities['unchanged']), 'No dependencies of the content entity will be unchanged.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of identifiers from an array of configuration entities.
|
||||
*
|
||||
* @param \Drupal\Core\Config\Entity\ConfigEntityInterface[] $dependents
|
||||
* An array of configuration entities.
|
||||
*
|
||||
* @return array
|
||||
* An array with values of entity_type_id:ID
|
||||
*/
|
||||
protected function getDependentIds(array $dependents) {
|
||||
$dependent_ids = array();
|
||||
foreach($dependents as $dependent) {
|
||||
$dependent_ids[] = $dependent->getEntityTypeId() . ':' . $dependent->id();
|
||||
}
|
||||
return $dependent_ids;
|
||||
}
|
||||
}
|
135
core/modules/config/src/Tests/ConfigDependencyWebTest.php
Normal file
135
core/modules/config/src/Tests/ConfigDependencyWebTest.php
Normal file
|
@ -0,0 +1,135 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigDependencyWebTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityStorage;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests configuration entities.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigDependencyWebTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* The maximum length for the entity storage used in this test.
|
||||
*/
|
||||
const MAX_ID_LENGTH = ConfigEntityStorage::MAX_ID_LENGTH;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_test');
|
||||
|
||||
/**
|
||||
* Tests ConfigDependencyDeleteFormTrait.
|
||||
*
|
||||
* @see \Drupal\Core\Config\Entity\ConfigDependencyDeleteFormTrait
|
||||
*/
|
||||
function testConfigDependencyDeleteFormTrait() {
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer site configuration']));
|
||||
|
||||
/** @var \Drupal\Core\Config\Entity\ConfigEntityStorage $storage */
|
||||
$storage = $this->container->get('entity.manager')->getStorage('config_test');
|
||||
// Entity1 will be deleted by the test.
|
||||
$entity1 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity1',
|
||||
'label' => 'Entity One',
|
||||
)
|
||||
);
|
||||
$entity1->save();
|
||||
|
||||
// Entity2 has a dependency on Entity1 but it can be fixed because
|
||||
// \Drupal\config_test\Entity::onDependencyRemoval() will remove the
|
||||
// dependency before config entities are deleted.
|
||||
$entity2 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity2',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'config' => array($entity1->getConfigDependencyName()),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
$entity2->save();
|
||||
|
||||
$this->drupalGet($entity2->urlInfo('delete-form'));
|
||||
$this->assertNoText(t('Configuration updates'), 'No configuration updates found.');
|
||||
$this->assertNoText(t('Configuration deletions'), 'No configuration deletes found.');
|
||||
$this->drupalGet($entity1->urlInfo('delete-form'));
|
||||
$this->assertNoText(t('Configuration updates'), 'No configuration updates found.');
|
||||
$this->assertText(t('Configuration deletions'), 'Configuration deletions found.');
|
||||
$this->assertText($entity2->id(), 'Entity2 id found');
|
||||
$this->drupalPostForm($entity1->urlInfo('delete-form'), array(), 'Delete');
|
||||
$storage->resetCache();
|
||||
$this->assertFalse($storage->loadMultiple([$entity1->id(), $entity2->id()]), 'Test entities deleted');
|
||||
|
||||
// Set a more complicated test where dependencies will be fixed.
|
||||
// Entity1 will be deleted by the test.
|
||||
$entity1 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity1',
|
||||
)
|
||||
);
|
||||
$entity1->save();
|
||||
\Drupal::state()->set('config_test.fix_dependencies', array($entity1->getConfigDependencyName()));
|
||||
|
||||
// Entity2 has a dependency on Entity1 but it can be fixed because
|
||||
// \Drupal\config_test\Entity::onDependencyRemoval() will remove the
|
||||
// dependency before config entities are deleted.
|
||||
$entity2 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity2',
|
||||
'label' => 'Entity Two',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'config' => array($entity1->getConfigDependencyName()),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
$entity2->save();
|
||||
|
||||
// Entity3 will be unchanged because it is dependent on Entity2 which can
|
||||
// be fixed.
|
||||
$entity3 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity3',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'config' => array($entity2->getConfigDependencyName()),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
$entity3->save();
|
||||
|
||||
$this->drupalGet($entity1->urlInfo('delete-form'));
|
||||
$this->assertText(t('Configuration updates'), 'Configuration updates found.');
|
||||
$this->assertNoText(t('Configuration deletions'), 'No configuration deletions found.');
|
||||
$this->assertNoText($entity2->id(), 'Entity2 id not found');
|
||||
$this->assertText($entity2->label(), 'Entity2 label not found');
|
||||
$this->assertNoText($entity3->id(), 'Entity3 id not found');
|
||||
$this->drupalPostForm($entity1->urlInfo('delete-form'), array(), 'Delete');
|
||||
$storage->resetCache();
|
||||
$this->assertFalse($storage->load('entity1'), 'Test entity 1 deleted');
|
||||
$entity2 = $storage->load('entity2');
|
||||
$this->assertTrue($entity2, 'Entity 2 not deleted');
|
||||
$this->assertEqual($entity2->calculateDependencies()['config'], array(), 'Entity 2 dependencies updated to remove dependency on Entity1.');
|
||||
$entity3 = $storage->load('entity3');
|
||||
$this->assertTrue($entity3, 'Entity 3 not deleted');
|
||||
$this->assertEqual($entity3->calculateDependencies()['config'], [$entity2->getConfigDependencyName()], 'Entity 3 still depends on Entity 2.');
|
||||
|
||||
}
|
||||
|
||||
}
|
150
core/modules/config/src/Tests/ConfigDiffTest.php
Normal file
150
core/modules/config/src/Tests/ConfigDiffTest.php
Normal file
|
@ -0,0 +1,150 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigDiffTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Calculating the difference between two sets of configuration.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigDiffTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_test', 'system');
|
||||
|
||||
/**
|
||||
* Tests calculating the difference between two sets of configuration.
|
||||
*/
|
||||
function testDiff() {
|
||||
$active = $this->container->get('config.storage');
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
$config_name = 'config_test.system';
|
||||
$change_key = 'foo';
|
||||
$remove_key = '404';
|
||||
$add_key = 'biff';
|
||||
$add_data = 'bangpow';
|
||||
$change_data = 'foobar';
|
||||
$original_data = array(
|
||||
'foo' => 'bar',
|
||||
'404' => 'herp',
|
||||
);
|
||||
|
||||
// Install the default config.
|
||||
$this->installConfig(array('config_test'));
|
||||
|
||||
// Change a configuration value in staging.
|
||||
$staging_data = $original_data;
|
||||
$staging_data[$change_key] = $change_data;
|
||||
$staging_data[$add_key] = $add_data;
|
||||
$staging->write($config_name, $staging_data);
|
||||
|
||||
// Verify that the diff reflects a change.
|
||||
$diff = \Drupal::service('config.manager')->diff($active, $staging, $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 staging value for key '%change_key' is '%change_data'.", array('%change_key' => $change_key, '%change_data' => $change_data)));
|
||||
|
||||
// Reset data back to original, and remove a key
|
||||
$staging_data = $original_data;
|
||||
unset($staging_data[$remove_key]);
|
||||
$staging->write($config_name, $staging_data);
|
||||
|
||||
// Verify that the diff reflects a removed key.
|
||||
$diff = \Drupal::service('config.manager')->diff($active, $staging, $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 staging.", array('%remove_key' => $remove_key)));
|
||||
|
||||
// Reset data back to original and add a key
|
||||
$staging_data = $original_data;
|
||||
$staging_data[$add_key] = $add_data;
|
||||
$staging->write($config_name, $staging_data);
|
||||
|
||||
// Verify that the diff reflects an added key.
|
||||
$diff = \Drupal::service('config.manager')->diff($active, $staging, $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 staging value for key '%add_key' is '%add_data'.", array('%add_key' => $add_key, '%add_data' => $add_data)));
|
||||
|
||||
// Test diffing a renamed config entity.
|
||||
$test_entity_id = $this->randomMachineName();
|
||||
$test_entity = entity_create('config_test', array(
|
||||
'id' => $test_entity_id,
|
||||
'label' => $this->randomMachineName(),
|
||||
));
|
||||
$test_entity->save();
|
||||
$data = $active->read('config_test.dynamic.' . $test_entity_id);
|
||||
$staging->write('config_test.dynamic.' . $test_entity_id, $data);
|
||||
$config_name = 'config_test.dynamic.' . $test_entity_id;
|
||||
$diff = \Drupal::service('config.manager')->diff($active, $staging, $config_name, $config_name);
|
||||
// Prove the fields match.
|
||||
$edits = $diff->getEdits();
|
||||
$this->assertEqual($edits[0]->type, 'copy', 'The first item in the diff is a copy.');
|
||||
$this->assertEqual(count($edits), 1, 'There is one item in the diff');
|
||||
|
||||
// Rename the entity.
|
||||
$new_test_entity_id = $this->randomMachineName();
|
||||
$test_entity->set('id', $new_test_entity_id);
|
||||
$test_entity->save();
|
||||
|
||||
$diff = \Drupal::service('config.manager')->diff($active, $staging, '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->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.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests calculating the difference between two sets of config collections.
|
||||
*/
|
||||
function testCollectionDiff() {
|
||||
/** @var \Drupal\Core\Config\StorageInterface $active */
|
||||
$active = $this->container->get('config.storage');
|
||||
/** @var \Drupal\Core\Config\StorageInterface $staging */
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
$active_test_collection = $active->createCollection('test');
|
||||
$staging_test_collection = $staging->createCollection('test');
|
||||
|
||||
$config_name = 'config_test.test';
|
||||
$data = array('foo' => 'bar');
|
||||
|
||||
$active->write($config_name, $data);
|
||||
$staging->write($config_name, $data);
|
||||
$active_test_collection->write($config_name, $data);
|
||||
$staging_test_collection->write($config_name, array('foo' => 'baz'));
|
||||
|
||||
// Test the fields match in the default collection diff.
|
||||
$diff = \Drupal::service('config.manager')->diff($active, $staging, $config_name);
|
||||
$edits = $diff->getEdits();
|
||||
$this->assertEqual($edits[0]->type, 'copy', 'The first item in the diff is a copy.');
|
||||
$this->assertEqual(count($edits), 1, 'There is one item in the diff');
|
||||
|
||||
// Test that the differences are detected when diffing the collection.
|
||||
$diff = \Drupal::service('config.manager')->diff($active, $staging, $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.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigEntityFormOverrideTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests that config overrides do not bleed through in entity forms and lists.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigEntityFormOverrideTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('config_test');
|
||||
|
||||
/**
|
||||
* Tests that overrides do not affect forms or listing screens.
|
||||
*/
|
||||
public function testFormsWithOverrides() {
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer site configuration']));
|
||||
|
||||
$original_label = 'Default';
|
||||
$overridden_label = 'Overridden label';
|
||||
$edited_label = 'Edited label';
|
||||
|
||||
// Set up an override.
|
||||
$settings['config']['config_test.dynamic.dotted.default']['label'] = (object) array(
|
||||
'value' => $overridden_label,
|
||||
'required' => TRUE,
|
||||
);
|
||||
$this->writeSettings($settings);
|
||||
|
||||
// Test that the overridden label is loaded with the entity.
|
||||
$this->assertEqual(config_test_load('dotted.default')->label(), $overridden_label);
|
||||
|
||||
// Test that the original label on the listing page is intact.
|
||||
$this->drupalGet('admin/structure/config_test');
|
||||
$this->assertText($original_label);
|
||||
$this->assertNoText($overridden_label);
|
||||
|
||||
// Test that the original label on the editing page is intact.
|
||||
$this->drupalGet('admin/structure/config_test/manage/dotted.default');
|
||||
$elements = $this->xpath('//input[@name="label"]');
|
||||
$this->assertIdentical((string) $elements[0]['value'], $original_label);
|
||||
$this->assertNoText($overridden_label);
|
||||
|
||||
// Change to a new label and test that the listing now has the edited label.
|
||||
$edit = array(
|
||||
'label' => $edited_label,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->drupalGet('admin/structure/config_test');
|
||||
$this->assertNoText($overridden_label);
|
||||
$this->assertText($edited_label);
|
||||
|
||||
// Test that the editing page now has the edited label.
|
||||
$this->drupalGet('admin/structure/config_test/manage/dotted.default');
|
||||
$elements = $this->xpath('//input[@name="label"]');
|
||||
$this->assertIdentical((string) $elements[0]['value'], $edited_label);
|
||||
|
||||
// Test that the overridden label is still loaded with the entity.
|
||||
$this->assertEqual(config_test_load('dotted.default')->label(), $overridden_label);
|
||||
}
|
||||
|
||||
}
|
282
core/modules/config/src/Tests/ConfigEntityListTest.php
Normal file
282
core/modules/config/src/Tests/ConfigEntityListTest.php
Normal file
|
@ -0,0 +1,282 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigEntityListTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\config_test\Entity\ConfigTest;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
|
||||
/**
|
||||
* Tests the listing of configuration entities.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigEntityListTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_test');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Delete the override config_test entity since it is not required by this
|
||||
// test.
|
||||
\Drupal::entityManager()->getStorage('config_test')->load('override')->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests entity list builder methods.
|
||||
*/
|
||||
function testList() {
|
||||
$controller = \Drupal::entityManager()->getListBuilder('config_test');
|
||||
|
||||
// Test getStorage() method.
|
||||
$this->assertTrue($controller->getStorage() instanceof EntityStorageInterface, 'EntityStorage instance in storage.');
|
||||
|
||||
// Get a list of ConfigTest entities and confirm that it contains the
|
||||
// ConfigTest entity provided by the config_test module.
|
||||
// @see config_test.dynamic.dotted.default.yml
|
||||
$list = $controller->load();
|
||||
$this->assertEqual(count($list), 1, '1 ConfigTest entity found.');
|
||||
$entity = $list['dotted.default'];
|
||||
$this->assertTrue(!empty($entity), '"Default" ConfigTest entity ID found.');
|
||||
$this->assertTrue($entity instanceof ConfigTest, '"Default" ConfigTest entity is an instance of ConfigTest.');
|
||||
|
||||
// Test getOperations() method.
|
||||
$expected_operations = array(
|
||||
'edit' => array (
|
||||
'title' => t('Edit'),
|
||||
'weight' => 10,
|
||||
'url' => $entity->urlInfo(),
|
||||
),
|
||||
'disable' => array(
|
||||
'title' => t('Disable'),
|
||||
'weight' => 40,
|
||||
'url' => $entity->urlInfo('disable'),
|
||||
),
|
||||
'delete' => array (
|
||||
'title' => t('Delete'),
|
||||
'weight' => 100,
|
||||
'url' => $entity->urlInfo('delete-form'),
|
||||
),
|
||||
);
|
||||
|
||||
$actual_operations = $controller->getOperations($entity);
|
||||
// Sort the operations to normalize link order.
|
||||
uasort($actual_operations, array('Drupal\Component\Utility\SortArray', 'sortByWeightElement'));
|
||||
$this->assertEqual($expected_operations, $actual_operations, 'The operations are identical.');
|
||||
|
||||
// Test buildHeader() method.
|
||||
$expected_items = array(
|
||||
'label' => 'Label',
|
||||
'id' => 'Machine name',
|
||||
'operations' => 'Operations',
|
||||
);
|
||||
$actual_items = $controller->buildHeader();
|
||||
$this->assertEqual($expected_items, $actual_items, 'Return value from buildHeader matches expected.');
|
||||
|
||||
// Test buildRow() method.
|
||||
$build_operations = $controller->buildOperations($entity);
|
||||
$expected_items = array(
|
||||
'label' => 'Default',
|
||||
'id' => 'dotted.default',
|
||||
'operations' => array(
|
||||
'data' => $build_operations,
|
||||
),
|
||||
);
|
||||
$actual_items = $controller->buildRow($entity);
|
||||
$this->assertEqual($expected_items, $actual_items, 'Return value from buildRow matches expected.');
|
||||
// Test sorting.
|
||||
$storage = $controller->getStorage();
|
||||
$entity = $storage->create(array(
|
||||
'id' => 'alpha',
|
||||
'label' => 'Alpha',
|
||||
'weight' => 1,
|
||||
));
|
||||
$entity->save();
|
||||
$entity = $storage->create(array(
|
||||
'id' => 'omega',
|
||||
'label' => 'Omega',
|
||||
'weight' => 1,
|
||||
));
|
||||
$entity->save();
|
||||
$entity = $storage->create(array(
|
||||
'id' => 'beta',
|
||||
'label' => 'Beta',
|
||||
'weight' => 0,
|
||||
));
|
||||
$entity->save();
|
||||
$list = $controller->load();
|
||||
$this->assertIdentical(array_keys($list), array('beta', 'dotted.default', 'alpha', 'omega'));
|
||||
|
||||
// Test that config entities that do not support status, do not have
|
||||
// enable/disable operations.
|
||||
$controller = $this->container->get('entity.manager')
|
||||
->getListBuilder('config_test_no_status');
|
||||
|
||||
$list = $controller->load();
|
||||
$entity = $list['default'];
|
||||
|
||||
// Test getOperations() method.
|
||||
$expected_operations = array(
|
||||
'edit' => array(
|
||||
'title' => t('Edit'),
|
||||
'weight' => 10,
|
||||
'url' => $entity->urlInfo(),
|
||||
),
|
||||
'delete' => array(
|
||||
'title' => t('Delete'),
|
||||
'weight' => 100,
|
||||
'url' => $entity->urlInfo('delete-form'),
|
||||
),
|
||||
);
|
||||
|
||||
$actual_operations = $controller->getOperations($entity);
|
||||
// Sort the operations to normalize link order.
|
||||
uasort($actual_operations, array('Drupal\Component\Utility\SortArray', 'sortByWeightElement'));
|
||||
$this->assertEqual($expected_operations, $actual_operations, 'The operations are identical.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the listing UI.
|
||||
*/
|
||||
function testListUI() {
|
||||
// Log in as an administrative user to access the full menu trail.
|
||||
$this->drupalLogin($this->drupalCreateUser(array('access administration pages', 'administer site configuration')));
|
||||
|
||||
// Get the list callback page.
|
||||
$this->drupalGet('admin/structure/config_test');
|
||||
|
||||
// Test for the page title.
|
||||
$this->assertTitle('Test configuration | Drupal');
|
||||
|
||||
// Test for the table.
|
||||
$element = $this->xpath('//div[@class="layout-content"]//table');
|
||||
$this->assertTrue($element, 'Configuration entity list table found.');
|
||||
|
||||
// Test the table header.
|
||||
$elements = $this->xpath('//div[@class="layout-content"]//table/thead/tr/th');
|
||||
$this->assertEqual(count($elements), 3, 'Correct number of table header cells found.');
|
||||
|
||||
// Test the contents of each th cell.
|
||||
$expected_items = array('Label', 'Machine name', 'Operations');
|
||||
foreach ($elements as $key => $element) {
|
||||
$this->assertIdentical((string) $element[0], $expected_items[$key]);
|
||||
}
|
||||
|
||||
// Check the number of table row cells.
|
||||
$elements = $this->xpath('//div[@class="layout-content"]//table/tbody/tr[@class="odd"]/td');
|
||||
$this->assertEqual(count($elements), 3, 'Correct number of table row cells found.');
|
||||
|
||||
// Check the contents of each row cell. The first cell contains the label,
|
||||
// the second contains the machine name, and the third contains the
|
||||
// operations list.
|
||||
$this->assertIdentical((string) $elements[0], 'Default');
|
||||
$this->assertIdentical((string) $elements[1], 'dotted.default');
|
||||
$this->assertTrue($elements[2]->children()->xpath('//ul'), 'Operations list found.');
|
||||
|
||||
// Add a new entity using the operations link.
|
||||
$this->assertLink('Add test configuration');
|
||||
$this->clickLink('Add test configuration');
|
||||
$this->assertResponse(200);
|
||||
$edit = array(
|
||||
'label' => 'Antelope',
|
||||
'id' => 'antelope',
|
||||
'weight' => 1,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
|
||||
// Ensure that the entity's sort method was called.
|
||||
$this->assertTrue(\Drupal::state()->get('config_entity_sort'), 'ConfigTest::sort() was called.');
|
||||
|
||||
// Confirm that the user is returned to the listing, and verify that the
|
||||
// text of the label and machine name appears in the list (versus elsewhere
|
||||
// on the page).
|
||||
$this->assertFieldByXpath('//td', 'Antelope', "Label found for added 'Antelope' entity.");
|
||||
$this->assertFieldByXpath('//td', 'antelope', "Machine name found for added 'Antelope' entity.");
|
||||
|
||||
// Edit the entity using the operations link.
|
||||
$this->assertLinkByHref('admin/structure/config_test/manage/antelope');
|
||||
$this->clickLink('Edit', 1);
|
||||
$this->assertResponse(200);
|
||||
$this->assertTitle('Edit Antelope | Drupal');
|
||||
$edit = array('label' => 'Albatross', 'id' => 'albatross');
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
|
||||
// Confirm that the user is returned to the listing, and verify that the
|
||||
// text of the label and machine name appears in the list (versus elsewhere
|
||||
// on the page).
|
||||
$this->assertFieldByXpath('//td', 'Albatross', "Label found for updated 'Albatross' entity.");
|
||||
$this->assertFieldByXpath('//td', 'albatross', "Machine name found for updated 'Albatross' entity.");
|
||||
|
||||
// Delete the added entity using the operations link.
|
||||
$this->assertLinkByHref('admin/structure/config_test/manage/albatross/delete');
|
||||
$this->clickLink('Delete', 1);
|
||||
$this->assertResponse(200);
|
||||
$this->assertTitle('Are you sure you want to delete the test configuration Albatross? | Drupal');
|
||||
$this->drupalPostForm(NULL, array(), t('Delete'));
|
||||
|
||||
// Verify that the text of the label and machine name does not appear in
|
||||
// the list (though it may appear elsewhere on the page).
|
||||
$this->assertNoFieldByXpath('//td', 'Albatross', "No label found for deleted 'Albatross' entity.");
|
||||
$this->assertNoFieldByXpath('//td', 'albatross', "No machine name found for deleted 'Albatross' entity.");
|
||||
|
||||
// Delete the original entity using the operations link.
|
||||
$this->clickLink('Delete');
|
||||
$this->assertResponse(200);
|
||||
$this->assertTitle('Are you sure you want to delete the test configuration Default? | Drupal');
|
||||
$this->drupalPostForm(NULL, array(), t('Delete'));
|
||||
|
||||
// Verify that the text of the label and machine name does not appear in
|
||||
// the list (though it may appear elsewhere on the page).
|
||||
$this->assertNoFieldByXpath('//td', 'Default', "No label found for deleted 'Default' entity.");
|
||||
$this->assertNoFieldByXpath('//td', 'dotted.default', "No machine name found for deleted 'Default' entity.");
|
||||
|
||||
// Confirm that the empty text is displayed.
|
||||
$this->assertText('There is no Test configuration yet.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test paging.
|
||||
*/
|
||||
public function testPager() {
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer site configuration']));
|
||||
|
||||
$storage = \Drupal::entityManager()->getListBuilder('config_test')->getStorage();
|
||||
|
||||
// Create 51 test entities.
|
||||
for ($i = 1; $i < 52; $i++) {
|
||||
$storage->create(array(
|
||||
'id' => str_pad($i, 2, '0', STR_PAD_LEFT),
|
||||
'label' => 'Test config entity ' . $i,
|
||||
'weight' => $i,
|
||||
'protected_property' => $i,
|
||||
))->save();
|
||||
}
|
||||
|
||||
// Load the listing page.
|
||||
$this->drupalGet('admin/structure/config_test');
|
||||
|
||||
// Item 51 should not be present.
|
||||
$this->assertRaw('Test config entity 50', 'Config entity 50 is shown.');
|
||||
$this->assertNoRaw('Test config entity 51', 'Config entity 51 is on the next page.');
|
||||
|
||||
// Browse to the next page.
|
||||
$this->clickLink(t('Page 2'));
|
||||
$this->assertNoRaw('Test config entity 50', 'Test config entity 50 is on the previous page.');
|
||||
$this->assertRaw('dotted.default', 'Default config entity appears on page 2.');
|
||||
$this->assertRaw('Test config entity 51', 'Test config entity 51 is on page 2.');
|
||||
}
|
||||
|
||||
}
|
51
core/modules/config/src/Tests/ConfigEntityNormalizeTest.php
Normal file
51
core/modules/config/src/Tests/ConfigEntityNormalizeTest.php
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigEntityNormalizeTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the listing of configuration entities.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigEntityNormalizeTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_test');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installConfig(static::$modules);
|
||||
}
|
||||
|
||||
public function testNormalize() {
|
||||
$config_entity = entity_create('config_test', array('id' => 'system', 'label' => 'foobar', 'weight' => 1));
|
||||
$config_entity->save();
|
||||
|
||||
// Modify stored config entity, this is comparable with a schema change.
|
||||
$config = $this->config('config_test.dynamic.system');
|
||||
$data = array(
|
||||
'label' => 'foobar',
|
||||
'additional_key' => TRUE
|
||||
) + $config->getRawData();
|
||||
$config->setData($data)->save();
|
||||
$this->assertNotIdentical($config_entity->toArray(), $config->getRawData(), 'Stored config entity is not is equivalent to config schema.');
|
||||
|
||||
$config_entity = entity_load('config_test', 'system', TRUE);
|
||||
$config_entity->save();
|
||||
|
||||
$config = $this->config('config_test.dynamic.system');
|
||||
$this->assertIdentical($config_entity->toArray(), $config->getRawData(), 'Stored config entity is equivalent to config schema.');
|
||||
}
|
||||
|
||||
}
|
110
core/modules/config/src/Tests/ConfigEntityStaticCacheTest.php
Normal file
110
core/modules/config/src/Tests/ConfigEntityStaticCacheTest.php
Normal file
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigEntityStaticCacheTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\config_entity_static_cache_test\ConfigOverrider;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the entity static cache when used by config entities.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigEntityStaticCacheTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_test', 'config_entity_static_cache_test');
|
||||
|
||||
/**
|
||||
* The type ID of the entity under test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityTypeId;
|
||||
|
||||
/**
|
||||
* The entity ID of the entity under test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityId;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->entityTypeId = 'config_test';
|
||||
$this->entityId = 'test_1';
|
||||
entity_create($this->entityTypeId, array('id' => $this->entityId, 'label' => 'Original label'))->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
// 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.
|
||||
$this->assertIdentical($entity_1->_loadStamp, $entity_2->_loadStamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the static cache is reset on entity save and delete.
|
||||
*/
|
||||
public function testReset() {
|
||||
$entity = entity_load($this->entityTypeId, $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);
|
||||
$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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the static cache is sensitive to config overrides.
|
||||
*/
|
||||
public function testConfigOverride() {
|
||||
/** @var \Drupal\Core\Config\Entity\ConfigEntityStorage $storage */
|
||||
$storage = \Drupal::entityManager()->getStorage($this->entityTypeId);
|
||||
// Prime the cache prior to adding a config override.
|
||||
$storage->load($this->entityId);
|
||||
|
||||
// Add the config override, and ensure that what is loaded is correct
|
||||
// despite the prior cache priming.
|
||||
\Drupal::configFactory()->addOverride(new ConfigOverrider());
|
||||
$entity_override = $storage->load($this->entityId);
|
||||
$this->assertIdentical($entity_override->label, 'Overridden label');
|
||||
|
||||
// Load override free to ensure that loading the config entity again does
|
||||
// not return the overridden value.
|
||||
$entity_no_override = $storage->loadOverrideFree($this->entityId);
|
||||
$this->assertNotIdentical($entity_no_override->label, 'Overridden label');
|
||||
$this->assertNotIdentical($entity_override->_loadStamp, $entity_no_override->_loadStamp);
|
||||
|
||||
// Reload the entity and ensure the cache is used.
|
||||
$this->assertIdentical($storage->loadOverrideFree($this->entityId)->_loadStamp, $entity_no_override->_loadStamp);
|
||||
|
||||
// Enable overrides and reload the entity and ensure the cache is used.
|
||||
$this->assertIdentical($storage->load($this->entityId)->_loadStamp, $entity_override->_loadStamp);
|
||||
}
|
||||
|
||||
}
|
47
core/modules/config/src/Tests/ConfigEntityStatusTest.php
Normal file
47
core/modules/config/src/Tests/ConfigEntityStatusTest.php
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigEntityStatusTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests configuration entity status functionality.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigEntityStatusTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_test');
|
||||
|
||||
/**
|
||||
* Tests the enabling/disabling of entities.
|
||||
*/
|
||||
function testCRUD() {
|
||||
$entity = entity_create('config_test', array(
|
||||
'id' => strtolower($this->randomMachineName()),
|
||||
));
|
||||
$this->assertTrue($entity->status(), 'Default status is enabled.');
|
||||
$entity->save();
|
||||
$this->assertTrue($entity->status(), 'Status is enabled after saving.');
|
||||
|
||||
$entity->disable()->save();
|
||||
$this->assertFalse($entity->status(), 'Entity is disabled after disabling.');
|
||||
|
||||
$entity->enable()->save();
|
||||
$this->assertTrue($entity->status(), 'Entity is enabled after enabling.');
|
||||
|
||||
$entity = entity_load('config_test', $entity->id());
|
||||
$this->assertTrue($entity->status(), 'Status is enabled after reload.');
|
||||
}
|
||||
|
||||
}
|
57
core/modules/config/src/Tests/ConfigEntityStatusUITest.php
Normal file
57
core/modules/config/src/Tests/ConfigEntityStatusUITest.php
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigEntityStatusUITest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Core\Entity\EntityMalformedException;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests configuration entity status UI functionality.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigEntityStatusUITest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_test');
|
||||
|
||||
/**
|
||||
* Tests status operations.
|
||||
*/
|
||||
function testCRUD() {
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer site configuration']));
|
||||
|
||||
$id = strtolower($this->randomMachineName());
|
||||
$edit = array(
|
||||
'id' => $id,
|
||||
'label' => $this->randomMachineName(),
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/config_test/add', $edit, 'Save');
|
||||
|
||||
$entity = entity_load('config_test', $id);
|
||||
|
||||
// Disable an entity.
|
||||
$disable_url = $entity->urlInfo('disable');
|
||||
$this->assertLinkByHref($disable_url->toString());
|
||||
$this->drupalGet($disable_url);
|
||||
$this->assertResponse(200);
|
||||
$this->assertNoLinkByHref($disable_url->toString());
|
||||
|
||||
// Enable an entity.
|
||||
$enable_url = $entity->urlInfo('enable');
|
||||
$this->assertLinkByHref($enable_url->toString());
|
||||
$this->drupalGet($enable_url);
|
||||
$this->assertResponse(200);
|
||||
$this->assertNoLinkByHref($enable_url->toString());
|
||||
}
|
||||
|
||||
}
|
57
core/modules/config/src/Tests/ConfigEntityStorageTest.php
Normal file
57
core/modules/config/src/Tests/ConfigEntityStorageTest.php
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigEntityStorageTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
use Drupal\Core\Config\ConfigDuplicateUUIDException;
|
||||
|
||||
/**
|
||||
* Tests staging and importing config entities with IDs and UUIDs that match
|
||||
* existing config.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigEntityStorageTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_test');
|
||||
|
||||
/**
|
||||
* Tests creating configuration entities with changed UUIDs.
|
||||
*/
|
||||
public function testUUIDConflict() {
|
||||
$entity_type = 'config_test';
|
||||
$id = 'test_1';
|
||||
// Load the original configuration entity.
|
||||
entity_create($entity_type, array('id' => $id))->save();
|
||||
$entity = entity_load($entity_type, $id);
|
||||
|
||||
$original_properties = $entity->toArray();
|
||||
|
||||
// Override with a new UUID and try to save.
|
||||
$new_uuid = $this->container->get('uuid')->generate();
|
||||
$entity->set('uuid', $new_uuid);
|
||||
|
||||
try {
|
||||
$entity->save();
|
||||
$this->fail('Exception thrown when attempting to save a configuration entity with a UUID that does not match the existing UUID.');
|
||||
}
|
||||
catch (ConfigDuplicateUUIDException $e) {
|
||||
$this->pass(format_string('Exception thrown when attempting to save a configuration entity with a UUID that does not match existing data: %e.', array('%e' => $e)));
|
||||
}
|
||||
|
||||
// Ensure that the config entity was not corrupted.
|
||||
$entity = entity_load('config_test', $entity->id(), TRUE);
|
||||
$this->assertIdentical($entity->toArray(), $original_properties);
|
||||
}
|
||||
|
||||
}
|
364
core/modules/config/src/Tests/ConfigEntityTest.php
Normal file
364
core/modules/config/src/Tests/ConfigEntityTest.php
Normal file
|
@ -0,0 +1,364 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigEntityTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Entity\EntityMalformedException;
|
||||
use Drupal\Core\Entity\EntityStorageException;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityStorage;
|
||||
use Drupal\Core\Config\Entity\Exception\ConfigEntityIdLengthException;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests configuration entities.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigEntityTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* The maximum length for the entity storage used in this test.
|
||||
*/
|
||||
const MAX_ID_LENGTH = ConfigEntityStorage::MAX_ID_LENGTH;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_test');
|
||||
|
||||
/**
|
||||
* Tests CRUD operations.
|
||||
*/
|
||||
function testCRUD() {
|
||||
$default_langcode = \Drupal::languageManager()->getDefaultLanguage()->getId();
|
||||
// Verify default properties on a newly created empty entity.
|
||||
$empty = entity_create('config_test');
|
||||
$this->assertTrue($empty->uuid());
|
||||
$this->assertIdentical($empty->label, NULL);
|
||||
$this->assertIdentical($empty->style, NULL);
|
||||
$this->assertIdentical($empty->language()->getId(), $default_langcode);
|
||||
|
||||
// Verify ConfigEntity properties/methods on the newly created empty entity.
|
||||
$this->assertIdentical($empty->isNew(), TRUE);
|
||||
$this->assertIdentical($empty->getOriginalId(), NULL);
|
||||
$this->assertIdentical($empty->bundle(), 'config_test');
|
||||
$this->assertIdentical($empty->id(), NULL);
|
||||
$this->assertTrue($empty->uuid());
|
||||
$this->assertIdentical($empty->label(), NULL);
|
||||
|
||||
$this->assertIdentical($empty->get('id'), NULL);
|
||||
$this->assertTrue($empty->get('uuid'));
|
||||
$this->assertIdentical($empty->get('label'), NULL);
|
||||
$this->assertIdentical($empty->get('style'), NULL);
|
||||
$this->assertIdentical($empty->language()->getId(), $default_langcode);
|
||||
|
||||
// Verify Entity properties/methods on the newly created empty entity.
|
||||
$this->assertIdentical($empty->getEntityTypeId(), 'config_test');
|
||||
// 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 = entity_create('config_test', 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.
|
||||
$config_test = entity_create('config_test', $expected = array(
|
||||
'id' => $this->randomMachineName(),
|
||||
'label' => $this->randomString(),
|
||||
'style' => $this->randomMachineName(),
|
||||
));
|
||||
$this->assertTrue($config_test->uuid());
|
||||
$this->assertNotEqual($config_test->uuid(), $empty->uuid());
|
||||
$this->assertIdentical($config_test->label, $expected['label']);
|
||||
$this->assertIdentical($config_test->style, $expected['style']);
|
||||
$this->assertIdentical($config_test->language()->getId(), $default_langcode);
|
||||
|
||||
// Verify methods on the newly created entity.
|
||||
$this->assertIdentical($config_test->isNew(), TRUE);
|
||||
$this->assertIdentical($config_test->getOriginalId(), $expected['id']);
|
||||
$this->assertIdentical($config_test->id(), $expected['id']);
|
||||
$this->assertTrue($config_test->uuid());
|
||||
$expected['uuid'] = $config_test->uuid();
|
||||
$this->assertIdentical($config_test->label(), $expected['label']);
|
||||
|
||||
// Verify that the entity can be saved.
|
||||
try {
|
||||
$status = $config_test->save();
|
||||
$this->pass('EntityMalformedException was not thrown.');
|
||||
}
|
||||
catch (EntityMalformedException $e) {
|
||||
$this->fail('EntityMalformedException was not thrown.');
|
||||
}
|
||||
|
||||
// The entity path can only be checked after saving.
|
||||
$this->assertIdentical($config_test->url(), Url::fromRoute('entity.config_test.edit_form', ['config_test' => $expected['id']])->toString());
|
||||
|
||||
// Verify that the correct status is returned and properties did not change.
|
||||
$this->assertIdentical($status, SAVED_NEW);
|
||||
$this->assertIdentical($config_test->id(), $expected['id']);
|
||||
$this->assertIdentical($config_test->uuid(), $expected['uuid']);
|
||||
$this->assertIdentical($config_test->label(), $expected['label']);
|
||||
$this->assertIdentical($config_test->isNew(), FALSE);
|
||||
$this->assertIdentical($config_test->getOriginalId(), $expected['id']);
|
||||
|
||||
// Save again, and verify correct status and properties again.
|
||||
$status = $config_test->save();
|
||||
$this->assertIdentical($status, SAVED_UPDATED);
|
||||
$this->assertIdentical($config_test->id(), $expected['id']);
|
||||
$this->assertIdentical($config_test->uuid(), $expected['uuid']);
|
||||
$this->assertIdentical($config_test->label(), $expected['label']);
|
||||
$this->assertIdentical($config_test->isNew(), FALSE);
|
||||
$this->assertIdentical($config_test->getOriginalId(), $expected['id']);
|
||||
|
||||
// Verify that a configuration entity can be saved with an ID of the
|
||||
// maximum allowed length, but not longer.
|
||||
|
||||
// Test with a short ID.
|
||||
$id_length_config_test = entity_create('config_test', array(
|
||||
'id' => $this->randomMachineName(8),
|
||||
));
|
||||
try {
|
||||
$id_length_config_test->save();
|
||||
$this->pass(SafeMarkup::format("config_test entity with ID length @length was saved.", array(
|
||||
'@length' => strlen($id_length_config_test->id()))
|
||||
));
|
||||
}
|
||||
catch (ConfigEntityIdLengthException $e) {
|
||||
$this->fail($e->getMessage());
|
||||
}
|
||||
|
||||
// Test with an ID of the maximum allowed length.
|
||||
$id_length_config_test = entity_create('config_test', array(
|
||||
'id' => $this->randomMachineName(static::MAX_ID_LENGTH),
|
||||
));
|
||||
try {
|
||||
$id_length_config_test->save();
|
||||
$this->pass(SafeMarkup::format("config_test entity with ID length @length was saved.", array(
|
||||
'@length' => strlen($id_length_config_test->id()),
|
||||
)));
|
||||
}
|
||||
catch (ConfigEntityIdLengthException $e) {
|
||||
$this->fail($e->getMessage());
|
||||
}
|
||||
|
||||
// Test with an ID exceeding the maximum allowed length.
|
||||
$id_length_config_test = entity_create('config_test', array(
|
||||
'id' => $this->randomMachineName(static::MAX_ID_LENGTH + 1),
|
||||
));
|
||||
try {
|
||||
$status = $id_length_config_test->save();
|
||||
$this->fail(SafeMarkup::format("config_test entity with ID length @length exceeding the maximum allowed length of @max saved successfully", array(
|
||||
'@length' => strlen($id_length_config_test->id()),
|
||||
'@max' => static::MAX_ID_LENGTH,
|
||||
)));
|
||||
}
|
||||
catch (ConfigEntityIdLengthException $e) {
|
||||
$this->pass(SafeMarkup::format("config_test entity with ID length @length exceeding the maximum allowed length of @max failed to save", array(
|
||||
'@length' => strlen($id_length_config_test->id()),
|
||||
'@max' => static::MAX_ID_LENGTH,
|
||||
)));
|
||||
}
|
||||
|
||||
// Ensure that creating an entity with the same id as an existing one is not
|
||||
// possible.
|
||||
$same_id = entity_create('config_test', array(
|
||||
'id' => $config_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($config_test->id(), $old_id);
|
||||
$this->assertIdentical($config_test->getOriginalId(), $old_id);
|
||||
|
||||
// Rename.
|
||||
$config_test->set('id', $new_id);
|
||||
$this->assertIdentical($config_test->id(), $new_id);
|
||||
$status = $config_test->save();
|
||||
$this->assertIdentical($status, SAVED_UPDATED);
|
||||
$this->assertIdentical($config_test->isNew(), FALSE);
|
||||
|
||||
// Verify that originalID points to new ID directly after renaming.
|
||||
$this->assertIdentical($config_test->id(), $new_id);
|
||||
$this->assertIdentical($config_test->getOriginalId(), $new_id);
|
||||
}
|
||||
|
||||
// Test config entity prepopulation.
|
||||
\Drupal::state()->set('config_test.prepopulate', TRUE);
|
||||
$config_test = entity_create('config_test', array('foo' => 'bar'));
|
||||
$this->assertEqual($config_test->get('foo'), 'baz', 'Initial value correctly populated');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests CRUD operations through the UI.
|
||||
*/
|
||||
function testCRUDUI() {
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer site configuration']));
|
||||
|
||||
$id = strtolower($this->randomMachineName());
|
||||
$label1 = $this->randomMachineName();
|
||||
$label2 = $this->randomMachineName();
|
||||
$label3 = $this->randomMachineName();
|
||||
$message_insert = format_string('%label configuration has been created.', array('%label' => $label1));
|
||||
$message_update = format_string('%label configuration has been updated.', array('%label' => $label2));
|
||||
$message_delete = format_string('The test configuration %label has been deleted.', array('%label' => $label2));
|
||||
|
||||
// Create a configuration entity.
|
||||
$edit = array(
|
||||
'id' => $id,
|
||||
'label' => $label1,
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/config_test/add', $edit, 'Save');
|
||||
$this->assertUrl('admin/structure/config_test');
|
||||
$this->assertResponse(200);
|
||||
$this->assertRaw($message_insert);
|
||||
$this->assertNoRaw($message_update);
|
||||
$this->assertLinkByHref("admin/structure/config_test/manage/$id");
|
||||
|
||||
// Update the configuration entity.
|
||||
$edit = array(
|
||||
'label' => $label2,
|
||||
);
|
||||
$this->drupalPostForm("admin/structure/config_test/manage/$id", $edit, 'Save');
|
||||
$this->assertUrl('admin/structure/config_test');
|
||||
$this->assertResponse(200);
|
||||
$this->assertNoRaw($message_insert);
|
||||
$this->assertRaw($message_update);
|
||||
$this->assertLinkByHref("admin/structure/config_test/manage/$id");
|
||||
$this->assertLinkByHref("admin/structure/config_test/manage/$id/delete");
|
||||
|
||||
// Delete the configuration entity.
|
||||
$this->drupalGet("admin/structure/config_test/manage/$id");
|
||||
$this->clickLink(t('Delete'));
|
||||
$this->assertUrl("admin/structure/config_test/manage/$id/delete");
|
||||
$this->drupalPostForm(NULL, array(), 'Delete');
|
||||
$this->assertUrl('admin/structure/config_test');
|
||||
$this->assertResponse(200);
|
||||
$this->assertNoRaw($message_update);
|
||||
$this->assertRaw($message_delete);
|
||||
$this->assertNoText($label1);
|
||||
$this->assertNoLinkByHref("admin/structure/config_test/manage/$id");
|
||||
|
||||
// Re-create a configuration entity.
|
||||
$edit = array(
|
||||
'id' => $id,
|
||||
'label' => $label1,
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/config_test/add', $edit, 'Save');
|
||||
$this->assertUrl('admin/structure/config_test');
|
||||
$this->assertResponse(200);
|
||||
$this->assertText($label1);
|
||||
$this->assertLinkByHref("admin/structure/config_test/manage/$id");
|
||||
|
||||
// Rename the configuration entity's ID/machine name.
|
||||
$edit = array(
|
||||
'id' => strtolower($this->randomMachineName()),
|
||||
'label' => $label3,
|
||||
);
|
||||
$this->drupalPostForm("admin/structure/config_test/manage/$id", $edit, 'Save');
|
||||
$this->assertUrl('admin/structure/config_test');
|
||||
$this->assertResponse(200);
|
||||
$this->assertNoText($label1);
|
||||
$this->assertNoText($label2);
|
||||
$this->assertText($label3);
|
||||
$this->assertNoLinkByHref("admin/structure/config_test/manage/$id");
|
||||
$id = $edit['id'];
|
||||
$this->assertLinkByHref("admin/structure/config_test/manage/$id");
|
||||
|
||||
// Create a configuration entity with '0' machine name.
|
||||
$edit = array(
|
||||
'id' => '0',
|
||||
'label' => '0',
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/config_test/add', $edit, 'Save');
|
||||
$this->assertResponse(200);
|
||||
$message_insert = format_string('%label configuration has been created.', array('%label' => $edit['label']));
|
||||
$this->assertRaw($message_insert);
|
||||
$this->assertLinkByHref('admin/structure/config_test/manage/0');
|
||||
$this->assertLinkByHref('admin/structure/config_test/manage/0/delete');
|
||||
$this->drupalPostForm('admin/structure/config_test/manage/0/delete', array(), 'Delete');
|
||||
$this->assertFalse(entity_load('config_test', '0'), 'Test entity deleted');
|
||||
|
||||
// Create a configuration entity with a property that uses AJAX to show
|
||||
// extra form elements.
|
||||
$this->drupalGet('admin/structure/config_test/add');
|
||||
|
||||
// Test that the dependent element is not shown initially.
|
||||
$this->assertFieldByName('size');
|
||||
$this->assertNoFieldByName('size_value');
|
||||
|
||||
$id = strtolower($this->randomMachineName());
|
||||
$edit = [
|
||||
'id' => $id,
|
||||
'label' => $this->randomString(),
|
||||
'size' => 'custom',
|
||||
];
|
||||
$this->drupalPostAjaxForm(NULL, $edit, 'size');
|
||||
|
||||
// Check that the dependent element is shown after selecting a 'size' value.
|
||||
$this->assertFieldByName('size');
|
||||
$this->assertFieldByName('size_value');
|
||||
|
||||
// Test the same scenario but it in a non-JS case by using a 'js-hidden'
|
||||
// submit button.
|
||||
$this->drupalGet('admin/structure/config_test/add');
|
||||
$this->assertFieldByName('size');
|
||||
$this->assertNoFieldByName('size_value');
|
||||
|
||||
$this->drupalPostForm(NULL, $edit, 'Change size');
|
||||
$this->assertFieldByName('size');
|
||||
$this->assertFieldByName('size_value');
|
||||
|
||||
// Submit the form with the regular 'Save' button and check that the entity
|
||||
// values are correct.
|
||||
$edit += ['size_value' => 'medium'];
|
||||
$this->drupalPostForm(NULL, $edit, 'Save');
|
||||
|
||||
$entity = entity_load('config_test', $id);
|
||||
$this->assertEqual($entity->get('size'), 'custom');
|
||||
$this->assertEqual($entity->get('size_value'), 'medium');
|
||||
}
|
||||
|
||||
}
|
117
core/modules/config/src/Tests/ConfigEntityUnitTest.php
Normal file
117
core/modules/config/src/Tests/ConfigEntityUnitTest.php
Normal file
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigEntityUnitTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Unit tests for configuration entity base methods.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigEntityUnitTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Exempt from strict schema checking.
|
||||
*
|
||||
* @see \Drupal\Core\Config\Testing\ConfigSchemaChecker
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $strictConfigSchema = FALSE;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_test');
|
||||
|
||||
/**
|
||||
* The config_test entity storage.
|
||||
*
|
||||
* @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->storage = $this->container->get('entity.manager')->getStorage('config_test');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests storage methods.
|
||||
*/
|
||||
public function testStorageMethods() {
|
||||
$entity_type = \Drupal::entityManager()->getDefinition('config_test');
|
||||
|
||||
// Test the static extractID() method.
|
||||
$expected_id = 'test_id';
|
||||
$config_name = $entity_type->getConfigPrefix() . '.' . $expected_id;
|
||||
$storage = $this->storage;
|
||||
$this->assertIdentical($storage::getIDFromConfigName($config_name, $entity_type->getConfigPrefix()), $expected_id);
|
||||
|
||||
// Create three entities, two with the same style.
|
||||
$style = $this->randomMachineName(8);
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$entity = $this->storage->create(array(
|
||||
'id' => $this->randomMachineName(),
|
||||
'label' => $this->randomString(),
|
||||
'style' => $style,
|
||||
));
|
||||
$entity->save();
|
||||
}
|
||||
$entity = $this->storage->create(array(
|
||||
'id' => $this->randomMachineName(),
|
||||
'label' => $this->randomString(),
|
||||
// Use a different length for the entity to ensure uniqueness.
|
||||
'style' => $this->randomMachineName(9),
|
||||
));
|
||||
$entity->save();
|
||||
|
||||
// Ensure that the configuration entity can be loaded by UUID.
|
||||
$entity_loaded_by_uuid = \Drupal::entityManager()->loadEntityByUuid($entity_type->id(), $entity->uuid());
|
||||
if (!$entity_loaded_by_uuid) {
|
||||
$this->fail(sprintf("Failed to load '%s' entity ID '%s' by UUID '%s'.", $entity_type->id(), $entity->id(), $entity->uuid()));
|
||||
}
|
||||
// Compare UUIDs as the objects are not identical since
|
||||
// $entity->enforceIsNew is FALSE and $entity_loaded_by_uuid->enforceIsNew
|
||||
// is NULL.
|
||||
$this->assertIdentical($entity->uuid(), $entity_loaded_by_uuid->uuid());
|
||||
|
||||
$entities = $this->storage->loadByProperties();
|
||||
$this->assertEqual(count($entities), 3, 'Three entities are loaded when no properties are specified.');
|
||||
|
||||
$entities = $this->storage->loadByProperties(array('style' => $style));
|
||||
$this->assertEqual(count($entities), 2, 'Two entities are loaded when the style property is specified.');
|
||||
|
||||
// Assert that both returned entities have a matching style property.
|
||||
foreach ($entities as $entity) {
|
||||
$this->assertIdentical($entity->get('style'), $style, 'The loaded entity has the correct style value specified.');
|
||||
}
|
||||
|
||||
// Test that schema type enforcement can be overridden by trusting the data.
|
||||
$entity = $this->storage->create(array(
|
||||
'id' => $this->randomMachineName(),
|
||||
'label' => $this->randomString(),
|
||||
'style' => 999
|
||||
));
|
||||
$entity->save();
|
||||
$this->assertIdentical('999', $entity->style);
|
||||
$entity->style = 999;
|
||||
$entity->trustData()->save();
|
||||
$this->assertIdentical(999, $entity->style);
|
||||
$entity->save();
|
||||
$this->assertIdentical('999', $entity->style);
|
||||
}
|
||||
|
||||
}
|
87
core/modules/config/src/Tests/ConfigEventsTest.php
Normal file
87
core/modules/config/src/Tests/ConfigEventsTest.php
Normal file
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigEventsTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Core\Config\Config;
|
||||
use Drupal\Core\Config\ConfigEvents;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests events fired on configuration objects.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigEventsTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_events_test');
|
||||
|
||||
/**
|
||||
* Tests configuration events.
|
||||
*/
|
||||
function testConfigEvents() {
|
||||
$name = 'config_events_test.test';
|
||||
|
||||
$config = new Config($name, \Drupal::service('config.storage'), \Drupal::service('event_dispatcher'), \Drupal::service('config.typed'));
|
||||
$config->set('key', 'initial');
|
||||
\Drupal::state()->get('config_events_test.event', FALSE);
|
||||
$this->assertIdentical(\Drupal::state()->get('config_events_test.event', array()), array(), 'No events fired by creating a new configuration object');
|
||||
$config->save();
|
||||
|
||||
$event = \Drupal::state()->get('config_events_test.event', array());
|
||||
$this->assertIdentical($event['event_name'], ConfigEvents::SAVE);
|
||||
$this->assertIdentical($event['current_config_data'], array('key' => 'initial'));
|
||||
$this->assertIdentical($event['raw_config_data'], array('key' => 'initial'));
|
||||
$this->assertIdentical($event['original_config_data'], array());
|
||||
|
||||
$config->set('key', 'updated')->save();
|
||||
$event = \Drupal::state()->get('config_events_test.event', array());
|
||||
$this->assertIdentical($event['event_name'], ConfigEvents::SAVE);
|
||||
$this->assertIdentical($event['current_config_data'], array('key' => 'updated'));
|
||||
$this->assertIdentical($event['raw_config_data'], array('key' => 'updated'));
|
||||
$this->assertIdentical($event['original_config_data'], array('key' => 'initial'));
|
||||
|
||||
$config->delete();
|
||||
$event = \Drupal::state()->get('config_events_test.event', array());
|
||||
$this->assertIdentical($event['event_name'], ConfigEvents::DELETE);
|
||||
$this->assertIdentical($event['current_config_data'], array());
|
||||
$this->assertIdentical($event['raw_config_data'], array());
|
||||
$this->assertIdentical($event['original_config_data'], array('key' => 'updated'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests configuration rename event that is fired from the ConfigFactory.
|
||||
*/
|
||||
function testConfigRenameEvent() {
|
||||
$name = 'config_events_test.test';
|
||||
$new_name = 'config_events_test.test_rename';
|
||||
$GLOBALS['config'][$name] = array('key' => 'overridden');
|
||||
$GLOBALS['config'][$new_name] = array('key' => 'new overridden');
|
||||
|
||||
$config = $this->config($name);
|
||||
$config->set('key', 'initial')->save();
|
||||
$event = \Drupal::state()->get('config_events_test.event', array());
|
||||
$this->assertIdentical($event['event_name'], ConfigEvents::SAVE);
|
||||
$this->assertIdentical($event['current_config_data'], array('key' => 'initial'));
|
||||
|
||||
// Override applies when getting runtime config.
|
||||
$this->assertEqual($GLOBALS['config'][$name], \Drupal::config($name)->get());
|
||||
|
||||
\Drupal::configFactory()->rename($name, $new_name);
|
||||
$event = \Drupal::state()->get('config_events_test.event', array());
|
||||
$this->assertIdentical($event['event_name'], ConfigEvents::RENAME);
|
||||
$this->assertIdentical($event['current_config_data'], array('key' => 'new overridden'));
|
||||
$this->assertIdentical($event['raw_config_data'], array('key' => 'initial'));
|
||||
$this->assertIdentical($event['original_config_data'], array('key' => 'new overridden'));
|
||||
}
|
||||
|
||||
}
|
323
core/modules/config/src/Tests/ConfigExportImportUITest.php
Normal file
323
core/modules/config/src/Tests/ConfigExportImportUITest.php
Normal file
|
@ -0,0 +1,323 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigExportImportUITest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Archiver\ArchiveTar;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests the user interface for importing/exporting configuration.
|
||||
*
|
||||
* Each testX method does a complete rebuild of a Drupal site, so values being
|
||||
* tested need to be stored in protected properties in order to survive until
|
||||
* the next rebuild.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigExportImportUITest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* The contents of the config export tarball, held between test methods.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $tarball;
|
||||
|
||||
/**
|
||||
* Holds the original 'site slogan' before testing.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $originalSlogan;
|
||||
|
||||
/**
|
||||
* Holds a randomly generated new 'site slogan' for testing.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $newSlogan;
|
||||
|
||||
|
||||
/**
|
||||
* Holds a content type.
|
||||
*
|
||||
* @var \Drupal\node\NodeInterface
|
||||
*/
|
||||
protected $contentType;
|
||||
|
||||
/**
|
||||
* Holds the randomly-generated name of a field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
/**
|
||||
* Holds the field storage entity for $fieldName.
|
||||
*
|
||||
* @var \Drupal\field\FieldStorageConfigInterface
|
||||
*/
|
||||
protected $fieldStorage;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config', 'node', 'field');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// The initial import must be done with uid 1 because if separately named
|
||||
// roles are created then the role is lost after import. If the roles
|
||||
// created have the same name then the sync will fail because they will
|
||||
// have different UUIDs.
|
||||
$this->drupalLogin($this->rootUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a simple site export import case.
|
||||
*/
|
||||
public function testExportImport() {
|
||||
// After installation there is no snapshot and nothing to import.
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
$this->assertNoText(t('Warning message'));
|
||||
$this->assertText(t('There are no configuration changes to import.'));
|
||||
|
||||
$this->originalSlogan = $this->config('system.site')->get('slogan');
|
||||
$this->newSlogan = $this->randomString(16);
|
||||
$this->assertNotEqual($this->newSlogan, $this->originalSlogan);
|
||||
$this->config('system.site')
|
||||
->set('slogan', $this->newSlogan)
|
||||
->save();
|
||||
$this->assertEqual($this->config('system.site')->get('slogan'), $this->newSlogan);
|
||||
|
||||
// Create a content type.
|
||||
$this->contentType = $this->drupalCreateContentType();
|
||||
|
||||
// Create a field.
|
||||
$this->fieldName = Unicode::strtolower($this->randomMachineName());
|
||||
$this->fieldStorage = entity_create('field_storage_config', array(
|
||||
'field_name' => $this->fieldName,
|
||||
'entity_type' => 'node',
|
||||
'type' => 'text',
|
||||
));
|
||||
$this->fieldStorage->save();
|
||||
entity_create('field_config', array(
|
||||
'field_storage' => $this->fieldStorage,
|
||||
'bundle' => $this->contentType->id(),
|
||||
))->save();
|
||||
// Update the displays so that configuration does not change unexpectedly on
|
||||
// import.
|
||||
entity_get_form_display('node', $this->contentType->id(), 'default')
|
||||
->setComponent($this->fieldName, array(
|
||||
'type' => 'text_textfield',
|
||||
))
|
||||
->save();
|
||||
entity_get_display('node', $this->contentType->id(), 'full')
|
||||
->setComponent($this->fieldName)
|
||||
->save();
|
||||
entity_get_display('node', $this->contentType->id(), 'default')
|
||||
->setComponent($this->fieldName)
|
||||
->save();
|
||||
entity_get_display('node', $this->contentType->id(), 'teaser')
|
||||
->removeComponent($this->fieldName)
|
||||
->save();
|
||||
|
||||
$this->drupalGet('node/add/' . $this->contentType->id());
|
||||
$this->assertFieldByName("{$this->fieldName}[0][value]", '', 'Widget is displayed');
|
||||
|
||||
// Export the configuration.
|
||||
$this->drupalPostForm('admin/config/development/configuration/full/export', array(), 'Export');
|
||||
$this->tarball = $this->getRawContent();
|
||||
|
||||
$this->config('system.site')
|
||||
->set('slogan', $this->originalSlogan)
|
||||
->save();
|
||||
$this->assertEqual($this->config('system.site')->get('slogan'), $this->originalSlogan);
|
||||
|
||||
// Delete the custom field.
|
||||
$fields = FieldConfig::loadMultiple();
|
||||
foreach ($fields as $field) {
|
||||
if ($field->getName() == $this->fieldName) {
|
||||
$field->delete();
|
||||
}
|
||||
}
|
||||
$field_storages = FieldStorageConfig::loadMultiple();
|
||||
foreach ($field_storages as $field_storage) {
|
||||
if ($field_storage->getName() == $this->fieldName) {
|
||||
$field_storage->delete();
|
||||
}
|
||||
}
|
||||
$this->drupalGet('node/add/' . $this->contentType->id());
|
||||
$this->assertNoFieldByName("{$this->fieldName}[0][value]", '', 'Widget is not displayed');
|
||||
|
||||
// Import the configuration.
|
||||
$filename = 'temporary://' . $this->randomMachineName();
|
||||
file_put_contents($filename, $this->tarball);
|
||||
$this->drupalPostForm('admin/config/development/configuration/full/import', array('files[import_tarball]' => $filename), 'Upload');
|
||||
// There is no snapshot yet because an import has never run.
|
||||
$this->assertNoText(t('Warning message'));
|
||||
$this->assertNoText(t('There are no configuration changes to import.'));
|
||||
$this->assertText($this->contentType->label());
|
||||
|
||||
$this->drupalPostForm(NULL, array(), 'Import all');
|
||||
// After importing the snapshot has been updated an there are no warnings.
|
||||
$this->assertNoText(t('Warning message'));
|
||||
$this->assertText(t('There are no configuration changes to import.'));
|
||||
|
||||
$this->assertEqual($this->config('system.site')->get('slogan'), $this->newSlogan);
|
||||
|
||||
$this->drupalGet('node/add');
|
||||
$this->assertFieldByName("{$this->fieldName}[0][value]", '', 'Widget is displayed');
|
||||
|
||||
$this->config('system.site')
|
||||
->set('slogan', $this->originalSlogan)
|
||||
->save();
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
$this->assertText(t('Warning message'));
|
||||
$this->assertText('The following items in your active configuration have changes since the last import that may be lost on the next import. system.site');
|
||||
// Remove everything from staging. The warning about differences between the
|
||||
// active and snapshot should no longer exist.
|
||||
\Drupal::service('config.storage.staging')->deleteAll();
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
$this->assertNoText(t('Warning message'));
|
||||
$this->assertNoText('The following items in your active configuration have changes since the last import that may be lost on the next import. system.site');
|
||||
$this->assertText(t('There are no configuration changes to import.'));
|
||||
// Write a file to staging. The warning about differences between the
|
||||
// active and snapshot should now exist.
|
||||
/** @var \Drupal\Core\Config\StorageInterface $staging */
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
$data = $this->config('system.site')->get();
|
||||
$data['slogan'] = 'in the face';
|
||||
$this->copyConfig($this->container->get('config.storage'), $staging);
|
||||
$staging->write('system.site', $data);
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
$this->assertText(t('Warning message'));
|
||||
$this->assertText('The following items in your active configuration have changes since the last import that may be lost on the next import. system.site');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an export and import of collections.
|
||||
*/
|
||||
public function testExportImportCollections() {
|
||||
|
||||
/** @var \Drupal\Core\Config\StorageInterface $active_storage */
|
||||
$active_storage = \Drupal::service('config.storage');
|
||||
$test1_storage = $active_storage->createCollection('collection.test1');
|
||||
$test1_storage->write('config_test.create', array('foo' => 'bar'));
|
||||
$test1_storage->write('config_test.update', array('foo' => 'bar'));
|
||||
$test2_storage = $active_storage->createCollection('collection.test2');
|
||||
$test2_storage->write('config_test.another_create', array('foo' => 'bar'));
|
||||
$test2_storage->write('config_test.another_update', array('foo' => 'bar'));
|
||||
|
||||
// Export the configuration.
|
||||
$this->drupalPostForm('admin/config/development/configuration/full/export', array(), 'Export');
|
||||
$this->tarball = $this->getRawContent();
|
||||
$filename = file_directory_temp() .'/' . $this->randomMachineName();
|
||||
file_put_contents($filename, $this->tarball);
|
||||
|
||||
// Set up the active storage collections to test import.
|
||||
$test1_storage->delete('config_test.create');
|
||||
$test1_storage->write('config_test.update', array('foo' => 'baz'));
|
||||
$test1_storage->write('config_test.delete', array('foo' => 'bar'));
|
||||
$test2_storage->delete('config_test.another_create');
|
||||
$test2_storage->write('config_test.another_update', array('foo' => 'baz'));
|
||||
$test2_storage->write('config_test.another_delete', array('foo' => 'bar'));
|
||||
|
||||
// Create a snapshot.
|
||||
$snapshot_storage = \Drupal::service('config.storage.snapshot');
|
||||
\Drupal::service('config.manager')->createSnapshot($active_storage, $snapshot_storage);
|
||||
|
||||
// Ensure that the snapshot has the expected collection data before import.
|
||||
$test1_snapshot = $snapshot_storage->createCollection('collection.test1');
|
||||
$data = $test1_snapshot->read('config_test.delete');
|
||||
$this->assertEqual($data, array('foo' => 'bar'), 'The config_test.delete in collection.test1 exists in the snapshot storage.');
|
||||
$data = $test1_snapshot->read('config_test.update');
|
||||
$this->assertEqual($data, array('foo' => 'baz'), 'The config_test.update in collection.test1 exists in the snapshot storage.');
|
||||
$this->assertFalse($test1_snapshot->read('config_test.create'), 'The config_test.create in collection.test1 does not exist in the snapshot storage.');
|
||||
$test2_snapshot = $snapshot_storage->createCollection('collection.test2');
|
||||
$data = $test2_snapshot->read('config_test.another_delete');
|
||||
$this->assertEqual($data, array('foo' => 'bar'), 'The config_test.another_delete in collection.test2 exists in the snapshot storage.');
|
||||
$data = $test2_snapshot->read('config_test.another_update');
|
||||
$this->assertEqual($data, array('foo' => 'baz'), 'The config_test.another_update in collection.test2 exists in the snapshot storage.');
|
||||
$this->assertFalse($test2_snapshot->read('config_test.another_create'), 'The config_test.another_create in collection.test2 does not exist in the snapshot storage.');
|
||||
|
||||
// Create the tar that contains the expected content for the collections.
|
||||
$tar = new ArchiveTar($filename, 'gz');
|
||||
$content_list = $tar->listContent();
|
||||
// Convert the list of files into something easy to search.
|
||||
$files = array();
|
||||
foreach ($content_list as $file) {
|
||||
$files[] = $file['filename'];
|
||||
}
|
||||
$this->assertTrue(in_array('collection/test1/config_test.create.yml', $files), 'Config export contains collection/test1/config_test.create.yml.');
|
||||
$this->assertTrue(in_array('collection/test2/config_test.another_create.yml', $files), 'Config export contains collection/test2/config_test.another_create.yml.');
|
||||
$this->assertTrue(in_array('collection/test1/config_test.update.yml', $files), 'Config export contains collection/test1/config_test.update.yml.');
|
||||
$this->assertTrue(in_array('collection/test2/config_test.another_update.yml', $files), 'Config export contains collection/test2/config_test.another_update.yml.');
|
||||
$this->assertFalse(in_array('collection/test1/config_test.delete.yml', $files), 'Config export does not contain collection/test1/config_test.delete.yml.');
|
||||
$this->assertFalse(in_array('collection/test2/config_test.another_delete.yml', $files), 'Config export does not contain collection/test2/config_test.another_delete.yml.');
|
||||
|
||||
$this->drupalPostForm('admin/config/development/configuration/full/import', array('files[import_tarball]' => $filename), 'Upload');
|
||||
// Verify that there are configuration differences to import.
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
$this->assertNoText(t('There are no configuration changes to import.'));
|
||||
$this->assertText(t('!collection configuration collection', array('!collection' => 'collection.test1')));
|
||||
$this->assertText(t('!collection configuration collection', array('!collection' => 'collection.test2')));
|
||||
$this->assertText('config_test.create');
|
||||
$this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test1/config_test.create');
|
||||
$this->assertText('config_test.update');
|
||||
$this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test1/config_test.update');
|
||||
$this->assertText('config_test.delete');
|
||||
$this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test1/config_test.delete');
|
||||
$this->assertText('config_test.another_create');
|
||||
$this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test2/config_test.another_create');
|
||||
$this->assertText('config_test.another_update');
|
||||
$this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test2/config_test.another_update');
|
||||
$this->assertText('config_test.another_delete');
|
||||
$this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test2/config_test.another_delete');
|
||||
|
||||
$this->drupalPostForm(NULL, array(), 'Import all');
|
||||
$this->assertText(t('There are no configuration changes to import.'));
|
||||
|
||||
// Test data in collections.
|
||||
$data = $test1_storage->read('config_test.create');
|
||||
$this->assertEqual($data, array('foo' => 'bar'), 'The config_test.create in collection.test1 has been created.');
|
||||
$data = $test1_storage->read('config_test.update');
|
||||
$this->assertEqual($data, array('foo' => 'bar'), 'The config_test.update in collection.test1 has been updated.');
|
||||
$this->assertFalse($test1_storage->read('config_test.delete'), 'The config_test.delete in collection.test1 has been deleted.');
|
||||
|
||||
$data = $test2_storage->read('config_test.another_create');
|
||||
$this->assertEqual($data, array('foo' => 'bar'), 'The config_test.another_create in collection.test2 has been created.');
|
||||
$data = $test2_storage->read('config_test.another_update');
|
||||
$this->assertEqual($data, array('foo' => 'bar'), 'The config_test.another_update in collection.test2 has been updated.');
|
||||
$this->assertFalse($test2_storage->read('config_test.another_delete'), 'The config_test.another_delete in collection.test2 has been deleted.');
|
||||
|
||||
// Ensure that the snapshot has been updated with the collection data.
|
||||
$snapshot_storage = \Drupal::service('config.storage.snapshot');
|
||||
$test1_snapshot = $snapshot_storage->createCollection('collection.test1');
|
||||
$data = $test1_snapshot->read('config_test.create');
|
||||
$this->assertEqual($data, array('foo' => 'bar'), 'The config_test.create in collection.test1 has been created in the snapshot storage.');
|
||||
$data = $test1_snapshot->read('config_test.update');
|
||||
$this->assertEqual($data, array('foo' => 'bar'), 'The config_test.update in collection.test1 has been updated in the snapshot storage.');
|
||||
$this->assertFalse($test1_snapshot->read('config_test.delete'), 'The config_test.delete in collection.test1 does not exist in the snapshot storage.');
|
||||
$test2_snapshot = $snapshot_storage->createCollection('collection.test2');
|
||||
$data = $test2_snapshot->read('config_test.another_create');
|
||||
$this->assertEqual($data, array('foo' => 'bar'), 'The config_test.another_create in collection.test2 has been created in the snapshot storage.');
|
||||
$data = $test2_snapshot->read('config_test.another_update');
|
||||
$this->assertEqual($data, array('foo' => 'bar'), 'The config_test.another_update in collection.test2 has been updated in the snapshot storage.');
|
||||
$this->assertFalse($test2_snapshot->read('config_test.another_delete'), 'The config_test.another_delete in collection.test2 does not exist in the snapshot storage.');
|
||||
}
|
||||
|
||||
}
|
80
core/modules/config/src/Tests/ConfigExportUITest.php
Normal file
80
core/modules/config/src/Tests/ConfigExportUITest.php
Normal file
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigExportUITest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Component\Serialization\Yaml;
|
||||
use Drupal\Core\Archiver\Tar;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests the user interface for exporting configuration.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigExportUITest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config', 'config_test', 'config_export_test');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser(array('export configuration')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests export of configuration.
|
||||
*/
|
||||
function testExport() {
|
||||
// Verify the export page with export submit button is available.
|
||||
$this->drupalGet('admin/config/development/configuration/full/export');
|
||||
$this->assertFieldById('edit-submit', t('Export'));
|
||||
|
||||
// Submit the export form and verify response.
|
||||
$this->drupalPostForm('admin/config/development/configuration/full/export', array(), t('Export'));
|
||||
$this->assertResponse(200, 'User can access the download callback.');
|
||||
|
||||
// Get the archived binary file provided to user for download.
|
||||
$archive_data = $this->getRawContent();
|
||||
|
||||
// Temporarily save the archive file.
|
||||
$uri = file_unmanaged_save_data($archive_data, 'temporary://config.tar.gz');
|
||||
|
||||
// Extract the archive and verify it's not empty.
|
||||
$file_path = file_directory_temp() . '/' . file_uri_target($uri);
|
||||
$archiver = new Tar($file_path);
|
||||
$archive_contents = $archiver->listContents();
|
||||
$this->assert(!empty($archive_contents), 'Downloaded archive file is not empty.');
|
||||
|
||||
// Prepare the list of config files from active storage, see
|
||||
// \Drupal\config\Controller\ConfigController::downloadExport().
|
||||
$storage_active = $this->container->get('config.storage');
|
||||
$config_files = array();
|
||||
foreach ($storage_active->listAll() as $config_name) {
|
||||
$config_files[] = $config_name . '.yml';
|
||||
}
|
||||
// Assert that the downloaded archive file contents are the same as the test
|
||||
// site active store.
|
||||
$this->assertIdentical($archive_contents, $config_files);
|
||||
|
||||
// Ensure the test configuration override is in effect but was not exported.
|
||||
$this->assertIdentical(\Drupal::config('system.maintenance')->get('message'), 'Foo');
|
||||
$archiver->extract(file_directory_temp(), array('system.maintenance.yml'));
|
||||
$file_contents = file_get_contents(file_directory_temp() . '/' . 'system.maintenance.yml');
|
||||
$exported = Yaml::decode($file_contents);
|
||||
$this->assertNotIdentical($exported['message'], 'Foo');
|
||||
}
|
||||
|
||||
}
|
235
core/modules/config/src/Tests/ConfigFileContentTest.php
Normal file
235
core/modules/config/src/Tests/ConfigFileContentTest.php
Normal file
|
@ -0,0 +1,235 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigFileContentTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Core\Config\FileStorage;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests reading and writing of configuration files.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigFileContentTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Exempt from strict schema checking.
|
||||
*
|
||||
* @see \Drupal\Core\Config\Testing\ConfigSchemaChecker
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $strictConfigSchema = FALSE;
|
||||
|
||||
/**
|
||||
* Tests setting, writing, and reading of a configuration setting.
|
||||
*/
|
||||
function testReadWriteConfig() {
|
||||
$storage = $this->container->get('config.storage');
|
||||
|
||||
$name = 'foo.bar';
|
||||
$key = 'foo';
|
||||
$value = 'bar';
|
||||
$nested_key = 'biff.bang';
|
||||
$nested_value = 'pow';
|
||||
$array_key = 'array';
|
||||
$array_value = array(
|
||||
'foo' => 'bar',
|
||||
'biff' => array(
|
||||
'bang' => 'pow',
|
||||
),
|
||||
);
|
||||
$casting_array_key = 'casting_array';
|
||||
$casting_array_false_value_key = 'casting_array.cast.false';
|
||||
$casting_array_value = array(
|
||||
'cast' => array(
|
||||
'false' => FALSE,
|
||||
),
|
||||
);
|
||||
$nested_array_key = 'nested.array';
|
||||
$true_key = 'true';
|
||||
$false_key = 'false';
|
||||
|
||||
// Attempt to read non-existing configuration.
|
||||
$config = $this->config($name);
|
||||
|
||||
// Verify a configuration object is returned.
|
||||
$this->assertEqual($config->getName(), $name);
|
||||
$this->assertTrue($config, 'Config object created.');
|
||||
|
||||
// Verify the configuration object is empty.
|
||||
$this->assertEqual($config->get(), array(), 'New config object is empty.');
|
||||
|
||||
// Verify nothing was saved.
|
||||
$data = $storage->read($name);
|
||||
$this->assertIdentical($data, FALSE);
|
||||
|
||||
// Add a top level value.
|
||||
$config = $this->config($name);
|
||||
$config->set($key, $value);
|
||||
|
||||
// Add a nested value.
|
||||
$config->set($nested_key, $nested_value);
|
||||
|
||||
// Add an array.
|
||||
$config->set($array_key, $array_value);
|
||||
|
||||
// Add a nested array.
|
||||
$config->set($nested_array_key, $array_value);
|
||||
|
||||
// Add a boolean false value. Should get cast to 0.
|
||||
$config->set($false_key, FALSE);
|
||||
|
||||
// Add a boolean true value. Should get cast to 1.
|
||||
$config->set($true_key, TRUE);
|
||||
|
||||
// Add a null value. Should get cast to an empty string.
|
||||
$config->set('null', NULL);
|
||||
|
||||
// Add an array with a nested boolean false that should get cast to 0.
|
||||
$config->set($casting_array_key, $casting_array_value);
|
||||
$config->save();
|
||||
|
||||
// Verify the database entry exists.
|
||||
$data = $storage->read($name);
|
||||
$this->assertTrue($data);
|
||||
|
||||
// Read top level value.
|
||||
$config = $this->config($name);
|
||||
$this->assertEqual($config->getName(), $name);
|
||||
$this->assertTrue($config, 'Config object created.');
|
||||
$this->assertEqual($config->get($key), 'bar', 'Top level configuration value found.');
|
||||
|
||||
// Read nested value.
|
||||
$this->assertEqual($config->get($nested_key), $nested_value, 'Nested configuration value found.');
|
||||
|
||||
// Read array.
|
||||
$this->assertEqual($config->get($array_key), $array_value, 'Top level array configuration value found.');
|
||||
|
||||
// Read nested array.
|
||||
$this->assertEqual($config->get($nested_array_key), $array_value, 'Nested array configuration value found.');
|
||||
|
||||
// Read a top level value that doesn't exist.
|
||||
$this->assertNull($config->get('i_dont_exist'), 'Non-existent top level value returned NULL.');
|
||||
|
||||
// Read a nested value that doesn't exist.
|
||||
$this->assertNull($config->get('i.dont.exist'), 'Non-existent nested value returned NULL.');
|
||||
|
||||
// Read false value.
|
||||
$this->assertEqual($config->get($false_key), '0', format_string("Boolean FALSE value returned the string '0'."));
|
||||
|
||||
// Read true value.
|
||||
$this->assertEqual($config->get($true_key), '1', format_string("Boolean TRUE value returned the string '1'."));
|
||||
|
||||
// Read null value.
|
||||
$this->assertIdentical($config->get('null'), NULL);
|
||||
|
||||
// Read false that had been nested in an array value.
|
||||
$this->assertEqual($config->get($casting_array_false_value_key), '0', format_string("Nested boolean FALSE value returned the string '0'."));
|
||||
|
||||
// Unset a top level value.
|
||||
$config->clear($key);
|
||||
|
||||
// Unset a nested value.
|
||||
$config->clear($nested_key);
|
||||
$config->save();
|
||||
$config = $this->config($name);
|
||||
|
||||
// Read unset top level value.
|
||||
$this->assertNull($config->get($key), 'Top level value unset.');
|
||||
|
||||
// Read unset nested value.
|
||||
$this->assertNull($config->get($nested_key), 'Nested value unset.');
|
||||
|
||||
// Create two new configuration files to test listing.
|
||||
$config = $this->config('foo.baz');
|
||||
$config->set($key, $value);
|
||||
$config->save();
|
||||
|
||||
// Test chained set()->save().
|
||||
$chained_name = 'biff.bang';
|
||||
$config = $this->config($chained_name);
|
||||
$config->set($key, $value)->save();
|
||||
|
||||
// Verify the database entry exists from a chained save.
|
||||
$data = $storage->read($chained_name);
|
||||
$this->assertEqual($data, $config->get());
|
||||
|
||||
// Get file listing for all files starting with 'foo'. Should return
|
||||
// two elements.
|
||||
$files = $storage->listAll('foo');
|
||||
$this->assertEqual(count($files), 2, 'Two files listed with the prefix \'foo\'.');
|
||||
|
||||
// Get file listing for all files starting with 'biff'. Should return
|
||||
// one element.
|
||||
$files = $storage->listAll('biff');
|
||||
$this->assertEqual(count($files), 1, 'One file listed with the prefix \'biff\'.');
|
||||
|
||||
// Get file listing for all files starting with 'foo.bar'. Should return
|
||||
// one element.
|
||||
$files = $storage->listAll('foo.bar');
|
||||
$this->assertEqual(count($files), 1, 'One file listed with the prefix \'foo.bar\'.');
|
||||
|
||||
// Get file listing for all files starting with 'bar'. Should return
|
||||
// an empty array.
|
||||
$files = $storage->listAll('bar');
|
||||
$this->assertEqual($files, array(), 'No files listed with the prefix \'bar\'.');
|
||||
|
||||
// Delete the configuration.
|
||||
$config = $this->config($name);
|
||||
$config->delete();
|
||||
|
||||
// Verify the database entry no longer exists.
|
||||
$data = $storage->read($name);
|
||||
$this->assertIdentical($data, FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests serialization of configuration to file.
|
||||
*/
|
||||
function testSerialization() {
|
||||
$name = $this->randomMachineName(10) . '.' . $this->randomMachineName(10);
|
||||
$config_data = array(
|
||||
// Indexed arrays; the order of elements is essential.
|
||||
'numeric keys' => array('i', 'n', 'd', 'e', 'x', 'e', 'd'),
|
||||
// Infinitely nested keys using arbitrary element names.
|
||||
'nested keys' => array(
|
||||
// HTML/XML in values.
|
||||
'HTML' => '<strong> <bold> <em> <blockquote>',
|
||||
// UTF-8 in values.
|
||||
'UTF-8' => 'FrançAIS is ÜBER-åwesome',
|
||||
// Unicode in keys and values.
|
||||
'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΣὨ' => 'αβγδεζηθικλμνξοσὠ',
|
||||
),
|
||||
'invalid xml' => '</title><script type="text/javascript">alert("Title XSS!");</script> & < > " \' ',
|
||||
);
|
||||
|
||||
// Encode and write, and reload and decode the configuration data.
|
||||
$filestorage = new FileStorage($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]);
|
||||
$filestorage->write($name, $config_data);
|
||||
$config_parsed = $filestorage->read($name);
|
||||
|
||||
$key = 'numeric keys';
|
||||
$this->assertIdentical($config_data[$key], $config_parsed[$key]);
|
||||
|
||||
$key = 'nested keys';
|
||||
$this->assertIdentical($config_data[$key], $config_parsed[$key]);
|
||||
|
||||
$key = 'HTML';
|
||||
$this->assertIdentical($config_data['nested keys'][$key], $config_parsed['nested keys'][$key]);
|
||||
|
||||
$key = 'UTF-8';
|
||||
$this->assertIdentical($config_data['nested keys'][$key], $config_parsed['nested keys'][$key]);
|
||||
|
||||
$key = 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΣὨ';
|
||||
$this->assertIdentical($config_data['nested keys'][$key], $config_parsed['nested keys'][$key]);
|
||||
|
||||
$key = 'invalid xml';
|
||||
$this->assertIdentical($config_data[$key], $config_parsed[$key]);
|
||||
}
|
||||
}
|
52
core/modules/config/src/Tests/ConfigFormOverrideTest.php
Normal file
52
core/modules/config/src/Tests/ConfigFormOverrideTest.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigFormOverrideTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests config overrides do not appear on forms that extend ConfigFormBase.
|
||||
*
|
||||
* @group config
|
||||
* @see \Drupal\Core\Form\ConfigFormBase
|
||||
*/
|
||||
class ConfigFormOverrideTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Tests that overrides do not affect forms.
|
||||
*/
|
||||
public function testFormsWithOverrides() {
|
||||
$this->drupalLogin($this->drupalCreateUser(array('access administration pages', 'administer site configuration')));
|
||||
|
||||
$overridden_name = 'Site name global conf override';
|
||||
|
||||
// Set up an override.
|
||||
$settings['config']['system.site']['name'] = (object) array(
|
||||
'value' => $overridden_name,
|
||||
'required' => TRUE,
|
||||
);
|
||||
$this->writeSettings($settings);
|
||||
|
||||
// Test that everything on the form is the same, but that the override
|
||||
// worked for the actual site name.
|
||||
$this->drupalGet('admin/config/system/site-information');
|
||||
$this->assertTitle('Site information | ' . $overridden_name);
|
||||
$elements = $this->xpath('//input[@name="site_name"]');
|
||||
$this->assertIdentical((string) $elements[0]['value'], 'Drupal');
|
||||
|
||||
// Submit the form and ensure the site name is not changed.
|
||||
$edit = array(
|
||||
'site_name' => 'Custom site name',
|
||||
);
|
||||
$this->drupalPostForm('admin/config/system/site-information', $edit, t('Save configuration'));
|
||||
$this->assertTitle('Site information | ' . $overridden_name);
|
||||
$elements = $this->xpath('//input[@name="site_name"]');
|
||||
$this->assertIdentical((string) $elements[0]['value'], $edit['site_name']);
|
||||
}
|
||||
|
||||
}
|
165
core/modules/config/src/Tests/ConfigImportAllTest.php
Normal file
165
core/modules/config/src/Tests/ConfigImportAllTest.php
Normal file
|
@ -0,0 +1,165 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigImportAllTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Core\Config\StorageComparer;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\system\Tests\Module\ModuleTestBase;
|
||||
use Drupal\shortcut\Entity\Shortcut;
|
||||
use Drupal\taxonomy\Entity\Term;
|
||||
|
||||
/**
|
||||
* Tests the largest configuration import possible with all available modules.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigImportAllTest extends ModuleTestBase {
|
||||
|
||||
use SchemaCheckTestTrait;
|
||||
|
||||
/**
|
||||
* A user with the 'synchronize configuration' permission.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
/**
|
||||
* The profile to install as a basis for testing.
|
||||
*
|
||||
* Using the standard profile as this has a lot of additional configuration.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $profile = 'standard';
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->webUser = $this->drupalCreateUser(array('synchronize configuration'));
|
||||
$this->drupalLogin($this->webUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a fixed set of modules can be installed and uninstalled.
|
||||
*/
|
||||
public function testInstallUninstall() {
|
||||
|
||||
// Get a list of modules to enable.
|
||||
$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;
|
||||
});
|
||||
|
||||
// Install every module possible.
|
||||
\Drupal::service('module_installer')->install(array_keys($all_modules));
|
||||
|
||||
$this->assertModules(array_keys($all_modules), TRUE);
|
||||
foreach($all_modules as $module => $info) {
|
||||
$this->assertModuleConfig($module);
|
||||
$this->assertModuleTablesExist($module);
|
||||
}
|
||||
|
||||
// Export active config to staging
|
||||
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.staging'));
|
||||
|
||||
system_list_reset();
|
||||
$this->resetAll();
|
||||
|
||||
// Delete every field on the site so all modules can be uninstalled. For
|
||||
// example, if a comment field exists then module becomes required and can
|
||||
// not be uninstalled.
|
||||
|
||||
$field_storages = \Drupal::entityManager()->getStorage('field_storage_config')->loadMultiple();
|
||||
\Drupal::entityManager()->getStorage('field_storage_config')->delete($field_storages);
|
||||
// Purge the data.
|
||||
field_purge_batch(1000);
|
||||
|
||||
// Delete all terms.
|
||||
$terms = Term::loadMultiple();
|
||||
entity_delete_multiple('taxonomy_term', array_keys($terms));
|
||||
|
||||
// Delete all filter formats.
|
||||
$filters = FilterFormat::loadMultiple();
|
||||
entity_delete_multiple('filter_format', array_keys($filters));
|
||||
|
||||
// Delete any shortcuts so the shortcut module can be uninstalled.
|
||||
$shortcuts = Shortcut::loadMultiple();
|
||||
entity_delete_multiple('shortcut', array_keys($shortcuts));
|
||||
|
||||
system_list_reset();
|
||||
$all_modules = system_rebuild_module_data();
|
||||
|
||||
// Ensure that only core required modules and the install profile can not be uninstalled.
|
||||
$validation_reasons = \Drupal::service('module_installer')->validateUninstall(array_keys($all_modules));
|
||||
$this->assertEqual(['standard', 'system', 'user'], array_keys($validation_reasons));
|
||||
|
||||
$modules_to_uninstall = array_filter($all_modules, function ($module) use ($validation_reasons) {
|
||||
// Filter required and not enabled modules.
|
||||
if (!empty($module->info['required']) || $module->status == FALSE) {
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
});
|
||||
|
||||
// Can not uninstall config and use admin/config/development/configuration!
|
||||
unset($modules_to_uninstall['config']);
|
||||
|
||||
$this->assertTrue(isset($modules_to_uninstall['comment']), 'The comment module will be disabled');
|
||||
$this->assertTrue(isset($modules_to_uninstall['file']), 'The File module will be disabled');
|
||||
$this->assertTrue(isset($modules_to_uninstall['editor']), 'The Editor module will be disabled');
|
||||
|
||||
// Uninstall all modules that can be uninstalled.
|
||||
\Drupal::service('module_installer')->uninstall(array_keys($modules_to_uninstall));
|
||||
|
||||
$this->assertModules(array_keys($modules_to_uninstall), FALSE);
|
||||
foreach($modules_to_uninstall as $module => $info) {
|
||||
$this->assertNoModuleConfig($module);
|
||||
$this->assertModuleTablesDoNotExist($module);
|
||||
}
|
||||
|
||||
// Import the configuration thereby re-installing all the modules.
|
||||
$this->drupalPostForm('admin/config/development/configuration', array(), t('Import all'));
|
||||
// Modules have been installed that have services.
|
||||
$this->rebuildContainer();
|
||||
|
||||
// Check that there are no errors.
|
||||
$this->assertIdentical($this->configImporter()->getErrors(), array());
|
||||
|
||||
// Check that all modules that were uninstalled are now reinstalled.
|
||||
$this->assertModules(array_keys($modules_to_uninstall), TRUE);
|
||||
foreach($modules_to_uninstall as $module => $info) {
|
||||
$this->assertModuleConfig($module);
|
||||
$this->assertModuleTablesExist($module);
|
||||
}
|
||||
|
||||
// Ensure that we have no configuration changes to import.
|
||||
$storage_comparer = new StorageComparer(
|
||||
$this->container->get('config.storage.staging'),
|
||||
$this->container->get('config.storage'),
|
||||
$this->container->get('config.manager')
|
||||
);
|
||||
$this->assertIdentical($storage_comparer->createChangelist()->getChangelist(), $storage_comparer->getEmptyChangelist());
|
||||
|
||||
// Now we have all configuration imported, test all of them for schema
|
||||
// conformance. Ensures all imported default configuration is valid when
|
||||
// all modules are enabled.
|
||||
$names = $this->container->get('config.storage')->listAll();
|
||||
/** @var \Drupal\Core\Config\TypedConfigManagerInterface $typed_config */
|
||||
$typed_config = $this->container->get('config.typed');
|
||||
foreach ($names as $name) {
|
||||
$config = $this->config($name);
|
||||
$this->assertConfigSchema($typed_config, $name, $config->get());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigImportInstallProfileTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests the importing/exporting configuration based on the install profile.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigImportInstallProfileTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* The profile to install as a basis for testing.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $profile = 'testing_config_import';
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config');
|
||||
|
||||
/**
|
||||
* A user with the 'synchronize configuration' permission.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->webUser = $this->drupalCreateUser(array('synchronize configuration'));
|
||||
$this->drupalLogin($this->webUser);
|
||||
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.staging'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests config importer cannot uninstall install profiles.
|
||||
*
|
||||
* @see \Drupal\Core\EventSubscriber\ConfigImportSubscriber
|
||||
*/
|
||||
public function testInstallProfileValidation() {
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
$this->copyConfig($this->container->get('config.storage'), $staging);
|
||||
$core = $staging->read('core.extension');
|
||||
|
||||
// Ensure install profiles can not be uninstalled.
|
||||
unset($core['module']['testing_config_import']);
|
||||
$staging->write('core.extension', $core);
|
||||
|
||||
$this->drupalPostForm('admin/config/development/configuration', array(), t('Import all'));
|
||||
$this->assertText('The configuration cannot be imported because it failed validation for the following reasons:');
|
||||
$this->assertText('Unable to uninstall the Testing config import profile since it is the install profile.');
|
||||
|
||||
// Uninstall dependencies of testing_config_import.
|
||||
$core['module']['testing_config_import'] = 0;
|
||||
unset($core['module']['syslog']);
|
||||
unset($core['theme']['stark']);
|
||||
$core['theme']['classy'] = 0;
|
||||
$staging->write('core.extension', $core);
|
||||
$staging->deleteAll('syslog.');
|
||||
$theme = $staging->read('system.theme');
|
||||
$theme['default'] = 'classy';
|
||||
$staging->write('system.theme', $theme);
|
||||
$this->drupalPostForm('admin/config/development/configuration', array(), t('Import all'));
|
||||
$this->assertText('The configuration was imported successfully.');
|
||||
$this->rebuildContainer();
|
||||
$this->assertFalse(\Drupal::moduleHandler()->moduleExists('syslog'), 'The syslog module has been uninstalled.');
|
||||
$this->assertFalse(\Drupal::service('theme_handler')->themeExists('stark'), 'The stark theme has been uninstalled.');
|
||||
$this->assertTrue(\Drupal::service('theme_handler')->themeExists('classy'), 'The classy theme has been installed.');
|
||||
}
|
||||
|
||||
}
|
110
core/modules/config/src/Tests/ConfigImportRecreateTest.php
Normal file
110
core/modules/config/src/Tests/ConfigImportRecreateTest.php
Normal file
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigImportRecreateTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Config\ConfigImporter;
|
||||
use Drupal\Core\Config\StorageComparer;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests importing recreated configuration entities.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigImportRecreateTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Config Importer object used for testing.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigImporter
|
||||
*/
|
||||
protected $configImporter;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system', 'field', 'text', 'user', 'node', 'entity_reference');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('node');
|
||||
$this->installConfig(array('field', 'node'));
|
||||
|
||||
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.staging'));
|
||||
|
||||
// Set up the ConfigImporter object for testing.
|
||||
$storage_comparer = new StorageComparer(
|
||||
$this->container->get('config.storage.staging'),
|
||||
$this->container->get('config.storage'),
|
||||
$this->container->get('config.manager')
|
||||
);
|
||||
$this->configImporter = new ConfigImporter(
|
||||
$storage_comparer->createChangelist(),
|
||||
$this->container->get('event_dispatcher'),
|
||||
$this->container->get('config.manager'),
|
||||
$this->container->get('lock'),
|
||||
$this->container->get('config.typed'),
|
||||
$this->container->get('module_handler'),
|
||||
$this->container->get('module_installer'),
|
||||
$this->container->get('theme_handler'),
|
||||
$this->container->get('string_translation')
|
||||
);
|
||||
}
|
||||
|
||||
public function testRecreateEntity() {
|
||||
$type_name = Unicode::strtolower($this->randomMachineName(16));
|
||||
$content_type = entity_create('node_type', array(
|
||||
'type' => $type_name,
|
||||
'name' => 'Node type one',
|
||||
));
|
||||
$content_type->save();
|
||||
node_add_body_field($content_type);
|
||||
/** @var \Drupal\Core\Config\StorageInterface $active */
|
||||
$active = $this->container->get('config.storage');
|
||||
/** @var \Drupal\Core\Config\StorageInterface $staging */
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
|
||||
$config_name = $content_type->getEntityType()->getConfigPrefix() . '.' . $content_type->id();
|
||||
$this->copyConfig($active, $staging);
|
||||
|
||||
// Delete the content type. This will also delete a field storage, a field,
|
||||
// an entity view display and an entity form display.
|
||||
$content_type->delete();
|
||||
$this->assertFalse($active->exists($config_name), 'Content type\'s old name does not exist active store.');
|
||||
// Recreate with the same type - this will have a different UUID.
|
||||
$content_type = entity_create('node_type', array(
|
||||
'type' => $type_name,
|
||||
'name' => 'Node type two',
|
||||
));
|
||||
$content_type->save();
|
||||
node_add_body_field($content_type);
|
||||
|
||||
$this->configImporter->reset();
|
||||
// A node type, a field, an entity view display and an entity form display
|
||||
// will be recreated.
|
||||
$creates = $this->configImporter->getUnprocessedConfiguration('create');
|
||||
$deletes = $this->configImporter->getUnprocessedConfiguration('delete');
|
||||
$this->assertEqual(5, count($creates), 'There are 5 configuration items to create.');
|
||||
$this->assertEqual(5, count($deletes), 'There are 5 configuration items to delete.');
|
||||
$this->assertEqual(0, count($this->configImporter->getUnprocessedConfiguration('update')), 'There are no configuration items to update.');
|
||||
$this->assertIdentical($creates, array_reverse($deletes), 'Deletes and creates contain the same configuration names in opposite orders due to dependencies.');
|
||||
|
||||
$this->configImporter->import();
|
||||
|
||||
// Verify that there is nothing more to import.
|
||||
$this->assertFalse($this->configImporter->reset()->hasUnprocessedConfigurationChanges());
|
||||
$content_type = NodeType::load($type_name);
|
||||
$this->assertEqual('Node type one', $content_type->label());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigImportRenameValidationTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Component\Uuid\Php;
|
||||
use Drupal\Core\Config\ConfigImporter;
|
||||
use Drupal\Core\Config\ConfigImporterException;
|
||||
use Drupal\Core\Config\StorageComparer;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests validating renamed configuration in a configuration import.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigImportRenameValidationTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Config Importer object used for testing.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigImporter
|
||||
*/
|
||||
protected $configImporter;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system', 'user', 'node', 'field', 'text', 'config_test', 'entity_reference');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('node');
|
||||
$this->installConfig(array('field'));
|
||||
|
||||
// Set up the ConfigImporter object for testing.
|
||||
$storage_comparer = new StorageComparer(
|
||||
$this->container->get('config.storage.staging'),
|
||||
$this->container->get('config.storage'),
|
||||
$this->container->get('config.manager')
|
||||
);
|
||||
$this->configImporter = new ConfigImporter(
|
||||
$storage_comparer->createChangelist(),
|
||||
$this->container->get('event_dispatcher'),
|
||||
$this->container->get('config.manager'),
|
||||
$this->container->get('lock.persistent'),
|
||||
$this->container->get('config.typed'),
|
||||
$this->container->get('module_handler'),
|
||||
$this->container->get('module_installer'),
|
||||
$this->container->get('theme_handler'),
|
||||
$this->container->get('string_translation')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests configuration renaming validation.
|
||||
*/
|
||||
public function testRenameValidation() {
|
||||
// Create a test entity.
|
||||
$test_entity_id = $this->randomMachineName();
|
||||
$test_entity = entity_create('config_test', array(
|
||||
'id' => $test_entity_id,
|
||||
'label' => $this->randomMachineName(),
|
||||
));
|
||||
$test_entity->save();
|
||||
$uuid = $test_entity->uuid();
|
||||
|
||||
// Stage the test entity and then delete it from the active storage.
|
||||
$active = $this->container->get('config.storage');
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
$this->copyConfig($active, $staging);
|
||||
$test_entity->delete();
|
||||
|
||||
// Create a content type with a matching UUID in the active storage.
|
||||
$content_type = entity_create('node_type', array(
|
||||
'type' => Unicode::strtolower($this->randomMachineName(16)),
|
||||
'name' => $this->randomMachineName(),
|
||||
'uuid' => $uuid,
|
||||
));
|
||||
$content_type->save();
|
||||
|
||||
// Confirm that the staged configuration is detected as a rename since the
|
||||
// UUIDs match.
|
||||
$this->configImporter->reset();
|
||||
$expected = array(
|
||||
'node.type.' . $content_type->id() . '::config_test.dynamic.' . $test_entity_id,
|
||||
);
|
||||
$renames = $this->configImporter->getUnprocessedConfiguration('rename');
|
||||
$this->assertIdentical($expected, $renames);
|
||||
|
||||
// Try to import the configuration. We expect an exception to be thrown
|
||||
// because the staged entity is of a different type.
|
||||
try {
|
||||
$this->configImporter->import();
|
||||
$this->fail('Expected ConfigImporterException thrown when a renamed configuration entity does not match the existing entity type.');
|
||||
}
|
||||
catch (ConfigImporterException $e) {
|
||||
$this->pass('Expected ConfigImporterException thrown when a renamed configuration entity does not match the existing entity type.');
|
||||
$expected = array(
|
||||
SafeMarkup::format('Entity type mismatch on rename. !old_type not equal to !new_type for existing configuration !old_name and staged configuration !new_name.', array('old_type' => 'node_type', 'new_type' => 'config_test', 'old_name' => 'node.type.' . $content_type->id(), 'new_name' => 'config_test.dynamic.' . $test_entity_id))
|
||||
);
|
||||
$this->assertIdentical($expected, $this->configImporter->getErrors());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests configuration renaming validation for simple configuration.
|
||||
*/
|
||||
public function testRenameSimpleConfigValidation() {
|
||||
$uuid = new Php();
|
||||
// Create a simple configuration with a UUID.
|
||||
$config = $this->config('config_test.new');
|
||||
$uuid_value = $uuid->generate();
|
||||
$config->set('uuid', $uuid_value)->save();
|
||||
|
||||
$active = $this->container->get('config.storage');
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
$this->copyConfig($active, $staging);
|
||||
$config->delete();
|
||||
|
||||
// Create another simple configuration with the same UUID.
|
||||
$config = $this->config('config_test.old');
|
||||
$config->set('uuid', $uuid_value)->save();
|
||||
|
||||
// Confirm that the staged configuration is detected as a rename since the
|
||||
// UUIDs match.
|
||||
$this->configImporter->reset();
|
||||
$expected = array(
|
||||
'config_test.old::config_test.new'
|
||||
);
|
||||
$renames = $this->configImporter->getUnprocessedConfiguration('rename');
|
||||
$this->assertIdentical($expected, $renames);
|
||||
|
||||
// Try to import the configuration. We expect an exception to be thrown
|
||||
// because the rename is for simple configuration.
|
||||
try {
|
||||
$this->configImporter->import();
|
||||
$this->fail('Expected ConfigImporterException thrown when simple configuration is renamed.');
|
||||
}
|
||||
catch (ConfigImporterException $e) {
|
||||
$this->pass('Expected ConfigImporterException thrown when simple configuration is renamed.');
|
||||
$expected = array(
|
||||
SafeMarkup::format('Rename operation for simple configuration. Existing configuration !old_name and staged configuration !new_name.', array('old_name' => 'config_test.old', 'new_name' => 'config_test.new'))
|
||||
);
|
||||
$this->assertIdentical($expected, $this->configImporter->getErrors());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
491
core/modules/config/src/Tests/ConfigImportUITest.php
Normal file
491
core/modules/config/src/Tests/ConfigImportUITest.php
Normal file
|
@ -0,0 +1,491 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigImportUITest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Config\InstallStorage;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests the user interface for importing/exporting configuration.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigImportUITest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config', 'config_test', 'config_import_test', 'text', 'options');
|
||||
|
||||
/**
|
||||
* A user with the 'synchronize configuration' permission.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->webUser = $this->drupalCreateUser(array('synchronize configuration'));
|
||||
$this->drupalLogin($this->webUser);
|
||||
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.staging'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests importing configuration.
|
||||
*/
|
||||
function testImport() {
|
||||
$name = 'system.site';
|
||||
$dynamic_name = 'config_test.dynamic.new';
|
||||
/** @var \Drupal\Core\Config\StorageInterface $staging */
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
$this->assertText('There are no configuration changes to import.');
|
||||
$this->assertNoFieldById('edit-submit', t('Import all'));
|
||||
|
||||
// Create updated configuration object.
|
||||
$new_site_name = 'Config import test ' . $this->randomString();
|
||||
$this->prepareSiteNameUpdate($new_site_name);
|
||||
$this->assertIdentical($staging->exists($name), TRUE, $name . ' found.');
|
||||
|
||||
// Create new config entity.
|
||||
$original_dynamic_data = array(
|
||||
'uuid' => '30df59bd-7b03-4cf7-bb35-d42fc49f0651',
|
||||
'langcode' => \Drupal::languageManager()->getDefaultLanguage()->getId(),
|
||||
'status' => TRUE,
|
||||
'dependencies' => array(),
|
||||
'id' => 'new',
|
||||
'label' => 'New',
|
||||
'weight' => 0,
|
||||
'style' => '',
|
||||
'size' => '',
|
||||
'size_value' => '',
|
||||
'protected_property' => '',
|
||||
);
|
||||
$staging->write($dynamic_name, $original_dynamic_data);
|
||||
$this->assertIdentical($staging->exists($dynamic_name), TRUE, $dynamic_name . ' found.');
|
||||
|
||||
// Enable the Action and Ban modules during import. The Ban
|
||||
// module is used because it creates a table during the install. The Action
|
||||
// module is used because it creates a single simple configuration file
|
||||
// during the install.
|
||||
$core_extension = $this->config('core.extension')->get();
|
||||
$core_extension['module']['action'] = 0;
|
||||
$core_extension['module']['ban'] = 0;
|
||||
$core_extension['module'] = module_config_sort($core_extension['module']);
|
||||
// Bartik is a subtheme of classy so classy must be enabled.
|
||||
$core_extension['theme']['classy'] = 0;
|
||||
$core_extension['theme']['bartik'] = 0;
|
||||
$staging->write('core.extension', $core_extension);
|
||||
|
||||
// Use the install storage so that we can read configuration from modules
|
||||
// and themes that are not installed.
|
||||
$install_storage = new InstallStorage();
|
||||
|
||||
// Set the Bartik theme as default.
|
||||
$system_theme = $this->config('system.theme')->get();
|
||||
$system_theme['default'] = 'bartik';
|
||||
$staging->write('system.theme', $system_theme);
|
||||
|
||||
// Read the action config from module default config folder.
|
||||
$action_settings = $install_storage->read('action.settings');
|
||||
$action_settings['recursion_limit'] = 50;
|
||||
$staging->write('action.settings', $action_settings);
|
||||
|
||||
// Uninstall the Options and Text modules to ensure that dependencies are
|
||||
// handled correctly. Options depends on Text so Text should be installed
|
||||
// first. Since they were enabled during the test setup the core.extension
|
||||
// file in staging will already contain them.
|
||||
\Drupal::service('module_installer')->uninstall(array('text', 'options'));
|
||||
|
||||
// Set the state system to record installations and uninstallations.
|
||||
\Drupal::state()->set('ConfigImportUITest.core.extension.modules_installed', array());
|
||||
\Drupal::state()->set('ConfigImportUITest.core.extension.modules_uninstalled', array());
|
||||
|
||||
// Verify that both appear as ready to import.
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
$this->assertRaw('<td>' . $name);
|
||||
$this->assertRaw('<td>' . $dynamic_name);
|
||||
$this->assertRaw('<td>core.extension');
|
||||
$this->assertRaw('<td>system.theme');
|
||||
$this->assertRaw('<td>action.settings');
|
||||
$this->assertFieldById('edit-submit', t('Import all'));
|
||||
|
||||
// Import and verify that both do not appear anymore.
|
||||
$this->drupalPostForm(NULL, array(), t('Import all'));
|
||||
$this->assertNoRaw('<td>' . $name);
|
||||
$this->assertNoRaw('<td>' . $dynamic_name);
|
||||
$this->assertNoRaw('<td>core.extension');
|
||||
$this->assertNoRaw('<td>system.theme');
|
||||
$this->assertNoRaw('<td>action.settings');
|
||||
|
||||
$this->assertNoFieldById('edit-submit', t('Import all'));
|
||||
|
||||
// Verify that there are no further changes to import.
|
||||
$this->assertText(t('There are no configuration changes to import.'));
|
||||
|
||||
// Verify site name has changed.
|
||||
$this->assertIdentical($new_site_name, $this->config('system.site')->get('name'));
|
||||
|
||||
// Verify that new config entity exists.
|
||||
$this->assertIdentical($original_dynamic_data, $this->config($dynamic_name)->get());
|
||||
|
||||
// Verify the cache got cleared.
|
||||
$this->assertTrue(isset($GLOBALS['hook_cache_flush']));
|
||||
|
||||
$this->rebuildContainer();
|
||||
$this->assertTrue(\Drupal::moduleHandler()->moduleExists('ban'), 'Ban module installed during import.');
|
||||
$this->assertTrue(\Drupal::database()->schema()->tableExists('ban_ip'), 'The database table ban_ip exists.');
|
||||
$this->assertTrue(\Drupal::moduleHandler()->moduleExists('action'), 'Action module installed during import.');
|
||||
$this->assertTrue(\Drupal::moduleHandler()->moduleExists('options'), 'Options module installed during import.');
|
||||
$this->assertTrue(\Drupal::moduleHandler()->moduleExists('text'), 'Text module installed during import.');
|
||||
|
||||
$theme_info = \Drupal::service('theme_handler')->listInfo();
|
||||
$this->assertTrue($theme_info['bartik']->status, 'Bartik theme installed during import.');
|
||||
|
||||
// Ensure installations and uninstallation occur as expected.
|
||||
$installed = \Drupal::state()->get('ConfigImportUITest.core.extension.modules_installed', array());
|
||||
$uninstalled = \Drupal::state()->get('ConfigImportUITest.core.extension.modules_uninstalled', array());
|
||||
$expected = array('action', 'ban', 'text', 'options');
|
||||
$this->assertIdentical($expected, $installed, 'Action, Ban, Text and Options modules installed in the correct order.');
|
||||
$this->assertTrue(empty($uninstalled), 'No modules uninstalled during import');
|
||||
|
||||
// Verify that the action.settings configuration object was only written
|
||||
// once during the import process and only with the value set in the staged
|
||||
// configuration. This verifies that the module's default configuration is
|
||||
// used during configuration import and, additionally, that after installing
|
||||
// a module, that configuration is not synced twice.
|
||||
$recursion_limit_values = \Drupal::state()->get('ConfigImportUITest.action.settings.recursion_limit', array());
|
||||
$this->assertIdentical($recursion_limit_values, array(50));
|
||||
|
||||
$core_extension = $this->config('core.extension')->get();
|
||||
unset($core_extension['module']['action']);
|
||||
unset($core_extension['module']['ban']);
|
||||
unset($core_extension['module']['options']);
|
||||
unset($core_extension['module']['text']);
|
||||
unset($core_extension['theme']['bartik']);
|
||||
$staging->write('core.extension', $core_extension);
|
||||
$staging->delete('action.settings');
|
||||
$staging->delete('text.settings');
|
||||
|
||||
$system_theme = $this->config('system.theme')->get();
|
||||
$system_theme['default'] = 'stark';
|
||||
$system_theme['admin'] = 'stark';
|
||||
$staging->write('system.theme', $system_theme);
|
||||
|
||||
// Set the state system to record installations and uninstallations.
|
||||
\Drupal::state()->set('ConfigImportUITest.core.extension.modules_installed', array());
|
||||
\Drupal::state()->set('ConfigImportUITest.core.extension.modules_uninstalled', array());
|
||||
|
||||
// Verify that both appear as ready to import.
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
$this->assertRaw('<td>core.extension');
|
||||
$this->assertRaw('<td>system.theme');
|
||||
$this->assertRaw('<td>action.settings');
|
||||
|
||||
// Import and verify that both do not appear anymore.
|
||||
$this->drupalPostForm(NULL, array(), t('Import all'));
|
||||
$this->assertNoRaw('<td>core.extension');
|
||||
$this->assertNoRaw('<td>system.theme');
|
||||
$this->assertNoRaw('<td>action.settings');
|
||||
|
||||
$this->rebuildContainer();
|
||||
$this->assertFalse(\Drupal::moduleHandler()->moduleExists('ban'), 'Ban module uninstalled during import.');
|
||||
$this->assertFalse(\Drupal::database()->schema()->tableExists('ban_ip'), 'The database table ban_ip does not exist.');
|
||||
$this->assertFalse(\Drupal::moduleHandler()->moduleExists('action'), 'Action module uninstalled during import.');
|
||||
$this->assertFalse(\Drupal::moduleHandler()->moduleExists('options'), 'Options module uninstalled during import.');
|
||||
$this->assertFalse(\Drupal::moduleHandler()->moduleExists('text'), 'Text module uninstalled during import.');
|
||||
|
||||
// Ensure installations and uninstallation occur as expected.
|
||||
$installed = \Drupal::state()->get('ConfigImportUITest.core.extension.modules_installed', array());
|
||||
$uninstalled = \Drupal::state()->get('ConfigImportUITest.core.extension.modules_uninstalled', array());
|
||||
$expected = array('options', 'text', 'ban', 'action');
|
||||
$this->assertIdentical($expected, $uninstalled, 'Options, Text, Ban and Action modules uninstalled in the correct order.');
|
||||
$this->assertTrue(empty($installed), 'No modules installed during import');
|
||||
|
||||
$theme_info = \Drupal::service('theme_handler')->listInfo();
|
||||
$this->assertFalse(isset($theme_info['bartik']), 'Bartik theme uninstalled during import.');
|
||||
|
||||
// Verify that the action.settings configuration object was only deleted
|
||||
// once during the import process.
|
||||
$delete_called = \Drupal::state()->get('ConfigImportUITest.action.settings.delete', 0);
|
||||
$this->assertIdentical($delete_called, 1, "The action.settings configuration was deleted once during configuration import.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests concurrent importing of configuration.
|
||||
*/
|
||||
function testImportLock() {
|
||||
// Create updated configuration object.
|
||||
$new_site_name = 'Config import test ' . $this->randomString();
|
||||
$this->prepareSiteNameUpdate($new_site_name);
|
||||
|
||||
// Verify that there are configuration differences to import.
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
$this->assertNoText(t('There are no configuration changes to import.'));
|
||||
|
||||
// Acquire a fake-lock on the import mechanism.
|
||||
$config_importer = $this->configImporter();
|
||||
$this->container->get('lock.persistent')->acquire($config_importer::LOCK_NAME);
|
||||
|
||||
// Attempt to import configuration and verify that an error message appears.
|
||||
$this->drupalPostForm(NULL, array(), t('Import all'));
|
||||
$this->assertText(t('Another request may be synchronizing configuration already.'));
|
||||
|
||||
// Release the lock, just to keep testing sane.
|
||||
$this->container->get('lock.persistent')->release($config_importer::LOCK_NAME);
|
||||
|
||||
// Verify site name has not changed.
|
||||
$this->assertNotEqual($new_site_name, $this->config('system.site')->get('name'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests verification of site UUID before importing configuration.
|
||||
*/
|
||||
function testImportSiteUuidValidation() {
|
||||
$staging = \Drupal::service('config.storage.staging');
|
||||
// Create updated configuration object.
|
||||
$config_data = $this->config('system.site')->get();
|
||||
// Generate a new site UUID.
|
||||
$config_data['uuid'] = \Drupal::service('uuid')->generate();
|
||||
$staging->write('system.site', $config_data);
|
||||
|
||||
// Verify that there are configuration differences to import.
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
$this->assertText(t('The staged configuration cannot be imported, because it originates from a different site than this site. You can only synchronize configuration between cloned instances of this site.'));
|
||||
$this->assertNoFieldById('edit-submit', t('Import all'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the screen that shows differences between active and staging.
|
||||
*/
|
||||
function testImportDiff() {
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
$config_name = 'config_test.system';
|
||||
$change_key = 'foo';
|
||||
$remove_key = '404';
|
||||
$add_key = 'biff';
|
||||
$add_data = 'bangpow';
|
||||
$change_data = 'foobar';
|
||||
$original_data = array(
|
||||
'foo' => 'bar',
|
||||
'404' => 'herp',
|
||||
);
|
||||
|
||||
// Change a configuration value in staging.
|
||||
$staging_data = $original_data;
|
||||
$staging_data[$change_key] = $change_data;
|
||||
$staging_data[$add_key] = $add_data;
|
||||
$staging->write($config_name, $staging_data);
|
||||
|
||||
// Load the diff UI and verify that the diff reflects the change.
|
||||
$this->drupalGet('admin/config/development/configuration/sync/diff/' . $config_name);
|
||||
$this->assertTitle(format_string('View changes of @config_name | Drupal', array('@config_name' => $config_name)));
|
||||
|
||||
// Reset data back to original, and remove a key
|
||||
$staging_data = $original_data;
|
||||
unset($staging_data[$remove_key]);
|
||||
$staging->write($config_name, $staging_data);
|
||||
|
||||
// Load the diff UI and verify that the diff reflects a removed key.
|
||||
$this->drupalGet('admin/config/development/configuration/sync/diff/' . $config_name);
|
||||
|
||||
// Reset data back to original and add a key
|
||||
$staging_data = $original_data;
|
||||
$staging_data[$add_key] = $add_data;
|
||||
$staging->write($config_name, $staging_data);
|
||||
|
||||
// Load the diff UI and verify that the diff reflects an added key.
|
||||
$this->drupalGet('admin/config/development/configuration/sync/diff/' . $config_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that multiple validation errors are listed on the page.
|
||||
*/
|
||||
public function testImportValidation() {
|
||||
// Set state value so that
|
||||
// \Drupal\config_import_test\EventSubscriber::onConfigImportValidate() logs
|
||||
// validation errors.
|
||||
\Drupal::state()->set('config_import_test.config_import_validate_fail', TRUE);
|
||||
// Ensure there is something to import.
|
||||
$new_site_name = 'Config import test ' . $this->randomString();
|
||||
$this->prepareSiteNameUpdate($new_site_name);
|
||||
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
$this->assertNoText(t('There are no configuration changes to import.'));
|
||||
$this->drupalPostForm(NULL, array(), t('Import all'));
|
||||
|
||||
// Verify that the validation messages appear.
|
||||
$this->assertText('The configuration cannot be imported because it failed validation for the following reasons:');
|
||||
$this->assertText('Config import validate error 1.');
|
||||
$this->assertText('Config import validate error 2.');
|
||||
|
||||
// Verify site name has not changed.
|
||||
$this->assertNotEqual($new_site_name, $this->config('system.site')->get('name'));
|
||||
}
|
||||
|
||||
public function testConfigUninstallConfigException() {
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
|
||||
$core_extension = $this->config('core.extension')->get();
|
||||
unset($core_extension['module']['config']);
|
||||
$staging->write('core.extension', $core_extension);
|
||||
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
$this->assertText('core.extension');
|
||||
|
||||
// Import and verify that both do not appear anymore.
|
||||
$this->drupalPostForm(NULL, array(), t('Import all'));
|
||||
$this->assertText('Can not uninstall the Configuration module as part of a configuration synchronization through the user interface.');
|
||||
}
|
||||
|
||||
function prepareSiteNameUpdate($new_site_name) {
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
// Create updated configuration object.
|
||||
$config_data = $this->config('system.site')->get();
|
||||
$config_data['name'] = $new_site_name;
|
||||
$staging->write('system.site', $config_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an import that results in an error.
|
||||
*/
|
||||
function testImportErrorLog() {
|
||||
$name_primary = 'config_test.dynamic.primary';
|
||||
$name_secondary = 'config_test.dynamic.secondary';
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
$uuid = $this->container->get('uuid');
|
||||
|
||||
$values_primary = array(
|
||||
'uuid' => $uuid->generate(),
|
||||
'langcode' => 'en',
|
||||
'status' => TRUE,
|
||||
'dependencies' => array(),
|
||||
'id' => 'primary',
|
||||
'label' => 'Primary',
|
||||
'weight' => 0,
|
||||
'style' => NULL,
|
||||
'size' => NULL,
|
||||
'size_value' => NULL,
|
||||
'protected_property' => NULL,
|
||||
);
|
||||
$staging->write($name_primary, $values_primary);
|
||||
$values_secondary = array(
|
||||
'uuid' => $uuid->generate(),
|
||||
'langcode' => 'en',
|
||||
'status' => TRUE,
|
||||
// Add a dependency on primary, to ensure that is synced first.
|
||||
'dependencies' => array(
|
||||
'config' => array($name_primary),
|
||||
),
|
||||
'id' => 'secondary',
|
||||
'label' => 'Secondary Sync',
|
||||
'weight' => 0,
|
||||
'style' => NULL,
|
||||
'size' => NULL,
|
||||
'size_value' => NULL,
|
||||
'protected_property' => NULL,
|
||||
);
|
||||
$staging->write($name_secondary, $values_secondary);
|
||||
// Verify that there are configuration differences to import.
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
$this->assertNoText(t('There are no configuration changes to import.'));
|
||||
|
||||
// Attempt to import configuration and verify that an error message appears.
|
||||
$this->drupalPostForm(NULL, array(), t('Import all'));
|
||||
$this->assertText(SafeMarkup::format('Deleted and replaced configuration entity "@name"', array('@name' => $name_secondary)));
|
||||
$this->assertText(t('The configuration was imported with errors.'));
|
||||
$this->assertNoText(t('The configuration was imported successfully.'));
|
||||
$this->assertText(t('There are no configuration changes to import.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the config importer cannot delete bundles with existing entities.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\Event\BundleConfigImportValidate
|
||||
*/
|
||||
public function testEntityBundleDelete() {
|
||||
\Drupal::service('module_installer')->install(array('node'));
|
||||
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.staging'));
|
||||
|
||||
$node_type = $this->drupalCreateContentType();
|
||||
$node = $this->drupalCreateNode(array('type' => $node_type->id()));
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
// The node type, body field and entity displays will be scheduled for
|
||||
// removal.
|
||||
$this->assertText(format_string('node.type.@type', array('@type' => $node_type->id())));
|
||||
$this->assertText(format_string('field.field.node.@type.body', array('@type' => $node_type->id())));
|
||||
$this->assertText(format_string('core.entity_view_display.node.@type.teaser', array('@type' => $node_type->id())));
|
||||
$this->assertText(format_string('core.entity_view_display.node.@type.default', array('@type' => $node_type->id())));
|
||||
$this->assertText(format_string('core.entity_form_display.node.@type.default', array('@type' => $node_type->id())));
|
||||
|
||||
// Attempt to import configuration and verify that an error message appears
|
||||
// and the node type, body field and entity displays are still scheduled for
|
||||
// removal.
|
||||
$this->drupalPostForm(NULL, array(), t('Import all'));
|
||||
$validation_message = t('Entities exist of type %entity_type and %bundle_label %bundle. These entities need to be deleted before importing.', array('%entity_type' => $node->getEntityType()->getLabel(), '%bundle_label' => $node->getEntityType()->getBundleLabel(), '%bundle' => $node_type->label()));
|
||||
$this->assertRaw($validation_message);
|
||||
$this->assertText(format_string('node.type.@type', array('@type' => $node_type->id())));
|
||||
$this->assertText(format_string('field.field.node.@type.body', array('@type' => $node_type->id())));
|
||||
$this->assertText(format_string('core.entity_view_display.node.@type.teaser', array('@type' => $node_type->id())));
|
||||
$this->assertText(format_string('core.entity_view_display.node.@type.default', array('@type' => $node_type->id())));
|
||||
$this->assertText(format_string('core.entity_form_display.node.@type.default', array('@type' => $node_type->id())));
|
||||
|
||||
// Delete the node and try to import again.
|
||||
$node->delete();
|
||||
$this->drupalPostForm(NULL, array(), t('Import all'));
|
||||
$this->assertNoRaw($validation_message);
|
||||
$this->assertText(t('There are no configuration changes to import.'));
|
||||
$this->assertNoText(format_string('node.type.@type', array('@type' => $node_type->id())));
|
||||
$this->assertNoText(format_string('field.field.node.@type.body', array('@type' => $node_type->id())));
|
||||
$this->assertNoText(format_string('core.entity_view_display.node.@type.teaser', array('@type' => $node_type->id())));
|
||||
$this->assertNoText(format_string('core.entity_view_display.node.@type.default', array('@type' => $node_type->id())));
|
||||
$this->assertNoText(format_string('core.entity_form_display.node.@type.default', array('@type' => $node_type->id())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests config importer cannot uninstall extensions which are depended on.
|
||||
*
|
||||
* @see \Drupal\Core\EventSubscriber\ConfigImportSubscriber
|
||||
*/
|
||||
public function testExtensionValidation() {
|
||||
\Drupal::service('module_installer')->install(['node']);
|
||||
\Drupal::service('theme_handler')->install(['bartik']);
|
||||
$this->rebuildContainer();
|
||||
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
$this->copyConfig($this->container->get('config.storage'), $staging);
|
||||
$core = $staging->read('core.extension');
|
||||
// Node depends on text.
|
||||
unset($core['module']['text']);
|
||||
$module_data = system_rebuild_module_data();
|
||||
$this->assertTrue(isset($module_data['node']->requires['text']), 'The Node module depends on the Text module.');
|
||||
// Bartik depends on classy.
|
||||
unset($core['theme']['classy']);
|
||||
$theme_data = \Drupal::service('theme_handler')->rebuildThemeData();
|
||||
$this->assertTrue(isset($theme_data['bartik']->requires['classy']), 'The Bartik theme depends on the Classy theme.');
|
||||
// This module does not exist.
|
||||
$core['module']['does_not_exist'] = 0;
|
||||
// This theme does not exist.
|
||||
$core['theme']['does_not_exist'] = 0;
|
||||
$staging->write('core.extension', $core);
|
||||
|
||||
$this->drupalPostForm('admin/config/development/configuration', array(), t('Import all'));
|
||||
$this->assertText('The configuration cannot be imported because it failed validation for the following reasons:');
|
||||
$this->assertText('Unable to uninstall the Text module since the Node module is installed.');
|
||||
$this->assertText('Unable to uninstall the Classy theme since the Bartik theme is installed.');
|
||||
$this->assertText('Unable to install the does_not_exist module since it does not exist.');
|
||||
$this->assertText('Unable to install the does_not_exist theme since it does not exist.');
|
||||
}
|
||||
|
||||
}
|
55
core/modules/config/src/Tests/ConfigImportUploadTest.php
Normal file
55
core/modules/config/src/Tests/ConfigImportUploadTest.php
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigImportUploadTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests importing configuration from an uploaded file.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigImportUploadTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* A user with the 'import configuration' permission.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->webUser = $this->drupalCreateUser(array('import configuration'));
|
||||
$this->drupalLogin($this->webUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests importing configuration.
|
||||
*/
|
||||
function testImport() {
|
||||
// Verify access to the config upload form.
|
||||
$this->drupalGet('admin/config/development/configuration/full/import');
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Attempt to upload a non-tar file.
|
||||
$text_file = current($this->drupalGetTestFiles('text'));
|
||||
$edit = array('files[import_tarball]' => drupal_realpath($text_file->uri));
|
||||
$this->drupalPostForm('admin/config/development/configuration/full/import', $edit, t('Upload'));
|
||||
$this->assertText(t('Could not extract the contents of the tar file'));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigImporterMissingContentTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Config\ConfigImporter;
|
||||
use Drupal\Core\Config\ConfigImporterException;
|
||||
use Drupal\Core\Config\StorageComparer;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests importing configuration which has missing content dependencies.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigImporterMissingContentTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Config Importer object used for testing.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigImporter
|
||||
*/
|
||||
protected $configImporter;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system', 'user', 'entity_test', 'config_test', 'config_import_test');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installSchema('system', 'sequences');
|
||||
$this->installEntitySchema('entity_test');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installConfig(array('config_test'));
|
||||
// Installing config_test's default configuration pollutes the global
|
||||
// variable being used for recording hook invocations by this test already,
|
||||
// so it has to be cleared out manually.
|
||||
unset($GLOBALS['hook_config_test']);
|
||||
|
||||
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.staging'));
|
||||
|
||||
// Set up the ConfigImporter object for testing.
|
||||
$storage_comparer = new StorageComparer(
|
||||
$this->container->get('config.storage.staging'),
|
||||
$this->container->get('config.storage'),
|
||||
$this->container->get('config.manager')
|
||||
);
|
||||
$this->configImporter = new ConfigImporter(
|
||||
$storage_comparer->createChangelist(),
|
||||
$this->container->get('event_dispatcher'),
|
||||
$this->container->get('config.manager'),
|
||||
$this->container->get('lock'),
|
||||
$this->container->get('config.typed'),
|
||||
$this->container->get('module_handler'),
|
||||
$this->container->get('module_installer'),
|
||||
$this->container->get('theme_handler'),
|
||||
$this->container->get('string_translation')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the missing content event is fired.
|
||||
*
|
||||
* @see \Drupal\Core\Config\ConfigImporter::processMissingContent()
|
||||
* @see \Drupal\config_import_test\EventSubscriber
|
||||
*/
|
||||
function testMissingContent() {
|
||||
\Drupal::state()->set('config_import_test.config_import_missing_content', TRUE);
|
||||
|
||||
// Update a configuration entity in the staging directory to have a
|
||||
// dependency on two content entities that do not exist.
|
||||
$storage = $this->container->get('config.storage');
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
$entity_one = entity_create('entity_test', array('name' => 'one'));
|
||||
$entity_two = entity_create('entity_test', array('name' => 'two'));
|
||||
$entity_three = entity_create('entity_test', array('name' => 'three'));
|
||||
$dynamic_name = 'config_test.dynamic.dotted.default';
|
||||
$original_dynamic_data = $storage->read($dynamic_name);
|
||||
// Entity one will be resolved by
|
||||
// \Drupal\config_import_test\EventSubscriber::onConfigImporterMissingContentOne().
|
||||
$original_dynamic_data['dependencies']['content'][] = $entity_one->getConfigDependencyName();
|
||||
// Entity two will be resolved by
|
||||
// \Drupal\config_import_test\EventSubscriber::onConfigImporterMissingContentTwo().
|
||||
$original_dynamic_data['dependencies']['content'][] = $entity_two->getConfigDependencyName();
|
||||
// Entity three will be resolved by
|
||||
// \Drupal\Core\Config\Importer\FinalMissingContentSubscriber.
|
||||
$original_dynamic_data['dependencies']['content'][] = $entity_three->getConfigDependencyName();
|
||||
$staging->write($dynamic_name, $original_dynamic_data);
|
||||
|
||||
// Import.
|
||||
$this->configImporter->reset()->import();
|
||||
$this->assertEqual([], $this->configImporter->getErrors(), 'There were no errors during the import.');
|
||||
$this->assertEqual($entity_one->uuid(), \Drupal::state()->get('config_import_test.config_import_missing_content_one'), 'The missing content event is fired during configuration import.');
|
||||
$this->assertEqual($entity_two->uuid(), \Drupal::state()->get('config_import_test.config_import_missing_content_two'), 'The missing content event is fired during configuration import.');
|
||||
$original_dynamic_data = $storage->read($dynamic_name);
|
||||
$this->assertEqual([$entity_one->getConfigDependencyName(), $entity_two->getConfigDependencyName(), $entity_three->getConfigDependencyName()], $original_dynamic_data['dependencies']['content'], 'The imported configuration entity has the missing content entity dependency.');
|
||||
}
|
||||
|
||||
}
|
676
core/modules/config/src/Tests/ConfigImporterTest.php
Normal file
676
core/modules/config/src/Tests/ConfigImporterTest.php
Normal file
|
@ -0,0 +1,676 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigImporterTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Config\ConfigImporter;
|
||||
use Drupal\Core\Config\ConfigImporterException;
|
||||
use Drupal\Core\Config\StorageComparer;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests importing configuration from files into active configuration.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigImporterTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Config Importer object used for testing.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigImporter
|
||||
*/
|
||||
protected $configImporter;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_test', 'system', 'config_import_test');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installConfig(array('config_test'));
|
||||
// Installing config_test's default configuration pollutes the global
|
||||
// variable being used for recording hook invocations by this test already,
|
||||
// so it has to be cleared out manually.
|
||||
unset($GLOBALS['hook_config_test']);
|
||||
|
||||
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.staging'));
|
||||
|
||||
// Set up the ConfigImporter object for testing.
|
||||
$storage_comparer = new StorageComparer(
|
||||
$this->container->get('config.storage.staging'),
|
||||
$this->container->get('config.storage'),
|
||||
$this->container->get('config.manager')
|
||||
);
|
||||
$this->configImporter = new ConfigImporter(
|
||||
$storage_comparer->createChangelist(),
|
||||
$this->container->get('event_dispatcher'),
|
||||
$this->container->get('config.manager'),
|
||||
$this->container->get('lock'),
|
||||
$this->container->get('config.typed'),
|
||||
$this->container->get('module_handler'),
|
||||
$this->container->get('module_installer'),
|
||||
$this->container->get('theme_handler'),
|
||||
$this->container->get('string_translation')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests omission of module APIs for bare configuration operations.
|
||||
*/
|
||||
function testNoImport() {
|
||||
$dynamic_name = 'config_test.dynamic.dotted.default';
|
||||
|
||||
// Verify the default configuration values exist.
|
||||
$config = $this->config($dynamic_name);
|
||||
$this->assertIdentical($config->get('id'), 'dotted.default');
|
||||
|
||||
// Verify that a bare $this->config() does not involve module APIs.
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that trying to import from an empty staging configuration directory
|
||||
* fails.
|
||||
*/
|
||||
function testEmptyImportFails() {
|
||||
try {
|
||||
$this->container->get('config.storage.staging')->deleteAll();
|
||||
$this->configImporter->reset()->import();
|
||||
$this->fail('ConfigImporterException thrown, successfully stopping an empty import.');
|
||||
}
|
||||
catch (ConfigImporterException $e) {
|
||||
$this->pass('ConfigImporterException thrown, successfully stopping an empty import.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests verification of site UUID before importing configuration.
|
||||
*/
|
||||
function testSiteUuidValidate() {
|
||||
$staging = \Drupal::service('config.storage.staging');
|
||||
// Create updated configuration object.
|
||||
$config_data = $this->config('system.site')->get();
|
||||
// Generate a new site UUID.
|
||||
$config_data['uuid'] = \Drupal::service('uuid')->generate();
|
||||
$staging->write('system.site', $config_data);
|
||||
try {
|
||||
$this->configImporter->reset()->import();
|
||||
$this->fail('ConfigImporterException not thrown, invalid import was not stopped due to mis-matching site UUID.');
|
||||
}
|
||||
catch (ConfigImporterException $e) {
|
||||
$this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.');
|
||||
$error_log = $this->configImporter->getErrors();
|
||||
$expected = array('Site UUID in source storage does not match the target storage.');
|
||||
$this->assertEqual($expected, $error_log);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests deletion of configuration during import.
|
||||
*/
|
||||
function testDeleted() {
|
||||
$dynamic_name = 'config_test.dynamic.dotted.default';
|
||||
$storage = $this->container->get('config.storage');
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
|
||||
// Verify the default configuration values exist.
|
||||
$config = $this->config($dynamic_name);
|
||||
$this->assertIdentical($config->get('id'), 'dotted.default');
|
||||
|
||||
// Delete the file from the staging directory.
|
||||
$staging->delete($dynamic_name);
|
||||
|
||||
// Import.
|
||||
$this->configImporter->reset()->import();
|
||||
|
||||
// Verify the file has been removed.
|
||||
$this->assertIdentical($storage->read($dynamic_name), FALSE);
|
||||
|
||||
$config = $this->config($dynamic_name);
|
||||
$this->assertIdentical($config->get('id'), NULL);
|
||||
|
||||
// Verify that appropriate module API hooks have been invoked.
|
||||
$this->assertTrue(isset($GLOBALS['hook_config_test']['load']));
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['presave']));
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['insert']));
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['update']));
|
||||
$this->assertTrue(isset($GLOBALS['hook_config_test']['predelete']));
|
||||
$this->assertTrue(isset($GLOBALS['hook_config_test']['delete']));
|
||||
|
||||
$this->assertFalse($this->configImporter->hasUnprocessedConfigurationChanges());
|
||||
$logs = $this->configImporter->getErrors();
|
||||
$this->assertEqual(count($logs), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests creation of configuration during import.
|
||||
*/
|
||||
function testNew() {
|
||||
$dynamic_name = 'config_test.dynamic.new';
|
||||
$storage = $this->container->get('config.storage');
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
|
||||
// Verify the configuration to create does not exist yet.
|
||||
$this->assertIdentical($storage->exists($dynamic_name), FALSE, $dynamic_name . ' not found.');
|
||||
|
||||
// Create new config entity.
|
||||
$original_dynamic_data = array(
|
||||
'uuid' => '30df59bd-7b03-4cf7-bb35-d42fc49f0651',
|
||||
'langcode' => \Drupal::languageManager()->getDefaultLanguage()->getId(),
|
||||
'status' => TRUE,
|
||||
'dependencies' => array(),
|
||||
'id' => 'new',
|
||||
'label' => 'New',
|
||||
'weight' => 0,
|
||||
'style' => '',
|
||||
'size' => '',
|
||||
'size_value' => '',
|
||||
'protected_property' => '',
|
||||
);
|
||||
$staging->write($dynamic_name, $original_dynamic_data);
|
||||
|
||||
$this->assertIdentical($staging->exists($dynamic_name), TRUE, $dynamic_name . ' found.');
|
||||
|
||||
// Import.
|
||||
$this->configImporter->reset()->import();
|
||||
|
||||
// Verify the values appeared.
|
||||
$config = $this->config($dynamic_name);
|
||||
$this->assertIdentical($config->get('label'), $original_dynamic_data['label']);
|
||||
|
||||
// Verify that appropriate module API hooks have been invoked.
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['load']));
|
||||
$this->assertTrue(isset($GLOBALS['hook_config_test']['presave']));
|
||||
$this->assertTrue(isset($GLOBALS['hook_config_test']['insert']));
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['update']));
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['predelete']));
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['delete']));
|
||||
|
||||
// Verify that hook_config_import_steps_alter() can add steps to
|
||||
// configuration synchronization.
|
||||
$this->assertTrue(isset($GLOBALS['hook_config_test']['config_import_steps_alter']));
|
||||
|
||||
// Verify that there is nothing more to import.
|
||||
$this->assertFalse($this->configImporter->hasUnprocessedConfigurationChanges());
|
||||
$logs = $this->configImporter->getErrors();
|
||||
$this->assertEqual(count($logs), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that secondary writes are overwritten.
|
||||
*/
|
||||
function testSecondaryWritePrimaryFirst() {
|
||||
$name_primary = 'config_test.dynamic.primary';
|
||||
$name_secondary = 'config_test.dynamic.secondary';
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
$uuid = $this->container->get('uuid');
|
||||
|
||||
$values_primary = array(
|
||||
'id' => 'primary',
|
||||
'label' => 'Primary',
|
||||
'weight' => 0,
|
||||
'uuid' => $uuid->generate(),
|
||||
);
|
||||
$staging->write($name_primary, $values_primary);
|
||||
$values_secondary = array(
|
||||
'id' => 'secondary',
|
||||
'label' => 'Secondary Sync',
|
||||
'weight' => 0,
|
||||
'uuid' => $uuid->generate(),
|
||||
// Add a dependency on primary, to ensure that is synced first.
|
||||
'dependencies' => array(
|
||||
'config' => array($name_primary),
|
||||
)
|
||||
);
|
||||
$staging->write($name_secondary, $values_secondary);
|
||||
|
||||
// Import.
|
||||
$this->configImporter->reset()->import();
|
||||
|
||||
$entity_storage = \Drupal::entityManager()->getStorage('config_test');
|
||||
$primary = $entity_storage->load('primary');
|
||||
$this->assertEqual($primary->id(), 'primary');
|
||||
$this->assertEqual($primary->uuid(), $values_primary['uuid']);
|
||||
$this->assertEqual($primary->label(), $values_primary['label']);
|
||||
$secondary = $entity_storage->load('secondary');
|
||||
$this->assertEqual($secondary->id(), 'secondary');
|
||||
$this->assertEqual($secondary->uuid(), $values_secondary['uuid']);
|
||||
$this->assertEqual($secondary->label(), $values_secondary['label']);
|
||||
|
||||
$logs = $this->configImporter->getErrors();
|
||||
$this->assertEqual(count($logs), 1);
|
||||
$this->assertEqual($logs[0], SafeMarkup::format('Deleted and replaced configuration entity "@name"', array('@name' => $name_secondary)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that secondary writes are overwritten.
|
||||
*/
|
||||
function testSecondaryWriteSecondaryFirst() {
|
||||
$name_primary = 'config_test.dynamic.primary';
|
||||
$name_secondary = 'config_test.dynamic.secondary';
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
$uuid = $this->container->get('uuid');
|
||||
|
||||
$values_primary = array(
|
||||
'id' => 'primary',
|
||||
'label' => 'Primary',
|
||||
'weight' => 0,
|
||||
'uuid' => $uuid->generate(),
|
||||
// Add a dependency on secondary, so that is synced first.
|
||||
'dependencies' => array(
|
||||
'config' => array($name_secondary),
|
||||
)
|
||||
);
|
||||
$staging->write($name_primary, $values_primary);
|
||||
$values_secondary = array(
|
||||
'id' => 'secondary',
|
||||
'label' => 'Secondary Sync',
|
||||
'weight' => 0,
|
||||
'uuid' => $uuid->generate(),
|
||||
);
|
||||
$staging->write($name_secondary, $values_secondary);
|
||||
|
||||
// Import.
|
||||
$this->configImporter->reset()->import();
|
||||
|
||||
$entity_storage = \Drupal::entityManager()->getStorage('config_test');
|
||||
$primary = $entity_storage->load('primary');
|
||||
$this->assertEqual($primary->id(), 'primary');
|
||||
$this->assertEqual($primary->uuid(), $values_primary['uuid']);
|
||||
$this->assertEqual($primary->label(), $values_primary['label']);
|
||||
$secondary = $entity_storage->load('secondary');
|
||||
$this->assertEqual($secondary->id(), 'secondary');
|
||||
$this->assertEqual($secondary->uuid(), $values_secondary['uuid']);
|
||||
$this->assertEqual($secondary->label(), $values_secondary['label']);
|
||||
|
||||
$logs = $this->configImporter->getErrors();
|
||||
$this->assertEqual(count($logs), 1);
|
||||
$message = SafeMarkup::format('config_test entity with ID @name already exists', array('@name' => 'secondary'));
|
||||
$this->assertEqual($logs[0], SafeMarkup::format('Unexpected error during import with operation @op for @name: @message.', array('@op' => 'create', '@name' => $name_primary, '@message' => $message)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that secondary updates for deleted files work as expected.
|
||||
*/
|
||||
function testSecondaryUpdateDeletedDeleterFirst() {
|
||||
$name_deleter = 'config_test.dynamic.deleter';
|
||||
$name_deletee = 'config_test.dynamic.deletee';
|
||||
$name_other = 'config_test.dynamic.other';
|
||||
$storage = $this->container->get('config.storage');
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
$uuid = $this->container->get('uuid');
|
||||
|
||||
$values_deleter = array(
|
||||
'id' => 'deleter',
|
||||
'label' => 'Deleter',
|
||||
'weight' => 0,
|
||||
'uuid' => $uuid->generate(),
|
||||
);
|
||||
$storage->write($name_deleter, $values_deleter);
|
||||
$values_deleter['label'] = 'Updated Deleter';
|
||||
$staging->write($name_deleter, $values_deleter);
|
||||
$values_deletee = array(
|
||||
'id' => 'deletee',
|
||||
'label' => 'Deletee',
|
||||
'weight' => 0,
|
||||
'uuid' => $uuid->generate(),
|
||||
// Add a dependency on deleter, to make sure that is synced first.
|
||||
'dependencies' => array(
|
||||
'config' => array($name_deleter),
|
||||
)
|
||||
);
|
||||
$storage->write($name_deletee, $values_deletee);
|
||||
$values_deletee['label'] = 'Updated Deletee';
|
||||
$staging->write($name_deletee, $values_deletee);
|
||||
|
||||
// Ensure that import will continue after the error.
|
||||
$values_other = array(
|
||||
'id' => 'other',
|
||||
'label' => 'Other',
|
||||
'weight' => 0,
|
||||
'uuid' => $uuid->generate(),
|
||||
// Add a dependency on deleter, to make sure that is synced first. This
|
||||
// will also be synced after the deletee due to alphabetical ordering.
|
||||
'dependencies' => array(
|
||||
'config' => array($name_deleter),
|
||||
)
|
||||
);
|
||||
$storage->write($name_other, $values_other);
|
||||
$values_other['label'] = 'Updated other';
|
||||
$staging->write($name_other, $values_other);
|
||||
|
||||
// Check update changelist order.
|
||||
$updates = $this->configImporter->reset()->getStorageComparer()->getChangelist('update');
|
||||
$expected = array(
|
||||
$name_deleter,
|
||||
$name_deletee,
|
||||
$name_other,
|
||||
);
|
||||
$this->assertIdentical($expected, $updates);
|
||||
|
||||
// Import.
|
||||
$this->configImporter->import();
|
||||
|
||||
$entity_storage = \Drupal::entityManager()->getStorage('config_test');
|
||||
$deleter = $entity_storage->load('deleter');
|
||||
$this->assertEqual($deleter->id(), 'deleter');
|
||||
$this->assertEqual($deleter->uuid(), $values_deleter['uuid']);
|
||||
$this->assertEqual($deleter->label(), $values_deleter['label']);
|
||||
|
||||
// The deletee was deleted in
|
||||
// \Drupal\config_test\Entity\ConfigTest::postSave().
|
||||
$this->assertFalse($entity_storage->load('deletee'));
|
||||
|
||||
$other = $entity_storage->load('other');
|
||||
$this->assertEqual($other->id(), 'other');
|
||||
$this->assertEqual($other->uuid(), $values_other['uuid']);
|
||||
$this->assertEqual($other->label(), $values_other['label']);
|
||||
|
||||
$logs = $this->configImporter->getErrors();
|
||||
$this->assertEqual(count($logs), 1);
|
||||
$this->assertEqual($logs[0], SafeMarkup::format('Update target "@name" is missing.', array('@name' => $name_deletee)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that secondary updates for deleted files work as expected.
|
||||
*
|
||||
* This test is completely hypothetical since we only support full
|
||||
* configuration tree imports. Therefore, any configuration updates that cause
|
||||
* secondary deletes should be reflected already in the staged configuration.
|
||||
*/
|
||||
function testSecondaryUpdateDeletedDeleteeFirst() {
|
||||
$name_deleter = 'config_test.dynamic.deleter';
|
||||
$name_deletee = 'config_test.dynamic.deletee';
|
||||
$storage = $this->container->get('config.storage');
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
$uuid = $this->container->get('uuid');
|
||||
|
||||
$values_deleter = array(
|
||||
'id' => 'deleter',
|
||||
'label' => 'Deleter',
|
||||
'weight' => 0,
|
||||
'uuid' => $uuid->generate(),
|
||||
// Add a dependency on deletee, to make sure that is synced first.
|
||||
'dependencies' => array(
|
||||
'config' => array($name_deletee),
|
||||
),
|
||||
);
|
||||
$storage->write($name_deleter, $values_deleter);
|
||||
$values_deleter['label'] = 'Updated Deleter';
|
||||
$staging->write($name_deleter, $values_deleter);
|
||||
$values_deletee = array(
|
||||
'id' => 'deletee',
|
||||
'label' => 'Deletee',
|
||||
'weight' => 0,
|
||||
'uuid' => $uuid->generate(),
|
||||
);
|
||||
$storage->write($name_deletee, $values_deletee);
|
||||
$values_deletee['label'] = 'Updated Deletee';
|
||||
$staging->write($name_deletee, $values_deletee);
|
||||
|
||||
// Import.
|
||||
$this->configImporter->reset()->import();
|
||||
|
||||
$entity_storage = \Drupal::entityManager()->getStorage('config_test');
|
||||
// Both entities are deleted. ConfigTest::postSave() causes updates of the
|
||||
// deleter entity to delete the deletee entity. Since the deleter depends on
|
||||
// the deletee, removing the deletee causes the deleter to be removed.
|
||||
$this->assertFalse($entity_storage->load('deleter'));
|
||||
$this->assertFalse($entity_storage->load('deletee'));
|
||||
$logs = $this->configImporter->getErrors();
|
||||
$this->assertEqual(count($logs), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that secondary deletes for deleted files work as expected.
|
||||
*/
|
||||
function testSecondaryDeletedDeleteeSecond() {
|
||||
$name_deleter = 'config_test.dynamic.deleter';
|
||||
$name_deletee = 'config_test.dynamic.deletee';
|
||||
$storage = $this->container->get('config.storage');
|
||||
|
||||
$uuid = $this->container->get('uuid');
|
||||
|
||||
$values_deleter = array(
|
||||
'id' => 'deleter',
|
||||
'label' => 'Deleter',
|
||||
'weight' => 0,
|
||||
'uuid' => $uuid->generate(),
|
||||
// Add a dependency on deletee, to make sure this delete is synced first.
|
||||
'dependencies' => array(
|
||||
'config' => array($name_deletee),
|
||||
),
|
||||
);
|
||||
$storage->write($name_deleter, $values_deleter);
|
||||
$values_deletee = array(
|
||||
'id' => 'deletee',
|
||||
'label' => 'Deletee',
|
||||
'weight' => 0,
|
||||
'uuid' => $uuid->generate(),
|
||||
);
|
||||
$storage->write($name_deletee, $values_deletee);
|
||||
|
||||
// Import.
|
||||
$this->configImporter->reset()->import();
|
||||
|
||||
$entity_storage = \Drupal::entityManager()->getStorage('config_test');
|
||||
$this->assertFalse($entity_storage->load('deleter'));
|
||||
$this->assertFalse($entity_storage->load('deletee'));
|
||||
// The deletee entity does not exist as the delete worked and although the
|
||||
// delete occurred in \Drupal\config_test\Entity\ConfigTest::postDelete()
|
||||
// this does not matter.
|
||||
$logs = $this->configImporter->getErrors();
|
||||
$this->assertEqual(count($logs), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests updating of configuration during import.
|
||||
*/
|
||||
function testUpdated() {
|
||||
$name = 'config_test.system';
|
||||
$dynamic_name = 'config_test.dynamic.dotted.default';
|
||||
$storage = $this->container->get('config.storage');
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
|
||||
// Verify that the configuration objects to import exist.
|
||||
$this->assertIdentical($storage->exists($name), TRUE, $name . ' found.');
|
||||
$this->assertIdentical($storage->exists($dynamic_name), TRUE, $dynamic_name . ' found.');
|
||||
|
||||
// Replace the file content of the existing configuration objects in the
|
||||
// staging directory.
|
||||
$original_name_data = array(
|
||||
'foo' => 'beer',
|
||||
);
|
||||
$staging->write($name, $original_name_data);
|
||||
$original_dynamic_data = $storage->read($dynamic_name);
|
||||
$original_dynamic_data['label'] = 'Updated';
|
||||
$staging->write($dynamic_name, $original_dynamic_data);
|
||||
|
||||
// Verify the active configuration still returns the default values.
|
||||
$config = $this->config($name);
|
||||
$this->assertIdentical($config->get('foo'), 'bar');
|
||||
$config = $this->config($dynamic_name);
|
||||
$this->assertIdentical($config->get('label'), 'Default');
|
||||
|
||||
// Import.
|
||||
$this->configImporter->reset()->import();
|
||||
|
||||
// Verify the values were updated.
|
||||
\Drupal::configFactory()->reset($name);
|
||||
$config = $this->config($name);
|
||||
$this->assertIdentical($config->get('foo'), 'beer');
|
||||
$config = $this->config($dynamic_name);
|
||||
$this->assertIdentical($config->get('label'), 'Updated');
|
||||
|
||||
// Verify that the original file content is still the same.
|
||||
$this->assertIdentical($staging->read($name), $original_name_data);
|
||||
$this->assertIdentical($staging->read($dynamic_name), $original_dynamic_data);
|
||||
|
||||
// Verify that appropriate module API hooks have been invoked.
|
||||
$this->assertTrue(isset($GLOBALS['hook_config_test']['load']));
|
||||
$this->assertTrue(isset($GLOBALS['hook_config_test']['presave']));
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['insert']));
|
||||
$this->assertTrue(isset($GLOBALS['hook_config_test']['update']));
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['predelete']));
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['delete']));
|
||||
|
||||
// Verify that there is nothing more to import.
|
||||
$this->assertFalse($this->configImporter->hasUnprocessedConfigurationChanges());
|
||||
$logs = $this->configImporter->getErrors();
|
||||
$this->assertEqual(count($logs), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the isInstallable method()
|
||||
*/
|
||||
function testIsInstallable() {
|
||||
$config_name = 'config_test.dynamic.isinstallable';
|
||||
$this->assertFalse($this->container->get('config.storage')->exists($config_name));
|
||||
\Drupal::state()->set('config_test.isinstallable', TRUE);
|
||||
$this->installConfig(array('config_test'));
|
||||
$this->assertTrue($this->container->get('config.storage')->exists($config_name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests dependency validation during configuration import.
|
||||
*
|
||||
* @see \Drupal\Core\EventSubscriber\ConfigImportSubscriber
|
||||
* @see \Drupal\Core\Config\ConfigImporter::createExtensionChangelist()
|
||||
*/
|
||||
public function testUnmetDependency() {
|
||||
$storage = $this->container->get('config.storage');
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
|
||||
// Test an unknown configuration owner.
|
||||
$staging->write('unknown.config', ['test' => 'test']);
|
||||
|
||||
// Make a config entity have unmet dependencies.
|
||||
$config_entity_data = $staging->read('config_test.dynamic.dotted.default');
|
||||
$config_entity_data['dependencies'] = ['module' => ['unknown']];
|
||||
$staging->write('config_test.dynamic.dotted.module', $config_entity_data);
|
||||
$config_entity_data['dependencies'] = ['theme' => ['unknown']];
|
||||
$staging->write('config_test.dynamic.dotted.theme', $config_entity_data);
|
||||
$config_entity_data['dependencies'] = ['config' => ['unknown']];
|
||||
$staging->write('config_test.dynamic.dotted.config', $config_entity_data);
|
||||
|
||||
// Make an active config depend on something that is missing in staging.
|
||||
// The whole configuration needs to be consistent, not only the updated one.
|
||||
$config_entity_data['dependencies'] = [];
|
||||
$storage->write('config_test.dynamic.dotted.deleted', $config_entity_data);
|
||||
$config_entity_data['dependencies'] = ['config' => ['config_test.dynamic.dotted.deleted']];
|
||||
$storage->write('config_test.dynamic.dotted.existing', $config_entity_data);
|
||||
$staging->write('config_test.dynamic.dotted.existing', $config_entity_data);
|
||||
|
||||
$extensions = $staging->read('core.extension');
|
||||
// Add a module and a theme that do not exist.
|
||||
$extensions['module']['unknown_module'] = 0;
|
||||
$extensions['theme']['unknown_theme'] = 0;
|
||||
// Add a module and a theme that depend on uninstalled extensions.
|
||||
$extensions['module']['book'] = 0;
|
||||
$extensions['theme']['bartik'] = 0;
|
||||
|
||||
$staging->write('core.extension', $extensions);
|
||||
try {
|
||||
$this->configImporter->reset()->import();
|
||||
$this->fail('ConfigImporterException not thrown; an invalid import was not stopped due to missing dependencies.');
|
||||
}
|
||||
catch (ConfigImporterException $e) {
|
||||
$this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.');
|
||||
$error_log = $this->configImporter->getErrors();
|
||||
$expected = [
|
||||
'Unable to install the <em class="placeholder">unknown_module</em> module since it does not exist.',
|
||||
'Unable to install the <em class="placeholder">Book</em> module since it requires the <em class="placeholder">Node, Text, Field, Filter, User, Entity Reference</em> modules.',
|
||||
'Unable to install the <em class="placeholder">unknown_theme</em> theme since it does not exist.',
|
||||
'Unable to install the <em class="placeholder">Bartik</em> theme since it requires the <em class="placeholder">Classy</em> theme.',
|
||||
'Configuration <em class="placeholder">config_test.dynamic.dotted.config</em> depends on the <em class="placeholder">unknown</em> configuration that will not exist after import.',
|
||||
'Configuration <em class="placeholder">config_test.dynamic.dotted.existing</em> depends on the <em class="placeholder">config_test.dynamic.dotted.deleted</em> configuration that will not exist after import.',
|
||||
'Configuration <em class="placeholder">config_test.dynamic.dotted.module</em> depends on the <em class="placeholder">unknown</em> module that will not be installed after import.',
|
||||
'Configuration <em class="placeholder">config_test.dynamic.dotted.theme</em> depends on the <em class="placeholder">unknown</em> theme that will not be installed after import.',
|
||||
'Configuration <em class="placeholder">unknown.config</em> depends on the <em class="placeholder">unknown</em> extension that will not be installed after import.',
|
||||
];
|
||||
foreach ($expected as $expected_message) {
|
||||
$this->assertTrue(in_array($expected_message, $error_log), $expected_message);
|
||||
}
|
||||
}
|
||||
|
||||
// Make a config entity have mulitple unmet dependencies.
|
||||
$config_entity_data = $staging->read('config_test.dynamic.dotted.default');
|
||||
$config_entity_data['dependencies'] = ['module' => ['unknown', 'dblog']];
|
||||
$staging->write('config_test.dynamic.dotted.module', $config_entity_data);
|
||||
$config_entity_data['dependencies'] = ['theme' => ['unknown', 'seven']];
|
||||
$staging->write('config_test.dynamic.dotted.theme', $config_entity_data);
|
||||
$config_entity_data['dependencies'] = ['config' => ['unknown', 'unknown2']];
|
||||
$staging->write('config_test.dynamic.dotted.config', $config_entity_data);
|
||||
try {
|
||||
$this->configImporter->reset()->import();
|
||||
$this->fail('ConfigImporterException not thrown, invalid import was not stopped due to missing dependencies.');
|
||||
}
|
||||
catch (ConfigImporterException $e) {
|
||||
$this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.');
|
||||
$error_log = $this->configImporter->getErrors();
|
||||
$expected = [
|
||||
'Configuration <em class="placeholder">config_test.dynamic.dotted.config</em> depends on configuration (<em class="placeholder">unknown, unknown2</em>) that will not exist after import.',
|
||||
'Configuration <em class="placeholder">config_test.dynamic.dotted.module</em> depends on modules (<em class="placeholder">unknown, Database Logging</em>) that will not be installed after import.',
|
||||
'Configuration <em class="placeholder">config_test.dynamic.dotted.theme</em> depends on themes (<em class="placeholder">unknown, Seven</em>) that will not be installed after import.',
|
||||
];
|
||||
foreach ($expected as $expected_message) {
|
||||
$this->assertTrue(in_array($expected_message, $error_log), $expected_message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests missing core.extension during configuration import.
|
||||
*
|
||||
* @see \Drupal\Core\EventSubscriber\ConfigImportSubscriber
|
||||
*/
|
||||
public function testMissingCoreExtension() {
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
$staging->delete('core.extension');
|
||||
try {
|
||||
$this->configImporter->reset()->import();
|
||||
$this->fail('ConfigImporterException not thrown, invalid import was not stopped due to missing dependencies.');
|
||||
}
|
||||
catch (ConfigImporterException $e) {
|
||||
$this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.');
|
||||
$error_log = $this->configImporter->getErrors();
|
||||
$this->assertEqual(['The core.extension configuration does not exist.'], $error_log);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests install profile validation during configuration import.
|
||||
*
|
||||
* @see \Drupal\Core\EventSubscriber\ConfigImportSubscriber
|
||||
*/
|
||||
public function testInstallProfile() {
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
|
||||
$extensions = $staging->read('core.extension');
|
||||
// Add an install profile.
|
||||
$extensions['module']['standard'] = 0;
|
||||
|
||||
$staging->write('core.extension', $extensions);
|
||||
try {
|
||||
$this->configImporter->reset()->import();
|
||||
$this->fail('ConfigImporterException not thrown; an invalid import was not stopped due to missing dependencies.');
|
||||
}
|
||||
catch (ConfigImporterException $e) {
|
||||
$this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.');
|
||||
$error_log = $this->configImporter->getErrors();
|
||||
// Install profiles should not even be scanned at this point.
|
||||
$this->assertEqual(['Unable to install the <em class="placeholder">standard</em> module since it does not exist.'], $error_log);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigInstallProfileOverrideTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Core\Config\InstallStorage;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Core\Config\FileStorage;
|
||||
use Drupal\system\Entity\Action;
|
||||
use Drupal\tour\Entity\Tour;
|
||||
|
||||
/**
|
||||
* Tests installation and removal of configuration objects in install, disable
|
||||
* and uninstall functionality.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigInstallProfileOverrideTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* The profile to install as a basis for testing.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $profile = 'testing_config_overrides';
|
||||
|
||||
|
||||
/**
|
||||
* Tests install profile config changes.
|
||||
*/
|
||||
function testInstallProfileConfigOverwrite() {
|
||||
$config_name = 'system.cron';
|
||||
// The expected configuration from the system module.
|
||||
$expected_original_data = array(
|
||||
'threshold' => array(
|
||||
'autorun' => 0,
|
||||
'requirements_warning' => 172800,
|
||||
'requirements_error' => 1209600,
|
||||
),
|
||||
);
|
||||
// The expected active configuration altered by the install profile.
|
||||
$expected_profile_data = array(
|
||||
'threshold' => array(
|
||||
'autorun' => 0,
|
||||
'requirements_warning' => 259200,
|
||||
'requirements_error' => 1209600,
|
||||
),
|
||||
);
|
||||
|
||||
// Verify that the original data matches. We have to read the module config
|
||||
// file directly, because the install profile default system.cron.yml
|
||||
// configuration file was used to create the active configuration.
|
||||
$config_dir = drupal_get_path('module', 'system') . '/'. InstallStorage::CONFIG_INSTALL_DIRECTORY;
|
||||
$this->assertTrue(is_dir($config_dir));
|
||||
$source_storage = new FileStorage($config_dir);
|
||||
$data = $source_storage->read($config_name);
|
||||
$this->assertIdentical($data, $expected_original_data);
|
||||
|
||||
// Verify that active configuration matches the expected data, which was
|
||||
// created from the testing install profile's system.cron.yml file.
|
||||
$config = $this->config($config_name);
|
||||
$this->assertIdentical($config->get(), $expected_profile_data);
|
||||
|
||||
// Ensure that the configuration entity has the expected dependencies and
|
||||
// overrides.
|
||||
$action = Action::load('user_block_user_action');
|
||||
$this->assertEqual($action->label(), 'Overridden block the selected user(s)');
|
||||
$action = Action::load('user_cancel_user_action');
|
||||
$this->assertEqual($action->label(), 'Cancel the selected user account(s)', 'Default configuration that is not overridden is not affected.');
|
||||
|
||||
// Ensure that optional configuration can be overridden.
|
||||
$tour = Tour::load('language');
|
||||
$this->assertEqual(count($tour->getTips()), 1, 'Optional configuration can be overridden. The language tour only has one tip');
|
||||
$tour = Tour::load('language-add');
|
||||
$this->assertEqual(count($tour->getTips()), 3, 'Optional configuration that is not overridden is not affected.');
|
||||
|
||||
// Ensure that optional configuration from a profile is created if
|
||||
// dependencies are met.
|
||||
$this->assertEqual(Tour::load('testing_config_overrides')->label(), 'Config override test');
|
||||
|
||||
// Ensure that optional configuration from a profile is not created if
|
||||
// dependencies are not met. Cannot use the entity system since the entity
|
||||
// type does not exist.
|
||||
$optional_dir = drupal_get_path('module', 'testing_config_overrides') . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY;
|
||||
$optional_storage = new FileStorage($optional_dir);
|
||||
foreach (['config_test.dynamic.dotted.default', 'config_test.dynamic.override','config_test.dynamic.override_unmet'] as $id) {
|
||||
$this->assertTrue(\Drupal::config($id)->isNew(), "The config_test entity $id contained in the profile's optional directory does not exist.");
|
||||
// Make that we don't get false positives from the assertion above.
|
||||
$this->assertTrue($optional_storage->exists($id), "The config_test entity $id does exist in the profile's optional directory.");
|
||||
}
|
||||
|
||||
// Install the config_test module and ensure that the override from the
|
||||
// install profile is not used. Optional configuration can not override
|
||||
// configuration in a modules config/install directory.
|
||||
$this->container->get('module_installer')->install(['config_test']);
|
||||
$this->rebuildContainer();
|
||||
$config_test_storage = \Drupal::entityManager()->getStorage('config_test');
|
||||
$this->assertEqual($config_test_storage->load('dotted.default')->label(), 'Default', 'The config_test entity is not overridden by the profile optional configuration.');
|
||||
// Test that override of optional configuration does work.
|
||||
$this->assertEqual($config_test_storage->load('override')->label(), 'Override', 'The optional config_test entity is overridden by the profile optional configuration.');
|
||||
// Test that override of optional configuration which introduces an unmet
|
||||
// dependency does not get created.
|
||||
$this->assertNull($config_test_storage->load('override_unmet'), 'The optional config_test entity with unmet dependencies is not created.');
|
||||
|
||||
// Installing dblog creates the optional configuration.
|
||||
$this->container->get('module_installer')->install(['dblog']);
|
||||
$this->rebuildContainer();
|
||||
$this->assertEqual($config_test_storage->load('override_unmet')->label(), 'Override', 'The optional config_test entity is overridden by the profile optional configuration and is installed when its dependencies are met.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigInstallProfileUnmetDependenciesTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Component\Serialization\Yaml;
|
||||
use Drupal\Core\Config\InstallStorage;
|
||||
use Drupal\simpletest\InstallerTestBase;
|
||||
|
||||
/**
|
||||
* Tests install profile config overrides can not add unmet dependencies.
|
||||
*
|
||||
* @group Config
|
||||
*/
|
||||
class ConfigInstallProfileUnmetDependenciesTest extends InstallerTestBase {
|
||||
|
||||
/**
|
||||
* The installation profile to install.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $profile = 'testing_config_overrides';
|
||||
|
||||
/**
|
||||
* Set to TRUE if the expected exception is thrown.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $expectedException = FALSE;
|
||||
|
||||
protected function setUp() {
|
||||
// Copy the testing_config_overrides install profile so we can change the
|
||||
// configuration to include a dependency that can not be met. File API
|
||||
// functions are not available yet.
|
||||
$dest = $this->siteDirectory . '/profiles/testing_config_overrides';
|
||||
mkdir($dest, 0777, TRUE);
|
||||
$source = DRUPAL_ROOT . '/core/profiles/testing_config_overrides';
|
||||
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST);
|
||||
foreach ($iterator as $item) {
|
||||
if ($item->isDir()) {
|
||||
mkdir($dest . DIRECTORY_SEPARATOR . $iterator->getSubPathName());
|
||||
}
|
||||
else {
|
||||
copy($item, $dest . DIRECTORY_SEPARATOR . $iterator->getSubPathName());
|
||||
}
|
||||
}
|
||||
|
||||
// Add a dependency that can not be met because User is installed before
|
||||
// Action.
|
||||
$config_file = $dest . DIRECTORY_SEPARATOR . InstallStorage::CONFIG_INSTALL_DIRECTORY . DIRECTORY_SEPARATOR . 'system.action.user_block_user_action.yml';
|
||||
$action = Yaml::decode(file_get_contents($config_file));
|
||||
$action['dependencies']['module'][] = 'action';
|
||||
file_put_contents($config_file, Yaml::encode($action));
|
||||
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Override the error method so we can test for the expected exception.
|
||||
*/
|
||||
protected function error($message = '', $group = 'Other', array $caller = NULL) {
|
||||
if ($group == 'User notice') {
|
||||
// Since 'User notice' is set by trigger_error() which is used for debug
|
||||
// set the message to a status of 'debug'.
|
||||
return $this->assert('debug', $message, 'Debug', $caller);
|
||||
}
|
||||
if ($group == 'Drupal\Core\Config\UnmetDependenciesException') {
|
||||
$this->expectedException = TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
return $this->assert('exception', $message, $group, $caller);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpSite() {
|
||||
// This step is not reached due to the exception.
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Confirms that the installation succeeded.
|
||||
*/
|
||||
public function testInstalled() {
|
||||
if ($this->expectedException) {
|
||||
$this->pass('Expected Drupal\Core\Config\UnmetDependenciesException exception thrown');
|
||||
}
|
||||
else {
|
||||
$this->fail('Expected Drupal\Core\Config\UnmetDependenciesException exception thrown');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
251
core/modules/config/src/Tests/ConfigInstallTest.php
Normal file
251
core/modules/config/src/Tests/ConfigInstallTest.php
Normal file
|
@ -0,0 +1,251 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigInstallTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Core\Config\InstallStorage;
|
||||
use Drupal\Core\Config\PreExistingConfigException;
|
||||
use Drupal\Core\Config\StorageInterface;
|
||||
use Drupal\Core\Config\UnmetDependenciesException;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests installation of configuration objects in installation functionality.
|
||||
*
|
||||
* @group config
|
||||
* @see \Drupal\Core\Config\ConfigInstaller
|
||||
*/
|
||||
class ConfigInstallTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['system'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installSchema('system', ['router']);
|
||||
|
||||
// Ensure the global variable being asserted by this test does not exist;
|
||||
// a previous test executed in this request/process might have set it.
|
||||
unset($GLOBALS['hook_config_test']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests module installation.
|
||||
*/
|
||||
function testModuleInstallation() {
|
||||
$default_config = 'config_test.system';
|
||||
$default_configuration_entity = 'config_test.dynamic.dotted.default';
|
||||
|
||||
// Verify that default module config does not exist before installation yet.
|
||||
$config = $this->config($default_config);
|
||||
$this->assertIdentical($config->isNew(), TRUE);
|
||||
$config = $this->config($default_configuration_entity);
|
||||
$this->assertIdentical($config->isNew(), TRUE);
|
||||
|
||||
// Ensure that schema provided by modules that are not installed is not
|
||||
// available.
|
||||
$this->assertFalse(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.someschema'), 'Configuration schema for config_schema_test.someschema does not exist.');
|
||||
|
||||
// Install the test module.
|
||||
$this->installModules(array('config_test'));
|
||||
|
||||
// Verify that default module config exists.
|
||||
\Drupal::configFactory()->reset($default_config);
|
||||
\Drupal::configFactory()->reset($default_configuration_entity);
|
||||
$config = $this->config($default_config);
|
||||
$this->assertIdentical($config->isNew(), FALSE);
|
||||
$config = $this->config($default_configuration_entity);
|
||||
$this->assertIdentical($config->isNew(), FALSE);
|
||||
|
||||
// Verify that config_test API hooks were invoked for the dynamic default
|
||||
// configuration entity.
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['load']));
|
||||
$this->assertTrue(isset($GLOBALS['hook_config_test']['presave']));
|
||||
$this->assertTrue(isset($GLOBALS['hook_config_test']['insert']));
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['update']));
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['predelete']));
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['delete']));
|
||||
|
||||
// Install the schema test module.
|
||||
$this->enableModules(array('config_schema_test'));
|
||||
$this->installConfig(array('config_schema_test'));
|
||||
|
||||
// After module installation the new schema should exist.
|
||||
$this->assertTrue(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.someschema'), 'Configuration schema for config_schema_test.someschema exists.');
|
||||
|
||||
// Test that uninstalling configuration removes configuration schema.
|
||||
$this->config('core.extension')->set('module', array())->save();
|
||||
\Drupal::service('config.manager')->uninstall('module', 'config_test');
|
||||
$this->assertFalse(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.someschema'), 'Configuration schema for config_schema_test.someschema does not exist.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that collections are ignored if the event does not return anything.
|
||||
*/
|
||||
public function testCollectionInstallationNoCollections() {
|
||||
// Install the test module.
|
||||
$this->enableModules(array('config_collection_install_test'));
|
||||
$this->installConfig(array('config_collection_install_test'));
|
||||
/** @var \Drupal\Core\Config\StorageInterface $active_storage */
|
||||
$active_storage = \Drupal::service('config.storage');
|
||||
$this->assertEqual(array(), $active_storage->getAllCollectionNames());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests config objects in collections are installed as expected.
|
||||
*/
|
||||
public function testCollectionInstallationCollections() {
|
||||
$collections = array(
|
||||
'another_collection',
|
||||
'collection.test1',
|
||||
'collection.test2',
|
||||
);
|
||||
// Set the event listener to return three possible collections.
|
||||
// @see \Drupal\config_collection_install_test\EventSubscriber
|
||||
\Drupal::state()->set('config_collection_install_test.collection_names', $collections);
|
||||
// Install the test module.
|
||||
$this->enableModules(array('config_collection_install_test'));
|
||||
$this->installConfig(array('config_collection_install_test'));
|
||||
/** @var \Drupal\Core\Config\StorageInterface $active_storage */
|
||||
$active_storage = \Drupal::service('config.storage');
|
||||
$this->assertEqual($collections, $active_storage->getAllCollectionNames());
|
||||
foreach ($collections as $collection) {
|
||||
$collection_storage = $active_storage->createCollection($collection);
|
||||
$data = $collection_storage->read('config_collection_install_test.test');
|
||||
$this->assertEqual($collection, $data['collection']);
|
||||
}
|
||||
|
||||
// Tests that clashing configuration in collections is detected.
|
||||
try {
|
||||
\Drupal::service('module_installer')->install(['config_collection_clash_install_test']);
|
||||
$this->fail('Expected PreExistingConfigException not thrown.');
|
||||
}
|
||||
catch (PreExistingConfigException $e) {
|
||||
$this->assertEqual($e->getExtension(), 'config_collection_clash_install_test');
|
||||
$this->assertEqual($e->getConfigObjects(), [
|
||||
'another_collection' => ['config_collection_install_test.test'],
|
||||
'collection.test1' => ['config_collection_install_test.test'],
|
||||
'collection.test2' => ['config_collection_install_test.test'],
|
||||
]);
|
||||
$this->assertEqual($e->getMessage(), 'Configuration objects (another_collection/config_collection_install_test.test, collection/test1/config_collection_install_test.test, collection/test2/config_collection_install_test.test) provided by config_collection_clash_install_test already exist in active configuration');
|
||||
}
|
||||
|
||||
// Test that the we can use the config installer to install all the
|
||||
// available default configuration in a particular collection for enabled
|
||||
// extensions.
|
||||
\Drupal::service('config.installer')->installCollectionDefaultConfig('entity');
|
||||
// The 'entity' collection will not exist because the 'config_test' module
|
||||
// is not enabled.
|
||||
$this->assertEqual($collections, $active_storage->getAllCollectionNames());
|
||||
// Enable the 'config_test' module and try again.
|
||||
$this->enableModules(array('config_test'));
|
||||
\Drupal::service('config.installer')->installCollectionDefaultConfig('entity');
|
||||
$collections[] = 'entity';
|
||||
$this->assertEqual($collections, $active_storage->getAllCollectionNames());
|
||||
$collection_storage = $active_storage->createCollection('entity');
|
||||
$data = $collection_storage->read('config_test.dynamic.dotted.default');
|
||||
$this->assertIdentical(array('label' => 'entity'), $data);
|
||||
|
||||
// Test that the config manager uninstalls configuration from collections
|
||||
// as expected.
|
||||
\Drupal::service('config.manager')->uninstall('module', 'config_collection_install_test');
|
||||
$this->assertEqual(array('entity'), $active_storage->getAllCollectionNames());
|
||||
\Drupal::service('config.manager')->uninstall('module', 'config_test');
|
||||
$this->assertEqual(array(), $active_storage->getAllCollectionNames());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests collections which do not support config entities install correctly.
|
||||
*
|
||||
* Config entity detection during config installation is done by matching
|
||||
* config name prefixes. If a collection provides a configuration with a
|
||||
* matching name but does not support config entities it should be created
|
||||
* using simple configuration.
|
||||
*/
|
||||
public function testCollectionInstallationCollectionConfigEntity() {
|
||||
$collections = array(
|
||||
'entity',
|
||||
);
|
||||
\Drupal::state()->set('config_collection_install_test.collection_names', $collections);
|
||||
// Install the test module.
|
||||
$this->installModules(array('config_test', 'config_collection_install_test'));
|
||||
/** @var \Drupal\Core\Config\StorageInterface $active_storage */
|
||||
$active_storage = \Drupal::service('config.storage');
|
||||
$this->assertEqual($collections, $active_storage->getAllCollectionNames());
|
||||
$collection_storage = $active_storage->createCollection('entity');
|
||||
|
||||
// The config_test.dynamic.dotted.default configuration object saved in the
|
||||
// active store should be a configuration entity complete with UUID. Because
|
||||
// the entity collection does not support configuration entities the
|
||||
// configuration object stored there with the same name should only contain
|
||||
// a label.
|
||||
$name = 'config_test.dynamic.dotted.default';
|
||||
$data = $active_storage->read($name);
|
||||
$this->assertTrue(isset($data['uuid']));
|
||||
$data = $collection_storage->read($name);
|
||||
$this->assertIdentical(array('label' => 'entity'), $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the configuration with unmet dependencies is not installed.
|
||||
*/
|
||||
public function testDependencyChecking() {
|
||||
$this->installModules(['config_test']);
|
||||
try {
|
||||
$this->installModules(['config_install_dependency_test']);
|
||||
$this->fail('Expected UnmetDependenciesException not thrown.');
|
||||
}
|
||||
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->installModules(['config_other_module_config_test']);
|
||||
$this->installModules(['config_install_dependency_test']);
|
||||
$this->assertTrue(entity_load('config_test', 'other_module_test_with_dependency'), 'The config_test.dynamic.other_module_test_with_dependency configuration has been created during install.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests imported configuration entities with and without language information.
|
||||
*/
|
||||
function testLanguage() {
|
||||
$this->installModules(['config_test_language']);
|
||||
// Test imported configuration with implicit language code.
|
||||
$storage = new InstallStorage();
|
||||
$data = $storage->read('config_test.dynamic.dotted.english');
|
||||
$this->assertTrue(!isset($data['langcode']));
|
||||
$this->assertEqual(
|
||||
$this->config('config_test.dynamic.dotted.english')->get('langcode'),
|
||||
'en'
|
||||
);
|
||||
|
||||
// Test imported configuration with explicit language code.
|
||||
$data = $storage->read('config_test.dynamic.dotted.french');
|
||||
$this->assertEqual($data['langcode'], 'fr');
|
||||
$this->assertEqual(
|
||||
$this->config('config_test.dynamic.dotted.french')->get('langcode'),
|
||||
'fr'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs a module.
|
||||
*
|
||||
* @param array $modules
|
||||
* The module names.
|
||||
*/
|
||||
protected function installModules(array $modules) {
|
||||
$this->container->get('module_installer')->install($modules);
|
||||
$this->container = \Drupal::getContainer();
|
||||
}
|
||||
|
||||
}
|
195
core/modules/config/src/Tests/ConfigInstallWebTest.php
Normal file
195
core/modules/config/src/Tests/ConfigInstallWebTest.php
Normal file
|
@ -0,0 +1,195 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigInstallWebTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Core\Config\PreExistingConfigException;
|
||||
use Drupal\Core\Config\StorageInterface;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests installation and removal of configuration objects in install, disable
|
||||
* and uninstall functionality.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigInstallWebTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* The admin user used in this test.
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->adminUser = $this->drupalCreateUser(array('administer modules', 'administer themes'));
|
||||
|
||||
// Ensure the global variable being asserted by this test does not exist;
|
||||
// a previous test executed in this request/process might have set it.
|
||||
unset($GLOBALS['hook_config_test']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests module re-installation.
|
||||
*/
|
||||
function testIntegrationModuleReinstallation() {
|
||||
$default_config = 'config_integration_test.settings';
|
||||
$default_configuration_entity = 'config_test.dynamic.config_integration_test';
|
||||
|
||||
// Install the config_test module we're integrating with.
|
||||
\Drupal::service('module_installer')->install(array('config_test'));
|
||||
|
||||
// Verify the configuration does not exist prior to installation.
|
||||
$config_static = $this->config($default_config);
|
||||
$this->assertIdentical($config_static->isNew(), TRUE);
|
||||
$config_entity = $this->config($default_configuration_entity);
|
||||
$this->assertIdentical($config_entity->isNew(), TRUE);
|
||||
|
||||
// Install the integration module.
|
||||
\Drupal::service('module_installer')->install(array('config_integration_test'));
|
||||
|
||||
// Verify that default module config exists.
|
||||
\Drupal::configFactory()->reset($default_config);
|
||||
\Drupal::configFactory()->reset($default_configuration_entity);
|
||||
$config_static = $this->config($default_config);
|
||||
$this->assertIdentical($config_static->isNew(), FALSE);
|
||||
$this->assertIdentical($config_static->get('foo'), 'default setting');
|
||||
$config_entity = $this->config($default_configuration_entity);
|
||||
$this->assertIdentical($config_entity->isNew(), FALSE);
|
||||
$this->assertIdentical($config_entity->get('label'), 'Default integration config label');
|
||||
|
||||
// Customize both configuration objects.
|
||||
$config_static->set('foo', 'customized setting')->save();
|
||||
$config_entity->set('label', 'Customized integration config label')->save();
|
||||
|
||||
// @todo FIXME: Setting config keys WITHOUT SAVING retains the changed config
|
||||
// object in memory. Every new call to $this->config() MUST revert in-memory changes
|
||||
// that haven't been saved!
|
||||
// In other words: This test passes even without this reset, but it shouldn't.
|
||||
$this->container->get('config.factory')->reset();
|
||||
|
||||
// Disable and uninstall the integration module.
|
||||
$this->container->get('module_installer')->uninstall(array('config_integration_test'));
|
||||
|
||||
// Verify the integration module's config was uninstalled.
|
||||
$config_static = $this->config($default_config);
|
||||
$this->assertIdentical($config_static->isNew(), TRUE);
|
||||
|
||||
// Verify the integration config still exists.
|
||||
$config_entity = $this->config($default_configuration_entity);
|
||||
$this->assertIdentical($config_entity->isNew(), FALSE);
|
||||
$this->assertIdentical($config_entity->get('label'), 'Customized integration config label');
|
||||
|
||||
// Reinstall the integration module.
|
||||
try {
|
||||
\Drupal::service('module_installer')->install(array('config_integration_test'));
|
||||
$this->fail('Expected PreExistingConfigException not thrown.');
|
||||
}
|
||||
catch (PreExistingConfigException $e) {
|
||||
$this->assertEqual($e->getExtension(), 'config_integration_test');
|
||||
$this->assertEqual($e->getConfigObjects(), [StorageInterface::DEFAULT_COLLECTION => ['config_test.dynamic.config_integration_test']]);
|
||||
$this->assertEqual($e->getMessage(), 'Configuration objects (config_test.dynamic.config_integration_test) provided by config_integration_test already exist in active configuration');
|
||||
}
|
||||
|
||||
// Delete the configuration entity so that the install will work.
|
||||
$config_entity->delete();
|
||||
\Drupal::service('module_installer')->install(array('config_integration_test'));
|
||||
|
||||
// Verify the integration module's config was re-installed.
|
||||
\Drupal::configFactory()->reset($default_config);
|
||||
\Drupal::configFactory()->reset($default_configuration_entity);
|
||||
$config_static = $this->config($default_config);
|
||||
$this->assertIdentical($config_static->isNew(), FALSE);
|
||||
$this->assertIdentical($config_static->get('foo'), 'default setting');
|
||||
|
||||
// Verify the integration config is using the default.
|
||||
$config_entity = \Drupal::config($default_configuration_entity);
|
||||
$this->assertIdentical($config_entity->isNew(), FALSE);
|
||||
$this->assertIdentical($config_entity->get('label'), 'Default integration config label');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests pre-existing configuration detection.
|
||||
*/
|
||||
public function testPreExistingConfigInstall() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Try to install config_install_fail_test and config_test. Doing this
|
||||
// will install the config_test module first because it is a dependency of
|
||||
// config_install_fail_test.
|
||||
// @see \Drupal\system\Form\ModulesListForm::submitForm()
|
||||
$this->drupalPostForm('admin/modules', array('modules[Testing][config_test][enable]' => TRUE, 'modules[Testing][config_install_fail_test][enable]' => TRUE), t('Save configuration'));
|
||||
$this->assertRaw('Unable to install Configuration install fail test, <em class="placeholder">config_test.dynamic.dotted.default</em> already exists in active configuration.');
|
||||
|
||||
// Uninstall the config_test module to test the confirm form.
|
||||
$this->drupalPostForm('admin/modules/uninstall', array('uninstall[config_test]' => TRUE), t('Uninstall'));
|
||||
$this->drupalPostForm(NULL, array(), t('Uninstall'));
|
||||
|
||||
// Try to install config_install_fail_test without selecting config_test.
|
||||
// The user is shown a confirm form because the config_test module is a
|
||||
// dependency.
|
||||
// @see \Drupal\system\Form\ModulesListConfirmForm::submitForm()
|
||||
$this->drupalPostForm('admin/modules', array('modules[Testing][config_install_fail_test][enable]' => TRUE), t('Save configuration'));
|
||||
$this->drupalPostForm(NULL, array(), t('Continue'));
|
||||
$this->assertRaw('Unable to install Configuration install fail test, <em class="placeholder">config_test.dynamic.dotted.default</em> already exists in active configuration.');
|
||||
|
||||
// Test that collection configuration clashes during a module install are
|
||||
// reported correctly.
|
||||
\Drupal::service('module_installer')->install(['language']);
|
||||
$this->rebuildContainer();
|
||||
ConfigurableLanguage::createFromLangcode('fr')->save();
|
||||
\Drupal::languageManager()
|
||||
->getLanguageConfigOverride('fr', 'config_test.dynamic.dotted.default')
|
||||
->set('label', 'Je suis Charlie')
|
||||
->save();
|
||||
|
||||
$this->drupalPostForm('admin/modules', array('modules[Testing][config_install_fail_test][enable]' => TRUE), t('Save configuration'));
|
||||
$this->assertRaw('Unable to install Configuration install fail test, <em class="placeholder">config_test.dynamic.dotted.default, language/fr/config_test.dynamic.dotted.default</em> already exist in active configuration.');
|
||||
|
||||
// Test installing a theme through the UI that has existing configuration.
|
||||
// This relies on the fact the config_test has been installed and created
|
||||
// the config_test.dynamic.dotted.default configuration and the translation
|
||||
// override created still exists.
|
||||
$this->drupalGet('admin/appearance');
|
||||
$url = $this->xpath("//a[contains(@href,'config_clash_test_theme') and contains(@href,'/install?')]/@href")[0];
|
||||
$this->drupalGet($this->getAbsoluteUrl($url));
|
||||
$this->assertRaw('Unable to install config_clash_test_theme, <em class="placeholder">config_test.dynamic.dotted.default, language/fr/config_test.dynamic.dotted.default</em> already exist in active configuration.');
|
||||
|
||||
// Test installing a theme through the API that has existing configuration.
|
||||
try {
|
||||
\Drupal::service('theme_handler')->install(['config_clash_test_theme']);
|
||||
$this->fail('Expected PreExistingConfigException not thrown.');
|
||||
}
|
||||
catch (PreExistingConfigException $e) {
|
||||
$this->assertEqual($e->getExtension(), 'config_clash_test_theme');
|
||||
$this->assertEqual($e->getConfigObjects(), [StorageInterface::DEFAULT_COLLECTION => ['config_test.dynamic.dotted.default'], 'language.fr' => ['config_test.dynamic.dotted.default']]);
|
||||
$this->assertEqual($e->getMessage(), 'Configuration objects (config_test.dynamic.dotted.default, language/fr/config_test.dynamic.dotted.default) provided by config_clash_test_theme already exist in active configuration');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests unmet dependencies detection.
|
||||
*/
|
||||
public function testUnmetDependenciesInstall() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
// We need to install separately since config_install_dependency_test does
|
||||
// not depend on config_test and order is important.
|
||||
$this->drupalPostForm('admin/modules', array('modules[Testing][config_test][enable]' => TRUE), t('Save configuration'));
|
||||
$this->drupalPostForm('admin/modules', array('modules[Testing][config_install_dependency_test][enable]' => TRUE), t('Save configuration'));
|
||||
$this->assertRaw('Unable to install Config install dependency test, <em class="placeholder">config_test.dynamic.other_module_test_with_dependency</em> has unmet dependencies.');
|
||||
|
||||
$this->drupalPostForm('admin/modules', array('modules[Testing][config_other_module_config_test][enable]' => TRUE), t('Save configuration'));
|
||||
$this->drupalPostForm('admin/modules', array('modules[Testing][config_install_dependency_test][enable]' => TRUE), t('Save configuration'));
|
||||
$this->rebuildContainer();
|
||||
$this->assertTrue(entity_load('config_test', 'other_module_test_with_dependency'), 'The config_test.dynamic.other_module_test_with_dependency configuration has been created during install.');
|
||||
}
|
||||
}
|
125
core/modules/config/src/Tests/ConfigLanguageOverrideTest.php
Normal file
125
core/modules/config/src/Tests/ConfigLanguageOverrideTest.php
Normal file
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigLanguageOverrideTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Confirm that language overrides work.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigLanguageOverrideTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('user', 'language', 'config_test', 'system', 'field');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installConfig(array('config_test'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests locale override based on language.
|
||||
*/
|
||||
function testConfigLanguageOverride() {
|
||||
// The language module implements a config factory override object that
|
||||
// overrides configuration when the Language module is enabled. This test ensures that
|
||||
// English overrides work.
|
||||
\Drupal::languageManager()->setConfigOverrideLanguage(\Drupal::languageManager()->getLanguage('en'));
|
||||
$config = \Drupal::config('config_test.system');
|
||||
$this->assertIdentical($config->get('foo'), 'en bar');
|
||||
|
||||
// Ensure that the raw data is not translated.
|
||||
$raw = $config->getRawData();
|
||||
$this->assertIdentical($raw['foo'], 'bar');
|
||||
|
||||
ConfigurableLanguage::createFromLangcode('fr')->save();
|
||||
ConfigurableLanguage::createFromLangcode('de')->save();
|
||||
|
||||
\Drupal::languageManager()->setConfigOverrideLanguage(\Drupal::languageManager()->getLanguage('fr'));
|
||||
$config = \Drupal::config('config_test.system');
|
||||
$this->assertIdentical($config->get('foo'), 'fr bar');
|
||||
|
||||
\Drupal::languageManager()->setConfigOverrideLanguage(\Drupal::languageManager()->getLanguage('de'));
|
||||
$config = \Drupal::config('config_test.system');
|
||||
$this->assertIdentical($config->get('foo'), 'de bar');
|
||||
|
||||
// Test overrides of completely new configuration objects. In normal runtime
|
||||
// this should only happen for configuration entities as we should not be
|
||||
// creating simple configuration objects on the fly.
|
||||
\Drupal::languageManager()
|
||||
->getLanguageConfigOverride('de', 'config_test.new')
|
||||
->set('language', 'override')
|
||||
->save();
|
||||
$config = \Drupal::config('config_test.new');
|
||||
$this->assertTrue($config->isNew(), 'The configuration object config_test.new is new');
|
||||
$this->assertIdentical($config->get('language'), 'override');
|
||||
$this->assertIdentical($config->getOriginal('language', FALSE), NULL);
|
||||
|
||||
// Test how overrides react to base configuration changes. Set up some base
|
||||
// values.
|
||||
\Drupal::configFactory()->getEditable('config_test.foo')
|
||||
->set('value', array('key' => 'original'))
|
||||
->set('label', 'Original')
|
||||
->save();
|
||||
\Drupal::languageManager()
|
||||
->getLanguageConfigOverride('de', 'config_test.foo')
|
||||
->set('value', array('key' => 'override'))
|
||||
->set('label', 'Override')
|
||||
->save();
|
||||
\Drupal::languageManager()
|
||||
->getLanguageConfigOverride('fr', 'config_test.foo')
|
||||
->set('value', array('key' => 'override'))
|
||||
->save();
|
||||
\Drupal::configFactory()->clearStaticCache();
|
||||
$config = \Drupal::config('config_test.foo');
|
||||
$this->assertIdentical($config->get('value'), array('key' => 'override'));
|
||||
|
||||
// Ensure renaming the config will rename the override.
|
||||
\Drupal::languageManager()->setConfigOverrideLanguage(\Drupal::languageManager()->getLanguage('en'));
|
||||
\Drupal::configFactory()->rename('config_test.foo', 'config_test.bar');
|
||||
$config = \Drupal::config('config_test.bar');
|
||||
$this->assertEqual($config->get('value'), array('key' => 'original'));
|
||||
$override = \Drupal::languageManager()->getLanguageConfigOverride('de', 'config_test.foo');
|
||||
$this->assertTrue($override->isNew());
|
||||
$this->assertEqual($override->get('value'), NULL);
|
||||
$override = \Drupal::languageManager()->getLanguageConfigOverride('de', 'config_test.bar');
|
||||
$this->assertFalse($override->isNew());
|
||||
$this->assertEqual($override->get('value'), array('key' => 'override'));
|
||||
$override = \Drupal::languageManager()->getLanguageConfigOverride('fr', 'config_test.bar');
|
||||
$this->assertFalse($override->isNew());
|
||||
$this->assertEqual($override->get('value'), array('key' => 'override'));
|
||||
|
||||
// Ensure changing data in the config will update the overrides.
|
||||
$config = \Drupal::configFactory()->getEditable('config_test.bar')->clear('value.key')->save();
|
||||
$this->assertEqual($config->get('value'), array());
|
||||
$override = \Drupal::languageManager()->getLanguageConfigOverride('de', 'config_test.bar');
|
||||
$this->assertFalse($override->isNew());
|
||||
$this->assertEqual($override->get('value'), NULL);
|
||||
// The French override will become empty and therefore removed.
|
||||
$override = \Drupal::languageManager()->getLanguageConfigOverride('fr', 'config_test.bar');
|
||||
$this->assertTrue($override->isNew());
|
||||
$this->assertEqual($override->get('value'), NULL);
|
||||
|
||||
// Ensure deleting the config will delete the override.
|
||||
\Drupal::configFactory()->getEditable('config_test.bar')->delete();
|
||||
$override = \Drupal::languageManager()->getLanguageConfigOverride('de', 'config_test.bar');
|
||||
$this->assertTrue($override->isNew());
|
||||
$this->assertEqual($override->get('value'), NULL);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigLanguageOverrideWebTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests language overrides applied through the website.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigLanguageOverrideWebTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('language', 'system');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests translating the site name.
|
||||
*/
|
||||
function testSiteNameTranslation() {
|
||||
$adminUser = $this->drupalCreateUser(array('administer site configuration', 'administer languages'));
|
||||
$this->drupalLogin($adminUser);
|
||||
|
||||
// Add a custom language.
|
||||
$langcode = 'xx';
|
||||
$name = $this->randomMachineName(16);
|
||||
$edit = array(
|
||||
'predefined_langcode' => 'custom',
|
||||
'langcode' => $langcode,
|
||||
'label' => $name,
|
||||
'direction' => LanguageInterface::DIRECTION_LTR,
|
||||
);
|
||||
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language'));
|
||||
\Drupal::languageManager()
|
||||
->getLanguageConfigOverride($langcode, 'system.site')
|
||||
->set('name', 'XX site name')
|
||||
->save();
|
||||
|
||||
$this->drupalLogout();
|
||||
|
||||
// The home page in English should not have the override.
|
||||
$this->drupalGet('');
|
||||
$this->assertNoText('XX site name');
|
||||
|
||||
// During path resolution the system.site configuration object is used to
|
||||
// determine the front page. This occurs before language negotiation causing
|
||||
// the configuration factory to cache an object without the correct
|
||||
// overrides. We are testing that the configuration factory is
|
||||
// re-initialised after language negotiation. Ensure that it applies when
|
||||
// we access the XX front page.
|
||||
// @see \Drupal\Core\PathProcessor::processInbound()
|
||||
$this->drupalGet('xx');
|
||||
$this->assertText('XX site name');
|
||||
|
||||
// Set the xx language to be the default language and delete the English
|
||||
// language so the site is no longer multilingual and confirm configuration
|
||||
// overrides still work.
|
||||
$language_manager = \Drupal::languageManager()->reset();
|
||||
$this->assertTrue($language_manager->isMultilingual(), 'The test site is multilingual.');
|
||||
$this->config('system.site')->set('default_langcode', 'xx')->save();
|
||||
|
||||
ConfigurableLanguage::load('en')->delete();
|
||||
$this->assertFalse($language_manager->isMultilingual(), 'The test site is monolingual.');
|
||||
|
||||
$this->drupalGet('xx');
|
||||
$this->assertText('XX site name');
|
||||
|
||||
}
|
||||
|
||||
}
|
55
core/modules/config/src/Tests/ConfigModuleOverridesTest.php
Normal file
55
core/modules/config/src/Tests/ConfigModuleOverridesTest.php
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigModuleOverridesTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests module overrides of configuration using event subscribers.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigModuleOverridesTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system', 'config', 'config_override_test');
|
||||
|
||||
public function testSimpleModuleOverrides() {
|
||||
$GLOBALS['config_test_run_module_overrides'] = TRUE;
|
||||
$name = 'system.site';
|
||||
$overridden_name = 'ZOMG overridden site name';
|
||||
$non_overridden_name = 'ZOMG this name is on disk mkay';
|
||||
$overridden_slogan = 'Yay for overrides!';
|
||||
$non_overridden_slogan = 'Yay for defaults!';
|
||||
$config_factory = $this->container->get('config.factory');
|
||||
$config_factory
|
||||
->getEditable($name)
|
||||
->set('name', $non_overridden_name)
|
||||
->set('slogan', $non_overridden_slogan)
|
||||
->save();
|
||||
|
||||
$this->assertEqual($non_overridden_name, $config_factory->get('system.site')->getOriginal('name', FALSE));
|
||||
$this->assertEqual($non_overridden_slogan, $config_factory->get('system.site')->getOriginal('slogan', FALSE));
|
||||
$this->assertEqual($overridden_name, $config_factory->get('system.site')->get('name'));
|
||||
$this->assertEqual($overridden_slogan, $config_factory->get('system.site')->get('slogan'));
|
||||
|
||||
// Test overrides of completely new configuration objects. In normal runtime
|
||||
// this should only happen for configuration entities as we should not be
|
||||
// creating simple configuration objects on the fly.
|
||||
$config = $config_factory->get('config_override_test.new');
|
||||
$this->assertTrue($config->isNew(), 'The configuration object config_override_test.new is new');
|
||||
$this->assertIdentical($config->get('module'), 'override');
|
||||
$this->assertIdentical($config->getOriginal('module', FALSE), NULL);
|
||||
|
||||
unset($GLOBALS['config_test_run_module_overrides']);
|
||||
}
|
||||
}
|
117
core/modules/config/src/Tests/ConfigOtherModuleTest.php
Normal file
117
core/modules/config/src/Tests/ConfigOtherModuleTest.php
Normal file
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigOtherModuleTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests default configuration provided by a module that does not own it.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigOtherModuleTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Tests enabling the provider of the default configuration first.
|
||||
*/
|
||||
public function testInstallOtherModuleFirst() {
|
||||
$this->installModule('config_other_module_config_test');
|
||||
|
||||
// Check that the config entity doesn't exist before the config_test module
|
||||
// is enabled. We cannot use the entity system because the config_test
|
||||
// entity type does not exist.
|
||||
$config = $this->config('config_test.dynamic.other_module_test');
|
||||
$this->assertTrue($config->isNew(), 'Default configuration for other modules is not installed if that module is not enabled.');
|
||||
|
||||
// Install the module that provides the entity type. This installs the
|
||||
// default configuration.
|
||||
$this->installModule('config_test');
|
||||
$this->assertTrue(entity_load('config_test', 'other_module_test', TRUE), 'Default configuration has been installed.');
|
||||
|
||||
// Uninstall the module that provides the entity type. This will remove the
|
||||
// default configuration.
|
||||
$this->uninstallModule('config_test');
|
||||
$config = $this->config('config_test.dynamic.other_module_test');
|
||||
$this->assertTrue($config->isNew(), 'Default configuration for other modules is removed when the config entity provider is disabled.');
|
||||
|
||||
// Install the module that provides the entity type again. This installs the
|
||||
// default configuration.
|
||||
$this->installModule('config_test');
|
||||
$other_module_config_entity = entity_load('config_test', 'other_module_test', TRUE);
|
||||
$this->assertTrue($other_module_config_entity, "Default configuration has been recreated.");
|
||||
|
||||
// Update the default configuration to test that the changes are preserved
|
||||
// if the module that provides the default configuration is uninstalled.
|
||||
$other_module_config_entity->set('style', "The piano ain't got no wrong notes.");
|
||||
$other_module_config_entity->save();
|
||||
|
||||
// Uninstall the module that provides the default configuration.
|
||||
$this->uninstallModule('config_other_module_config_test');
|
||||
$this->assertTrue(entity_load('config_test', 'other_module_test', TRUE), 'Default configuration for other modules is not removed when the module that provides it is uninstalled.');
|
||||
|
||||
// Default configuration provided by config_test should still exist.
|
||||
$this->assertTrue(entity_load('config_test', 'dotted.default', TRUE), 'The configuration is not deleted.');
|
||||
|
||||
// Re-enable module to test that pre-existing optional configuration does
|
||||
// not throw an error.
|
||||
$this->installModule('config_other_module_config_test');
|
||||
$this->assertTrue(\Drupal::moduleHandler()->moduleExists('config_other_module_config_test'), 'The config_other_module_config_test module is installed.');
|
||||
|
||||
// Ensure that optional configuration with unmet dependencies is only
|
||||
// installed once all the dependencies are met.
|
||||
$this->assertNull(entity_load('config_test', 'other_module_test_unmet', TRUE), 'The optional configuration whose dependencies are met is not created.');
|
||||
$this->installModule('config_install_dependency_test');
|
||||
$this->assertTrue(entity_load('config_test', 'other_module_test_unmet', TRUE), 'The optional configuration whose dependencies are met is now created.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests enabling the provider of the config entity type first.
|
||||
*/
|
||||
public function testInstallConfigEntityModuleFirst() {
|
||||
$this->installModule('config_test');
|
||||
$this->assertFalse(entity_load('config_test', 'other_module_test', TRUE), 'Default configuration provided by config_other_module_config_test does not exist.');
|
||||
|
||||
$this->installModule('config_other_module_config_test');
|
||||
$this->assertTrue(entity_load('config_test', 'other_module_test', TRUE), 'Default configuration provided by config_other_module_config_test has been installed.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests uninstalling Node module removes views which are dependent on it.
|
||||
*/
|
||||
public function testUninstall() {
|
||||
$this->installModule('views');
|
||||
$this->assertTrue(entity_load('view', 'frontpage', TRUE) === NULL, 'After installing Views, frontpage view which is dependant on the Node and Views modules does not exist.');
|
||||
$this->installModule('node');
|
||||
$this->assertTrue(entity_load('view', 'frontpage', TRUE) !== NULL, 'After installing Node, frontpage view which is dependant on the Node and Views modules exists.');
|
||||
$this->uninstallModule('node');
|
||||
$this->assertTrue(entity_load('view', 'frontpage', TRUE) === NULL, 'After uninstalling Node, frontpage view which is dependant on the Node and Views modules does not exist.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs a module.
|
||||
*
|
||||
* @param string $module
|
||||
* The module name.
|
||||
*/
|
||||
protected function installModule($module) {
|
||||
$this->container->get('module_installer')->install(array($module));
|
||||
$this->container = \Drupal::getContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstalls a module.
|
||||
*
|
||||
* @param string $module
|
||||
* The module name.
|
||||
*/
|
||||
protected function uninstallModule($module) {
|
||||
$this->container->get('module_installer')->uninstall(array($module));
|
||||
$this->container = \Drupal::getContainer();
|
||||
}
|
||||
|
||||
}
|
141
core/modules/config/src/Tests/ConfigOverrideTest.php
Normal file
141
core/modules/config/src/Tests/ConfigOverrideTest.php
Normal file
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigOverrideTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests configuration overrides via $config in settings.php.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigOverrideTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system', 'config_test');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.staging'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests configuration override.
|
||||
*/
|
||||
function testConfOverride() {
|
||||
$expected_original_data = array(
|
||||
'foo' => 'bar',
|
||||
'baz' => NULL,
|
||||
'404' => 'herp',
|
||||
);
|
||||
|
||||
// Set globals before installing to prove that the installed file does not
|
||||
// contain these values.
|
||||
$overrides['config_test.system']['foo'] = 'overridden';
|
||||
$overrides['config_test.system']['baz'] = 'injected';
|
||||
$overrides['config_test.system']['404'] = 'derp';
|
||||
$GLOBALS['config'] = $overrides;
|
||||
|
||||
$this->installConfig(array('config_test'));
|
||||
|
||||
// Verify that the original configuration data exists. Have to read storage
|
||||
// directly otherwise overrides will apply.
|
||||
$active = $this->container->get('config.storage');
|
||||
$data = $active->read('config_test.system');
|
||||
$this->assertIdentical($data['foo'], $expected_original_data['foo']);
|
||||
$this->assertFalse(isset($data['baz']));
|
||||
$this->assertIdentical($data['404'], $expected_original_data['404']);
|
||||
|
||||
// Get the configuration object with overrides.
|
||||
$config = \Drupal::configFactory()->get('config_test.system');
|
||||
|
||||
// Verify that it contains the overridden data from $config.
|
||||
$this->assertIdentical($config->get('foo'), $overrides['config_test.system']['foo']);
|
||||
$this->assertIdentical($config->get('baz'), $overrides['config_test.system']['baz']);
|
||||
$this->assertIdentical($config->get('404'), $overrides['config_test.system']['404']);
|
||||
|
||||
// Get the configuration object which does not have overrides.
|
||||
$config = \Drupal::configFactory()->getEditable('config_test.system');
|
||||
|
||||
// Verify that it does not contains the overridden data from $config.
|
||||
$this->assertIdentical($config->get('foo'), $expected_original_data['foo']);
|
||||
$this->assertIdentical($config->get('baz'), NULL);
|
||||
$this->assertIdentical($config->get('404'), $expected_original_data['404']);
|
||||
|
||||
// Set the value for 'baz' (on the original data).
|
||||
$expected_original_data['baz'] = 'original baz';
|
||||
$config->set('baz', $expected_original_data['baz']);
|
||||
|
||||
// Set the value for '404' (on the original data).
|
||||
$expected_original_data['404'] = 'original 404';
|
||||
$config->set('404', $expected_original_data['404']);
|
||||
|
||||
// Save the configuration object (having overrides applied).
|
||||
$config->save();
|
||||
|
||||
// Reload it and verify that it still contains overridden data from $config.
|
||||
$config = \Drupal::config('config_test.system');
|
||||
$this->assertIdentical($config->get('foo'), $overrides['config_test.system']['foo']);
|
||||
$this->assertIdentical($config->get('baz'), $overrides['config_test.system']['baz']);
|
||||
$this->assertIdentical($config->get('404'), $overrides['config_test.system']['404']);
|
||||
|
||||
// Verify that raw config data has changed.
|
||||
$this->assertIdentical($config->getOriginal('foo', FALSE), $expected_original_data['foo']);
|
||||
$this->assertIdentical($config->getOriginal('baz', FALSE), $expected_original_data['baz']);
|
||||
$this->assertIdentical($config->getOriginal('404', FALSE), $expected_original_data['404']);
|
||||
|
||||
// Write file to staging.
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
$expected_new_data = array(
|
||||
'foo' => 'barbar',
|
||||
'404' => 'herpderp',
|
||||
);
|
||||
$staging->write('config_test.system', $expected_new_data);
|
||||
|
||||
// Import changed data from staging to active.
|
||||
$this->configImporter()->import();
|
||||
$data = $active->read('config_test.system');
|
||||
|
||||
// Verify that the new configuration data exists. Have to read storage
|
||||
// directly otherwise overrides will apply.
|
||||
$this->assertIdentical($data['foo'], $expected_new_data['foo']);
|
||||
$this->assertFalse(isset($data['baz']));
|
||||
$this->assertIdentical($data['404'], $expected_new_data['404']);
|
||||
|
||||
// Verify that the overrides are still working.
|
||||
$config = \Drupal::config('config_test.system');
|
||||
$this->assertIdentical($config->get('foo'), $overrides['config_test.system']['foo']);
|
||||
$this->assertIdentical($config->get('baz'), $overrides['config_test.system']['baz']);
|
||||
$this->assertIdentical($config->get('404'), $overrides['config_test.system']['404']);
|
||||
|
||||
// Test overrides of completely new configuration objects. In normal runtime
|
||||
// this should only happen for configuration entities as we should not be
|
||||
// creating simple configuration objects on the fly.
|
||||
$GLOBALS['config']['config_test.new']['key'] = 'override';
|
||||
$config = \Drupal::config('config_test.new');
|
||||
$this->assertTrue($config->isNew(), 'The configuration object config_test.new is new');
|
||||
$this->assertIdentical($config->get('key'), 'override');
|
||||
$config_raw = \Drupal::configFactory()->getEditable('config_test.new');
|
||||
$this->assertIdentical($config_raw->get('key'), NULL);
|
||||
$config_raw
|
||||
->set('key', 'raw')
|
||||
->set('new_key', 'new_value')
|
||||
->save();
|
||||
// Ensure override is preserved but all other data has been updated
|
||||
// accordingly.
|
||||
$this->assertIdentical($config->get('key'), 'override');
|
||||
$this->assertIdentical($config->get('new_key'), 'new_value');
|
||||
$raw_data = $config->getRawData();
|
||||
$this->assertIdentical($raw_data['key'], 'raw');
|
||||
}
|
||||
|
||||
}
|
100
core/modules/config/src/Tests/ConfigOverridesPriorityTest.php
Normal file
100
core/modules/config/src/Tests/ConfigOverridesPriorityTest.php
Normal file
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigOverridesPriorityTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests that language, module and settings.php are applied in the correct
|
||||
* order.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigOverridesPriorityTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system', 'config', 'config_override_test', 'language');
|
||||
|
||||
public function testOverridePriorities() {
|
||||
$GLOBALS['config_test_run_module_overrides'] = FALSE;
|
||||
|
||||
$non_overridden_mail = 'site@example.com';
|
||||
$language_overridden_mail = 'french@example.com';
|
||||
|
||||
$language_overridden_name = 'French site name';
|
||||
$module_overridden_name = 'ZOMG overridden site name';
|
||||
$non_overridden_name = 'ZOMG this name is on disk mkay';
|
||||
|
||||
$module_overridden_slogan = 'Yay for overrides!';
|
||||
$non_overridden_slogan = 'Yay for defaults!';
|
||||
|
||||
/** @var \Drupal\Core\Config\ConfigFactoryInterface $config_factory */
|
||||
$config_factory = $this->container->get('config.factory');
|
||||
$config_factory
|
||||
->getEditable('system.site')
|
||||
->set('name', $non_overridden_name)
|
||||
->set('slogan', $non_overridden_slogan)
|
||||
->set('mail', $non_overridden_mail)
|
||||
->set('weight_select_max', 50)
|
||||
->save();
|
||||
|
||||
// Ensure that no overrides are applying.
|
||||
$this->assertEqual($non_overridden_name, $config_factory->get('system.site')->get('name'));
|
||||
$this->assertEqual($non_overridden_slogan, $config_factory->get('system.site')->get('slogan'));
|
||||
$this->assertEqual($non_overridden_mail, $config_factory->get('system.site')->get('mail'));
|
||||
$this->assertEqual(50, $config_factory->get('system.site')->get('weight_select_max'));
|
||||
|
||||
// Override using language.
|
||||
$language = new Language(array(
|
||||
'name' => 'French',
|
||||
'id' => 'fr',
|
||||
));
|
||||
\Drupal::languageManager()->setConfigOverrideLanguage($language);
|
||||
\Drupal::languageManager()
|
||||
->getLanguageConfigOverride($language->getId(), 'system.site')
|
||||
->set('name', $language_overridden_name)
|
||||
->set('mail', $language_overridden_mail)
|
||||
->save();
|
||||
|
||||
$this->assertEqual($language_overridden_name, $config_factory->get('system.site')->get('name'));
|
||||
$this->assertEqual($non_overridden_slogan, $config_factory->get('system.site')->get('slogan'));
|
||||
$this->assertEqual($language_overridden_mail, $config_factory->get('system.site')->get('mail'));
|
||||
$this->assertEqual(50, $config_factory->get('system.site')->get('weight_select_max'));
|
||||
|
||||
// Enable module overrides. Do not override system.site:mail to prove that
|
||||
// the language override still applies.
|
||||
$GLOBALS['config_test_run_module_overrides'] = TRUE;
|
||||
$config_factory->reset('system.site');
|
||||
$this->assertEqual($module_overridden_name, $config_factory->get('system.site')->get('name'));
|
||||
$this->assertEqual($module_overridden_slogan, $config_factory->get('system.site')->get('slogan'));
|
||||
$this->assertEqual($language_overridden_mail, $config_factory->get('system.site')->get('mail'));
|
||||
$this->assertEqual(50, $config_factory->get('system.site')->get('weight_select_max'));
|
||||
|
||||
// Configure a global override to simulate overriding using settings.php. Do
|
||||
// not override system.site:mail or system.site:slogan to prove that the
|
||||
// language and module overrides still apply.
|
||||
$GLOBALS['config']['system.site']['name'] = 'Site name global conf override';
|
||||
$config_factory->reset('system.site');
|
||||
$this->assertEqual('Site name global conf override', $config_factory->get('system.site')->get('name'));
|
||||
$this->assertEqual($module_overridden_slogan, $config_factory->get('system.site')->get('slogan'));
|
||||
$this->assertEqual($language_overridden_mail, $config_factory->get('system.site')->get('mail'));
|
||||
$this->assertEqual(50, $config_factory->get('system.site')->get('weight_select_max'));
|
||||
|
||||
$this->assertEqual($non_overridden_name, $config_factory->get('system.site')->getOriginal('name', FALSE));
|
||||
$this->assertEqual($non_overridden_slogan, $config_factory->get('system.site')->getOriginal('slogan', FALSE));
|
||||
$this->assertEqual($non_overridden_mail, $config_factory->get('system.site')->getOriginal('mail', FALSE));
|
||||
$this->assertEqual(50, $config_factory->get('system.site')->getOriginal('weight_select_max', FALSE));
|
||||
|
||||
unset($GLOBALS['config_test_run_module_overrides']);
|
||||
}
|
||||
}
|
501
core/modules/config/src/Tests/ConfigSchemaTest.php
Normal file
501
core/modules/config/src/Tests/ConfigSchemaTest.php
Normal file
|
@ -0,0 +1,501 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigSchemaTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Core\Config\FileStorage;
|
||||
use Drupal\Core\Config\InstallStorage;
|
||||
use Drupal\Core\Config\Schema\ConfigSchemaAlterException;
|
||||
use Drupal\Core\TypedData\Type\IntegerInterface;
|
||||
use Drupal\Core\TypedData\Type\StringInterface;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests schema for configuration objects.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigSchemaTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system', 'language', 'field', 'image', 'config_test', 'config_schema_test');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installConfig(array('system', 'image', 'config_schema_test'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the basic metadata retrieval layer.
|
||||
*/
|
||||
function testSchemaMapping() {
|
||||
// Nonexistent configuration key will have Undefined as metadata.
|
||||
$this->assertIdentical(FALSE, \Drupal::service('config.typed')->hasConfigSchema('config_schema_test.no_such_key'));
|
||||
$definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.no_such_key');
|
||||
$expected = array();
|
||||
$expected['label'] = 'Undefined';
|
||||
$expected['class'] = '\Drupal\Core\Config\Schema\Undefined';
|
||||
$expected['type'] = 'undefined';
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition';
|
||||
$this->assertEqual($definition, $expected, 'Retrieved the right metadata for nonexistent configuration.');
|
||||
|
||||
// Configuration file without schema will return Undefined as well.
|
||||
$this->assertIdentical(FALSE, \Drupal::service('config.typed')->hasConfigSchema('config_schema_test.noschema'));
|
||||
$definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.noschema');
|
||||
$this->assertEqual($definition, $expected, 'Retrieved the right metadata for configuration with no schema.');
|
||||
|
||||
// Configuration file with only some schema.
|
||||
$this->assertIdentical(TRUE, \Drupal::service('config.typed')->hasConfigSchema('config_schema_test.someschema'));
|
||||
$definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.someschema');
|
||||
$expected = array();
|
||||
$expected['label'] = 'Schema test data';
|
||||
$expected['class'] = '\Drupal\Core\Config\Schema\Mapping';
|
||||
$expected['mapping']['langcode']['type'] = 'string';
|
||||
$expected['mapping']['langcode']['label'] = 'Language code';
|
||||
$expected['mapping']['testitem'] = array('label' => 'Test item');
|
||||
$expected['mapping']['testlist'] = array('label' => 'Test list');
|
||||
$expected['type'] = 'config_schema_test.someschema';
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
|
||||
$this->assertEqual($definition, $expected, 'Retrieved the right metadata for configuration with only some schema.');
|
||||
|
||||
// Check type detection on elements with undefined types.
|
||||
$config = \Drupal::service('config.typed')->get('config_schema_test.someschema');
|
||||
$definition = $config->get('testitem')->getDataDefinition()->toArray();
|
||||
$expected = array();
|
||||
$expected['label'] = 'Test item';
|
||||
$expected['class'] = '\Drupal\Core\Config\Schema\Undefined';
|
||||
$expected['type'] = 'undefined';
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition';
|
||||
$this->assertEqual($definition, $expected, 'Automatic type detected for a scalar is undefined.');
|
||||
$definition = $config->get('testlist')->getDataDefinition()->toArray();
|
||||
$expected = array();
|
||||
$expected['label'] = 'Test list';
|
||||
$expected['class'] = '\Drupal\Core\Config\Schema\Undefined';
|
||||
$expected['type'] = 'undefined';
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition';
|
||||
$this->assertEqual($definition, $expected, 'Automatic type detected for a list is undefined.');
|
||||
$definition = $config->get('testnoschema')->getDataDefinition()->toArray();
|
||||
$expected = array();
|
||||
$expected['label'] = 'Undefined';
|
||||
$expected['class'] = '\Drupal\Core\Config\Schema\Undefined';
|
||||
$expected['type'] = 'undefined';
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition';
|
||||
$this->assertEqual($definition, $expected, 'Automatic type detected for an undefined integer is undefined.');
|
||||
|
||||
// Simple case, straight metadata.
|
||||
$definition = \Drupal::service('config.typed')->getDefinition('system.maintenance');
|
||||
$expected = array();
|
||||
$expected['label'] = 'Maintenance mode';
|
||||
$expected['class'] = '\Drupal\Core\Config\Schema\Mapping';
|
||||
$expected['mapping']['message'] = array(
|
||||
'label' => 'Message to display when in maintenance mode',
|
||||
'type' => 'text',
|
||||
);
|
||||
$expected['mapping']['langcode'] = array(
|
||||
'label' => 'Language code',
|
||||
'type' => 'string',
|
||||
);
|
||||
$expected['type'] = 'system.maintenance';
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
|
||||
$this->assertEqual($definition, $expected, 'Retrieved the right metadata for system.maintenance');
|
||||
|
||||
// Mixed schema with ignore elements.
|
||||
$definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.ignore');
|
||||
$expected = array();
|
||||
$expected['label'] = 'Ignore test';
|
||||
$expected['class'] = '\Drupal\Core\Config\Schema\Mapping';
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
|
||||
$expected['mapping']['langcode'] = array(
|
||||
'type' => 'string',
|
||||
'label' => 'Language code',
|
||||
);
|
||||
$expected['mapping']['label'] = array(
|
||||
'label' => 'Label',
|
||||
'type' => 'label',
|
||||
);
|
||||
$expected['mapping']['irrelevant'] = array(
|
||||
'label' => 'Irrelevant',
|
||||
'type' => 'ignore',
|
||||
);
|
||||
$expected['mapping']['indescribable'] = array(
|
||||
'label' => 'Indescribable',
|
||||
'type' => 'ignore',
|
||||
);
|
||||
$expected['mapping']['weight'] = array(
|
||||
'label' => 'Weight',
|
||||
'type' => 'integer',
|
||||
);
|
||||
$expected['type'] = 'config_schema_test.ignore';
|
||||
|
||||
$this->assertEqual($definition, $expected);
|
||||
|
||||
// The ignore elements themselves.
|
||||
$definition = \Drupal::service('config.typed')->get('config_schema_test.ignore')->get('irrelevant')->getDataDefinition()->toArray();
|
||||
$expected = array();
|
||||
$expected['type'] = 'ignore';
|
||||
$expected['label'] = 'Irrelevant';
|
||||
$expected['class'] = '\Drupal\Core\Config\Schema\Ignore';
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition';
|
||||
$this->assertEqual($definition, $expected);
|
||||
$definition = \Drupal::service('config.typed')->get('config_schema_test.ignore')->get('indescribable')->getDataDefinition()->toArray();
|
||||
$expected['label'] = 'Indescribable';
|
||||
$this->assertEqual($definition, $expected);
|
||||
|
||||
// More complex case, generic type. Metadata for image style.
|
||||
$definition = \Drupal::service('config.typed')->getDefinition('image.style.large');
|
||||
$expected = array();
|
||||
$expected['label'] = 'Image style';
|
||||
$expected['class'] = '\Drupal\Core\Config\Schema\Mapping';
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
|
||||
$expected['mapping']['name']['type'] = 'string';
|
||||
$expected['mapping']['uuid']['type'] = 'string';
|
||||
$expected['mapping']['uuid']['label'] = 'UUID';
|
||||
$expected['mapping']['langcode']['type'] = 'string';
|
||||
$expected['mapping']['langcode']['label'] = 'Language code';
|
||||
$expected['mapping']['status']['type'] = 'boolean';
|
||||
$expected['mapping']['status']['label'] = 'Status';
|
||||
$expected['mapping']['dependencies']['type'] = 'config_dependencies';
|
||||
$expected['mapping']['dependencies']['label'] = 'Dependencies';
|
||||
$expected['mapping']['name']['type'] = 'string';
|
||||
$expected['mapping']['label']['type'] = 'label';
|
||||
$expected['mapping']['label']['label'] = 'Label';
|
||||
$expected['mapping']['effects']['type'] = 'sequence';
|
||||
$expected['mapping']['effects']['sequence']['type'] = 'mapping';
|
||||
$expected['mapping']['effects']['sequence']['mapping']['id']['type'] = 'string';
|
||||
$expected['mapping']['effects']['sequence']['mapping']['data']['type'] = 'image.effect.[%parent.id]';
|
||||
$expected['mapping']['effects']['sequence']['mapping']['weight']['type'] = 'integer';
|
||||
$expected['mapping']['effects']['sequence']['mapping']['uuid']['type'] = 'string';
|
||||
$expected['mapping']['third_party_settings']['type'] = 'sequence';
|
||||
$expected['mapping']['third_party_settings']['label'] = 'Third party settings';
|
||||
$expected['mapping']['third_party_settings']['sequence']['type'] = '[%parent.%parent.%type].third_party.[%key]';
|
||||
$expected['type'] = 'image.style.*';
|
||||
|
||||
$this->assertEqual($definition, $expected);
|
||||
|
||||
// More complex, type based on a complex one.
|
||||
$definition = \Drupal::service('config.typed')->getDefinition('image.effect.image_scale');
|
||||
// This should be the schema for image.effect.image_scale.
|
||||
$expected = array();
|
||||
$expected['label'] = 'Image scale';
|
||||
$expected['class'] = '\Drupal\Core\Config\Schema\Mapping';
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
|
||||
$expected['mapping']['width']['type'] = 'integer';
|
||||
$expected['mapping']['width']['label'] = 'Width';
|
||||
$expected['mapping']['height']['type'] = 'integer';
|
||||
$expected['mapping']['height']['label'] = 'Height';
|
||||
$expected['mapping']['upscale']['type'] = 'boolean';
|
||||
$expected['mapping']['upscale']['label'] = 'Upscale';
|
||||
$expected['type'] = 'image.effect.image_scale';
|
||||
|
||||
|
||||
$this->assertEqual($definition, $expected, 'Retrieved the right metadata for image.effect.image_scale');
|
||||
|
||||
// Most complex case, get metadata for actual configuration element.
|
||||
$effects = \Drupal::service('config.typed')->get('image.style.medium')->get('effects');
|
||||
$definition = $effects->get('bddf0d06-42f9-4c75-a700-a33cafa25ea0')->get('data')->getDataDefinition()->toArray();
|
||||
// This should be the schema for image.effect.image_scale, reuse previous one.
|
||||
$expected['type'] = 'image.effect.image_scale';
|
||||
|
||||
$this->assertEqual($definition, $expected, 'Retrieved the right metadata for the first effect of image.style.medium');
|
||||
|
||||
$a = \Drupal::config('config_test.dynamic.third_party');
|
||||
$test = \Drupal::service('config.typed')->get('config_test.dynamic.third_party')->get('third_party_settings.config_schema_test');
|
||||
$definition = $test->getDataDefinition()->toArray();
|
||||
$expected = array();
|
||||
$expected['type'] = 'config_test.dynamic.*.third_party.config_schema_test';
|
||||
$expected['label'] = 'Mapping';
|
||||
$expected['class'] = '\Drupal\Core\Config\Schema\Mapping';
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
|
||||
$expected['mapping'] = [
|
||||
'integer' => ['type' => 'integer'],
|
||||
'string' => ['type' => 'string'],
|
||||
];
|
||||
$this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_test.dynamic.third_party:third_party_settings.config_schema_test');
|
||||
|
||||
// More complex, several level deep test.
|
||||
$definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.someschema.somemodule.section_one.subsection');
|
||||
// This should be the schema of config_schema_test.someschema.somemodule.*.*.
|
||||
$expected = array();
|
||||
$expected['label'] = 'Schema multiple filesystem marker test';
|
||||
$expected['class'] = '\Drupal\Core\Config\Schema\Mapping';
|
||||
$expected['mapping']['langcode']['type'] = 'string';
|
||||
$expected['mapping']['langcode']['label'] = 'Language code';
|
||||
$expected['mapping']['testid']['type'] = 'string';
|
||||
$expected['mapping']['testid']['label'] = 'ID';
|
||||
$expected['mapping']['testdescription']['type'] = 'text';
|
||||
$expected['mapping']['testdescription']['label'] = 'Description';
|
||||
$expected['type'] = 'config_schema_test.someschema.somemodule.*.*';
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
|
||||
|
||||
$this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_schema_test.someschema.somemodule.section_one.subsection');
|
||||
|
||||
$definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.someschema.somemodule.section_two.subsection');
|
||||
// The other file should have the same schema.
|
||||
$this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_schema_test.someschema.somemodule.section_two.subsection');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests metadata retrieval with several levels of %parent indirection.
|
||||
*/
|
||||
function testSchemaMappingWithParents() {
|
||||
$config_data = \Drupal::service('config.typed')->get('config_schema_test.someschema.with_parents');
|
||||
|
||||
// Test fetching parent one level up.
|
||||
$entry = $config_data->get('one_level');
|
||||
$definition = $entry->get('testitem')->getDataDefinition()->toArray();
|
||||
$expected = array(
|
||||
'type' => 'config_schema_test.someschema.with_parents.key_1',
|
||||
'label' => 'Test item nested one level',
|
||||
'class' => '\Drupal\Core\TypedData\Plugin\DataType\StringData',
|
||||
'definition_class' => '\Drupal\Core\TypedData\DataDefinition',
|
||||
);
|
||||
$this->assertEqual($definition, $expected);
|
||||
|
||||
// Test fetching parent two levels up.
|
||||
$entry = $config_data->get('two_levels');
|
||||
$definition = $entry->get('wrapper')->get('testitem')->getDataDefinition()->toArray();
|
||||
$expected = array(
|
||||
'type' => 'config_schema_test.someschema.with_parents.key_2',
|
||||
'label' => 'Test item nested two levels',
|
||||
'class' => '\Drupal\Core\TypedData\Plugin\DataType\StringData',
|
||||
'definition_class' => '\Drupal\Core\TypedData\DataDefinition',
|
||||
);
|
||||
$this->assertEqual($definition, $expected);
|
||||
|
||||
// Test fetching parent three levels up.
|
||||
$entry = $config_data->get('three_levels');
|
||||
$definition = $entry->get('wrapper_1')->get('wrapper_2')->get('testitem')->getDataDefinition()->toArray();
|
||||
$expected = array(
|
||||
'type' => 'config_schema_test.someschema.with_parents.key_3',
|
||||
'label' => 'Test item nested three levels',
|
||||
'class' => '\Drupal\Core\TypedData\Plugin\DataType\StringData',
|
||||
'definition_class' => '\Drupal\Core\TypedData\DataDefinition',
|
||||
);
|
||||
$this->assertEqual($definition, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests metadata applied to configuration objects.
|
||||
*/
|
||||
function testSchemaData() {
|
||||
// Try a simple property.
|
||||
$meta = \Drupal::service('config.typed')->get('system.site');
|
||||
$property = $meta->get('page')->get('front');
|
||||
$this->assertTrue($property instanceof StringInterface, 'Got the right wrapper fo the page.front property.');
|
||||
$this->assertEqual($property->getValue(), '/user/login', 'Got the right value for page.front data.');
|
||||
$definition = $property->getDataDefinition();
|
||||
$this->assertTrue(empty($definition['translatable']), 'Got the right translatability setting for page.front data.');
|
||||
|
||||
// Check nested array of properties.
|
||||
$list = $meta->get('page')->getElements();
|
||||
$this->assertEqual(count($list), 3, 'Got a list with the right number of properties for site page data');
|
||||
$this->assertTrue(isset($list['front']) && isset($list['403']) && isset($list['404']), 'Got a list with the right properties for site page data.');
|
||||
$this->assertEqual($list['front']->getValue(), '/user/login', 'Got the right value for page.front data from the list.');
|
||||
|
||||
// And test some TypedConfigInterface methods.
|
||||
$properties = $list;
|
||||
$this->assertTrue(count($properties) == 3 && $properties['front'] == $list['front'], 'Got the right properties for site page.');
|
||||
$values = $meta->get('page')->toArray();
|
||||
$this->assertTrue(count($values) == 3 && $values['front'] == '/user/login', 'Got the right property values for site page.');
|
||||
|
||||
// Now let's try something more complex, with nested objects.
|
||||
$wrapper = \Drupal::service('config.typed')->get('image.style.large');
|
||||
$effects = $wrapper->get('effects');
|
||||
$this->assertTrue(count($effects->toArray()) == 1, 'Got an array with effects for image.style.large data');
|
||||
$uuid = key($effects->getValue());
|
||||
$effect = $effects->get($uuid)->getElements();
|
||||
$this->assertTrue(!$effect['data']->isEmpty() && $effect['id']->getValue() == 'image_scale', 'Got data for the image scale effect from metadata.');
|
||||
$this->assertTrue($effect['data']->get('width') instanceof IntegerInterface, 'Got the right type for the scale effect width.');
|
||||
$this->assertEqual($effect['data']->get('width')->getValue(), 480, 'Got the right value for the scale effect width.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test configuration value data type enforcement using schemas.
|
||||
*/
|
||||
public function testConfigSaveWithSchema() {
|
||||
$untyped_values = array(
|
||||
'string' => 1,
|
||||
'empty_string' => '',
|
||||
'null_string' => NULL,
|
||||
'integer' => '100',
|
||||
'null_integer' => '',
|
||||
'boolean' => 1,
|
||||
// If the config schema doesn't have a type it shouldn't be casted.
|
||||
'no_type' => 1,
|
||||
'mapping' => array(
|
||||
'string' => 1
|
||||
),
|
||||
'float' => '3.14',
|
||||
'null_float' => '',
|
||||
'sequence' => array (1, 0, 1),
|
||||
'sequence_bc' => array(1, 0, 1),
|
||||
// Not in schema and therefore should be left untouched.
|
||||
'not_present_in_schema' => TRUE,
|
||||
// Test a custom type.
|
||||
'config_schema_test_integer' => '1',
|
||||
'config_schema_test_integer_empty_string' => '',
|
||||
);
|
||||
$untyped_to_typed = $untyped_values;
|
||||
|
||||
$typed_values = array(
|
||||
'string' => '1',
|
||||
'empty_string' => '',
|
||||
'null_string' => NULL,
|
||||
'integer' => 100,
|
||||
'null_integer' => NULL,
|
||||
'boolean' => TRUE,
|
||||
'no_type' => 1,
|
||||
'mapping' => array(
|
||||
'string' => '1'
|
||||
),
|
||||
'float' => 3.14,
|
||||
'null_float' => NULL,
|
||||
'sequence' => array (TRUE, FALSE, TRUE),
|
||||
'sequence_bc' => array(TRUE, FALSE, TRUE),
|
||||
'not_present_in_schema' => TRUE,
|
||||
'config_schema_test_integer' => 1,
|
||||
'config_schema_test_integer_empty_string' => NULL,
|
||||
);
|
||||
|
||||
// Save config which has a schema that enforces types.
|
||||
$this->config('config_schema_test.schema_data_types')
|
||||
->setData($untyped_to_typed)
|
||||
->save();
|
||||
$this->assertIdentical($this->config('config_schema_test.schema_data_types')->get(), $typed_values);
|
||||
|
||||
// Save config which does not have a schema that enforces types.
|
||||
$this->config('config_schema_test.no_schema_data_types')
|
||||
->setData($untyped_values)
|
||||
->save();
|
||||
$this->assertIdentical($this->config('config_schema_test.no_schema_data_types')->get(), $untyped_values);
|
||||
|
||||
// Ensure that configuration objects with keys marked as ignored are not
|
||||
// changed when saved. The 'config_schema_test.ignore' will have been saved
|
||||
// during the installation of configuration in the setUp method.
|
||||
$extension_path = drupal_get_path('module', 'config_schema_test');
|
||||
$install_storage = new FileStorage($extension_path . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY);
|
||||
$original_data = $install_storage->read('config_schema_test.ignore');
|
||||
$this->assertIdentical($this->config('config_schema_test.ignore')->get(), $original_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests fallback to a greedy wildcard.
|
||||
*/
|
||||
function testSchemaFallback() {
|
||||
$definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.wildcard_fallback.something');
|
||||
// This should be the schema of config_schema_test.wildcard_fallback.*.
|
||||
$expected = array();
|
||||
$expected['label'] = 'Schema wildcard fallback test';
|
||||
$expected['class'] = '\Drupal\Core\Config\Schema\Mapping';
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
|
||||
$expected['mapping']['langcode']['type'] = 'string';
|
||||
$expected['mapping']['langcode']['label'] = 'Language code';
|
||||
$expected['mapping']['testid']['type'] = 'string';
|
||||
$expected['mapping']['testid']['label'] = 'ID';
|
||||
$expected['mapping']['testdescription']['type'] = 'text';
|
||||
$expected['mapping']['testdescription']['label'] = 'Description';
|
||||
$expected['type'] = 'config_schema_test.wildcard_fallback.*';
|
||||
|
||||
$this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_schema_test.wildcard_fallback.something');
|
||||
|
||||
$definition2 = \Drupal::service('config.typed')->getDefinition('config_schema_test.wildcard_fallback.something.something');
|
||||
// This should be the schema of config_schema_test.wildcard_fallback.* as
|
||||
//well.
|
||||
$this->assertIdentical($definition, $definition2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests use of colons in schema type determination.
|
||||
*
|
||||
* @see \Drupal\Core\Config\TypedConfigManager::getFallbackName()
|
||||
*/
|
||||
function testColonsInSchemaTypeDetermination() {
|
||||
$tests = \Drupal::service('config.typed')->get('config_schema_test.plugin_types')->get('tests')->getElements();
|
||||
$definition = $tests[0]->getDataDefinition()->toArray();
|
||||
$this->assertEqual($definition['type'], 'test.plugin_types.boolean');
|
||||
|
||||
$definition = $tests[1]->getDataDefinition()->toArray();
|
||||
$this->assertEqual($definition['type'], 'test.plugin_types.boolean:*');
|
||||
|
||||
$definition = $tests[2]->getDataDefinition()->toArray();
|
||||
$this->assertEqual($definition['type'], 'test.plugin_types.*');
|
||||
|
||||
$definition = $tests[3]->getDataDefinition()->toArray();
|
||||
$this->assertEqual($definition['type'], 'test.plugin_types.*');
|
||||
|
||||
$tests = \Drupal::service('config.typed')->get('config_schema_test.plugin_types')->get('test_with_parents')->getElements();
|
||||
$definition = $tests[0]->get('settings')->getDataDefinition()->toArray();
|
||||
$this->assertEqual($definition['type'], 'test_with_parents.plugin_types.boolean');
|
||||
|
||||
$definition = $tests[1]->get('settings')->getDataDefinition()->toArray();
|
||||
$this->assertEqual($definition['type'], 'test_with_parents.plugin_types.boolean:*');
|
||||
|
||||
$definition = $tests[2]->get('settings')->getDataDefinition()->toArray();
|
||||
$this->assertEqual($definition['type'], 'test_with_parents.plugin_types.*');
|
||||
|
||||
$definition = $tests[3]->get('settings')->getDataDefinition()->toArray();
|
||||
$this->assertEqual($definition['type'], 'test_with_parents.plugin_types.*');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests hook_config_schema_info_alter().
|
||||
*/
|
||||
public function testConfigSchemaInfoAlter() {
|
||||
/** @var \Drupal\Core\Config\TypedConfigManagerInterface $typed_config */
|
||||
$typed_config = \Drupal::service('config.typed');
|
||||
$typed_config->clearCachedDefinitions();
|
||||
|
||||
// Ensure that keys can not be added or removed by
|
||||
// hook_config_schema_info_alter().
|
||||
\Drupal::state()->set('config_schema_test_exception_remove', TRUE);
|
||||
$message = 'Expected ConfigSchemaAlterException thrown.';
|
||||
try {
|
||||
$typed_config->getDefinitions();
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (ConfigSchemaAlterException $e) {
|
||||
$this->pass($message);
|
||||
$this->assertEqual($e->getMessage(), 'Invoking hook_config_schema_info_alter() has removed (config_schema_test.hook) schema definitions');
|
||||
}
|
||||
|
||||
\Drupal::state()->set('config_schema_test_exception_add', TRUE);
|
||||
$message = 'Expected ConfigSchemaAlterException thrown.';
|
||||
try {
|
||||
$typed_config->getDefinitions();
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (ConfigSchemaAlterException $e) {
|
||||
$this->pass($message);
|
||||
$this->assertEqual($e->getMessage(), 'Invoking hook_config_schema_info_alter() has added (config_schema_test.hook_added_defintion) and removed (config_schema_test.hook) schema definitions');
|
||||
}
|
||||
|
||||
\Drupal::state()->set('config_schema_test_exception_remove', FALSE);
|
||||
$message = 'Expected ConfigSchemaAlterException thrown.';
|
||||
try {
|
||||
$typed_config->getDefinitions();
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (ConfigSchemaAlterException $e) {
|
||||
$this->pass($message);
|
||||
$this->assertEqual($e->getMessage(), 'Invoking hook_config_schema_info_alter() has added (config_schema_test.hook_added_defintion) schema definitions');
|
||||
}
|
||||
|
||||
// Tests that hook_config_schema_info_alter() can add additional metadata to
|
||||
// existing configuration schema.
|
||||
\Drupal::state()->set('config_schema_test_exception_add', FALSE);
|
||||
$definitions = $typed_config->getDefinitions();
|
||||
$this->assertEqual($definitions['config_schema_test.hook']['additional_metadata'], 'new schema info');
|
||||
}
|
||||
|
||||
}
|
172
core/modules/config/src/Tests/ConfigSingleImportExportTest.php
Normal file
172
core/modules/config/src/Tests/ConfigSingleImportExportTest.php
Normal file
|
@ -0,0 +1,172 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigSingleImportExportTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Component\Serialization\Yaml;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests the user interface for importing/exporting a single configuration.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigSingleImportExportTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config', 'config_test');
|
||||
|
||||
/**
|
||||
* Tests importing a single configuration file.
|
||||
*/
|
||||
public function testImport() {
|
||||
$storage = \Drupal::entityManager()->getStorage('config_test');
|
||||
$uuid = \Drupal::service('uuid');
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser(array('import configuration')));
|
||||
$import = <<<EOD
|
||||
label: First
|
||||
weight: 0
|
||||
style: ''
|
||||
status: '1'
|
||||
EOD;
|
||||
$edit = array(
|
||||
'config_type' => 'config_test',
|
||||
'import' => $import,
|
||||
);
|
||||
// Attempt an import with a missing ID.
|
||||
$this->drupalPostForm('admin/config/development/configuration/single/import', $edit, t('Import'));
|
||||
$this->assertText(t('Missing ID key "@id_key" for this @entity_type import.', array('@id_key' => 'id', '@entity_type' => 'Test configuration')));
|
||||
|
||||
// Perform an import with no specified UUID and a unique ID.
|
||||
$this->assertNull($storage->load('first'));
|
||||
$edit['import'] = "id: first\n" . $edit['import'];
|
||||
$this->drupalPostForm('admin/config/development/configuration/single/import', $edit, t('Import'));
|
||||
$this->assertRaw(t('Are you sure you want to create a new %name @type?', array('%name' => 'first', '@type' => 'test configuration')));
|
||||
$this->drupalPostForm(NULL, array(), t('Confirm'));
|
||||
$entity = $storage->load('first');
|
||||
$this->assertIdentical($entity->label(), 'First');
|
||||
$this->assertIdentical($entity->id(), 'first');
|
||||
$this->assertTrue($entity->status());
|
||||
$this->assertRaw(t('The @entity_type %label was imported.', array('@entity_type' => 'config_test', '%label' => $entity->label())));
|
||||
|
||||
// Attempt an import with an existing ID but missing UUID.
|
||||
$this->drupalPostForm('admin/config/development/configuration/single/import', $edit, t('Import'));
|
||||
$this->assertText(t('An entity with this machine name already exists but the import did not specify a UUID.'));
|
||||
|
||||
// Attempt an import with a mismatched UUID and existing ID.
|
||||
$edit['import'] .= "\nuuid: " . $uuid->generate();
|
||||
$this->drupalPostForm('admin/config/development/configuration/single/import', $edit, t('Import'));
|
||||
$this->assertText(t('An entity with this machine name already exists but the UUID does not match.'));
|
||||
|
||||
// Attempt an import with a custom ID.
|
||||
$edit['custom_entity_id'] = 'custom_id';
|
||||
$this->drupalPostForm('admin/config/development/configuration/single/import', $edit, t('Import'));
|
||||
$this->assertRaw(t('Are you sure you want to create a new %name @type?', array('%name' => 'custom_id', '@type' => 'test configuration')));
|
||||
$this->drupalPostForm(NULL, array(), t('Confirm'));
|
||||
$entity = $storage->load('custom_id');
|
||||
$this->assertRaw(t('The @entity_type %label was imported.', array('@entity_type' => 'config_test', '%label' => $entity->label())));
|
||||
|
||||
// Perform an import with a unique ID and UUID.
|
||||
$import = <<<EOD
|
||||
id: second
|
||||
label: Second
|
||||
weight: 0
|
||||
style: ''
|
||||
status: '0'
|
||||
EOD;
|
||||
$edit = array(
|
||||
'config_type' => 'config_test',
|
||||
'import' => $import,
|
||||
);
|
||||
$second_uuid = $uuid->generate();
|
||||
$edit['import'] .= "\nuuid: " . $second_uuid;
|
||||
$this->drupalPostForm('admin/config/development/configuration/single/import', $edit, t('Import'));
|
||||
$this->assertRaw(t('Are you sure you want to create a new %name @type?', array('%name' => 'second', '@type' => 'test configuration')));
|
||||
$this->drupalPostForm(NULL, array(), t('Confirm'));
|
||||
$entity = $storage->load('second');
|
||||
$this->assertRaw(t('The @entity_type %label was imported.', array('@entity_type' => 'config_test', '%label' => $entity->label())));
|
||||
$this->assertIdentical($entity->label(), 'Second');
|
||||
$this->assertIdentical($entity->id(), 'second');
|
||||
$this->assertFalse($entity->status());
|
||||
$this->assertIdentical($entity->uuid(), $second_uuid);
|
||||
|
||||
// Perform an update.
|
||||
$import = <<<EOD
|
||||
id: second
|
||||
uuid: $second_uuid
|
||||
label: 'Second updated'
|
||||
weight: 0
|
||||
style: ''
|
||||
status: '0'
|
||||
EOD;
|
||||
$edit = array(
|
||||
'config_type' => 'config_test',
|
||||
'import' => $import,
|
||||
);
|
||||
$this->drupalPostForm('admin/config/development/configuration/single/import', $edit, t('Import'));
|
||||
$this->assertRaw(t('Are you sure you want to update the %name @type?', array('%name' => 'second', '@type' => 'test configuration')));
|
||||
$this->drupalPostForm(NULL, array(), t('Confirm'));
|
||||
$entity = $storage->load('second');
|
||||
$this->assertRaw(t('The @entity_type %label was imported.', array('@entity_type' => 'config_test', '%label' => $entity->label())));
|
||||
$this->assertIdentical($entity->label(), 'Second updated');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests importing a simple configuration file.
|
||||
*/
|
||||
public function testImportSimpleConfiguration() {
|
||||
$this->drupalLogin($this->drupalCreateUser(array('import configuration')));
|
||||
$config = $this->config('system.site')->set('name', 'Test simple import');
|
||||
$edit = array(
|
||||
'config_type' => 'system.simple',
|
||||
'config_name' => $config->getName(),
|
||||
'import' => Yaml::encode($config->get()),
|
||||
);
|
||||
$this->drupalPostForm('admin/config/development/configuration/single/import', $edit, t('Import'));
|
||||
$this->assertRaw(t('Are you sure you want to update the %name @type?', array('%name' => $config->getName(), '@type' => 'simple configuration')));
|
||||
$this->drupalPostForm(NULL, array(), t('Confirm'));
|
||||
$this->drupalGet('');
|
||||
$this->assertText('Test simple import');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests exporting a single configuration file.
|
||||
*/
|
||||
public function testExport() {
|
||||
$this->drupalLogin($this->drupalCreateUser(array('export configuration')));
|
||||
|
||||
$this->drupalGet('admin/config/development/configuration/single/export/system.simple');
|
||||
$this->assertFieldByXPath('//select[@name="config_type"]//option[@selected="selected"]', t('Simple configuration'), 'The simple configuration option is selected when specified in the URL.');
|
||||
// Spot check several known simple configuration files.
|
||||
$element = $this->xpath('//select[@name="config_name"]');
|
||||
$options = $this->getAllOptions($element[0]);
|
||||
$expected_options = array('system.site', 'user.settings');
|
||||
foreach ($options as &$option) {
|
||||
$option = (string) $option;
|
||||
}
|
||||
$this->assertIdentical($expected_options, array_intersect($expected_options, $options), 'The expected configuration files are listed.');
|
||||
|
||||
$this->drupalGet('admin/config/development/configuration/single/export/system.simple/system.image');
|
||||
$this->assertFieldByXPath('//textarea[@name="export"]', "toolkit: gd\n", 'The expected system configuration is displayed.');
|
||||
|
||||
$this->drupalGet('admin/config/development/configuration/single/export/date_format');
|
||||
$this->assertFieldByXPath('//select[@name="config_type"]//option[@selected="selected"]', t('Date format'), 'The date format entity type is selected when specified in the URL.');
|
||||
|
||||
$this->drupalGet('admin/config/development/configuration/single/export/date_format/fallback');
|
||||
$this->assertFieldByXPath('//select[@name="config_name"]//option[@selected="selected"]', t('Fallback date format'), 'The fallback date format config entity is selected when specified in the URL.');
|
||||
|
||||
$fallback_date = \Drupal::entityManager()->getStorage('date_format')->load('fallback');
|
||||
$data = Yaml::encode($fallback_date->toArray());
|
||||
$this->assertFieldByXPath('//textarea[@name="export"]', $data, 'The fallback date format config entity export code is displayed.');
|
||||
}
|
||||
|
||||
}
|
91
core/modules/config/src/Tests/ConfigSnapshotTest.php
Normal file
91
core/modules/config/src/Tests/ConfigSnapshotTest.php
Normal file
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\ConfigSnapshotTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Core\Config\StorageComparer;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests config snapshot creation and updating.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigSnapshotTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_test', 'system');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Update the config snapshot. This allows the parent::setUp() to write
|
||||
// configuration files.
|
||||
\Drupal::service('config.manager')->createSnapshot(\Drupal::service('config.storage'), \Drupal::service('config.storage.snapshot'));
|
||||
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.staging'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests config snapshot creation and updating.
|
||||
*/
|
||||
function testSnapshot() {
|
||||
$active = $this->container->get('config.storage');
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
$snapshot = $this->container->get('config.storage.snapshot');
|
||||
$config_manager = $this->container->get('config.manager');
|
||||
$config_name = 'config_test.system';
|
||||
$config_key = 'foo';
|
||||
$new_data = 'foobar';
|
||||
|
||||
$active_snapshot_comparer = new StorageComparer($active, $snapshot, $config_manager);
|
||||
$staging_snapshot_comparer = new StorageComparer($staging, $snapshot, $config_manager);
|
||||
|
||||
// Verify that we have an initial snapshot that matches the active
|
||||
// configuration. This has to be true as no config should be installed.
|
||||
$this->assertFalse($active_snapshot_comparer->createChangelist()->hasChanges());
|
||||
|
||||
// Install the default config.
|
||||
$this->installConfig(array('config_test'));
|
||||
// Although we have imported config this has not affected the snapshot.
|
||||
$this->assertTrue($active_snapshot_comparer->reset()->hasChanges());
|
||||
|
||||
// Update the config snapshot.
|
||||
\Drupal::service('config.manager')->createSnapshot($active, $snapshot);
|
||||
|
||||
// The snapshot and active config should now contain the same config
|
||||
// objects.
|
||||
$this->assertFalse($active_snapshot_comparer->reset()->hasChanges());
|
||||
|
||||
// Change a configuration value in staging.
|
||||
$staging_data = $this->config($config_name)->get();
|
||||
$staging_data[$config_key] = $new_data;
|
||||
$staging->write($config_name, $staging_data);
|
||||
|
||||
// Verify that active and snapshot match, and that staging doesn't match
|
||||
// active.
|
||||
$this->assertFalse($active_snapshot_comparer->reset()->hasChanges());
|
||||
$this->assertTrue($staging_snapshot_comparer->createChangelist()->hasChanges());
|
||||
|
||||
// Import changed data from staging to active.
|
||||
$this->configImporter()->import();
|
||||
|
||||
// Verify changed config was properly imported.
|
||||
\Drupal::configFactory()->reset($config_name);
|
||||
$this->assertIdentical($this->config($config_name)->get($config_key), $new_data);
|
||||
|
||||
// Verify that a new snapshot was created which and that it matches
|
||||
// the active config.
|
||||
$this->assertFalse($active_snapshot_comparer->reset()->hasChanges());
|
||||
}
|
||||
|
||||
}
|
85
core/modules/config/src/Tests/DefaultConfigTest.php
Normal file
85
core/modules/config/src/Tests/DefaultConfigTest.php
Normal file
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\DefaultConfigTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\config_test\TestInstallStorage;
|
||||
use Drupal\Core\Config\InstallStorage;
|
||||
use Drupal\Core\Config\TypedConfigManager;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* Tests that default configuration provided by all modules matches schema.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class DefaultConfigTest extends KernelTestBase {
|
||||
|
||||
use SchemaCheckTestTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* Enable the system module so that system_config_schema_info_alter() fires.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system', 'config_test');
|
||||
|
||||
/**
|
||||
* Themes which provide default configuration and need enabling.
|
||||
*
|
||||
* If a theme provides default configuration but does not have a schema
|
||||
* because it can rely on schemas added by system_config_schema_info_alter()
|
||||
* then this test needs to enable it.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $themes = ['seven'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
\Drupal::service('theme_handler')->install($this->themes);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function containerBuild(ContainerBuilder $container) {
|
||||
parent::containerBuild($container);
|
||||
$container->register('default_config_test.schema_storage')
|
||||
->setClass('\Drupal\config_test\TestInstallStorage')
|
||||
->addArgument(InstallStorage::CONFIG_SCHEMA_DIRECTORY);
|
||||
|
||||
$definition = $container->getDefinition('config.typed');
|
||||
$definition->replaceArgument(1, new Reference('default_config_test.schema_storage'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests default configuration data type.
|
||||
*/
|
||||
public function testDefaultConfig() {
|
||||
$typed_config = \Drupal::service('config.typed');
|
||||
// Create a configuration storage with access to default configuration in
|
||||
// every module, profile and theme.
|
||||
$default_config_storage = new TestInstallStorage();
|
||||
|
||||
foreach ($default_config_storage->listAll() as $config_name) {
|
||||
// Skip files provided by the config_schema_test module since that module
|
||||
// is explicitly for testing schema.
|
||||
if (strpos($config_name, 'config_schema_test') === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = $default_config_storage->read($config_name);
|
||||
$this->assertConfigSchema($typed_config, $config_name, $data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
64
core/modules/config/src/Tests/SchemaCheckTestTrait.php
Normal file
64
core/modules/config/src/Tests/SchemaCheckTestTrait.php
Normal file
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\SchemaCheckTestTrait.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Core\Config\TypedConfigManagerInterface;
|
||||
use Drupal\Core\Config\Schema\SchemaCheckTrait;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
|
||||
/**
|
||||
* Provides a class for checking configuration schema.
|
||||
*/
|
||||
trait SchemaCheckTestTrait {
|
||||
|
||||
use SchemaCheckTrait;
|
||||
|
||||
/**
|
||||
* Asserts the TypedConfigManager has a valid schema for the configuration.
|
||||
*
|
||||
* @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config
|
||||
* The TypedConfigManager.
|
||||
* @param string $config_name
|
||||
* The configuration name.
|
||||
* @param array $config_data
|
||||
* The configuration data.
|
||||
*/
|
||||
public function assertConfigSchema(TypedConfigManagerInterface $typed_config, $config_name, $config_data) {
|
||||
$errors = $this->checkConfigSchema($typed_config, $config_name, $config_data);
|
||||
if ($errors === FALSE) {
|
||||
// @todo Since the use of this trait is under TestBase, it works.
|
||||
// Can be fixed as part of https://www.drupal.org/node/2260053.
|
||||
$this->fail(SafeMarkup::format('No schema for !config_name', array('!config_name' => $config_name)));
|
||||
return;
|
||||
}
|
||||
elseif ($errors === TRUE) {
|
||||
// @todo Since the use of this trait is under TestBase, it works.
|
||||
// Can be fixed as part of https://www.drupal.org/node/2260053.
|
||||
$this->pass(SafeMarkup::format('Schema found for !config_name and values comply with schema.', array('!config_name' => $config_name)));
|
||||
}
|
||||
else {
|
||||
foreach ($errors as $key => $error) {
|
||||
// @todo Since the use of this trait is under TestBase, it works.
|
||||
// Can be fixed as part of https://www.drupal.org/node/2260053.
|
||||
$this->fail(SafeMarkup::format('Schema key @key failed with: @error', array('@key' => $key, '@error' => $error)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts configuration, specified by name, has a valid schema.
|
||||
*
|
||||
* @param string $config_name
|
||||
* The configuration name.
|
||||
*/
|
||||
public function assertConfigSchemaByName($config_name) {
|
||||
$config = $this->config($config_name);
|
||||
$this->assertConfigSchema(\Drupal::service('config.typed'), $config->getName(), $config->get());
|
||||
}
|
||||
|
||||
}
|
72
core/modules/config/src/Tests/SchemaCheckTraitTest.php
Normal file
72
core/modules/config/src/Tests/SchemaCheckTraitTest.php
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\SchemaCheckTraitTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Core\Config\Schema\SchemaCheckTrait;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
|
||||
/**
|
||||
* Tests the functionality of SchemaCheckTrait.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class SchemaCheckTraitTest extends KernelTestBase {
|
||||
|
||||
use SchemaCheckTrait;
|
||||
|
||||
/**
|
||||
* The typed config manager.
|
||||
*
|
||||
* @var \Drupal\Core\Config\TypedConfigManagerInterface
|
||||
*/
|
||||
protected $typedConfig;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_test', 'config_schema_test');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installConfig(array('config_test', 'config_schema_test'));
|
||||
$this->typedConfig = \Drupal::service('config.typed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests \Drupal\Core\Config\Schema\SchemaCheckTrait.
|
||||
*/
|
||||
public function testTrait() {
|
||||
// Test a non existing schema.
|
||||
$ret = $this->checkConfigSchema($this->typedConfig, 'config_schema_test.noschema', $this->config('config_schema_test.noschema')->get());
|
||||
$this->assertIdentical($ret, FALSE);
|
||||
|
||||
// Test an existing schema with valid data.
|
||||
$config_data = $this->config('config_test.types')->get();
|
||||
$ret = $this->checkConfigSchema($this->typedConfig, 'config_test.types', $config_data);
|
||||
$this->assertIdentical($ret, TRUE);
|
||||
|
||||
// Add a new key, a new array and overwrite boolean with array to test the
|
||||
// error messages.
|
||||
$config_data = array('new_key' => 'new_value', 'new_array' => array()) + $config_data;
|
||||
$config_data['boolean'] = array();
|
||||
$ret = $this->checkConfigSchema($this->typedConfig, 'config_test.types', $config_data);
|
||||
$expected = array(
|
||||
'config_test.types:new_key' => 'missing schema',
|
||||
'config_test.types:new_array' => 'missing schema',
|
||||
'config_test.types:boolean' => 'non-scalar value but not defined as an array (such as mapping or sequence)',
|
||||
);
|
||||
$this->assertEqual($ret, $expected);
|
||||
}
|
||||
|
||||
}
|
67
core/modules/config/src/Tests/SchemaConfigListenerTest.php
Normal file
67
core/modules/config/src/Tests/SchemaConfigListenerTest.php
Normal file
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\SchemaConfigListenerTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Core\Config\Schema\SchemaCheckTrait;
|
||||
use Drupal\Core\Config\Schema\SchemaIncompleteException;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the functionality of ConfigSchemaChecker in KernelTestBase tests.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class SchemaConfigListenerTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('config_test');
|
||||
|
||||
/**
|
||||
* Tests \Drupal\Core\Config\Testing\ConfigSchemaChecker.
|
||||
*/
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\SchemaConfigListenerWebTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\Core\Config\Schema\SchemaCheckTrait;
|
||||
use Drupal\Core\Config\Schema\SchemaIncompleteException;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests the functionality of ConfigSchemaChecker in WebTestBase tests.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class SchemaConfigListenerWebTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('config_test');
|
||||
|
||||
/**
|
||||
* Tests \Drupal\Core\Config\Testing\ConfigSchemaChecker.
|
||||
*/
|
||||
public function testConfigSchemaChecker() {
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer site configuration']));
|
||||
|
||||
// Test a non-existing schema.
|
||||
$msg = 'Expected SchemaIncompleteException thrown';
|
||||
try {
|
||||
$this->config('config_schema_test.schemaless')->set('foo', 'bar')->save();
|
||||
$this->fail($msg);
|
||||
}
|
||||
catch (SchemaIncompleteException $e) {
|
||||
$this->pass($msg);
|
||||
$this->assertEqual('No schema for config_schema_test.schemaless', $e->getMessage());
|
||||
}
|
||||
|
||||
// Test a valid schema.
|
||||
$msg = 'Unexpected SchemaIncompleteException thrown';
|
||||
$config = $this->config('config_test.types')->set('int', 10);
|
||||
try {
|
||||
$config->save();
|
||||
$this->pass($msg);
|
||||
}
|
||||
catch (SchemaIncompleteException $e) {
|
||||
$this->fail($msg);
|
||||
}
|
||||
|
||||
// Test an invalid schema.
|
||||
$msg = 'Expected SchemaIncompleteException thrown';
|
||||
$config = $this->config('config_test.types')
|
||||
->set('foo', 'bar')
|
||||
->set('array', 1);
|
||||
try {
|
||||
$config->save();
|
||||
$this->fail($msg);
|
||||
}
|
||||
catch (SchemaIncompleteException $e) {
|
||||
$this->pass($msg);
|
||||
$this->assertEqual('Schema errors for config_test.types with the following errors: config_test.types:array variable type is integer but applied schema class is Drupal\Core\Config\Schema\Sequence, config_test.types:foo missing schema', $e->getMessage());
|
||||
}
|
||||
|
||||
// Test that the config event listener is working in the child site.
|
||||
$this->drupalGet('config_test/schema_listener');
|
||||
$this->assertText('No schema for config_schema_test.schemaless');
|
||||
}
|
||||
|
||||
}
|
97
core/modules/config/src/Tests/Storage/CachedStorageTest.php
Normal file
97
core/modules/config/src/Tests/Storage/CachedStorageTest.php
Normal file
|
@ -0,0 +1,97 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\Storage\CachedStorageTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests\Storage;
|
||||
|
||||
use Drupal\Core\Config\FileStorage;
|
||||
use Drupal\Core\Config\CachedStorage;
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* Tests CachedStorage operations.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class CachedStorageTest extends ConfigStorageTestBase {
|
||||
|
||||
/**
|
||||
* The cache backend the cached storage is using.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheBackendInterface
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* The file storage the cached storage is using.
|
||||
*
|
||||
* @var \Drupal\Core\Config\FileStorage
|
||||
*/
|
||||
protected $fileStorage;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->fileStorage = new FileStorage($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]);
|
||||
$this->storage = new CachedStorage($this->fileStorage, \Drupal::service('cache.config'));
|
||||
$this->cache = \Drupal::service('cache_factory')->get('config');
|
||||
// ::listAll() verifications require other configuration data to exist.
|
||||
$this->storage->write('system.performance', array());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testInvalidStorage() {
|
||||
// No-op as this test does not make sense.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function read($name) {
|
||||
$data = $this->cache->get($name);
|
||||
// Cache misses fall through to the underlying storage.
|
||||
return $data ? $data->data : $this->fileStorage->read($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function insert($name, $data) {
|
||||
$this->fileStorage->write($name, $data);
|
||||
$this->cache->set($name, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function update($name, $data) {
|
||||
$this->fileStorage->write($name, $data);
|
||||
$this->cache->set($name, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function delete($name) {
|
||||
$this->cache->delete($name);
|
||||
unlink($this->fileStorage->getFilePath($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function containerBuild(ContainerBuilder $container) {
|
||||
parent::containerBuild($container);
|
||||
// Use the regular database cache backend to aid testing.
|
||||
$container->register('cache_factory', 'Drupal\Core\Cache\DatabaseBackendFactory')
|
||||
->addArgument(new Reference('database'))
|
||||
->addArgument(new Reference('cache_tags.invalidator.checksum'));
|
||||
}
|
||||
|
||||
}
|
271
core/modules/config/src/Tests/Storage/ConfigStorageTestBase.php
Normal file
271
core/modules/config/src/Tests/Storage/ConfigStorageTestBase.php
Normal file
|
@ -0,0 +1,271 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\Storage\ConfigStorageTestBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests\Storage;
|
||||
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Base class for testing storage operations.
|
||||
*
|
||||
* All configuration storages are expected to behave identically in
|
||||
* terms of reading, writing, listing, deleting, as well as error handling.
|
||||
*
|
||||
* Therefore, storage tests use an uncommon test case class structure;
|
||||
* the base class defines the test method(s) to execute, which are identical
|
||||
* for all storages. The storage specific test case classes supply the
|
||||
* necessary helper methods to interact with the raw/native storage
|
||||
* directly.
|
||||
*/
|
||||
abstract class ConfigStorageTestBase extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Config\StorageInterface
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Config\StorageInterface
|
||||
*/
|
||||
protected $invalidStorage;
|
||||
|
||||
/**
|
||||
* Tests storage CRUD operations.
|
||||
*
|
||||
* @todo Coverage: Trigger PDOExceptions / Database exceptions.
|
||||
*/
|
||||
function testCRUD() {
|
||||
$name = 'config_test.storage';
|
||||
|
||||
// Checking whether a non-existing name exists returns FALSE.
|
||||
$this->assertIdentical($this->storage->exists($name), FALSE);
|
||||
|
||||
// Reading a non-existing name returns FALSE.
|
||||
$data = $this->storage->read($name);
|
||||
$this->assertIdentical($data, FALSE);
|
||||
|
||||
// Writing data returns TRUE and the data has been written.
|
||||
$data = array('foo' => 'bar');
|
||||
$result = $this->storage->write($name, $data);
|
||||
$this->assertIdentical($result, TRUE);
|
||||
|
||||
$raw_data = $this->read($name);
|
||||
$this->assertIdentical($raw_data, $data);
|
||||
|
||||
// Checking whether an existing name exists returns TRUE.
|
||||
$this->assertIdentical($this->storage->exists($name), TRUE);
|
||||
|
||||
// Writing the identical data again still returns TRUE.
|
||||
$result = $this->storage->write($name, $data);
|
||||
$this->assertIdentical($result, TRUE);
|
||||
|
||||
// Listing all names returns all.
|
||||
$names = $this->storage->listAll();
|
||||
$this->assertTrue(in_array('system.performance', $names));
|
||||
$this->assertTrue(in_array($name, $names));
|
||||
|
||||
// Listing all names with prefix returns names with that prefix only.
|
||||
$names = $this->storage->listAll('config_test.');
|
||||
$this->assertFalse(in_array('system.performance', $names));
|
||||
$this->assertTrue(in_array($name, $names));
|
||||
|
||||
// Rename the configuration storage object.
|
||||
$new_name = 'config_test.storage_rename';
|
||||
$this->storage->rename($name, $new_name);
|
||||
$raw_data = $this->read($new_name);
|
||||
$this->assertIdentical($raw_data, $data);
|
||||
// Rename it back so further tests work.
|
||||
$this->storage->rename($new_name, $name);
|
||||
|
||||
// Deleting an existing name returns TRUE.
|
||||
$result = $this->storage->delete($name);
|
||||
$this->assertIdentical($result, TRUE);
|
||||
|
||||
// Deleting a non-existing name returns FALSE.
|
||||
$result = $this->storage->delete($name);
|
||||
$this->assertIdentical($result, FALSE);
|
||||
|
||||
// Deleting all names with prefix deletes the appropriate data and returns
|
||||
// TRUE.
|
||||
$files = array(
|
||||
'config_test.test.biff',
|
||||
'config_test.test.bang',
|
||||
'config_test.test.pow',
|
||||
);
|
||||
foreach ($files as $name) {
|
||||
$this->storage->write($name, $data);
|
||||
}
|
||||
|
||||
$result = $this->storage->deleteAll('config_test.');
|
||||
$names = $this->storage->listAll('config_test.');
|
||||
$this->assertIdentical($result, TRUE);
|
||||
$this->assertIdentical($names, array());
|
||||
|
||||
// Test renaming an object that does not exist throws an exception.
|
||||
try {
|
||||
$this->storage->rename('config_test.storage_does_not_exist', 'config_test.storage_does_not_exist_rename');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$class = get_class($e);
|
||||
$this->pass($class . ' thrown upon renaming a nonexistent storage bin.');
|
||||
}
|
||||
|
||||
// Test renaming to an object that already exists throws an exception.
|
||||
try {
|
||||
$this->storage->rename('system.cron', 'system.performance');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$class = get_class($e);
|
||||
$this->pass($class . ' thrown upon renaming a nonexistent storage bin.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an invalid storage.
|
||||
*/
|
||||
public function testInvalidStorage() {
|
||||
$name = 'config_test.storage';
|
||||
|
||||
// Write something to the valid storage to prove that the storages do not
|
||||
// pollute one another.
|
||||
$data = array('foo' => 'bar');
|
||||
$result = $this->storage->write($name, $data);
|
||||
$this->assertIdentical($result, TRUE);
|
||||
|
||||
$raw_data = $this->read($name);
|
||||
$this->assertIdentical($raw_data, $data);
|
||||
|
||||
// Reading from a non-existing storage bin returns FALSE.
|
||||
$result = $this->invalidStorage->read($name);
|
||||
$this->assertIdentical($result, FALSE);
|
||||
|
||||
// Deleting from a non-existing storage bin throws an exception.
|
||||
try {
|
||||
$this->invalidStorage->delete($name);
|
||||
$this->fail('Exception not thrown upon deleting from a non-existing storage bin.');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$class = get_class($e);
|
||||
$this->pass($class . ' thrown upon deleting from a non-existing storage bin.');
|
||||
}
|
||||
|
||||
// Listing on a non-existing storage bin returns an empty array.
|
||||
$result = $this->invalidStorage->listAll();
|
||||
$this->assertIdentical($result, array());
|
||||
// Writing to a non-existing storage bin creates the bin.
|
||||
$this->invalidStorage->write($name, array('foo' => 'bar'));
|
||||
$result = $this->invalidStorage->read($name);
|
||||
$this->assertIdentical($result, array('foo' => 'bar'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests storage writing and reading data preserving data type.
|
||||
*/
|
||||
function testDataTypes() {
|
||||
$name = 'config_test.types';
|
||||
$data = array(
|
||||
'array' => array(),
|
||||
'boolean' => TRUE,
|
||||
'exp' => 1.2e+34,
|
||||
'float' => 3.14159,
|
||||
'hex' => 0xC,
|
||||
'int' => 99,
|
||||
'octal' => 0775,
|
||||
'string' => 'string',
|
||||
'string_int' => '1',
|
||||
);
|
||||
|
||||
$result = $this->storage->write($name, $data);
|
||||
$this->assertIdentical($result, TRUE);
|
||||
|
||||
$read_data = $this->storage->read($name);
|
||||
$this->assertIdentical($read_data, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the storage supports collections.
|
||||
*/
|
||||
public function testCollection() {
|
||||
$name = 'config_test.storage';
|
||||
$data = array('foo' => 'bar');
|
||||
$result = $this->storage->write($name, $data);
|
||||
$this->assertIdentical($result, TRUE);
|
||||
$this->assertIdentical($data, $this->storage->read($name));
|
||||
|
||||
// Create configuration in a new collection.
|
||||
$new_storage = $this->storage->createCollection('collection.sub.new');
|
||||
$this->assertFalse($new_storage->exists($name));
|
||||
$this->assertEqual(array(), $new_storage->listAll());
|
||||
$new_storage->write($name, $data);
|
||||
$this->assertIdentical($result, TRUE);
|
||||
$this->assertIdentical($data, $new_storage->read($name));
|
||||
$this->assertEqual(array($name), $new_storage->listAll());
|
||||
$this->assertTrue($new_storage->exists($name));
|
||||
$new_data = array('foo' => 'baz');
|
||||
$new_storage->write($name, $new_data);
|
||||
$this->assertIdentical($result, TRUE);
|
||||
$this->assertIdentical($new_data, $new_storage->read($name));
|
||||
|
||||
// Create configuration in another collection.
|
||||
$another_storage = $this->storage->createCollection('collection.sub.another');
|
||||
$this->assertFalse($another_storage->exists($name));
|
||||
$this->assertEqual(array(), $another_storage->listAll());
|
||||
$another_storage->write($name, $new_data);
|
||||
$this->assertIdentical($result, TRUE);
|
||||
$this->assertIdentical($new_data, $another_storage->read($name));
|
||||
$this->assertEqual(array($name), $another_storage->listAll());
|
||||
$this->assertTrue($another_storage->exists($name));
|
||||
|
||||
// Create configuration in yet another collection.
|
||||
$alt_storage = $this->storage->createCollection('alternate');
|
||||
$alt_storage->write($name, $new_data);
|
||||
$this->assertIdentical($result, TRUE);
|
||||
$this->assertIdentical($new_data, $alt_storage->read($name));
|
||||
|
||||
// Switch back to the collection-less mode and check the data still exists
|
||||
// add has not been touched.
|
||||
$this->assertIdentical($data, $this->storage->read($name));
|
||||
|
||||
// Check that the getAllCollectionNames() method works.
|
||||
$this->assertIdentical(array('alternate', 'collection.sub.another', 'collection.sub.new'), $this->storage->getAllCollectionNames());
|
||||
|
||||
// Check that the collections are removed when they are empty.
|
||||
$alt_storage->delete($name);
|
||||
$this->assertIdentical(array('collection.sub.another', 'collection.sub.new'), $this->storage->getAllCollectionNames());
|
||||
|
||||
// Create configuration in collection called 'collection'. This ensures that
|
||||
// FileStorage's collection storage works regardless of its use of
|
||||
// subdirectories.
|
||||
$parent_storage = $this->storage->createCollection('collection');
|
||||
$this->assertFalse($parent_storage->exists($name));
|
||||
$this->assertEqual(array(), $parent_storage->listAll());
|
||||
$parent_storage->write($name, $new_data);
|
||||
$this->assertIdentical($result, TRUE);
|
||||
$this->assertIdentical($new_data, $parent_storage->read($name));
|
||||
$this->assertEqual(array($name), $parent_storage->listAll());
|
||||
$this->assertTrue($parent_storage->exists($name));
|
||||
$this->assertIdentical(array('collection', 'collection.sub.another', 'collection.sub.new'), $this->storage->getAllCollectionNames());
|
||||
$parent_storage->deleteAll();
|
||||
$this->assertIdentical(array('collection.sub.another', 'collection.sub.new'), $this->storage->getAllCollectionNames());
|
||||
|
||||
// Check that the having an empty collection-less storage does not break
|
||||
// anything. Before deleting check that the previous delete did not affect
|
||||
// data in another collection.
|
||||
$this->assertIdentical($data, $this->storage->read($name));
|
||||
$this->storage->delete($name);
|
||||
$this->assertIdentical(array('collection.sub.another', 'collection.sub.new'), $this->storage->getAllCollectionNames());
|
||||
}
|
||||
|
||||
abstract protected function read($name);
|
||||
|
||||
abstract protected function insert($name, $data);
|
||||
|
||||
abstract protected function update($name, $data);
|
||||
|
||||
abstract protected function delete($name);
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\Storage\DatabaseStorageTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests\Storage;
|
||||
|
||||
use Drupal\Core\Config\DatabaseStorage;
|
||||
|
||||
/**
|
||||
* Tests DatabaseStorage operations.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class DatabaseStorageTest extends ConfigStorageTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->storage = new DatabaseStorage($this->container->get('database'), 'config');
|
||||
$this->invalidStorage = new DatabaseStorage($this->container->get('database'), 'invalid');
|
||||
|
||||
// ::listAll() verifications require other configuration data to exist.
|
||||
$this->storage->write('system.performance', array());
|
||||
}
|
||||
|
||||
protected function read($name) {
|
||||
$data = db_query('SELECT data FROM {config} WHERE name = :name', array(':name' => $name))->fetchField();
|
||||
return unserialize($data);
|
||||
}
|
||||
|
||||
protected function insert($name, $data) {
|
||||
db_insert('config')->fields(array('name' => $name, 'data' => $data))->execute();
|
||||
}
|
||||
|
||||
protected function update($name, $data) {
|
||||
db_update('config')->fields(array('data' => $data))->condition('name', $name)->execute();
|
||||
}
|
||||
|
||||
protected function delete($name) {
|
||||
db_delete('config')->condition('name', $name)->execute();
|
||||
}
|
||||
}
|
69
core/modules/config/src/Tests/Storage/FileStorageTest.php
Normal file
69
core/modules/config/src/Tests/Storage/FileStorageTest.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config\Tests\Storage\FileStorageTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests\Storage;
|
||||
|
||||
use Drupal\Component\Serialization\Yaml;
|
||||
use Drupal\Core\Config\FileStorage;
|
||||
|
||||
/**
|
||||
* Tests FileStorage operations.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class FileStorageTest extends ConfigStorageTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->storage = new FileStorage($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]);
|
||||
$this->invalidStorage = new FileStorage($this->configDirectories[CONFIG_ACTIVE_DIRECTORY] . '/nonexisting');
|
||||
|
||||
// FileStorage::listAll() requires other configuration data to exist.
|
||||
$this->storage->write('system.performance', $this->config('system.performance')->get());
|
||||
$this->storage->write('core.extension', array('module' => array()));
|
||||
}
|
||||
|
||||
protected function read($name) {
|
||||
$data = file_get_contents($this->storage->getFilePath($name));
|
||||
return Yaml::decode($data);
|
||||
}
|
||||
|
||||
protected function insert($name, $data) {
|
||||
file_put_contents($this->storage->getFilePath($name), $data);
|
||||
}
|
||||
|
||||
protected function update($name, $data) {
|
||||
file_put_contents($this->storage->getFilePath($name), $data);
|
||||
}
|
||||
|
||||
protected function delete($name) {
|
||||
unlink($this->storage->getFilePath($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the FileStorage::listAll method with a relative and absolute path.
|
||||
*/
|
||||
public function testlistAll() {
|
||||
$expected_files = array(
|
||||
'core.extension',
|
||||
'system.performance',
|
||||
);
|
||||
|
||||
$config_files = $this->storage->listAll();
|
||||
$this->assertIdentical($config_files, $expected_files, 'Relative path, two config files found.');
|
||||
|
||||
// Initialize FileStorage with absolute file path.
|
||||
$absolute_path = realpath($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]);
|
||||
$storage_absolute_path = new FileStorage($absolute_path);
|
||||
$config_files = $storage_absolute_path->listAll();
|
||||
$this->assertIdentical($config_files, $expected_files, 'Absolute path, two config files found.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
# Clashes with default configuration provided by the config_test module.
|
||||
id: dotted.default
|
||||
label: 'Config install fail'
|
||||
weight: 0
|
||||
protected_property: Default
|
||||
# Intentionally commented out to verify default status behavior.
|
||||
# status: 1
|
|
@ -0,0 +1,2 @@
|
|||
# Clashes with default configuration provided by the config_test module.
|
||||
label: 'Je suis'
|
|
@ -0,0 +1,10 @@
|
|||
name: 'Test theme for configuration clash detection'
|
||||
type: theme
|
||||
description: 'Test theme for configuration clash detection'
|
||||
version: VERSION
|
||||
base theme: classy
|
||||
core: 8.x
|
||||
regions:
|
||||
content: Content
|
||||
left: Left
|
||||
right: Right
|
|
@ -0,0 +1 @@
|
|||
collection: another_collection
|
|
@ -0,0 +1 @@
|
|||
collection: collection.test1
|
|
@ -0,0 +1 @@
|
|||
collection: collection.test2
|
|
@ -0,0 +1 @@
|
|||
label: entity
|
|
@ -0,0 +1,9 @@
|
|||
# This should contain a copy of the configuration from the
|
||||
# config_collection_install_test module.
|
||||
name: 'Config collection clash test module'
|
||||
type: module
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- config_collection_install_test
|
|
@ -0,0 +1 @@
|
|||
collection: another_collection
|
|
@ -0,0 +1 @@
|
|||
collection: collection.test1
|
|
@ -0,0 +1 @@
|
|||
collection: collection.test2
|
|
@ -0,0 +1 @@
|
|||
label: entity
|
|
@ -0,0 +1,7 @@
|
|||
config_collection_install_test.test:
|
||||
type: config_object
|
||||
label: 'Collection test'
|
||||
mapping:
|
||||
collection:
|
||||
type: string
|
||||
label: 'Collection'
|
|
@ -0,0 +1,5 @@
|
|||
name: 'Configuration events test'
|
||||
type: module
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
|
@ -0,0 +1,6 @@
|
|||
services:
|
||||
config_events_test.event_subscriber:
|
||||
class: Drupal\config_collection_install_test\EventSubscriber
|
||||
arguments: ['@state']
|
||||
tags:
|
||||
- { name: event_subscriber }
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_collection_install_test\EventSubscriber.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_collection_install_test;
|
||||
|
||||
use Drupal\Core\Config\ConfigCollectionInfo;
|
||||
use Drupal\Core\Config\ConfigEvents;
|
||||
use Drupal\Core\State\StateInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
class EventSubscriber implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* The state key value store.
|
||||
*
|
||||
* @var \Drupal\Core\State\StateInterface
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* Constructs the Event Subscriber object.
|
||||
*
|
||||
* @param \Drupal\Core\State\StateInterface $state
|
||||
* The state key value store.
|
||||
*/
|
||||
public function __construct(StateInterface $state) {
|
||||
$this->state = $state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reacts to the ConfigEvents::COLLECTION_NAMES event.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigCollectionInfo $collection_info
|
||||
* The configuration collection names event.
|
||||
*/
|
||||
public function addCollections(ConfigCollectionInfo $collection_info) {
|
||||
$collections = $this->state->get('config_collection_install_test.collection_names', array());
|
||||
foreach ($collections as $collection) {
|
||||
$collection_info->addCollection($collection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
static function getSubscribedEvents() {
|
||||
$events[ConfigEvents::COLLECTION_INFO][] = array('addCollections');
|
||||
return $events;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
name: 'Configuration entity static cache test'
|
||||
type: module
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides configuration entity static cache test helpers.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Utility\Random;
|
||||
|
||||
/**
|
||||
* Implements hook_ENTITY_TYPE_load() for 'static_cache_test_config_test'.
|
||||
*/
|
||||
function config_entity_static_cache_test_config_test_load($entities) {
|
||||
static $random;
|
||||
if (!$random) {
|
||||
$random = new Random();
|
||||
}
|
||||
foreach ($entities as $entity) {
|
||||
// Add a random stamp for every load(), so that during tests, we can tell
|
||||
// if an entity was retrieved from cache (unchanged stamp) or reloaded.
|
||||
$entity->_loadStamp = $random->string(8, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_type_alter().
|
||||
*/
|
||||
function config_entity_static_cache_test_entity_type_alter(array &$entity_types) {
|
||||
/** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
|
||||
$entity_types['config_test']->set('static_cache', TRUE);
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_entity_static_cache_test\ConfigOverrider.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_entity_static_cache_test;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryOverrideInterface;
|
||||
use Drupal\Core\Config\StorageInterface;
|
||||
|
||||
/**
|
||||
* Tests module overrides for configuration.
|
||||
*/
|
||||
class ConfigOverrider implements ConfigFactoryOverrideInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadOverrides($names) {
|
||||
return array(
|
||||
'config_test.dynamic.test_1' => array(
|
||||
'label' => 'Overridden label',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheSuffix() {
|
||||
return 'config_entity_static_cache_test';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createConfigObject($name, $collection = StorageInterface::DEFAULT_COLLECTION) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
config_events_test.test:
|
||||
type: config_object
|
||||
label: 'Configuration events test'
|
||||
mapping:
|
||||
key:
|
||||
type: string
|
||||
label: 'Value'
|
|
@ -0,0 +1,5 @@
|
|||
name: 'Configuration events test'
|
||||
type: module
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
|
@ -0,0 +1,6 @@
|
|||
services:
|
||||
config_events_test.event_subscriber:
|
||||
class: Drupal\config_events_test\EventSubscriber
|
||||
arguments: ['@state']
|
||||
tags:
|
||||
- { name: event_subscriber }
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_events_test\EventSubscriber.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_events_test;
|
||||
|
||||
|
||||
use Drupal\Core\Config\ConfigCrudEvent;
|
||||
use Drupal\Core\Config\ConfigEvents;
|
||||
use Drupal\Core\State\StateInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
class EventSubscriber implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* The state key value store.
|
||||
*
|
||||
* @var \Drupal\Core\State\StateInterface
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* Constructs the Event Subscriber object.
|
||||
*
|
||||
* @param \Drupal\Core\State\StateInterface $state
|
||||
* The state key value store.
|
||||
*/
|
||||
public function __construct(StateInterface $state) {
|
||||
$this->state = $state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reacts to config event.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigCrudEvent $event
|
||||
* The configuration event.
|
||||
* @param string $name
|
||||
* The event name.
|
||||
*/
|
||||
public function configEventRecorder(ConfigCrudEvent $event, $name) {
|
||||
$config = $event->getConfig();
|
||||
$this->state->set('config_events_test.event', array(
|
||||
'event_name' => $name,
|
||||
'current_config_data' => $config->get(),
|
||||
'original_config_data' => $config->getOriginal(),
|
||||
'raw_config_data' => $config->getRawData()
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
static function getSubscribedEvents() {
|
||||
$events[ConfigEvents::SAVE][] = array('configEventRecorder');
|
||||
$events[ConfigEvents::DELETE][] = array('configEventRecorder');
|
||||
$events[ConfigEvents::RENAME][] = array('configEventRecorder');
|
||||
return $events;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
name: 'Configuration export test'
|
||||
type: module
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides config export testing support functionality.
|
||||
*/
|
||||
|
||||
// Override the system maintenance message for testing.
|
||||
$GLOBALS['config']['system.maintenance']['message'] = 'Foo';
|
|
@ -0,0 +1,5 @@
|
|||
name: 'Configuration import test'
|
||||
type: module
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides configuration import test helpers.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_config_import_steps_alter().
|
||||
*/
|
||||
function config_import_test_config_import_steps_alter(&$sync_steps) {
|
||||
$sync_steps[] = '_config_import_test_config_import_steps_alter';
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements configuration synchronization step added by an alter for testing.
|
||||
*
|
||||
* @param array $context
|
||||
* The batch context.
|
||||
*/
|
||||
function _config_import_test_config_import_steps_alter(&$context) {
|
||||
$GLOBALS['hook_config_test']['config_import_steps_alter'] = TRUE;
|
||||
$context['finished'] = 1;
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
services:
|
||||
config_import_test.event_subscriber:
|
||||
class: Drupal\config_import_test\EventSubscriber
|
||||
tags:
|
||||
- { name: event_subscriber }
|
||||
arguments: ['@state']
|
|
@ -0,0 +1,147 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_import_test\EventSubscriber.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_import_test;
|
||||
|
||||
use Drupal\Core\Config\ConfigCrudEvent;
|
||||
use Drupal\Core\Config\ConfigEvents;
|
||||
use Drupal\Core\Config\ConfigImporterEvent;
|
||||
use Drupal\Core\Config\Importer\MissingContentEvent;
|
||||
use Drupal\Core\State\StateInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* Config import subscriber for config import events.
|
||||
*/
|
||||
class EventSubscriber implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* The key value store.
|
||||
*
|
||||
* @var \Drupal\Core\State\StateInterface
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* Constructs the event subscriber.
|
||||
*
|
||||
* @param \Drupal\Core\State\StateInterface $state
|
||||
* The key value store.
|
||||
*/
|
||||
public function __construct(StateInterface $state) {
|
||||
$this->state = $state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the configuration to be imported.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigImporterEvent $event
|
||||
* The Event to process.
|
||||
*
|
||||
* @throws \Drupal\Core\Config\ConfigNameException
|
||||
*/
|
||||
public function onConfigImporterValidate(ConfigImporterEvent $event) {
|
||||
if ($this->state->get('config_import_test.config_import_validate_fail', FALSE)) {
|
||||
// Log more than one error to test multiple validation errors.
|
||||
$event->getConfigImporter()->logError('Config import validate error 1.');
|
||||
$event->getConfigImporter()->logError('Config import validate error 2.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the missing content event.
|
||||
*
|
||||
* @param \Drupal\Core\Config\Importer\MissingContentEvent $event
|
||||
* The missing content event.
|
||||
*/
|
||||
public function onConfigImporterMissingContentOne(MissingContentEvent $event) {
|
||||
if ($this->state->get('config_import_test.config_import_missing_content', FALSE) && $this->state->get('config_import_test.config_import_missing_content_one', FALSE) === FALSE) {
|
||||
$missing = $event->getMissingContent();
|
||||
$uuid = key($missing);
|
||||
$this->state->set('config_import_test.config_import_missing_content_one', key($missing));
|
||||
$event->resolveMissingContent($uuid);
|
||||
// Stopping propagation ensures that onConfigImporterMissingContentTwo
|
||||
// will be fired on the next batch step.
|
||||
$event->stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the missing content event.
|
||||
*
|
||||
* @param \Drupal\Core\Config\Importer\MissingContentEvent $event
|
||||
* The missing content event.
|
||||
*/
|
||||
public function onConfigImporterMissingContentTwo(MissingContentEvent $event) {
|
||||
if ($this->state->get('config_import_test.config_import_missing_content', FALSE) && $this->state->get('config_import_test.config_import_missing_content_two', FALSE) === FALSE) {
|
||||
$missing = $event->getMissingContent();
|
||||
$uuid = key($missing);
|
||||
$this->state->set('config_import_test.config_import_missing_content_two', key($missing));
|
||||
$event->resolveMissingContent($uuid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reacts to a config save and records information in state for testing.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigCrudEvent $event
|
||||
*/
|
||||
public function onConfigSave(ConfigCrudEvent $event) {
|
||||
$config = $event->getConfig();
|
||||
if ($config->getName() == 'action.settings') {
|
||||
$values = $this->state->get('ConfigImportUITest.action.settings.recursion_limit', array());
|
||||
$values[] = $config->get('recursion_limit');
|
||||
$this->state->set('ConfigImportUITest.action.settings.recursion_limit', $values);
|
||||
}
|
||||
|
||||
if ($config->getName() == 'core.extension') {
|
||||
$installed = $this->state->get('ConfigImportUITest.core.extension.modules_installed', array());
|
||||
$uninstalled = $this->state->get('ConfigImportUITest.core.extension.modules_uninstalled', array());
|
||||
$original = $config->getOriginal('module');
|
||||
$data = $config->get('module');
|
||||
$install = array_diff_key($data, $original);
|
||||
if (!empty($install)) {
|
||||
$installed[] = key($install);
|
||||
}
|
||||
$uninstall = array_diff_key($original, $data);
|
||||
if (!empty($uninstall)) {
|
||||
$uninstalled[] = key($uninstall);
|
||||
}
|
||||
|
||||
$this->state->set('ConfigImportUITest.core.extension.modules_installed', $installed);
|
||||
$this->state->set('ConfigImportUITest.core.extension.modules_uninstalled', $uninstalled);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reacts to a config delete and records information in state for testing.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigCrudEvent $event
|
||||
*/
|
||||
public function onConfigDelete(ConfigCrudEvent $event) {
|
||||
$config = $event->getConfig();
|
||||
if ($config->getName() == 'action.settings') {
|
||||
$value = $this->state->get('ConfigImportUITest.action.settings.delete', 0);
|
||||
$this->state->set('ConfigImportUITest.action.settings.delete', $value + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the methods in this class that should be listeners.
|
||||
*
|
||||
* @return array
|
||||
* An array of event listener definitions.
|
||||
*/
|
||||
static function getSubscribedEvents() {
|
||||
$events[ConfigEvents::SAVE][] = array('onConfigSave', 40);
|
||||
$events[ConfigEvents::DELETE][] = array('onConfigDelete', 40);
|
||||
$events[ConfigEvents::IMPORT_VALIDATE] = array('onConfigImporterValidate');
|
||||
$events[ConfigEvents::IMPORT_MISSING_CONTENT] = array(array('onConfigImporterMissingContentOne'), array('onConfigImporterMissingContentTwo', -100));
|
||||
return $events;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
id: other_module_test_with_dependency
|
||||
label: 'Other module test with dependency'
|
||||
weight: 0
|
||||
style: ''
|
||||
status: true
|
||||
langcode: en
|
||||
protected_property: Default
|
||||
dependencies:
|
||||
enforced:
|
||||
module:
|
||||
- config_other_module_config_test
|
|
@ -0,0 +1,5 @@
|
|||
name: 'Config install dependency test'
|
||||
type: module
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
|
@ -0,0 +1,7 @@
|
|||
# Clashes with default configuration provided by the config_test module.
|
||||
id: dotted.default
|
||||
label: 'Config install fail'
|
||||
weight: 0
|
||||
protected_property: Default
|
||||
# Intentionally commented out to verify default status behavior.
|
||||
# status: 1
|
|
@ -0,0 +1,2 @@
|
|||
# Clashes with default configuration provided by the config_test module.
|
||||
label: 'Je suis'
|
|
@ -0,0 +1,7 @@
|
|||
name: 'Configuration install fail test'
|
||||
type: module
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- config_test
|
|
@ -0,0 +1 @@
|
|||
foo: 'default setting'
|
|
@ -0,0 +1,2 @@
|
|||
id: config_integration_test
|
||||
label: 'Default integration config label'
|
|
@ -0,0 +1,9 @@
|
|||
# Schema for the configuration files of the Configuration Integration Test module.
|
||||
|
||||
config_integration_test.settings:
|
||||
type: config_object
|
||||
label: 'Configuration integration test settings'
|
||||
mapping:
|
||||
foo:
|
||||
type: string
|
||||
label: 'Foo'
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue