1fbecbe3212a999abdc923f3ac0ca05f7bf0e94a
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / BaseTestCase.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 /**
18 * The mother of all test cases.
19 *
20 * Don't sub class this test case but rather choose a more specialized base test case,
21 * such as UnitTestCase or FunctionalTestCase
22 *
23 */
24 abstract class BaseTestCase extends \PHPUnit_Framework_TestCase {
25
26 /**
27 * Whether global variables should be backed up
28 *
29 * @var bool
30 */
31 protected $backupGlobals = TRUE;
32
33 /**
34 * Whether static attributes should be backed up
35 *
36 * @var bool
37 */
38 protected $backupStaticAttributes = FALSE;
39
40 /**
41 * Creates a mock object which allows for calling protected methods and access of protected properties.
42 *
43 * @param string $originalClassName name of class to create the mock object of, must not be empty
44 * @param array<string> $methods name of the methods to mock
45 * @param array $arguments arguments to pass to constructor
46 * @param string $mockClassName the class name to use for the mock class
47 * @param bool $callOriginalConstructor whether to call the constructor
48 * @param bool $callOriginalClone whether to call the __clone method
49 * @param bool $callAutoload whether to call any autoload function
50 *
51 * @return \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface
52 * a mock of $originalClassName with access methods added
53 *
54 * @throws \InvalidArgumentException
55 */
56 protected function getAccessibleMock(
57 $originalClassName, array $methods = array(), array $arguments = array(), $mockClassName = '',
58 $callOriginalConstructor = TRUE, $callOriginalClone = TRUE, $callAutoload = TRUE
59 ) {
60 if ($originalClassName === '') {
61 throw new \InvalidArgumentException('$originalClassName must not be empty.', 1334701880);
62 }
63
64 return $this->getMock(
65 $this->buildAccessibleProxy($originalClassName),
66 $methods,
67 $arguments,
68 $mockClassName,
69 $callOriginalConstructor,
70 $callOriginalClone,
71 $callAutoload
72 );
73 }
74
75 /**
76 * Returns a mock object which allows for calling protected methods and access
77 * of protected properties.
78 *
79 * @param string $originalClassName Full qualified name of the original class
80 * @param array $arguments
81 * @param string $mockClassName
82 * @param bool $callOriginalConstructor
83 * @param bool $callOriginalClone
84 * @param bool $callAutoload
85 *
86 * @throws \InvalidArgumentException
87 *
88 * @return \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface
89 *
90 */
91 protected function getAccessibleMockForAbstractClass(
92 $originalClassName, array $arguments = array(), $mockClassName = '',
93 $callOriginalConstructor = TRUE, $callOriginalClone = TRUE, $callAutoload = TRUE
94 ) {
95 if ($originalClassName === '') {
96 throw new \InvalidArgumentException('$originalClassName must not be empty.', 1384268260);
97 }
98
99 return $this->getMockForAbstractClass(
100 $this->buildAccessibleProxy($originalClassName),
101 $arguments,
102 $mockClassName,
103 $callOriginalConstructor,
104 $callOriginalClone,
105 $callAutoload
106 );
107 }
108
109 /**
110 * Creates a proxy class of the specified class which allows
111 * for calling even protected methods and access of protected properties.
112 *
113 * @param string $className Name of class to make available, must not be empty
114 * @return string Fully qualified name of the built class, will not be empty
115 */
116 protected function buildAccessibleProxy($className) {
117 $accessibleClassName = str_replace('.', '', uniqid('Tx_Phpunit_AccessibleProxy', TRUE));
118 $class = new \ReflectionClass($className);
119 $abstractModifier = $class->isAbstract() ? 'abstract ' : '';
120
121 eval(
122 $abstractModifier . 'class ' . $accessibleClassName .
123 ' extends ' . $className . ' implements ' . \TYPO3\CMS\Core\Tests\AccessibleObjectInterface::class . ' {' .
124 'public function _call($methodName) {' .
125 'if ($methodName === \'\') {' .
126 'throw new \InvalidArgumentException(\'$methodName must not be empty.\', 1334663993);' .
127 '}' .
128 '$args = func_get_args();' .
129 'return call_user_func_array(array($this, $methodName), array_slice($args, 1));' .
130 '}' .
131 'public function _callRef(' .
132 '$methodName, &$arg1 = NULL, &$arg2 = NULL, &$arg3 = NULL, &$arg4 = NULL, &$arg5= NULL, &$arg6 = NULL, ' .
133 '&$arg7 = NULL, &$arg8 = NULL, &$arg9 = NULL' .
134 ') {' .
135 'if ($methodName === \'\') {' .
136 'throw new \InvalidArgumentException(\'$methodName must not be empty.\', 1334664210);' .
137 '}' .
138 'switch (func_num_args()) {' .
139 'case 0:' .
140 'throw new RuntimeException(\'The case of 0 arguments is not supposed to happen.\', 1334703124);' .
141 'break;' .
142 'case 1:' .
143 '$returnValue = $this->$methodName();' .
144 'break;' .
145 'case 2:' .
146 '$returnValue = $this->$methodName($arg1);' .
147 'break;' .
148 'case 3:' .
149 '$returnValue = $this->$methodName($arg1, $arg2);' .
150 'break;' .
151 'case 4:' .
152 '$returnValue = $this->$methodName($arg1, $arg2, $arg3);' .
153 'break;' .
154 'case 5:' .
155 '$returnValue = $this->$methodName($arg1, $arg2, $arg3, $arg4);' .
156 'break;' .
157 'case 6:' .
158 '$returnValue = $this->$methodName($arg1, $arg2, $arg3, $arg4, $arg5);' .
159 'break;' .
160 'case 7:' .
161 '$returnValue = $this->$methodName($arg1, $arg2, $arg3, $arg4, $arg5, $arg6);' .
162 'break;' .
163 'case 8:' .
164 '$returnValue = $this->$methodName($arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7);' .
165 'break;' .
166 'case 9:' .
167 '$returnValue = $this->$methodName($arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7, $arg8);' .
168 'break;' .
169 'case 10:' .
170 '$returnValue = $this->$methodName(' .
171 '$arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7, $arg8, $arg9' .
172 ');' .
173 'break;' .
174 'default:' .
175 'throw new \InvalidArgumentException(' .
176 '\'_callRef currently only allows calls to methods with no more than 9 parameters.\'' .
177 ');' .
178 '}' .
179 'return $returnValue;' .
180 '}' .
181 'public function _set($propertyName, $value) {' .
182 'if ($propertyName === \'\') {' .
183 'throw new \InvalidArgumentException(\'$propertyName must not be empty.\', 1334664355);' .
184 '}' .
185 '$this->$propertyName = $value;' .
186 '}' .
187 'public function _setRef($propertyName, &$value) {' .
188 'if ($propertyName === \'\') {' .
189 'throw new \InvalidArgumentException(\'$propertyName must not be empty.\', 1334664545);' .
190 '}' .
191 '$this->$propertyName = $value;' .
192 '}' .
193 'public function _setStatic($propertyName, $value) {' .
194 'if ($propertyName === \'\') {' .
195 'throw new \InvalidArgumentException(\'$propertyName must not be empty.\', 1344242602);' .
196 '}' .
197 'self::$$propertyName = $value;' .
198 '}' .
199 'public function _get($propertyName) {' .
200 'if ($propertyName === \'\') {' .
201 'throw new \InvalidArgumentException(\'$propertyName must not be empty.\', 1334664967);' .
202 '}' .
203 'return $this->$propertyName;' .
204 '}' .
205 'public function _getStatic($propertyName) {' .
206 'if ($propertyName === \'\') {' .
207 'throw new \InvalidArgumentException(\'$propertyName must not be empty.\', 1344242603);' .
208 '}' .
209 'return self::$$propertyName;' .
210 '}' .
211 '}'
212 );
213
214 return $accessibleClassName;
215 }
216
217 /**
218 * Helper function to call protected or private methods
219 *
220 * @param object $object The object to be invoked
221 * @param string $name the name of the method to call
222 * @return mixed
223 */
224 protected function callInaccessibleMethod($object, $name) {
225 // Remove first two arguments ($object and $name)
226 $arguments = func_get_args();
227 array_splice($arguments, 0, 2);
228
229 $reflectionObject = new \ReflectionObject($object);
230 $reflectionMethod = $reflectionObject->getMethod($name);
231 $reflectionMethod->setAccessible(TRUE);
232 return $reflectionMethod->invokeArgs($object, $arguments);
233 }
234
235 /**
236 * Injects $dependency into property $name of $target
237 *
238 * This is a convenience method for setting a protected or private property in
239 * a test subject for the purpose of injecting a dependency.
240 *
241 * @param object $target The instance which needs the dependency
242 * @param string $name Name of the property to be injected
243 * @param object $dependency The dependency to inject – usually an object but can also be any other type
244 * @return void
245 * @throws \RuntimeException
246 * @throws \InvalidArgumentException
247 */
248 protected function inject($target, $name, $dependency) {
249 if (!is_object($target)) {
250 throw new \InvalidArgumentException('Wrong type for argument $target, must be object.');
251 }
252
253 $objectReflection = new \ReflectionObject($target);
254 $methodNamePart = strtoupper($name[0]) . substr($name, 1);
255 if ($objectReflection->hasMethod('set' . $methodNamePart)) {
256 $methodName = 'set' . $methodNamePart;
257 $target->$methodName($dependency);
258 } elseif ($objectReflection->hasMethod('inject' . $methodNamePart)) {
259 $methodName = 'inject' . $methodNamePart;
260 $target->$methodName($dependency);
261 } elseif ($objectReflection->hasProperty($name)) {
262 $property = $objectReflection->getProperty($name);
263 $property->setAccessible(TRUE);
264 $property->setValue($target, $dependency);
265 } else {
266 throw new \RuntimeException('Could not inject ' . $name . ' into object of type ' . get_class($target));
267 }
268 }
269
270 }