Chris@0: enableModules( Chris@12: $this->modulesWithSubdirectory( Chris@12: 'src' . DIRECTORY_SEPARATOR . 'Plugin' . DIRECTORY_SEPARATOR . 'Field' Chris@12: ) Chris@12: ); Chris@0: Chris@0: /** @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface $field_type_manager */ Chris@0: $field_type_manager = \Drupal::service('plugin.manager.field.field_type'); Chris@0: Chris@0: /** @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface $field_type_manager */ Chris@0: $field_formatter_manager = \Drupal::service('plugin.manager.field.formatter'); Chris@0: Chris@0: /** @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface $field_type_manager */ Chris@0: $field_widget_manager = \Drupal::service('plugin.manager.field.widget'); Chris@0: Chris@0: // Load the IDs of all available field type plugins. Chris@0: $available_field_type_ids = []; Chris@0: foreach ($field_type_manager->getDefinitions() as $definition) { Chris@0: $available_field_type_ids[] = $definition['id']; Chris@0: } Chris@0: Chris@0: // Load the IDs of all available field widget plugins. Chris@0: $available_field_widget_ids = []; Chris@0: foreach ($field_widget_manager->getDefinitions() as $definition) { Chris@0: $available_field_widget_ids[] = $definition['id']; Chris@0: } Chris@0: Chris@0: // Load the IDs of all available field formatter plugins. Chris@0: $available_field_formatter_ids = []; Chris@0: foreach ($field_formatter_manager->getDefinitions() as $definition) { Chris@0: $available_field_formatter_ids[] = $definition['id']; Chris@0: } Chris@0: Chris@0: // Test the field type plugins. Chris@0: foreach ($field_type_manager->getDefinitions() as $definition) { Chris@0: // Test default field widgets. Chris@0: if (isset($definition['default_widget'])) { Chris@0: if (in_array($definition['default_widget'], $available_field_widget_ids)) { Chris@0: $this->pass(sprintf('Field type %s uses an existing field widget by default.', $definition['id'])); Chris@0: } Chris@0: else { Chris@0: $this->fail(sprintf('Field type %s uses a non-existent field widget by default: %s', $definition['id'], $definition['default_widget'])); Chris@0: } Chris@0: } Chris@0: Chris@0: // Test default field formatters. Chris@0: if (isset($definition['default_formatter'])) { Chris@0: if (in_array($definition['default_formatter'], $available_field_formatter_ids)) { Chris@0: $this->pass(sprintf('Field type %s uses an existing field formatter by default.', $definition['id'])); Chris@0: } Chris@0: else { Chris@0: $this->fail(sprintf('Field type %s uses a non-existent field formatter by default: %s', $definition['id'], $definition['default_formatter'])); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: // Test the field widget plugins. Chris@0: foreach ($field_widget_manager->getDefinitions() as $definition) { Chris@0: $missing_field_type_ids = array_diff($definition['field_types'], $available_field_type_ids); Chris@0: if ($missing_field_type_ids) { Chris@0: $this->fail(sprintf('Field widget %s integrates with non-existent field types: %s', $definition['id'], implode(', ', $missing_field_type_ids))); Chris@0: } Chris@0: else { Chris@0: $this->pass(sprintf('Field widget %s integrates with existing field types.', $definition['id'])); Chris@0: } Chris@0: } Chris@0: Chris@0: // Test the field formatter plugins. Chris@0: foreach ($field_formatter_manager->getDefinitions() as $definition) { Chris@0: $missing_field_type_ids = array_diff($definition['field_types'], $available_field_type_ids); Chris@0: if ($missing_field_type_ids) { Chris@0: $this->fail(sprintf('Field formatter %s integrates with non-existent field types: %s', $definition['id'], implode(', ', $missing_field_type_ids))); Chris@0: } Chris@0: else { Chris@0: $this->pass(sprintf('Field formatter %s integrates with existing field types.', $definition['id'])); Chris@0: } Chris@12: Chris@0: } Chris@0: } Chris@0: Chris@12: /** Chris@12: * Tests to load field plugin definitions used in core's existing entities. Chris@12: */ Chris@12: public function testFieldPluginDefinitionAvailability() { Chris@12: $this->enableModules( Chris@12: $this->modulesWithSubdirectory('src' . DIRECTORY_SEPARATOR . 'Entity') Chris@12: ); Chris@12: Chris@12: /** @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface $field_type_manager */ Chris@12: $field_formatter_manager = $this->container->get('plugin.manager.field.formatter'); Chris@12: Chris@12: /** @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface $field_type_manager */ Chris@12: $field_widget_manager = $this->container->get('plugin.manager.field.widget'); Chris@12: Chris@12: /** @var \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager */ Chris@12: $entity_field_manager = $this->container->get('entity_field.manager'); Chris@12: Chris@12: /** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager */ Chris@12: $entity_type_manager = $this->container->get('entity_type.manager'); Chris@12: Chris@12: /** @var \Drupal\Core\Field\BaseFieldDefinition[][] $field_definitions */ Chris@12: $field_definitions = []; Chris@12: Chris@12: /** @var \Drupal\Core\Entity\EntityTypeInterface[] $content_entity_types */ Chris@12: $content_entity_types = array_filter($entity_type_manager->getDefinitions(), function (EntityTypeInterface $entity_type) { Chris@12: return $entity_type instanceof ContentEntityTypeInterface; Chris@12: }); Chris@12: Chris@12: foreach ($content_entity_types as $entity_type_id => $entity_type_definition) { Chris@12: $field_definitions[$entity_type_id] = $entity_field_manager->getBaseFieldDefinitions($entity_type_id); Chris@12: } Chris@12: Chris@12: foreach ($field_definitions as $entity_type_id => $definitions) { Chris@12: foreach ($definitions as $field_id => $field_definition) { Chris@12: $this->checkDisplayOption($entity_type_id, $field_id, $field_definition, $field_formatter_manager, 'view'); Chris@12: $this->checkDisplayOption($entity_type_id, $field_id, $field_definition, $field_widget_manager, 'form'); Chris@12: } Chris@12: } Chris@12: } Chris@12: Chris@12: /** Chris@12: * Helper method that tries to load plugin definitions. Chris@12: * Chris@12: * @param string $entity_type_id Chris@12: * Id of entity type. Required by message. Chris@12: * @param string $field_id Chris@12: * Id of field. Required by message. Chris@12: * @param \Drupal\Core\Field\BaseFieldDefinition $field_definition Chris@12: * Field definition that provide display options. Chris@12: * @param \Drupal\Component\Plugin\Discovery\DiscoveryInterface $plugin_manager Chris@12: * Plugin manager that will try to provide plugin definition. Chris@12: * @param string $display_context Chris@12: * Defines which display options should be loaded. Chris@12: */ Chris@12: protected function checkDisplayOption($entity_type_id, $field_id, BaseFieldDefinition $field_definition, DiscoveryInterface $plugin_manager, $display_context) { Chris@12: $display_options = $field_definition->getDisplayOptions($display_context); Chris@12: if (!empty($display_options['type'])) { Chris@12: try { Chris@12: $plugin_manager->getDefinition($display_options['type']); Chris@12: } Chris@12: catch (PluginNotFoundException $e) { Chris@12: $this->fail(sprintf( Chris@12: 'PluginNotFoundException here for "%s" field %s display options of "%s" entity type. Original message: %s', Chris@12: $field_id, Chris@12: $display_context, Chris@12: $entity_type_id, Chris@12: $e->getMessage() Chris@12: )); Chris@12: } Chris@12: } Chris@12: } Chris@12: Chris@12: /** Chris@12: * Find modules with a specified subdirectory. Chris@12: * Chris@12: * @param string $subdirectory Chris@12: * The required path, relative to the module directory. Chris@12: * Chris@12: * @return string[] Chris@12: * A list of module names satisfying these criteria: Chris@12: * - provided by core Chris@12: * - not hidden Chris@12: * - not already enabled Chris@12: * - not in the Testing package Chris@12: * - containing the required $subdirectory Chris@12: * and all modules required by any of these modules. Chris@12: */ Chris@12: protected function modulesWithSubdirectory($subdirectory) { Chris@12: $modules = system_rebuild_module_data(); Chris@12: $modules = array_filter($modules, function (Extension $module) use ($subdirectory) { Chris@12: // Filter contrib, hidden, already enabled modules and modules in the Chris@12: // Testing package. Chris@12: return ($module->origin === 'core' Chris@12: && empty($module->info['hidden']) Chris@12: && $module->status == FALSE Chris@12: && $module->info['package'] !== 'Testing' Chris@12: && is_readable($module->getPath() . DIRECTORY_SEPARATOR . $subdirectory)); Chris@12: }); Chris@12: // Gather the dependencies of the modules. Chris@12: $dependencies = NestedArray::mergeDeepArray(array_map(function (Extension $module) { Chris@12: return array_keys($module->requires); Chris@12: }, $modules)); Chris@12: Chris@12: return array_unique(NestedArray::mergeDeep(array_keys($modules), $dependencies)); Chris@12: } Chris@12: Chris@0: }