3 namespace Drupal\KernelTests\Core\Config;
5 use Drupal\Core\Config\FileStorage;
6 use Drupal\Core\Config\InstallStorage;
7 use Drupal\Core\Config\Schema\ConfigSchemaAlterException;
8 use Drupal\Core\Config\Schema\Ignore;
9 use Drupal\Core\Config\Schema\Mapping;
10 use Drupal\Core\Config\Schema\Undefined;
11 use Drupal\Core\TypedData\Plugin\DataType\StringData;
12 use Drupal\Core\TypedData\Type\IntegerInterface;
13 use Drupal\Core\TypedData\Type\StringInterface;
14 use Drupal\KernelTests\KernelTestBase;
17 * Tests schema for configuration objects.
21 class ConfigSchemaTest extends KernelTestBase {
28 public static $modules = ['system', 'language', 'field', 'image', 'config_test', 'config_schema_test'];
33 protected function setUp() {
35 $this->installConfig(['system', 'image', 'config_schema_test']);
39 * Tests the basic metadata retrieval layer.
41 public function testSchemaMapping() {
42 // Nonexistent configuration key will have Undefined as metadata.
43 $this->assertSame(FALSE, \Drupal::service('config.typed')->hasConfigSchema('config_schema_test.no_such_key'));
44 $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.no_such_key');
46 $expected['label'] = 'Undefined';
47 $expected['class'] = Undefined::class;
48 $expected['type'] = 'undefined';
49 $expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition';
50 $expected['unwrap_for_canonical_representation'] = TRUE;
51 $this->assertEqual($definition, $expected, 'Retrieved the right metadata for nonexistent configuration.');
53 // Configuration file without schema will return Undefined as well.
54 $this->assertSame(FALSE, \Drupal::service('config.typed')->hasConfigSchema('config_schema_test.noschema'));
55 $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.noschema');
56 $this->assertEqual($definition, $expected, 'Retrieved the right metadata for configuration with no schema.');
58 // Configuration file with only some schema.
59 $this->assertSame(TRUE, \Drupal::service('config.typed')->hasConfigSchema('config_schema_test.someschema'));
60 $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.someschema');
62 $expected['label'] = 'Schema test data';
63 $expected['class'] = Mapping::class;
64 $expected['mapping']['langcode']['type'] = 'string';
65 $expected['mapping']['langcode']['label'] = 'Language code';
66 $expected['mapping']['_core']['type'] = '_core_config_info';
67 $expected['mapping']['testitem'] = ['label' => 'Test item'];
68 $expected['mapping']['testlist'] = ['label' => 'Test list'];
69 $expected['type'] = 'config_schema_test.someschema';
70 $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
71 $expected['unwrap_for_canonical_representation'] = TRUE;
72 $this->assertEqual($definition, $expected, 'Retrieved the right metadata for configuration with only some schema.');
74 // Check type detection on elements with undefined types.
75 $config = \Drupal::service('config.typed')->get('config_schema_test.someschema');
76 $definition = $config->get('testitem')->getDataDefinition()->toArray();
78 $expected['label'] = 'Test item';
79 $expected['class'] = Undefined::class;
80 $expected['type'] = 'undefined';
81 $expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition';
82 $expected['unwrap_for_canonical_representation'] = TRUE;
83 $this->assertEqual($definition, $expected, 'Automatic type detected for a scalar is undefined.');
84 $definition = $config->get('testlist')->getDataDefinition()->toArray();
86 $expected['label'] = 'Test list';
87 $expected['class'] = Undefined::class;
88 $expected['type'] = 'undefined';
89 $expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition';
90 $expected['unwrap_for_canonical_representation'] = TRUE;
91 $this->assertEqual($definition, $expected, 'Automatic type detected for a list is undefined.');
92 $definition = $config->get('testnoschema')->getDataDefinition()->toArray();
94 $expected['label'] = 'Undefined';
95 $expected['class'] = Undefined::class;
96 $expected['type'] = 'undefined';
97 $expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition';
98 $expected['unwrap_for_canonical_representation'] = TRUE;
99 $this->assertEqual($definition, $expected, 'Automatic type detected for an undefined integer is undefined.');
101 // Simple case, straight metadata.
102 $definition = \Drupal::service('config.typed')->getDefinition('system.maintenance');
104 $expected['label'] = 'Maintenance mode';
105 $expected['class'] = Mapping::class;
106 $expected['mapping']['message'] = [
107 'label' => 'Message to display when in maintenance mode',
110 $expected['mapping']['langcode'] = [
111 'label' => 'Language code',
114 $expected['mapping']['_core']['type'] = '_core_config_info';
115 $expected['type'] = 'system.maintenance';
116 $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
117 $expected['unwrap_for_canonical_representation'] = TRUE;
118 $this->assertEqual($definition, $expected, 'Retrieved the right metadata for system.maintenance');
120 // Mixed schema with ignore elements.
121 $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.ignore');
123 $expected['label'] = 'Ignore test';
124 $expected['class'] = Mapping::class;
125 $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
126 $expected['mapping']['langcode'] = [
128 'label' => 'Language code',
130 $expected['mapping']['_core']['type'] = '_core_config_info';
131 $expected['mapping']['label'] = [
135 $expected['mapping']['irrelevant'] = [
136 'label' => 'Irrelevant',
139 $expected['mapping']['indescribable'] = [
140 'label' => 'Indescribable',
143 $expected['mapping']['weight'] = [
147 $expected['type'] = 'config_schema_test.ignore';
148 $expected['unwrap_for_canonical_representation'] = TRUE;
150 $this->assertEqual($definition, $expected);
152 // The ignore elements themselves.
153 $definition = \Drupal::service('config.typed')->get('config_schema_test.ignore')->get('irrelevant')->getDataDefinition()->toArray();
155 $expected['type'] = 'ignore';
156 $expected['label'] = 'Irrelevant';
157 $expected['class'] = Ignore::class;
158 $expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition';
159 $expected['unwrap_for_canonical_representation'] = TRUE;
160 $this->assertEqual($definition, $expected);
161 $definition = \Drupal::service('config.typed')->get('config_schema_test.ignore')->get('indescribable')->getDataDefinition()->toArray();
162 $expected['label'] = 'Indescribable';
163 $this->assertEqual($definition, $expected);
165 // More complex case, generic type. Metadata for image style.
166 $definition = \Drupal::service('config.typed')->getDefinition('image.style.large');
168 $expected['label'] = 'Image style';
169 $expected['class'] = Mapping::class;
170 $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
171 $expected['unwrap_for_canonical_representation'] = TRUE;
172 $expected['mapping']['name']['type'] = 'string';
173 $expected['mapping']['uuid']['type'] = 'uuid';
174 $expected['mapping']['uuid']['label'] = 'UUID';
175 $expected['mapping']['langcode']['type'] = 'string';
176 $expected['mapping']['langcode']['label'] = 'Language code';
177 $expected['mapping']['status']['type'] = 'boolean';
178 $expected['mapping']['status']['label'] = 'Status';
179 $expected['mapping']['dependencies']['type'] = 'config_dependencies';
180 $expected['mapping']['dependencies']['label'] = 'Dependencies';
181 $expected['mapping']['name']['type'] = 'string';
182 $expected['mapping']['label']['type'] = 'label';
183 $expected['mapping']['label']['label'] = 'Label';
184 $expected['mapping']['effects']['type'] = 'sequence';
185 $expected['mapping']['effects']['sequence']['type'] = 'mapping';
186 $expected['mapping']['effects']['sequence']['mapping']['id']['type'] = 'string';
187 $expected['mapping']['effects']['sequence']['mapping']['data']['type'] = 'image.effect.[%parent.id]';
188 $expected['mapping']['effects']['sequence']['mapping']['weight']['type'] = 'integer';
189 $expected['mapping']['effects']['sequence']['mapping']['uuid']['type'] = 'uuid';
190 $expected['mapping']['third_party_settings']['type'] = 'sequence';
191 $expected['mapping']['third_party_settings']['label'] = 'Third party settings';
192 $expected['mapping']['third_party_settings']['sequence']['type'] = '[%parent.%parent.%type].third_party.[%key]';
193 $expected['mapping']['_core']['type'] = '_core_config_info';
194 $expected['type'] = 'image.style.*';
196 $this->assertEqual($definition, $expected);
198 // More complex, type based on a complex one.
199 $definition = \Drupal::service('config.typed')->getDefinition('image.effect.image_scale');
200 // This should be the schema for image.effect.image_scale.
202 $expected['label'] = 'Image scale';
203 $expected['class'] = Mapping::class;
204 $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
205 $expected['unwrap_for_canonical_representation'] = TRUE;
206 $expected['mapping']['width']['type'] = 'integer';
207 $expected['mapping']['width']['label'] = 'Width';
208 $expected['mapping']['height']['type'] = 'integer';
209 $expected['mapping']['height']['label'] = 'Height';
210 $expected['mapping']['upscale']['type'] = 'boolean';
211 $expected['mapping']['upscale']['label'] = 'Upscale';
212 $expected['type'] = 'image.effect.image_scale';
214 $this->assertEqual($definition, $expected, 'Retrieved the right metadata for image.effect.image_scale');
216 // Most complex case, get metadata for actual configuration element.
217 $effects = \Drupal::service('config.typed')->get('image.style.medium')->get('effects');
218 $definition = $effects->get('bddf0d06-42f9-4c75-a700-a33cafa25ea0')->get('data')->getDataDefinition()->toArray();
219 // This should be the schema for image.effect.image_scale, reuse previous one.
220 $expected['type'] = 'image.effect.image_scale';
222 $this->assertEqual($definition, $expected, 'Retrieved the right metadata for the first effect of image.style.medium');
224 $a = \Drupal::config('config_test.dynamic.third_party');
225 $test = \Drupal::service('config.typed')->get('config_test.dynamic.third_party')->get('third_party_settings.config_schema_test');
226 $definition = $test->getDataDefinition()->toArray();
228 $expected['type'] = 'config_test.dynamic.*.third_party.config_schema_test';
229 $expected['label'] = 'Mapping';
230 $expected['class'] = Mapping::class;
231 $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
232 $expected['unwrap_for_canonical_representation'] = TRUE;
233 $expected['mapping'] = [
234 'integer' => ['type' => 'integer'],
235 'string' => ['type' => 'string'],
237 $this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_test.dynamic.third_party:third_party_settings.config_schema_test');
239 // More complex, several level deep test.
240 $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.someschema.somemodule.section_one.subsection');
241 // This should be the schema of config_schema_test.someschema.somemodule.*.*.
243 $expected['label'] = 'Schema multiple filesystem marker test';
244 $expected['class'] = Mapping::class;
245 $expected['mapping']['langcode']['type'] = 'string';
246 $expected['mapping']['langcode']['label'] = 'Language code';
247 $expected['mapping']['_core']['type'] = '_core_config_info';
248 $expected['mapping']['testid']['type'] = 'string';
249 $expected['mapping']['testid']['label'] = 'ID';
250 $expected['mapping']['testdescription']['type'] = 'text';
251 $expected['mapping']['testdescription']['label'] = 'Description';
252 $expected['type'] = 'config_schema_test.someschema.somemodule.*.*';
253 $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
254 $expected['unwrap_for_canonical_representation'] = TRUE;
256 $this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_schema_test.someschema.somemodule.section_one.subsection');
258 $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.someschema.somemodule.section_two.subsection');
259 // The other file should have the same schema.
260 $this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_schema_test.someschema.somemodule.section_two.subsection');
264 * Tests metadata retrieval with several levels of %parent indirection.
266 public function testSchemaMappingWithParents() {
267 $config_data = \Drupal::service('config.typed')->get('config_schema_test.someschema.with_parents');
269 // Test fetching parent one level up.
270 $entry = $config_data->get('one_level');
271 $definition = $entry->get('testitem')->getDataDefinition()->toArray();
273 'type' => 'config_schema_test.someschema.with_parents.key_1',
274 'label' => 'Test item nested one level',
275 'class' => StringData::class,
276 'definition_class' => '\Drupal\Core\TypedData\DataDefinition',
277 'unwrap_for_canonical_representation' => TRUE,
279 $this->assertEqual($definition, $expected);
281 // Test fetching parent two levels up.
282 $entry = $config_data->get('two_levels');
283 $definition = $entry->get('wrapper')->get('testitem')->getDataDefinition()->toArray();
285 'type' => 'config_schema_test.someschema.with_parents.key_2',
286 'label' => 'Test item nested two levels',
287 'class' => StringData::class,
288 'definition_class' => '\Drupal\Core\TypedData\DataDefinition',
289 'unwrap_for_canonical_representation' => TRUE,
291 $this->assertEqual($definition, $expected);
293 // Test fetching parent three levels up.
294 $entry = $config_data->get('three_levels');
295 $definition = $entry->get('wrapper_1')->get('wrapper_2')->get('testitem')->getDataDefinition()->toArray();
297 'type' => 'config_schema_test.someschema.with_parents.key_3',
298 'label' => 'Test item nested three levels',
299 'class' => StringData::class,
300 'definition_class' => '\Drupal\Core\TypedData\DataDefinition',
301 'unwrap_for_canonical_representation' => TRUE,
303 $this->assertEqual($definition, $expected);
307 * Tests metadata applied to configuration objects.
309 public function testSchemaData() {
310 // Try a simple property.
311 $meta = \Drupal::service('config.typed')->get('system.site');
312 $property = $meta->get('page')->get('front');
313 $this->assertTrue($property instanceof StringInterface, 'Got the right wrapper fo the page.front property.');
314 $this->assertEqual($property->getValue(), '/user/login', 'Got the right value for page.front data.');
315 $definition = $property->getDataDefinition();
316 $this->assertTrue(empty($definition['translatable']), 'Got the right translatability setting for page.front data.');
318 // Check nested array of properties.
319 $list = $meta->get('page')->getElements();
320 $this->assertEqual(count($list), 3, 'Got a list with the right number of properties for site page data');
321 $this->assertTrue(isset($list['front']) && isset($list['403']) && isset($list['404']), 'Got a list with the right properties for site page data.');
322 $this->assertEqual($list['front']->getValue(), '/user/login', 'Got the right value for page.front data from the list.');
324 // And test some TypedConfigInterface methods.
326 $this->assertTrue(count($properties) == 3 && $properties['front'] == $list['front'], 'Got the right properties for site page.');
327 $values = $meta->get('page')->toArray();
328 $this->assertTrue(count($values) == 3 && $values['front'] == '/user/login', 'Got the right property values for site page.');
330 // Now let's try something more complex, with nested objects.
331 $wrapper = \Drupal::service('config.typed')->get('image.style.large');
332 $effects = $wrapper->get('effects');
333 $this->assertTrue(count($effects->toArray()) == 1, 'Got an array with effects for image.style.large data');
334 $uuid = key($effects->getValue());
335 $effect = $effects->get($uuid)->getElements();
336 $this->assertTrue(!$effect['data']->isEmpty() && $effect['id']->getValue() == 'image_scale', 'Got data for the image scale effect from metadata.');
337 $this->assertTrue($effect['data']->get('width') instanceof IntegerInterface, 'Got the right type for the scale effect width.');
338 $this->assertEqual($effect['data']->get('width')->getValue(), 480, 'Got the right value for the scale effect width.');
342 * Test configuration value data type enforcement using schemas.
344 public function testConfigSaveWithSchema() {
347 'empty_string' => '',
348 'null_string' => NULL,
350 'null_integer' => '',
352 // If the config schema doesn't have a type it shouldn't be casted.
359 'sequence' => [1, 0, 1],
360 'sequence_bc' => [1, 0, 1],
361 // Not in schema and therefore should be left untouched.
362 'not_present_in_schema' => TRUE,
363 // Test a custom type.
364 'config_schema_test_integer' => '1',
365 'config_schema_test_integer_empty_string' => '',
367 $untyped_to_typed = $untyped_values;
371 'empty_string' => '',
372 'null_string' => NULL,
374 'null_integer' => NULL,
381 'null_float' => NULL,
382 'sequence' => [TRUE, FALSE, TRUE],
383 'sequence_bc' => [TRUE, FALSE, TRUE],
384 'not_present_in_schema' => TRUE,
385 'config_schema_test_integer' => 1,
386 'config_schema_test_integer_empty_string' => NULL,
389 // Save config which has a schema that enforces types.
390 $this->config('config_schema_test.schema_data_types')
391 ->setData($untyped_to_typed)
393 $this->assertIdentical($this->config('config_schema_test.schema_data_types')->get(), $typed_values);
395 // Save config which does not have a schema that enforces types.
396 $this->config('config_schema_test.no_schema_data_types')
397 ->setData($untyped_values)
399 $this->assertIdentical($this->config('config_schema_test.no_schema_data_types')->get(), $untyped_values);
401 // Ensure that configuration objects with keys marked as ignored are not
402 // changed when saved. The 'config_schema_test.ignore' will have been saved
403 // during the installation of configuration in the setUp method.
404 $extension_path = __DIR__ . '/../../../../../modules/config/tests/config_schema_test/';
405 $install_storage = new FileStorage($extension_path . InstallStorage::CONFIG_INSTALL_DIRECTORY);
406 $original_data = $install_storage->read('config_schema_test.ignore');
407 $installed_data = $this->config('config_schema_test.ignore')->get();
408 unset($installed_data['_core']);
409 $this->assertIdentical($installed_data, $original_data);
413 * Tests configuration sequence sorting using schemas.
415 public function testConfigSaveWithSequenceSorting() {
426 // Save config which has a schema that enforces sorting.
427 $this->config('config_schema_test.schema_sequence_sort')
430 $this->assertSame(['a' => '2', 'b' => '1'], $this->config('config_schema_test.schema_sequence_sort')->get('keyed_sort'));
431 $this->assertSame(['b' => '2', 'a' => '1'], $this->config('config_schema_test.schema_sequence_sort')->get('no_sort'));
434 'value_sort' => ['b', 'a'],
435 'no_sort' => ['b', 'a'],
437 // Save config which has a schema that enforces sorting.
438 $this->config('config_schema_test.schema_sequence_sort')
442 $this->assertSame(['a', 'b'], $this->config('config_schema_test.schema_sequence_sort')->get('value_sort'));
443 $this->assertSame(['b', 'a'], $this->config('config_schema_test.schema_sequence_sort')->get('no_sort'));
445 // Value sort does not preserve keys - this is intentional.
447 'value_sort' => [1 => 'b', 2 => 'a'],
448 'no_sort' => [1 => 'b', 2 => 'a'],
450 // Save config which has a schema that enforces sorting.
451 $this->config('config_schema_test.schema_sequence_sort')
455 $this->assertSame(['a', 'b'], $this->config('config_schema_test.schema_sequence_sort')->get('value_sort'));
456 $this->assertSame([1 => 'b', 2 => 'a'], $this->config('config_schema_test.schema_sequence_sort')->get('no_sort'));
458 // Test sorts do not destroy complex values.
460 'complex_sort_value' => [['foo' => 'b', 'bar' => 'b'] , ['foo' => 'a', 'bar' => 'a']],
461 'complex_sort_key' => ['b' => ['foo' => '1', 'bar' => '1'] , 'a' => ['foo' => '2', 'bar' => '2']],
463 $this->config('config_schema_test.schema_sequence_sort')
466 $this->assertSame([['foo' => 'a', 'bar' => 'a'], ['foo' => 'b', 'bar' => 'b']], $this->config('config_schema_test.schema_sequence_sort')->get('complex_sort_value'));
467 $this->assertSame(['a' => ['foo' => '2', 'bar' => '2'], 'b' => ['foo' => '1', 'bar' => '1']], $this->config('config_schema_test.schema_sequence_sort')->get('complex_sort_key'));
469 // Swap the previous test scenario around.
471 'complex_sort_value' => ['b' => ['foo' => '1', 'bar' => '1'] , 'a' => ['foo' => '2', 'bar' => '2']],
472 'complex_sort_key' => [['foo' => 'b', 'bar' => 'b'] , ['foo' => 'a', 'bar' => 'a']],
474 $this->config('config_schema_test.schema_sequence_sort')
477 $this->assertSame([['foo' => '1', 'bar' => '1'], ['foo' => '2', 'bar' => '2']], $this->config('config_schema_test.schema_sequence_sort')->get('complex_sort_value'));
478 $this->assertSame([['foo' => 'b', 'bar' => 'b'], ['foo' => 'a', 'bar' => 'a']], $this->config('config_schema_test.schema_sequence_sort')->get('complex_sort_key'));
483 * Tests fallback to a greedy wildcard.
485 public function testSchemaFallback() {
486 $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.wildcard_fallback.something');
487 // This should be the schema of config_schema_test.wildcard_fallback.*.
489 $expected['label'] = 'Schema wildcard fallback test';
490 $expected['class'] = Mapping::class;
491 $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
492 $expected['unwrap_for_canonical_representation'] = TRUE;
493 $expected['mapping']['langcode']['type'] = 'string';
494 $expected['mapping']['langcode']['label'] = 'Language code';
495 $expected['mapping']['_core']['type'] = '_core_config_info';
496 $expected['mapping']['testid']['type'] = 'string';
497 $expected['mapping']['testid']['label'] = 'ID';
498 $expected['mapping']['testdescription']['type'] = 'text';
499 $expected['mapping']['testdescription']['label'] = 'Description';
500 $expected['type'] = 'config_schema_test.wildcard_fallback.*';
502 $this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_schema_test.wildcard_fallback.something');
504 $definition2 = \Drupal::service('config.typed')->getDefinition('config_schema_test.wildcard_fallback.something.something');
505 // This should be the schema of config_schema_test.wildcard_fallback.* as
507 $this->assertSame($definition, $definition2);
511 * Tests use of colons in schema type determination.
513 * @see \Drupal\Core\Config\TypedConfigManager::getFallbackName()
515 public function testColonsInSchemaTypeDetermination() {
516 $tests = \Drupal::service('config.typed')->get('config_schema_test.plugin_types')->get('tests')->getElements();
517 $definition = $tests[0]->getDataDefinition()->toArray();
518 $this->assertEqual($definition['type'], 'test.plugin_types.boolean');
520 $definition = $tests[1]->getDataDefinition()->toArray();
521 $this->assertEqual($definition['type'], 'test.plugin_types.boolean:*');
523 $definition = $tests[2]->getDataDefinition()->toArray();
524 $this->assertEqual($definition['type'], 'test.plugin_types.*');
526 $definition = $tests[3]->getDataDefinition()->toArray();
527 $this->assertEqual($definition['type'], 'test.plugin_types.*');
529 $tests = \Drupal::service('config.typed')->get('config_schema_test.plugin_types')->get('test_with_parents')->getElements();
530 $definition = $tests[0]->get('settings')->getDataDefinition()->toArray();
531 $this->assertEqual($definition['type'], 'test_with_parents.plugin_types.boolean');
533 $definition = $tests[1]->get('settings')->getDataDefinition()->toArray();
534 $this->assertEqual($definition['type'], 'test_with_parents.plugin_types.boolean:*');
536 $definition = $tests[2]->get('settings')->getDataDefinition()->toArray();
537 $this->assertEqual($definition['type'], 'test_with_parents.plugin_types.*');
539 $definition = $tests[3]->get('settings')->getDataDefinition()->toArray();
540 $this->assertEqual($definition['type'], 'test_with_parents.plugin_types.*');
544 * Tests hook_config_schema_info_alter().
546 public function testConfigSchemaInfoAlter() {
547 /** @var \Drupal\Core\Config\TypedConfigManagerInterface $typed_config */
548 $typed_config = \Drupal::service('config.typed');
549 $typed_config->clearCachedDefinitions();
551 // Ensure that keys can not be added or removed by
552 // hook_config_schema_info_alter().
553 \Drupal::state()->set('config_schema_test_exception_remove', TRUE);
554 $message = 'Expected ConfigSchemaAlterException thrown.';
556 $typed_config->getDefinitions();
557 $this->fail($message);
559 catch (ConfigSchemaAlterException $e) {
560 $this->pass($message);
561 $this->assertEqual($e->getMessage(), 'Invoking hook_config_schema_info_alter() has removed (config_schema_test.hook) schema definitions');
564 \Drupal::state()->set('config_schema_test_exception_add', TRUE);
565 $message = 'Expected ConfigSchemaAlterException thrown.';
567 $typed_config->getDefinitions();
568 $this->fail($message);
570 catch (ConfigSchemaAlterException $e) {
571 $this->pass($message);
572 $this->assertEqual($e->getMessage(), 'Invoking hook_config_schema_info_alter() has added (config_schema_test.hook_added_defintion) and removed (config_schema_test.hook) schema definitions');
575 \Drupal::state()->set('config_schema_test_exception_remove', FALSE);
576 $message = 'Expected ConfigSchemaAlterException thrown.';
578 $typed_config->getDefinitions();
579 $this->fail($message);
581 catch (ConfigSchemaAlterException $e) {
582 $this->pass($message);
583 $this->assertEqual($e->getMessage(), 'Invoking hook_config_schema_info_alter() has added (config_schema_test.hook_added_defintion) schema definitions');
586 // Tests that hook_config_schema_info_alter() can add additional metadata to
587 // existing configuration schema.
588 \Drupal::state()->set('config_schema_test_exception_add', FALSE);
589 $definitions = $typed_config->getDefinitions();
590 $this->assertEqual($definitions['config_schema_test.hook']['additional_metadata'], 'new schema info');
594 * Tests saving config when the type is wrapped by a dynamic type.
596 public function testConfigSaveWithWrappingSchema() {
600 'wrapper_value' => 'foo',
601 'plugin_id' => 'wrapper:foo',
602 'internal_value' => 100,
610 'wrapper_value' => 'foo',
611 'plugin_id' => 'wrapper:foo',
612 'internal_value' => '100',
617 // Save config which has a schema that enforces types.
618 \Drupal::configFactory()->getEditable('wrapping.config_schema_test.plugin_types')
619 ->setData($untyped_values)
621 $this->assertIdentical(\Drupal::config('wrapping.config_schema_test.plugin_types')
622 ->get(), $typed_values);
626 * Tests dynamic config schema type with multiple sub-key references.
628 public function testConfigSaveWithWrappingSchemaDoubleBrackets() {
632 'wrapper_value' => 'foo',
635 // Converted to a string by 'test.double_brackets.turtle.horse'
637 'another_key' => '100',
645 'wrapper_value' => 'foo',
648 'another_key' => 100,
653 // Save config which has a schema that enforces types.
654 \Drupal::configFactory()->getEditable('wrapping.config_schema_test.double_brackets')
655 ->setData($untyped_values)
657 $this->assertIdentical(\Drupal::config('wrapping.config_schema_test.double_brackets')
658 ->get(), $typed_values);
660 $tests = \Drupal::service('config.typed')->get('wrapping.config_schema_test.double_brackets')->get('tests')->getElements();
661 $definition = $tests[0]->getDataDefinition()->toArray();
662 $this->assertEqual($definition['type'], 'wrapping.test.double_brackets.*||test.double_brackets.turtle.horse');
667 'wrapper_value' => 'foo',
670 // Converted to a string by 'test.double_brackets.cat.dog' schema.
671 'another_key' => 100,
679 'wrapper_value' => 'foo',
682 'another_key' => '100',
687 // Save config which has a schema that enforces types.
688 \Drupal::configFactory()->getEditable('wrapping.config_schema_test.double_brackets')
689 ->setData($untyped_values)
691 $this->assertIdentical(\Drupal::config('wrapping.config_schema_test.double_brackets')
692 ->get(), $typed_values);
694 $tests = \Drupal::service('config.typed')->get('wrapping.config_schema_test.double_brackets')->get('tests')->getElements();
695 $definition = $tests[0]->getDataDefinition()->toArray();
696 $this->assertEqual($definition['type'], 'wrapping.test.double_brackets.*||test.double_brackets.cat.dog');
698 // Combine everything in a single save.
702 'wrapper_value' => 'foo',
705 'another_key' => 100,
708 'wrapper_value' => 'foo',
711 'another_key' => '100',
715 \Drupal::configFactory()->getEditable('wrapping.config_schema_test.double_brackets')
716 ->setData($typed_values)
718 $tests = \Drupal::service('config.typed')->get('wrapping.config_schema_test.double_brackets')->get('tests')->getElements();
719 $definition = $tests[0]->getDataDefinition()->toArray();
720 $this->assertEqual($definition['type'], 'wrapping.test.double_brackets.*||test.double_brackets.cat.dog');
721 $definition = $tests[1]->getDataDefinition()->toArray();
722 $this->assertEqual($definition['type'], 'wrapping.test.double_brackets.*||test.double_brackets.turtle.horse');
727 'id' => 'cat:persion.dog',
730 'breed' => 'persion',
735 \Drupal::configFactory()->getEditable('wrapping.config_schema_test.other_double_brackets')
736 ->setData($typed_values)
738 $tests = \Drupal::service('config.typed')->get('wrapping.config_schema_test.other_double_brackets')->get('tests')->getElements();
739 $definition = $tests[0]->getDataDefinition()->toArray();
740 // Check that definition type is a merge of the expected types.
741 $this->assertEqual($definition['type'], 'wrapping.test.other_double_brackets.*||test.double_brackets.cat:*.*');
742 // Check that breed was inherited from parent definition.
743 $this->assertEqual($definition['mapping']['breed'], ['type' => 'string']);