first reimport from platal
[platal.git] / htdocs / TESTS / simpletest / mock_objects.php
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 ?>