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