TYPO3 CMS  TYPO3_8-7
PropertyMapperTest.php
Go to the documentation of this file.
1 <?php
3 
4 /* *
5  * This script belongs to the Extbase framework. *
6  * *
7  * It is free software; you can redistribute it and/or modify it under *
8  * the terms of the GNU Lesser General Public License as published by the *
9  * Free Software Foundation, either version 3 of the License, or (at your *
10  * option) any later version. *
11  * *
12  * This script is distributed in the hope that it will be useful, but *
13  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- *
14  * TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser *
15  * General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU Lesser General Public *
18  * License along with the script. *
19  * If not, see http://www.gnu.org/licenses/lgpl.html *
20  * *
21  * The TYPO3 project - inspiring people to share! *
22  * */
23 
30 
34 class PropertyMapperTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
35 {
40 
44  protected $mockConfiguration;
45 
49  protected function setUp()
50  {
51  $this->mockConfigurationBuilder = $this->createMock(\TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationBuilder::class);
52  $this->mockConfiguration = $this->createMock(\TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface::class);
53  }
54 
58  public function validSourceTypes()
59  {
60  return [
61  ['someString', 'string'],
62  [42, 'integer'],
63  [3.5, 'float'],
64  [true, 'boolean'],
65  [[], 'array']
66  ];
67  }
68 
75  public function sourceTypeCanBeCorrectlyDetermined($source, $sourceType)
76  {
78  $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, ['dummy']);
79  $this->assertEquals($sourceType, $propertyMapper->_call('determineSourceType', $source));
80  }
81 
85  public function invalidSourceTypes()
86  {
87  return [
88  [null],
89  [new \stdClass()],
90  [new \ArrayObject()]
91  ];
92  }
93 
99  public function sourceWhichIsNoSimpleTypeThrowsException($source)
100  {
101  $this->expectException(InvalidSourceException::class);
102  $this->expectExceptionCode(1297773150);
104  $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, ['dummy']);
105  $propertyMapper->_call('determineSourceType', $source);
106  }
107 
115  protected function getMockTypeConverter($name = '', $canConvertFrom = true, $properties = [], $typeOfSubObject = '')
116  {
117  $mockTypeConverter = $this->createMock(\TYPO3\CMS\Extbase\Property\TypeConverterInterface::class);
118  $mockTypeConverter->_name = $name;
119  $mockTypeConverter->expects($this->any())->method('canConvertFrom')->will($this->returnValue($canConvertFrom));
120  $mockTypeConverter->expects($this->any())->method('convertFrom')->will($this->returnValue($name));
121  $mockTypeConverter->expects($this->any())->method('getSourceChildPropertiesToBeConverted')->will($this->returnValue($properties));
122  $mockTypeConverter->expects($this->any())->method('getTypeOfChildProperty')->will($this->returnValue($typeOfSubObject));
123  return $mockTypeConverter;
124  }
125 
129  public function findTypeConverterShouldReturnTypeConverterFromConfigurationIfItIsSet()
130  {
131  $mockTypeConverter = $this->getMockTypeConverter();
132  $this->mockConfiguration->expects($this->any())->method('getTypeConverter')->will($this->returnValue($mockTypeConverter));
134  $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, ['dummy']);
135  $this->assertSame($mockTypeConverter, $propertyMapper->_call('findTypeConverter', 'someSource', 'someTargetType', $this->mockConfiguration));
136  }
137 
143  {
144  return [
145  ['someStringSource', 'string', [
146  'string' => [
147  'string' => [
148  10 => $this->getMockTypeConverter('string2string,prio10'),
149  1 => $this->getMockTypeConverter('string2string,prio1'),
150  ]
151  ]], 'string2string,prio10'
152  ],
153  [['some' => 'array'], 'string', [
154  'array' => [
155  'string' => [
156  10 => $this->getMockTypeConverter('array2string,prio10'),
157  1 => $this->getMockTypeConverter('array2string,prio1'),
158  ]
159  ]], 'array2string,prio10'
160  ],
161  ['someStringSource', 'bool', [
162  'string' => [
163  'boolean' => [
164  10 => $this->getMockTypeConverter('string2boolean,prio10'),
165  1 => $this->getMockTypeConverter('string2boolean,prio1'),
166  ]
167  ]], 'string2boolean,prio10'
168  ],
169  ['someStringSource', 'int', [
170  'string' => [
171  'integer' => [
172  10 => $this->getMockTypeConverter('string2integer,prio10'),
173  1 => $this->getMockTypeConverter('string2integer,prio1'),
174  ],
175  ]], 'string2integer,prio10'
176  ]
177  ];
178  }
179 
188  public function findTypeConverterShouldReturnHighestPriorityTypeConverterForSimpleType($source, $targetType, $typeConverters, $expectedTypeConverter)
189  {
190  $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, ['dummy']);
191  $propertyMapper->_set('typeConverters', $typeConverters);
192  $actualTypeConverter = $propertyMapper->_call('findTypeConverter', $source, $targetType, $this->mockConfiguration);
193  $this->assertSame($expectedTypeConverter, $actualTypeConverter->_name);
194  }
195 
200  {
201  $data = [];
202 
203  $className2 = DataProviderTwo::class;
204  $className3 = DataProviderThree::class;
205 
206  $interfaceName1 = DataProviderOneInterface::class;
207  $interfaceName2 = DataProviderTwoInterface::class;
208  $interfaceName3 = DataProviderThreeInterface::class;
209 
210  // The most specific converter should win
211  $data[] = [
212  'target' => $className3,
213  'expectedConverter' => 'Class3Converter',
214  'typeConverters' => [
215  $className2 => [0 => $this->getMockTypeConverter('Class2Converter')],
216  $className3 => [0 => $this->getMockTypeConverter('Class3Converter')],
217 
218  $interfaceName1 => [0 => $this->getMockTypeConverter('Interface1Converter')],
219  $interfaceName2 => [0 => $this->getMockTypeConverter('Interface2Converter')],
220  $interfaceName3 => [0 => $this->getMockTypeConverter('Interface3Converter')],
221  ]
222  ];
223 
224  // In case the most specific converter does not want to handle this conversion, the second one is taken.
225  $data[] = [
226  'target' => $className3,
227  'expectedConverter' => 'Class2Converter',
228  'typeConverters' => [
229  $className2 => [0 => $this->getMockTypeConverter('Class2Converter')],
230  $className3 => [0 => $this->getMockTypeConverter('Class3Converter', false)],
231 
232  $interfaceName1 => [0 => $this->getMockTypeConverter('Interface1Converter')],
233  $interfaceName2 => [0 => $this->getMockTypeConverter('Interface2Converter')],
234  $interfaceName3 => [0 => $this->getMockTypeConverter('Interface3Converter')],
235  ]
236  ];
237 
238  // In case there is no most-specific-converter, we climb ub the type hierarchy
239  $data[] = [
240  'target' => $className3,
241  'expectedConverter' => 'Class2Converter-HighPriority',
242  'typeConverters' => [
243  $className2 => [0 => $this->getMockTypeConverter('Class2Converter'), 10 => $this->getMockTypeConverter('Class2Converter-HighPriority')]
244  ]
245  ];
246 
247  // If no parent class converter wants to handle it, we ask for all interface converters.
248  $data[] = [
249  'target' => $className3,
250  'expectedConverter' => 'Interface1Converter',
251  'typeConverters' => [
252  $className2 => [0 => $this->getMockTypeConverter('Class2Converter', false), 10 => $this->getMockTypeConverter('Class2Converter-HighPriority', false)],
253 
254  $interfaceName1 => [4 => $this->getMockTypeConverter('Interface1Converter')],
255  $interfaceName2 => [1 => $this->getMockTypeConverter('Interface2Converter')],
256  $interfaceName3 => [2 => $this->getMockTypeConverter('Interface3Converter')],
257  ]
258  ];
259 
260  // If two interface converters have the same priority, an exception is thrown.
261  $data[] = [
262  'target' => $className3,
263  'expectedConverter' => 'Interface1Converter',
264  'typeConverters' => [
265  $className2 => [0 => $this->getMockTypeConverter('Class2Converter', false), 10 => $this->getMockTypeConverter('Class2Converter-HighPriority', false)],
266 
267  $interfaceName1 => [4 => $this->getMockTypeConverter('Interface1Converter')],
268  $interfaceName2 => [2 => $this->getMockTypeConverter('Interface2Converter')],
269  $interfaceName3 => [2 => $this->getMockTypeConverter('Interface3Converter')],
270  ],
271  'shouldFailWithException' => \TYPO3\CMS\Extbase\Property\Exception\DuplicateTypeConverterException::class
272  ];
273 
274  // If no interface converter wants to handle it, a converter for "object" is looked up.
275  $data[] = [
276  'target' => $className3,
277  'expectedConverter' => 'GenericObjectConverter-HighPriority',
278  'typeConverters' => [
279  $className2 => [0 => $this->getMockTypeConverter('Class2Converter', false), 10 => $this->getMockTypeConverter('Class2Converter-HighPriority', false)],
280 
281  $interfaceName1 => [4 => $this->getMockTypeConverter('Interface1Converter', false)],
282  $interfaceName2 => [3 => $this->getMockTypeConverter('Interface2Converter', false)],
283  $interfaceName3 => [2 => $this->getMockTypeConverter('Interface3Converter', false)],
284  'object' => [1 => $this->getMockTypeConverter('GenericObjectConverter'), 10 => $this->getMockTypeConverter('GenericObjectConverter-HighPriority')]
285  ],
286  ];
287 
288  // If the target is no valid class name and no simple type, an exception is thrown
289  $data[] = [
290  'target' => 'SomeNotExistingClassName',
291  'expectedConverter' => 'GenericObjectConverter-HighPriority',
292  'typeConverters' => [],
293  'shouldFailWithException' => \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException::class
294  ];
295 
296  // if the type converter is not found, we expect an exception
297  $data[] = [
298  'target' => $className3,
299  'expectedConverter' => 'Class3Converter',
300  'typeConverters' => [],
301  'shouldFailWithException' => \TYPO3\CMS\Extbase\Property\Exception\TypeConverterException::class
302  ];
303 
304  // If The target type is no string, we expect an exception.
305  $data[] = [
306  'target' => new \stdClass(),
307  'expectedConverter' => '',
308  'typeConverters' => [],
309  'shouldFailWithException' => \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException::class
310  ];
311  return $data;
312  }
313 
323  public function findTypeConverterShouldReturnConverterForTargetObjectIfItExists($targetClass, $expectedTypeConverter, $typeConverters, $shouldFailWithException = false)
324  {
325  $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, ['dummy']);
326  $propertyMapper->_set('typeConverters', ['string' => $typeConverters]);
327  try {
328  $actualTypeConverter = $propertyMapper->_call('findTypeConverter', 'someSourceString', $targetClass, $this->mockConfiguration);
329  if ($shouldFailWithException) {
330  $this->fail('Expected exception ' . $shouldFailWithException . ' which was not thrown.');
331  }
332  $this->assertSame($expectedTypeConverter, $actualTypeConverter->_name);
333  } catch (\Exception $e) {
334  if ($shouldFailWithException === false) {
335  throw $e;
336  }
337  $this->assertInstanceOf($shouldFailWithException, $e);
338  }
339  }
340 
345  {
346  $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, ['dummy']);
347  $this->inject($propertyMapper, 'configurationBuilder', $this->mockConfigurationBuilder);
348 
349  $this->mockConfigurationBuilder->expects($this->once())->method('build')->will($this->returnValue($this->mockConfiguration));
350 
351  $converter = $this->getMockTypeConverter('string2string');
352  $typeConverters = [
353  'string' => [
354  'string' => [10 => $converter]
355  ]
356  ];
357 
358  $propertyMapper->_set('typeConverters', $typeConverters);
359  $this->assertEquals('string2string', $propertyMapper->convert('source', 'string'));
360  }
361 
365  public function findFirstEligibleTypeConverterInObjectHierarchyShouldReturnNullIfSourceTypeIsUnknown()
366  {
368  $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, ['dummy']);
369  $this->assertNull($propertyMapper->_call('findFirstEligibleTypeConverterInObjectHierarchy', 'source', 'unknownSourceType', \TYPO3\CMS\Extbase\Core\Bootstrap::class));
370  }
371 
376  {
377  $source = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
378  $targetType = \TYPO3\CMS\Extbase\Persistence\ObjectStorage::class;
379  $propertyPath = '';
380  $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, ['dummy']);
381  $this->assertSame($source, $propertyMapper->_callRef('doMapping', $source, $targetType, $this->mockConfiguration, $propertyPath));
382  }
383 
388  {
389  $source = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
390  $targetType = \TYPO3\CMS\Extbase\Persistence\ObjectStorage::class . '<SomeEntity>';
391  $propertyPath = '';
392  $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, ['dummy']);
393  $this->assertSame($source, $propertyMapper->_callRef('doMapping', $source, $targetType, $this->mockConfiguration, $propertyPath));
394  }
395 
400  {
401  $source = ['firstProperty' => 1, 'secondProperty' => 2];
402  $typeConverters = [
403  'array' => [
404  'stdClass' => [10 => $this->getMockTypeConverter('array2object', true, $source, 'integer')]
405  ],
406  'integer' => [
407  'integer' => [10 => $this->getMockTypeConverter('integer2integer')]
408  ]
409  ];
410  $configuration = new \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration();
411 
412  $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, ['dummy']);
413  $propertyMapper->_set('typeConverters', $typeConverters);
414 
415  $propertyMapper->convert($source, 'stdClass', $configuration->allowProperties('firstProperty')->skipProperties('secondProperty'));
416  }
417 
422  {
423  $source = ['firstProperty' => 1, 'secondProperty' => 2];
424  $typeConverters = [
425  'array' => [
426  'stdClass' => [10 => $this->getMockTypeConverter('array2object', true, $source, 'integer')]
427  ],
428  'integer' => [
429  'integer' => [10 => $this->getMockTypeConverter('integer2integer')]
430  ]
431  ];
432  $configuration = new \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration();
433 
434  $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, ['dummy']);
435  $propertyMapper->_set('typeConverters', $typeConverters);
436 
437  $propertyMapper->convert($source, 'stdClass', $configuration->allowProperties('firstProperty')->skipUnknownProperties());
438  }
439 }
getMockTypeConverter($name='', $canConvertFrom=true, $properties=[], $typeOfSubObject='')
findTypeConverterShouldReturnHighestPriorityTypeConverterForSimpleType($source, $targetType, $typeConverters, $expectedTypeConverter)
findTypeConverterShouldReturnConverterForTargetObjectIfItExists($targetClass, $expectedTypeConverter, $typeConverters, $shouldFailWithException=false)