1<?php
2/**
3 *  base include file for SimpleTest
4 *  @package    SimpleTest
5 *  @subpackage UnitTester
6 *  @version    $Id$
7 */
8
9/**#@+
10 *  include other SimpleTest class files
11 */
12require_once(dirname(__FILE__) . '/test_case.php');
13require_once(dirname(__FILE__) . '/dumper.php');
14/**#@-*/
15
16/**
17 *    Standard unit test class for day to day testing
18 *    of PHP code XP style. Adds some useful standard
19 *    assertions.
20 *    @package  SimpleTest
21 *    @subpackage   UnitTester
22 */
23class UnitTestCase extends SimpleTestCase {
24
25    /**
26     *    Creates an empty test case. Should be subclassed
27     *    with test methods for a functional test case.
28     *    @param string $label     Name of test case. Will use
29     *                             the class name if none specified.
30     *    @access public
31     */
32    function __construct($label = false) {
33        if (! $label) {
34            $label = get_class($this);
35        }
36        parent::__construct($label);
37    }
38
39    /**
40     *    Called from within the test methods to register
41     *    passes and failures.
42     *    @param boolean $result    Pass on true.
43     *    @param string $message    Message to display describing
44     *                              the test state.
45     *    @return boolean           True on pass
46     *    @access public
47     */
48    function assertTrue($result, $message = '%s') {
49        return $this->assert(new TrueExpectation(), $result, $message);
50    }
51
52    /**
53     *    Will be true on false and vice versa. False
54     *    is the PHP definition of false, so that null,
55     *    empty strings, zero and an empty array all count
56     *    as false.
57     *    @param boolean $result    Pass on false.
58     *    @param string $message    Message to display.
59     *    @return boolean           True on pass
60     *    @access public
61     */
62    function assertFalse($result, $message = '%s') {
63        return $this->assert(new FalseExpectation(), $result, $message);
64    }
65
66    /**
67     *    Will be true if the value is null.
68     *    @param null $value       Supposedly null value.
69     *    @param string $message   Message to display.
70     *    @return boolean                        True on pass
71     *    @access public
72     */
73    function assertNull($value, $message = '%s') {
74        $dumper = new SimpleDumper();
75        $message = sprintf(
76                $message,
77                '[' . $dumper->describeValue($value) . '] should be null');
78        return $this->assertTrue(! isset($value), $message);
79    }
80
81    /**
82     *    Will be true if the value is set.
83     *    @param mixed $value           Supposedly set value.
84     *    @param string $message        Message to display.
85     *    @return boolean               True on pass.
86     *    @access public
87     */
88    function assertNotNull($value, $message = '%s') {
89        $dumper = new SimpleDumper();
90        $message = sprintf(
91                $message,
92                '[' . $dumper->describeValue($value) . '] should not be null');
93        return $this->assertTrue(isset($value), $message);
94    }
95
96    /**
97     *    Type and class test. Will pass if class
98     *    matches the type name or is a subclass or
99     *    if not an object, but the type is correct.
100     *    @param mixed $object         Object to test.
101     *    @param string $type          Type name as string.
102     *    @param string $message       Message to display.
103     *    @return boolean              True on pass.
104     *    @access public
105     */
106    function assertIsA($object, $type, $message = '%s') {
107        return $this->assert(
108                new IsAExpectation($type),
109                $object,
110                $message);
111    }
112
113    /**
114     *    Type and class mismatch test. Will pass if class
115     *    name or underling type does not match the one
116     *    specified.
117     *    @param mixed $object         Object to test.
118     *    @param string $type          Type name as string.
119     *    @param string $message       Message to display.
120     *    @return boolean              True on pass.
121     *    @access public
122     */
123    function assertNotA($object, $type, $message = '%s') {
124        return $this->assert(
125                new NotAExpectation($type),
126                $object,
127                $message);
128    }
129
130    /**
131     *    Will trigger a pass if the two parameters have
132     *    the same value only. Otherwise a fail.
133     *    @param mixed $first          Value to compare.
134     *    @param mixed $second         Value to compare.
135     *    @param string $message       Message to display.
136     *    @return boolean              True on pass
137     *    @access public
138     */
139    function assertEqual($first, $second, $message = '%s') {
140        return $this->assert(
141                new EqualExpectation($first),
142                $second,
143                $message);
144    }
145
146    /**
147     *    Will trigger a pass if the two parameters have
148     *    a different value. Otherwise a fail.
149     *    @param mixed $first           Value to compare.
150     *    @param mixed $second          Value to compare.
151     *    @param string $message        Message to display.
152     *    @return boolean               True on pass
153     *    @access public
154     */
155    function assertNotEqual($first, $second, $message = '%s') {
156        return $this->assert(
157                new NotEqualExpectation($first),
158                $second,
159                $message);
160    }
161
162    /**
163     *    Will trigger a pass if the if the first parameter
164     *    is near enough to the second by the margin.
165     *    @param mixed $first          Value to compare.
166     *    @param mixed $second         Value to compare.
167     *    @param mixed $margin         Fuzziness of match.
168     *    @param string $message       Message to display.
169     *    @return boolean              True on pass
170     *    @access public
171     */
172    function assertWithinMargin($first, $second, $margin, $message = '%s') {
173        return $this->assert(
174                new WithinMarginExpectation($first, $margin),
175                $second,
176                $message);
177    }
178
179    /**
180     *    Will trigger a pass if the two parameters differ
181     *    by more than the margin.
182     *    @param mixed $first          Value to compare.
183     *    @param mixed $second         Value to compare.
184     *    @param mixed $margin         Fuzziness of match.
185     *    @param string $message       Message to display.
186     *    @return boolean              True on pass
187     *    @access public
188     */
189    function assertOutsideMargin($first, $second, $margin, $message = '%s') {
190        return $this->assert(
191                new OutsideMarginExpectation($first, $margin),
192                $second,
193                $message);
194    }
195
196    /**
197     *    Will trigger a pass if the two parameters have
198     *    the same value and same type. Otherwise a fail.
199     *    @param mixed $first           Value to compare.
200     *    @param mixed $second          Value to compare.
201     *    @param string $message        Message to display.
202     *    @return boolean               True on pass
203     *    @access public
204     */
205    function assertIdentical($first, $second, $message = '%s') {
206        return $this->assert(
207                new IdenticalExpectation($first),
208                $second,
209                $message);
210    }
211
212    /**
213     *    Will trigger a pass if the two parameters have
214     *    the different value or different type.
215     *    @param mixed $first           Value to compare.
216     *    @param mixed $second          Value to compare.
217     *    @param string $message        Message to display.
218     *    @return boolean               True on pass
219     *    @access public
220     */
221    function assertNotIdentical($first, $second, $message = '%s') {
222        return $this->assert(
223                new NotIdenticalExpectation($first),
224                $second,
225                $message);
226    }
227
228    /**
229     *    Will trigger a pass if both parameters refer
230     *    to the same object or value. Fail otherwise.
231     *    This will cause problems testing objects under
232     *    E_STRICT.
233     *    TODO: Replace with expectation.
234     *    @param mixed $first           Reference to check.
235     *    @param mixed $second          Hopefully the same variable.
236     *    @param string $message        Message to display.
237     *    @return boolean               True on pass
238     *    @access public
239     */
240    function assertReference(&$first, &$second, $message = '%s') {
241        $dumper = new SimpleDumper();
242        $message = sprintf(
243                $message,
244                '[' . $dumper->describeValue($first) .
245                        '] and [' . $dumper->describeValue($second) .
246                        '] should reference the same object');
247        return $this->assertTrue(
248                SimpleTestCompatibility::isReference($first, $second),
249                $message);
250    }
251
252    /**
253     *    Will trigger a pass if both parameters refer
254     *    to the same object. Fail otherwise. This has
255     *    the same semantics at the PHPUnit assertSame.
256     *    That is, if values are passed in it has roughly
257     *    the same affect as assertIdentical.
258     *    TODO: Replace with expectation.
259     *    @param mixed $first           Object reference to check.
260     *    @param mixed $second          Hopefully the same object.
261     *    @param string $message        Message to display.
262     *    @return boolean               True on pass
263     *    @access public
264     */
265    function assertSame($first, $second, $message = '%s') {
266        $dumper = new SimpleDumper();
267        $message = sprintf(
268                $message,
269                '[' . $dumper->describeValue($first) .
270                        '] and [' . $dumper->describeValue($second) .
271                        '] should reference the same object');
272        return $this->assertTrue($first === $second, $message);
273    }
274
275    /**
276     *    Will trigger a pass if both parameters refer
277     *    to different objects. Fail otherwise. The objects
278     *    have to be identical though.
279     *    @param mixed $first           Object reference to check.
280     *    @param mixed $second          Hopefully not the same object.
281     *    @param string $message        Message to display.
282     *    @return boolean               True on pass
283     *    @access public
284     */
285    function assertClone($first, $second, $message = '%s') {
286        $dumper = new SimpleDumper();
287        $message = sprintf(
288                $message,
289                '[' . $dumper->describeValue($first) .
290                        '] and [' . $dumper->describeValue($second) .
291                        '] should not be the same object');
292        $identical = new IdenticalExpectation($first);
293        return $this->assertTrue(
294                $identical->test($second) && ! ($first === $second),
295                $message);
296    }
297
298    /**
299     *    Will trigger a pass if both parameters refer
300     *    to different variables. Fail otherwise. The objects
301     *    have to be identical references though.
302     *    This will fail under E_STRICT with objects. Use
303     *    assertClone() for this.
304     *    @param mixed $first           Object reference to check.
305     *    @param mixed $second          Hopefully not the same object.
306     *    @param string $message        Message to display.
307     *    @return boolean               True on pass
308     *    @access public
309     */
310    function assertCopy(&$first, &$second, $message = "%s") {
311        $dumper = new SimpleDumper();
312        $message = sprintf(
313                $message,
314                "[" . $dumper->describeValue($first) .
315                        "] and [" . $dumper->describeValue($second) .
316                        "] should not be the same object");
317        return $this->assertFalse(
318                SimpleTestCompatibility::isReference($first, $second),
319                $message);
320    }
321
322    /**
323     *    Will trigger a pass if the Perl regex pattern
324     *    is found in the subject. Fail otherwise.
325     *    @param string $pattern    Perl regex to look for including
326     *                              the regex delimiters.
327     *    @param string $subject    String to search in.
328     *    @param string $message    Message to display.
329     *    @return boolean           True on pass
330     *    @access public
331     */
332    function assertPattern($pattern, $subject, $message = '%s') {
333        return $this->assert(
334                new PatternExpectation($pattern),
335                $subject,
336                $message);
337    }
338
339    /**
340     *    Will trigger a pass if the perl regex pattern
341     *    is not present in subject. Fail if found.
342     *    @param string $pattern    Perl regex to look for including
343     *                              the regex delimiters.
344     *    @param string $subject    String to search in.
345     *    @param string $message    Message to display.
346     *    @return boolean           True on pass
347     *    @access public
348     */
349    function assertNoPattern($pattern, $subject, $message = '%s') {
350        return $this->assert(
351                new NoPatternExpectation($pattern),
352                $subject,
353                $message);
354    }
355
356    /**
357     *    Prepares for an error. If the error mismatches it
358     *    passes through, otherwise it is swallowed. Any
359     *    left over errors trigger failures.
360     *    @param SimpleExpectation/string $expected   The error to match.
361     *    @param string $message                      Message on failure.
362     *    @access public
363     */
364    function expectError($expected = false, $message = '%s') {
365        $queue = SimpleTest::getContext()->get('SimpleErrorQueue');
366        $queue->expectError($this->coerceExpectation($expected), $message);
367    }
368
369    /**
370     *    Prepares for an exception. If the error mismatches it
371     *    passes through, otherwise it is swallowed. Any
372     *    left over errors trigger failures.
373     *    @param SimpleExpectation/Exception $expected  The error to match.
374     *    @param string $message                        Message on failure.
375     *    @access public
376     */
377    function expectException($expected = false, $message = '%s') {
378        $queue = SimpleTest::getContext()->get('SimpleExceptionTrap');
379        $line = $this->getAssertionLine();
380        $queue->expectException($expected, $message . $line);
381    }
382
383    /**
384     *    Tells SimpleTest to ignore an upcoming exception as not relevant
385     *    to the current test. It doesn't affect the test, whether thrown or
386     *    not.
387     *    @param SimpleExpectation/Exception $ignored  The error to ignore.
388     *    @access public
389     */
390    function ignoreException($ignored = false) {
391        SimpleTest::getContext()->get('SimpleExceptionTrap')->ignoreException($ignored);
392    }
393
394    /**
395     *    Creates an equality expectation if the
396     *    object/value is not already some type
397     *    of expectation.
398     *    @param mixed $expected      Expected value.
399     *    @return SimpleExpectation   Expectation object.
400     *    @access private
401     */
402    protected function coerceExpectation($expected) {
403        if ($expected == false) {
404            return new TrueExpectation();
405        }
406        if (SimpleTestCompatibility::isA($expected, 'SimpleExpectation')) {
407            return $expected;
408        }
409        return new EqualExpectation(
410                is_string($expected) ? str_replace('%', '%%', $expected) : $expected);
411    }
412}
413?>