c79f2cfc9f4d14df0854426d5ff3a0a8a9e46a97
[yaffs-website] / web / core / tests / Drupal / KernelTests / Core / TypedData / TypedDataTest.php
1 <?php
2
3 namespace Drupal\KernelTests\Core\TypedData;
4
5 use Drupal\Core\Datetime\DrupalDateTime;
6 use Drupal\Core\Field\BaseFieldDefinition;
7 use Drupal\Core\TypedData\DataDefinition;
8 use Drupal\Core\TypedData\ListDataDefinition;
9 use Drupal\Core\TypedData\MapDataDefinition;
10 use Drupal\Core\TypedData\Type\BinaryInterface;
11 use Drupal\Core\TypedData\Type\BooleanInterface;
12 use Drupal\Core\TypedData\Type\DateTimeInterface;
13 use Drupal\Core\TypedData\Type\DurationInterface;
14 use Drupal\Core\TypedData\Type\FloatInterface;
15 use Drupal\Core\TypedData\Type\IntegerInterface;
16 use Drupal\Core\TypedData\Type\StringInterface;
17 use Drupal\Core\TypedData\Type\UriInterface;
18 use Drupal\Core\TypedData\TypedDataInterface;
19 use Drupal\file\Entity\File;
20 use Drupal\KernelTests\KernelTestBase;
21
22 /**
23  * Tests the functionality of all core data types.
24  *
25  * @group TypedData
26  */
27 class TypedDataTest extends KernelTestBase {
28
29   /**
30    * The typed data manager to use.
31    *
32    * @var \Drupal\Core\TypedData\TypedDataManager
33    */
34   protected $typedDataManager;
35
36   /**
37    * Modules to enable.
38    *
39    * @var array
40    */
41   public static $modules = ['system', 'field', 'file', 'user'];
42
43   protected function setUp() {
44     parent::setup();
45
46     $this->installEntitySchema('file');
47     $this->typedDataManager = $this->container->get('typed_data_manager');
48   }
49
50   /**
51    * Creates a typed data object and ensures it implements TypedDataInterface.
52    *
53    * @see \Drupal\Core\TypedData\TypedDataManager::create()
54    */
55   protected function createTypedData($definition, $value = NULL, $name = NULL) {
56     if (is_array($definition)) {
57       $definition = DataDefinition::create($definition['type']);
58     }
59     $data = $this->typedDataManager->create($definition, $value, $name);
60     $this->assertTrue($data instanceof TypedDataInterface, 'Typed data object is an instance of the typed data interface.');
61     return $data;
62   }
63
64   /**
65    * Tests the basics around constructing and working with typed data objects.
66    */
67   public function testGetAndSet() {
68     // Boolean type.
69     $typed_data = $this->createTypedData(['type' => 'boolean'], TRUE);
70     $this->assertTrue($typed_data instanceof BooleanInterface, 'Typed data object is an instance of BooleanInterface.');
71     $this->assertTrue($typed_data->getValue() === TRUE, 'Boolean value was fetched.');
72     $this->assertEqual($typed_data->validate()->count(), 0);
73     $typed_data->setValue(FALSE);
74     $this->assertTrue($typed_data->getValue() === FALSE, 'Boolean value was changed.');
75     $this->assertEqual($typed_data->validate()->count(), 0);
76     $this->assertTrue(is_string($typed_data->getString()), 'Boolean value was converted to string');
77     $typed_data->setValue(NULL);
78     $this->assertNull($typed_data->getValue(), 'Boolean wrapper is null-able.');
79     $this->assertEqual($typed_data->validate()->count(), 0);
80     $typed_data->setValue('invalid');
81     $this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
82
83     // String type.
84     $value = $this->randomString();
85     $typed_data = $this->createTypedData(['type' => 'string'], $value);
86     $this->assertTrue($typed_data instanceof StringInterface, 'Typed data object is an instance of StringInterface.');
87     $this->assertTrue($typed_data->getValue() === $value, 'String value was fetched.');
88     $this->assertEqual($typed_data->validate()->count(), 0);
89     $new_value = $this->randomString();
90     $typed_data->setValue($new_value);
91     $this->assertTrue($typed_data->getValue() === $new_value, 'String value was changed.');
92     $this->assertEqual($typed_data->validate()->count(), 0);
93     // Funky test.
94     $this->assertTrue(is_string($typed_data->getString()), 'String value was converted to string');
95     $typed_data->setValue(NULL);
96     $this->assertNull($typed_data->getValue(), 'String wrapper is null-able.');
97     $this->assertEqual($typed_data->validate()->count(), 0);
98     $typed_data->setValue(['no string']);
99     $this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
100
101     // Integer type.
102     $value = rand();
103     $typed_data = $this->createTypedData(['type' => 'integer'], $value);
104     $this->assertTrue($typed_data instanceof IntegerInterface, 'Typed data object is an instance of IntegerInterface.');
105     $this->assertTrue($typed_data->getValue() === $value, 'Integer value was fetched.');
106     $this->assertEqual($typed_data->validate()->count(), 0);
107     $new_value = rand();
108     $typed_data->setValue($new_value);
109     $this->assertTrue($typed_data->getValue() === $new_value, 'Integer value was changed.');
110     $this->assertTrue(is_string($typed_data->getString()), 'Integer value was converted to string');
111     $this->assertEqual($typed_data->validate()->count(), 0);
112     $typed_data->setValue(NULL);
113     $this->assertNull($typed_data->getValue(), 'Integer wrapper is null-able.');
114     $this->assertEqual($typed_data->validate()->count(), 0);
115     $typed_data->setValue('invalid');
116     $this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
117
118     // Float type.
119     $value = 123.45;
120     $typed_data = $this->createTypedData(['type' => 'float'], $value);
121     $this->assertTrue($typed_data instanceof FloatInterface, 'Typed data object is an instance of FloatInterface.');
122     $this->assertTrue($typed_data->getValue() === $value, 'Float value was fetched.');
123     $this->assertEqual($typed_data->validate()->count(), 0);
124     $new_value = 678.90;
125     $typed_data->setValue($new_value);
126     $this->assertTrue($typed_data->getValue() === $new_value, 'Float value was changed.');
127     $this->assertTrue(is_string($typed_data->getString()), 'Float value was converted to string');
128     $this->assertEqual($typed_data->validate()->count(), 0);
129     $typed_data->setValue(NULL);
130     $this->assertNull($typed_data->getValue(), 'Float wrapper is null-able.');
131     $this->assertEqual($typed_data->validate()->count(), 0);
132     $typed_data->setValue('invalid');
133     $this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
134
135     // Date Time type.
136     $value = '2014-01-01T20:00:00+00:00';
137     $typed_data = $this->createTypedData(['type' => 'datetime_iso8601'], $value);
138     $this->assertTrue($typed_data instanceof DateTimeInterface, 'Typed data object is an instance of DateTimeInterface.');
139     $this->assertTrue($typed_data->getValue() == $value, 'Date value was fetched.');
140     $this->assertEqual($typed_data->getValue(), $typed_data->getDateTime()->format('c'), 'Value representation of a date is ISO 8601');
141     $this->assertEqual($typed_data->validate()->count(), 0);
142     $new_value = '2014-01-02T20:00:00+00:00';
143     $typed_data->setValue($new_value);
144     $this->assertTrue($typed_data->getDateTime()->format('c') === $new_value, 'Date value was changed and set by an ISO8601 date.');
145     $this->assertEqual($typed_data->validate()->count(), 0);
146     $this->assertTrue($typed_data->getDateTime()->format('Y-m-d') == '2014-01-02', 'Date value was changed and set by date string.');
147     $this->assertEqual($typed_data->validate()->count(), 0);
148     $typed_data->setValue(NULL);
149     $this->assertNull($typed_data->getDateTime(), 'Date wrapper is null-able.');
150     $this->assertEqual($typed_data->validate()->count(), 0);
151     $typed_data->setValue('invalid');
152     $this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
153     // Check implementation of DateTimeInterface.
154     $typed_data = $this->createTypedData(['type' => 'datetime_iso8601'], '2014-01-01T20:00:00+00:00');
155     $this->assertTrue($typed_data->getDateTime() instanceof DrupalDateTime);
156     $typed_data->setDateTime(new DrupalDateTime('2014-01-02T20:00:00+00:00'));
157     $this->assertEqual($typed_data->getValue(), '2014-01-02T20:00:00+00:00');
158     $typed_data->setValue(NULL);
159     $this->assertNull($typed_data->getDateTime());
160
161     // Timestamp type.
162     $value = REQUEST_TIME;
163     $typed_data = $this->createTypedData(['type' => 'timestamp'], $value);
164     $this->assertTrue($typed_data instanceof DateTimeInterface, 'Typed data object is an instance of DateTimeInterface.');
165     $this->assertTrue($typed_data->getValue() == $value, 'Timestamp value was fetched.');
166     $this->assertEqual($typed_data->validate()->count(), 0);
167     $new_value = REQUEST_TIME + 1;
168     $typed_data->setValue($new_value);
169     $this->assertTrue($typed_data->getValue() === $new_value, 'Timestamp value was changed and set.');
170     $this->assertEqual($typed_data->validate()->count(), 0);
171     $typed_data->setValue(NULL);
172     $this->assertNull($typed_data->getDateTime(), 'Timestamp wrapper is null-able.');
173     $this->assertEqual($typed_data->validate()->count(), 0);
174     $typed_data->setValue('invalid');
175     $this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
176     // Check implementation of DateTimeInterface.
177     $typed_data = $this->createTypedData(['type' => 'timestamp'], REQUEST_TIME);
178     $this->assertTrue($typed_data->getDateTime() instanceof DrupalDateTime);
179     $typed_data->setDateTime(DrupalDateTime::createFromTimestamp(REQUEST_TIME + 1));
180     $this->assertEqual($typed_data->getValue(), REQUEST_TIME + 1);
181     $typed_data->setValue(NULL);
182     $this->assertNull($typed_data->getDateTime());
183
184     // DurationIso8601 type.
185     $value = 'PT20S';
186     $typed_data = $this->createTypedData(['type' => 'duration_iso8601'], $value);
187     $this->assertTrue($typed_data instanceof DurationInterface, 'Typed data object is an instance of DurationInterface.');
188     $this->assertIdentical($typed_data->getValue(), $value, 'DurationIso8601 value was fetched.');
189     $this->assertEqual($typed_data->validate()->count(), 0);
190     $typed_data->setValue('P40D');
191     $this->assertEqual($typed_data->getDuration()->d, 40, 'DurationIso8601 value was changed and set by duration string.');
192     $this->assertTrue(is_string($typed_data->getString()), 'DurationIso8601 value was converted to string');
193     $this->assertEqual($typed_data->validate()->count(), 0);
194     $typed_data->setValue(NULL);
195     $this->assertNull($typed_data->getValue(), 'DurationIso8601 wrapper is null-able.');
196     $this->assertEqual($typed_data->validate()->count(), 0);
197     $typed_data->setValue('invalid');
198     $this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
199     // Check implementation of DurationInterface.
200     $typed_data = $this->createTypedData(['type' => 'duration_iso8601'], 'PT20S');
201     $this->assertTrue($typed_data->getDuration() instanceof \DateInterval);
202     $typed_data->setDuration(new \DateInterval('P40D'));
203     // @todo: Should we make this "nicer"?
204     $this->assertEqual($typed_data->getValue(), 'P0Y0M40DT0H0M0S');
205     $typed_data->setValue(NULL);
206     $this->assertNull($typed_data->getDuration());
207
208     // Time span type.
209     $value = 20;
210     $typed_data = $this->createTypedData(['type' => 'timespan'], $value);
211     $this->assertTrue($typed_data instanceof DurationInterface, 'Typed data object is an instance of DurationInterface.');
212     $this->assertIdentical($typed_data->getValue(), $value, 'Time span value was fetched.');
213     $this->assertEqual($typed_data->validate()->count(), 0);
214     $typed_data->setValue(60 * 60 * 4);
215     $this->assertEqual($typed_data->getDuration()->s, 14400, 'Time span was changed');
216     $this->assertTrue(is_string($typed_data->getString()), 'Time span value was converted to string');
217     $this->assertEqual($typed_data->validate()->count(), 0);
218     $typed_data->setValue(NULL);
219     $this->assertNull($typed_data->getValue(), 'Time span wrapper is null-able.');
220     $this->assertEqual($typed_data->validate()->count(), 0);
221     $typed_data->setValue('invalid');
222     $this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
223     // Check implementation of DurationInterface.
224     $typed_data = $this->createTypedData(['type' => 'timespan'], 20);
225     $this->assertTrue($typed_data->getDuration() instanceof \DateInterval);
226     $typed_data->setDuration(new \DateInterval('PT4H'));
227     $this->assertEqual($typed_data->getValue(), 60 * 60 * 4);
228     $typed_data->setValue(NULL);
229     $this->assertNull($typed_data->getDuration());
230
231     // URI type.
232     $uri = 'http://example.com/foo/';
233     $typed_data = $this->createTypedData(['type' => 'uri'], $uri);
234     $this->assertTrue($typed_data instanceof UriInterface, 'Typed data object is an instance of UriInterface.');
235     $this->assertTrue($typed_data->getValue() === $uri, 'URI value was fetched.');
236     $this->assertEqual($typed_data->validate()->count(), 0);
237     $typed_data->setValue($uri . 'bar.txt');
238     $this->assertTrue($typed_data->getValue() === $uri . 'bar.txt', 'URI value was changed.');
239     $this->assertTrue(is_string($typed_data->getString()), 'URI value was converted to string');
240     $this->assertEqual($typed_data->validate()->count(), 0);
241     $typed_data->setValue(NULL);
242     $this->assertNull($typed_data->getValue(), 'URI wrapper is null-able.');
243     $this->assertEqual($typed_data->validate()->count(), 0);
244     $typed_data->setValue('invalid');
245     $this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
246     $typed_data->setValue('public://field/image/Photo on 4-28-14 at 12.01 PM.jpg');
247     $this->assertEqual($typed_data->validate()->count(), 0, 'Filename with spaces is valid.');
248
249     // Generate some files that will be used to test the binary data type.
250     $files = [];
251     for ($i = 0; $i < 3; $i++) {
252       $path = "public://example_$i.png";
253       file_unmanaged_copy(\Drupal::root() . '/core/misc/druplicon.png', $path);
254       $image = File::create(['uri' => $path]);
255       $image->save();
256       $files[] = $image;
257     }
258
259     // Email type.
260     $value = $this->randomString();
261     $typed_data = $this->createTypedData(['type' => 'email'], $value);
262     $this->assertTrue($typed_data instanceof StringInterface, 'Typed data object is an instance of StringInterface.');
263     $this->assertIdentical($typed_data->getValue(), $value, 'Email value was fetched.');
264     $new_value = 'test@example.com';
265     $typed_data->setValue($new_value);
266     $this->assertIdentical($typed_data->getValue(), $new_value, 'Email value was changed.');
267     $this->assertTrue(is_string($typed_data->getString()), 'Email value was converted to string');
268     $this->assertEqual($typed_data->validate()->count(), 0);
269     $typed_data->setValue(NULL);
270     $this->assertNull($typed_data->getValue(), 'Email wrapper is null-able.');
271     $this->assertEqual($typed_data->validate()->count(), 0);
272     $typed_data->setValue('invalidATexample.com');
273     $this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
274
275     // Binary type.
276     $typed_data = $this->createTypedData(['type' => 'binary'], $files[0]->getFileUri());
277     $this->assertTrue($typed_data instanceof BinaryInterface, 'Typed data object is an instance of BinaryInterface.');
278     $this->assertTrue(is_resource($typed_data->getValue()), 'Binary value was fetched.');
279     $this->assertEqual($typed_data->validate()->count(), 0);
280     // Try setting by URI.
281     $typed_data->setValue($files[1]->getFileUri());
282     $this->assertEqual(fgets($typed_data->getValue()), fgets(fopen($files[1]->getFileUri(), 'r')), 'Binary value was changed.');
283     $this->assertTrue(is_string($typed_data->getString()), 'Binary value was converted to string');
284     $this->assertEqual($typed_data->validate()->count(), 0);
285     // Try setting by resource.
286     $typed_data->setValue(fopen($files[2]->getFileUri(), 'r'));
287     $this->assertEqual(fgets($typed_data->getValue()), fgets(fopen($files[2]->getFileUri(), 'r')), 'Binary value was changed.');
288     $this->assertTrue(is_string($typed_data->getString()), 'Binary value was converted to string');
289     $this->assertEqual($typed_data->validate()->count(), 0);
290     $typed_data->setValue(NULL);
291     $this->assertNull($typed_data->getValue(), 'Binary wrapper is null-able.');
292     $this->assertEqual($typed_data->validate()->count(), 0);
293     $typed_data->setValue('invalid');
294     $this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
295
296     // Any type.
297     $value = ['foo'];
298     $typed_data = $this->createTypedData(['type' => 'any'], $value);
299     $this->assertIdentical($typed_data->getValue(), $value, 'Any value was fetched.');
300     $new_value = 'test@example.com';
301     $typed_data->setValue($new_value);
302     $this->assertIdentical($typed_data->getValue(), $new_value, 'Any value was changed.');
303     $this->assertTrue(is_string($typed_data->getString()), 'Any value was converted to string');
304     $this->assertEqual($typed_data->validate()->count(), 0);
305     $typed_data->setValue(NULL);
306     $this->assertNull($typed_data->getValue(), 'Any wrapper is null-able.');
307     $this->assertEqual($typed_data->validate()->count(), 0);
308     // We cannot test invalid values as everything is valid for the any type,
309     // but make sure an array or object value passes validation also.
310     $typed_data->setValue(['entry']);
311     $this->assertEqual($typed_data->validate()->count(), 0);
312     $typed_data->setValue((object) ['entry']);
313     $this->assertEqual($typed_data->validate()->count(), 0);
314   }
315
316   /**
317    * Tests using typed data lists.
318    */
319   public function testTypedDataLists() {
320     // Test working with an existing list of strings.
321     $value = ['one', 'two', 'three'];
322     $typed_data = $this->createTypedData(ListDataDefinition::create('string'), $value);
323     $this->assertEqual($typed_data->getValue(), $value, 'List value has been set.');
324     // Test iterating.
325     $count = 0;
326     foreach ($typed_data as $item) {
327       $this->assertTrue($item instanceof TypedDataInterface);
328       $count++;
329     }
330     $this->assertEqual($count, 3);
331
332     // Test getting the string representation.
333     $this->assertEqual($typed_data->getString(), 'one, two, three');
334     $typed_data[1] = '';
335     $this->assertEqual($typed_data->getString(), 'one, three');
336
337     // Test using array access.
338     $this->assertEqual($typed_data[0]->getValue(), 'one');
339     $typed_data[] = 'four';
340     $this->assertEqual($typed_data[3]->getValue(), 'four');
341     $this->assertEqual($typed_data->count(), 4);
342     $this->assertTrue(isset($typed_data[0]));
343     $this->assertTrue(!isset($typed_data[6]));
344
345     // Test isEmpty and cloning.
346     $this->assertFalse($typed_data->isEmpty());
347     $clone = clone $typed_data;
348     $this->assertTrue($typed_data->getValue() === $clone->getValue());
349     $this->assertTrue($typed_data[0] !== $clone[0]);
350     $clone->setValue([]);
351     $this->assertTrue($clone->isEmpty());
352
353     // Make sure that resetting the value using NULL results in an empty array.
354     $clone->setValue([]);
355     $typed_data->setValue(NULL);
356     $this->assertIdentical($typed_data->getValue(), []);
357     $this->assertIdentical($clone->getValue(), []);
358
359     // Test dealing with NULL items.
360     $typed_data[] = NULL;
361     $this->assertTrue($typed_data->isEmpty());
362     $this->assertEqual(count($typed_data), 1);
363     $typed_data[] = '';
364     $this->assertFalse($typed_data->isEmpty());
365     $this->assertEqual(count($typed_data), 2);
366     $typed_data[] = 'three';
367     $this->assertFalse($typed_data->isEmpty());
368     $this->assertEqual(count($typed_data), 3);
369
370     $this->assertEqual($typed_data->getValue(), [NULL, '', 'three']);
371     // Test unsetting.
372     unset($typed_data[1]);
373     $this->assertEqual(count($typed_data), 2);
374     // Check that items were shifted.
375     $this->assertEqual($typed_data[1]->getValue(), 'three');
376
377     // Getting a not set list item returns NULL, and does not create a new item.
378     $this->assertNull($typed_data[2]);
379     $this->assertEqual(count($typed_data), 2);
380
381     // Test setting the list with less values.
382     $typed_data->setValue(['one']);
383     $this->assertEqual($typed_data->count(), 1);
384
385     // Test setting invalid values.
386     try {
387       $typed_data->setValue('string');
388       $this->fail('No exception has been thrown when setting an invalid value.');
389     }
390     catch (\Exception $e) {
391       $this->pass('Exception thrown:' . $e->getMessage());
392     }
393   }
394
395   /**
396    * Tests the filter() method on typed data lists.
397    */
398   public function testTypedDataListsFilter() {
399     // Check that an all-pass filter leaves the list untouched.
400     $value = ['zero', 'one'];
401     $typed_data = $this->createTypedData(ListDataDefinition::create('string'), $value);
402     $typed_data->filter(function(TypedDataInterface $item) {
403       return TRUE;
404     });
405     $this->assertEqual($typed_data->count(), 2);
406     $this->assertEqual($typed_data[0]->getValue(), 'zero');
407     $this->assertEqual($typed_data[0]->getName(), 0);
408     $this->assertEqual($typed_data[1]->getValue(), 'one');
409     $this->assertEqual($typed_data[1]->getName(), 1);
410
411     // Check that a none-pass filter empties the list.
412     $value = ['zero', 'one'];
413     $typed_data = $this->createTypedData(ListDataDefinition::create('string'), $value);
414     $typed_data->filter(function(TypedDataInterface $item) {
415       return FALSE;
416     });
417     $this->assertEqual($typed_data->count(), 0);
418
419     // Check that filtering correctly renumbers elements.
420     $value = ['zero', 'one', 'two'];
421     $typed_data = $this->createTypedData(ListDataDefinition::create('string'), $value);
422     $typed_data->filter(function(TypedDataInterface $item) {
423       return $item->getValue() !== 'one';
424     });
425     $this->assertEqual($typed_data->count(), 2);
426     $this->assertEqual($typed_data[0]->getValue(), 'zero');
427     $this->assertEqual($typed_data[0]->getName(), 0);
428     $this->assertEqual($typed_data[1]->getValue(), 'two');
429     $this->assertEqual($typed_data[1]->getName(), 1);
430   }
431
432   /**
433    * Tests using a typed data map.
434    */
435   public function testTypedDataMaps() {
436     // Test working with a simple map.
437     $value = [
438       'one' => 'eins',
439       'two' => 'zwei',
440       'three' => 'drei',
441     ];
442     $definition = MapDataDefinition::create()
443       ->setPropertyDefinition('one', DataDefinition::create('string'))
444       ->setPropertyDefinition('two', DataDefinition::create('string'))
445       ->setPropertyDefinition('three', DataDefinition::create('string'));
446
447     $typed_data = $this->createTypedData($definition, $value);
448
449     // Test iterating.
450     $count = 0;
451     foreach ($typed_data as $item) {
452       $this->assertTrue($item instanceof TypedDataInterface);
453       $count++;
454     }
455     $this->assertEqual($count, 3);
456
457     // Test retrieving metadata.
458     $this->assertEqual(array_keys($typed_data->getDataDefinition()->getPropertyDefinitions()), array_keys($value));
459     $definition = $typed_data->getDataDefinition()->getPropertyDefinition('one');
460     $this->assertEqual($definition->getDataType(), 'string');
461     $this->assertNull($typed_data->getDataDefinition()->getPropertyDefinition('invalid'));
462
463     // Test getting and setting properties.
464     $this->assertEqual($typed_data->get('one')->getValue(), 'eins');
465     $this->assertEqual($typed_data->toArray(), $value);
466     $typed_data->set('one', 'uno');
467     $this->assertEqual($typed_data->get('one')->getValue(), 'uno');
468     // Make sure the update is reflected in the value of the map also.
469     $value = $typed_data->getValue();
470     $this->assertEqual($value, [
471       'one' => 'uno',
472       'two' => 'zwei',
473       'three' => 'drei'
474     ]);
475
476     $properties = $typed_data->getProperties();
477     $this->assertEqual(array_keys($properties), array_keys($value));
478     $this->assertIdentical($properties['one'], $typed_data->get('one'), 'Properties are identical.');
479
480     // Test setting a not defined property. It shouldn't show up in the
481     // properties, but be kept in the values.
482     $typed_data->setValue(['foo' => 'bar']);
483     $this->assertEqual(array_keys($typed_data->getProperties()), ['one', 'two', 'three']);
484     $this->assertEqual(array_keys($typed_data->getValue()), ['foo', 'one', 'two', 'three']);
485
486     // Test getting the string representation.
487     $typed_data->setValue(['one' => 'eins', 'two' => '', 'three' => 'drei']);
488     $this->assertEqual($typed_data->getString(), 'eins, drei');
489
490     // Test isEmpty and cloning.
491     $this->assertFalse($typed_data->isEmpty());
492     $clone = clone $typed_data;
493     $this->assertTrue($typed_data->getValue() === $clone->getValue());
494     $this->assertTrue($typed_data->get('one') !== $clone->get('one'));
495     $clone->setValue([]);
496     $this->assertTrue($clone->isEmpty());
497
498     // Make sure the difference between NULL (not set) and an empty array is
499     // kept.
500     $typed_data->setValue(NULL);
501     $this->assertNull($typed_data->getValue());
502     $typed_data->setValue([]);
503     $value = $typed_data->getValue();
504     $this->assertTrue(isset($value) && is_array($value));
505
506     // Test accessing invalid properties.
507     $typed_data->setValue($value);
508     try {
509       $typed_data->get('invalid');
510       $this->fail('No exception has been thrown when getting an invalid value.');
511     }
512     catch (\Exception $e) {
513       $this->pass('Exception thrown:' . $e->getMessage());
514     }
515
516     // Test setting invalid values.
517     try {
518       $typed_data->setValue('invalid');
519       $this->fail('No exception has been thrown when setting an invalid value.');
520     }
521     catch (\Exception $e) {
522       $this->pass('Exception thrown:' . $e->getMessage());
523     }
524
525     // Test adding a new property to the map.
526     $typed_data->getDataDefinition()->setPropertyDefinition('zero', DataDefinition::create('any'));
527     $typed_data->set('zero', 'null');
528     $this->assertEqual($typed_data->get('zero')->getValue(), 'null');
529     $definition = $typed_data->get('zero')->getDataDefinition();
530     $this->assertEqual($definition->getDataType(), 'any', 'Definition for a new map entry returned.');
531   }
532
533   /**
534    * Tests typed data validation.
535    */
536   public function testTypedDataValidation() {
537     $definition = DataDefinition::create('integer')
538       ->setConstraints([
539         'Range' => ['min' => 5],
540       ]);
541     $violations = $this->typedDataManager->create($definition, 10)->validate();
542     $this->assertEqual($violations->count(), 0);
543
544     $integer = $this->typedDataManager->create($definition, 1);
545     $violations = $integer->validate();
546     $this->assertEqual($violations->count(), 1);
547
548     // Test translating violation messages.
549     $message = t('This value should be %limit or more.', ['%limit' => 5]);
550     $this->assertEqual($violations[0]->getMessage(), $message, 'Translated violation message retrieved.');
551     $this->assertEqual($violations[0]->getPropertyPath(), '');
552     $this->assertIdentical($violations[0]->getRoot(), $integer, 'Root object returned.');
553
554     // Test translating violation messages when pluralization is used.
555     $definition = DataDefinition::create('string')
556       ->setConstraints([
557         'Length' => ['min' => 10],
558       ]);
559     $violations = $this->typedDataManager->create($definition, "short")->validate();
560     $this->assertEqual($violations->count(), 1);
561     $message = t('This value is too short. It should have %limit characters or more.', ['%limit' => 10]);
562     $this->assertEqual($violations[0]->getMessage(), $message, 'Translated violation message retrieved.');
563
564     // Test having multiple violations.
565     $definition = DataDefinition::create('integer')
566       ->setConstraints([
567         'Range' => ['min' => 5],
568         'Null' => [],
569       ]);
570     $violations = $this->typedDataManager->create($definition, 10)->validate();
571     $this->assertEqual($violations->count(), 1);
572     $violations = $this->typedDataManager->create($definition, 1)->validate();
573     $this->assertEqual($violations->count(), 2);
574
575     // Test validating property containers and make sure the NotNull and Null
576     // constraints work with typed data containers.
577     $definition = BaseFieldDefinition::create('integer')
578       ->setConstraints(['NotNull' => []]);
579     $field_item = $this->typedDataManager->create($definition, ['value' => 10]);
580     $violations = $field_item->validate();
581     $this->assertEqual($violations->count(), 0);
582
583     $field_item = $this->typedDataManager->create($definition, ['value' => 'no integer']);
584     $violations = $field_item->validate();
585     $this->assertEqual($violations->count(), 1);
586     $this->assertEqual($violations[0]->getPropertyPath(), '0.value');
587
588     // Test that the field item may not be empty.
589     $field_item = $this->typedDataManager->create($definition);
590     $violations = $field_item->validate();
591     $this->assertEqual($violations->count(), 1);
592
593     // Test the Null constraint with typed data containers.
594     $definition = BaseFieldDefinition::create('float')
595       ->setConstraints(['Null' => []]);
596     $field_item = $this->typedDataManager->create($definition, ['value' => 11.5]);
597     $violations = $field_item->validate();
598     $this->assertEqual($violations->count(), 1);
599     $field_item = $this->typedDataManager->create($definition);
600     $violations = $field_item->validate();
601     $this->assertEqual($violations->count(), 0);
602
603     // Test getting constraint definitions by type.
604     $definitions = $this->typedDataManager->getValidationConstraintManager()->getDefinitionsByType('entity');
605     $this->assertTrue(isset($definitions['EntityType']), 'Constraint plugin found for type entity.');
606     $this->assertTrue(isset($definitions['Null']), 'Constraint plugin found for type entity.');
607     $this->assertTrue(isset($definitions['NotNull']), 'Constraint plugin found for type entity.');
608
609     $definitions = $this->typedDataManager->getValidationConstraintManager()->getDefinitionsByType('string');
610     $this->assertFalse(isset($definitions['EntityType']), 'Constraint plugin not found for type string.');
611     $this->assertTrue(isset($definitions['Null']), 'Constraint plugin found for type string.');
612     $this->assertTrue(isset($definitions['NotNull']), 'Constraint plugin found for type string.');
613
614     // Test automatic 'required' validation.
615     $definition = DataDefinition::create('integer')
616       ->setRequired(TRUE);
617     $violations = $this->typedDataManager->create($definition)->validate();
618     $this->assertEqual($violations->count(), 1);
619     $violations = $this->typedDataManager->create($definition, 0)->validate();
620     $this->assertEqual($violations->count(), 0);
621
622     // Test validating a list of a values and make sure property paths starting
623     // with "0" are created.
624     $definition = BaseFieldDefinition::create('integer');
625     $violations = $this->typedDataManager->create($definition, [['value' => 10]])->validate();
626     $this->assertEqual($violations->count(), 0);
627     $violations = $this->typedDataManager->create($definition, [['value' => 'string']])->validate();
628     $this->assertEqual($violations->count(), 1);
629
630     $this->assertEqual($violations[0]->getInvalidValue(), 'string');
631     $this->assertIdentical($violations[0]->getPropertyPath(), '0.value');
632   }
633
634 }