0337d704 |
1 | <?php |
2 | /** |
3 | * base include file for SimpleTest |
4 | * @package SimpleTest |
5 | * @subpackage MockObjects |
6 | * @version $Id: mock_objects.php,v 1.49 2004/08/18 19:10:54 lastcraft Exp $ |
7 | */ |
8 | |
9 | /**#@+ |
10 | * include SimpleTest files |
11 | */ |
12 | require_once(dirname(__FILE__) . '/expectation.php'); |
13 | require_once(dirname(__FILE__) . '/options.php'); |
14 | require_once(dirname(__FILE__) . '/dumper.php'); |
15 | /**#@-*/ |
16 | |
17 | /** |
18 | * Default character simpletest will substitute for any value |
19 | */ |
20 | define('MOCK_WILDCARD', '*'); |
21 | |
22 | /** |
23 | * A wildcard expectation always matches. |
24 | * @package SimpleTest |
25 | * @subpackage MockObjects |
26 | */ |
27 | class WildcardExpectation extends SimpleExpectation { |
28 | |
29 | /** |
30 | * Chains constructor only. |
31 | * @access public |
32 | */ |
33 | function WildcardExpectation() { |
34 | $this->SimpleExpectation(); |
35 | } |
36 | |
37 | /** |
38 | * Tests the expectation. Always true. |
39 | * @param mixed $compare Ignored. |
40 | * @return boolean True. |
41 | * @access public |
42 | */ |
43 | function test($compare) { |
44 | return true; |
45 | } |
46 | |
47 | /** |
48 | * Returns a human readable test message. |
49 | * @param mixed $compare Comparison value. |
50 | * @return string Description of success |
51 | * or failure. |
52 | * @access public |
53 | */ |
54 | function testMessage($compare) { |
55 | $dumper = &$this->_getDumper(); |
56 | return 'Wildcard always matches [' . $dumper->describeValue($compare) . ']'; |
57 | } |
58 | } |
59 | |
60 | /** |
61 | * Parameter comparison assertion. |
62 | * @package SimpleTest |
63 | * @subpackage MockObjects |
64 | */ |
65 | class ParametersExpectation extends SimpleExpectation { |
66 | var $_expected; |
67 | |
68 | /** |
69 | * Sets the expected parameter list. |
70 | * @param array $parameters Array of parameters including |
71 | * those that are wildcarded. |
72 | * If the value is not an array |
73 | * then it is considered to match any. |
74 | * @param mixed $wildcard Any parameter matching this |
75 | * will always match. |
76 | * @param string $message Customised message on failure. |
77 | * @access public |
78 | */ |
79 | function ParametersExpectation($expected = false, $message = '%s') { |
80 | $this->SimpleExpectation($message); |
81 | $this->_expected = $expected; |
82 | } |
83 | |
84 | /** |
85 | * Tests the assertion. True if correct. |
86 | * @param array $parameters Comparison values. |
87 | * @return boolean True if correct. |
88 | * @access public |
89 | */ |
90 | function test($parameters) { |
91 | if (! is_array($this->_expected)) { |
92 | return true; |
93 | } |
94 | if (count($this->_expected) != count($parameters)) { |
95 | return false; |
96 | } |
97 | for ($i = 0; $i < count($this->_expected); $i++) { |
98 | if (! $this->_testParameter($parameters[$i], $this->_expected[$i])) { |
99 | return false; |
100 | } |
101 | } |
102 | return true; |
103 | } |
104 | |
105 | /** |
106 | * Tests an individual parameter. |
107 | * @param mixed $parameter Value to test. |
108 | * @param mixed $expected Comparison value. |
109 | * @return boolean True if expectation |
110 | * fulfilled. |
111 | * @access private |
112 | */ |
113 | function _testParameter($parameter, $expected) { |
114 | $comparison = $this->_coerceToExpectation($expected); |
115 | return $comparison->test($parameter); |
116 | } |
117 | |
118 | /** |
119 | * Returns a human readable test message. |
120 | * @param array $comparison Incoming parameter list. |
121 | * @return string Description of success |
122 | * or failure. |
123 | * @access public |
124 | */ |
125 | function testMessage($parameters) { |
126 | if ($this->test($parameters)) { |
127 | return "Expectation of " . count($this->_expected) . |
128 | " arguments of [" . $this->_renderArguments($this->_expected) . |
129 | "] is correct"; |
130 | } else { |
131 | return $this->_describeDifference($this->_expected, $parameters); |
132 | } |
133 | } |
134 | |
135 | /** |
136 | * Message to display if expectation differs from |
137 | * the parameters actually received. |
138 | * @param array $expected Expected parameters as list. |
139 | * @param array $parameters Actual parameters received. |
140 | * @return string Description of difference. |
141 | * @access private |
142 | */ |
143 | function _describeDifference($expected, $parameters) { |
144 | if (count($expected) != count($parameters)) { |
145 | return "Expected " . count($expected) . |
146 | " arguments of [" . $this->_renderArguments($expected) . |
147 | "] but got " . count($parameters) . |
148 | " arguments of [" . $this->_renderArguments($parameters) . "]"; |
149 | } |
150 | $messages = array(); |
151 | for ($i = 0; $i < count($expected); $i++) { |
152 | $comparison = $this->_coerceToExpectation($expected[$i]); |
153 | if (! $comparison->test($parameters[$i])) { |
154 | $messages[] = "parameter " . ($i + 1) . " with [" . |
155 | $comparison->overlayMessage($parameters[$i]) . "]"; |
156 | } |
157 | } |
158 | return "Parameter expectation differs at " . implode(" and ", $messages); |
159 | } |
160 | |
161 | /** |
162 | * Creates an identical expectation if the |
163 | * object/value is not already some type |
164 | * of expectation. |
165 | * @param mixed $expected Expected value. |
166 | * @return SimpleExpectation Expectation object. |
167 | * @access private |
168 | */ |
169 | function _coerceToExpectation($expected) { |
170 | if (SimpleTestCompatibility::isA($expected, 'SimpleExpectation')) { |
171 | return $expected; |
172 | } |
173 | return new IdenticalExpectation($expected); |
174 | } |
175 | |
176 | /** |
177 | * Renders the argument list as a string for |
178 | * messages. |
179 | * @param array $args Incoming arguments. |
180 | * @return string Simple description of type and value. |
181 | * @access private |
182 | */ |
183 | function _renderArguments($args) { |
184 | $descriptions = array(); |
185 | if (is_array($args)) { |
186 | foreach ($args as $arg) { |
187 | $dumper = &new SimpleDumper(); |
188 | $descriptions[] = $dumper->describeValue($arg); |
189 | } |
190 | } |
191 | return implode(', ', $descriptions); |
192 | } |
193 | } |
194 | |
195 | /** |
196 | * Confirms that the number of calls on a method is as expected. |
197 | */ |
198 | class CallCountExpectation extends SimpleExpectation { |
199 | var $_method; |
200 | var $_count; |
201 | |
202 | /** |
203 | * Stashes the method and expected count for later |
204 | * reporting. |
205 | * @param string $method Name of method to confirm against. |
206 | * @param integer $count Expected number of calls. |
207 | * @param string $message Custom error message. |
208 | */ |
209 | function CallCountExpectation($method, $count, $message = '%s') { |
210 | $this->_method = $method; |
211 | $this->_count = $count; |
212 | $this->SimpleExpectation($message); |
213 | } |
214 | |
215 | /** |
216 | * Tests the assertion. True if correct. |
217 | * @param integer $compare Measured call count. |
218 | * @return boolean True if expected. |
219 | * @access public |
220 | */ |
221 | function test($compare) { |
222 | return ($this->_count == $compare); |
223 | } |
224 | |
225 | /** |
226 | * Reports the comparison. |
227 | * @param integer $compare Measured call count. |
228 | * @return string Message to show. |
229 | * @access public |
230 | */ |
231 | function testMessage($compare) { |
232 | return 'Expected call count for [' . $this->_method . |
233 | '] was [' . $this->_count . |
234 | '] got [' . $compare . ']'; |
235 | } |
236 | } |
237 | |
238 | /** |
239 | * Confirms that the number of calls on a method is as expected. |
240 | */ |
241 | class MinimumCallCountExpectation extends SimpleExpectation { |
242 | var $_method; |
243 | var $_count; |
244 | |
245 | /** |
246 | * Stashes the method and expected count for later |
247 | * reporting. |
248 | * @param string $method Name of method to confirm against. |
249 | * @param integer $count Minimum number of calls. |
250 | * @param string $message Custom error message. |
251 | */ |
252 | function MinimumCallCountExpectation($method, $count, $message = '%s') { |
253 | $this->_method = $method; |
254 | $this->_count = $count; |
255 | $this->SimpleExpectation($message); |
256 | } |
257 | |
258 | /** |
259 | * Tests the assertion. True if correct. |
260 | * @param integer $compare Measured call count. |
261 | * @return boolean True if enough. |
262 | * @access public |
263 | */ |
264 | function test($compare) { |
265 | return ($this->_count <= $compare); |
266 | } |
267 | |
268 | /** |
269 | * Reports the comparison. |
270 | * @param integer $compare Measured call count. |
271 | * @return string Message to show. |
272 | * @access public |
273 | */ |
274 | function testMessage($compare) { |
275 | return 'Minimum call count for [' . $this->_method . |
276 | '] was [' . $this->_count . |
277 | '] got [' . $compare . ']'; |
278 | } |
279 | } |
280 | |
281 | /** |
282 | * Confirms that the number of calls on a method is as expected. |
283 | */ |
284 | class MaximumCallCountExpectation extends SimpleExpectation { |
285 | var $_method; |
286 | var $_count; |
287 | |
288 | /** |
289 | * Stashes the method and expected count for later |
290 | * reporting. |
291 | * @param string $method Name of method to confirm against. |
292 | * @param integer $count Minimum number of calls. |
293 | * @param string $message Custom error message. |
294 | */ |
295 | function MaximumCallCountExpectation($method, $count, $message = '%s') { |
296 | $this->_method = $method; |
297 | $this->_count = $count; |
298 | $this->SimpleExpectation($message); |
299 | } |
300 | |
301 | /** |
302 | * Tests the assertion. True if correct. |
303 | * @param integer $compare Measured call count. |
304 | * @return boolean True if not over. |
305 | * @access public |
306 | */ |
307 | function test($compare) { |
308 | return ($this->_count >= $compare); |
309 | } |
310 | |
311 | /** |
312 | * Reports the comparison. |
313 | * @param integer $compare Measured call count. |
314 | * @return string Message to show. |
315 | * @access public |
316 | */ |
317 | function testMessage($compare) { |
318 | return 'Miximum call count for [' . $this->_method . |
319 | '] was [' . $this->_count . |
320 | '] got [' . $compare . ']'; |
321 | } |
322 | } |
323 | |
324 | /** |
325 | * Retrieves values and references by searching the |
326 | * parameter lists until a match is found. |
327 | * @package SimpleTest |
328 | * @subpackage MockObjects |
329 | */ |
330 | class CallMap { |
331 | var $_map; |
332 | |
333 | /** |
334 | * Creates an empty call map. |
335 | * @access public |
336 | */ |
337 | function CallMap() { |
338 | $this->_map = array(); |
339 | } |
340 | |
341 | /** |
342 | * Stashes a value against a method call. |
343 | * @param array $parameters Arguments including wildcards. |
344 | * @param mixed $value Value copied into the map. |
345 | * @access public |
346 | */ |
347 | function addValue($parameters, $value) { |
348 | $this->addReference($parameters, $value); |
349 | } |
350 | |
351 | /** |
352 | * Stashes a reference against a method call. |
353 | * @param array $parameters Array of arguments (including wildcards). |
354 | * @param mixed $reference Array reference placed in the map. |
355 | * @access public |
356 | */ |
357 | function addReference($parameters, &$reference) { |
358 | $place = count($this->_map); |
359 | $this->_map[$place] = array(); |
360 | $this->_map[$place]["params"] = new ParametersExpectation($parameters); |
361 | $this->_map[$place]["content"] = &$reference; |
362 | } |
363 | |
364 | /** |
365 | * Searches the call list for a matching parameter |
366 | * set. Returned by reference. |
367 | * @param array $parameters Parameters to search by |
368 | * without wildcards. |
369 | * @return object Object held in the first matching |
370 | * slot, otherwise null. |
371 | * @access public |
372 | */ |
373 | function &findFirstMatch($parameters) { |
374 | $slot = $this->_findFirstSlot($parameters); |
375 | if (!isset($slot)) { |
376 | return null; |
377 | } |
378 | return $slot["content"]; |
379 | } |
380 | |
381 | /** |
382 | * Searches the call list for a matching parameter |
383 | * set. True if successful. |
384 | * @param array $parameters Parameters to search by |
385 | * without wildcards. |
386 | * @return boolean True if a match is present. |
387 | * @access public |
388 | */ |
389 | function isMatch($parameters) { |
390 | return ($this->_findFirstSlot($parameters) != null); |
391 | } |
392 | |
393 | /** |
394 | * Searches the map for a matching item. |
395 | * @param array $parameters Parameters to search by |
396 | * without wildcards. |
397 | * @return array Reference to slot or null. |
398 | * @access private |
399 | */ |
400 | function &_findFirstSlot($parameters) { |
401 | for ($i = 0; $i < count($this->_map); $i++) { |
402 | if ($this->_map[$i]["params"]->test($parameters)) { |
403 | return $this->_map[$i]; |
404 | } |
405 | } |
406 | return null; |
407 | } |
408 | } |
409 | |
410 | /** |
411 | * An empty collection of methods that can have their |
412 | * return values set. Used for prototyping. |
413 | * @package SimpleTest |
414 | * @subpackage MockObjects |
415 | */ |
416 | class SimpleStub { |
417 | var $_wildcard; |
418 | var $_is_strict; |
419 | var $_returns; |
420 | var $_return_sequence; |
421 | var $_call_counts; |
422 | |
423 | /** |
424 | * Sets up the wildcard and everything else empty. |
425 | * @param mixed $wildcard Parameter matching wildcard. |
426 | * @param boolean $is_strict Enables method name checks. |
427 | * @access public |
428 | */ |
429 | function SimpleStub($wildcard, $is_strict = true) { |
430 | $this->_wildcard = $wildcard; |
431 | $this->_is_strict = $is_strict; |
432 | $this->_returns = array(); |
433 | $this->_return_sequence = array(); |
434 | $this->_call_counts = array(); |
435 | } |
436 | |
437 | /** |
438 | * Replaces wildcard matches with wildcard |
439 | * expectations in the argument list. |
440 | * @param array $args Raw argument list. |
441 | * @return array Argument list with |
442 | * expectations. |
443 | * @access private |
444 | */ |
445 | function _replaceWildcards($args) { |
446 | if ($args === false) { |
447 | return false; |
448 | } |
449 | for ($i = 0; $i < count($args); $i++) { |
450 | if ($args[$i] === $this->_wildcard) { |
451 | $args[$i] = new WildcardExpectation(); |
452 | } |
453 | } |
454 | return $args; |
455 | } |
456 | |
457 | /** |
458 | * Returns the expected value for the method name. |
459 | * @param string $method Name of method to simulate. |
460 | * @param array $args Arguments as an array. |
461 | * @return mixed Stored return. |
462 | * @access private |
463 | */ |
464 | function &_invoke($method, $args) { |
465 | $method = strtolower($method); |
466 | $step = $this->getCallCount($method); |
467 | $this->_addCall($method, $args); |
468 | return $this->_getReturn($method, $args, $step); |
469 | } |
470 | |
471 | /** |
472 | * Triggers a PHP error if the method is not part |
473 | * of this object. |
474 | * @param string $method Name of method. |
475 | * @param string $task Description of task attempt. |
476 | * @access protected |
477 | */ |
478 | function _dieOnNoMethod($method, $task) { |
479 | if ($this->_is_strict && !method_exists($this, $method)) { |
480 | trigger_error( |
481 | "Cannot $task as no ${method}() in class " . get_class($this), |
482 | E_USER_ERROR); |
483 | } |
484 | } |
485 | |
486 | /** |
487 | * Adds one to the call count of a method. |
488 | * @param string $method Method called. |
489 | * @param array $args Arguments as an array. |
490 | * @access protected |
491 | */ |
492 | function _addCall($method, $args) { |
493 | if (!isset($this->_call_counts[$method])) { |
494 | $this->_call_counts[$method] = 0; |
495 | } |
496 | $this->_call_counts[$method]++; |
497 | } |
498 | |
499 | /** |
500 | * Fetches the call count of a method so far. |
501 | * @param string $method Method name called. |
502 | * @return Number of calls so far. |
503 | * @access public |
504 | */ |
505 | function getCallCount($method) { |
506 | $this->_dieOnNoMethod($method, "get call count"); |
507 | $method = strtolower($method); |
508 | if (! isset($this->_call_counts[$method])) { |
509 | return 0; |
510 | } |
511 | return $this->_call_counts[$method]; |
512 | } |
513 | |
514 | /** |
515 | * Sets a return for a parameter list that will |
516 | * be passed by value for all calls to this method. |
517 | * @param string $method Method name. |
518 | * @param mixed $value Result of call passed by value. |
519 | * @param array $args List of parameters to match |
520 | * including wildcards. |
521 | * @access public |
522 | */ |
523 | function setReturnValue($method, $value, $args = false) { |
524 | $this->_dieOnNoMethod($method, "set return value"); |
525 | $args = $this->_replaceWildcards($args); |
526 | $method = strtolower($method); |
527 | if (! isset($this->_returns[$method])) { |
528 | $this->_returns[$method] = new CallMap(); |
529 | } |
530 | $this->_returns[$method]->addValue($args, $value); |
531 | } |
532 | |
533 | /** |
534 | * Sets a return for a parameter list that will |
535 | * be passed by value only when the required call count |
536 | * is reached. |
537 | * @param integer $timing Number of calls in the future |
538 | * to which the result applies. If |
539 | * not set then all calls will return |
540 | * the value. |
541 | * @param string $method Method name. |
542 | * @param mixed $value Result of call passed by value. |
543 | * @param array $args List of parameters to match |
544 | * including wildcards. |
545 | * @access public |
546 | */ |
547 | function setReturnValueAt($timing, $method, $value, $args = false) { |
548 | $this->_dieOnNoMethod($method, "set return value sequence"); |
549 | $args = $this->_replaceWildcards($args); |
550 | $method = strtolower($method); |
551 | if (! isset($this->_return_sequence[$method])) { |
552 | $this->_return_sequence[$method] = array(); |
553 | } |
554 | if (! isset($this->_return_sequence[$method][$timing])) { |
555 | $this->_return_sequence[$method][$timing] = new CallMap(); |
556 | } |
557 | $this->_return_sequence[$method][$timing]->addValue($args, $value); |
558 | } |
559 | |
560 | /** |
561 | * Sets a return for a parameter list that will |
562 | * be passed by reference for all calls. |
563 | * @param string $method Method name. |
564 | * @param mixed $reference Result of the call will be this object. |
565 | * @param array $args List of parameters to match |
566 | * including wildcards. |
567 | * @access public |
568 | */ |
569 | function setReturnReference($method, &$reference, $args = false) { |
570 | $this->_dieOnNoMethod($method, "set return reference"); |
571 | $args = $this->_replaceWildcards($args); |
572 | $method = strtolower($method); |
573 | if (! isset($this->_returns[$method])) { |
574 | $this->_returns[$method] = new CallMap(); |
575 | } |
576 | $this->_returns[$method]->addReference($args, $reference); |
577 | } |
578 | |
579 | /** |
580 | * Sets a return for a parameter list that will |
581 | * be passed by value only when the required call count |
582 | * is reached. |
583 | * @param integer $timing Number of calls in the future |
584 | * to which the result applies. If |
585 | * not set then all calls will return |
586 | * the value. |
587 | * @param string $method Method name. |
588 | * @param mixed $reference Result of the call will be this object. |
589 | * @param array $args List of parameters to match |
590 | * including wildcards. |
591 | * @access public |
592 | */ |
593 | function setReturnReferenceAt($timing, $method, &$reference, $args = false) { |
594 | $this->_dieOnNoMethod($method, "set return reference sequence"); |
595 | $args = $this->_replaceWildcards($args); |
596 | $method = strtolower($method); |
597 | if (! isset($this->_return_sequence[$method])) { |
598 | $this->_return_sequence[$method] = array(); |
599 | } |
600 | if (! isset($this->_return_sequence[$method][$timing])) { |
601 | $this->_return_sequence[$method][$timing] = new CallMap(); |
602 | } |
603 | $this->_return_sequence[$method][$timing]->addReference($args, $reference); |
604 | } |
605 | |
606 | /** |
607 | * Finds the return value matching the incoming |
608 | * arguments. If there is no matching value found |
609 | * then an error is triggered. |
610 | * @param string $method Method name. |
611 | * @param array $args Calling arguments. |
612 | * @param integer $step Current position in the |
613 | * call history. |
614 | * @return mixed Stored return. |
615 | * @access protected |
616 | */ |
617 | function &_getReturn($method, $args, $step) { |
618 | if (isset($this->_return_sequence[$method][$step])) { |
619 | if ($this->_return_sequence[$method][$step]->isMatch($args)) { |
620 | return $this->_return_sequence[$method][$step]->findFirstMatch($args); |
621 | } |
622 | } |
623 | if (isset($this->_returns[$method])) { |
624 | return $this->_returns[$method]->findFirstMatch($args); |
625 | } |
626 | return null; |
627 | } |
628 | } |
629 | |
630 | /** |
631 | * An empty collection of methods that can have their |
632 | * return values set and expectations made of the |
633 | * calls upon them. The mock will assert the |
634 | * expectations against it's attached test case in |
635 | * addition to the server stub behaviour. |
636 | * @package SimpleTest |
637 | * @subpackage MockObjects |
638 | */ |
639 | class SimpleMock extends SimpleStub { |
640 | var $_test; |
641 | var $_expected_counts; |
642 | var $_max_counts; |
643 | var $_expected_args; |
644 | var $_expected_args_at; |
645 | |
646 | /** |
647 | * Creates an empty return list and expectation list. |
648 | * All call counts are set to zero. |
649 | * @param SimpleTestCase $test Test case to test expectations in. |
650 | * @param mixed $wildcard Parameter matching wildcard. |
651 | * @param boolean $is_strict Enables method name checks on |
652 | * expectations. |
653 | * @access public |
654 | */ |
655 | function SimpleMock(&$test, $wildcard, $is_strict = true) { |
656 | $this->SimpleStub($wildcard, $is_strict); |
657 | if (! $test) { |
658 | trigger_error('No unit tester for mock object', E_USER_ERROR); |
659 | return; |
660 | } |
661 | $this->_test = &$test; |
662 | $this->_expected_counts = array(); |
663 | $this->_max_counts = array(); |
664 | $this->_expected_args = array(); |
665 | $this->_expected_args_at = array(); |
666 | } |
667 | |
668 | /** |
669 | * Accessor for attached unit test so that when |
670 | * subclassed, new expectations can be added easily. |
671 | * @return SimpleTestCase Unit test passed in constructor. |
672 | * @access public |
673 | */ |
674 | function &getTest() { |
675 | return $this->_test; |
676 | } |
677 | |
678 | /** |
679 | * Die if bad arguments array is passed |
680 | * @param mixed $args The arguments value to be checked. |
681 | * @param string $task Description of task attempt. |
682 | * @return boolean Valid arguments |
683 | * @access private |
684 | */ |
685 | function _checkArgumentsIsArray($args, $task) { |
686 | if (! is_array($args)) { |
687 | trigger_error( |
688 | "Cannot $task as \$args parameter is not an array", |
689 | E_USER_ERROR); |
690 | } |
691 | } |
692 | |
693 | /** |
694 | * Sets up an expected call with a set of |
695 | * expected parameters in that call. All |
696 | * calls will be compared to these expectations |
697 | * regardless of when the call is made. |
698 | * @param string $method Method call to test. |
699 | * @param array $args Expected parameters for the call |
700 | * including wildcards. |
701 | * @param string $message Overridden message. |
702 | * @access public |
703 | */ |
704 | function expectArguments($method, $args, $message = '%s') { |
705 | $this->_dieOnNoMethod($method, 'set expected arguments'); |
706 | $this->_checkArgumentsIsArray($args, 'set expected arguments'); |
707 | $args = $this->_replaceWildcards($args); |
708 | $message .= Mock::getExpectationLine(' at line [%d]'); |
709 | $this->_expected_args[strtolower($method)] = |
710 | new ParametersExpectation($args, $message); |
711 | } |
712 | |
713 | /** |
714 | * Sets up an expected call with a set of |
715 | * expected parameters in that call. The |
716 | * expected call count will be adjusted if it |
717 | * is set too low to reach this call. |
718 | * @param integer $timing Number of calls in the future at |
719 | * which to test. Next call is 0. |
720 | * @param string $method Method call to test. |
721 | * @param array $args Expected parameters for the call |
722 | * including wildcards. |
723 | * @param string $message Overridden message. |
724 | * @access public |
725 | */ |
726 | function expectArgumentsAt($timing, $method, $args, $message = '%s') { |
727 | $this->_dieOnNoMethod($method, "set expected arguments at time"); |
728 | $this->_checkArgumentsIsArray($args, "set expected arguments"); |
729 | $args = $this->_replaceWildcards($args); |
730 | if (! isset($this->_expected_args_at[$timing])) { |
731 | $this->_expected_args_at[$timing] = array(); |
732 | } |
733 | $method = strtolower($method); |
734 | $message .= Mock::getExpectationLine(' at line [%d]'); |
735 | $this->_expected_args_at[$timing][$method] = |
736 | new ParametersExpectation($args, $message); |
737 | } |
738 | |
739 | /** |
740 | * Sets an expectation for the number of times |
741 | * a method will be called. The tally method |
742 | * is used to check this. |
743 | * @param string $method Method call to test. |
744 | * @param integer $count Number of times it should |
745 | * have been called at tally. |
746 | * @param string $message Overridden message. |
747 | * @access public |
748 | */ |
749 | function expectCallCount($method, $count, $message = '%s') { |
750 | $this->_dieOnNoMethod($method, "set expected call count"); |
751 | $message .= Mock::getExpectationLine(' at line [%d]'); |
752 | $this->_expected_counts[strtolower($method)] = |
753 | new CallCountExpectation($method, $count, $message); |
754 | } |
755 | |
756 | /** |
757 | * Sets the number of times a method may be called |
758 | * before a test failure is triggered. |
759 | * @param string $method Method call to test. |
760 | * @param integer $count Most number of times it should |
761 | * have been called. |
762 | * @param string $message Overridden message. |
763 | * @access public |
764 | */ |
765 | function expectMaximumCallCount($method, $count, $message = '%s') { |
766 | $this->_dieOnNoMethod($method, "set maximum call count"); |
767 | $message .= Mock::getExpectationLine(' at line [%d]'); |
768 | $this->_max_counts[strtolower($method)] = |
769 | new MaximumCallCountExpectation($method, $count, $message); |
770 | } |
771 | |
772 | /** |
773 | * Sets the number of times to call a method to prevent |
774 | * a failure on the tally. |
775 | * @param string $method Method call to test. |
776 | * @param integer $count Least number of times it should |
777 | * have been called. |
778 | * @param string $message Overridden message. |
779 | * @access public |
780 | */ |
781 | function expectMinimumCallCount($method, $count, $message = '%s') { |
782 | $this->_dieOnNoMethod($method, "set minimum call count"); |
783 | $message .= Mock::getExpectationLine(' at line [%d]'); |
784 | $this->_expected_counts[strtolower($method)] = |
785 | new MinimumCallCountExpectation($method, $count, $message); |
786 | } |
787 | |
788 | /** |
789 | * Convenience method for barring a method |
790 | * call. |
791 | * @param string $method Method call to ban. |
792 | * @param string $message Overridden message. |
793 | * @access public |
794 | */ |
795 | function expectNever($method, $message = '%s') { |
796 | $this->expectMaximumCallCount($method, 0, $message); |
797 | } |
798 | |
799 | /** |
800 | * Convenience method for a single method |
801 | * call. |
802 | * @param string $method Method call to track. |
803 | * @param array $args Expected argument list or |
804 | * false for any arguments. |
805 | * @param string $message Overridden message. |
806 | * @access public |
807 | */ |
808 | function expectOnce($method, $args = false, $message = '%s') { |
809 | $this->expectCallCount($method, 1, $message); |
810 | if ($args !== false) { |
811 | $this->expectArguments($method, $args, $message); |
812 | } |
813 | } |
814 | |
815 | /** |
816 | * Convenience method for requiring a method |
817 | * call. |
818 | * @param string $method Method call to track. |
819 | * @param array $args Expected argument list or |
820 | * false for any arguments. |
821 | * @param string $message Overridden message. |
822 | * @access public |
823 | */ |
824 | function expectAtLeastOnce($method, $args = false, $message = '%s') { |
825 | $this->expectMinimumCallCount($method, 1, $message); |
826 | if ($args !== false) { |
827 | $this->expectArguments($method, $args, $message); |
828 | } |
829 | } |
830 | |
831 | /** |
832 | * Totals up the call counts and triggers a test |
833 | * assertion if a test is present for expected |
834 | * call counts. |
835 | * This method must be called explicitly for the call |
836 | * count assertions to be triggered. |
837 | * @access public |
838 | */ |
839 | function tally() { |
840 | foreach ($this->_expected_counts as $method => $expectation) { |
841 | $this->_assertTrue( |
842 | $expectation->test($this->getCallCount($method)), |
843 | $expectation->overlayMessage($this->getCallCount($method))); |
844 | } |
845 | } |
846 | |
847 | /** |
848 | * Returns the expected value for the method name |
849 | * and checks expectations. Will generate any |
850 | * test assertions as a result of expectations |
851 | * if there is a test present. |
852 | * @param string $method Name of method to simulate. |
853 | * @param array $args Arguments as an array. |
854 | * @return mixed Stored return. |
855 | * @access private |
856 | */ |
857 | function &_invoke($method, $args) { |
858 | $method = strtolower($method); |
859 | $step = $this->getCallCount($method); |
860 | $this->_addCall($method, $args); |
861 | $this->_checkExpectations($method, $args, $step); |
862 | return $this->_getReturn($method, $args, $step); |
863 | } |
864 | |
865 | /** |
866 | * Tests the arguments against expectations. |
867 | * @param string $method Method to check. |
868 | * @param array $args Argument list to match. |
869 | * @param integer $timing The position of this call |
870 | * in the call history. |
871 | * @access private |
872 | */ |
873 | function _checkExpectations($method, $args, $timing) { |
874 | if (isset($this->_max_counts[$method])) { |
875 | if (! $this->_max_counts[$method]->test($timing + 1)) { |
876 | $this->_assertTrue( |
877 | false, |
878 | $this->_max_counts[$method]->overlayMessage($timing + 1)); |
879 | } |
880 | } |
881 | if (isset($this->_expected_args_at[$timing][$method])) { |
882 | $this->_assertTrue( |
883 | $this->_expected_args_at[$timing][$method]->test($args), |
884 | "Mock method [$method] at [$timing] -> " . |
885 | $this->_expected_args_at[$timing][$method]->overlayMessage($args)); |
886 | } elseif (isset($this->_expected_args[$method])) { |
887 | $this->_assertTrue( |
888 | $this->_expected_args[$method]->test($args), |
889 | "Mock method [$method] -> " . $this->_expected_args[$method]->overlayMessage($args)); |
890 | } |
891 | } |
892 | |
893 | /** |
894 | * Triggers an assertion on the held test case. |
895 | * Should be overridden when using another test |
896 | * framework other than the SimpleTest one if the |
897 | * assertion method has a different name. |
898 | * @param boolean $assertion True will pass. |
899 | * @param string $message Message that will go with |
900 | * the test event. |
901 | * @access protected |
902 | */ |
903 | function _assertTrue($assertion, $message) { |
904 | $this->_test->assertTrue($assertion, $message); |
905 | } |
906 | } |
907 | |
908 | /** |
909 | * Static methods only service class for code generation of |
910 | * server stubs. |
911 | * @package SimpleTest |
912 | * @subpackage MockObjects |
913 | */ |
914 | class Stub { |
915 | |
916 | /** |
917 | * Factory for server stub classes. |
918 | */ |
919 | function Stub() { |
920 | trigger_error('Stub factory methods are class only.'); |
921 | } |
922 | |
923 | /** |
924 | * Clones a class' interface and creates a stub version |
925 | * that can have return values set. |
926 | * @param string $class Class to clone. |
927 | * @param string $stub_class New class name. Default is |
928 | * the old name with "Stub" |
929 | * prepended. |
930 | * @param array $methods Additional methods to add beyond |
931 | * those in th cloned class. Use this |
932 | * to emulate the dynamic addition of |
933 | * methods in the cloned class or when |
934 | * the class hasn't been written yet. |
935 | * @static |
936 | * @access public |
937 | */ |
938 | function generate($class, $stub_class = false, $methods = false) { |
939 | if (! class_exists($class)) { |
940 | return false; |
941 | } |
942 | if (! $stub_class) { |
943 | $stub_class = "Stub" . $class; |
944 | } |
945 | if (class_exists($stub_class)) { |
946 | return false; |
947 | } |
948 | return eval(Stub::_createClassCode( |
949 | $class, |
950 | $stub_class, |
951 | $methods ? $methods : array()) . " return true;"); |
952 | } |
953 | |
954 | /** |
955 | * The new server stub class code in string form. |
956 | * @param string $class Class to clone. |
957 | * @param string $mock_class New class name. |
958 | * @param array $methods Additional methods. |
959 | * @static |
960 | * @access private |
961 | */ |
962 | function _createClassCode($class, $stub_class, $methods) { |
963 | $stub_base = SimpleTestOptions::getStubBaseClass(); |
964 | $code = "class $stub_class extends $stub_base {\n"; |
965 | $code .= " function $stub_class(\$wildcard = MOCK_WILDCARD) {\n"; |
966 | $code .= " \$this->$stub_base(\$wildcard);\n"; |
967 | $code .= " }\n"; |
968 | $code .= Stub::_createHandlerCode($class, $stub_base, $methods); |
969 | $code .= "}\n"; |
970 | return $code; |
971 | } |
972 | |
973 | /** |
974 | * Creates code within a class to generate replaced |
975 | * methods. All methods call the _invoke() handler |
976 | * with the method name and the arguments in an |
977 | * array. |
978 | * @param string $class Class to clone. |
979 | * @param string $base Base class with methods that |
980 | * cannot be cloned. |
981 | * @param array $methods Additional methods. |
982 | * @static |
983 | * @access private |
984 | */ |
985 | function _createHandlerCode($class, $base, $methods) { |
986 | $code = ""; |
987 | $methods = array_merge($methods, get_class_methods($class)); |
988 | foreach ($methods as $method) { |
989 | if (($method == '__construct') || ($method == '__clone')) { |
990 | continue; |
991 | } |
992 | if (in_array($method, get_class_methods($base))) { |
993 | continue; |
994 | } |
995 | $code .= " function &$method() {\n"; |
996 | $code .= " \$args = func_get_args();\n"; |
997 | $code .= " return \$this->_invoke(\"$method\", \$args);\n"; |
998 | $code .= " }\n"; |
999 | } |
1000 | return $code; |
1001 | } |
1002 | } |
1003 | |
1004 | /** |
1005 | * Static methods only service class for code generation of |
1006 | * mock objects. |
1007 | * @package SimpleTest |
1008 | * @subpackage MockObjects |
1009 | */ |
1010 | class Mock { |
1011 | |
1012 | /** |
1013 | * Factory for mock object classes. |
1014 | * @access public |
1015 | */ |
1016 | function Mock() { |
1017 | trigger_error("Mock factory methods are class only."); |
1018 | } |
1019 | |
1020 | /** |
1021 | * Clones a class' interface and creates a mock version |
1022 | * that can have return values and expectations set. |
1023 | * @param string $class Class to clone. |
1024 | * @param string $mock_class New class name. Default is |
1025 | * the old name with "Mock" |
1026 | * prepended. |
1027 | * @param array $methods Additional methods to add beyond |
1028 | * those in th cloned class. Use this |
1029 | * to emulate the dynamic addition of |
1030 | * methods in the cloned class or when |
1031 | * the class hasn't been written yet. |
1032 | * @static |
1033 | * @access public |
1034 | */ |
1035 | function generate($class, $mock_class = false, $methods = false) { |
1036 | if (! class_exists($class)) { |
1037 | return false; |
1038 | } |
1039 | if (! $mock_class) { |
1040 | $mock_class = "Mock" . $class; |
1041 | } |
1042 | if (class_exists($mock_class)) { |
1043 | return false; |
1044 | } |
1045 | return eval(Mock::_createClassCode( |
1046 | $class, |
1047 | $mock_class, |
1048 | $methods ? $methods : array()) . " return true;"); |
1049 | } |
1050 | |
1051 | /** |
1052 | * Generates a version of a class with selected |
1053 | * methods mocked only. Inherits the old class |
1054 | * and chains the mock methods of an aggregated |
1055 | * mock object. |
1056 | * @param string $class Class to clone. |
1057 | * @param string $mock_class New class name. |
1058 | * @param array $methods Methods to be overridden |
1059 | * with mock versions. |
1060 | * @static |
1061 | * @access public |
1062 | */ |
1063 | function generatePartial($class, $mock_class, $methods) { |
1064 | if (! class_exists($class)) { |
1065 | return false; |
1066 | } |
1067 | if (class_exists($mock_class)) { |
1068 | trigger_error("Partial mock class [$mock_class] already exists"); |
1069 | return false; |
1070 | } |
1071 | return eval(Mock::_extendClassCode($class, $mock_class, $methods)); |
1072 | } |
1073 | |
1074 | /** |
1075 | * The new mock class code as a string. |
1076 | * @param string $class Class to clone. |
1077 | * @param string $mock_class New class name. |
1078 | * @param array $methods Additional methods. |
1079 | * @return string Code for new mock class. |
1080 | * @static |
1081 | * @access private |
1082 | */ |
1083 | function _createClassCode($class, $mock_class, $methods) { |
1084 | $mock_base = SimpleTestOptions::getMockBaseClass(); |
1085 | $code = "class $mock_class extends $mock_base {\n"; |
1086 | $code .= " function $mock_class(&\$test, \$wildcard = MOCK_WILDCARD) {\n"; |
1087 | $code .= " \$this->$mock_base(\$test, \$wildcard);\n"; |
1088 | $code .= " }\n"; |
1089 | $code .= Stub::_createHandlerCode($class, $mock_base, $methods); |
1090 | $code .= "}\n"; |
1091 | return $code; |
1092 | } |
1093 | |
1094 | /** |
1095 | * The extension class code as a string. The class |
1096 | * composites a mock object and chains mocked methods |
1097 | * to it. |
1098 | * @param string $class Class to extend. |
1099 | * @param string $mock_class New class name. |
1100 | * @param array $methods Mocked methods. |
1101 | * @return string Code for a new class. |
1102 | * @static |
1103 | * @access private |
1104 | */ |
1105 | function _extendClassCode($class, $mock_class, $methods) { |
1106 | $mock_base = SimpleTestOptions::getMockBaseClass(); |
1107 | $code = "class $mock_class extends $class {\n"; |
1108 | $code .= " var \$_mock;\n"; |
1109 | $code .= Mock::_addMethodList($methods); |
1110 | $code .= "\n"; |
1111 | $code .= " function $mock_class(&\$test, \$wildcard = MOCK_WILDCARD) {\n"; |
1112 | $code .= " \$this->_mock = &new $mock_base(\$test, \$wildcard, false);\n"; |
1113 | $code .= " }\n"; |
1114 | $code .= Mock::_chainMockReturns(); |
1115 | $code .= Mock::_chainMockExpectations(); |
1116 | $code .= Mock::_overrideMethods($methods); |
1117 | $code .= SimpleTestOptions::getPartialMockCode(); |
1118 | $code .= "}\n"; |
1119 | return $code; |
1120 | } |
1121 | |
1122 | /** |
1123 | * Creates a list of mocked methods for error checking. |
1124 | * @param array $methods Mocked methods. |
1125 | * @return string Code for a method list. |
1126 | * @access private |
1127 | */ |
1128 | function _addMethodList($methods) { |
1129 | return " var \$_mocked_methods = array('" . |
1130 | implode("', '", $methods) . "');\n"; |
1131 | } |
1132 | |
1133 | /** |
1134 | * Creates code to abandon the expectation if not mocked. |
1135 | * @param string $alias Parameter name of method name. |
1136 | * @return string Code for bail out. |
1137 | * @access private |
1138 | */ |
1139 | function _bailOutIfNotMocked($alias) { |
1140 | $code = " if (! in_array($alias, \$this->_mocked_methods)) {\n"; |
1141 | $code .= " trigger_error('Method [$alias] is not mocked');\n"; |
1142 | $code .= " return;\n"; |
1143 | $code .= " }\n"; |
1144 | return $code; |
1145 | } |
1146 | |
1147 | /** |
1148 | * Creates source code for chaining to the composited |
1149 | * mock object. |
1150 | * @return string Code for mock set up. |
1151 | * @access private |
1152 | */ |
1153 | function _chainMockReturns() { |
1154 | $code = " function setReturnValue(\$method, \$value, \$args = false) {\n"; |
1155 | $code .= Mock::_bailOutIfNotMocked("\$method"); |
1156 | $code .= " \$this->_mock->setReturnValue(\$method, \$value, \$args);\n"; |
1157 | $code .= " }\n"; |
1158 | $code .= " function setReturnValueAt(\$timing, \$method, \$value, \$args = false) {\n"; |
1159 | $code .= Mock::_bailOutIfNotMocked("\$method"); |
1160 | $code .= " \$this->_mock->setReturnValueAt(\$timing, \$method, \$value, \$args);\n"; |
1161 | $code .= " }\n"; |
1162 | $code .= " function setReturnReference(\$method, &\$ref, \$args = false) {\n"; |
1163 | $code .= Mock::_bailOutIfNotMocked("\$method"); |
1164 | $code .= " \$this->_mock->setReturnReference(\$method, \$ref, \$args);\n"; |
1165 | $code .= " }\n"; |
1166 | $code .= " function setReturnReferenceAt(\$timing, \$method, &\$ref, \$args = false) {\n"; |
1167 | $code .= Mock::_bailOutIfNotMocked("\$method"); |
1168 | $code .= " \$this->_mock->setReturnReferenceAt(\$timing, \$method, \$ref, \$args);\n"; |
1169 | $code .= " }\n"; |
1170 | return $code; |
1171 | } |
1172 | |
1173 | /** |
1174 | * Creates source code for chaining to an aggregated |
1175 | * mock object. |
1176 | * @return string Code for expectations. |
1177 | * @access private |
1178 | */ |
1179 | function _chainMockExpectations() { |
1180 | $code = " function expectArguments(\$method, \$args = false) {\n"; |
1181 | $code .= Mock::_bailOutIfNotMocked("\$method"); |
1182 | $code .= " \$this->_mock->expectArguments(\$method, \$args);\n"; |
1183 | $code .= " }\n"; |
1184 | $code .= " function expectArgumentsAt(\$timing, \$method, \$args = false) {\n"; |
1185 | $code .= Mock::_bailOutIfNotMocked("\$method"); |
1186 | $code .= " \$this->_mock->expectArgumentsAt(\$timing, \$method, \$args);\n"; |
1187 | $code .= " }\n"; |
1188 | $code .= " function expectCallCount(\$method, \$count) {\n"; |
1189 | $code .= Mock::_bailOutIfNotMocked("\$method"); |
1190 | $code .= " \$this->_mock->expectCallCount(\$method, \$count);\n"; |
1191 | $code .= " }\n"; |
1192 | $code .= " function expectMaximumCallCount(\$method, \$count) {\n"; |
1193 | $code .= Mock::_bailOutIfNotMocked("\$method"); |
1194 | $code .= " \$this->_mock->expectMaximumCallCount(\$method, \$count);\n"; |
1195 | $code .= " }\n"; |
1196 | $code .= " function expectMinimumCallCount(\$method, \$count) {\n"; |
1197 | $code .= Mock::_bailOutIfNotMocked("\$method"); |
1198 | $code .= " \$this->_mock->expectMinimumCallCount(\$method, \$count);\n"; |
1199 | $code .= " }\n"; |
1200 | $code .= " function expectNever(\$method) {\n"; |
1201 | $code .= Mock::_bailOutIfNotMocked("\$method"); |
1202 | $code .= " \$this->_mock->expectNever(\$method);\n"; |
1203 | $code .= " }\n"; |
1204 | $code .= " function expectOnce(\$method, \$args = false) {\n"; |
1205 | $code .= Mock::_bailOutIfNotMocked("\$method"); |
1206 | $code .= " \$this->_mock->expectOnce(\$method, \$args);\n"; |
1207 | $code .= " }\n"; |
1208 | $code .= " function expectAtLeastOnce(\$method, \$args = false) {\n"; |
1209 | $code .= Mock::_bailOutIfNotMocked("\$method"); |
1210 | $code .= " \$this->_mock->expectAtLeastOnce(\$method, \$args);\n"; |
1211 | $code .= " }\n"; |
1212 | $code .= " function tally() {\n"; |
1213 | $code .= " \$this->_mock->tally();\n"; |
1214 | $code .= " }\n"; |
1215 | return $code; |
1216 | } |
1217 | |
1218 | /** |
1219 | * Creates source code to override a list of methods |
1220 | * with mock versions. |
1221 | * @param array $methods Methods to be overridden |
1222 | * with mock versions. |
1223 | * @return string Code for overridden chains. |
1224 | * @access private |
1225 | */ |
1226 | function _overrideMethods($methods) { |
1227 | $code = ""; |
1228 | foreach ($methods as $method) { |
1229 | $code .= " function &$method() {\n"; |
1230 | $code .= " \$args = func_get_args();\n"; |
1231 | $code .= " return \$this->_mock->_invoke(\"$method\", \$args);\n"; |
1232 | $code .= " }\n"; |
1233 | } |
1234 | return $code; |
1235 | } |
1236 | |
1237 | /** |
1238 | * Uses a stack trace to find the line of an assertion. |
1239 | * @param string $format String formatting. |
1240 | * @param array $stack Stack frames top most first. Only |
1241 | * needed if not using the PHP |
1242 | * backtrace function. |
1243 | * @return string Line number of first assert* |
1244 | * method embedded in format string. |
1245 | * @access public |
1246 | * @static |
1247 | */ |
1248 | function getExpectationLine($format = '%d', $stack = false) { |
1249 | if ($stack === false) { |
1250 | $stack = SimpleTestCompatibility::getStackTrace(); |
1251 | } |
1252 | return SimpleDumper::getFormattedAssertionLine($stack, $format, 'expect'); |
1253 | } |
1254 | } |
1255 | ?> |