Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / web / core / tests / Drupal / KernelTests / Core / Config / ConfigSchemaTest.php
1 <?php
2
3 namespace Drupal\KernelTests\Core\Config;
4
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;
15
16 /**
17  * Tests schema for configuration objects.
18  *
19  * @group config
20  */
21 class ConfigSchemaTest extends KernelTestBase {
22
23   /**
24    * Modules to enable.
25    *
26    * @var array
27    */
28   public static $modules = ['system', 'language', 'field', 'image', 'config_test', 'config_schema_test'];
29
30   /**
31    * {@inheritdoc}
32    */
33   protected function setUp() {
34     parent::setUp();
35     $this->installConfig(['system', 'image', 'config_schema_test']);
36   }
37
38   /**
39    * Tests the basic metadata retrieval layer.
40    */
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');
45     $expected = [];
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.');
52
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.');
57
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');
61     $expected = [];
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.');
73
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();
77     $expected = [];
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();
85     $expected = [];
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();
93     $expected = [];
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.');
100
101     // Simple case, straight metadata.
102     $definition = \Drupal::service('config.typed')->getDefinition('system.maintenance');
103     $expected = [];
104     $expected['label'] = 'Maintenance mode';
105     $expected['class'] = Mapping::class;
106     $expected['mapping']['message'] = [
107       'label' => 'Message to display when in maintenance mode',
108       'type' => 'text',
109     ];
110     $expected['mapping']['langcode'] = [
111       'label' => 'Language code',
112       'type' => 'string',
113     ];
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');
119
120     // Mixed schema with ignore elements.
121     $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.ignore');
122     $expected = [];
123     $expected['label'] = 'Ignore test';
124     $expected['class'] = Mapping::class;
125     $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
126     $expected['mapping']['langcode'] = [
127       'type' => 'string',
128       'label' => 'Language code',
129     ];
130     $expected['mapping']['_core']['type'] = '_core_config_info';
131     $expected['mapping']['label'] = [
132       'label' => 'Label',
133       'type' => 'label',
134     ];
135     $expected['mapping']['irrelevant'] = [
136       'label' => 'Irrelevant',
137       'type' => 'ignore',
138     ];
139     $expected['mapping']['indescribable'] = [
140       'label' => 'Indescribable',
141       'type' => 'ignore',
142     ];
143     $expected['mapping']['weight'] = [
144       'label' => 'Weight',
145       'type' => 'integer',
146     ];
147     $expected['type'] = 'config_schema_test.ignore';
148     $expected['unwrap_for_canonical_representation'] = TRUE;
149
150     $this->assertEqual($definition, $expected);
151
152     // The ignore elements themselves.
153     $definition = \Drupal::service('config.typed')->get('config_schema_test.ignore')->get('irrelevant')->getDataDefinition()->toArray();
154     $expected = [];
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);
164
165     // More complex case, generic type. Metadata for image style.
166     $definition = \Drupal::service('config.typed')->getDefinition('image.style.large');
167     $expected = [];
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.*';
195
196     $this->assertEqual($definition, $expected);
197
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.
201     $expected = [];
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';
213
214     $this->assertEqual($definition, $expected, 'Retrieved the right metadata for image.effect.image_scale');
215
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';
221
222     $this->assertEqual($definition, $expected, 'Retrieved the right metadata for the first effect of image.style.medium');
223
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();
227     $expected = [];
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'],
236     ];
237     $this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_test.dynamic.third_party:third_party_settings.config_schema_test');
238
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.*.*.
242     $expected = [];
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;
255
256     $this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_schema_test.someschema.somemodule.section_one.subsection');
257
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');
261   }
262
263   /**
264    * Tests metadata retrieval with several levels of %parent indirection.
265    */
266   public function testSchemaMappingWithParents() {
267     $config_data = \Drupal::service('config.typed')->get('config_schema_test.someschema.with_parents');
268
269     // Test fetching parent one level up.
270     $entry = $config_data->get('one_level');
271     $definition = $entry->get('testitem')->getDataDefinition()->toArray();
272     $expected = [
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,
278     ];
279     $this->assertEqual($definition, $expected);
280
281     // Test fetching parent two levels up.
282     $entry = $config_data->get('two_levels');
283     $definition = $entry->get('wrapper')->get('testitem')->getDataDefinition()->toArray();
284     $expected = [
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,
290     ];
291     $this->assertEqual($definition, $expected);
292
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();
296     $expected = [
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,
302     ];
303     $this->assertEqual($definition, $expected);
304   }
305
306   /**
307    * Tests metadata applied to configuration objects.
308    */
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.');
317
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.');
323
324     // And test some TypedConfigInterface methods.
325     $properties = $list;
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.');
329
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.');
339   }
340
341   /**
342    * Test configuration value data type enforcement using schemas.
343    */
344   public function testConfigSaveWithSchema() {
345     $untyped_values = [
346       'string' => 1,
347       'empty_string' => '',
348       'null_string' => NULL,
349       'integer' => '100',
350       'null_integer' => '',
351       'boolean' => 1,
352       // If the config schema doesn't have a type it shouldn't be casted.
353       'no_type' => 1,
354       'mapping' => [
355         'string' => 1,
356       ],
357       'float' => '3.14',
358       'null_float' => '',
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' => '',
366     ];
367     $untyped_to_typed = $untyped_values;
368
369     $typed_values = [
370       'string' => '1',
371       'empty_string' => '',
372       'null_string' => NULL,
373       'integer' => 100,
374       'null_integer' => NULL,
375       'boolean' => TRUE,
376       'no_type' => 1,
377       'mapping' => [
378         'string' => '1',
379       ],
380       'float' => 3.14,
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,
387     ];
388
389     // Save config which has a schema that enforces types.
390     $this->config('config_schema_test.schema_data_types')
391       ->setData($untyped_to_typed)
392       ->save();
393     $this->assertIdentical($this->config('config_schema_test.schema_data_types')->get(), $typed_values);
394
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)
398       ->save();
399     $this->assertIdentical($this->config('config_schema_test.no_schema_data_types')->get(), $untyped_values);
400
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);
410   }
411
412   /**
413    * Tests configuration sequence sorting using schemas.
414    */
415   public function testConfigSaveWithSequenceSorting() {
416     $data = [
417       'keyed_sort' => [
418         'b' => '1',
419         'a' => '2',
420       ],
421       'no_sort' => [
422         'b' => '2',
423         'a' => '1',
424       ],
425     ];
426     // Save config which has a schema that enforces sorting.
427     $this->config('config_schema_test.schema_sequence_sort')
428       ->setData($data)
429       ->save();
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'));
432
433     $data = [
434       'value_sort' => ['b', 'a'],
435       'no_sort' => ['b', 'a'],
436     ];
437     // Save config which has a schema that enforces sorting.
438     $this->config('config_schema_test.schema_sequence_sort')
439       ->setData($data)
440       ->save();
441
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'));
444
445     // Value sort does not preserve keys - this is intentional.
446     $data = [
447       'value_sort' => [1 => 'b', 2 => 'a'],
448       'no_sort' => [1 => 'b', 2 => 'a'],
449     ];
450     // Save config which has a schema that enforces sorting.
451     $this->config('config_schema_test.schema_sequence_sort')
452       ->setData($data)
453       ->save();
454
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'));
457
458     // Test sorts do not destroy complex values.
459     $data = [
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']],
462     ];
463     $this->config('config_schema_test.schema_sequence_sort')
464       ->setData($data)
465       ->save();
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'));
468
469     // Swap the previous test scenario around.
470     $data = [
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']],
473     ];
474     $this->config('config_schema_test.schema_sequence_sort')
475       ->setData($data)
476       ->save();
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'));
479
480   }
481
482   /**
483    * Tests fallback to a greedy wildcard.
484    */
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.*.
488     $expected = [];
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.*';
501
502     $this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_schema_test.wildcard_fallback.something');
503
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
506     // well.
507     $this->assertSame($definition, $definition2);
508   }
509
510   /**
511    * Tests use of colons in schema type determination.
512    *
513    * @see \Drupal\Core\Config\TypedConfigManager::getFallbackName()
514    */
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');
519
520     $definition = $tests[1]->getDataDefinition()->toArray();
521     $this->assertEqual($definition['type'], 'test.plugin_types.boolean:*');
522
523     $definition = $tests[2]->getDataDefinition()->toArray();
524     $this->assertEqual($definition['type'], 'test.plugin_types.*');
525
526     $definition = $tests[3]->getDataDefinition()->toArray();
527     $this->assertEqual($definition['type'], 'test.plugin_types.*');
528
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');
532
533     $definition = $tests[1]->get('settings')->getDataDefinition()->toArray();
534     $this->assertEqual($definition['type'], 'test_with_parents.plugin_types.boolean:*');
535
536     $definition = $tests[2]->get('settings')->getDataDefinition()->toArray();
537     $this->assertEqual($definition['type'], 'test_with_parents.plugin_types.*');
538
539     $definition = $tests[3]->get('settings')->getDataDefinition()->toArray();
540     $this->assertEqual($definition['type'], 'test_with_parents.plugin_types.*');
541   }
542
543   /**
544    * Tests hook_config_schema_info_alter().
545    */
546   public function testConfigSchemaInfoAlter() {
547     /** @var \Drupal\Core\Config\TypedConfigManagerInterface $typed_config */
548     $typed_config = \Drupal::service('config.typed');
549     $typed_config->clearCachedDefinitions();
550
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.';
555     try {
556       $typed_config->getDefinitions();
557       $this->fail($message);
558     }
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');
562     }
563
564     \Drupal::state()->set('config_schema_test_exception_add', TRUE);
565     $message = 'Expected ConfigSchemaAlterException thrown.';
566     try {
567       $typed_config->getDefinitions();
568       $this->fail($message);
569     }
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');
573     }
574
575     \Drupal::state()->set('config_schema_test_exception_remove', FALSE);
576     $message = 'Expected ConfigSchemaAlterException thrown.';
577     try {
578       $typed_config->getDefinitions();
579       $this->fail($message);
580     }
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');
584     }
585
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');
591   }
592
593   /**
594    * Tests saving config when the type is wrapped by a dynamic type.
595    */
596   public function testConfigSaveWithWrappingSchema() {
597     $untyped_values = [
598       'tests' => [
599         [
600           'wrapper_value' => 'foo',
601           'plugin_id' => 'wrapper:foo',
602           'internal_value' => 100,
603         ],
604       ],
605     ];
606
607     $typed_values = [
608       'tests' => [
609         [
610           'wrapper_value' => 'foo',
611           'plugin_id' => 'wrapper:foo',
612           'internal_value' => '100',
613         ],
614       ],
615     ];
616
617     // Save config which has a schema that enforces types.
618     \Drupal::configFactory()->getEditable('wrapping.config_schema_test.plugin_types')
619       ->setData($untyped_values)
620       ->save();
621     $this->assertIdentical(\Drupal::config('wrapping.config_schema_test.plugin_types')
622       ->get(), $typed_values);
623   }
624
625   /**
626    * Tests dynamic config schema type with multiple sub-key references.
627    */
628   public function testConfigSaveWithWrappingSchemaDoubleBrackets() {
629     $untyped_values = [
630       'tests' => [
631         [
632           'wrapper_value' => 'foo',
633           'foo' => 'turtle',
634           'bar' => 'horse',
635           // Converted to a string by 'test.double_brackets.turtle.horse'
636           // schema.
637           'another_key' => '100',
638         ],
639       ],
640     ];
641
642     $typed_values = [
643       'tests' => [
644         [
645           'wrapper_value' => 'foo',
646           'foo' => 'turtle',
647           'bar' => 'horse',
648           'another_key' => 100,
649         ],
650       ],
651     ];
652
653     // Save config which has a schema that enforces types.
654     \Drupal::configFactory()->getEditable('wrapping.config_schema_test.double_brackets')
655       ->setData($untyped_values)
656       ->save();
657     $this->assertIdentical(\Drupal::config('wrapping.config_schema_test.double_brackets')
658       ->get(), $typed_values);
659
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');
663
664     $untyped_values = [
665       'tests' => [
666         [
667           'wrapper_value' => 'foo',
668           'foo' => 'cat',
669           'bar' => 'dog',
670           // Converted to a string by 'test.double_brackets.cat.dog' schema.
671           'another_key' => 100,
672         ],
673       ],
674     ];
675
676     $typed_values = [
677       'tests' => [
678         [
679           'wrapper_value' => 'foo',
680           'foo' => 'cat',
681           'bar' => 'dog',
682           'another_key' => '100',
683         ],
684       ],
685     ];
686
687     // Save config which has a schema that enforces types.
688     \Drupal::configFactory()->getEditable('wrapping.config_schema_test.double_brackets')
689       ->setData($untyped_values)
690       ->save();
691     $this->assertIdentical(\Drupal::config('wrapping.config_schema_test.double_brackets')
692       ->get(), $typed_values);
693
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');
697
698     // Combine everything in a single save.
699     $typed_values = [
700       'tests' => [
701         [
702           'wrapper_value' => 'foo',
703           'foo' => 'cat',
704           'bar' => 'dog',
705           'another_key' => 100,
706         ],
707         [
708           'wrapper_value' => 'foo',
709           'foo' => 'turtle',
710           'bar' => 'horse',
711           'another_key' => '100',
712         ],
713       ],
714     ];
715     \Drupal::configFactory()->getEditable('wrapping.config_schema_test.double_brackets')
716       ->setData($typed_values)
717       ->save();
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');
723
724     $typed_values = [
725       'tests' => [
726         [
727           'id' => 'cat:persion.dog',
728           'foo' => 'cat',
729           'bar' => 'dog',
730           'breed' => 'persion',
731         ],
732       ],
733     ];
734
735     \Drupal::configFactory()->getEditable('wrapping.config_schema_test.other_double_brackets')
736       ->setData($typed_values)
737       ->save();
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']);
744   }
745
746 }