Subversion-Projekte lars-tiefland.prado

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
    /**
3
     *	Base include file for SimpleTest
4
     *	@package	SimpleTest
5
     *	@subpackage	UnitTester
6
     *	@version	$Id: simple_test.php 1532 2006-12-01 12:28:55Z xue $
7
     */
8
 
9
    /**#@+
10
     * Includes SimpleTest files and defined the root constant
11
     * for dependent libraries.
12
     */
13
    require_once(dirname(__FILE__) . '/errors.php');
14
    require_once(dirname(__FILE__) . '/options.php');
15
    require_once(dirname(__FILE__) . '/runner.php');
16
    require_once(dirname(__FILE__) . '/scorer.php');
17
    require_once(dirname(__FILE__) . '/expectation.php');
18
    require_once(dirname(__FILE__) . '/dumper.php');
19
    if (! defined('SIMPLE_TEST')) {
20
        define('SIMPLE_TEST', dirname(__FILE__) . '/');
21
    }
22
    /**#@-*/
23
 
24
    /**
25
     *    Basic test case. This is the smallest unit of a test
26
     *    suite. It searches for
27
     *    all methods that start with the the string "test" and
28
     *    runs them. Working test cases extend this class.
29
	 *    @package		SimpleTest
30
	 *    @subpackage	UnitTester
31
     */
32
    class SimpleTestCase {
33
        protected $_label;
34
        protected $_runner;
35
 
36
        /**
37
         *    Sets up the test with no display.
38
         *    @param string $label    If no test name is given then
39
         *                            the class name is used.
40
         *    @access public
41
         */
42
        function SimpleTestCase($label = false) {
43
            $this->_label = $label ? $label : get_class($this);
44
            $this->_runner = false;
45
        }
46
 
47
        /**
48
         *    Accessor for the test name for subclasses.
49
         *    @return string           Name of the test.
50
         *    @access public
51
         */
52
        function getLabel() {
53
            return $this->_label;
54
        }
55
 
56
        /**
57
         *    Used to invoke the single tests.
58
         *    @return SimpleInvoker        Individual test runner.
59
         *    @access public
60
         */
61
        function createInvoker() {
62
            return new SimpleErrorTrappingInvoker(new SimpleInvoker($this));
63
        }
64
 
65
        /**
66
         *    Can modify the incoming reporter so as to run
67
         *    the tests differently. This version simply
68
         *    passes it straight through.
69
         *    @param SimpleReporter $reporter    Incoming observer.
70
         *    @return SimpleReporter
71
         *    @access protected
72
         */
73
        function _createRunner($reporter) {
74
            return new SimpleRunner($this, $reporter);
75
        }
76
 
77
        /**
78
         *    Uses reflection to run every method within itself
79
         *    starting with the string "test" unless a method
80
         *    is specified.
81
         *    @param SimpleReporter $reporter    Current test reporter.
82
         *    @access public
83
         */
84
        function run($reporter) {
85
            $reporter->paintCaseStart($this->getLabel());
86
            $this->_runner = $this->_createRunner($reporter);
87
            $this->_runner->run();
88
            $reporter->paintCaseEnd($this->getLabel());
89
            return $reporter->getStatus();
90
        }
91
 
92
        /**
93
         *    Sets up unit test wide variables at the start
94
         *    of each test method. To be overridden in
95
         *    actual user test cases.
96
         *    @access public
97
         */
98
        function setUp() {
99
        }
100
 
101
        /**
102
         *    Clears the data set in the setUp() method call.
103
         *    To be overridden by the user in actual user test cases.
104
         *    @access public
105
         */
106
        function tearDown() {
107
        }
108
 
109
        /**
110
         *    Sends a pass event with a message.
111
         *    @param string $message        Message to send.
112
         *    @access public
113
         */
114
        function pass($message = "Pass") {
115
            $this->_runner->paintPass($message . $this->getAssertionLine(' at line [%d]'));
116
        }
117
 
118
        /**
119
         *    Sends a fail event with a message.
120
         *    @param string $message        Message to send.
121
         *    @access public
122
         */
123
        function fail($message = "Fail") {
124
            $this->_runner->paintFail($message . $this->getAssertionLine(' at line [%d]'));
125
        }
126
 
127
        /**
128
         *    Formats a PHP error and dispatches it to the
129
         *    runner.
130
         *    @param integer $severity  PHP error code.
131
         *    @param string $message    Text of error.
132
         *    @param string $file       File error occoured in.
133
         *    @param integer $line      Line number of error.
134
         *    @param hash $globals      PHP super global arrays.
135
         *    @access public
136
         */
137
        function error($severity, $message, $file, $line, $globals) {
138
            $severity = SimpleErrorQueue::getSeverityAsString($severity);
139
            $this->_runner->paintError(
140
                    "Unexpected PHP error [$message] severity [$severity] in [$file] line [$line]");
141
        }
142
 
143
        /**
144
         *    Sends a user defined event to the test runner.
145
         *    This is for small scale extension where
146
         *    both the test case and either the runner or
147
         *    display are subclassed.
148
         *    @param string $type       Type of event.
149
         *    @param mixed $payload     Object or message to deliver.
150
         *    @access public
151
         */
152
        function signal($type, $payload) {
153
            $this->_runner->paintSignal($type, $payload);
154
        }
155
 
156
        /**
157
         *    Cancels any outstanding errors.
158
         *    @access public
159
         */
160
        function swallowErrors() {
161
            $queue = SimpleErrorQueue::instance();
162
            $queue->clear();
163
        }
164
 
165
        /**
166
         *    Runs an expectation directly, for extending the
167
         *    tests with new expectation classes.
168
         *    @param SimpleExpectation $expectation  Expectation subclass.
169
         *    @param mixed $test_value               Value to compare.
170
         *    @param string $message                 Message to display.
171
         *    @return boolean                        True on pass
172
         *    @access public
173
         */
174
        function assertExpectation($expectation, $test_value, $message = '%s') {
175
            return $this->assertTrue(
176
                    $expectation->test($test_value),
177
                    sprintf($message, $expectation->overlayMessage($test_value)));
178
        }
179
 
180
        /**
181
         *    Called from within the test methods to register
182
         *    passes and failures.
183
         *    @param boolean $result    Pass on true.
184
         *    @param string $message    Message to display describing
185
         *                              the test state.
186
         *    @return boolean           True on pass
187
         *    @access public
188
         */
189
        function assertTrue($result, $message = false) {
190
            if (! $message) {
191
                $message = 'True assertion got ' . ($result ? 'True' : 'False');
192
            }
193
            if ($result) {
194
                $this->pass($message);
195
                return true;
196
            } else {
197
                $this->fail($message);
198
                return false;
199
            }
200
        }
201
 
202
        /**
203
         *    Will be true on false and vice versa. False
204
         *    is the PHP definition of false, so that null,
205
         *    empty strings, zero and an empty array all count
206
         *    as false.
207
         *    @param boolean $result    Pass on false.
208
         *    @param string $message    Message to display.
209
         *    @return boolean           True on pass
210
         *    @access public
211
         */
212
        function assertFalse($result, $message = false) {
213
            if (! $message) {
214
                $message = 'False assertion got ' . ($result ? 'True' : 'False');
215
            }
216
            return $this->assertTrue(! $result, $message);
217
        }
218
 
219
        /**
220
         *    Uses a stack trace to find the line of an assertion.
221
         *    @param string $format    String formatting.
222
         *    @param array $stack      Stack frames top most first. Only
223
         *                             needed if not using the PHP
224
         *                             backtrace function.
225
         *    @return string           Line number of first assert*
226
         *                             method embedded in format string.
227
         *    @access public
228
         */
229
        function getAssertionLine($format = '%d', $stack = false) {
230
            if ($stack === false) {
231
                $stack = SimpleTestCompatibility::getStackTrace();
232
            }
233
            return SimpleDumper::getFormattedAssertionLine($stack, $format);
234
        }
235
 
236
        /**
237
         *    Sends a formatted dump of a variable to the
238
         *    test suite for those emergency debugging
239
         *    situations.
240
         *    @param mixed $variable    Variable to display.
241
         *    @param string $message    Message to display.
242
         *    @return mixed             The original variable.
243
         *    @access public
244
         */
245
        function dump($variable, $message = false) {
246
            $formatted = SimpleDumper::dump($variable);
247
            if ($message) {
248
                $formatted = $message . "\n" . $formatted;
249
            }
250
            $this->_runner->paintFormattedMessage($formatted);
251
            return $variable;
252
        }
253
 
254
        /**
255
         *    Dispatches a text message straight to the
256
         *    test suite. Useful for status bar displays.
257
         *    @param string $message        Message to show.
258
         *    @access public
259
         */
260
        function sendMessage($message) {
261
            $this->_runner->PaintMessage($message);
262
        }
263
 
264
        /**
265
         *    Accessor for the number of subtests.
266
         *    @return integer           Number of test cases.
267
         *    @access public
268
         *    @static
269
         */
270
        static function getSize() {
271
            return 1;
272
        }
273
    }
274
 
275
    /**
276
     *    This is a composite test class for combining
277
     *    test cases and other RunnableTest classes into
278
     *    a group test.
279
	 *    @package		SimpleTest
280
	 *    @subpackage	UnitTester
281
     */
282
    class GroupTest {
283
        protected $_label;
284
        protected $_test_cases;
285
        protected $_old_track_errors;
286
        protected $_xdebug_is_enabled;
287
 
288
        /**
289
         *    Sets the name of the test suite.
290
         *    @param string $label    Name sent at the start and end
291
         *                            of the test.
292
         *    @access public
293
         */
294
        function GroupTest($label) {
295
            $this->_label = $label;
296
            $this->_test_cases = array();
297
            $this->_old_track_errors = ini_get('track_errors');
298
            $this->_xdebug_is_enabled = function_exists('xdebug_is_enabled') ?
299
                    xdebug_is_enabled() : false;
300
        }
301
 
302
        /**
303
         *    Accessor for the test name for subclasses.
304
         *    @return string           Name of the test.
305
         *    @access public
306
         */
307
        function getLabel() {
308
            return $this->_label;
309
        }
310
 
311
        /**
312
         *    Adds a test into the suite. Can be either a group
313
         *    test or some other unit test.
314
         *    @param SimpleTestCase $test_case  Suite or individual test
315
         *                                      case implementing the
316
         *                                      runnable test interface.
317
         *    @access public
318
         */
319
        function addTestCase($test_case) {
320
            $this->_test_cases[] = $test_case;
321
        }
322
 
323
        /**
324
         *    Adds a test into the suite by class name. The class will
325
         *    be instantiated as needed.
326
         *    @param SimpleTestCase $test_case  Suite or individual test
327
         *                                      case implementing the
328
         *                                      runnable test interface.
329
         *    @access public
330
         */
331
        function addTestClass($class) {
332
            $this->_test_cases[] = $class;
333
        }
334
 
335
        /**
336
         *    Builds a group test from a library of test cases.
337
         *    The new group is composed into this one.
338
         *    @param string $test_file        File name of library with
339
         *                                    test case classes.
340
         *    @access public
341
         */
342
        function addTestFile($test_file) {
343
            $existing_classes = get_declared_classes();
344
            if ($error = $this->_requireWithError($test_file)) {
345
                $this->addTestCase(new BadGroupTest($test_file, $error));
346
                return;
347
            }
348
            $classes = $this->_selectRunnableTests($existing_classes, get_declared_classes());
349
            if (count($classes) == 0) {
350
                $this->addTestCase(new BadGroupTest($test_file, 'No new test cases'));
351
                return;
352
            }
353
            $this->addTestCase($this->_createGroupFromClasses($test_file, $classes));
354
        }
355
 
356
        /**
357
         *    Requires a source file recording any syntax errors.
358
         *    @param string $file        File name to require in.
359
         *    @return string/boolean     An error message on failure or false
360
         *                               if no errors.
361
         *    @access private
362
         */
363
        function _requireWithError($file) {
364
            $this->_enableErrorReporting();
365
            include($file);
366
            $error = isset($php_errormsg) ? $php_errormsg : false;
367
            $this->_disableErrorReporting();
368
            $self_inflicted = array(
369
                    'Assigning the return value of new by reference is deprecated',
370
                    'var: Deprecated. Please use the public/private/protected modifiers');
371
            if (in_array($error, $self_inflicted)) {
372
                return false;
373
            }
374
            return $error;
375
        }
376
 
377
        /**
378
         *    Sets up detection of parse errors. Note that XDebug
379
         *    interferes with this and has to be disabled. This is
380
         *    to make sure the correct error code is returned
381
         *    from unattended scripts.
382
         *    @access private
383
         */
384
        function _enableErrorReporting() {
385
            if ($this->_xdebug_is_enabled) {
386
                xdebug_disable();
387
            }
388
            ini_set('track_errors', true);
389
        }
390
 
391
        /**
392
         *    Resets detection of parse errors to their old values.
393
         *    This is to make sure the correct error code is returned
394
         *    from unattended scripts.
395
         *    @access private
396
         */
397
        function _disableErrorReporting() {
398
            ini_set('track_errors', $this->_old_track_errors);
399
            if ($this->_xdebug_is_enabled) {
400
                xdebug_enable();
401
            }
402
        }
403
 
404
        /**
405
         *    Calculates the incoming test cases from a before
406
         *    and after list of loaded classes.
407
         *    @param array $existing_classes   Classes before require().
408
         *    @param array $new_classes        Classes after require().
409
         *    @return array                    New classes which are test
410
         *                                     cases that shouldn't be ignored.
411
         *    @access private
412
         */
413
        function _selectRunnableTests($existing_classes, $new_classes) {
414
            $classes = array();
415
            foreach ($new_classes as $class) {
416
                if (in_array($class, $existing_classes)) {
417
                    continue;
418
                }
419
                if (! $this->_isTestCase($class)) {
420
                    continue;
421
                }
422
                $classes[] = $class;
423
            }
424
            return $classes;
425
        }
426
 
427
        /**
428
         *    Builds a group test from a class list.
429
         *    @param string $title       Title of new group.
430
         *    @param array $classes      Test classes.
431
         *    @return GroupTest          Group loaded with the new
432
         *                               test cases.
433
         *    @access private
434
         */
435
        function _createGroupFromClasses($title, $classes) {
436
            $group = new GroupTest($title);
437
            foreach ($classes as $class) {
438
                if (SimpleTestOptions::isIgnored($class)) {
439
                    continue;
440
                }
441
                $group->addTestClass($class);
442
            }
443
            return $group;
444
        }
445
 
446
        /**
447
         *    Test to see if a class is derived from the
448
         *    TestCase class.
449
         *    @param string $class            Class name.
450
         *    @access private
451
         */
452
        function _isTestCase($class) {
453
            while ($class = get_parent_class($class)) {
454
                $class = strtolower($class);
455
                if ($class == "simpletestcase" || $class == "grouptest") {
456
                    return true;
457
                }
458
            }
459
            return false;
460
        }
461
 
462
        /**
463
         *    Invokes run() on all of the held test cases, instantiating
464
         *    them if necessary.
465
         *    @param SimpleReporter $reporter    Current test reporter.
466
         *    @access public
467
         */
468
        function run($reporter) {
469
            $reporter->paintGroupStart($this->getLabel(), $this->getSize());
470
            for ($i = 0, $count = count($this->_test_cases); $i < $count; $i++) {
471
                if (is_string($this->_test_cases[$i])) {
472
                    $class = $this->_test_cases[$i];
473
                    $test = new $class();
474
                    $test->run($reporter);
475
                } else {
476
                    $this->_test_cases[$i]->run($reporter);
477
                }
478
            }
479
            $reporter->paintGroupEnd($this->getLabel());
480
            return $reporter->getStatus();
481
        }
482
 
483
        /**
484
         *    Number of contained test cases.
485
         *    @return integer     Total count of cases in the group.
486
         *    @access public
487
         */
488
        function getSize() {
489
            $count = 0;
490
            foreach ($this->_test_cases as $case) {
491
                if (is_string($case)) {
492
                    $count++;
493
                } else {
494
                    $count += $case->getSize();
495
                }
496
            }
497
            return $count;
498
        }
499
    }
500
 
501
    /**
502
     *    This is a failing group test for when a test suite hasn't
503
     *    loaded properly.
504
	 *    @package		SimpleTest
505
	 *    @subpackage	UnitTester
506
     */
507
    class BadGroupTest {
508
        protected $_label;
509
        protected $_error;
510
 
511
        /**
512
         *    Sets the name of the test suite and error message.
513
         *    @param string $label    Name sent at the start and end
514
         *                            of the test.
515
         *    @access public
516
         */
517
        function BadGroupTest($label, $error) {
518
            $this->_label = $label;
519
            $this->_error = $error;
520
        }
521
 
522
        /**
523
         *    Accessor for the test name for subclasses.
524
         *    @return string           Name of the test.
525
         *    @access public
526
         */
527
        function getLabel() {
528
            return $this->_label;
529
        }
530
 
531
        /**
532
         *    Sends a single error to the reporter.
533
         *    @param SimpleReporter $reporter    Current test reporter.
534
         *    @access public
535
         */
536
        function run($reporter) {
537
            $reporter->paintGroupStart($this->getLabel(), $this->getSize());
538
            $reporter->paintFail('Bad GroupTest [' . $this->getLabel() .
539
                    '] with error [' . $this->_error . ']');
540
            $reporter->paintGroupEnd($this->getLabel());
541
            return $reporter->getStatus();
542
        }
543
 
544
        /**
545
         *    Number of contained test cases. Always zero.
546
         *    @return integer     Total count of cases in the group.
547
         *    @access public
548
         */
549
        function getSize() {
550
            return 0;
551
        }
552
    }
553
?>