class CheckpointStorageTest

Same name in this branch
  1. 11.x core/tests/Drupal/KernelTests/Core/Config/Storage/Checkpoint/CheckpointStorageTest.php \Drupal\KernelTests\Core\Config\Storage\Checkpoint\CheckpointStorageTest
Same name in other branches
  1. 10 core/tests/Drupal/KernelTests/Core/Config/Storage/Checkpoint/CheckpointStorageTest.php \Drupal\KernelTests\Core\Config\Storage\Checkpoint\CheckpointStorageTest
  2. 10 core/tests/Drupal/Tests/Core/Config/Checkpoint/CheckpointStorageTest.php \Drupal\Tests\Core\Config\Checkpoint\CheckpointStorageTest

@coversDefaultClass \Drupal\Core\Config\Checkpoint\CheckpointStorage @group Config

Hierarchy

Expanded class hierarchy of CheckpointStorageTest

File

core/tests/Drupal/Tests/Core/Config/Checkpoint/CheckpointStorageTest.php, line 27

Namespace

Drupal\Tests\Core\Config\Checkpoint
View source
class CheckpointStorageTest extends UnitTestCase {
    use StorageCopyTrait;
    
    /**
     * The memory storage containing the data.
     *
     * @var \Drupal\Core\Config\MemoryStorage
     */
    protected MemoryStorage $memory;
    
    /**
     * The checkpoint storage under test.
     *
     * @var \Drupal\Core\Config\Checkpoint\CheckpointStorage
     */
    protected CheckpointStorage $storage;
    
    /**
     * {@inheritdoc}
     */
    protected function setUp() : void {
        parent::setUp();
        // Set up a memory storage we can manipulate to set fixtures.
        $this->memory = new MemoryStorage();
        $keyValueMemoryFactory = new KeyValueMemoryFactory();
        $state = new State($keyValueMemoryFactory, new NullBackend('test'), new NullLockBackend());
        $time = new Time();
        $checkpoints = new LinearHistory($state, $time);
        $this->storage = new CheckpointStorage($this->memory, $checkpoints, $keyValueMemoryFactory);
    }
    
    /**
     * @covers ::checkpoint
     * @covers \Drupal\Core\Config\Checkpoint\Checkpoint
     */
    public function testCheckpointCreation() : void {
        $checkpoint = $this->storage
            ->checkpoint('Test');
        $this->assertInstanceOf(Checkpoint::class, $checkpoint);
        $this->assertSame('Test', $checkpoint->label);
        $checkpoint2 = $this->storage
            ->checkpoint('This will not make a checkpoint because nothing has changed');
        $this->assertSame($checkpoint2, $checkpoint);
        $config = $this->prophesize(Config::class);
        $config->getName()
            ->willReturn('test.config');
        $config->getOriginal('', FALSE)
            ->willReturn([]);
        $config->getRawData()
            ->willReturn([
            'foo' => 'bar',
        ]);
        $config->getStorage()
            ->willReturn($this->storage);
        $event = new ConfigCrudEvent($config->reveal());
        $this->storage
            ->onConfigSaveAndDelete($event);
        $checkpoint3 = $this->storage
            ->checkpoint('Created test.config');
        $this->assertNotSame($checkpoint3, $checkpoint);
        $this->assertSame('Created test.config', $checkpoint3->label);
        $checkpoint4 = $this->storage
            ->checkpoint('This will not create a checkpoint either');
        $this->assertSame($checkpoint4, $checkpoint3);
        // Simulate a save with no change.
        $config = $this->prophesize(Config::class);
        $config->getName()
            ->willReturn('test.config');
        $config->getOriginal('', FALSE)
            ->willReturn([
            'foo' => 'bar',
        ]);
        $config->getRawData()
            ->willReturn([
            'foo' => 'bar',
        ]);
        $config->getStorage()
            ->willReturn($this->storage);
        $event = new ConfigCrudEvent($config->reveal());
        $this->storage
            ->onConfigSaveAndDelete($event);
        $checkpoint5 = $this->storage
            ->checkpoint('Save with no change');
        $this->assertSame($checkpoint5, $checkpoint3);
        // Create collection and ensure that checkpoints are kept in sync.
        $collection = $this->storage
            ->createCollection('test');
        $config = $this->prophesize(Config::class);
        $config->getName()
            ->willReturn('test.config');
        $config->getOriginal('', FALSE)
            ->willReturn([
            'foo' => 'bar',
        ]);
        $config->getRawData()
            ->willReturn([
            'foo' => 'collection_bar',
        ]);
        $config->getStorage()
            ->willReturn($collection);
        $event = new ConfigCrudEvent($config->reveal());
        $collection->onConfigSaveAndDelete($event);
        $checkpoint6 = $this->storage
            ->checkpoint('Save in collection');
        $this->assertNotSame($checkpoint6, $checkpoint3);
        $this->assertSame($collection->checkpoint('Calling checkpoint on collection'), $checkpoint6);
    }
    
    /**
     * @covers ::exists
     * @covers ::read
     * @covers ::readMultiple
     * @covers ::listAll
     *
     * @dataProvider readMethodsProvider
     */
    public function testReadOperations(string $method, array $arguments, array $fixture) : void {
        // Create a checkpoint so the checkpoint storage can be read from.
        $this->storage
            ->checkpoint('');
        $this->setRandomFixtureConfig($fixture);
        $expected = call_user_func_array([
            $this->memory,
            $method,
        ], $arguments);
        $actual = call_user_func_array([
            $this->storage,
            $method,
        ], $arguments);
        $this->assertEquals($expected, $actual);
    }
    
    /**
     * Provide the methods that work transparently.
     *
     * @return array
     *   The data.
     */
    public static function readMethodsProvider() : array {
        $fixture = [
            StorageInterface::DEFAULT_COLLECTION => [
                'config.a',
                'config.b',
                'other.a',
            ],
        ];
        $data = [];
        $data[] = [
            'exists',
            [
                'config.a',
            ],
            $fixture,
        ];
        $data[] = [
            'exists',
            [
                'not.existing',
            ],
            $fixture,
        ];
        $data[] = [
            'read',
            [
                'config.a',
            ],
            $fixture,
        ];
        $data[] = [
            'read',
            [
                'not.existing',
            ],
            $fixture,
        ];
        $data[] = [
            'readMultiple',
            [
                [
                    'config.a',
                    'config.b',
                    'not',
                ],
            ],
            $fixture,
        ];
        $data[] = [
            'listAll',
            [
                '',
            ],
            $fixture,
        ];
        $data[] = [
            'listAll',
            [
                'config',
            ],
            $fixture,
        ];
        $data[] = [
            'listAll',
            [
                'none',
            ],
            $fixture,
        ];
        return $data;
    }
    
    /**
     * @covers ::write
     * @covers ::delete
     * @covers ::rename
     * @covers ::deleteAll
     *
     * @dataProvider writeMethodsProvider
     */
    public function testWriteOperations(string $method, array $arguments, array $fixture) : void {
        $this->setRandomFixtureConfig($fixture);
        // Create an independent memory storage as a backup.
        $backup = new MemoryStorage();
        static::replaceStorageContents($this->memory, $backup);
        try {
            call_user_func_array([
                $this->storage,
                $method,
            ], $arguments);
            $this->fail("exception not thrown");
        } catch (\BadMethodCallException $exception) {
            $this->assertEquals(CheckpointStorage::class . '::' . $method . ' is not allowed on a CheckpointStorage', $exception->getMessage());
        }
        // Assert that the memory storage has not been altered.
        $this->assertEquals($backup, $this->memory);
    }
    
    /**
     * Provide the methods that throw an exception.
     *
     * @return array
     *   The data
     */
    public static function writeMethodsProvider() : array {
        $fixture = [
            StorageInterface::DEFAULT_COLLECTION => [
                'config.a',
                'config.b',
            ],
        ];
        $data = [];
        $data[] = [
            'write',
            [
                'config.a',
                (array) Random::getGenerator()->object(),
            ],
            $fixture,
        ];
        $data[] = [
            'write',
            [
                Random::machineName(),
                (array) Random::getGenerator()->object(),
            ],
            $fixture,
        ];
        $data[] = [
            'delete',
            [
                'config.a',
            ],
            $fixture,
        ];
        $data[] = [
            'delete',
            [
                Random::machineName(),
            ],
            $fixture,
        ];
        $data[] = [
            'rename',
            [
                'config.a',
                'config.b',
            ],
            $fixture,
        ];
        $data[] = [
            'rename',
            [
                'config.a',
                Random::machineName(),
            ],
            $fixture,
        ];
        $data[] = [
            'rename',
            [
                Random::machineName(),
                Random::machineName(),
            ],
            $fixture,
        ];
        $data[] = [
            'deleteAll',
            [
                '',
            ],
            $fixture,
        ];
        $data[] = [
            'deleteAll',
            [
                'config',
            ],
            $fixture,
        ];
        $data[] = [
            'deleteAll',
            [
                'other',
            ],
            $fixture,
        ];
        return $data;
    }
    
    /**
     * @covers ::getAllCollectionNames
     * @covers ::getCollectionName
     * @covers ::createCollection
     */
    public function testCollections() : void {
        $ref_readFromCheckpoint = new \ReflectionProperty($this->storage, 'readFromCheckpoint');
        // Create some checkpoints so the checkpoint storage can be read from.
        $checkpoint1 = $this->storage
            ->checkpoint('1');
        $config = $this->prophesize(Config::class);
        $config->getName()
            ->willReturn('test.config');
        $config->getOriginal('', FALSE)
            ->willReturn([]);
        $config->getRawData()
            ->willReturn([
            'foo' => 'bar',
        ]);
        $config->getStorage()
            ->willReturn($this->storage);
        $event = new ConfigCrudEvent($config->reveal());
        $this->storage
            ->onConfigSaveAndDelete($event);
        $checkpoint2 = $this->storage
            ->checkpoint('2');
        $fixture = [
            StorageInterface::DEFAULT_COLLECTION => [
                $this->randomMachineName(),
            ],
            'A' => [
                $this->randomMachineName(),
            ],
            'B' => [
                $this->randomMachineName(),
            ],
            'C' => [
                $this->randomMachineName(),
            ],
        ];
        $this->setRandomFixtureConfig($fixture);
        $this->assertEquals([
            'A',
            'B',
            'C',
        ], $this->storage
            ->getAllCollectionNames());
        foreach (array_keys($fixture) as $collection) {
            $storage = $this->storage
                ->createCollection($collection);
            // Assert that the collection storage is still a checkpoint storage.
            $this->assertInstanceOf(CheckpointStorage::class, $storage);
            $this->assertEquals($collection, $storage->getCollectionName());
            // Ensure that the
            // \Drupal\Core\Config\Checkpoint\CheckpointStorage::$readFromCheckpoint
            // property is kept in sync.
            $this->storage
                ->setCheckpointToReadFrom($checkpoint2);
            $this->assertSame($checkpoint2->id, $ref_readFromCheckpoint->getValue($storage->createCollection($collection))?->id);
            if (isset($previous_collection)) {
                $previous_collection->setCheckpointToReadFrom($checkpoint1);
                $this->assertSame($checkpoint1->id, $ref_readFromCheckpoint->getValue($storage->createCollection($collection))?->id);
                $this->assertSame($checkpoint1->id, $ref_readFromCheckpoint->getValue($this->storage
                    ->createCollection($collection))?->id);
            }
            // Save the storage in a variable so we can test use
            // setCheckpointToReadFrom() on it.
            $previous_collection = $storage;
        }
    }
    
    /**
     * @covers ::encode
     * @covers ::decode
     */
    public function testEncodeDecode() : void {
        $array = (array) $this->getRandomGenerator()
            ->object();
        $string = $this->getRandomGenerator()
            ->string();
        // Assert reversibility of encoding and decoding.
        $this->assertEquals($array, $this->storage
            ->decode($this->storage
            ->encode($array)));
        $this->assertEquals($string, $this->storage
            ->encode($this->storage
            ->decode($string)));
        // Assert same results as the decorated storage.
        $this->assertEquals($this->memory
            ->encode($array), $this->storage
            ->encode($array));
        $this->assertEquals($this->memory
            ->decode($string), $this->storage
            ->decode($string));
    }
    
    /**
     * Generate random config in the memory storage.
     *
     * @param array $config
     *   The config keys, keyed by the collection.
     */
    protected function setRandomFixtureConfig(array $config) : void {
        // Erase previous fixture.
        foreach (array_merge([
            StorageInterface::DEFAULT_COLLECTION,
        ], $this->memory
            ->getAllCollectionNames()) as $collection) {
            $this->memory
                ->createCollection($collection)
                ->deleteAll();
        }
        foreach ($config as $collection => $keys) {
            $storage = $this->memory
                ->createCollection($collection);
            foreach ($keys as $key) {
                // Create some random config.
                $storage->write($key, (array) $this->getRandomGenerator()
                    ->object());
            }
        }
    }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title
CheckpointStorageTest::$memory protected property The memory storage containing the data.
CheckpointStorageTest::$storage protected property The checkpoint storage under test.
CheckpointStorageTest::readMethodsProvider public static function Provide the methods that work transparently.
CheckpointStorageTest::setRandomFixtureConfig protected function Generate random config in the memory storage.
CheckpointStorageTest::setUp protected function Overrides UnitTestCase::setUp
CheckpointStorageTest::testCheckpointCreation public function @covers ::checkpoint
@covers \Drupal\Core\Config\Checkpoint\Checkpoint
CheckpointStorageTest::testCollections public function @covers ::getAllCollectionNames
@covers ::getCollectionName
@covers ::createCollection
CheckpointStorageTest::testEncodeDecode public function @covers ::encode
@covers ::decode
CheckpointStorageTest::testReadOperations public function @covers ::exists
@covers ::read
@covers ::readMultiple
@covers ::listAll
CheckpointStorageTest::testWriteOperations public function @covers ::write
@covers ::delete
@covers ::rename
@covers ::deleteAll
CheckpointStorageTest::writeMethodsProvider public static function Provide the methods that throw an exception.
ExpectDeprecationTrait::expectDeprecation public function Adds an expected deprecation.
ExpectDeprecationTrait::setUpErrorHandler public function Sets up the test error handler.
ExpectDeprecationTrait::tearDownErrorHandler public function Tears down the test error handler.
RandomGeneratorTrait::getRandomGenerator protected function Gets the random generator for the utility methods.
RandomGeneratorTrait::randomMachineName protected function Generates a unique random string containing letters and numbers.
RandomGeneratorTrait::randomObject public function Generates a random PHP object.
RandomGeneratorTrait::randomString public function Generates a pseudo-random string of ASCII characters of codes 32 to 126.
StorageCopyTrait::replaceStorageContents protected static function Copy the configuration from one storage to another and remove stale items.
UnitTestCase::$root protected property The app root.
UnitTestCase::getClassResolverStub protected function Returns a stub class resolver.
UnitTestCase::getConfigFactoryStub public function Returns a stub config factory that behaves according to the passed array.
UnitTestCase::getContainerWithCacheTagsInvalidator protected function Sets up a container with a cache tags invalidator.
UnitTestCase::getStringTranslationStub public function Returns a stub translation manager that just returns the passed string.
UnitTestCase::setDebugDumpHandler public static function Registers the dumper CLI handler when the DebugDump extension is enabled.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.