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