1<?php
2/**
3 *  base include file for SimpleTest
4 *  @package    SimpleTest
5 *  @subpackage MockObjects
6 *  @version    $Id$
7 */
8
9/**#@+
10 * include SimpleTest files
11 */
12require_once(dirname(__FILE__) . '/expectation.php');
13require_once(dirname(__FILE__) . '/simpletest.php');
14require_once(dirname(__FILE__) . '/dumper.php');
15require_once(dirname(__FILE__) . '/reflection_php5.php');
16/**#@-*/
17
18/**
19 * Default character simpletest will substitute for any value
20 */
21if (! defined('MOCK_ANYTHING')) {
22    define('MOCK_ANYTHING', '*');
23}
24
25/**
26 *    Parameter comparison assertion.
27 *    @package SimpleTest
28 *    @subpackage MockObjects
29 */
30class ParametersExpectation extends SimpleExpectation {
31    private $expected;
32
33    /**
34     *    Sets the expected parameter list.
35     *    @param array $parameters  Array of parameters including
36     *                              those that are wildcarded.
37     *                              If the value is not an array
38     *                              then it is considered to match any.
39     *    @param string $message    Customised message on failure.
40     */
41    function __construct($expected = false, $message = '%s') {
42        parent::__construct($message);
43        $this->expected = $expected;
44    }
45
46    /**
47     *    Tests the assertion. True if correct.
48     *    @param array $parameters     Comparison values.
49     *    @return boolean              True if correct.
50     */
51    function test($parameters) {
52        if (! is_array($this->expected)) {
53            return true;
54        }
55        if (count($this->expected) != count($parameters)) {
56            return false;
57        }
58        for ($i = 0; $i < count($this->expected); $i++) {
59            if (! $this->testParameter($parameters[$i], $this->expected[$i])) {
60                return false;
61            }
62        }
63        return true;
64    }
65
66    /**
67     *    Tests an individual parameter.
68     *    @param mixed $parameter    Value to test.
69     *    @param mixed $expected     Comparison value.
70     *    @return boolean            True if expectation
71     *                               fulfilled.
72     */
73    protected function testParameter($parameter, $expected) {
74        $comparison = $this->coerceToExpectation($expected);
75        return $comparison->test($parameter);
76    }
77
78    /**
79     *    Returns a human readable test message.
80     *    @param array $comparison   Incoming parameter list.
81     *    @return string             Description of success
82     *                               or failure.
83     */
84    function testMessage($parameters) {
85        if ($this->test($parameters)) {
86            return "Expectation of " . count($this->expected) .
87                    " arguments of [" . $this->renderArguments($this->expected) .
88                    "] is correct";
89        } else {
90            return $this->describeDifference($this->expected, $parameters);
91        }
92    }
93
94    /**
95     *    Message to display if expectation differs from
96     *    the parameters actually received.
97     *    @param array $expected      Expected parameters as list.
98     *    @param array $parameters    Actual parameters received.
99     *    @return string              Description of difference.
100     */
101    protected function describeDifference($expected, $parameters) {
102        if (count($expected) != count($parameters)) {
103            return "Expected " . count($expected) .
104                    " arguments of [" . $this->renderArguments($expected) .
105                    "] but got " . count($parameters) .
106                    " arguments of [" . $this->renderArguments($parameters) . "]";
107        }
108        $messages = array();
109        for ($i = 0; $i < count($expected); $i++) {
110            $comparison = $this->coerceToExpectation($expected[$i]);
111            if (! $comparison->test($parameters[$i])) {
112                $messages[] = "parameter " . ($i + 1) . " with [" .
113                        $comparison->overlayMessage($parameters[$i], $this->getDumper()) . "]";
114            }
115        }
116        return "Parameter expectation differs at " . implode(" and ", $messages);
117    }
118
119    /**
120     *    Creates an identical expectation if the
121     *    object/value is not already some type
122     *    of expectation.
123     *    @param mixed $expected      Expected value.
124     *    @return SimpleExpectation   Expectation object.
125     */
126    protected function coerceToExpectation($expected) {
127        if (SimpleExpectation::isExpectation($expected)) {
128            return $expected;
129        }
130        return new IdenticalExpectation($expected);
131    }
132
133    /**
134     *    Renders the argument list as a string for
135     *    messages.
136     *    @param array $args    Incoming arguments.
137     *    @return string        Simple description of type and value.
138     */
139    protected function renderArguments($args) {
140        $descriptions = array();
141        if (is_array($args)) {
142            foreach ($args as $arg) {
143                $dumper = new SimpleDumper();
144                $descriptions[] = $dumper->describeValue($arg);
145            }
146        }
147        return implode(', ', $descriptions);
148    }
149}
150
151/**
152 *    Confirms that the number of calls on a method is as expected.
153 *  @package    SimpleTest
154 *  @subpackage MockObjects
155 */
156class CallCountExpectation extends SimpleExpectation {
157    private $method;
158    private $count;
159
160    /**
161     *    Stashes the method and expected count for later
162     *    reporting.
163     *    @param string $method    Name of method to confirm against.
164     *    @param integer $count    Expected number of calls.
165     *    @param string $message   Custom error message.
166     */
167    function __construct($method, $count, $message = '%s') {
168        $this->method = $method;
169        $this->count = $count;
170        parent::__construct($message);
171    }
172
173    /**
174     *    Tests the assertion. True if correct.
175     *    @param integer $compare     Measured call count.
176     *    @return boolean             True if expected.
177     */
178    function test($compare) {
179        return ($this->count == $compare);
180    }
181
182    /**
183     *    Reports the comparison.
184     *    @param integer $compare     Measured call count.
185     *    @return string              Message to show.
186     */
187    function testMessage($compare) {
188        return 'Expected call count for [' . $this->method .
189                '] was [' . $this->count .
190                '] got [' . $compare . ']';
191    }
192}
193
194/**
195 *    Confirms that the number of calls on a method is as expected.
196 *  @package    SimpleTest
197 *  @subpackage MockObjects
198 */
199class MinimumCallCountExpectation extends SimpleExpectation {
200    private $method;
201    private $count;
202
203    /**
204     *    Stashes the method and expected count for later
205     *    reporting.
206     *    @param string $method    Name of method to confirm against.
207     *    @param integer $count    Minimum number of calls.
208     *    @param string $message   Custom error message.
209     */
210    function __construct($method, $count, $message = '%s') {
211        $this->method = $method;
212        $this->count = $count;
213        parent::__construct($message);
214    }
215
216    /**
217     *    Tests the assertion. True if correct.
218     *    @param integer $compare     Measured call count.
219     *    @return boolean             True if enough.
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     */
230    function testMessage($compare) {
231        return 'Minimum call count for [' . $this->method .
232                '] was [' . $this->count .
233                '] got [' . $compare . ']';
234    }
235}
236
237/**
238 *    Confirms that the number of calls on a method is as expected.
239 *    @package      SimpleTest
240 *    @subpackage   MockObjects
241 */
242class MaximumCallCountExpectation extends SimpleExpectation {
243    private $method;
244    private $count;
245
246    /**
247     *    Stashes the method and expected count for later
248     *    reporting.
249     *    @param string $method    Name of method to confirm against.
250     *    @param integer $count    Minimum number of calls.
251     *    @param string $message   Custom error message.
252     */
253    function __construct($method, $count, $message = '%s') {
254        $this->method = $method;
255        $this->count = $count;
256        parent::__construct($message);
257    }
258
259    /**
260     *    Tests the assertion. True if correct.
261     *    @param integer $compare     Measured call count.
262     *    @return boolean             True if not over.
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     */
273    function testMessage($compare) {
274        return 'Maximum call count for [' . $this->method .
275                '] was [' . $this->count .
276                '] got [' . $compare . ']';
277    }
278}
279
280/**
281 *    Retrieves method actions by searching the
282 *    parameter lists until an expected match is found.
283 *    @package SimpleTest
284 *    @subpackage MockObjects
285 */
286class SimpleSignatureMap {
287    private $map;
288
289    /**
290     *    Creates an empty call map.
291     */
292    function __construct() {
293        $this->map = array();
294    }
295
296    /**
297     *    Stashes a reference against a method call.
298     *    @param array $parameters    Array of arguments (including wildcards).
299     *    @param mixed $action        Reference placed in the map.
300     */
301    function add($parameters, $action) {
302        $place = count($this->map);
303        $this->map[$place] = array();
304        $this->map[$place]['params'] = new ParametersExpectation($parameters);
305        $this->map[$place]['content'] = $action;
306    }
307
308    /**
309     *    Searches the call list for a matching parameter
310     *    set. Returned by reference.
311     *    @param array $parameters    Parameters to search by
312     *                                without wildcards.
313     *    @return object              Object held in the first matching
314     *                                slot, otherwise null.
315     */
316    function &findFirstAction($parameters) {
317        $slot = $this->findFirstSlot($parameters);
318        if (isset($slot) && isset($slot['content'])) {
319            return $slot['content'];
320        }
321        $null = null;
322        return $null;
323    }
324
325    /**
326     *    Searches the call list for a matching parameter
327     *    set. True if successful.
328     *    @param array $parameters    Parameters to search by
329     *                                without wildcards.
330     *    @return boolean             True if a match is present.
331     */
332    function isMatch($parameters) {
333        return ($this->findFirstSlot($parameters) != null);
334    }
335
336    /**
337     *    Compares the incoming parameters with the
338     *    internal expectation. Uses the incoming $test
339     *    to dispatch the test message.
340     *    @param SimpleTestCase $test   Test to dispatch to.
341     *    @param array $parameters      The actual calling arguments.
342     *    @param string $message        The message to overlay.
343     */
344    function test($test, $parameters, $message) {
345    }
346
347    /**
348     *    Searches the map for a matching item.
349     *    @param array $parameters    Parameters to search by
350     *                                without wildcards.
351     *    @return array               Reference to slot or null.
352     */
353    function &findFirstSlot($parameters) {
354        $count = count($this->map);
355        for ($i = 0; $i < $count; $i++) {
356            if ($this->map[$i]["params"]->test($parameters)) {
357                return $this->map[$i];
358            }
359        }
360        $null = null;
361        return $null;
362    }
363}
364
365/**
366 *    Allows setting of actions against call signatures either
367 *    at a specific time, or always. Specific time settings
368 *    trump lasting ones, otherwise the most recently added
369 *    will mask an earlier match.
370 *    @package SimpleTest
371 *    @subpackage MockObjects
372 */
373class SimpleCallSchedule {
374    private $wildcard = MOCK_ANYTHING;
375    private $always;
376    private $at;
377
378    /**
379     *    Sets up an empty response schedule.
380     *    Creates an empty call map.
381     */
382    function __construct() {
383        $this->always = array();
384        $this->at = array();
385    }
386
387    /**
388     *    Stores an action against a signature that
389     *    will always fire unless masked by a time
390     *    specific one.
391     *    @param string $method        Method name.
392     *    @param array $args           Calling parameters.
393     *    @param SimpleAction $action  Actually simpleByValue, etc.
394     */
395    function register($method, $args, $action) {
396        $args = $this->replaceWildcards($args);
397        $method = strtolower($method);
398        if (! isset($this->always[$method])) {
399            $this->always[$method] = new SimpleSignatureMap();
400        }
401        $this->always[$method]->add($args, $action);
402    }
403
404    /**
405     *    Stores an action against a signature that
406     *    will fire at a specific time in the future.
407     *    @param integer $step         delay of calls to this method,
408     *                                 0 is next.
409     *    @param string $method        Method name.
410     *    @param array $args           Calling parameters.
411     *    @param SimpleAction $action  Actually SimpleByValue, etc.
412     */
413    function registerAt($step, $method, $args, $action) {
414        $args = $this->replaceWildcards($args);
415        $method = strtolower($method);
416        if (! isset($this->at[$method])) {
417            $this->at[$method] = array();
418        }
419        if (! isset($this->at[$method][$step])) {
420            $this->at[$method][$step] = new SimpleSignatureMap();
421        }
422        $this->at[$method][$step]->add($args, $action);
423    }
424
425    /**
426     *  Sets up an expectation on the argument list.
427     *  @param string $method       Method to test.
428     *  @param array $args          Bare arguments or list of
429     *                              expectation objects.
430     *  @param string $message      Failure message.
431     */
432    function expectArguments($method, $args, $message) {
433        $args = $this->replaceWildcards($args);
434        $message .= Mock::getExpectationLine();
435        $this->expected_args[strtolower($method)] =
436                new ParametersExpectation($args, $message);
437
438    }
439
440    /**
441     *    Actually carry out the action stored previously,
442     *    if the parameters match.
443     *    @param integer $step      Time of call.
444     *    @param string $method     Method name.
445     *    @param array $args        The parameters making up the
446     *                              rest of the call.
447     *    @return mixed             The result of the action.
448     */
449    function &respond($step, $method, $args) {
450        $method = strtolower($method);
451        if (isset($this->at[$method][$step])) {
452            if ($this->at[$method][$step]->isMatch($args)) {
453                $action = $this->at[$method][$step]->findFirstAction($args);
454                if (isset($action)) {
455                    return $action->act();
456                }
457            }
458        }
459        if (isset($this->always[$method])) {
460            $action = $this->always[$method]->findFirstAction($args);
461            if (isset($action)) {
462                return $action->act();
463            }
464        }
465        $null = null;
466        return $null;
467    }
468
469    /**
470     *    Replaces wildcard matches with wildcard
471     *    expectations in the argument list.
472     *    @param array $args      Raw argument list.
473     *    @return array           Argument list with
474     *                            expectations.
475     */
476    protected function replaceWildcards($args) {
477        if ($args === false) {
478            return false;
479        }
480        for ($i = 0; $i < count($args); $i++) {
481            if ($args[$i] === $this->wildcard) {
482                $args[$i] = new AnythingExpectation();
483            }
484        }
485        return $args;
486    }
487}
488
489/**
490 *    A type of SimpleMethodAction.
491 *    Stashes a value for returning later. Follows usual
492 *    PHP5 semantics of objects being returned by reference.
493 *    @package SimpleTest
494 *    @subpackage MockObjects
495 */
496class SimpleReturn {
497    private $value;
498
499    /**
500     *    Stashes it for later.
501     *    @param mixed $value     You need to clone objects
502     *                            if you want copy semantics
503     *                            for these.
504     */
505    function __construct($value) {
506        $this->value = $value;
507    }
508
509    /**
510     *    Returns the value stored earlier.
511     *    @return mixed    Whatever was stashed.
512     */
513    function act() {
514        return $this->value;
515    }
516}
517
518/**
519 *    A type of SimpleMethodAction.
520 *    Stashes a reference for returning later.
521 *    @package SimpleTest
522 *    @subpackage MockObjects
523 */
524class SimpleByReference {
525    private $reference;
526
527    /**
528     *    Stashes it for later.
529     *    @param mixed $reference     Actual PHP4 style reference.
530     */
531    function __construct(&$reference) {
532        $this->reference = &$reference;
533    }
534
535    /**
536     *    Returns the reference stored earlier.
537     *    @return mixed    Whatever was stashed.
538     */
539    function &act() {
540        return $this->reference;
541    }
542}
543
544/**
545 *    A type of SimpleMethodAction.
546 *    Stashes a value for returning later.
547 *    @package SimpleTest
548 *    @subpackage MockObjects
549 */
550class SimpleByValue {
551    private $value;
552
553    /**
554     *    Stashes it for later.
555     *    @param mixed $value     You need to clone objects
556     *                            if you want copy semantics
557     *                            for these.
558     */
559    function __construct($value) {
560        $this->value = $value;
561    }
562
563    /**
564     *    Returns the value stored earlier.
565     *    @return mixed    Whatever was stashed.
566     */
567    function &act() {
568        $dummy = $this->value;
569        return $dummy;
570    }
571}
572
573/**
574 *    A type of SimpleMethodAction.
575 *    Stashes an exception for throwing later.
576 *    @package SimpleTest
577 *    @subpackage MockObjects
578 */
579class SimpleThrower {
580    private $exception;
581
582    /**
583     *    Stashes it for later.
584     *    @param Exception $exception    The exception object to throw.
585     */
586    function __construct($exception) {
587        $this->exception = $exception;
588    }
589
590    /**
591     *    Throws the exceptins stashed earlier.
592     */
593    function act() {
594        throw $this->exception;
595    }
596}
597
598/**
599 *    A type of SimpleMethodAction.
600 *    Stashes an error for emitting later.
601 *    @package SimpleTest
602 *    @subpackage MockObjects
603 */
604class SimpleErrorThrower {
605    private $error;
606    private $severity;
607
608    /**
609     *    Stashes an error to throw later.
610     *    @param string $error      Error message.
611     *    @param integer $severity  PHP error constant, e.g E_USER_ERROR.
612     */
613    function __construct($error, $severity) {
614        $this->error = $error;
615        $this->severity = $severity;
616    }
617
618    /**
619     *    Triggers the stashed error.
620     */
621    function &act() {
622        trigger_error($this->error, $this->severity);
623        $null = null;
624        return $null;
625    }
626}
627
628/**
629 *    A base class or delegate that extends an
630 *    empty collection of methods that can have their
631 *    return values set and expectations made of the
632 *    calls upon them. The mock will assert the
633 *    expectations against it's attached test case in
634 *    addition to the server stub behaviour or returning
635 *    preprogrammed responses.
636 *    @package SimpleTest
637 *    @subpackage MockObjects
638 */
639class SimpleMock {
640    private $actions;
641    private $expectations;
642    private $wildcard = MOCK_ANYTHING;
643    private $is_strict = true;
644    private $call_counts;
645    private $expected_counts;
646    private $max_counts;
647    private $expected_args;
648    private $expected_args_at;
649
650    /**
651     *    Creates an empty action list and expectation list.
652     *    All call counts are set to zero.
653     */
654    function SimpleMock() {
655        $this->actions = new SimpleCallSchedule();
656        $this->expectations = new SimpleCallSchedule();
657        $this->call_counts = array();
658        $this->expected_counts = array();
659        $this->max_counts = array();
660        $this->expected_args = array();
661        $this->expected_args_at = array();
662        $this->getCurrentTestCase()->tell($this);
663    }
664
665    /**
666     *    Disables a name check when setting expectations.
667     *    This hack is needed for the partial mocks.
668     */
669    function disableExpectationNameChecks() {
670        $this->is_strict = false;
671    }
672
673    /**
674     *    Finds currently running test.
675     *    @return SimpeTestCase    Current test case.
676     */
677    protected function getCurrentTestCase() {
678        return SimpleTest::getContext()->getTest();
679    }
680
681    /**
682     *    Die if bad arguments array is passed.
683     *    @param mixed $args     The arguments value to be checked.
684     *    @param string $task    Description of task attempt.
685     *    @return boolean        Valid arguments
686     */
687    protected function checkArgumentsIsArray($args, $task) {
688        if (! is_array($args)) {
689            trigger_error(
690                "Cannot $task as \$args parameter is not an array",
691                E_USER_ERROR);
692        }
693    }
694
695    /**
696     *    Triggers a PHP error if the method is not part
697     *    of this object.
698     *    @param string $method        Name of method.
699     *    @param string $task          Description of task attempt.
700     */
701    protected function dieOnNoMethod($method, $task) {
702        if ($this->is_strict && ! method_exists($this, $method)) {
703            trigger_error(
704                    "Cannot $task as no ${method}() in class " . get_class($this),
705                    E_USER_ERROR);
706        }
707    }
708
709    /**
710     *    Replaces wildcard matches with wildcard
711     *    expectations in the argument list.
712     *    @param array $args      Raw argument list.
713     *    @return array           Argument list with
714     *                            expectations.
715     */
716    function replaceWildcards($args) {
717        if ($args === false) {
718            return false;
719        }
720        for ($i = 0; $i < count($args); $i++) {
721            if ($args[$i] === $this->wildcard) {
722                $args[$i] = new AnythingExpectation();
723            }
724        }
725        return $args;
726    }
727
728    /**
729     *    Adds one to the call count of a method.
730     *    @param string $method        Method called.
731     *    @param array $args           Arguments as an array.
732     */
733    protected function addCall($method, $args) {
734        if (! isset($this->call_counts[$method])) {
735            $this->call_counts[$method] = 0;
736        }
737        $this->call_counts[$method]++;
738    }
739
740    /**
741     *    Fetches the call count of a method so far.
742     *    @param string $method        Method name called.
743     *    @return integer              Number of calls so far.
744     */
745    function getCallCount($method) {
746        $this->dieOnNoMethod($method, "get call count");
747        $method = strtolower($method);
748        if (! isset($this->call_counts[$method])) {
749            return 0;
750        }
751        return $this->call_counts[$method];
752    }
753
754    /**
755     *    Sets a return for a parameter list that will
756     *    be passed on by all calls to this method that match.
757     *    @param string $method       Method name.
758     *    @param mixed $value         Result of call by value/handle.
759     *    @param array $args          List of parameters to match
760     *                                including wildcards.
761     */
762    function returns($method, $value, $args = false) {
763        $this->dieOnNoMethod($method, "set return");
764        $this->actions->register($method, $args, new SimpleReturn($value));
765    }
766
767    /**
768     *    Sets a return for a parameter list that will
769     *    be passed only when the required call count
770     *    is reached.
771     *    @param integer $timing   Number of calls in the future
772     *                             to which the result applies. If
773     *                             not set then all calls will return
774     *                             the value.
775     *    @param string $method    Method name.
776     *    @param mixed $value      Result of call passed.
777     *    @param array $args       List of parameters to match
778     *                             including wildcards.
779     */
780    function returnsAt($timing, $method, $value, $args = false) {
781        $this->dieOnNoMethod($method, "set return value sequence");
782        $this->actions->registerAt($timing, $method, $args, new SimpleReturn($value));
783    }
784
785    /**
786     *    Sets a return for a parameter list that will
787     *    be passed by value for all calls to this method.
788     *    @param string $method       Method name.
789     *    @param mixed $value         Result of call passed by value.
790     *    @param array $args          List of parameters to match
791     *                                including wildcards.
792     */
793    function returnsByValue($method, $value, $args = false) {
794        $this->dieOnNoMethod($method, "set return value");
795        $this->actions->register($method, $args, new SimpleByValue($value));
796    }
797
798    /** @deprecated */
799    function setReturnValue($method, $value, $args = false) {
800        $this->returnsByValue($method, $value, $args);
801    }
802
803    /**
804     *    Sets a return for a parameter list that will
805     *    be passed by value only when the required call count
806     *    is reached.
807     *    @param integer $timing   Number of calls in the future
808     *                             to which the result applies. If
809     *                             not set then all calls will return
810     *                             the value.
811     *    @param string $method    Method name.
812     *    @param mixed $value      Result of call passed by value.
813     *    @param array $args       List of parameters to match
814     *                             including wildcards.
815     */
816    function returnsByValueAt($timing, $method, $value, $args = false) {
817        $this->dieOnNoMethod($method, "set return value sequence");
818        $this->actions->registerAt($timing, $method, $args, new SimpleByValue($value));
819    }
820
821    /** @deprecated */
822    function setReturnValueAt($timing, $method, $value, $args = false) {
823        $this->returnsByValueAt($timing, $method, $value, $args);
824    }
825
826    /**
827     *    Sets a return for a parameter list that will
828     *    be passed by reference for all calls.
829     *    @param string $method       Method name.
830     *    @param mixed $reference     Result of the call will be this object.
831     *    @param array $args          List of parameters to match
832     *                                including wildcards.
833     */
834    function returnsByReference($method, &$reference, $args = false) {
835        $this->dieOnNoMethod($method, "set return reference");
836        $this->actions->register($method, $args, new SimpleByReference($reference));
837    }
838
839    /** @deprecated */
840    function setReturnReference($method, &$reference, $args = false) {
841        $this->returnsByReference($method, $reference, $args);
842    }
843
844    /**
845     *    Sets a return for a parameter list that will
846     *    be passed by value only when the required call count
847     *    is reached.
848     *    @param integer $timing    Number of calls in the future
849     *                              to which the result applies. If
850     *                              not set then all calls will return
851     *                              the value.
852     *    @param string $method     Method name.
853     *    @param mixed $reference   Result of the call will be this object.
854     *    @param array $args        List of parameters to match
855     *                              including wildcards.
856     */
857    function returnsByReferenceAt($timing, $method, &$reference, $args = false) {
858        $this->dieOnNoMethod($method, "set return reference sequence");
859        $this->actions->registerAt($timing, $method, $args, new SimpleByReference($reference));
860    }
861
862    /** @deprecated */
863    function setReturnReferenceAt($timing, $method, &$reference, $args = false) {
864        $this->returnsByReferenceAt($timing, $method, $reference, $args);
865    }
866
867    /**
868     *    Sets up an expected call with a set of
869     *    expected parameters in that call. All
870     *    calls will be compared to these expectations
871     *    regardless of when the call is made.
872     *    @param string $method        Method call to test.
873     *    @param array $args           Expected parameters for the call
874     *                                 including wildcards.
875     *    @param string $message       Overridden message.
876     */
877    function expect($method, $args, $message = '%s') {
878        $this->dieOnNoMethod($method, 'set expected arguments');
879        $this->checkArgumentsIsArray($args, 'set expected arguments');
880        $this->expectations->expectArguments($method, $args, $message);
881        $args = $this->replaceWildcards($args);
882        $message .= Mock::getExpectationLine();
883        $this->expected_args[strtolower($method)] =
884                new ParametersExpectation($args, $message);
885    }
886
887    /**
888     *    Sets up an expected call with a set of
889     *    expected parameters in that call. The
890     *    expected call count will be adjusted if it
891     *    is set too low to reach this call.
892     *    @param integer $timing    Number of calls in the future at
893     *                              which to test. Next call is 0.
894     *    @param string $method     Method call to test.
895     *    @param array $args        Expected parameters for the call
896     *                              including wildcards.
897     *    @param string $message    Overridden message.
898     */
899    function expectAt($timing, $method, $args, $message = '%s') {
900        $this->dieOnNoMethod($method, 'set expected arguments at time');
901        $this->checkArgumentsIsArray($args, 'set expected arguments at time');
902        $args = $this->replaceWildcards($args);
903        if (! isset($this->expected_args_at[$timing])) {
904            $this->expected_args_at[$timing] = array();
905        }
906        $method = strtolower($method);
907        $message .= Mock::getExpectationLine();
908        $this->expected_args_at[$timing][$method] =
909                new ParametersExpectation($args, $message);
910    }
911
912    /**
913     *    Sets an expectation for the number of times
914     *    a method will be called. The tally method
915     *    is used to check this.
916     *    @param string $method        Method call to test.
917     *    @param integer $count        Number of times it should
918     *                                 have been called at tally.
919     *    @param string $message       Overridden message.
920     */
921    function expectCallCount($method, $count, $message = '%s') {
922        $this->dieOnNoMethod($method, 'set expected call count');
923        $message .= Mock::getExpectationLine();
924        $this->expected_counts[strtolower($method)] =
925                new CallCountExpectation($method, $count, $message);
926    }
927
928    /**
929     *    Sets the number of times a method may be called
930     *    before a test failure is triggered.
931     *    @param string $method        Method call to test.
932     *    @param integer $count        Most number of times it should
933     *                                 have been called.
934     *    @param string $message       Overridden message.
935     */
936    function expectMaximumCallCount($method, $count, $message = '%s') {
937        $this->dieOnNoMethod($method, 'set maximum call count');
938        $message .= Mock::getExpectationLine();
939        $this->max_counts[strtolower($method)] =
940                new MaximumCallCountExpectation($method, $count, $message);
941    }
942
943    /**
944     *    Sets the number of times to call a method to prevent
945     *    a failure on the tally.
946     *    @param string $method      Method call to test.
947     *    @param integer $count      Least number of times it should
948     *                               have been called.
949     *    @param string $message     Overridden message.
950     */
951    function expectMinimumCallCount($method, $count, $message = '%s') {
952        $this->dieOnNoMethod($method, 'set minimum call count');
953        $message .= Mock::getExpectationLine();
954        $this->expected_counts[strtolower($method)] =
955                new MinimumCallCountExpectation($method, $count, $message);
956    }
957
958    /**
959     *    Convenience method for barring a method
960     *    call.
961     *    @param string $method        Method call to ban.
962     *    @param string $message       Overridden message.
963     */
964    function expectNever($method, $message = '%s') {
965        $this->expectMaximumCallCount($method, 0, $message);
966    }
967
968    /**
969     *    Convenience method for a single method
970     *    call.
971     *    @param string $method     Method call to track.
972     *    @param array $args        Expected argument list or
973     *                              false for any arguments.
974     *    @param string $message    Overridden message.
975     */
976    function expectOnce($method, $args = false, $message = '%s') {
977        $this->expectCallCount($method, 1, $message);
978        if ($args !== false) {
979            $this->expect($method, $args, $message);
980        }
981    }
982
983    /**
984     *    Convenience method for requiring a method
985     *    call.
986     *    @param string $method       Method call to track.
987     *    @param array $args          Expected argument list or
988     *                                false for any arguments.
989     *    @param string $message      Overridden message.
990     */
991    function expectAtLeastOnce($method, $args = false, $message = '%s') {
992        $this->expectMinimumCallCount($method, 1, $message);
993        if ($args !== false) {
994            $this->expect($method, $args, $message);
995        }
996    }
997
998    /**
999     *    Sets up a trigger to throw an exception upon the
1000     *    method call.
1001     *    @param string $method     Method name to throw on.
1002     *    @param object $exception  Exception object to throw.
1003     *                              If not given then a simple
1004     *                              Exception object is thrown.
1005     *    @param array $args        Optional argument list filter.
1006     *                              If given then the exception
1007     *                              will only be thrown if the
1008     *                              method call matches the arguments.
1009     */
1010    function throwOn($method, $exception = false, $args = false) {
1011        $this->dieOnNoMethod($method, "throw on");
1012        $this->actions->register($method, $args,
1013                new SimpleThrower($exception ? $exception : new Exception()));
1014    }
1015
1016    /**
1017     *    Sets up a trigger to throw an exception upon the
1018     *    method call.
1019     *    @param integer $timing    When to throw the exception. A
1020     *                              value of 0 throws immediately.
1021     *                              A value of 1 actually allows one call
1022     *                              to this method before throwing. 2
1023     *                              will allow two calls before throwing
1024     *                              and so on.
1025     *    @param string $method     Method name to throw on.
1026     *    @param object $exception  Exception object to throw.
1027     *                              If not given then a simple
1028     *                              Exception object is thrown.
1029     *    @param array $args        Optional argument list filter.
1030     *                              If given then the exception
1031     *                              will only be thrown if the
1032     *                              method call matches the arguments.
1033     */
1034    function throwAt($timing, $method, $exception = false, $args = false) {
1035        $this->dieOnNoMethod($method, "throw at");
1036        $this->actions->registerAt($timing, $method, $args,
1037                new SimpleThrower($exception ? $exception : new Exception()));
1038    }
1039
1040    /**
1041     *    Sets up a trigger to throw an error upon the
1042     *    method call.
1043     *    @param string $method     Method name to throw on.
1044     *    @param object $error      Error message to trigger.
1045     *    @param array $args        Optional argument list filter.
1046     *                              If given then the exception
1047     *                              will only be thrown if the
1048     *                              method call matches the arguments.
1049     *    @param integer $severity  The PHP severity level. Defaults
1050     *                              to E_USER_ERROR.
1051     */
1052    function errorOn($method, $error = 'A mock error', $args = false, $severity = E_USER_ERROR) {
1053        $this->dieOnNoMethod($method, "error on");
1054        $this->actions->register($method, $args, new SimpleErrorThrower($error, $severity));
1055    }
1056
1057    /**
1058     *    Sets up a trigger to throw an error upon a specific
1059     *    method call.
1060     *    @param integer $timing    When to throw the exception. A
1061     *                              value of 0 throws immediately.
1062     *                              A value of 1 actually allows one call
1063     *                              to this method before throwing. 2
1064     *                              will allow two calls before throwing
1065     *                              and so on.
1066     *    @param string $method     Method name to throw on.
1067     *    @param object $error      Error message to trigger.
1068     *    @param array $args        Optional argument list filter.
1069     *                              If given then the exception
1070     *                              will only be thrown if the
1071     *                              method call matches the arguments.
1072     *    @param integer $severity  The PHP severity level. Defaults
1073     *                              to E_USER_ERROR.
1074     */
1075    function errorAt($timing, $method, $error = 'A mock error', $args = false, $severity = E_USER_ERROR) {
1076        $this->dieOnNoMethod($method, "error at");
1077        $this->actions->registerAt($timing, $method, $args, new SimpleErrorThrower($error, $severity));
1078    }
1079
1080    /**
1081     *    Receives event from unit test that the current
1082     *    test method has finished. Totals up the call
1083     *    counts and triggers a test assertion if a test
1084     *    is present for expected call counts.
1085     *    @param string $test_method      Current method name.
1086     *    @param SimpleTestCase $test     Test to send message to.
1087     */
1088    function atTestEnd($test_method, &$test) {
1089        foreach ($this->expected_counts as $method => $expectation) {
1090            $test->assert($expectation, $this->getCallCount($method));
1091        }
1092        foreach ($this->max_counts as $method => $expectation) {
1093            if ($expectation->test($this->getCallCount($method))) {
1094                $test->assert($expectation, $this->getCallCount($method));
1095            }
1096        }
1097    }
1098
1099    /**
1100     *    Returns the expected value for the method name
1101     *    and checks expectations. Will generate any
1102     *    test assertions as a result of expectations
1103     *    if there is a test present.
1104     *    @param string $method       Name of method to simulate.
1105     *    @param array $args          Arguments as an array.
1106     *    @return mixed               Stored return.
1107     */
1108    function &invoke($method, $args) {
1109        $method = strtolower($method);
1110        $step = $this->getCallCount($method);
1111        $this->addCall($method, $args);
1112        $this->checkExpectations($method, $args, $step);
1113        $was = $this->disableEStrict();
1114        try {
1115            $result = &$this->emulateCall($method, $args, $step);
1116        } catch (Exception $e) {
1117            $this->restoreEStrict($was);
1118            throw $e;
1119        }
1120        $this->restoreEStrict($was);
1121        return $result;
1122    }
1123
1124    /**
1125     *    Finds the return value matching the incoming
1126     *    arguments. If there is no matching value found
1127     *    then an error is triggered.
1128     *    @param string $method      Method name.
1129     *    @param array $args         Calling arguments.
1130     *    @param integer $step       Current position in the
1131     *                               call history.
1132     *    @return mixed              Stored return or other action.
1133     */
1134    protected function &emulateCall($method, $args, $step) {
1135        return $this->actions->respond($step, $method, $args);
1136    }
1137
1138    /**
1139     *    Tests the arguments against expectations.
1140     *    @param string $method        Method to check.
1141     *    @param array $args           Argument list to match.
1142     *    @param integer $timing       The position of this call
1143     *                                 in the call history.
1144     */
1145    protected function checkExpectations($method, $args, $timing) {
1146        $test = $this->getCurrentTestCase();
1147        if (isset($this->max_counts[$method])) {
1148            if (! $this->max_counts[$method]->test($timing + 1)) {
1149                $test->assert($this->max_counts[$method], $timing + 1);
1150            }
1151        }
1152        if (isset($this->expected_args_at[$timing][$method])) {
1153            $test->assert(
1154                    $this->expected_args_at[$timing][$method],
1155                    $args,
1156                    "Mock method [$method] at [$timing] -> %s");
1157        } elseif (isset($this->expected_args[$method])) {
1158            $test->assert(
1159                    $this->expected_args[$method],
1160                    $args,
1161                    "Mock method [$method] -> %s");
1162        }
1163    }
1164
1165    /**
1166     *   Our mock has to be able to return anything, including
1167     *   variable references. To allow for these mixed returns
1168     *   we have to disable the E_STRICT warnings while the
1169     *   method calls are emulated.
1170     */
1171    private function disableEStrict() {
1172        $was = error_reporting();
1173        error_reporting($was & ~E_STRICT);
1174        return $was;
1175    }
1176
1177    /**
1178     *  Restores the E_STRICT level if it was previously set.
1179     *  @param integer $was     Previous error reporting level.
1180     */
1181    private function restoreEStrict($was) {
1182        error_reporting($was);
1183    }
1184}
1185
1186/**
1187 *    Static methods only service class for code generation of
1188 *    mock objects.
1189 *    @package SimpleTest
1190 *    @subpackage MockObjects
1191 */
1192class Mock {
1193
1194    /**
1195     *    Factory for mock object classes.
1196     */
1197    function __construct() {
1198        trigger_error('Mock factory methods are static.');
1199    }
1200
1201    /**
1202     *    Clones a class' interface and creates a mock version
1203     *    that can have return values and expectations set.
1204     *    @param string $class         Class to clone.
1205     *    @param string $mock_class    New class name. Default is
1206     *                                 the old name with "Mock"
1207     *                                 prepended.
1208     *    @param array $methods        Additional methods to add beyond
1209     *                                 those in the cloned class. Use this
1210     *                                 to emulate the dynamic addition of
1211     *                                 methods in the cloned class or when
1212     *                                 the class hasn't been written yet.sta
1213     */
1214    static function generate($class, $mock_class = false, $methods = false) {
1215        $generator = new MockGenerator($class, $mock_class);
1216        return @$generator->generateSubclass($methods);
1217    }
1218
1219    /**
1220     *    Generates a version of a class with selected
1221     *    methods mocked only. Inherits the old class
1222     *    and chains the mock methods of an aggregated
1223     *    mock object.
1224     *    @param string $class            Class to clone.
1225     *    @param string $mock_class       New class name.
1226     *    @param array $methods           Methods to be overridden
1227     *                                    with mock versions.
1228     */
1229    static function generatePartial($class, $mock_class, $methods) {
1230        $generator = new MockGenerator($class, $mock_class);
1231        return @$generator->generatePartial($methods);
1232    }
1233
1234    /**
1235     *    Uses a stack trace to find the line of an assertion.
1236     */
1237    static function getExpectationLine() {
1238        $trace = new SimpleStackTrace(array('expect'));
1239        return $trace->traceMethod();
1240    }
1241}
1242
1243/**
1244 *    Service class for code generation of mock objects.
1245 *    @package SimpleTest
1246 *    @subpackage MockObjects
1247 */
1248class MockGenerator {
1249    private $class;
1250    private $mock_class;
1251    private $mock_base;
1252    private $reflection;
1253
1254    /**
1255     *    Builds initial reflection object.
1256     *    @param string $class        Class to be mocked.
1257     *    @param string $mock_class   New class with identical interface,
1258     *                                but no behaviour.
1259     */
1260    function __construct($class, $mock_class) {
1261        $this->class = $class;
1262        $this->mock_class = $mock_class;
1263        if (! $this->mock_class) {
1264            $this->mock_class = 'Mock' . $this->class;
1265        }
1266        $this->mock_base = SimpleTest::getMockBaseClass();
1267        $this->reflection = new SimpleReflection($this->class);
1268    }
1269
1270    /**
1271     *    Clones a class' interface and creates a mock version
1272     *    that can have return values and expectations set.
1273     *    @param array $methods        Additional methods to add beyond
1274     *                                 those in th cloned class. Use this
1275     *                                 to emulate the dynamic addition of
1276     *                                 methods in the cloned class or when
1277     *                                 the class hasn't been written yet.
1278     */
1279    function generate($methods) {
1280        if (! $this->reflection->classOrInterfaceExists()) {
1281            return false;
1282        }
1283        $mock_reflection = new SimpleReflection($this->mock_class);
1284        if ($mock_reflection->classExistsSansAutoload()) {
1285            return false;
1286        }
1287        $code = $this->createClassCode($methods ? $methods : array());
1288        return eval("$code return \$code;");
1289    }
1290
1291    /**
1292     *    Subclasses a class and overrides every method with a mock one
1293     *    that can have return values and expectations set. Chains
1294     *    to an aggregated SimpleMock.
1295     *    @param array $methods        Additional methods to add beyond
1296     *                                 those in the cloned class. Use this
1297     *                                 to emulate the dynamic addition of
1298     *                                 methods in the cloned class or when
1299     *                                 the class hasn't been written yet.
1300     */
1301    function generateSubclass($methods) {
1302        if (! $this->reflection->classOrInterfaceExists()) {
1303            return false;
1304        }
1305        $mock_reflection = new SimpleReflection($this->mock_class);
1306        if ($mock_reflection->classExistsSansAutoload()) {
1307            return false;
1308        }
1309        if ($this->reflection->isInterface() || $this->reflection->hasFinal()) {
1310            $code = $this->createClassCode($methods ? $methods : array());
1311            return eval("$code return \$code;");
1312        } else {
1313            $code = $this->createSubclassCode($methods ? $methods : array());
1314            return eval("$code return \$code;");
1315        }
1316    }
1317
1318    /**
1319     *    Generates a version of a class with selected
1320     *    methods mocked only. Inherits the old class
1321     *    and chains the mock methods of an aggregated
1322     *    mock object.
1323     *    @param array $methods           Methods to be overridden
1324     *                                    with mock versions.
1325     */
1326    function generatePartial($methods) {
1327        if (! $this->reflection->classExists($this->class)) {
1328            return false;
1329        }
1330        $mock_reflection = new SimpleReflection($this->mock_class);
1331        if ($mock_reflection->classExistsSansAutoload()) {
1332            trigger_error('Partial mock class [' . $this->mock_class . '] already exists');
1333            return false;
1334        }
1335        $code = $this->extendClassCode($methods);
1336        return eval("$code return \$code;");
1337    }
1338
1339    /**
1340     *    The new mock class code as a string.
1341     *    @param array $methods          Additional methods.
1342     *    @return string                 Code for new mock class.
1343     */
1344    protected function createClassCode($methods) {
1345        $implements = '';
1346        $interfaces = $this->reflection->getInterfaces();
1347        if (function_exists('spl_classes')) {
1348            $interfaces = array_diff($interfaces, array('Traversable'));
1349        }
1350        if (count($interfaces) > 0) {
1351            $implements = 'implements ' . implode(', ', $interfaces);
1352        }
1353        $code = "class " . $this->mock_class . " extends " . $this->mock_base . " $implements {\n";
1354        $code .= "    function " . $this->mock_class . "() {\n";
1355        $code .= "        \$this->" . $this->mock_base . "();\n";
1356        $code .= "    }\n";
1357        if (in_array('__construct', $this->reflection->getMethods())) {
1358            $code .= "    function __construct() {\n";
1359            $code .= "        \$this->" . $this->mock_base . "();\n";
1360            $code .= "    }\n";
1361        }
1362        $code .= $this->createHandlerCode($methods);
1363        $code .= "}\n";
1364        return $code;
1365    }
1366
1367    /**
1368     *    The new mock class code as a string. The mock will
1369     *    be a subclass of the original mocked class.
1370     *    @param array $methods          Additional methods.
1371     *    @return string                 Code for new mock class.
1372     */
1373    protected function createSubclassCode($methods) {
1374        $code  = "class " . $this->mock_class . " extends " . $this->class . " {\n";
1375        $code .= "    public \$mock;\n";
1376        $code .= $this->addMethodList(array_merge($methods, $this->reflection->getMethods()));
1377        $code .= "\n";
1378        $code .= "    function " . $this->mock_class . "() {\n";
1379        $code .= "        \$this->mock = new " . $this->mock_base . "();\n";
1380        $code .= "        \$this->mock->disableExpectationNameChecks();\n";
1381        $code .= "    }\n";
1382        $code .= $this->chainMockReturns();
1383        $code .= $this->chainMockExpectations();
1384        $code .= $this->chainThrowMethods();
1385        $code .= $this->overrideMethods($this->reflection->getMethods());
1386        $code .= $this->createNewMethodCode($methods);
1387        $code .= "}\n";
1388        return $code;
1389    }
1390
1391    /**
1392     *    The extension class code as a string. The class
1393     *    composites a mock object and chains mocked methods
1394     *    to it.
1395     *    @param array  $methods       Mocked methods.
1396     *    @return string               Code for a new class.
1397     */
1398    protected function extendClassCode($methods) {
1399        $code  = "class " . $this->mock_class . " extends " . $this->class . " {\n";
1400        $code .= "    protected \$mock;\n";
1401        $code .= $this->addMethodList($methods);
1402        $code .= "\n";
1403        $code .= "    function " . $this->mock_class . "() {\n";
1404        $code .= "        \$this->mock = new " . $this->mock_base . "();\n";
1405        $code .= "        \$this->mock->disableExpectationNameChecks();\n";
1406        $code .= "    }\n";
1407        $code .= $this->chainMockReturns();
1408        $code .= $this->chainMockExpectations();
1409        $code .= $this->chainThrowMethods();
1410        $code .= $this->overrideMethods($methods);
1411        $code .= "}\n";
1412        return $code;
1413    }
1414
1415    /**
1416     *    Creates code within a class to generate replaced
1417     *    methods. All methods call the invoke() handler
1418     *    with the method name and the arguments in an
1419     *    array.
1420     *    @param array $methods    Additional methods.
1421     */
1422    protected function createHandlerCode($methods) {
1423        $code = '';
1424        $methods = array_merge($methods, $this->reflection->getMethods());
1425        foreach ($methods as $method) {
1426            if ($this->isConstructor($method)) {
1427                continue;
1428            }
1429            $mock_reflection = new SimpleReflection($this->mock_base);
1430            if (in_array($method, $mock_reflection->getMethods())) {
1431                continue;
1432            }
1433            $code .= "    " . $this->reflection->getSignature($method) . " {\n";
1434            $code .= "        \$args = func_get_args();\n";
1435            $code .= "        \$result = &\$this->invoke(\"$method\", \$args);\n";
1436            $code .= "        return \$result;\n";
1437            $code .= "    }\n";
1438        }
1439        return $code;
1440    }
1441
1442    /**
1443     *    Creates code within a class to generate a new
1444     *    methods. All methods call the invoke() handler
1445     *    on the internal mock with the method name and
1446     *    the arguments in an array.
1447     *    @param array $methods    Additional methods.
1448     */
1449    protected function createNewMethodCode($methods) {
1450        $code = '';
1451        foreach ($methods as $method) {
1452            if ($this->isConstructor($method)) {
1453                continue;
1454            }
1455            $mock_reflection = new SimpleReflection($this->mock_base);
1456            if (in_array($method, $mock_reflection->getMethods())) {
1457                continue;
1458            }
1459            $code .= "    " . $this->reflection->getSignature($method) . " {\n";
1460            $code .= "        \$args = func_get_args();\n";
1461            $code .= "        \$result = &\$this->mock->invoke(\"$method\", \$args);\n";
1462            $code .= "        return \$result;\n";
1463            $code .= "    }\n";
1464        }
1465        return $code;
1466    }
1467
1468    /**
1469     *    Tests to see if a special PHP method is about to
1470     *    be stubbed by mistake.
1471     *    @param string $method    Method name.
1472     *    @return boolean          True if special.
1473     */
1474    protected function isConstructor($method) {
1475        return in_array(
1476                strtolower($method),
1477                array('__construct', '__destruct'));
1478    }
1479
1480    /**
1481     *    Creates a list of mocked methods for error checking.
1482     *    @param array $methods       Mocked methods.
1483     *    @return string              Code for a method list.
1484     */
1485    protected function addMethodList($methods) {
1486        return "    protected \$mocked_methods = array('" .
1487                implode("', '", array_map('strtolower', $methods)) .
1488                "');\n";
1489    }
1490
1491    /**
1492     *    Creates code to abandon the expectation if not mocked.
1493     *    @param string $alias       Parameter name of method name.
1494     *    @return string             Code for bail out.
1495     */
1496    protected function bailOutIfNotMocked($alias) {
1497        $code  = "        if (! in_array(strtolower($alias), \$this->mocked_methods)) {\n";
1498        $code .= "            trigger_error(\"Method [$alias] is not mocked\");\n";
1499        $code .= "            \$null = null;\n";
1500        $code .= "            return \$null;\n";
1501        $code .= "        }\n";
1502        return $code;
1503    }
1504
1505    /**
1506     *    Creates source code for chaining to the composited
1507     *    mock object.
1508     *    @return string           Code for mock set up.
1509     */
1510    protected function chainMockReturns() {
1511        $code  = "    function returns(\$method, \$value, \$args = false) {\n";
1512        $code .= $this->bailOutIfNotMocked("\$method");
1513        $code .= "        \$this->mock->returns(\$method, \$value, \$args);\n";
1514        $code .= "    }\n";
1515        $code .= "    function returnsAt(\$timing, \$method, \$value, \$args = false) {\n";
1516        $code .= $this->bailOutIfNotMocked("\$method");
1517        $code .= "        \$this->mock->returnsAt(\$timing, \$method, \$value, \$args);\n";
1518        $code .= "    }\n";
1519        $code .= "    function returnsByValue(\$method, \$value, \$args = false) {\n";
1520        $code .= $this->bailOutIfNotMocked("\$method");
1521        $code .= "        \$this->mock->setReturnValue(\$method, \$value, \$args);\n";
1522        $code .= "    }\n";
1523        $code .= "    function returnsByValueAt(\$timing, \$method, \$value, \$args = false) {\n";
1524        $code .= $this->bailOutIfNotMocked("\$method");
1525        $code .= "        \$this->mock->setReturnValueAt(\$timing, \$method, \$value, \$args);\n";
1526        $code .= "    }\n";
1527        $code .= "    function returnsByReference(\$method, &\$ref, \$args = false) {\n";
1528        $code .= $this->bailOutIfNotMocked("\$method");
1529        $code .= "        \$this->mock->setReturnReference(\$method, \$ref, \$args);\n";
1530        $code .= "    }\n";
1531        $code .= "    function returnsByReferenceAt(\$timing, \$method, &\$ref, \$args = false) {\n";
1532        $code .= $this->bailOutIfNotMocked("\$method");
1533        $code .= "        \$this->mock->setReturnReferenceAt(\$timing, \$method, \$ref, \$args);\n";
1534        $code .= "    }\n";
1535        $code .= "    function setReturnValue(\$method, \$value, \$args = false) {\n";
1536        $code .= $this->bailOutIfNotMocked("\$method");
1537        $code .= "        \$this->mock->setReturnValue(\$method, \$value, \$args);\n";
1538        $code .= "    }\n";
1539        $code .= "    function setReturnValueAt(\$timing, \$method, \$value, \$args = false) {\n";
1540        $code .= $this->bailOutIfNotMocked("\$method");
1541        $code .= "        \$this->mock->setReturnValueAt(\$timing, \$method, \$value, \$args);\n";
1542        $code .= "    }\n";
1543        $code .= "    function setReturnReference(\$method, &\$ref, \$args = false) {\n";
1544        $code .= $this->bailOutIfNotMocked("\$method");
1545        $code .= "        \$this->mock->setReturnReference(\$method, \$ref, \$args);\n";
1546        $code .= "    }\n";
1547        $code .= "    function setReturnReferenceAt(\$timing, \$method, &\$ref, \$args = false) {\n";
1548        $code .= $this->bailOutIfNotMocked("\$method");
1549        $code .= "        \$this->mock->setReturnReferenceAt(\$timing, \$method, \$ref, \$args);\n";
1550        $code .= "    }\n";
1551        return $code;
1552    }
1553
1554    /**
1555     *    Creates source code for chaining to an aggregated
1556     *    mock object.
1557     *    @return string                 Code for expectations.
1558     */
1559    protected function chainMockExpectations() {
1560        $code  = "    function expect(\$method, \$args = false, \$msg = '%s') {\n";
1561        $code .= $this->bailOutIfNotMocked("\$method");
1562        $code .= "        \$this->mock->expect(\$method, \$args, \$msg);\n";
1563        $code .= "    }\n";
1564        $code .= "    function expectAt(\$timing, \$method, \$args = false, \$msg = '%s') {\n";
1565        $code .= $this->bailOutIfNotMocked("\$method");
1566        $code .= "        \$this->mock->expectAt(\$timing, \$method, \$args, \$msg);\n";
1567        $code .= "    }\n";
1568        $code .= "    function expectCallCount(\$method, \$count) {\n";
1569        $code .= $this->bailOutIfNotMocked("\$method");
1570        $code .= "        \$this->mock->expectCallCount(\$method, \$count, \$msg = '%s');\n";
1571        $code .= "    }\n";
1572        $code .= "    function expectMaximumCallCount(\$method, \$count, \$msg = '%s') {\n";
1573        $code .= $this->bailOutIfNotMocked("\$method");
1574        $code .= "        \$this->mock->expectMaximumCallCount(\$method, \$count, \$msg = '%s');\n";
1575        $code .= "    }\n";
1576        $code .= "    function expectMinimumCallCount(\$method, \$count, \$msg = '%s') {\n";
1577        $code .= $this->bailOutIfNotMocked("\$method");
1578        $code .= "        \$this->mock->expectMinimumCallCount(\$method, \$count, \$msg = '%s');\n";
1579        $code .= "    }\n";
1580        $code .= "    function expectNever(\$method) {\n";
1581        $code .= $this->bailOutIfNotMocked("\$method");
1582        $code .= "        \$this->mock->expectNever(\$method);\n";
1583        $code .= "    }\n";
1584        $code .= "    function expectOnce(\$method, \$args = false, \$msg = '%s') {\n";
1585        $code .= $this->bailOutIfNotMocked("\$method");
1586        $code .= "        \$this->mock->expectOnce(\$method, \$args, \$msg);\n";
1587        $code .= "    }\n";
1588        $code .= "    function expectAtLeastOnce(\$method, \$args = false, \$msg = '%s') {\n";
1589        $code .= $this->bailOutIfNotMocked("\$method");
1590        $code .= "        \$this->mock->expectAtLeastOnce(\$method, \$args, \$msg);\n";
1591        $code .= "    }\n";
1592        return $code;
1593    }
1594
1595    /**
1596     *    Adds code for chaining the throw methods.
1597     *    @return string           Code for chains.
1598     */
1599    protected function chainThrowMethods() {
1600        $code  = "    function throwOn(\$method, \$exception = false, \$args = false) {\n";
1601        $code .= $this->bailOutIfNotMocked("\$method");
1602        $code .= "        \$this->mock->throwOn(\$method, \$exception, \$args);\n";
1603        $code .= "    }\n";
1604        $code .= "    function throwAt(\$timing, \$method, \$exception = false, \$args = false) {\n";
1605        $code .= $this->bailOutIfNotMocked("\$method");
1606        $code .= "        \$this->mock->throwAt(\$timing, \$method, \$exception, \$args);\n";
1607        $code .= "    }\n";
1608        $code .= "    function errorOn(\$method, \$error = 'A mock error', \$args = false, \$severity = E_USER_ERROR) {\n";
1609        $code .= $this->bailOutIfNotMocked("\$method");
1610        $code .= "        \$this->mock->errorOn(\$method, \$error, \$args, \$severity);\n";
1611        $code .= "    }\n";
1612        $code .= "    function errorAt(\$timing, \$method, \$error = 'A mock error', \$args = false, \$severity = E_USER_ERROR) {\n";
1613        $code .= $this->bailOutIfNotMocked("\$method");
1614        $code .= "        \$this->mock->errorAt(\$timing, \$method, \$error, \$args, \$severity);\n";
1615        $code .= "    }\n";
1616        return $code;
1617    }
1618
1619    /**
1620     *    Creates source code to override a list of methods
1621     *    with mock versions.
1622     *    @param array $methods    Methods to be overridden
1623     *                             with mock versions.
1624     *    @return string           Code for overridden chains.
1625     */
1626    protected function overrideMethods($methods) {
1627        $code = "";
1628        foreach ($methods as $method) {
1629            if ($this->isConstructor($method)) {
1630                continue;
1631            }
1632            $code .= "    " . $this->reflection->getSignature($method) . " {\n";
1633            $code .= "        \$args = func_get_args();\n";
1634            $code .= "        \$result = &\$this->mock->invoke(\"$method\", \$args);\n";
1635            $code .= "        return \$result;\n";
1636            $code .= "    }\n";
1637        }
1638        return $code;
1639    }
1640}
1641?>