]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - tests/PHPUnit/Framework/MockObject/Mock.php
Added unit tests.
[Github/sugarcrm.git] / tests / PHPUnit / Framework / MockObject / Mock.php
1 <?php
2 /**
3  * PHPUnit
4  *
5  * Copyright (c) 2002-2009, Sebastian Bergmann <sb@sebastian-bergmann.de>.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  *   * Redistributions of source code must retain the above copyright
13  *     notice, this list of conditions and the following disclaimer.
14  *
15  *   * Redistributions in binary form must reproduce the above copyright
16  *     notice, this list of conditions and the following disclaimer in
17  *     the documentation and/or other materials provided with the
18  *     distribution.
19  *
20  *   * Neither the name of Sebastian Bergmann nor the names of his
21  *     contributors may be used to endorse or promote products derived
22  *     from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35  * POSSIBILITY OF SUCH DAMAGE.
36  *
37  * @category   Testing
38  * @package    PHPUnit
39  * @author     Jan Borsodi <jb@ez.no>
40  * @author     Sebastian Bergmann <sb@sebastian-bergmann.de>
41  * @copyright  2002-2009 Sebastian Bergmann <sb@sebastian-bergmann.de>
42  * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
43
44  * @link       http://www.phpunit.de/
45  * @since      File available since Release 3.0.0
46  */
47
48 require_once 'PHPUnit/Util/Class.php';
49 require_once 'PHPUnit/Util/Filter.php';
50 require_once 'PHPUnit/Framework/MockObject/Matcher.php';
51 require_once 'PHPUnit/Framework/MockObject/Invocation.php';
52 require_once 'PHPUnit/Framework/MockObject/MockObject.php';
53
54 PHPUnit_Util_Filter::addFileToFilter(__FILE__, 'PHPUNIT');
55
56 /**
57  * Provides generation of mock classes and objects from existing classes.
58  *
59  * The mocked class will contain all the methods of the original class but with
60  * a different implementation which will call the current
61  * PHPUnit_Framework_MockObject_InvocationMocker object, this objects takes
62  * care of checking expectations and stubs.
63  * It is also possible to define which methods are mocked by passing an array
64  * of method names.
65  *
66  * The simplest way to define a mock object is to do:
67  *
68  * <code>
69  * PHPUnit_Framework_MockObject_Mock::generate('MyClass');
70  * $o = new Mock_MyClass;
71  * </code>
72  *
73  * The generate() method returns an object which can be queried.
74  *
75  * <code>
76  * $m = PHPUnit_Framework_MockObject::generate('MyClass');
77  * $o = new $m->mockClassName;
78  * print "original class was: . $m->className;
79  * </code>
80  *
81  * @category   Testing
82  * @package    PHPUnit
83  * @author     Jan Borsodi <jb@ez.no>
84  * @author     Sebastian Bergmann <sb@sebastian-bergmann.de>
85  * @copyright  2002-2009 Sebastian Bergmann <sb@sebastian-bergmann.de>
86  * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
87  * @version    Release: 3.3.17
88  * @link       http://www.phpunit.de/
89  * @since      Class available since Release 3.0.0
90  */
91 class PHPUnit_Framework_MockObject_Mock
92 {
93     public $mockClassName;
94     public $className;
95     public $fullClassName;
96     public $namespaceName;
97     public $methods;
98     protected $callOriginalConstructor;
99     protected $callOriginalClone;
100     protected $callAutoload;
101     protected static $cache = array();
102
103     public function __construct($className, $methods = array(), $mockClassName = '', $callOriginalConstructor = TRUE, $callOriginalClone = TRUE, $callAutoload = TRUE)
104     {
105         $classNameParts = explode('\\', $className);
106
107         if (count($classNameParts) > 1) {
108             $className           = array_pop($classNameParts);
109             $namespaceName       = join('\\', $classNameParts);
110             $this->fullClassName = $namespaceName . '\\' . $className;
111         } else {
112             $namespaceName       = '';
113             $this->fullClassName = $className;
114         }
115
116         if ($mockClassName === '') {
117             do {
118                 $mockClassName = 'Mock_' . $className . '_' . substr(md5(microtime()), 0, 8);
119             }
120             while (class_exists($mockClassName, FALSE));
121         }
122
123         else if (class_exists($mockClassName, FALSE)) {
124             throw new RuntimeException(
125               sprintf(
126                 'Class "%s" already exists.',
127                 $mockClassName
128               )
129             );
130         }
131
132         $isClass     = class_exists($className, $callAutoload);
133         $isInterface = interface_exists($className, $callAutoload);
134
135         if (is_array($methods) && empty($methods) && ($isClass || $isInterface)) {
136             $methods = get_class_methods($className);
137         }
138
139         if ($isInterface) {
140             $callOriginalConstructor = FALSE;
141         }
142
143         $this->mockClassName           = $mockClassName;
144         $this->className               = $className;
145         $this->namespaceName           = $namespaceName;
146         $this->methods                 = $methods;
147         $this->callOriginalConstructor = $callOriginalConstructor;
148         $this->callOriginalClone       = $callOriginalClone;
149         $this->callAutoload            = $callAutoload;
150     }
151
152     public static function generate($className, $methods = array(), $mockClassName = '', $callOriginalConstructor = TRUE, $callOriginalClone = TRUE, $callAutoload = TRUE)
153     {
154         if ($mockClassName == '') {
155             $key = md5(
156               $className .
157               serialize($methods) .
158               serialize($callOriginalConstructor) .
159               serialize($callOriginalClone)
160             );
161
162             if (!isset(self::$cache[$key])) {
163                 self::$cache[$key] = self::generateMock(
164                   $className,
165                   $methods,
166                   $mockClassName,
167                   $callOriginalConstructor,
168                   $callOriginalClone,
169                   $callAutoload
170                 );
171             }
172
173             return self::$cache[$key];
174         }
175
176         return self::generateMock(
177           $className,
178           $methods,
179           $mockClassName,
180           $callOriginalConstructor,
181           $callOriginalClone,
182           $callAutoload
183         );
184     }
185
186     protected static function generateMock($className, $methods, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload)
187     {
188         $mock = new PHPUnit_Framework_MockObject_Mock(
189           $className,
190           $methods,
191           $mockClassName,
192           $callOriginalConstructor,
193           $callOriginalClone,
194           $callAutoload
195         );
196
197         $mock->generateClass();
198
199         return $mock;
200     }
201
202     protected function generateClass()
203     {
204         if (!class_exists($this->fullClassName, $this->callAutoload) && !interface_exists($this->fullClassName, $this->callAutoload)) {
205             $code = 'class ' . $this->className . ' {}';
206
207             if (!empty($this->namespaceName)) {
208                 $code = 'namespace ' . $this->namespaceName . ';' . $code;
209             }
210
211             eval($code);
212         }
213
214         try {
215             $class = new ReflectionClass($this->fullClassName);
216
217             if ($class->isFinal()) {
218                 throw new RuntimeException(
219                   sprintf(
220                     'Class "%s" is declared "final" and cannot be mocked.',
221                     $this->fullClassName
222                   )
223                 );
224             }
225
226             $code = $this->generateClassDefinition($class);
227
228             eval($code);
229         }
230
231         catch (Exception $e) {
232             throw new RuntimeException(
233               sprintf(
234                 'Failed to generate mock class "%s" for class "%s".\n%s',
235                 $this->mockClassName,
236                 $this->fullClassName,
237                 $e->getMessage()
238               )
239             );
240         }
241     }
242
243     protected function generateClassDefinition(ReflectionClass $class)
244     {
245         $code = 'class ';
246
247         if ($class->isInterface()) {
248             $code .= sprintf(
249               "%s implements %s%s {\n",
250               $this->mockClassName,
251               !empty($this->namespaceName) ? $this->namespaceName . '\\' : '',
252               $this->className
253             );
254         } else {
255             $code .= sprintf(
256               "%s extends %s%s {\n",
257               $this->mockClassName,
258               !empty($this->namespaceName) ? $this->namespaceName . '\\' : '',
259               $this->className
260             );
261         }
262
263         $code .= $this->generateMockApi($class);
264
265         if (is_array($this->methods)) {
266             foreach ($this->methods as $methodName) {
267                 try {
268                     $method = $class->getMethod($methodName);
269
270                     if ($this->canMockMethod($method)) {
271                         $code .= $this->generateMethodDefinitionFromExisting($method);
272                     }
273                 }
274
275                 catch (ReflectionException $e) {
276                     $code .= $this->generateMethodDefinition($class->getName(), $methodName, 'public');
277                 }
278             }
279         }
280
281         $code .= "}\n";
282
283         return $code;
284     }
285
286     protected function canMockMethod(ReflectionMethod $method)
287     {
288         $className  = $method->getDeclaringClass()->getName();
289         $methodName = $method->getName();
290
291         if ($method->isFinal() || $method->isStatic() ||
292             $methodName == '__construct' || $methodName == $className ||
293             $methodName == '__destruct'  || $method->getName() == '__clone') {
294             return FALSE;
295         }
296
297         return TRUE;
298     }
299
300     protected function generateMethodDefinitionFromExisting(ReflectionMethod $method)
301     {
302         if ($method->isPrivate()) {
303             $modifier = 'private';
304         }
305
306         else if ($method->isProtected()) {
307             $modifier = 'protected';
308         }
309
310         else {
311             $modifier = 'public';
312         }
313
314         if ($method->returnsReference()) {
315             $reference = '&';
316         } else {
317             $reference = '';
318         }
319
320         return $this->generateMethodDefinition(
321           $method->getDeclaringClass()->getName(),
322           $method->getName(),
323           $modifier,
324           $reference,
325           PHPUnit_Util_Class::getMethodParameters($method)
326         );
327     }
328
329     protected function generateMethodDefinition($className, $methodName, $modifier, $reference = '', $parameters = '')
330     {
331         return sprintf(
332           "\n    %s function %s%s(%s) {\n" .
333           "        \$args   = func_get_args();\n" .
334           "        \$result = \$this->invocationMocker->invoke(\n" .
335           "          new PHPUnit_Framework_MockObject_Invocation(\$this, \"%s\", \"%s\", \$args)\n" .
336           "        );\n\n" .
337           "        return \$result;\n" .
338           "    }\n",
339
340           $modifier,
341           $reference,
342           $methodName,
343           $parameters,
344           $className,
345           $methodName
346         );
347     }
348
349     protected function generateMockApi(ReflectionClass $class)
350     {
351         if ($this->callOriginalConstructor) {
352             $constructorCode = $this->generateConstructorCodeWithParentCall($class);
353         } else {
354             $constructorCode = $this->generateConstructorCode($class);
355         }
356
357         if ($this->callOriginalClone && $class->hasMethod('__clone')) {
358             $cloneCode = $this->generateCloneCodeWithParentCall();
359         } else {
360             $cloneCode = $this->generateCloneCode();
361         }
362
363         return sprintf(
364           "    private \$invocationMocker;\n\n" .
365           "%s" .
366           "%s" .
367           "    public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation \$matcher) {\n" .
368           "        return \$this->invocationMocker->expects(\$matcher);\n" .
369           "    }\n\n" .
370           "    public function __phpunit_getInvocationMocker() {\n" .
371           "        return \$this->invocationMocker;\n" .
372           "    }\n\n" .
373           "    public function __phpunit_verify() {\n" .
374           "        \$this->invocationMocker->verify();\n" .
375           "    }\n",
376
377           $constructorCode,
378           $cloneCode
379         );
380     }
381
382     protected function generateConstructorCode(ReflectionClass $class)
383     {
384         $arguments   = '';
385         $constructor = $class->getConstructor();
386
387         if ($constructor !== NULL) {
388             $constructorName = $constructor->getName();
389
390             foreach (PHPUnit_Util_Class::getHierarchy($class->getName(), TRUE) as $_class) {
391                 foreach ($_class->getInterfaces() as $interface) {
392                     if ($interface->hasMethod($constructorName)) {
393                         $arguments = PHPUnit_Util_Class::getMethodParameters($constructor);
394                         break 2;
395                     }
396                 }
397             }
398         }
399
400         return sprintf(
401           "    public function __construct(%s) {\n" .
402           "        \$this->invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker(\$this);\n" .
403           "    }\n\n",
404
405           $arguments
406         );
407     }
408
409     protected function generateConstructorCodeWithParentCall(ReflectionClass $class)
410     {
411         $constructor = $class->getConstructor();
412
413         if ($constructor !== NULL) {
414             return sprintf(
415               "    public function __construct(%s) {\n" .
416               "        \$args = func_get_args();\n" .
417               "        \$this->invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker;\n" .
418               "        \$class = new ReflectionClass('%s');\n" .
419               "        \$class->getParentClass()->getConstructor()->invokeArgs(\$this, \$args);\n" .
420               "    }\n\n",
421
422               PHPUnit_Util_Class::getMethodParameters($constructor),
423               $this->mockClassName
424             );
425         } else {
426             return $this->generateConstructorCode($class);
427         }
428     }
429
430     protected function generateCloneCode()
431     {
432         return "    public function __clone() {\n" .
433                "        \$this->invocationMocker = clone \$this->invocationMocker;\n" .
434                "    }\n\n";
435     }
436
437     protected function generateCloneCodeWithParentCall()
438     {
439         return "    public function __clone() {\n" .
440                "        \$this->invocationMocker = clone \$this->invocationMocker;\n" .
441                "        parent::__clone();\n" .
442                "    }\n\n";
443     }
444 }
445 ?>