Unverified Commit 21c35049 authored by alexpott's avatar alexpott
Browse files

Issue #3016429 by bircher, mpotter, alexpott, johndevman, borisson_, Upchuk,...

Issue #3016429 by bircher, mpotter, alexpott, johndevman, borisson_, Upchuk, larowlan: Add a config storage copy utility trait and fix ConfigManager::createSnapshot
parent a916e4d4
......@@ -22,6 +22,7 @@
class ConfigManager implements ConfigManagerInterface {
use StringTranslationTrait;
use DeprecatedServicePropertyTrait;
use StorageCopyTrait;
/**
* {@inheritdoc}
......@@ -207,23 +208,7 @@ public function diff(StorageInterface $source_storage, StorageInterface $target_
* {@inheritdoc}
*/
public function createSnapshot(StorageInterface $source_storage, StorageInterface $snapshot_storage) {
// Empty the snapshot of all configuration.
$snapshot_storage->deleteAll();
foreach ($snapshot_storage->getAllCollectionNames() as $collection) {
$snapshot_collection = $snapshot_storage->createCollection($collection);
$snapshot_collection->deleteAll();
}
foreach ($source_storage->listAll() as $name) {
$snapshot_storage->write($name, $source_storage->read($name));
}
// Copy collections as well.
foreach ($source_storage->getAllCollectionNames() as $collection) {
$source_collection = $source_storage->createCollection($collection);
$snapshot_collection = $snapshot_storage->createCollection($collection);
foreach ($source_collection->listAll() as $name) {
$snapshot_collection->write($name, $source_collection->read($name));
}
}
self::replaceStorageContents($source_storage, $snapshot_storage);
}
/**
......
<?php
namespace Drupal\Core\Config;
/**
* Utility trait to copy configuration from one storage to another.
*/
trait StorageCopyTrait {
/**
* Copy the configuration from one storage to another and remove stale items.
*
* This method empties target storage and copies all collections from source.
* Configuration is only copied and not imported, should not be used
* with the active storage as the target.
*
* @param \Drupal\Core\Config\StorageInterface $source
* The configuration storage to copy from.
* @param \Drupal\Core\Config\StorageInterface $target
* The configuration storage to copy to.
*/
protected static function replaceStorageContents(StorageInterface $source, StorageInterface &$target) {
// Make sure there is no stale configuration in the target storage.
foreach (array_merge([StorageInterface::DEFAULT_COLLECTION], $target->getAllCollectionNames()) as $collection) {
$target->createCollection($collection)->deleteAll();
}
// Copy all the configuration from all the collections.
foreach (array_merge([StorageInterface::DEFAULT_COLLECTION], $source->getAllCollectionNames()) as $collection) {
$source_collection = $source->createCollection($collection);
$target_collection = $target->createCollection($collection);
foreach ($source_collection->listAll() as $name) {
$target_collection->write($name, $source_collection->read($name));
}
}
// Make sure that the target is set to the same collection as the source.
$target = $target->createCollection($source->getCollectionName());
}
}
......@@ -4,6 +4,7 @@
use Drupal\Core\Config\ConfigImporter;
use Drupal\Core\Config\StorageComparer;
use Drupal\Core\Config\StorageCopyTrait;
use Drupal\Core\Config\StorageInterface;
/**
......@@ -11,6 +12,8 @@
*/
trait ConfigTestTrait {
use StorageCopyTrait;
/**
* Returns a ConfigImporter object to import test configuration.
*
......@@ -49,10 +52,7 @@ protected function configImporter() {
* The target config storage service.
*/
protected function copyConfig(StorageInterface $source_storage, StorageInterface $target_storage) {
$target_storage->deleteAll();
foreach ($source_storage->listAll() as $name) {
$target_storage->write($name, $source_storage->read($name));
}
static::replaceStorageContents($source_storage, $target_storage);
}
}
<?php
namespace Drupal\Tests\Core\Config;
use Drupal\Core\Config\MemoryStorage;
use Drupal\Core\Config\StorageCopyTrait;
use Drupal\Core\Config\StorageInterface;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Config\StorageCopyTrait
* @group Config
*/
class StorageCopyTraitTest extends UnitTestCase {
use StorageCopyTrait;
/**
* @covers ::replaceStorageContents
*
* @dataProvider providerTestReplaceStorageContents
*/
public function testReplaceStorageContents($source_collections, $target_collections) {
$source = new MemoryStorage();
$target = new MemoryStorage();
// Empty the storage should be the same.
$this->assertArrayEquals(self::toArray($source), self::toArray($target));
// When the source is populated, they are not the same any more.
$this->generateRandomData($source, $source_collections);
$this->assertNotEquals(self::toArray($source), self::toArray($target));
// When the target is filled with random data they are also not the same.
$this->generateRandomData($target, $target_collections);
$this->assertNotEquals(self::toArray($source), self::toArray($target));
// Set the active collection to a random one on both source and target.
if ($source_collections) {
$collections = $source->getAllCollectionNames();
$source = $source->createCollection($collections[array_rand($collections)]);
}
if ($target_collections) {
$collections = $target->getAllCollectionNames();
$target = $target->createCollection($collections[array_rand($collections)]);
}
$source_data = self::toArray($source);
$source_name = $source->getCollectionName();
// After copying they are the same, this asserts that items not present
// in the source get removed from the target.
self::replaceStorageContents($source, $target);
$this->assertArrayEquals($source_data, self::toArray($target));
// Assert that the copy method did indeed not change the source.
$this->assertArrayEquals($source_data, self::toArray($source));
// Assert that the active collection is the same as the original source.
$this->assertEquals($source_name, $source->getCollectionName());
$this->assertEquals($source_name, $target->getCollectionName());
}
/**
* Provides data for testCheckRequirements().
*/
public function providerTestReplaceStorageContents() {
$data = [];
$data[] = [TRUE, TRUE];
$data[] = [TRUE, FALSE];
$data[] = [FALSE, TRUE];
$data[] = [FALSE, FALSE];
return $data;
}
/**
* Get the protected config data out of a MemoryStorage.
*
* @param \Drupal\Core\Config\MemoryStorage $storage
* The config storage to extract the data from.
*
* @return array
*/
protected static function toArray(MemoryStorage $storage) {
$reflection = new \ReflectionObject($storage);
$property = $reflection->getProperty('config');
$property->setAccessible(TRUE);
return $property->getValue($storage)->getArrayCopy();
}
/**
* Generate random data in a config storage.
*
* @param \Drupal\Core\Config\StorageInterface $storage
* The storage to populate with random data.
* @param bool $collections
* Add random collections or not.
*/
protected function generateRandomData(StorageInterface $storage, $collections = TRUE) {
$generator = $this->getRandomGenerator();
for ($i = 0; $i < rand(2, 10); $i++) {
$storage->write($this->randomMachineName(), (array) $generator->object());
}
if ($collections) {
for ($i = 0; $i < rand(1, 5); $i++) {
$collection = $storage->createCollection($this->randomMachineName());
for ($i = 0; $i < rand(2, 10); $i++) {
$collection->write($this->randomMachineName(), (array) $generator->object());
}
}
}
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment