annotate core/modules/field/tests/src/Kernel/FieldDefinitionIntegrityTest.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 7a779792577d
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\Tests\field\Kernel;
Chris@0 4
Chris@12 5 use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
Chris@12 6 use Drupal\Component\Plugin\Exception\PluginNotFoundException;
Chris@12 7 use Drupal\Component\Utility\NestedArray;
Chris@12 8 use Drupal\Core\Entity\ContentEntityTypeInterface;
Chris@12 9 use Drupal\Core\Entity\EntityTypeInterface;
Chris@0 10 use Drupal\Core\Extension\Extension;
Chris@12 11 use Drupal\Core\Field\BaseFieldDefinition;
Chris@0 12 use Drupal\KernelTests\KernelTestBase;
Chris@0 13
Chris@0 14 /**
Chris@0 15 * Tests the integrity of field API plugin definitions.
Chris@0 16 *
Chris@0 17 * @group field
Chris@0 18 */
Chris@0 19 class FieldDefinitionIntegrityTest extends KernelTestBase {
Chris@0 20
Chris@0 21 /**
Chris@0 22 * @var array
Chris@0 23 */
Chris@0 24 public static $modules = ['system'];
Chris@0 25
Chris@0 26 /**
Chris@0 27 * Tests the integrity of field plugin definitions.
Chris@0 28 */
Chris@0 29 public function testFieldPluginDefinitionIntegrity() {
Chris@12 30 // Enable all core modules that provide field plugins, and their
Chris@12 31 // dependencies.
Chris@12 32 $this->enableModules(
Chris@12 33 $this->modulesWithSubdirectory(
Chris@12 34 'src' . DIRECTORY_SEPARATOR . 'Plugin' . DIRECTORY_SEPARATOR . 'Field'
Chris@12 35 )
Chris@12 36 );
Chris@0 37
Chris@0 38 /** @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface $field_type_manager */
Chris@0 39 $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
Chris@0 40
Chris@0 41 /** @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface $field_type_manager */
Chris@0 42 $field_formatter_manager = \Drupal::service('plugin.manager.field.formatter');
Chris@0 43
Chris@0 44 /** @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface $field_type_manager */
Chris@0 45 $field_widget_manager = \Drupal::service('plugin.manager.field.widget');
Chris@0 46
Chris@0 47 // Load the IDs of all available field type plugins.
Chris@0 48 $available_field_type_ids = [];
Chris@0 49 foreach ($field_type_manager->getDefinitions() as $definition) {
Chris@0 50 $available_field_type_ids[] = $definition['id'];
Chris@0 51 }
Chris@0 52
Chris@0 53 // Load the IDs of all available field widget plugins.
Chris@0 54 $available_field_widget_ids = [];
Chris@0 55 foreach ($field_widget_manager->getDefinitions() as $definition) {
Chris@0 56 $available_field_widget_ids[] = $definition['id'];
Chris@0 57 }
Chris@0 58
Chris@0 59 // Load the IDs of all available field formatter plugins.
Chris@0 60 $available_field_formatter_ids = [];
Chris@0 61 foreach ($field_formatter_manager->getDefinitions() as $definition) {
Chris@0 62 $available_field_formatter_ids[] = $definition['id'];
Chris@0 63 }
Chris@0 64
Chris@0 65 // Test the field type plugins.
Chris@0 66 foreach ($field_type_manager->getDefinitions() as $definition) {
Chris@0 67 // Test default field widgets.
Chris@0 68 if (isset($definition['default_widget'])) {
Chris@0 69 if (in_array($definition['default_widget'], $available_field_widget_ids)) {
Chris@0 70 $this->pass(sprintf('Field type %s uses an existing field widget by default.', $definition['id']));
Chris@0 71 }
Chris@0 72 else {
Chris@0 73 $this->fail(sprintf('Field type %s uses a non-existent field widget by default: %s', $definition['id'], $definition['default_widget']));
Chris@0 74 }
Chris@0 75 }
Chris@0 76
Chris@0 77 // Test default field formatters.
Chris@0 78 if (isset($definition['default_formatter'])) {
Chris@0 79 if (in_array($definition['default_formatter'], $available_field_formatter_ids)) {
Chris@0 80 $this->pass(sprintf('Field type %s uses an existing field formatter by default.', $definition['id']));
Chris@0 81 }
Chris@0 82 else {
Chris@0 83 $this->fail(sprintf('Field type %s uses a non-existent field formatter by default: %s', $definition['id'], $definition['default_formatter']));
Chris@0 84 }
Chris@0 85 }
Chris@0 86 }
Chris@0 87
Chris@0 88 // Test the field widget plugins.
Chris@0 89 foreach ($field_widget_manager->getDefinitions() as $definition) {
Chris@0 90 $missing_field_type_ids = array_diff($definition['field_types'], $available_field_type_ids);
Chris@0 91 if ($missing_field_type_ids) {
Chris@0 92 $this->fail(sprintf('Field widget %s integrates with non-existent field types: %s', $definition['id'], implode(', ', $missing_field_type_ids)));
Chris@0 93 }
Chris@0 94 else {
Chris@0 95 $this->pass(sprintf('Field widget %s integrates with existing field types.', $definition['id']));
Chris@0 96 }
Chris@0 97 }
Chris@0 98
Chris@0 99 // Test the field formatter plugins.
Chris@0 100 foreach ($field_formatter_manager->getDefinitions() as $definition) {
Chris@0 101 $missing_field_type_ids = array_diff($definition['field_types'], $available_field_type_ids);
Chris@0 102 if ($missing_field_type_ids) {
Chris@0 103 $this->fail(sprintf('Field formatter %s integrates with non-existent field types: %s', $definition['id'], implode(', ', $missing_field_type_ids)));
Chris@0 104 }
Chris@0 105 else {
Chris@0 106 $this->pass(sprintf('Field formatter %s integrates with existing field types.', $definition['id']));
Chris@0 107 }
Chris@12 108
Chris@0 109 }
Chris@0 110 }
Chris@0 111
Chris@12 112 /**
Chris@12 113 * Tests to load field plugin definitions used in core's existing entities.
Chris@12 114 */
Chris@12 115 public function testFieldPluginDefinitionAvailability() {
Chris@12 116 $this->enableModules(
Chris@12 117 $this->modulesWithSubdirectory('src' . DIRECTORY_SEPARATOR . 'Entity')
Chris@12 118 );
Chris@12 119
Chris@12 120 /** @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface $field_type_manager */
Chris@12 121 $field_formatter_manager = $this->container->get('plugin.manager.field.formatter');
Chris@12 122
Chris@12 123 /** @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface $field_type_manager */
Chris@12 124 $field_widget_manager = $this->container->get('plugin.manager.field.widget');
Chris@12 125
Chris@12 126 /** @var \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager */
Chris@12 127 $entity_field_manager = $this->container->get('entity_field.manager');
Chris@12 128
Chris@12 129 /** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager */
Chris@12 130 $entity_type_manager = $this->container->get('entity_type.manager');
Chris@12 131
Chris@12 132 /** @var \Drupal\Core\Field\BaseFieldDefinition[][] $field_definitions */
Chris@12 133 $field_definitions = [];
Chris@12 134
Chris@12 135 /** @var \Drupal\Core\Entity\EntityTypeInterface[] $content_entity_types */
Chris@12 136 $content_entity_types = array_filter($entity_type_manager->getDefinitions(), function (EntityTypeInterface $entity_type) {
Chris@12 137 return $entity_type instanceof ContentEntityTypeInterface;
Chris@12 138 });
Chris@12 139
Chris@12 140 foreach ($content_entity_types as $entity_type_id => $entity_type_definition) {
Chris@12 141 $field_definitions[$entity_type_id] = $entity_field_manager->getBaseFieldDefinitions($entity_type_id);
Chris@12 142 }
Chris@12 143
Chris@12 144 foreach ($field_definitions as $entity_type_id => $definitions) {
Chris@12 145 foreach ($definitions as $field_id => $field_definition) {
Chris@12 146 $this->checkDisplayOption($entity_type_id, $field_id, $field_definition, $field_formatter_manager, 'view');
Chris@12 147 $this->checkDisplayOption($entity_type_id, $field_id, $field_definition, $field_widget_manager, 'form');
Chris@12 148 }
Chris@12 149 }
Chris@12 150 }
Chris@12 151
Chris@12 152 /**
Chris@12 153 * Helper method that tries to load plugin definitions.
Chris@12 154 *
Chris@12 155 * @param string $entity_type_id
Chris@12 156 * Id of entity type. Required by message.
Chris@12 157 * @param string $field_id
Chris@12 158 * Id of field. Required by message.
Chris@12 159 * @param \Drupal\Core\Field\BaseFieldDefinition $field_definition
Chris@12 160 * Field definition that provide display options.
Chris@12 161 * @param \Drupal\Component\Plugin\Discovery\DiscoveryInterface $plugin_manager
Chris@12 162 * Plugin manager that will try to provide plugin definition.
Chris@12 163 * @param string $display_context
Chris@12 164 * Defines which display options should be loaded.
Chris@12 165 */
Chris@12 166 protected function checkDisplayOption($entity_type_id, $field_id, BaseFieldDefinition $field_definition, DiscoveryInterface $plugin_manager, $display_context) {
Chris@12 167 $display_options = $field_definition->getDisplayOptions($display_context);
Chris@12 168 if (!empty($display_options['type'])) {
Chris@12 169 try {
Chris@12 170 $plugin_manager->getDefinition($display_options['type']);
Chris@12 171 }
Chris@12 172 catch (PluginNotFoundException $e) {
Chris@12 173 $this->fail(sprintf(
Chris@12 174 'PluginNotFoundException here for "%s" field %s display options of "%s" entity type. Original message: %s',
Chris@12 175 $field_id,
Chris@12 176 $display_context,
Chris@12 177 $entity_type_id,
Chris@12 178 $e->getMessage()
Chris@12 179 ));
Chris@12 180 }
Chris@12 181 }
Chris@12 182 }
Chris@12 183
Chris@12 184 /**
Chris@12 185 * Find modules with a specified subdirectory.
Chris@12 186 *
Chris@12 187 * @param string $subdirectory
Chris@12 188 * The required path, relative to the module directory.
Chris@12 189 *
Chris@12 190 * @return string[]
Chris@12 191 * A list of module names satisfying these criteria:
Chris@12 192 * - provided by core
Chris@12 193 * - not hidden
Chris@12 194 * - not already enabled
Chris@12 195 * - not in the Testing package
Chris@12 196 * - containing the required $subdirectory
Chris@12 197 * and all modules required by any of these modules.
Chris@12 198 */
Chris@12 199 protected function modulesWithSubdirectory($subdirectory) {
Chris@12 200 $modules = system_rebuild_module_data();
Chris@12 201 $modules = array_filter($modules, function (Extension $module) use ($subdirectory) {
Chris@12 202 // Filter contrib, hidden, already enabled modules and modules in the
Chris@12 203 // Testing package.
Chris@12 204 return ($module->origin === 'core'
Chris@12 205 && empty($module->info['hidden'])
Chris@12 206 && $module->status == FALSE
Chris@12 207 && $module->info['package'] !== 'Testing'
Chris@12 208 && is_readable($module->getPath() . DIRECTORY_SEPARATOR . $subdirectory));
Chris@12 209 });
Chris@12 210 // Gather the dependencies of the modules.
Chris@12 211 $dependencies = NestedArray::mergeDeepArray(array_map(function (Extension $module) {
Chris@12 212 return array_keys($module->requires);
Chris@12 213 }, $modules));
Chris@12 214
Chris@12 215 return array_unique(NestedArray::mergeDeep(array_keys($modules), $dependencies));
Chris@12 216 }
Chris@12 217
Chris@0 218 }