[TASK] Removes eval() in extbase unit tests for TypeConverter
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Tests / Unit / Property / PropertyMapperTest.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Tests\Unit\Property;
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
24 use TYPO3\CMS\Extbase\Tests\Unit\Property\Fixtures\DataProviderOne;
25 use TYPO3\CMS\Extbase\Tests\Unit\Property\Fixtures\DataProviderOneInterface;
26 use TYPO3\CMS\Extbase\Tests\Unit\Property\Fixtures\DataProviderThree;
27 use TYPO3\CMS\Extbase\Tests\Unit\Property\Fixtures\DataProviderThreeInterface;
28 use TYPO3\CMS\Extbase\Tests\Unit\Property\Fixtures\DataProviderTwo;
29 use TYPO3\CMS\Extbase\Tests\Unit\Property\Fixtures\DataProviderTwoInterface;
30
31 /**
32 * Test case
33 */
34 class PropertyMapperTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
35
36 protected $mockConfigurationBuilder;
37
38 protected $mockConfiguration;
39
40 /**
41 * Sets up this test case
42 *
43 * @return void
44 */
45 protected function setUp() {
46 $this->mockConfigurationBuilder = $this->getMock(\TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationBuilder::class);
47 $this->mockConfiguration = $this->getMock(\TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface::class);
48 }
49
50 /**
51 * @return array
52 */
53 public function validSourceTypes() {
54 return array(
55 array('someString', 'string'),
56 array(42, 'integer'),
57 array(3.5, 'float'),
58 array(TRUE, 'boolean'),
59 array(array(), 'array')
60 );
61 }
62
63 /**
64 * @test
65 * @dataProvider validSourceTypes
66 * @param mixed $source
67 * @param mixed $sourceType
68 */
69 public function sourceTypeCanBeCorrectlyDetermined($source, $sourceType) {
70 /** @var \TYPO3\CMS\Extbase\Property\PropertyMapper|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface */
71 $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, array('dummy'));
72 $this->assertEquals($sourceType, $propertyMapper->_call('determineSourceType', $source));
73 }
74
75 /**
76 * @return array
77 */
78 public function invalidSourceTypes() {
79 return array(
80 array(NULL),
81 array(new \stdClass()),
82 array(new \ArrayObject())
83 );
84 }
85
86 /**
87 * @test
88 * @dataProvider invalidSourceTypes
89 * @expectedException \TYPO3\CMS\Extbase\Property\Exception\InvalidSourceException
90 * @param mixed $source
91 */
92 public function sourceWhichIsNoSimpleTypeThrowsException($source) {
93 /** @var \TYPO3\CMS\Extbase\Property\PropertyMapper|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface */
94 $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, array('dummy'));
95 $propertyMapper->_call('determineSourceType', $source);
96 }
97
98 /**
99 * @param string $name
100 * @param bool $canConvertFrom
101 * @param array $properties
102 * @param string $typeOfSubObject
103 * @return \PHPUnit_Framework_MockObject_MockObject
104 */
105 protected function getMockTypeConverter($name = '', $canConvertFrom = TRUE, $properties = array(), $typeOfSubObject = '') {
106 $mockTypeConverter = $this->getMock(\TYPO3\CMS\Extbase\Property\TypeConverterInterface::class);
107 $mockTypeConverter->_name = $name;
108 $mockTypeConverter->expects($this->any())->method('canConvertFrom')->will($this->returnValue($canConvertFrom));
109 $mockTypeConverter->expects($this->any())->method('convertFrom')->will($this->returnValue($name));
110 $mockTypeConverter->expects($this->any())->method('getSourceChildPropertiesToBeConverted')->will($this->returnValue($properties));
111 $mockTypeConverter->expects($this->any())->method('getTypeOfChildProperty')->will($this->returnValue($typeOfSubObject));
112 return $mockTypeConverter;
113 }
114
115 /**
116 * @test
117 */
118 public function findTypeConverterShouldReturnTypeConverterFromConfigurationIfItIsSet() {
119 $mockTypeConverter = $this->getMockTypeConverter();
120 $this->mockConfiguration->expects($this->any())->method('getTypeConverter')->will($this->returnValue($mockTypeConverter));
121 /** @var \TYPO3\CMS\Extbase\Property\PropertyMapper|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface */
122 $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, array('dummy'));
123 $this->assertSame($mockTypeConverter, $propertyMapper->_call('findTypeConverter', 'someSource', 'someTargetType', $this->mockConfiguration));
124 }
125
126 /**
127 * Simple type conversion
128 * @return array
129 */
130 public function dataProviderForFindTypeConverter() {
131 return array(
132 array('someStringSource', 'string', array(
133 'string' => array(
134 'string' => array(
135 10 => $this->getMockTypeConverter('string2string,prio10'),
136 1 => $this->getMockTypeConverter('string2string,prio1'),
137 )
138 )), 'string2string,prio10'
139 ),
140 array(array('some' => 'array'), 'string', array(
141 'array' => array(
142 'string' => array(
143 10 => $this->getMockTypeConverter('array2string,prio10'),
144 1 => $this->getMockTypeConverter('array2string,prio1'),
145 )
146 )), 'array2string,prio10'
147 ),
148 array('someStringSource', 'bool', array(
149 'string' => array(
150 'boolean' => array(
151 10 => $this->getMockTypeConverter('string2boolean,prio10'),
152 1 => $this->getMockTypeConverter('string2boolean,prio1'),
153 )
154 )), 'string2boolean,prio10'
155 ),
156 array('someStringSource', 'int', array(
157 'string' => array(
158 'integer' => array(
159 10 => $this->getMockTypeConverter('string2integer,prio10'),
160 1 => $this->getMockTypeConverter('string2integer,prio1'),
161 ),
162 )), 'string2integer,prio10'
163 )
164 );
165 }
166
167 /**
168 * @test
169 * @dataProvider dataProviderForFindTypeConverter
170 * @param mixed $source
171 * @param mixed $targetType
172 * @param mixed $typeConverters
173 * @param mixed $expectedTypeConverter
174 */
175 public function findTypeConverterShouldReturnHighestPriorityTypeConverterForSimpleType($source, $targetType, $typeConverters, $expectedTypeConverter) {
176 $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, array('dummy'));
177 $propertyMapper->_set('typeConverters', $typeConverters);
178 $actualTypeConverter = $propertyMapper->_call('findTypeConverter', $source, $targetType, $this->mockConfiguration);
179 $this->assertSame($expectedTypeConverter, $actualTypeConverter->_name);
180 }
181
182 /**
183 * @return array
184 */
185 public function dataProviderForObjectTypeConverters() {
186 $data = array();
187
188 $className2 = DataProviderTwo::class;
189 $className3 = DataProviderThree::class;
190
191 $interfaceName1 = DataProviderOneInterface::class;
192 $interfaceName2 = DataProviderTwoInterface::class;
193 $interfaceName3 = DataProviderThreeInterface::class;
194
195 // The most specific converter should win
196 $data[] = array(
197 'target' => $className3,
198 'expectedConverter' => 'Class3Converter',
199 'typeConverters' => array(
200 $className2 => array(0 => $this->getMockTypeConverter('Class2Converter')),
201 $className3 => array(0 => $this->getMockTypeConverter('Class3Converter')),
202
203 $interfaceName1 => array(0 => $this->getMockTypeConverter('Interface1Converter')),
204 $interfaceName2 => array(0 => $this->getMockTypeConverter('Interface2Converter')),
205 $interfaceName3 => array(0 => $this->getMockTypeConverter('Interface3Converter')),
206 )
207 );
208
209 // In case the most specific converter does not want to handle this conversion, the second one is taken.
210 $data[] = array(
211 'target' => $className3,
212 'expectedConverter' => 'Class2Converter',
213 'typeConverters' => array(
214 $className2 => array(0 => $this->getMockTypeConverter('Class2Converter')),
215 $className3 => array(0 => $this->getMockTypeConverter('Class3Converter', FALSE)),
216
217 $interfaceName1 => array(0 => $this->getMockTypeConverter('Interface1Converter')),
218 $interfaceName2 => array(0 => $this->getMockTypeConverter('Interface2Converter')),
219 $interfaceName3 => array(0 => $this->getMockTypeConverter('Interface3Converter')),
220 )
221 );
222
223 // In case there is no most-specific-converter, we climb ub the type hierarchy
224 $data[] = array(
225 'target' => $className3,
226 'expectedConverter' => 'Class2Converter-HighPriority',
227 'typeConverters' => array(
228 $className2 => array(0 => $this->getMockTypeConverter('Class2Converter'), 10 => $this->getMockTypeConverter('Class2Converter-HighPriority'))
229 )
230 );
231
232 // If no parent class converter wants to handle it, we ask for all interface converters.
233 $data[] = array(
234 'target' => $className3,
235 'expectedConverter' => 'Interface1Converter',
236 'typeConverters' => array(
237 $className2 => array(0 => $this->getMockTypeConverter('Class2Converter', FALSE), 10 => $this->getMockTypeConverter('Class2Converter-HighPriority', FALSE)),
238
239 $interfaceName1 => array(4 => $this->getMockTypeConverter('Interface1Converter')),
240 $interfaceName2 => array(1 => $this->getMockTypeConverter('Interface2Converter')),
241 $interfaceName3 => array(2 => $this->getMockTypeConverter('Interface3Converter')),
242 )
243 );
244
245 // If two interface converters have the same priority, an exception is thrown.
246 $data[] = array(
247 'target' => $className3,
248 'expectedConverter' => 'Interface1Converter',
249 'typeConverters' => array(
250 $className2 => array(0 => $this->getMockTypeConverter('Class2Converter', FALSE), 10 => $this->getMockTypeConverter('Class2Converter-HighPriority', FALSE)),
251
252 $interfaceName1 => array(4 => $this->getMockTypeConverter('Interface1Converter')),
253 $interfaceName2 => array(2 => $this->getMockTypeConverter('Interface2Converter')),
254 $interfaceName3 => array(2 => $this->getMockTypeConverter('Interface3Converter')),
255 ),
256 'shouldFailWithException' => \TYPO3\CMS\Extbase\Property\Exception\DuplicateTypeConverterException::class
257 );
258
259 // If no interface converter wants to handle it, a converter for "object" is looked up.
260 $data[] = array(
261 'target' => $className3,
262 'expectedConverter' => 'GenericObjectConverter-HighPriority',
263 'typeConverters' => array(
264 $className2 => array(0 => $this->getMockTypeConverter('Class2Converter', FALSE), 10 => $this->getMockTypeConverter('Class2Converter-HighPriority', FALSE)),
265
266 $interfaceName1 => array(4 => $this->getMockTypeConverter('Interface1Converter', FALSE)),
267 $interfaceName2 => array(3 => $this->getMockTypeConverter('Interface2Converter', FALSE)),
268 $interfaceName3 => array(2 => $this->getMockTypeConverter('Interface3Converter', FALSE)),
269 'object' => array(1 => $this->getMockTypeConverter('GenericObjectConverter'), 10 => $this->getMockTypeConverter('GenericObjectConverter-HighPriority'))
270 ),
271 );
272
273 // If the target is no valid class name and no simple type, an exception is thrown
274 $data[] = array(
275 'target' => 'SomeNotExistingClassName',
276 'expectedConverter' => 'GenericObjectConverter-HighPriority',
277 'typeConverters' => array(),
278 'shouldFailWithException' => \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException::class
279 );
280
281 // if the type converter is not found, we expect an exception
282 $data[] = array(
283 'target' => $className3,
284 'expectedConverter' => 'Class3Converter',
285 'typeConverters' => array(),
286 'shouldFailWithException' => \TYPO3\CMS\Extbase\Property\Exception\TypeConverterException::class
287 );
288
289 // If The target type is no string, we expect an exception.
290 $data[] = array(
291 'target' => new \stdClass(),
292 'expectedConverter' => '',
293 'typeConverters' => array(),
294 'shouldFailWithException' => \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException::class
295 );
296 return $data;
297 }
298
299 /**
300 * @test
301 * @dataProvider dataProviderForObjectTypeConverters
302 * @param mixed $targetClass
303 * @param mixed $expectedTypeConverter
304 * @param mixed $typeConverters
305 * @param bool $shouldFailWithException
306 * @throws \Exception
307 * @return void
308 */
309 public function findTypeConverterShouldReturnConverterForTargetObjectIfItExists($targetClass, $expectedTypeConverter, $typeConverters, $shouldFailWithException = FALSE) {
310 $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, array('dummy'));
311 $propertyMapper->_set('typeConverters', array('string' => $typeConverters));
312 try {
313 $actualTypeConverter = $propertyMapper->_call('findTypeConverter', 'someSourceString', $targetClass, $this->mockConfiguration);
314 if ($shouldFailWithException) {
315 $this->fail('Expected exception ' . $shouldFailWithException . ' which was not thrown.');
316 }
317 $this->assertSame($expectedTypeConverter, $actualTypeConverter->_name);
318 } catch (\Exception $e) {
319 if ($shouldFailWithException === FALSE) {
320 throw $e;
321 }
322 $this->assertInstanceOf($shouldFailWithException, $e);
323 }
324 }
325
326 /**
327 * @test
328 */
329 public function convertShouldAskConfigurationBuilderForDefaultConfiguration() {
330 $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, array('dummy'));
331 $this->inject($propertyMapper, 'configurationBuilder', $this->mockConfigurationBuilder);
332
333 $this->mockConfigurationBuilder->expects($this->once())->method('build')->will($this->returnValue($this->mockConfiguration));
334
335 $converter = $this->getMockTypeConverter('string2string');
336 $typeConverters = array(
337 'string' => array(
338 'string' => array(10 => $converter)
339 )
340 );
341
342 $propertyMapper->_set('typeConverters', $typeConverters);
343 $this->assertEquals('string2string', $propertyMapper->convert('source', 'string'));
344 }
345
346 /**
347 * @test
348 */
349 public function findFirstEligibleTypeConverterInObjectHierarchyShouldReturnNullIfSourceTypeIsUnknown() {
350 /** @var \TYPO3\CMS\Extbase\Property\PropertyMapper|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface */
351 $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, array('dummy'));
352 $this->assertNull($propertyMapper->_call('findFirstEligibleTypeConverterInObjectHierarchy', 'source', 'unknownSourceType', \TYPO3\CMS\Extbase\Core\Bootstrap::class));
353 }
354
355 /**
356 * @test
357 */
358 public function doMappingReturnsSourceUnchangedIfAlreadyConverted() {
359 $source = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
360 $targetType = \TYPO3\CMS\Extbase\Persistence\ObjectStorage::class;
361 $propertyPath = '';
362 $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, array('dummy'));
363 $this->assertSame($source, $propertyMapper->_callRef('doMapping', $source, $targetType, $this->mockConfiguration, $propertyPath));
364 }
365
366 /**
367 * @test
368 */
369 public function doMappingReturnsSourceUnchangedIfAlreadyConvertedToCompositeType() {
370 $source = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
371 $targetType = \TYPO3\CMS\Extbase\Persistence\ObjectStorage::class . '<SomeEntity>';
372 $propertyPath = '';
373 $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, array('dummy'));
374 $this->assertSame($source, $propertyMapper->_callRef('doMapping', $source, $targetType, $this->mockConfiguration, $propertyPath));
375 }
376
377 /**
378 * @test
379 */
380 public function convertSkipsPropertiesIfConfiguredTo() {
381 $source = array('firstProperty' => 1, 'secondProperty' => 2);
382 $typeConverters = array(
383 'array' => array(
384 'stdClass' => array(10 => $this->getMockTypeConverter('array2object', TRUE, $source, 'integer'))
385 ),
386 'integer' => array(
387 'integer' => array(10 => $this->getMockTypeConverter('integer2integer'))
388 )
389 );
390 $configuration = new \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration();
391
392 $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, array('dummy'));
393 $propertyMapper->_set('typeConverters', $typeConverters);
394
395 $propertyMapper->convert($source, 'stdClass', $configuration->allowProperties('firstProperty')->skipProperties('secondProperty'));
396 }
397
398 /**
399 * @test
400 */
401 public function convertSkipsUnknownPropertiesIfConfiguredTo() {
402 $source = array('firstProperty' => 1, 'secondProperty' => 2);
403 $typeConverters = array(
404 'array' => array(
405 'stdClass' => array(10 => $this->getMockTypeConverter('array2object', TRUE, $source, 'integer'))
406 ),
407 'integer' => array(
408 'integer' => array(10 => $this->getMockTypeConverter('integer2integer'))
409 )
410 );
411 $configuration = new \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration();
412
413 $propertyMapper = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Property\PropertyMapper::class, array('dummy'));
414 $propertyMapper->_set('typeConverters', $typeConverters);
415
416 $propertyMapper->convert($source, 'stdClass', $configuration->allowProperties('firstProperty')->skipUnknownProperties());
417 }
418
419 }