ConfigSchemaTest.php 15 KB
Newer Older
1 2 3 4 5 6 7 8 9
<?php

/**
 * @file
 * Contains \Drupal\config\Tests\ConfigSchemaTest.
 */

namespace Drupal\config\Tests;

10 11
use Drupal\Core\TypedData\Type\IntegerInterface;
use Drupal\Core\TypedData\Type\StringInterface;
12 13 14 15 16 17 18 19 20 21 22 23
use Drupal\simpletest\DrupalUnitTestBase;

/**
 * Tests schema for configuration objects.
 */
class ConfigSchemaTest extends DrupalUnitTestBase {

  /**
   * Modules to enable.
   *
   * @var array
   */
24
  public static $modules = array('system', 'language', 'locale', 'field', 'image', 'config_schema_test');
25 26 27 28 29 30 31 32 33 34 35

  public static function getInfo() {
    return array(
      'name' => 'Configuration schema',
      'description' => 'Tests Metadata for configuration objects.',
      'group' => 'Configuration',
    );
  }

  public function setUp() {
    parent::setUp();
36
    $this->installConfig(array('system', 'image', 'config_schema_test'));
37 38 39 40 41 42
  }

  /**
   * Tests the basic metadata retrieval layer.
   */
  function testSchemaMapping() {
43
    // Nonexistent configuration key will have Unknown as metadata.
44 45
    $this->assertIdentical(FALSE, \Drupal::service('config.typed')->hasConfigSchema('config_schema_test.no_such_key'));
    $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.no_such_key');
46 47 48 49 50 51
    $expected = array();
    $expected['label'] = 'Unknown';
    $expected['class'] = '\Drupal\Core\Config\Schema\Property';
    $this->assertEqual($definition, $expected, 'Retrieved the right metadata for nonexistent configuration.');

    // Configuration file without schema will return Unknown as well.
52 53
    $this->assertIdentical(FALSE, \Drupal::service('config.typed')->hasConfigSchema('config_schema_test.noschema'));
    $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.noschema');
54 55 56
    $this->assertEqual($definition, $expected, 'Retrieved the right metadata for configuration with no schema.');

    // Configuration file with only some schema.
57 58
    $this->assertIdentical(TRUE, \Drupal::service('config.typed')->hasConfigSchema('config_schema_test.someschema'));
    $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.someschema');
59 60 61 62 63 64 65 66
    $expected = array();
    $expected['label'] = 'Schema test data';
    $expected['class'] = '\Drupal\Core\Config\Schema\Mapping';
    $expected['mapping']['testitem'] = array('label' => 'Test item');
    $expected['mapping']['testlist'] = array('label' => 'Test list');
    $this->assertEqual($definition, $expected, 'Retrieved the right metadata for configuration with only some schema.');

    // Check type detection on elements with undefined types.
67
    $config = \Drupal::service('config.typed')->get('config_schema_test.someschema');
68
    $definition = $config['testitem']->getDataDefinition();
69 70
    $expected = array();
    $expected['label'] = 'Test item';
71
    $expected['class'] = '\Drupal\Core\TypedData\Plugin\DataType\String';
72 73
    $expected['type'] = 'string';
    $this->assertEqual($definition, $expected, 'Automatic type detection on string item worked.');
74
    $definition = $config['testlist']->getDataDefinition();
75 76 77 78 79 80
    $expected = array();
    $expected['label'] = 'Test list';
    $expected['class'] = '\Drupal\Core\Config\Schema\Property';
    $expected['type'] = 'undefined';
    $this->assertEqual($definition, $expected, 'Automatic type fallback on non-string item worked.');

81
    // Simple case, straight metadata.
82
    $definition = \Drupal::service('config.typed')->getDefinition('system.maintenance');
83 84 85 86 87 88 89
    $expected = array();
    $expected['label'] = 'Maintenance mode';
    $expected['class'] = '\Drupal\Core\Config\Schema\Mapping';
    $expected['mapping']['message'] = array(
      'label' =>  'Message to display when in maintenance mode',
      'type' => 'text',
    );
90 91 92 93
    $expected['mapping']['langcode'] = array(
      'label' => 'Default language',
      'type' => 'string',
    );
94 95 96
    $this->assertEqual($definition, $expected, 'Retrieved the right metadata for system.maintenance');

    // More complex case, generic type. Metadata for image style.
97
    $definition = \Drupal::service('config.typed')->getDefinition('image.style.large');
98 99 100 101
    $expected = array();
    $expected['label'] = 'Image style';
    $expected['class'] = '\Drupal\Core\Config\Schema\Mapping';
    $expected['mapping']['name']['type'] = 'string';
102 103
    $expected['mapping']['uuid']['label'] = 'UUID';
    $expected['mapping']['uuid']['type'] = 'string';
104 105 106
    $expected['mapping']['label']['type'] = 'label';
    $expected['mapping']['effects']['type'] = 'sequence';
    $expected['mapping']['effects']['sequence'][0]['type'] = 'mapping';
107 108
    $expected['mapping']['effects']['sequence'][0]['mapping']['id']['type'] = 'string';
    $expected['mapping']['effects']['sequence'][0]['mapping']['data']['type'] = 'image.effect.[%parent.id]';
109
    $expected['mapping']['effects']['sequence'][0]['mapping']['weight']['type'] = 'integer';
110
    $expected['mapping']['effects']['sequence'][0]['mapping']['uuid']['type'] = 'string';
111 112
    $expected['mapping']['langcode']['label'] = 'Default language';
    $expected['mapping']['langcode']['type'] = 'string';
113 114
    $expected['mapping']['status']['label'] = 'Enabled';
    $expected['mapping']['status']['type'] = 'boolean';
115 116 117 118

    $this->assertEqual($definition, $expected, 'Retrieved the right metadata for image.style.large');

    // More complex, type based on a complex one.
119
    $definition = \Drupal::service('config.typed')->getDefinition('image.effect.image_scale');
120 121 122 123 124 125 126 127 128 129 130 131 132 133
    // This should be the schema for image.effect.image_scale.
    $expected = array();
    $expected['label'] = 'Image scale';
    $expected['class'] = '\Drupal\Core\Config\Schema\Mapping';
    $expected['mapping']['width']['type'] = 'integer';
    $expected['mapping']['width']['label'] = 'Width';
    $expected['mapping']['height']['type'] = 'integer';
    $expected['mapping']['height']['label'] = 'Height';
    $expected['mapping']['upscale']['type'] = 'boolean';
    $expected['mapping']['upscale']['label'] = 'Upscale';

    $this->assertEqual($definition, $expected, 'Retrieved the right metadata for image.effect.image_scale');

    // Most complex case, get metadata for actual configuration element.
134
    $effects = \Drupal::service('config.typed')->get('image.style.medium')->get('effects');
135
    $definition = $effects['bddf0d06-42f9-4c75-a700-a33cafa25ea0']['data']->getDataDefinition();
136 137 138 139
    // This should be the schema for image.effect.image_scale, reuse previous one.
    $expected['type'] =  'image.effect.image_scale';

    $this->assertEqual($definition, $expected, 'Retrieved the right metadata for the first effect of image.style.medium');
140 141

    // More complex, multiple filesystem marker test.
142 143
    $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.someschema.somemodule.section_one.subsection');
    // This should be the schema of config_schema_test.someschema.somemodule.*.*.
144 145 146
    $expected = array();
    $expected['label'] = 'Schema multiple filesytem marker test';
    $expected['class'] = '\Drupal\Core\Config\Schema\Mapping';
147 148 149 150
    $expected['mapping']['testid']['type'] = 'string';
    $expected['mapping']['testid']['label'] = 'ID';
    $expected['mapping']['testdescription']['type'] = 'text';
    $expected['mapping']['testdescription']['label'] = 'Description';
151

152
    $this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_schema_test.someschema.somemodule.section_one.subsection');
153

154
    $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.someschema.somemodule.section_two.subsection');
155
    // The other file should have the same schema.
156
    $this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_schema_test.someschema.somemodule.section_two.subsection');
157
  }
158

159 160 161 162
  /**
   * Tests metadata retrieval with several levels of %parent indirection.
   */
  function testSchemaMappingWithParents() {
163
    $config_data = \Drupal::service('config.typed')->get('config_schema_test.someschema.with_parents');
164 165 166

    // Test fetching parent one level up.
    $entry = $config_data->get('one_level');
167
    $definition = $entry['testitem']->getDataDefinition();
168
    $expected = array(
169
      'type' => 'config_schema_test.someschema.with_parents.key_1',
170 171 172 173 174 175 176
      'label' => 'Test item nested one level',
      'class' => '\Drupal\Core\TypedData\Plugin\DataType\String',
    );
    $this->assertEqual($definition, $expected);

    // Test fetching parent two levels up.
    $entry = $config_data->get('two_levels');
177
    $definition = $entry['wrapper']['testitem']->getDataDefinition();
178
    $expected = array(
179
      'type' => 'config_schema_test.someschema.with_parents.key_2',
180 181 182 183 184 185 186
      'label' => 'Test item nested two levels',
      'class' => '\Drupal\Core\TypedData\Plugin\DataType\String',
    );
    $this->assertEqual($definition, $expected);

    // Test fetching parent three levels up.
    $entry = $config_data->get('three_levels');
187
    $definition = $entry['wrapper_1']['wrapper_2']['testitem']->getDataDefinition();
188
    $expected = array(
189
      'type' => 'config_schema_test.someschema.with_parents.key_3',
190 191 192 193
      'label' => 'Test item nested three levels',
      'class' => '\Drupal\Core\TypedData\Plugin\DataType\String',
    );
    $this->assertEqual($definition, $expected);
194 195 196 197 198 199 200
  }

  /**
   * Tests metadata applied to configuration objects.
   */
  function testSchemaData() {
    // Try some simple properties.
201
    $meta = \Drupal::service('config.typed')->get('system.site');
202
    $property = $meta->get('name');
203
    $this->assertTrue($property instanceof StringInterface, 'Got the right wrapper fo the site name property.');
204
    $this->assertEqual($property->getValue(), 'Drupal', 'Got the right string value for site name data.');
205
    $definition = $property->getDataDefinition();
206
    $this->assertTrue($definition['translatable'], 'Got the right translatability setting for site name data.');
207 208

    $property = $meta->get('page')->get('front');
209
    $this->assertTrue($property instanceof StringInterface, 'Got the right wrapper fo the page.front property.');
210
    $this->assertEqual($property->getValue(), 'user', 'Got the right value for page.front data.');
211
    $definition = $property->getDataDefinition();
212
    $this->assertTrue(empty($definition['translatable']), 'Got the right translatability setting for page.front data.');
213 214 215 216 217 218 219 220 221 222

    // Check nested array of properties.
    $list = $meta->get('page');
    $this->assertEqual(count($list), 3, 'Got a list with the right number of properties for site page data');
    $this->assertTrue(isset($list['front']) && isset($list['403']) && isset($list['404']), 'Got a list with the right properties for site page data.');
    $this->assertEqual($list['front']->getValue(), 'user', 'Got the right value for page.front data from the list.');

    // And test some ComplexDataInterface methods.
    $properties = $list->getProperties();
    $this->assertTrue(count($properties) == 3 && $properties['front'] == $list['front'], 'Got the right properties for site page.');
223
    $values = $list->toArray();
224 225 226
    $this->assertTrue(count($values) == 3 && $values['front'] == 'user', 'Got the right property values for site page.');

    // Now let's try something more complex, with nested objects.
227
    $wrapper = \Drupal::service('config.typed')->get('image.style.large');
228 229 230 231
    $effects = $wrapper->get('effects');

    // The function is_array() doesn't work with ArrayAccess, so we use count().
    $this->assertTrue(count($effects) == 1, 'Got an array with effects for image.style.large data');
232 233 234
    $uuid = key($effects->getValue());
    $effect = $effects[$uuid];
    $this->assertTrue(count($effect['data']) && $effect['id']->getValue() == 'image_scale', 'Got data for the image scale effect from metadata.');
235
    $this->assertTrue($effect['data']['width'] instanceof IntegerInterface, 'Got the right type for the scale effect width.');
236 237 238 239
    $this->assertEqual($effect['data']['width']->getValue(), 480, 'Got the right value for the scale effect width.' );

    // Finally update some object using a configuration wrapper.
    $new_slogan = 'Site slogan for testing configuration metadata';
240
    $wrapper = \Drupal::service('config.typed')->get('system.site');
241 242 243 244 245
    $wrapper->set('slogan', $new_slogan);
    $site_slogan = $wrapper->get('slogan');
    $this->assertEqual($site_slogan->getValue(), $new_slogan, 'Successfully updated the contained configuration data');
  }

246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
  /**
   * Test configuration value data type enforcement using schemas.
   */
  public function testConfigSaveWithSchema() {
    $untyped_values = array(
      'string' => 1,
      'empty_string' => '',
      'null_string' => NULL,
      'integer' => '100',
      'null_integer' => '',
      'boolean' => 1,
      // If the config schema doesn't have a type it should be casted to string.
      'no_type' => 1,
      'mapping' => array(
        'string' => 1
      ),
      'float' => '3.14',
      'null_float' => '',
      'sequence' => array (1, 0, 1),
      // Not in schema and therefore should be left untouched.
      'not_present_in_schema' => TRUE,
      // Test a custom type.
268 269
      'config_schema_test_integer' => '1',
      'config_schema_test_integer_empty_string' => '',
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
    );
    $untyped_to_typed = $untyped_values;

    $typed_values = array(
      'string' => '1',
      'empty_string' => '',
      'null_string' => NULL,
      'integer' => 100,
      'null_integer' => NULL,
      'boolean' => TRUE,
      'no_type' => '1',
      'mapping' => array(
        'string' => '1'
      ),
      'float' => 3.14,
      'null_float' => NULL,
      'sequence' => array (TRUE, FALSE, TRUE),
      'not_present_in_schema' => TRUE,
288 289
      'config_schema_test_integer' => 1,
      'config_schema_test_integer_empty_string' => NULL,
290 291 292
    );

    // Save config which has a schema that enforces types.
293
    \Drupal::config('config_schema_test.schema_data_types')
294 295
      ->setData($untyped_to_typed)
      ->save();
296
    $this->assertIdentical(\Drupal::config('config_schema_test.schema_data_types')->get(), $typed_values);
297 298

    // Save config which does not have a schema that enforces types.
299
    \Drupal::config('config_schema_test.no_schema_data_types')
300 301
      ->setData($untyped_values)
      ->save();
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
    $this->assertIdentical(\Drupal::config('config_schema_test.no_schema_data_types')->get(), $untyped_values);
  }

  /**
   * Tests fallback to a greedy wildcard.
   */
  function testSchemaFallback() {
    $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.wildcard_fallback.something');
    // This should be the schema of config_schema_test.wildcard_fallback.*.
    $expected = array();
    $expected['label'] = 'Schema wildcard fallback test';
    $expected['class'] = '\Drupal\Core\Config\Schema\Mapping';
    $expected['mapping']['testid']['type'] = 'string';
    $expected['mapping']['testid']['label'] = 'ID';
    $expected['mapping']['testdescription']['type'] = 'text';
    $expected['mapping']['testdescription']['label'] = 'Description';

    $this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_schema_test.wildcard_fallback.something');

    $definition2 = \Drupal::service('config.typed')->getDefinition('config_schema_test.wildcard_fallback.something.something');
    // This should be the schema of config_schema_test.wildcard_fallback.* as
    //well.
    $this->assertIdentical($definition, $definition2);
325 326
  }

327
}