contextHandler = new ContextHandler(); } /** * @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->getMock('Drupal\Core\Plugin\Context\ContextInterface'); $context_any->expects($this->atLeastOnce()) ->method('getContextDefinition') ->will($this->returnValue(new ContextDefinition('empty'))); $requirement_specific = new ContextDefinition('specific'); $requirement_specific->setConstraints(['bar' => 'baz']); $context_constraint_mismatch = $this->getMock('Drupal\Core\Plugin\Context\ContextInterface'); $context_constraint_mismatch->expects($this->atLeastOnce()) ->method('getContextDefinition') ->will($this->returnValue(new ContextDefinition('foo'))); $context_datatype_mismatch = $this->getMock('Drupal\Core\Plugin\Context\ContextInterface'); $context_datatype_mismatch->expects($this->atLeastOnce()) ->method('getContextDefinition') ->will($this->returnValue(new ContextDefinition('fuzzy'))); $context_definition_specific = new ContextDefinition('specific'); $context_definition_specific->setConstraints(['bar' => 'baz']); $context_specific = $this->getMock('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('specific'); $requirement_specific->setConstraints(['bar' => 'baz']); $context_any = $this->getMock('Drupal\Core\Plugin\Context\ContextInterface'); $context_any->expects($this->atLeastOnce()) ->method('getContextDefinition') ->will($this->returnValue(new ContextDefinition('empty'))); $context_constraint_mismatch = $this->getMock('Drupal\Core\Plugin\Context\ContextInterface'); $context_constraint_mismatch->expects($this->atLeastOnce()) ->method('getContextDefinition') ->will($this->returnValue(new ContextDefinition('foo'))); $context_datatype_mismatch = $this->getMock('Drupal\Core\Plugin\Context\ContextInterface'); $context_datatype_mismatch->expects($this->atLeastOnce()) ->method('getContextDefinition') ->will($this->returnValue(new ContextDefinition('fuzzy'))); $context_definition_specific = new ContextDefinition('specific'); $context_definition_specific->setConstraints(['bar' => 'baz']); $context_specific = $this->getMock('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->getMock('Drupal\Core\Plugin\Context\ContextInterface'); $expected_context_definition = (new ContextDefinition('expected_data_type'))->setConstraints(['expected_constraint_name' => 'expected_constraint_value']); $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_plugin' => []]; // No context, all plugins available. $data[] = [FALSE, $plugins, $plugins]; $plugins = ['expected_plugin' => ['context' => []]]; // No context, all plugins available. $data[] = [FALSE, $plugins, $plugins]; $plugins = ['expected_plugin' => ['context' => ['context1' => new ContextDefinition('expected_data_type')]]]; // 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_plugin' => ['context' => ['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_plugin' => ['context' => ['context1' => $optional_mismatched_context_definition]]]; // Optional mismatched constraint, all plugins available. $data[] = [FALSE, $plugins, $plugins]; $expected_context_definition = (new ContextDefinition('expected_data_type'))->setConstraints(['expected_constraint_name' => 'expected_constraint_value']); $plugins = ['expected_plugin' => ['context' => ['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_plugin' => ['context' => ['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_plugin' => ['context' => ['context1' => $unexpected_context_definition]], 'expected_plugin' => ['context' => ['context2' => new ContextDefinition('expected_data_type')]], ]; // Context only satisfies one plugin. $data[] = [TRUE, $plugins, ['expected_plugin' => $plugins['expected_plugin']]]; return $data; } /** * @covers ::applyContextMapping */ public function testApplyContextMapping() { $context_hit_data = StringData::createInstance(DataDefinition::create('string')); $context_hit_data->setValue('foo'); $context_hit = $this->getMock('Drupal\Core\Plugin\Context\ContextInterface'); $context_hit->expects($this->atLeastOnce()) ->method('getContextData') ->will($this->returnValue($context_hit_data)); $context_miss_data = StringData::createInstance(DataDefinition::create('string')); $context_miss_data->setValue('bar'); $context_hit->expects($this->atLeastOnce()) ->method('hasContextValue') ->willReturn(TRUE); $context_miss = $this->getMock('Drupal\Core\Plugin\Context\ContextInterface'); $context_miss->expects($this->never()) ->method('getContextData'); $contexts = [ 'hit' => $context_hit, 'miss' => $context_miss, ]; $context_definition = $this->getMock('Drupal\Core\Plugin\Context\ContextDefinitionInterface'); $plugin = $this->getMock('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('setContextValue') ->with('hit', $context_hit_data); // Make sure that the cacheability metadata is passed to the plugin context. $plugin_context = $this->getMock('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->getMock('Drupal\Core\Plugin\Context\ContextInterface'); $context->expects($this->never()) ->method('getContextValue'); $contexts = [ 'name' => $context, ]; $context_definition = $this->getMock('Drupal\Core\Plugin\Context\ContextDefinitionInterface'); $context_definition->expects($this->atLeastOnce()) ->method('isRequired') ->willReturn(TRUE); $plugin = $this->getMock('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('setContextValue'); // No context, so no cacheability metadata can be passed along. $plugin->expects($this->never()) ->method('getContext'); $this->setExpectedException(ContextException::class, 'Required contexts without a value: hit.'); $this->contextHandler->applyContextMapping($plugin, $contexts); } /** * @covers ::applyContextMapping */ public function testApplyContextMappingMissingNotRequired() { $context = $this->getMock('Drupal\Core\Plugin\Context\ContextInterface'); $context->expects($this->never()) ->method('getContextValue'); $contexts = [ 'name' => $context, ]; $context_definition = $this->getMock('Drupal\Core\Plugin\Context\ContextDefinitionInterface'); $context_definition->expects($this->atLeastOnce()) ->method('isRequired') ->willReturn(FALSE); $plugin = $this->getMock('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('setContextValue'); // No context, so no cacheability metadata can be passed along. $plugin->expects($this->never()) ->method('getContext'); $this->contextHandler->applyContextMapping($plugin, $contexts); } /** * @covers ::applyContextMapping */ public function testApplyContextMappingNoValueRequired() { $context = $this->getMock('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->getMock('Drupal\Core\Plugin\Context\ContextDefinitionInterface'); $context_definition->expects($this->atLeastOnce()) ->method('isRequired') ->willReturn(TRUE); $plugin = $this->getMock('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('setContextValue'); $this->setExpectedException(ContextException::class, 'Required contexts without a value: hit.'); $this->contextHandler->applyContextMapping($plugin, $contexts); } /** * @covers ::applyContextMapping */ public function testApplyContextMappingNoValueNonRequired() { $context = $this->getMock('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->getMock('Drupal\Core\Plugin\Context\ContextDefinitionInterface'); $context_definition->expects($this->atLeastOnce()) ->method('isRequired') ->willReturn(FALSE); $plugin = $this->getMock('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('setContextValue'); $this->contextHandler->applyContextMapping($plugin, $contexts); } /** * @covers ::applyContextMapping */ public function testApplyContextMappingConfigurableAssigned() { $context_data = StringData::createInstance(DataDefinition::create('string')); $context_data->setValue('foo'); $context = $this->getMock('Drupal\Core\Plugin\Context\ContextInterface'); $context->expects($this->atLeastOnce()) ->method('getContextData') ->will($this->returnValue($context_data)); $context->expects($this->atLeastOnce()) ->method('hasContextValue') ->willReturn(TRUE); $contexts = [ 'name' => $context, ]; $context_definition = $this->getMock('Drupal\Core\Plugin\Context\ContextDefinitionInterface'); $plugin = $this->getMock('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('setContextValue') ->with('hit', $context_data); // Make sure that the cacheability metadata is passed to the plugin context. $plugin_context = $this->getMock('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->getMock('Drupal\Core\Plugin\Context\ContextInterface'); $context->expects($this->never()) ->method('getContextValue'); $contexts = [ 'name' => $context, ]; $context_definition = $this->getMock('Drupal\Core\Plugin\Context\ContextDefinitionInterface'); $plugin = $this->getMock('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('setContextValue'); $this->setExpectedException(ContextException::class, 'Assigned contexts were not satisfied: miss'); $this->contextHandler->applyContextMapping($plugin, $contexts, ['miss' => 'name']); } } interface TestConfigurableContextAwarePluginInterface extends ContextAwarePluginInterface, ConfigurablePluginInterface { }