PropertyAccessorCollectionTest.php 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\PropertyAccess\Tests;
  11. use Symfony\Component\PropertyAccess\Exception\ExceptionInterface;
  12. use Symfony\Component\PropertyAccess\PropertyAccessor;
  13. use Symfony\Component\PropertyAccess\StringUtil;
  14. class PropertyAccessorCollectionTest_Car
  15. {
  16. private $axes;
  17. public function __construct($axes = null)
  18. {
  19. $this->axes = $axes;
  20. }
  21. // In the test, use a name that StringUtil can't uniquely singularify
  22. public function addAxis($axis)
  23. {
  24. $this->axes[] = $axis;
  25. }
  26. public function removeAxis($axis)
  27. {
  28. foreach ($this->axes as $key => $value) {
  29. if ($value === $axis) {
  30. unset($this->axes[$key]);
  31. return;
  32. }
  33. }
  34. }
  35. public function getAxes()
  36. {
  37. return $this->axes;
  38. }
  39. }
  40. class PropertyAccessorCollectionTest_CarCustomSingular
  41. {
  42. public function addFoo($axis) {}
  43. public function removeFoo($axis) {}
  44. public function getAxes() {}
  45. }
  46. class PropertyAccessorCollectionTest_Engine
  47. {
  48. }
  49. class PropertyAccessorCollectionTest_CarOnlyAdder
  50. {
  51. public function addAxis($axis) {}
  52. public function getAxes() {}
  53. }
  54. class PropertyAccessorCollectionTest_CarOnlyRemover
  55. {
  56. public function removeAxis($axis) {}
  57. public function getAxes() {}
  58. }
  59. class PropertyAccessorCollectionTest_CarNoAdderAndRemover
  60. {
  61. public function getAxes() {}
  62. }
  63. class PropertyAccessorCollectionTest_CarNoAdderAndRemoverWithProperty
  64. {
  65. protected $axes = array();
  66. public function getAxes() {}
  67. }
  68. class PropertyAccessorCollectionTest_CompositeCar
  69. {
  70. public function getStructure() {}
  71. public function setStructure($structure) {}
  72. }
  73. class PropertyAccessorCollectionTest_CarStructure
  74. {
  75. public function addAxis($axis) {}
  76. public function removeAxis($axis) {}
  77. public function getAxes() {}
  78. }
  79. abstract class PropertyAccessorCollectionTest extends \PHPUnit_Framework_TestCase
  80. {
  81. /**
  82. * @var PropertyAccessor
  83. */
  84. private $propertyAccessor;
  85. protected function setUp()
  86. {
  87. $this->propertyAccessor = new PropertyAccessor();
  88. }
  89. abstract protected function getCollection(array $array);
  90. public function testGetValueReadsArrayAccess()
  91. {
  92. $object = $this->getCollection(array('firstName' => 'Bernhard'));
  93. $this->assertEquals('Bernhard', $this->propertyAccessor->getValue($object, '[firstName]'));
  94. }
  95. public function testGetValueReadsNestedArrayAccess()
  96. {
  97. $object = $this->getCollection(array('person' => array('firstName' => 'Bernhard')));
  98. $this->assertEquals('Bernhard', $this->propertyAccessor->getValue($object, '[person][firstName]'));
  99. }
  100. /**
  101. * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
  102. */
  103. public function testGetValueThrowsExceptionIfArrayAccessExpected()
  104. {
  105. $this->propertyAccessor->getValue(new \stdClass(), '[firstName]');
  106. }
  107. public function testSetValueUpdatesArrayAccess()
  108. {
  109. $object = $this->getCollection(array());
  110. $this->propertyAccessor->setValue($object, '[firstName]', 'Bernhard');
  111. $this->assertEquals('Bernhard', $object['firstName']);
  112. }
  113. public function testSetValueUpdatesNestedArrayAccess()
  114. {
  115. $object = $this->getCollection(array());
  116. $this->propertyAccessor->setValue($object, '[person][firstName]', 'Bernhard');
  117. $this->assertEquals('Bernhard', $object['person']['firstName']);
  118. }
  119. /**
  120. * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
  121. */
  122. public function testSetValueThrowsExceptionIfArrayAccessExpected()
  123. {
  124. $this->propertyAccessor->setValue(new \stdClass(), '[firstName]', 'Bernhard');
  125. }
  126. public function testSetValueCallsAdderAndRemoverForCollections()
  127. {
  128. $axesBefore = $this->getCollection(array(1 => 'second', 3 => 'fourth', 4 => 'fifth'));
  129. $axesMerged = $this->getCollection(array(1 => 'first', 2 => 'second', 3 => 'third'));
  130. $axesAfter = $this->getCollection(array(1 => 'second', 5 => 'first', 6 => 'third'));
  131. $axesMergedCopy = is_object($axesMerged) ? clone $axesMerged : $axesMerged;
  132. // Don't use a mock in order to test whether the collections are
  133. // modified while iterating them
  134. $car = new PropertyAccessorCollectionTest_Car($axesBefore);
  135. $this->propertyAccessor->setValue($car, 'axes', $axesMerged);
  136. $this->assertEquals($axesAfter, $car->getAxes());
  137. // The passed collection was not modified
  138. $this->assertEquals($axesMergedCopy, $axesMerged);
  139. }
  140. public function testSetValueCallsAdderAndRemoverForNestedCollections()
  141. {
  142. $car = $this->getMock(__CLASS__.'_CompositeCar');
  143. $structure = $this->getMock(__CLASS__.'_CarStructure');
  144. $axesBefore = $this->getCollection(array(1 => 'second', 3 => 'fourth'));
  145. $axesAfter = $this->getCollection(array(0 => 'first', 1 => 'second', 2 => 'third'));
  146. $car->expects($this->any())
  147. ->method('getStructure')
  148. ->will($this->returnValue($structure));
  149. $structure->expects($this->at(0))
  150. ->method('getAxes')
  151. ->will($this->returnValue($axesBefore));
  152. $structure->expects($this->at(1))
  153. ->method('removeAxis')
  154. ->with('fourth');
  155. $structure->expects($this->at(2))
  156. ->method('addAxis')
  157. ->with('first');
  158. $structure->expects($this->at(3))
  159. ->method('addAxis')
  160. ->with('third');
  161. $this->propertyAccessor->setValue($car, 'structure.axes', $axesAfter);
  162. }
  163. public function testSetValueCallsCustomAdderAndRemover()
  164. {
  165. $this->markTestSkipped('This feature is temporarily disabled as of 2.1');
  166. $car = $this->getMock(__CLASS__.'_CarCustomSingular');
  167. $axesBefore = $this->getCollection(array(1 => 'second', 3 => 'fourth'));
  168. $axesAfter = $this->getCollection(array(0 => 'first', 1 => 'second', 2 => 'third'));
  169. $car->expects($this->at(0))
  170. ->method('getAxes')
  171. ->will($this->returnValue($axesBefore));
  172. $car->expects($this->at(1))
  173. ->method('removeFoo')
  174. ->with('fourth');
  175. $car->expects($this->at(2))
  176. ->method('addFoo')
  177. ->with('first');
  178. $car->expects($this->at(3))
  179. ->method('addFoo')
  180. ->with('third');
  181. $this->propertyAccessor->setValue($car, 'axes|foo', $axesAfter);
  182. }
  183. /**
  184. * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
  185. */
  186. public function testSetValueFailsIfOnlyAdderFound()
  187. {
  188. $car = $this->getMock(__CLASS__.'_CarOnlyAdder');
  189. $axesBefore = $this->getCollection(array(1 => 'second', 3 => 'fourth'));
  190. $axesAfter = $this->getCollection(array(0 => 'first', 1 => 'second', 2 => 'third'));
  191. $car->expects($this->any())
  192. ->method('getAxes')
  193. ->will($this->returnValue($axesBefore));
  194. $this->propertyAccessor->setValue($car, 'axes', $axesAfter);
  195. }
  196. /**
  197. * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
  198. */
  199. public function testSetValueFailsIfOnlyRemoverFound()
  200. {
  201. $car = $this->getMock(__CLASS__.'_CarOnlyRemover');
  202. $axesBefore = $this->getCollection(array(1 => 'second', 3 => 'fourth'));
  203. $axesAfter = $this->getCollection(array(0 => 'first', 1 => 'second', 2 => 'third'));
  204. $car->expects($this->any())
  205. ->method('getAxes')
  206. ->will($this->returnValue($axesBefore));
  207. $this->propertyAccessor->setValue($car, 'axes', $axesAfter);
  208. }
  209. /**
  210. * @dataProvider noAdderRemoverData
  211. */
  212. public function testNoAdderAndRemoverThrowsSensibleError($car, $path, $message)
  213. {
  214. $axes = $this->getCollection(array(0 => 'first', 1 => 'second', 2 => 'third'));
  215. try {
  216. $this->propertyAccessor->setValue($car, $path, $axes);
  217. $this->fail('An expected exception was not thrown!');
  218. } catch (ExceptionInterface $e) {
  219. $this->assertEquals($message, $e->getMessage());
  220. }
  221. }
  222. public function noAdderRemoverData()
  223. {
  224. $data = array();
  225. $car = $this->getMock(__CLASS__.'_CarNoAdderAndRemover');
  226. $propertyPath = 'axes';
  227. $expectedMessage = sprintf(
  228. 'Neither the property "axes" nor one of the methods "addAx()", '.
  229. '"addAxe()", "addAxis()", "setAxes()", "__set()" or "__call()" exist and have '.
  230. 'public access in class "%s".',
  231. get_class($car)
  232. );
  233. $data[] = array($car, $propertyPath, $expectedMessage);
  234. /*
  235. Temporarily disabled in 2.1
  236. $propertyPath = new PropertyPath('axes|boo');
  237. $expectedMessage = sprintf(
  238. 'Neither element "axes" nor method "setAxes()" exists in class '
  239. .'"%s", nor could adders and removers be found based on the '
  240. .'passed singular: %s',
  241. get_class($car),
  242. 'boo'
  243. );
  244. $data[] = array($car, $propertyPath, $expectedMessage);
  245. */
  246. $car = $this->getMock(__CLASS__.'_CarNoAdderAndRemoverWithProperty');
  247. $propertyPath = 'axes';
  248. $expectedMessage = sprintf(
  249. 'Neither the property "axes" nor one of the methods "addAx()", '.
  250. '"addAxe()", "addAxis()", "setAxes()", "__set()" or "__call()" exist and have '.
  251. 'public access in class "%s".',
  252. get_class($car)
  253. );
  254. $data[] = array($car, $propertyPath, $expectedMessage);
  255. return $data;
  256. }
  257. }