Source for file mock_objects.php

Documentation is available at mock_objects.php

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

Documentation generated on Sun, 31 Oct 2010 16:31:48 -0500 by phpDocumentor 1.4.3