123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570 |
- <?php
- /**
- * @file
- * Contains \Drupal\Tests\Core\Plugin\ContextHandlerTest.
- */
- namespace Drupal\Tests\Core\Plugin;
- use Drupal\Component\Plugin\ConfigurableInterface;
- use Drupal\Component\Plugin\DependentPluginInterface;
- use Drupal\Component\Plugin\Definition\ContextAwarePluginDefinitionInterface;
- use Drupal\Component\Plugin\Definition\ContextAwarePluginDefinitionTrait;
- use Drupal\Component\Plugin\Definition\PluginDefinition;
- use Drupal\Component\Plugin\Exception\ContextException;
- use Drupal\Component\Plugin\Exception\MissingValueContextException;
- use Drupal\Core\Cache\NullBackend;
- use Drupal\Core\DependencyInjection\ClassResolverInterface;
- use Drupal\Core\DependencyInjection\ContainerBuilder;
- use Drupal\Core\Extension\ModuleHandlerInterface;
- use Drupal\Core\Plugin\Context\ContextDefinition;
- use Drupal\Core\Plugin\Context\ContextHandler;
- use Drupal\Core\Plugin\ContextAwarePluginInterface;
- use Drupal\Core\TypedData\TypedDataManager;
- use Drupal\Core\Validation\ConstraintManager;
- use Drupal\Tests\UnitTestCase;
- use Prophecy\Argument;
- /**
- * @coversDefaultClass \Drupal\Core\Plugin\Context\ContextHandler
- * @group Plugin
- */
- class ContextHandlerTest extends UnitTestCase {
- /**
- * The context handler.
- *
- * @var \Drupal\Core\Plugin\Context\ContextHandler
- */
- protected $contextHandler;
- /**
- * {@inheritdoc}
- */
- protected function setUp() {
- parent::setUp();
- $this->contextHandler = new ContextHandler();
- $namespaces = new \ArrayObject([
- 'Drupal\\Core\\TypedData' => $this->root . '/core/lib/Drupal/Core/TypedData',
- 'Drupal\\Core\\Validation' => $this->root . '/core/lib/Drupal/Core/Validation',
- ]);
- $cache_backend = new NullBackend('cache');
- $module_handler = $this->prophesize(ModuleHandlerInterface::class);
- $class_resolver = $this->prophesize(ClassResolverInterface::class);
- $class_resolver->getInstanceFromDefinition(Argument::type('string'))->will(function ($arguments) {
- $class_name = $arguments[0];
- return new $class_name();
- });
- $type_data_manager = new TypedDataManager($namespaces, $cache_backend, $module_handler->reveal(), $class_resolver->reveal());
- $type_data_manager->setValidationConstraintManager(
- new ConstraintManager($namespaces, $cache_backend, $module_handler->reveal())
- );
- $container = new ContainerBuilder();
- $container->set('typed_data_manager', $type_data_manager);
- \Drupal::setContainer($container);
- }
- /**
- * @covers ::checkRequirements
- *
- * @dataProvider providerTestCheckRequirements
- */
- public function testCheckRequirements($contexts, $requirements, $expected) {
- $this->assertSame($expected, $this->contextHandler->checkRequirements($contexts, $requirements));
- }
- /**
- * Provides data for testCheckRequirements().
- */
- public function providerTestCheckRequirements() {
- $requirement_optional = new ContextDefinition();
- $requirement_optional->setRequired(FALSE);
- $requirement_any = new ContextDefinition();
- $requirement_any->setRequired(TRUE);
- $context_any = $this->createMock('Drupal\Core\Plugin\Context\ContextInterface');
- $context_any->expects($this->atLeastOnce())
- ->method('getContextDefinition')
- ->will($this->returnValue(new ContextDefinition('any')));
- $requirement_specific = new ContextDefinition('string');
- $requirement_specific->setConstraints(['Blank' => []]);
- $context_constraint_mismatch = $this->createMock('Drupal\Core\Plugin\Context\ContextInterface');
- $context_constraint_mismatch->expects($this->atLeastOnce())
- ->method('getContextDefinition')
- ->will($this->returnValue(new ContextDefinition('foo')));
- $context_datatype_mismatch = $this->createMock('Drupal\Core\Plugin\Context\ContextInterface');
- $context_datatype_mismatch->expects($this->atLeastOnce())
- ->method('getContextDefinition')
- ->will($this->returnValue(new ContextDefinition('fuzzy')));
- $context_definition_specific = new ContextDefinition('string');
- $context_definition_specific->setConstraints(['Blank' => []]);
- $context_specific = $this->createMock('Drupal\Core\Plugin\Context\ContextInterface');
- $context_specific->expects($this->atLeastOnce())
- ->method('getContextDefinition')
- ->will($this->returnValue($context_definition_specific));
- $data = [];
- $data[] = [[], [], TRUE];
- $data[] = [[], [$requirement_any], FALSE];
- $data[] = [[], [$requirement_optional], TRUE];
- $data[] = [[], [$requirement_any, $requirement_optional], FALSE];
- $data[] = [[$context_any], [$requirement_any], TRUE];
- $data[] = [[$context_constraint_mismatch], [$requirement_specific], FALSE];
- $data[] = [[$context_datatype_mismatch], [$requirement_specific], FALSE];
- $data[] = [[$context_specific], [$requirement_specific], TRUE];
- return $data;
- }
- /**
- * @covers ::getMatchingContexts
- *
- * @dataProvider providerTestGetMatchingContexts
- */
- public function testGetMatchingContexts($contexts, $requirement, $expected = NULL) {
- if (is_null($expected)) {
- $expected = $contexts;
- }
- $this->assertSame($expected, $this->contextHandler->getMatchingContexts($contexts, $requirement));
- }
- /**
- * Provides data for testGetMatchingContexts().
- */
- public function providerTestGetMatchingContexts() {
- $requirement_any = new ContextDefinition();
- $requirement_specific = new ContextDefinition('string');
- $requirement_specific->setConstraints(['Blank' => []]);
- $context_any = $this->createMock('Drupal\Core\Plugin\Context\ContextInterface');
- $context_any->expects($this->atLeastOnce())
- ->method('getContextDefinition')
- ->will($this->returnValue(new ContextDefinition('any')));
- $context_constraint_mismatch = $this->createMock('Drupal\Core\Plugin\Context\ContextInterface');
- $context_constraint_mismatch->expects($this->atLeastOnce())
- ->method('getContextDefinition')
- ->will($this->returnValue(new ContextDefinition('foo')));
- $context_datatype_mismatch = $this->createMock('Drupal\Core\Plugin\Context\ContextInterface');
- $context_datatype_mismatch->expects($this->atLeastOnce())
- ->method('getContextDefinition')
- ->will($this->returnValue(new ContextDefinition('fuzzy')));
- $context_definition_specific = new ContextDefinition('string');
- $context_definition_specific->setConstraints(['Blank' => []]);
- $context_specific = $this->createMock('Drupal\Core\Plugin\Context\ContextInterface');
- $context_specific->expects($this->atLeastOnce())
- ->method('getContextDefinition')
- ->will($this->returnValue($context_definition_specific));
- $data = [];
- // No context will return no valid contexts.
- $data[] = [[], $requirement_any];
- // A context with a generic matching requirement is valid.
- $data[] = [[$context_any], $requirement_any];
- // A context with a specific matching requirement is valid.
- $data[] = [[$context_specific], $requirement_specific];
- // A context with a mismatched constraint is invalid.
- $data[] = [[$context_constraint_mismatch], $requirement_specific, []];
- // A context with a mismatched datatype is invalid.
- $data[] = [[$context_datatype_mismatch], $requirement_specific, []];
- return $data;
- }
- /**
- * @covers ::filterPluginDefinitionsByContexts
- *
- * @dataProvider providerTestFilterPluginDefinitionsByContexts
- */
- public function testFilterPluginDefinitionsByContexts($has_context, $definitions, $expected) {
- if ($has_context) {
- $context = $this->createMock('Drupal\Core\Plugin\Context\ContextInterface');
- $expected_context_definition = (new ContextDefinition('string'))->setConstraints(['Blank' => []]);
- $context->expects($this->atLeastOnce())
- ->method('getContextDefinition')
- ->will($this->returnValue($expected_context_definition));
- $contexts = [$context];
- }
- else {
- $contexts = [];
- }
- $this->assertSame($expected, $this->contextHandler->filterPluginDefinitionsByContexts($contexts, $definitions));
- }
- /**
- * Provides data for testFilterPluginDefinitionsByContexts().
- */
- public function providerTestFilterPluginDefinitionsByContexts() {
- $data = [];
- $plugins = [];
- // No context and no plugins, no plugins available.
- $data[] = [FALSE, $plugins, []];
- $plugins = [
- 'expected_array_plugin' => [],
- 'expected_object_plugin' => new ContextAwarePluginDefinition(),
- ];
- // No context, all plugins available.
- $data[] = [FALSE, $plugins, $plugins];
- $plugins = [
- 'expected_array_plugin' => ['context_definitions' => []],
- 'expected_object_plugin' => new ContextAwarePluginDefinition(),
- ];
- // No context, all plugins available.
- $data[] = [FALSE, $plugins, $plugins];
- $plugins = [
- 'expected_array_plugin' => [
- 'context_definitions' => ['context1' => new ContextDefinition('string')],
- ],
- 'expected_object_plugin' => (new ContextAwarePluginDefinition())
- ->addContextDefinition('context1', new ContextDefinition('string')),
- ];
- // Missing context, no plugins available.
- $data[] = [FALSE, $plugins, []];
- // Satisfied context, all plugins available.
- $data[] = [TRUE, $plugins, $plugins];
- $mismatched_context_definition = (new ContextDefinition('expected_data_type'))->setConstraints(['mismatched_constraint_name' => 'mismatched_constraint_value']);
- $plugins = [
- 'expected_array_plugin' => [
- 'context_definitions' => ['context1' => $mismatched_context_definition],
- ],
- 'expected_object_plugin' => (new ContextAwarePluginDefinition())
- ->addContextDefinition('context1', $mismatched_context_definition),
- ];
- // Mismatched constraints, no plugins available.
- $data[] = [TRUE, $plugins, []];
- $optional_mismatched_context_definition = clone $mismatched_context_definition;
- $optional_mismatched_context_definition->setRequired(FALSE);
- $plugins = [
- 'expected_array_plugin' => [
- 'context_definitions' => ['context1' => $optional_mismatched_context_definition],
- ],
- 'expected_object_plugin' => (new ContextAwarePluginDefinition())
- ->addContextDefinition('context1', $optional_mismatched_context_definition),
- ];
- // Optional mismatched constraint, all plugins available.
- $data[] = [FALSE, $plugins, $plugins];
- $expected_context_definition = (new ContextDefinition('string'))->setConstraints(['Blank' => []]);
- $plugins = [
- 'expected_array_plugin' => [
- 'context_definitions' => ['context1' => $expected_context_definition],
- ],
- 'expected_object_plugin' => (new ContextAwarePluginDefinition())
- ->addContextDefinition('context1', $expected_context_definition),
- ];
- // Satisfied context with constraint, all plugins available.
- $data[] = [TRUE, $plugins, $plugins];
- $optional_expected_context_definition = clone $expected_context_definition;
- $optional_expected_context_definition->setRequired(FALSE);
- $plugins = [
- 'expected_array_plugin' => [
- 'context_definitions' => ['context1' => $optional_expected_context_definition],
- ],
- 'expected_object_plugin' => (new ContextAwarePluginDefinition())
- ->addContextDefinition('context1', $optional_expected_context_definition),
- ];
- // Optional unsatisfied context, all plugins available.
- $data[] = [FALSE, $plugins, $plugins];
- $unexpected_context_definition = (new ContextDefinition('unexpected_data_type'))->setConstraints(['mismatched_constraint_name' => 'mismatched_constraint_value']);
- $plugins = [
- 'unexpected_array_plugin' => [
- 'context_definitions' => ['context1' => $unexpected_context_definition],
- ],
- 'expected_array_plugin' => [
- 'context_definitions' => ['context2' => new ContextDefinition('string')],
- ],
- 'unexpected_object_plugin' => (new ContextAwarePluginDefinition())
- ->addContextDefinition('context1', $unexpected_context_definition),
- 'expected_object_plugin' => (new ContextAwarePluginDefinition())
- ->addContextDefinition('context2', new ContextDefinition('string')),
- ];
- // Context only satisfies two plugins.
- $data[] = [
- TRUE,
- $plugins,
- [
- 'expected_array_plugin' => $plugins['expected_array_plugin'],
- 'expected_object_plugin' => $plugins['expected_object_plugin'],
- ],
- ];
- return $data;
- }
- /**
- * @covers ::applyContextMapping
- */
- public function testApplyContextMapping() {
- $context_hit = $this->createMock('Drupal\Core\Plugin\Context\ContextInterface');
- $context_hit->expects($this->atLeastOnce())
- ->method('hasContextValue')
- ->willReturn(TRUE);
- $context_miss = $this->createMock('Drupal\Core\Plugin\Context\ContextInterface');
- $contexts = [
- 'hit' => $context_hit,
- 'miss' => $context_miss,
- ];
- $context_definition = $this->createMock('Drupal\Core\Plugin\Context\ContextDefinitionInterface');
- $plugin = $this->createMock('Drupal\Core\Plugin\ContextAwarePluginInterface');
- $plugin->expects($this->once())
- ->method('getContextMapping')
- ->willReturn([]);
- $plugin->expects($this->once())
- ->method('getContextDefinitions')
- ->will($this->returnValue(['hit' => $context_definition]));
- $plugin->expects($this->once())
- ->method('setContext')
- ->with('hit', $context_hit);
- // Make sure that the cacheability metadata is passed to the plugin context.
- $plugin_context = $this->createMock('Drupal\Core\Plugin\Context\ContextInterface');
- $plugin_context->expects($this->once())
- ->method('addCacheableDependency')
- ->with($context_hit);
- $plugin->expects($this->once())
- ->method('getContext')
- ->with('hit')
- ->willReturn($plugin_context);
- $this->contextHandler->applyContextMapping($plugin, $contexts);
- }
- /**
- * @covers ::applyContextMapping
- */
- public function testApplyContextMappingMissingRequired() {
- $context = $this->createMock('Drupal\Core\Plugin\Context\ContextInterface');
- $context->expects($this->never())
- ->method('getContextValue');
- $contexts = [
- 'name' => $context,
- ];
- $context_definition = $this->createMock('Drupal\Core\Plugin\Context\ContextDefinitionInterface');
- $context_definition->expects($this->atLeastOnce())
- ->method('isRequired')
- ->willReturn(TRUE);
- $plugin = $this->createMock('Drupal\Tests\Core\Plugin\TestConfigurableContextAwarePluginInterface');
- $plugin->expects($this->once())
- ->method('getContextMapping')
- ->willReturn([]);
- $plugin->expects($this->once())
- ->method('getContextDefinitions')
- ->will($this->returnValue(['hit' => $context_definition]));
- $plugin->expects($this->never())
- ->method('setContext');
- // No context, so no cacheability metadata can be passed along.
- $plugin->expects($this->any())
- ->method('getContext')
- ->willThrowException(new ContextException());
- $this->expectException(MissingValueContextException::class);
- $this->expectExceptionMessage('Required contexts without a value: hit');
- $this->contextHandler->applyContextMapping($plugin, $contexts);
- }
- /**
- * @covers ::applyContextMapping
- */
- public function testApplyContextMappingMissingNotRequired() {
- $context = $this->createMock('Drupal\Core\Plugin\Context\ContextInterface');
- $context->expects($this->never())
- ->method('getContextValue');
- $contexts = [
- 'name' => $context,
- ];
- $context_definition = $this->createMock('Drupal\Core\Plugin\Context\ContextDefinitionInterface');
- $context_definition->expects($this->atLeastOnce())
- ->method('isRequired')
- ->willReturn(FALSE);
- $plugin = $this->createMock('Drupal\Tests\Core\Plugin\TestConfigurableContextAwarePluginInterface');
- $plugin->expects($this->once())
- ->method('getContextMapping')
- ->willReturn(['optional' => 'missing']);
- $plugin->expects($this->once())
- ->method('getContextDefinitions')
- ->will($this->returnValue(['optional' => $context_definition]));
- $plugin->expects($this->never())
- ->method('setContext');
- // No context, so no cacheability metadata can be passed along.
- $plugin->expects($this->any())
- ->method('getContext')
- ->willThrowException(new ContextException());
- $this->contextHandler->applyContextMapping($plugin, $contexts);
- }
- /**
- * @covers ::applyContextMapping
- */
- public function testApplyContextMappingNoValueRequired() {
- $context = $this->createMock('Drupal\Core\Plugin\Context\ContextInterface');
- $context->expects($this->never())
- ->method('getContextValue');
- $context->expects($this->atLeastOnce())
- ->method('hasContextValue')
- ->willReturn(FALSE);
- $contexts = [
- 'hit' => $context,
- ];
- $context_definition = $this->createMock('Drupal\Core\Plugin\Context\ContextDefinitionInterface');
- $context_definition->expects($this->atLeastOnce())
- ->method('isRequired')
- ->willReturn(TRUE);
- $plugin = $this->createMock('Drupal\Tests\Core\Plugin\TestConfigurableContextAwarePluginInterface');
- $plugin->expects($this->once())
- ->method('getContextMapping')
- ->willReturn([]);
- $plugin->expects($this->once())
- ->method('getContextDefinitions')
- ->will($this->returnValue(['hit' => $context_definition]));
- $plugin->expects($this->never())
- ->method('setContext');
- $this->expectException(MissingValueContextException::class);
- $this->expectExceptionMessage('Required contexts without a value: hit');
- $this->contextHandler->applyContextMapping($plugin, $contexts);
- }
- /**
- * @covers ::applyContextMapping
- */
- public function testApplyContextMappingNoValueNonRequired() {
- $context = $this->createMock('Drupal\Core\Plugin\Context\ContextInterface');
- $context->expects($this->never())
- ->method('getContextValue');
- $context->expects($this->atLeastOnce())
- ->method('hasContextValue')
- ->willReturn(FALSE);
- $contexts = [
- 'hit' => $context,
- ];
- $context_definition = $this->createMock('Drupal\Core\Plugin\Context\ContextDefinitionInterface');
- $context_definition->expects($this->atLeastOnce())
- ->method('isRequired')
- ->willReturn(FALSE);
- $plugin = $this->createMock('Drupal\Tests\Core\Plugin\TestConfigurableContextAwarePluginInterface');
- $plugin->expects($this->once())
- ->method('getContextMapping')
- ->willReturn([]);
- $plugin->expects($this->once())
- ->method('getContextDefinitions')
- ->will($this->returnValue(['hit' => $context_definition]));
- $plugin->expects($this->never())
- ->method('setContext');
- $this->contextHandler->applyContextMapping($plugin, $contexts);
- }
- /**
- * @covers ::applyContextMapping
- */
- public function testApplyContextMappingConfigurableAssigned() {
- $context = $this->createMock('Drupal\Core\Plugin\Context\ContextInterface');
- $context->expects($this->atLeastOnce())
- ->method('hasContextValue')
- ->willReturn(TRUE);
- $contexts = [
- 'name' => $context,
- ];
- $context_definition = $this->createMock('Drupal\Core\Plugin\Context\ContextDefinitionInterface');
- $plugin = $this->createMock('Drupal\Tests\Core\Plugin\TestConfigurableContextAwarePluginInterface');
- $plugin->expects($this->once())
- ->method('getContextMapping')
- ->willReturn([]);
- $plugin->expects($this->once())
- ->method('getContextDefinitions')
- ->will($this->returnValue(['hit' => $context_definition]));
- $plugin->expects($this->once())
- ->method('setContext')
- ->with('hit', $context);
- // Make sure that the cacheability metadata is passed to the plugin context.
- $plugin_context = $this->createMock('Drupal\Core\Plugin\Context\ContextInterface');
- $plugin_context->expects($this->once())
- ->method('addCacheableDependency')
- ->with($context);
- $plugin->expects($this->once())
- ->method('getContext')
- ->with('hit')
- ->willReturn($plugin_context);
- $this->contextHandler->applyContextMapping($plugin, $contexts, ['hit' => 'name']);
- }
- /**
- * @covers ::applyContextMapping
- */
- public function testApplyContextMappingConfigurableAssignedMiss() {
- $context = $this->createMock('Drupal\Core\Plugin\Context\ContextInterface');
- $context->expects($this->never())
- ->method('getContextValue');
- $contexts = [
- 'name' => $context,
- ];
- $context_definition = $this->createMock('Drupal\Core\Plugin\Context\ContextDefinitionInterface');
- $plugin = $this->createMock('Drupal\Tests\Core\Plugin\TestConfigurableContextAwarePluginInterface');
- $plugin->expects($this->once())
- ->method('getContextMapping')
- ->willReturn([]);
- $plugin->expects($this->once())
- ->method('getContextDefinitions')
- ->will($this->returnValue(['hit' => $context_definition]));
- $plugin->expects($this->never())
- ->method('setContext');
- $this->expectException(ContextException::class);
- $this->expectExceptionMessage('Assigned contexts were not satisfied: miss');
- $this->contextHandler->applyContextMapping($plugin, $contexts, ['miss' => 'name']);
- }
- }
- interface TestConfigurableContextAwarePluginInterface extends ContextAwarePluginInterface, ConfigurableInterface, DependentPluginInterface {
- }
- class ContextAwarePluginDefinition extends PluginDefinition implements ContextAwarePluginDefinitionInterface {
- use ContextAwarePluginDefinitionTrait;
- }
|