-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Issue #3173393 by bircher: Add test traits to harmonize tests
- Loading branch information
Showing
2 changed files
with
227 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
<?php | ||
|
||
namespace Drupal\Tests\config_filter\Kernel; | ||
|
||
use Drupal\Core\Config\ExportStorageManager; | ||
use Drupal\Core\Config\FileStorage; | ||
use Drupal\Core\Config\ReadOnlyStorage; | ||
use Drupal\Core\Config\StorageInterface; | ||
use Drupal\Core\Lock\NullLockBackend; | ||
use Drupal\Core\Site\Settings; | ||
|
||
/** | ||
* Trait to easily test import and export of config by comparing storages. | ||
* | ||
* The "new" config storage transformation API which was added to drupal 8.8 | ||
* transforms the storage before importing and exporting via the event system. | ||
* This is a very different API from Config Filter 1.x but they work together. | ||
* | ||
* This test trait aims to provide methods to guide developers writing tests | ||
* that work independently of whether the module uses Config Filter plugins or | ||
* the new core events. You do this by comparing what you expect to be imported | ||
* or exported. | ||
* | ||
* Drupal core <8.8 with config filter: | ||
* export: | ||
* [active] ----------------------------------------> [filter wrapping [sync]] | ||
* import: | ||
* [filter wrapping [sync]] ----------------------------------------> [active] | ||
* | ||
* Drupal core >=8.8: | ||
* export: | ||
* [active] ---------------> [export transformation] -----------------> [sync] | ||
* import: | ||
* [sync] -----------------> [import transformation] ---------------> [active] | ||
* | ||
* The test for exporting should set up the active store by saving config and | ||
* what is expected to be exported and then compare the export storage with it. | ||
* The test for importing should set up the sync store by writing files (or by | ||
* saving arrays to the FileStorage) and then comparing the expectations with | ||
* the import storage. | ||
* | ||
* Tests using this trait will work with Config Filter 1.x and 2.x and they will | ||
* also work when using the core api directly provided the module has the same | ||
* behaviour with the event listener as with the config filter plugin. | ||
* | ||
* @see \Drupal\Tests\config_filter\Kernel\ExampleStorageKernelTest | ||
*/ | ||
trait ConfigStorageTestTrait { | ||
|
||
/** | ||
* Copies configuration objects from source storage to target storage. | ||
* | ||
* This method is defined in a trait used in KernelTestBase. | ||
*/ | ||
abstract protected function copyConfig(StorageInterface $source_storage, StorageInterface $target_storage); | ||
|
||
/** | ||
* The active config store, read only. | ||
* | ||
* @return \Drupal\Core\Config\StorageInterface | ||
* The storage with the active config. | ||
*/ | ||
protected function getActiveStorage(): StorageInterface { | ||
// Return the active storage in read-only mode. Config should be properly | ||
// saved and the active storage should not be directly manipulated. | ||
return new ReadOnlyStorage($this->container->get('config.storage')); | ||
} | ||
|
||
/** | ||
* The file storage associated with the sync storage. | ||
* | ||
* Use this to manipulate the files in the sync folder. In order to export the | ||
* configuration to the sync storage use the following: | ||
* @code | ||
* $this->copyConfig($this->getExportStorage(), $this->getSyncFileStorage()); | ||
* @endcode | ||
* | ||
* @return \Drupal\Core\Config\StorageInterface | ||
* The storage with the files from the sync folder. | ||
*/ | ||
protected function getSyncFileStorage(): StorageInterface { | ||
// We do not return the config.storage.sync service so that we can bypass | ||
// config filter. | ||
return new FileStorage(Settings::get('config_sync_directory')); | ||
} | ||
|
||
/** | ||
* Trigger the export transformation and return its result. | ||
* | ||
* @return \Drupal\Core\Config\StorageInterface | ||
* The storage with the config to be exported. | ||
*/ | ||
protected function getExportStorage(): StorageInterface { | ||
$manager = new ExportStorageManager( | ||
$this->container->get('config.storage'), | ||
$this->container->get('database'), | ||
$this->container->get('event_dispatcher'), | ||
// We use the null lock because these tests can anyway not be run in | ||
// parallel. We would have to lock around accessing the storage and | ||
// copying the content to a new memory storage. | ||
new NullLockBackend() | ||
); | ||
// This is the same essentially as the config.storage.export service | ||
// but the container doesn't cache it so we can access it several times | ||
// with updated config in the same test and trigger the transformation anew. | ||
return $manager->getStorage(); | ||
} | ||
|
||
/** | ||
* Trigger the import transformation and return its result. | ||
* | ||
* @return \Drupal\Core\Config\StorageInterface | ||
* The storage with the config to be imported. | ||
*/ | ||
protected function getImportStorage(): StorageInterface { | ||
// Transform the sync storage for import. | ||
return $this->container->get('config.import_transformer')->transform($this->container->get('config.storage.sync')); | ||
} | ||
|
||
/** | ||
* Asserts that two config storage objects have the same content. | ||
* | ||
* @param \Drupal\Core\Config\StorageInterface $expected | ||
* The storage with the expected data. | ||
* @param \Drupal\Core\Config\StorageInterface $actual | ||
* The storage with the actual data. | ||
* @param string $message | ||
* The message to add to the assertion. | ||
*/ | ||
protected static function assertStorageEquals(StorageInterface $expected, StorageInterface $actual, string $message = '') { | ||
// The same collections have to exist. | ||
static::assertEqualsCanonicalizing($expected->getAllCollectionNames(), $actual->getAllCollectionNames(), $message); | ||
// Now loop over all collections and assert the data to be equal. | ||
foreach (array_merge([StorageInterface::DEFAULT_COLLECTION], $expected->getAllCollectionNames()) as $collection) { | ||
$expected_collection = $expected->createCollection($collection); | ||
$actual_collection = $actual->createCollection($collection); | ||
// The same names are present in both. | ||
static::assertEqualsCanonicalizing($expected_collection->listAll(), $actual_collection->listAll(), $message); | ||
foreach ($expected_collection->listAll() as $name) { | ||
// The same data can be read from both. | ||
static::assertEquals($expected_collection->read($name), $actual_collection->read($name), $message . ' ' . $name); | ||
} | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
<?php | ||
|
||
namespace Drupal\Tests\config_filter\Kernel; | ||
|
||
use Drupal\Core\Config\MemoryStorage; | ||
use Drupal\KernelTests\KernelTestBase; | ||
|
||
/** | ||
* Example class to test the export and import with the test trait. | ||
* | ||
* @group config_filter_example | ||
*/ | ||
class ExampleStorageKernelTest extends KernelTestBase { | ||
|
||
use ConfigStorageTestTrait; | ||
|
||
/** | ||
* Modules to enable. | ||
* | ||
* @var array | ||
*/ | ||
public static $modules = [ | ||
'system', | ||
'config_filter', | ||
'config_filter_test', | ||
]; | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
protected function setUp(): void { | ||
parent::setUp(); | ||
// Install the system config. | ||
$this->installConfig(['system']); | ||
|
||
// Set the site name and slogan. | ||
$this->config('system.site')->set('name', 'Config Test')->set('slogan', 'Testing is fun!')->save(); | ||
} | ||
|
||
/** | ||
* Example to test export. | ||
*/ | ||
public function testExampleExport() { | ||
// Set up expectation for export. | ||
$expectedExport = new MemoryStorage(); | ||
$this->copyConfig($this->getActiveStorage(), $expectedExport); | ||
|
||
// Simulate the filter. | ||
$system_site = $expectedExport->read('system.site'); | ||
// Exporting means triggering the write filter methods. | ||
$system_site['slogan'] = 'Testing is fun! Arrr'; | ||
$expectedExport->write('system.site', $system_site); | ||
|
||
// Do assertions. | ||
static::assertStorageEquals($expectedExport, $this->getExportStorage()); | ||
} | ||
|
||
/** | ||
* Example to test import. | ||
*/ | ||
public function testExampleImport() { | ||
// Write active config to file system as is. | ||
// This is not a config export, but for the sake of the test we set up | ||
// the sync storage to contain the same content as the active config. | ||
$this->copyConfig($this->getActiveStorage(), $this->getSyncFileStorage()); | ||
|
||
// Set up expectation for import. | ||
$expectedImport = new MemoryStorage(); | ||
$this->copyConfig($this->getSyncFileStorage(), $expectedImport); | ||
|
||
// Simulate the filter. | ||
$system_site = $expectedImport->read('system.site'); | ||
// Importing means triggering the read filter methods. | ||
$system_site['name'] = 'Config Test Arrr'; | ||
$expectedImport->write('system.site', $system_site); | ||
|
||
// Do assertions. | ||
static::assertStorageEquals($expectedImport, $this->getImportStorage()); | ||
} | ||
|
||
} |