Version 1
[yaffs-website] / web / core / tests / Drupal / Tests / Core / Plugin / ContextHandlerTest.php
diff --git a/web/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php b/web/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php
new file mode 100644 (file)
index 0000000..4ed5e6e
--- /dev/null
@@ -0,0 +1,493 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Plugin\ContextHandlerTest.
+ */
+
+namespace Drupal\Tests\Core\Plugin;
+
+use Drupal\Component\Plugin\ConfigurablePluginInterface;
+use Drupal\Component\Plugin\Exception\ContextException;
+use Drupal\Core\Plugin\Context\ContextDefinition;
+use Drupal\Core\Plugin\Context\ContextHandler;
+use Drupal\Core\Plugin\ContextAwarePluginInterface;
+use Drupal\Core\TypedData\DataDefinition;
+use Drupal\Core\TypedData\Plugin\DataType\StringData;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @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();
+  }
+
+  /**
+   * @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 {
+}