Spike PHPCoverage Details: test_case.php

Line #FrequencySource Line
1 <?php
2 /**
3  *  Base include file for SimpleTest
4  *  @package    SimpleTest
5  *  @subpackage UnitTester
6  *  @version    $Id: test_case.php 1672 2008-03-02 04:47:34Z edwardzyang $
7  */
8 
9 /**#@+
10  * Includes SimpleTest files and defined the root constant
11  * for dependent libraries.
12  */
13 require_once(dirname(__FILE__) . '/invoker.php');
14 require_once(dirname(__FILE__) . '/errors.php');
15 require_once(dirname(__FILE__) . '/compatibility.php');
16 require_once(dirname(__FILE__) . '/scorer.php');
17 require_once(dirname(__FILE__) . '/expectation.php');
18 require_once(dirname(__FILE__) . '/dumper.php');
19 require_once(dirname(__FILE__) . '/simpletest.php');
20 if (version_compare(phpversion(), '5') >= 0) {
21     require_once(dirname(__FILE__) . '/exceptions.php');
22     require_once(dirname(__FILE__) . '/reflection_php5.php');
23 } else {
24     require_once(dirname(__FILE__) . '/reflection_php4.php');
25 }
26 if (! defined('SIMPLE_TEST')) {
27     /**
28      * @ignore
29      */
30     define('SIMPLE_TEST', dirname(__FILE__) . DIRECTORY_SEPARATOR);
31 }
32 /**#@-*/
33 
34 /**
35  *    Basic test case. This is the smallest unit of a test
36  *    suite. It searches for
37  *    all methods that start with the the string "test" and
38  *    runs them. Working test cases extend this class.
39  *    @package      SimpleTest
40  *    @subpackage   UnitTester
41  */
42 class SimpleTestCase {
43     var $_label = false;
44     var $_reporter;
45     var $_observers;
46     var $_should_skip = false;
47 
48     /**
49      *    Sets up the test with no display.
50      *    @param string $label    If no test name is given then
51      *                            the class name is used.
52      *    @access public
53      */
54     function SimpleTestCase($label = false) {
551        if ($label) {
561            $this->_label = $label;
57         }
58     }
59 
60     /**
61      *    Accessor for the test name for subclasses.
62      *    @return string           Name of the test.
63      *    @access public
64      */
65     function getLabel() {
661        return $this->_label ? $this->_label : get_class($this);
67     }
68 
69     /**
70      *    This is a placeholder for skipping tests. In this
71      *    method you place skipIf() and skipUnless() calls to
72      *    set the skipping state.
73      *    @access public
74      */
75     function skip() {
76     }
77 
78     /**
79      *    Will issue a message to the reporter and tell the test
80      *    case to skip if the incoming flag is true.
81      *    @param string $should_skip    Condition causing the tests to be skipped.
82      *    @param string $message        Text of skip condition.
83      *    @access public
84      */
85     function skipIf($should_skip, $message = '%s') {
86         if ($should_skip && ! $this->_should_skip) {
87             $this->_should_skip = true;
88             $message = sprintf($message, 'Skipping [' . get_class($this) . ']');
89             $this->_reporter->paintSkip($message . $this->getAssertionLine());
90         }
91     }
92 
93     /**
94      *    Will issue a message to the reporter and tell the test
95      *    case to skip if the incoming flag is false.
96      *    @param string $shouldnt_skip  Condition causing the tests to be run.
97      *    @param string $message        Text of skip condition.
98      *    @access public
99      */
100     function skipUnless($shouldnt_skip, $message = false) {
101         $this->skipIf(! $shouldnt_skip, $message);
102     }
103 
104     /**
105      *    Used to invoke the single tests.
106      *    @return SimpleInvoker        Individual test runner.
107      *    @access public
108      */
109     function &createInvoker() {
1101        $invoker = &new SimpleErrorTrappingInvoker(new SimpleInvoker($this));
1111        if (version_compare(phpversion(), '5') >= 0) {
1121            $invoker = &new SimpleExceptionTrappingInvoker($invoker);
113         }
1141        return $invoker;
115     }
116 
117     /**
118      *    Uses reflection to run every method within itself
119      *    starting with the string "test" unless a method
120      *    is specified.
121      *    @param SimpleReporter $reporter    Current test reporter.
122      *    @return boolean                    True if all tests passed.
123      *    @access public
124      */
125     function run(&$reporter) {
1261        $context = &SimpleTest::getContext();
1271        $context->setTest($this);
1281        $context->setReporter($reporter);
1291        $this->_reporter = &$reporter;
1301        $started = false;
1311        foreach ($this->getTests() as $method) {
1321            if ($reporter->shouldInvoke($this->getLabel(), $method)) {
1331                $this->skip();
1341                if ($this->_should_skip) {
135                     break;
136                 }
1371                if (! $started) {
1381                    $reporter->paintCaseStart($this->getLabel());
1391                    $started = true;
140                 }
1411                $invoker = &$this->_reporter->createInvoker($this->createInvoker());
1421                $invoker->before($method);
1431                $invoker->invoke($method);
1441                $invoker->after($method);
145             }
146         }
1471        if ($started) {
1481            $reporter->paintCaseEnd($this->getLabel());
149         }
1501        unset($this->_reporter);
1511        return $reporter->getStatus();
152     }
153 
154     /**
155      *    Gets a list of test names. Normally that will
156      *    be all internal methods that start with the
157      *    name "test". This method should be overridden
158      *    if you want a different rule.
159      *    @return array        List of test names.
160      *    @access public
161      */
162     function getTests() {
163         $methods = array();
1641        foreach (get_class_methods(get_class($this)) as $method) {
1651            if ($this->_isTest($method)) {
1661                $methods[] = $method;
167             }
168         }
1691        return $methods;
170     }
171 
172     /**
173      *    Tests to see if the method is a test that should
174      *    be run. Currently any method that starts with 'test'
175      *    is a candidate unless it is the constructor.
176      *    @param string $method        Method name to try.
177      *    @return boolean              True if test method.
178      *    @access protected
179      */
180     function _isTest($method) {
1811        if (strtolower(substr($method, 0, 4)) == 'test') {
1821            return ! SimpleTestCompatibility::isA($this, strtolower($method));
183         }
1841        return false;
185     }
186 
187     /**
188      *    Announces the start of the test.
189      *    @param string $method    Test method just started.
190      *    @access public
191      */
192     function before($method) {
1931        $this->_reporter->paintMethodStart($method);
1941        $this->_observers = array();
195     }
196 
197     /**
198      *    Sets up unit test wide variables at the start
199      *    of each test method. To be overridden in
200      *    actual user test cases.
201      *    @access public
202      */
203     function setUp() {
204     }
205 
206     /**
207      *    Clears the data set in the setUp() method call.
208      *    To be overridden by the user in actual user test cases.
209      *    @access public
210      */
211     function tearDown() {
212     }
213 
214     /**
215      *    Announces the end of the test. Includes private clean up.
216      *    @param string $method    Test method just finished.
217      *    @access public
218      */
219     function after($method) {
2201        for ($i = 0; $i < count($this->_observers); $i++) {
221             $this->_observers[$i]->atTestEnd($method, $this);
222         }
2231        $this->_reporter->paintMethodEnd($method);
224     }
225 
226     /**
227      *    Sets up an observer for the test end.
228      *    @param object $observer    Must have atTestEnd()
229      *                               method.
230      *    @access public
231      */
232     function tell(&$observer) {
233         $this->_observers[] = &$observer;
234     }
235 
236     /**
237      *    @deprecated
238      */
239     function pass($message = "Pass") {
2401        if (! isset($this->_reporter)) {
241             trigger_error('Can only make assertions within test methods');
242         }
2431        $this->_reporter->paintPass(
244                 $message . $this->getAssertionLine());
2451        return true;
246     }
247 
248     /**
249      *    Sends a fail event with a message.
250      *    @param string $message        Message to send.
251      *    @access public
252      */
253     function fail($message = "Fail") {
254         if (! isset($this->_reporter)) {
255             trigger_error('Can only make assertions within test methods');
256         }
257         $this->_reporter->paintFail(
258                 $message . $this->getAssertionLine());
259         return false;
260     }
261 
262     /**
263      *    Formats a PHP error and dispatches it to the
264      *    reporter.
265      *    @param integer $severity  PHP error code.
266      *    @param string $message    Text of error.
267      *    @param string $file       File error occoured in.
268      *    @param integer $line      Line number of error.
269      *    @access public
270      */
271     function error($severity, $message, $file, $line) {
272         if (! isset($this->_reporter)) {
273             trigger_error('Can only make assertions within test methods');
274         }
275         $this->_reporter->paintError(
276                 "Unexpected PHP error [$message] severity [$severity] in [$file line $line]");
277     }
278 
279     /**
280      *    Formats an exception and dispatches it to the
281      *    reporter.
282      *    @param Exception $exception    Object thrown.
283      *    @access public
284      */
285     function exception($exception) {
286         $this->_reporter->paintException($exception);
287     }
288 
289     /**
290      *    @deprecated
291      */
292     function signal($type, &$payload) {
293         if (! isset($this->_reporter)) {
294             trigger_error('Can only make assertions within test methods');
295         }
296         $this->_reporter->paintSignal($type, $payload);
297     }
298 
299     /**
300      *    Runs an expectation directly, for extending the
301      *    tests with new expectation classes.
302      *    @param SimpleExpectation $expectation  Expectation subclass.
303      *    @param mixed $compare               Value to compare.
304      *    @param string $message                 Message to display.
305      *    @return boolean                        True on pass
306      *    @access public
307      */
308     function assert(&$expectation, $compare, $message = '%s') {
3091        if ($expectation->test($compare)) {
3101            return $this->pass(sprintf(
311                     $message,
312                     $expectation->overlayMessage($compare, $this->_reporter->getDumper())));
313         } else {
314             return $this->fail(sprintf(
315                     $message,
316                     $expectation->overlayMessage($compare, $this->_reporter->getDumper())));
317         }
318     }
319 
320     /**
321      *    @deprecated
322      */
323     function assertExpectation(&$expectation, $compare, $message = '%s') {
324         return $this->assert($expectation, $compare, $message);
325     }
326 
327     /**
328      *    Uses a stack trace to find the line of an assertion.
329      *    @return string           Line number of first assert*
330      *                             method embedded in format string.
331      *    @access public
332      */
333     function getAssertionLine() {
3341        $trace = new SimpleStackTrace(array('assert', 'expect', 'pass', 'fail', 'skip'));
3351        return $trace->traceMethod();
336     }
337 
338     /**
339      *    Sends a formatted dump of a variable to the
340      *    test suite for those emergency debugging
341      *    situations.
342      *    @param mixed $variable    Variable to display.
343      *    @param string $message    Message to display.
344      *    @return mixed             The original variable.
345      *    @access public
346      */
347     function dump($variable, $message = false) {
348         $dumper = $this->_reporter->getDumper();
349         $formatted = $dumper->dump($variable);
350         if ($message) {
351             $formatted = $message . "\n" . $formatted;
352         }
353         $this->_reporter->paintFormattedMessage($formatted);
354         return $variable;
355     }
356 
357     /**
358      *    @deprecated
359      */
360     function sendMessage($message) {
361         $this->_reporter->PaintMessage($message);
362     }
363 
364     /**
365      *    Accessor for the number of subtests.
366      *    @return integer           Number of test cases.
367      *    @access public
368      *    @static
369      */
370     function getSize() {
3711        return 1;
372     }
373 }
374 
375 /**
376  *  Helps to extract test cases automatically from a file.
377  */
378 class SimpleFileLoader {
379 
380     /**
381      *    Builds a test suite from a library of test cases.
382      *    The new suite is composed into this one.
383      *    @param string $test_file        File name of library with
384      *                                    test case classes.
385      *    @return TestSuite               The new test suite.
386      *    @access public
387      */
388     function &load($test_file) {
389         $existing_classes = get_declared_classes();
390         include_once($test_file);
391         $file_classes = array_diff(get_declared_classes(), $existing_classes);
392 
393         // Lookup classnames from file contents, in case the file may have been included before:
394         if( empty($file_classes) ) {
395             preg_match_all( '~^\s*class\s+(\w+)(\s+(extends|implements)\s+\w+)*\s*\{~mi', file_get_contents($test_file), $matches );
396             $file_classes = $matches[1];
397         }
398 
399         $classes = $this->selectRunnableTests($file_classes);
400         $suite = &$this->createSuiteFromClasses($test_file, $classes);
401         return $suite;
402     }
403 
404     /**
405      *    Calculates the incoming test cases. Skips abstract
406      *    and ignored classes.
407      *    @param array $candidates   Candidate classes.
408      *    @return array              New classes which are test
409      *                               cases that shouldn't be ignored.
410      *    @access public
411      */
412     function selectRunnableTests($candidates) {
413         $classes = array();
414         foreach ($candidates as $class) {
415             if (TestSuite::getBaseTestCase($class)) {
416                 $reflection = new SimpleReflection($class);
417                 if ($reflection->isAbstract()) {
418                     SimpleTest::ignore($class);
419                 } else {
420                     $classes[] = $class;
421                 }
422             }
423         }
424         return $classes;
425     }
426 
427     /**
428      *    Builds a test suite from a class list.
429      *    @param string $title       Title of new group.
430      *    @param array $classes      Test classes.
431      *    @return TestSuite          Group loaded with the new
432      *                               test cases.
433      *    @access public
434      */
435     function &createSuiteFromClasses($title, $classes) {
436         if (count($classes) == 0) {
437             $suite = &new BadTestSuite($title, "No runnable test cases in [$title]");
438             return $suite;
439         }
440         SimpleTest::ignoreParentsIfIgnored($classes);
441         $suite = &new TestSuite($title);
442         foreach ($classes as $class) {
443             if (! SimpleTest::isIgnored($class)) {
444                 $suite->addTestClass($class);
445             }
446         }
447         return $suite;
448     }
449 }
450 
451 /**
452  *    This is a composite test class for combining
453  *    test cases and other RunnableTest classes into
454  *    a group test.
455  *    @package      SimpleTest
456  *    @subpackage   UnitTester
457  */
458 class TestSuite {
459     var $_label;
460     var $_test_cases;
461 
462     /**
463      *    Sets the name of the test suite.
464      *    @param string $label    Name sent at the start and end
465      *                            of the test.
466      *    @access public
467      */
468     function TestSuite($label = false) {
4691        $this->_label = $label;
4701        $this->_test_cases = array();
471     }
472 
473     /**
474      *    Accessor for the test name for subclasses. If the suite
475      *    wraps a single test case the label defaults to the name of that test.
476      *    @return string           Name of the test.
477      *    @access public
478      */
479     function getLabel() {
4801        if (! $this->_label) {
481             return ($this->getSize() == 1) ?
482                     get_class($this->_test_cases[0]) : get_class($this);
483         } else {
4841            return $this->_label;
485         }
486     }
487 
488     /**
489      *    @deprecated
490      */
491     function addTestCase(&$test_case) {
4921        $this->_test_cases[] = &$test_case;
493     }
494 
495     /**
496      *    @deprecated
497      */
498     function addTestClass($class) {
499         if (TestSuite::getBaseTestCase($class) == 'testsuite') {
500             $this->_test_cases[] = &new $class();
501         } else {
502             $this->_test_cases[] = $class;
503         }
504     }
505 
506     /**
507      *    Adds a test into the suite by instance or class. The class will
508      *    be instantiated if it's a test suite.
509      *    @param SimpleTestCase $test_case  Suite or individual test
510      *                                      case implementing the
511      *                                      runnable test interface.
512      *    @access public
513      */
514     function add(&$test_case) {
515         if (! is_string($test_case)) {
516             $this->_test_cases[] = &$test_case;
517         } elseif (TestSuite::getBaseTestCase($class) == 'testsuite') {
518             $this->_test_cases[] = &new $class();
519         } else {
520             $this->_test_cases[] = $class;
521         }
522     }
523 
524     /**
525      *    @deprecated
526      */
527     function addTestFile($test_file) {
528         $this->addFile($test_file);
529     }
530 
531     /**
532      *    Builds a test suite from a library of test cases.
533      *    The new suite is composed into this one.
534      *    @param string $test_file        File name of library with
535      *                                    test case classes.
536      *    @access public
537      */
538     function addFile($test_file) {
539         $extractor = new SimpleFileLoader();
540         $this->add($extractor->load($test_file));
541     }
542 
543     /**
544      *    Delegates to a visiting collector to add test
545      *    files.
546      *    @param string $path                  Path to scan from.
547      *    @param SimpleCollector $collector    Directory scanner.
548      *    @access public
549      */
550     function collect($path, &$collector) {
551         $collector->collect($this, $path);
552     }
553 
554     /**
555      *    Invokes run() on all of the held test cases, instantiating
556      *    them if necessary.
557      *    @param SimpleReporter $reporter    Current test reporter.
558      *    @access public
559      */
560     function run(&$reporter) {
5611        $reporter->paintGroupStart($this->getLabel(), $this->getSize());
5621        for ($i = 0, $count = count($this->_test_cases); $i < $count; $i++) {
5631            if (is_string($this->_test_cases[$i])) {
564                 $class = $this->_test_cases[$i];
565                 $test = &new $class();
566                 $test->run($reporter);
567                 unset($test);
568             } else {
5691                $this->_test_cases[$i]->run($reporter);
570             }
571         }
5721        $reporter->paintGroupEnd($this->getLabel());
5731        return $reporter->getStatus();
574     }
575 
576     /**
577      *    Number of contained test cases.
578      *    @return integer     Total count of cases in the group.
579      *    @access public
580      */
581     function getSize() {
5821        $count = 0;
5831        foreach ($this->_test_cases as $case) {
5841            if (is_string($case)) {
585                 $count++;
586             } else {
5871                $count += $case->getSize();
588             }
589         }
5901        return $count;
591     }
592 
593     /**
594      *    Test to see if a class is derived from the
595      *    SimpleTestCase class.
596      *    @param string $class     Class name.
597      *    @access public
598      *    @static
599      */
600     function getBaseTestCase($class) {
601         while ($class = get_parent_class($class)) {
602             $class = strtolower($class);
603             if ($class == 'simpletestcase' || $class == 'testsuite') {
604                 return $class;
605             }
606         }
607         return false;
608     }
609 }
610 
611 /**
612  *    @package      SimpleTest
613  *    @subpackage   UnitTester
614  *    @deprecated
615  */
616 class GroupTest extends TestSuite { }
617 
618 /**
619  *    This is a failing group test for when a test suite hasn't
620  *    loaded properly.
621  *    @package      SimpleTest
622  *    @subpackage   UnitTester
623  */
624 class BadTestSuite {
625     var $_label;
626     var $_error;
627 
628     /**
629      *    Sets the name of the test suite and error message.
630      *    @param string $label    Name sent at the start and end
631      *                            of the test.
632      *    @access public
633      */
634     function BadTestSuite($label, $error) {
635         $this->_label = $label;
636         $this->_error = $error;
637     }
638 
639     /**
640      *    Accessor for the test name for subclasses.
641      *    @return string           Name of the test.
642      *    @access public
643      */
644     function getLabel() {
645         return $this->_label;
646     }
647 
648     /**
649      *    Sends a single error to the reporter.
650      *    @param SimpleReporter $reporter    Current test reporter.
651      *    @access public
652      */
653     function run(&$reporter) {
654         $reporter->paintGroupStart($this->getLabel(), $this->getSize());
655         $reporter->paintFail('Bad TestSuite [' . $this->getLabel() .
656                 '] with error [' . $this->_error . ']');
657         $reporter->paintGroupEnd($this->getLabel());
658         return $reporter->getStatus();
659     }
660 
661     /**
662      *    Number of contained test cases. Always zero.
663      *    @return integer     Total count of cases in the group.
664      *    @access public
665      */
666     function getSize() {
667         return 0;
668     }
669 }
670 
671 /**
672  *    @package      SimpleTest
673  *    @subpackage   UnitTester
674  *    @deprecated
675  */
676 class BadGroupTest extends BadTestSuite { }
677 ?>