TYPO3 CMS  TYPO3_7-6
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 
29 
34 {
36 
37  protected $mockConfiguration;
38 
44  protected function setUp()
45  {
46  $this->mockConfigurationBuilder = $this->getMock(\TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationBuilder::class);
47  $this->mockConfiguration = $this->getMock(\TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface::class);
48  }
49 
53  public function validSourceTypes()
54  {
55  return [
56  ['someString', 'string'],
57  [42, 'integer'],
58  [3.5, 'float'],
59  [true, 'boolean'],
60  [[], 'array']
61  ];
62  }
63 
70  public function sourceTypeCanBeCorrectlyDetermined($source, $sourceType)
71  {
73  $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, ['dummy']);
74  $this->assertEquals($sourceType, $propertyMapper->_call('determineSourceType', $source));
75  }
76 
80  public function invalidSourceTypes()
81  {
82  return [
83  [null],
84  [new \stdClass()],
85  [new \ArrayObject()]
86  ];
87  }
88 
95  public function sourceWhichIsNoSimpleTypeThrowsException($source)
96  {
98  $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, ['dummy']);
99  $propertyMapper->_call('determineSourceType', $source);
100  }
101 
109  protected function getMockTypeConverter($name = '', $canConvertFrom = true, $properties = [], $typeOfSubObject = '')
110  {
111  $mockTypeConverter = $this->getMock(\TYPO3\CMS\Extbase\Property\TypeConverterInterface::class);
112  $mockTypeConverter->_name = $name;
113  $mockTypeConverter->expects($this->any())->method('canConvertFrom')->will($this->returnValue($canConvertFrom));
114  $mockTypeConverter->expects($this->any())->method('convertFrom')->will($this->returnValue($name));
115  $mockTypeConverter->expects($this->any())->method('getSourceChildPropertiesToBeConverted')->will($this->returnValue($properties));
116  $mockTypeConverter->expects($this->any())->method('getTypeOfChildProperty')->will($this->returnValue($typeOfSubObject));
117  return $mockTypeConverter;
118  }
119 
123  public function findTypeConverterShouldReturnTypeConverterFromConfigurationIfItIsSet()
124  {
125  $mockTypeConverter = $this->getMockTypeConverter();
126  $this->mockConfiguration->expects($this->any())->method('getTypeConverter')->will($this->returnValue($mockTypeConverter));
128  $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, ['dummy']);
129  $this->assertSame($mockTypeConverter, $propertyMapper->_call('findTypeConverter', 'someSource', 'someTargetType', $this->mockConfiguration));
130  }
131 
137  {
138  return [
139  ['someStringSource', 'string', [
140  'string' => [
141  'string' => [
142  10 => $this->getMockTypeConverter('string2string,prio10'),
143  1 => $this->getMockTypeConverter('string2string,prio1'),
144  ]
145  ]], 'string2string,prio10'
146  ],
147  [['some' => 'array'], 'string', [
148  'array' => [
149  'string' => [
150  10 => $this->getMockTypeConverter('array2string,prio10'),
151  1 => $this->getMockTypeConverter('array2string,prio1'),
152  ]
153  ]], 'array2string,prio10'
154  ],
155  ['someStringSource', 'bool', [
156  'string' => [
157  'boolean' => [
158  10 => $this->getMockTypeConverter('string2boolean,prio10'),
159  1 => $this->getMockTypeConverter('string2boolean,prio1'),
160  ]
161  ]], 'string2boolean,prio10'
162  ],
163  ['someStringSource', 'int', [
164  'string' => [
165  'integer' => [
166  10 => $this->getMockTypeConverter('string2integer,prio10'),
167  1 => $this->getMockTypeConverter('string2integer,prio1'),
168  ],
169  ]], 'string2integer,prio10'
170  ]
171  ];
172  }
173 
182  public function findTypeConverterShouldReturnHighestPriorityTypeConverterForSimpleType($source, $targetType, $typeConverters, $expectedTypeConverter)
183  {
184  $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, ['dummy']);
185  $propertyMapper->_set('typeConverters', $typeConverters);
186  $actualTypeConverter = $propertyMapper->_call('findTypeConverter', $source, $targetType, $this->mockConfiguration);
187  $this->assertSame($expectedTypeConverter, $actualTypeConverter->_name);
188  }
189 
194  {
195  $data = [];
196 
197  $className2 = DataProviderTwo::class;
198  $className3 = DataProviderThree::class;
199 
200  $interfaceName1 = DataProviderOneInterface::class;
201  $interfaceName2 = DataProviderTwoInterface::class;
202  $interfaceName3 = DataProviderThreeInterface::class;
203 
204  // The most specific converter should win
205  $data[] = [
206  'target' => $className3,
207  'expectedConverter' => 'Class3Converter',
208  'typeConverters' => [
209  $className2 => [0 => $this->getMockTypeConverter('Class2Converter')],
210  $className3 => [0 => $this->getMockTypeConverter('Class3Converter')],
211 
212  $interfaceName1 => [0 => $this->getMockTypeConverter('Interface1Converter')],
213  $interfaceName2 => [0 => $this->getMockTypeConverter('Interface2Converter')],
214  $interfaceName3 => [0 => $this->getMockTypeConverter('Interface3Converter')],
215  ]
216  ];
217 
218  // In case the most specific converter does not want to handle this conversion, the second one is taken.
219  $data[] = [
220  'target' => $className3,
221  'expectedConverter' => 'Class2Converter',
222  'typeConverters' => [
223  $className2 => [0 => $this->getMockTypeConverter('Class2Converter')],
224  $className3 => [0 => $this->getMockTypeConverter('Class3Converter', false)],
225 
226  $interfaceName1 => [0 => $this->getMockTypeConverter('Interface1Converter')],
227  $interfaceName2 => [0 => $this->getMockTypeConverter('Interface2Converter')],
228  $interfaceName3 => [0 => $this->getMockTypeConverter('Interface3Converter')],
229  ]
230  ];
231 
232  // In case there is no most-specific-converter, we climb ub the type hierarchy
233  $data[] = [
234  'target' => $className3,
235  'expectedConverter' => 'Class2Converter-HighPriority',
236  'typeConverters' => [
237  $className2 => [0 => $this->getMockTypeConverter('Class2Converter'), 10 => $this->getMockTypeConverter('Class2Converter-HighPriority')]
238  ]
239  ];
240 
241  // If no parent class converter wants to handle it, we ask for all interface converters.
242  $data[] = [
243  'target' => $className3,
244  'expectedConverter' => 'Interface1Converter',
245  'typeConverters' => [
246  $className2 => [0 => $this->getMockTypeConverter('Class2Converter', false), 10 => $this->getMockTypeConverter('Class2Converter-HighPriority', false)],
247 
248  $interfaceName1 => [4 => $this->getMockTypeConverter('Interface1Converter')],
249  $interfaceName2 => [1 => $this->getMockTypeConverter('Interface2Converter')],
250  $interfaceName3 => [2 => $this->getMockTypeConverter('Interface3Converter')],
251  ]
252  ];
253 
254  // If two interface converters have the same priority, an exception is thrown.
255  $data[] = [
256  'target' => $className3,
257  'expectedConverter' => 'Interface1Converter',
258  'typeConverters' => [
259  $className2 => [0 => $this->getMockTypeConverter('Class2Converter', false), 10 => $this->getMockTypeConverter('Class2Converter-HighPriority', false)],
260 
261  $interfaceName1 => [4 => $this->getMockTypeConverter('Interface1Converter')],
262  $interfaceName2 => [2 => $this->getMockTypeConverter('Interface2Converter')],
263  $interfaceName3 => [2 => $this->getMockTypeConverter('Interface3Converter')],
264  ],
265  'shouldFailWithException' => \TYPO3\CMS\Extbase\Property\Exception\DuplicateTypeConverterException::class
266  ];
267 
268  // If no interface converter wants to handle it, a converter for "object" is looked up.
269  $data[] = [
270  'target' => $className3,
271  'expectedConverter' => 'GenericObjectConverter-HighPriority',
272  'typeConverters' => [
273  $className2 => [0 => $this->getMockTypeConverter('Class2Converter', false), 10 => $this->getMockTypeConverter('Class2Converter-HighPriority', false)],
274 
275  $interfaceName1 => [4 => $this->getMockTypeConverter('Interface1Converter', false)],
276  $interfaceName2 => [3 => $this->getMockTypeConverter('Interface2Converter', false)],
277  $interfaceName3 => [2 => $this->getMockTypeConverter('Interface3Converter', false)],
278  'object' => [1 => $this->getMockTypeConverter('GenericObjectConverter'), 10 => $this->getMockTypeConverter('GenericObjectConverter-HighPriority')]
279  ],
280  ];
281 
282  // If the target is no valid class name and no simple type, an exception is thrown
283  $data[] = [
284  'target' => 'SomeNotExistingClassName',
285  'expectedConverter' => 'GenericObjectConverter-HighPriority',
286  'typeConverters' => [],
287  'shouldFailWithException' => \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException::class
288  ];
289 
290  // if the type converter is not found, we expect an exception
291  $data[] = [
292  'target' => $className3,
293  'expectedConverter' => 'Class3Converter',
294  'typeConverters' => [],
295  'shouldFailWithException' => \TYPO3\CMS\Extbase\Property\Exception\TypeConverterException::class
296  ];
297 
298  // If The target type is no string, we expect an exception.
299  $data[] = [
300  'target' => new \stdClass(),
301  'expectedConverter' => '',
302  'typeConverters' => [],
303  'shouldFailWithException' => \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException::class
304  ];
305  return $data;
306  }
307 
318  public function findTypeConverterShouldReturnConverterForTargetObjectIfItExists($targetClass, $expectedTypeConverter, $typeConverters, $shouldFailWithException = false)
319  {
320  $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, ['dummy']);
321  $propertyMapper->_set('typeConverters', ['string' => $typeConverters]);
322  try {
323  $actualTypeConverter = $propertyMapper->_call('findTypeConverter', 'someSourceString', $targetClass, $this->mockConfiguration);
324  if ($shouldFailWithException) {
325  $this->fail('Expected exception ' . $shouldFailWithException . ' which was not thrown.');
326  }
327  $this->assertSame($expectedTypeConverter, $actualTypeConverter->_name);
328  } catch (\Exception $e) {
329  if ($shouldFailWithException === false) {
330  throw $e;
331  }
332  $this->assertInstanceOf($shouldFailWithException, $e);
333  }
334  }
335 
340  {
341  $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, ['dummy']);
342  $this->inject($propertyMapper, 'configurationBuilder', $this->mockConfigurationBuilder);
343 
344  $this->mockConfigurationBuilder->expects($this->once())->method('build')->will($this->returnValue($this->mockConfiguration));
345 
346  $converter = $this->getMockTypeConverter('string2string');
347  $typeConverters = [
348  'string' => [
349  'string' => [10 => $converter]
350  ]
351  ];
352 
353  $propertyMapper->_set('typeConverters', $typeConverters);
354  $this->assertEquals('string2string', $propertyMapper->convert('source', 'string'));
355  }
356 
360  public function findFirstEligibleTypeConverterInObjectHierarchyShouldReturnNullIfSourceTypeIsUnknown()
361  {
363  $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, ['dummy']);
364  $this->assertNull($propertyMapper->_call('findFirstEligibleTypeConverterInObjectHierarchy', 'source', 'unknownSourceType', \TYPO3\CMS\Extbase\Core\Bootstrap::class));
365  }
366 
371  {
372  $source = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
373  $targetType = \TYPO3\CMS\Extbase\Persistence\ObjectStorage::class;
374  $propertyPath = '';
375  $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, ['dummy']);
376  $this->assertSame($source, $propertyMapper->_callRef('doMapping', $source, $targetType, $this->mockConfiguration, $propertyPath));
377  }
378 
383  {
384  $source = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
385  $targetType = \TYPO3\CMS\Extbase\Persistence\ObjectStorage::class . '<SomeEntity>';
386  $propertyPath = '';
387  $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, ['dummy']);
388  $this->assertSame($source, $propertyMapper->_callRef('doMapping', $source, $targetType, $this->mockConfiguration, $propertyPath));
389  }
390 
395  {
396  $source = ['firstProperty' => 1, 'secondProperty' => 2];
397  $typeConverters = [
398  'array' => [
399  'stdClass' => [10 => $this->getMockTypeConverter('array2object', true, $source, 'integer')]
400  ],
401  'integer' => [
402  'integer' => [10 => $this->getMockTypeConverter('integer2integer')]
403  ]
404  ];
405  $configuration = new \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration();
406 
407  $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, ['dummy']);
408  $propertyMapper->_set('typeConverters', $typeConverters);
409 
410  $propertyMapper->convert($source, 'stdClass', $configuration->allowProperties('firstProperty')->skipProperties('secondProperty'));
411  }
412 
417  {
418  $source = ['firstProperty' => 1, 'secondProperty' => 2];
419  $typeConverters = [
420  'array' => [
421  'stdClass' => [10 => $this->getMockTypeConverter('array2object', true, $source, 'integer')]
422  ],
423  'integer' => [
424  'integer' => [10 => $this->getMockTypeConverter('integer2integer')]
425  ]
426  ];
427  $configuration = new \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration();
428 
429  $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, ['dummy']);
430  $propertyMapper->_set('typeConverters', $typeConverters);
431 
432  $propertyMapper->convert($source, 'stdClass', $configuration->allowProperties('firstProperty')->skipUnknownProperties());
433  }
434 }
inject($target, $name, $dependency)
getMockTypeConverter($name='', $canConvertFrom=true, $properties=[], $typeOfSubObject='')
findTypeConverterShouldReturnHighestPriorityTypeConverterForSimpleType($source, $targetType, $typeConverters, $expectedTypeConverter)
getAccessibleMock( $originalClassName, $methods=[], array $arguments=[], $mockClassName='', $callOriginalConstructor=true, $callOriginalClone=true, $callAutoload=true)
findTypeConverterShouldReturnConverterForTargetObjectIfItExists($targetClass, $expectedTypeConverter, $typeConverters, $shouldFailWithException=false)