0a524ea1a80ec784367a528540da634d8ed8ed37
[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 * Testcase for the Property Mapper
25 *
26 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later
27 * @covers \TYPO3\CMS\Extbase\Property\PropertyMapper
28 */
29 class PropertyMapperTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
30
31 protected $mockConfigurationBuilder;
32
33 protected $mockConfiguration;
34
35 /**
36 * Sets up this test case
37 *
38 * @return void
39 * @author Sebastian Kurfürst <sebastian@typo3.org>
40 */
41 public function setUp() {
42 $this->mockConfigurationBuilder = $this->getMock('TYPO3\\CMS\\Extbase\\Property\\PropertyMappingConfigurationBuilder');
43 $this->mockConfiguration = $this->getMock('TYPO3\\CMS\\Extbase\\Property\\PropertyMappingConfigurationInterface');
44 }
45
46 /**
47 * @return array
48 */
49 public function validSourceTypes() {
50 return array(
51 array('someString', 'string'),
52 array(42, 'integer'),
53 array(3.5, 'float'),
54 array(TRUE, 'boolean'),
55 array(array(), 'array')
56 );
57 }
58
59 /**
60 * @test
61 * @dataProvider validSourceTypes
62 * @author Sebastian Kurfürst <sebastian@typo3.org>
63 * @param mixed $source
64 * @param mixed $sourceType
65 */
66 public function sourceTypeCanBeCorrectlyDetermined($source, $sourceType) {
67 /** @var \TYPO3\CMS\Extbase\Property\PropertyMapper|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface */
68 $propertyMapper = $this->getAccessibleMock('TYPO3\\CMS\\Extbase\\Property\\PropertyMapper', array('dummy'));
69 $this->assertEquals($sourceType, $propertyMapper->_call('determineSourceType', $source));
70 }
71
72 /**
73 * @return array
74 */
75 public function invalidSourceTypes() {
76 return array(
77 array(NULL),
78 array(new \stdClass()),
79 array(new \ArrayObject())
80 );
81 }
82
83 /**
84 * @test
85 * @dataProvider invalidSourceTypes
86 * @expectedException \TYPO3\CMS\Extbase\Property\Exception\InvalidSourceException
87 * @author Sebastian Kurfürst <sebastian@typo3.org>
88 * @param mixed $source
89 */
90 public function sourceWhichIsNoSimpleTypeThrowsException($source) {
91 /** @var \TYPO3\CMS\Extbase\Property\PropertyMapper|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface */
92 $propertyMapper = $this->getAccessibleMock('TYPO3\\CMS\\Extbase\\Property\\PropertyMapper', array('dummy'));
93 $propertyMapper->_call('determineSourceType', $source);
94 }
95
96 /**
97 * @param string $name
98 * @param boolean $canConvertFrom
99 * @param array $properties
100 * @param string $typeOfSubObject
101 * @return \PHPUnit_Framework_MockObject_MockObject
102 */
103 protected function getMockTypeConverter($name = '', $canConvertFrom = TRUE, $properties = array(), $typeOfSubObject = '') {
104 $mockTypeConverter = $this->getMock('TYPO3\\CMS\\Extbase\\Property\\TypeConverterInterface');
105 $mockTypeConverter->_name = $name;
106 $mockTypeConverter->expects($this->any())->method('canConvertFrom')->will($this->returnValue($canConvertFrom));
107 $mockTypeConverter->expects($this->any())->method('convertFrom')->will($this->returnValue($name));
108 $mockTypeConverter->expects($this->any())->method('getSourceChildPropertiesToBeConverted')->will($this->returnValue($properties));
109 $mockTypeConverter->expects($this->any())->method('getTypeOfChildProperty')->will($this->returnValue($typeOfSubObject));
110 return $mockTypeConverter;
111 }
112
113 /**
114 * @test
115 * @author Sebastian Kurfürst <sebastian@typo3.org>
116 */
117 public function findTypeConverterShouldReturnTypeConverterFromConfigurationIfItIsSet() {
118 $mockTypeConverter = $this->getMockTypeConverter();
119 $this->mockConfiguration->expects($this->any())->method('getTypeConverter')->will($this->returnValue($mockTypeConverter));
120 /** @var \TYPO3\CMS\Extbase\Property\PropertyMapper|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface */
121 $propertyMapper = $this->getAccessibleMock('TYPO3\\CMS\\Extbase\\Property\\PropertyMapper', array('dummy'));
122 $this->assertSame($mockTypeConverter, $propertyMapper->_call('findTypeConverter', 'someSource', 'someTargetType', $this->mockConfiguration));
123 }
124
125 /**
126 * Simple type conversion
127 *
128 * @return array
129 */
130 public function dataProviderForFindTypeConverter() {
131 return array(
132 array(
133 'someStringSource',
134 'string',
135 array(
136 'string' => array(
137 'string' => array(
138 10 => $this->getMockTypeConverter('string2string,prio10'),
139 1 => $this->getMockTypeConverter('string2string,prio1')
140 )
141 )
142 ),
143 'string2string,prio10'
144 ),
145 array(array('some' => 'array'), 'string', array(
146 'array' => array(
147 'string' => array(
148 10 => $this->getMockTypeConverter('array2string,prio10'),
149 1 => $this->getMockTypeConverter('array2string,prio1')
150 )
151 )
152 ), 'array2string,prio10')
153 );
154 }
155
156 /**
157 * @test
158 * @dataProvider dataProviderForFindTypeConverter
159 * @author Sebastian Kurfürst <sebastian@typo3.org>
160 * @param mixed $source
161 * @param mixed $targetType
162 * @param mixed $typeConverters
163 * @param mixed $expectedTypeConverter
164 */
165 public function findTypeConverterShouldReturnHighestPriorityTypeConverterForSimpleType($source, $targetType, $typeConverters, $expectedTypeConverter) {
166 $mockTypeHandlingService = $this->getMock('TYPO3\\CMS\\Extbase\\Service\\TypeHandlingService');
167 $mockTypeHandlingService->expects($this->any())->method('isSimpleType')->will($this->returnValue(TRUE));
168 /** @var \TYPO3\CMS\Extbase\Property\PropertyMapper|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface */
169 $propertyMapper = $this->getAccessibleMock('TYPO3\\CMS\\Extbase\\Property\\PropertyMapper', array('dummy'));
170 $propertyMapper->_set('typeHandlingService', $mockTypeHandlingService);
171 $propertyMapper->_set('typeConverters', $typeConverters);
172 $actualTypeConverter = $propertyMapper->_call('findTypeConverter', $source, $targetType, $this->mockConfiguration);
173 $this->assertSame($expectedTypeConverter, $actualTypeConverter->_name);
174 }
175
176 /**
177 * @return array
178 */
179 public function dataProviderForObjectTypeConverters() {
180 $data = array();
181 $className1 = uniqid('F3_FLOW3_Testclass1_', FALSE);
182 $className2 = uniqid('F3_FLOW3_Testclass2_', FALSE);
183 $className3 = uniqid('F3_FLOW3_Testclass3_', FALSE);
184 $interfaceName1 = uniqid('F3_FLOW3_TestInterface1_', FALSE);
185 $interfaceName2 = uniqid('F3_FLOW3_TestInterface2_', FALSE);
186 $interfaceName3 = uniqid('F3_FLOW3_TestInterface3_', FALSE);
187 eval("\n\t\t\tinterface {$interfaceName2} {}\n\t\t\tinterface {$interfaceName1} {}\n\n\t\t\tinterface {$interfaceName3} extends {$interfaceName2} {}\n\n\t\t\tclass {$className1} implements {$interfaceName1} {}\n\t\t\tclass {$className2} extends {$className1} {}\n\t\t\tclass {$className3} extends {$className2} implements {$interfaceName3} {}\n\t\t");
188 // The most specific converter should win
189 $data[] = array(
190 'target' => $className3,
191 'expectedConverter' => 'Class3Converter',
192 'typeConverters' => array(
193 $className2 => array(0 => $this->getMockTypeConverter('Class2Converter')),
194 $className3 => array(0 => $this->getMockTypeConverter('Class3Converter')),
195 $interfaceName1 => array(0 => $this->getMockTypeConverter('Interface1Converter')),
196 $interfaceName2 => array(0 => $this->getMockTypeConverter('Interface2Converter')),
197 $interfaceName3 => array(0 => $this->getMockTypeConverter('Interface3Converter'))
198 )
199 );
200 // In case the most specific converter does not want to handle this conversion, the second one is taken.
201 $data[] = array(
202 'target' => $className3,
203 'expectedConverter' => 'Class2Converter',
204 'typeConverters' => array(
205 $className2 => array(0 => $this->getMockTypeConverter('Class2Converter')),
206 $className3 => array(0 => $this->getMockTypeConverter('Class3Converter', FALSE)),
207 $interfaceName1 => array(0 => $this->getMockTypeConverter('Interface1Converter')),
208 $interfaceName2 => array(0 => $this->getMockTypeConverter('Interface2Converter')),
209 $interfaceName3 => array(0 => $this->getMockTypeConverter('Interface3Converter'))
210 )
211 );
212 // In case there is no most-specific-converter, we climb ub the type hierarchy
213 $data[] = array(
214 'target' => $className3,
215 'expectedConverter' => 'Class2Converter-HighPriority',
216 'typeConverters' => array(
217 $className2 => array(0 => $this->getMockTypeConverter('Class2Converter'), 10 => $this->getMockTypeConverter('Class2Converter-HighPriority'))
218 )
219 );
220 // If no parent class converter wants to handle it, we ask for all interface converters.
221 $data[] = array(
222 'target' => $className3,
223 'expectedConverter' => 'Interface1Converter',
224 'typeConverters' => array(
225 $className2 => array(0 => $this->getMockTypeConverter('Class2Converter', FALSE), 10 => $this->getMockTypeConverter('Class2Converter-HighPriority', FALSE)),
226 $interfaceName1 => array(4 => $this->getMockTypeConverter('Interface1Converter')),
227 $interfaceName2 => array(1 => $this->getMockTypeConverter('Interface2Converter')),
228 $interfaceName3 => array(2 => $this->getMockTypeConverter('Interface3Converter'))
229 )
230 );
231 // If two interface converters have the same priority, an exception is thrown.
232 $data[] = array(
233 'target' => $className3,
234 'expectedConverter' => 'Interface1Converter',
235 'typeConverters' => array(
236 $className2 => array(0 => $this->getMockTypeConverter('Class2Converter', FALSE), 10 => $this->getMockTypeConverter('Class2Converter-HighPriority', FALSE)),
237 $interfaceName1 => array(4 => $this->getMockTypeConverter('Interface1Converter')),
238 $interfaceName2 => array(2 => $this->getMockTypeConverter('Interface2Converter')),
239 $interfaceName3 => array(2 => $this->getMockTypeConverter('Interface3Converter'))
240 ),
241 'shouldFailWithException' => 'TYPO3\\CMS\\Extbase\\Property\\Exception\\DuplicateTypeConverterException'
242 );
243 // If no interface converter wants to handle it, a converter for "object" is looked up.
244 $data[] = array(
245 'target' => $className3,
246 'expectedConverter' => 'GenericObjectConverter-HighPriority',
247 'typeConverters' => array(
248 $className2 => array(0 => $this->getMockTypeConverter('Class2Converter', FALSE), 10 => $this->getMockTypeConverter('Class2Converter-HighPriority', FALSE)),
249 $interfaceName1 => array(4 => $this->getMockTypeConverter('Interface1Converter', FALSE)),
250 $interfaceName2 => array(3 => $this->getMockTypeConverter('Interface2Converter', FALSE)),
251 $interfaceName3 => array(2 => $this->getMockTypeConverter('Interface3Converter', FALSE)),
252 'object' => array(1 => $this->getMockTypeConverter('GenericObjectConverter'), 10 => $this->getMockTypeConverter('GenericObjectConverter-HighPriority'))
253 )
254 );
255 // If the target is no valid class name and no simple type, an exception is thrown
256 $data[] = array(
257 'target' => 'SomeNotExistingClassName',
258 'expectedConverter' => 'GenericObjectConverter-HighPriority',
259 'typeConverters' => array(),
260 'shouldFailWithException' => 'TYPO3\\CMS\\Extbase\\Property\\Exception\\InvalidTargetException'
261 );
262 // if the type converter is not found, we expect an exception
263 $data[] = array(
264 'target' => $className3,
265 'expectedConverter' => 'Class3Converter',
266 'typeConverters' => array(),
267 'shouldFailWithException' => 'TYPO3\\CMS\\Extbase\\Property\\Exception\\TypeConverterException'
268 );
269 // If The target type is no string, we expect an exception.
270 $data[] = array(
271 'target' => new \stdClass(),
272 'expectedConverter' => '',
273 'typeConverters' => array(),
274 'shouldFailWithException' => 'TYPO3\\CMS\\Extbase\\Property\\Exception\\InvalidTargetException'
275 );
276 return $data;
277 }
278
279 /**
280 * @test
281 * @dataProvider dataProviderForObjectTypeConverters
282 * @author Sebastian Kurfürst <sebastian@typo3.org>
283 * @param mixed $targetClass
284 * @param mixed $expectedTypeConverter
285 * @param mixed $typeConverters
286 * @param boolean $shouldFailWithException
287 * @throws \Exception
288 * @return void
289 */
290 public function findTypeConverterShouldReturnConverterForTargetObjectIfItExists($targetClass, $expectedTypeConverter, $typeConverters, $shouldFailWithException = FALSE) {
291 $mockTypeHandlingService = $this->getMock('TYPO3\\CMS\\Extbase\\Service\\TypeHandlingService');
292 $mockTypeHandlingService->expects($this->any())->method('isSimpleType')->will($this->returnValue(FALSE));
293 /** @var \TYPO3\CMS\Extbase\Property\PropertyMapper|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface */
294 $propertyMapper = $this->getAccessibleMock('TYPO3\\CMS\\Extbase\\Property\\PropertyMapper', array('dummy'));
295 $propertyMapper->injectTypeHandlingService($mockTypeHandlingService);
296 $propertyMapper->_set('typeConverters', array('string' => $typeConverters));
297 try {
298 $actualTypeConverter = $propertyMapper->_call('findTypeConverter', 'someSourceString', $targetClass, $this->mockConfiguration);
299 if ($shouldFailWithException) {
300 $this->fail('Expected exception ' . $shouldFailWithException . ' which was not thrown.');
301 }
302 $this->assertSame($expectedTypeConverter, $actualTypeConverter->_name);
303 } catch (\Exception $e) {
304 if ($shouldFailWithException === FALSE) {
305 throw $e;
306 }
307 $this->assertInstanceOf($shouldFailWithException, $e);
308 }
309 }
310
311 /**
312 * @test
313 * @author Sebastian Kurfürst <sebastian@typo3.org>
314 */
315 public function convertShouldAskConfigurationBuilderForDefaultConfiguration() {
316 $mockTypeHandlingService = $this->getMock('TYPO3\\CMS\\Extbase\\Service\\TypeHandlingService');
317 $mockTypeHandlingService->expects($this->any())->method('isSimpleType')->will($this->returnValue(TRUE));
318 /** @var \TYPO3\CMS\Extbase\Property\PropertyMapper|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface */
319 $propertyMapper = $this->getAccessibleMock('TYPO3\\CMS\\Extbase\\Property\\PropertyMapper', array('dummy'));
320 $propertyMapper->injectTypeHandlingService($mockTypeHandlingService);
321 $propertyMapper->injectPropertyMappingConfigurationBuilder($this->mockConfigurationBuilder);
322 $this->mockConfigurationBuilder->expects($this->once())->method('build')->will($this->returnValue($this->mockConfiguration));
323 $converter = $this->getMockTypeConverter('string2string');
324 $typeConverters = array(
325 'string' => array(
326 'string' => array(10 => $converter)
327 )
328 );
329 $propertyMapper->_set('typeConverters', $typeConverters);
330 $this->assertEquals('string2string', $propertyMapper->convert('source', 'string'));
331 }
332
333 /**
334 * @test
335 * @author Sebastian Kurfürst <sebastian@typo3.org>
336 */
337 public function findFirstEligibleTypeConverterInObjectHierarchyShouldReturnNullIfSourceTypeIsUnknown() {
338 /** @var \TYPO3\CMS\Extbase\Property\PropertyMapper|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface */
339 $propertyMapper = $this->getAccessibleMock('TYPO3\\CMS\\Extbase\\Property\\PropertyMapper', array('dummy'));
340 $this->assertNull($propertyMapper->_call('findFirstEligibleTypeConverterInObjectHierarchy', 'source', 'unknownSourceType', 'TYPO3\\CMS\\Extbase\\Core\\Bootstrap'));
341 }
342
343 /**
344 * @test
345 */
346 public function convertReturnsSourceUnchangedIfAlreadyConverted() {
347 $source = new \ArrayObject();
348 $targetType = 'ArrayObject';
349 $propertyPath = '';
350 $propertyMapper = new \TYPO3\CMS\Extbase\Property\PropertyMapper();
351 $this->assertSame($source, $propertyMapper->convert($source, $targetType, $this->mockConfiguration, $propertyPath));
352 }
353
354 /**
355 * @test
356 */
357 public function convertReturnsSourceUnchangedIfAlreadyConvertedToCompositeType() {
358 $source = new \ArrayObject();
359 $targetType = 'ArrayObject<SomeEntity>';
360 $propertyPath = '';
361 $propertyMapper = new \TYPO3\CMS\Extbase\Property\PropertyMapper();
362 $this->assertSame($source, $propertyMapper->convert($source, $targetType, $this->mockConfiguration, $propertyPath));
363 }
364
365 }
366
367 ?>