Хранилища Subversion ant

Редакция

Редакция 69 | Только различия | Не учитывать пробелы | Содержимое файла | Авторство | Последнее изменение | Открыть журнал | RSS

Редакция 69 Редакция 94
1
<?php
1
<?php
2
/**
2
/**
3
 * PEAR_Command_Test (run-tests)
3
 * PEAR_Command_Test (run-tests)
4
 *
4
 *
5
 * PHP versions 4 and 5
5
 * PHP versions 4 and 5
6
 *
6
 *
7
 * LICENSE: This source file is subject to version 3.0 of the PHP license
7
 * LICENSE: This source file is subject to version 3.0 of the PHP license
8
 * that is available through the world-wide-web at the following URI:
8
 * that is available through the world-wide-web at the following URI:
9
 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
9
 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
10
 * the PHP License and are unable to obtain it through the web, please
10
 * the PHP License and are unable to obtain it through the web, please
11
 * send a note to license@php.net so we can mail you a copy immediately.
11
 * send a note to license@php.net so we can mail you a copy immediately.
12
 *
12
 *
13
 * @category   pear
13
 * @category   pear
14
 * @package    PEAR
14
 * @package    PEAR
15
 * @author     Stig Bakken <ssb@php.net>
15
 * @author     Stig Bakken <ssb@php.net>
16
 * @author     Martin Jansen <mj@php.net>
16
 * @author     Martin Jansen <mj@php.net>
17
 * @author     Greg Beaver <cellog@php.net>
17
 * @author     Greg Beaver <cellog@php.net>
18
 * @copyright  1997-2008 The PHP Group
18
 * @copyright  1997-2008 The PHP Group
19
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
19
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
20
 * @version    CVS: $Id: Test.php,v 1.27 2008/01/03 20:26:36 cellog Exp $
20
 * @version    CVS: $Id: Test.php,v 1.27 2008/01/03 20:26:36 cellog Exp $
21
 * @link       http://pear.php.net/package/PEAR
21
 * @link       http://pear.php.net/package/PEAR
22
 * @since      File available since Release 0.1
22
 * @since      File available since Release 0.1
23
 */
23
 */
24
24
25
/**
25
/**
26
 * base class
26
 * base class
27
 */
27
 */
28
require_once 'PEAR/Command/Common.php';
28
require_once 'PEAR/Command/Common.php';
29
29
30
/**
30
/**
31
 * PEAR commands for login/logout
31
 * PEAR commands for login/logout
32
 *
32
 *
33
 * @category   pear
33
 * @category   pear
34
 * @package    PEAR
34
 * @package    PEAR
35
 * @author     Stig Bakken <ssb@php.net>
35
 * @author     Stig Bakken <ssb@php.net>
36
 * @author     Martin Jansen <mj@php.net>
36
 * @author     Martin Jansen <mj@php.net>
37
 * @author     Greg Beaver <cellog@php.net>
37
 * @author     Greg Beaver <cellog@php.net>
38
 * @copyright  1997-2008 The PHP Group
38
 * @copyright  1997-2008 The PHP Group
39
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
39
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
40
 * @version    Release: 1.7.2
40
 * @version    Release: 1.7.2
41
 * @link       http://pear.php.net/package/PEAR
41
 * @link       http://pear.php.net/package/PEAR
42
 * @since      Class available since Release 0.1
42
 * @since      Class available since Release 0.1
43
 */
43
 */
44
44
45
class PEAR_Command_Test extends PEAR_Command_Common
45
class PEAR_Command_Test extends PEAR_Command_Common
46
{
46
{
47
    // {{{ properties
47
    // {{{ properties
48
48
49
    var $commands = array(
49
    var $commands = array(
50
        'run-tests' => array(
50
        'run-tests' => array(
51
            'summary' => 'Run Regression Tests',
51
            'summary' => 'Run Regression Tests',
52
            'function' => 'doRunTests',
52
            'function' => 'doRunTests',
53
            'shortcut' => 'rt',
53
            'shortcut' => 'rt',
54
            'options' => array(
54
            'options' => array(
55
                'recur' => array(
55
                'recur' => array(
56
                    'shortopt' => 'r',
56
                    'shortopt' => 'r',
57
                    'doc' => 'Run tests in child directories, recursively.  4 dirs deep maximum',
57
                    'doc' => 'Run tests in child directories, recursively.  4 dirs deep maximum',
58
                ),
58
                ),
59
                'ini' => array(
59
                'ini' => array(
60
                    'shortopt' => 'i',
60
                    'shortopt' => 'i',
61
                    'doc' => 'actual string of settings to pass to php in format " -d setting=blah"',
61
                    'doc' => 'actual string of settings to pass to php in format " -d setting=blah"',
62
                    'arg' => 'SETTINGS'
62
                    'arg' => 'SETTINGS'
63
                ),
63
                ),
64
                'realtimelog' => array(
64
                'realtimelog' => array(
65
                    'shortopt' => 'l',
65
                    'shortopt' => 'l',
66
                    'doc' => 'Log test runs/results as they are run',
66
                    'doc' => 'Log test runs/results as they are run',
67
                ),
67
                ),
68
                'quiet' => array(
68
                'quiet' => array(
69
                    'shortopt' => 'q',
69
                    'shortopt' => 'q',
70
                    'doc' => 'Only display detail for failed tests',
70
                    'doc' => 'Only display detail for failed tests',
71
                ),
71
                ),
72
                'simple' => array(
72
                'simple' => array(
73
                    'shortopt' => 's',
73
                    'shortopt' => 's',
74
                    'doc' => 'Display simple output for all tests',
74
                    'doc' => 'Display simple output for all tests',
75
                ),
75
                ),
76
                'package' => array(
76
                'package' => array(
77
                    'shortopt' => 'p',
77
                    'shortopt' => 'p',
78
                    'doc' => 'Treat parameters as installed packages from which to run tests',
78
                    'doc' => 'Treat parameters as installed packages from which to run tests',
79
                ),
79
                ),
80
                'phpunit' => array(
80
                'phpunit' => array(
81
                    'shortopt' => 'u',
81
                    'shortopt' => 'u',
82
                    'doc' => 'Search parameters for AllTests.php, and use that to run phpunit-based tests
82
                    'doc' => 'Search parameters for AllTests.php, and use that to run phpunit-based tests
83
If none is found, all .phpt tests will be tried instead.',
83
If none is found, all .phpt tests will be tried instead.',
84
                ),
84
                ),
85
                'tapoutput' => array(
85
                'tapoutput' => array(
86
                    'shortopt' => 't',
86
                    'shortopt' => 't',
87
                    'doc' => 'Output run-tests.log in TAP-compliant format',
87
                    'doc' => 'Output run-tests.log in TAP-compliant format',
88
                ),
88
                ),
89
                'cgi' => array(
89
                'cgi' => array(
90
                    'shortopt' => 'c',
90
                    'shortopt' => 'c',
91
                    'doc' => 'CGI php executable (needed for tests with POST/GET section)',
91
                    'doc' => 'CGI php executable (needed for tests with POST/GET section)',
92
                    'arg' => 'PHPCGI',
92
                    'arg' => 'PHPCGI',
93
                ),
93
                ),
94
                'coverage' => array(
94
                'coverage' => array(
95
                    'shortopt' => 'x',
95
                    'shortopt' => 'x',
96
                    'doc'      => 'Generate a code coverage report (requires Xdebug 2.0.0+)',
96
                    'doc'      => 'Generate a code coverage report (requires Xdebug 2.0.0+)',
97
                ),
97
                ),
98
            ),
98
            ),
99
            'doc' => '[testfile|dir ...]
99
            'doc' => '[testfile|dir ...]
100
Run regression tests with PHP\'s regression testing script (run-tests.php).',
100
Run regression tests with PHP\'s regression testing script (run-tests.php).',
101
            ),
101
            ),
102
        );
102
        );
103
103
104
    var $output;
104
    var $output;
105
105
106
    // }}}
106
    // }}}
107
    // {{{ constructor
107
    // {{{ constructor
108
108
109
    /**
109
    /**
110
     * PEAR_Command_Test constructor.
110
     * PEAR_Command_Test constructor.
111
     *
111
     *
112
     * @access public
112
     * @access public
113
     */
113
     */
114
    function PEAR_Command_Test(&$ui, &$config)
114
    function PEAR_Command_Test(&$ui, &$config)
115
    {
115
    {
116
        parent::PEAR_Command_Common($ui, $config);
116
        parent::PEAR_Command_Common($ui, $config);
117
    }
117
    }
118
118
119
    // }}}
119
    // }}}
120
    // {{{ doRunTests()
120
    // {{{ doRunTests()
121
121
122
    function doRunTests($command, $options, $params)
122
    function doRunTests($command, $options, $params)
123
    {
123
    {
124
        if (isset($options['phpunit']) && isset($options['tapoutput'])) {
124
        if (isset($options['phpunit']) && isset($options['tapoutput'])) {
125
            return $this->raiseError('ERROR: cannot use both --phpunit and --tapoutput at the same time');
125
            return $this->raiseError('ERROR: cannot use both --phpunit and --tapoutput at the same time');
126
        }
126
        }
127
        require_once 'PEAR/Common.php';
127
        require_once 'PEAR/Common.php';
128
        require_once 'System.php';
128
        require_once 'System.php';
129
        $log = new PEAR_Common;
129
        $log = new PEAR_Common;
130
        $log->ui = &$this->ui; // slightly hacky, but it will work
130
        $log->ui = &$this->ui; // slightly hacky, but it will work
131
        $tests = array();
131
        $tests = array();
132
        $depth = isset($options['recur']) ? 4 : 1;
132
        $depth = isset($options['recur']) ? 4 : 1;
133
133
134
        if (!count($params)) {
134
        if (!count($params)) {
135
            $params[] = '.';
135
            $params[] = '.';
136
        }
136
        }
137
        if (isset($options['package'])) {
137
        if (isset($options['package'])) {
138
            $oldparams = $params;
138
            $oldparams = $params;
139
            $params = array();
139
            $params = array();
140
            $reg = &$this->config->getRegistry();
140
            $reg = &$this->config->getRegistry();
141
            foreach ($oldparams as $param) {
141
            foreach ($oldparams as $param) {
142
                $pname = $reg->parsePackageName($param, $this->config->get('default_channel'));
142
                $pname = $reg->parsePackageName($param, $this->config->get('default_channel'));
143
                if (PEAR::isError($pname)) {
143
                if (PEAR::isError($pname)) {
144
                    return $this->raiseError($pname);
144
                    return $this->raiseError($pname);
145
                }
145
                }
146
146
147
                $package = &$reg->getPackage($pname['package'], $pname['channel']);
147
                $package = &$reg->getPackage($pname['package'], $pname['channel']);
148
                if (!$package) {
148
                if (!$package) {
149
                    return PEAR::raiseError('Unknown package "' .
149
                    return PEAR::raiseError('Unknown package "' .
150
                        $reg->parsedPackageNameToString($pname) . '"');
150
                        $reg->parsedPackageNameToString($pname) . '"');
151
                }
151
                }
152
152
153
                $filelist = $package->getFilelist();
153
                $filelist = $package->getFilelist();
154
                foreach ($filelist as $name => $atts) {
154
                foreach ($filelist as $name => $atts) {
155
                    if (isset($atts['role']) && $atts['role'] != 'test') {
155
                    if (isset($atts['role']) && $atts['role'] != 'test') {
156
                        continue;
156
                        continue;
157
                    }
157
                    }
158
158
159
                    if (isset($options['phpunit']) && preg_match('/AllTests\.php\\z/i', $name)) {
159
                    if (isset($options['phpunit']) && preg_match('/AllTests\.php\\z/i', $name)) {
160
                        $params[] = $atts['installed_as'];
160
                        $params[] = $atts['installed_as'];
161
                        continue;
161
                        continue;
162
                    } elseif (!preg_match('/\.phpt\\z/', $name)) {
162
                    } elseif (!preg_match('/\.phpt\\z/', $name)) {
163
                        continue;
163
                        continue;
164
                    }
164
                    }
165
                    $params[] = $atts['installed_as'];
165
                    $params[] = $atts['installed_as'];
166
                }
166
                }
167
            }
167
            }
168
        }
168
        }
169
169
170
        foreach ($params as $p) {
170
        foreach ($params as $p) {
171
            if (is_dir($p)) {
171
            if (is_dir($p)) {
172
                if (isset($options['phpunit'])) {
172
                if (isset($options['phpunit'])) {
173
                    $dir = System::find(array($p, '-type', 'f',
173
                    $dir = System::find(array($p, '-type', 'f',
174
                                                '-maxdepth', $depth,
174
                                                '-maxdepth', $depth,
175
                                                '-name', 'AllTests.php'));
175
                                                '-name', 'AllTests.php'));
176
                    if (count($dir)) {
176
                    if (count($dir)) {
177
                        foreach ($dir as $p) {
177
                        foreach ($dir as $p) {
178
                            $p = realpath($p);
178
                            $p = realpath($p);
179
                            if (!count($tests) ||
179
                            if (!count($tests) ||
180
                                  (count($tests) && strlen($p) < strlen($tests[0]))) {
180
                                  (count($tests) && strlen($p) < strlen($tests[0]))) {
181
                                // this is in a higher-level directory, use this one instead.
181
                                // this is in a higher-level directory, use this one instead.
182
                                $tests = array($p);
182
                                $tests = array($p);
183
                            }
183
                            }
184
                        }
184
                        }
185
                    }
185
                    }
186
                    continue;
186
                    continue;
187
                }
187
                }
188
                $dir = System::find(array($p, '-type', 'f',
188
                $dir = System::find(array($p, '-type', 'f',
189
                                            '-maxdepth', $depth,
189
                                            '-maxdepth', $depth,
190
                                            '-name', '*.phpt'));
190
                                            '-name', '*.phpt'));
191
                $tests = array_merge($tests, $dir);
191
                $tests = array_merge($tests, $dir);
192
            } else {
192
            } else {
193
                if (isset($options['phpunit'])) {
193
                if (isset($options['phpunit'])) {
194
                    if (preg_match('/AllTests\.php\\z/i', $p)) {
194
                    if (preg_match('/AllTests\.php\\z/i', $p)) {
195
                        $p = realpath($p);
195
                        $p = realpath($p);
196
                        if (!count($tests) ||
196
                        if (!count($tests) ||
197
                              (count($tests) && strlen($p) < strlen($tests[0]))) {
197
                              (count($tests) && strlen($p) < strlen($tests[0]))) {
198
                            // this is in a higher-level directory, use this one instead.
198
                            // this is in a higher-level directory, use this one instead.
199
                            $tests = array($p);
199
                            $tests = array($p);
200
                        }
200
                        }
201
                    }
201
                    }
202
                    continue;
202
                    continue;
203
                }
203
                }
204
204
205
                if (file_exists($p) && preg_match('/\.phpt$/', $p)) {
205
                if (file_exists($p) && preg_match('/\.phpt$/', $p)) {
206
                    $tests[] = $p;
206
                    $tests[] = $p;
207
                    continue;
207
                    continue;
208
                }
208
                }
209
209
210
                if (!preg_match('/\.phpt\\z/', $p)) {
210
                if (!preg_match('/\.phpt\\z/', $p)) {
211
                    $p .= '.phpt';
211
                    $p .= '.phpt';
212
                }
212
                }
213
                $dir = System::find(array(dirname($p), '-type', 'f',
213
                $dir = System::find(array(dirname($p), '-type', 'f',
214
                                            '-maxdepth', $depth,
214
                                            '-maxdepth', $depth,
215
                                            '-name', $p));
215
                                            '-name', $p));
216
                $tests = array_merge($tests, $dir);
216
                $tests = array_merge($tests, $dir);
217
            }
217
            }
218
        }
218
        }
219
219
220
        $ini_settings = '';
220
        $ini_settings = '';
221
        if (isset($options['ini'])) {
221
        if (isset($options['ini'])) {
222
            $ini_settings .= $options['ini'];
222
            $ini_settings .= $options['ini'];
223
        }
223
        }
224
224
225
        if (isset($_ENV['TEST_PHP_INCLUDE_PATH'])) {
225
        if (isset($_ENV['TEST_PHP_INCLUDE_PATH'])) {
226
            $ini_settings .= " -d include_path={$_ENV['TEST_PHP_INCLUDE_PATH']}";
226
            $ini_settings .= " -d include_path={$_ENV['TEST_PHP_INCLUDE_PATH']}";
227
        }
227
        }
228
228
229
        if ($ini_settings) {
229
        if ($ini_settings) {
230
            $this->ui->outputData('Using INI settings: "' . $ini_settings . '"');
230
            $this->ui->outputData('Using INI settings: "' . $ini_settings . '"');
231
        }
231
        }
232
        $skipped = $passed = $failed = array();
232
        $skipped = $passed = $failed = array();
233
        $tests_count = count($tests);
233
        $tests_count = count($tests);
234
        $this->ui->outputData('Running ' . $tests_count . ' tests', $command);
234
        $this->ui->outputData('Running ' . $tests_count . ' tests', $command);
235
        $start = time();
235
        $start = time();
236
        if (isset($options['realtimelog']) && file_exists('run-tests.log')) {
236
        if (isset($options['realtimelog']) && file_exists('run-tests.log')) {
237
            unlink('run-tests.log');
237
            unlink('run-tests.log');
238
        }
238
        }
239
239
240
        if (isset($options['tapoutput'])) {
240
        if (isset($options['tapoutput'])) {
241
            $tap = '1..' . $tests_count . "\n";
241
            $tap = '1..' . $tests_count . "\n";
242
        }
242
        }
243
243
244
        require_once 'PEAR/RunTest.php';
244
        require_once 'PEAR/RunTest.php';
245
        $run = new PEAR_RunTest($log, $options);
245
        $run = new PEAR_RunTest($log, $options);
246
        $run->tests_count = $tests_count;
246
        $run->tests_count = $tests_count;
247
247
248
        if (isset($options['coverage']) && extension_loaded('xdebug')){
248
        if (isset($options['coverage']) && extension_loaded('xdebug')){
249
            $run->xdebug_loaded = true;
249
            $run->xdebug_loaded = true;
250
        } else {
250
        } else {
251
            $run->xdebug_loaded = false;
251
            $run->xdebug_loaded = false;
252
        }
252
        }
253
253
254
        $j = $i = 1;
254
        $j = $i = 1;
255
        foreach ($tests as $t) {
255
        foreach ($tests as $t) {
256
            if (isset($options['realtimelog'])) {
256
            if (isset($options['realtimelog'])) {
257
                $fp = @fopen('run-tests.log', 'a');
257
                $fp = @fopen('run-tests.log', 'a');
258
                if ($fp) {
258
                if ($fp) {
259
                    fwrite($fp, "Running test [$i / $tests_count] $t...");
259
                    fwrite($fp, "Running test [$i / $tests_count] $t...");
260
                    fclose($fp);
260
                    fclose($fp);
261
                }
261
                }
262
            }
262
            }
263
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
263
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
264
            if (isset($options['phpunit'])) {
264
            if (isset($options['phpunit'])) {
265
                $result = $run->runPHPUnit($t, $ini_settings);
265
                $result = $run->runPHPUnit($t, $ini_settings);
266
            } else {
266
            } else {
267
                $result = $run->run($t, $ini_settings, $j);
267
                $result = $run->run($t, $ini_settings, $j);
268
            }
268
            }
269
            PEAR::staticPopErrorHandling();
269
            PEAR::staticPopErrorHandling();
270
            if (PEAR::isError($result)) {
270
            if (PEAR::isError($result)) {
271
                $this->ui->log($result->getMessage());
271
                $this->ui->log($result->getMessage());
272
                continue;
272
                continue;
273
            }
273
            }
274
274
275
            if (isset($options['tapoutput'])) {
275
            if (isset($options['tapoutput'])) {
276
                $tap .= $result[0] . ' ' . $i . $result[1] . "\n";
276
                $tap .= $result[0] . ' ' . $i . $result[1] . "\n";
277
                continue;
277
                continue;
278
            }
278
            }
279
279
280
            if (isset($options['realtimelog'])) {
280
            if (isset($options['realtimelog'])) {
281
                $fp = @fopen('run-tests.log', 'a');
281
                $fp = @fopen('run-tests.log', 'a');
282
                if ($fp) {
282
                if ($fp) {
283
                    fwrite($fp, "$result\n");
283
                    fwrite($fp, "$result\n");
284
                    fclose($fp);
284
                    fclose($fp);
285
                }
285
                }
286
            }
286
            }
287
           
287
           
288
            if ($result == 'FAILED') {
288
            if ($result == 'FAILED') {
289
                $failed[] = $t;
289
                $failed[] = $t;
290
            }
290
            }
291
            if ($result == 'PASSED') {
291
            if ($result == 'PASSED') {
292
                $passed[] = $t;
292
                $passed[] = $t;
293
            }
293
            }
294
            if ($result == 'SKIPPED') {
294
            if ($result == 'SKIPPED') {
295
                $skipped[] = $t;
295
                $skipped[] = $t;
296
            }
296
            }
297
           
297
           
298
            $j++;
298
            $j++;
299
        }
299
        }
300
300
301
        $total = date('i:s', time() - $start);
301
        $total = date('i:s', time() - $start);
302
        if (isset($options['tapoutput'])) {
302
        if (isset($options['tapoutput'])) {
303
            $fp = @fopen('run-tests.log', 'w');
303
            $fp = @fopen('run-tests.log', 'w');
304
            if ($fp) {
304
            if ($fp) {
305
                fwrite($fp, $tap, strlen($tap));
305
                fwrite($fp, $tap, strlen($tap));
306
                fclose($fp);
306
                fclose($fp);
307
                $this->ui->outputData('wrote TAP-format log to "' .realpath('run-tests.log') .
307
                $this->ui->outputData('wrote TAP-format log to "' .realpath('run-tests.log') .
308
                    '"', $command);
308
                    '"', $command);
309
            }
309
            }
310
        } else {
310
        } else {
311
            if (count($failed)) {
311
            if (count($failed)) {
312
                $output = "TOTAL TIME: $total\n";
312
                $output = "TOTAL TIME: $total\n";
313
                $output .= count($passed) . " PASSED TESTS\n";
313
                $output .= count($passed) . " PASSED TESTS\n";
314
                $output .= count($skipped) . " SKIPPED TESTS\n";
314
                $output .= count($skipped) . " SKIPPED TESTS\n";
315
                        $output .= count($failed) . " FAILED TESTS:\n";
315
                        $output .= count($failed) . " FAILED TESTS:\n";
316
                foreach ($failed as $failure) {
316
                foreach ($failed as $failure) {
317
                        $output .= $failure . "\n";
317
                        $output .= $failure . "\n";
318
                }
318
                }
319
               
319
               
320
                $mode = isset($options['realtimelog']) ? 'a' : 'w';
320
                $mode = isset($options['realtimelog']) ? 'a' : 'w';
321
                $fp   = @fopen('run-tests.log', $mode);
321
                $fp   = @fopen('run-tests.log', $mode);
322
322
323
                if ($fp) {
323
                if ($fp) {
324
                    fwrite($fp, $output, strlen($output));
324
                    fwrite($fp, $output, strlen($output));
325
                    fclose($fp);
325
                    fclose($fp);
326
                    $this->ui->outputData('wrote log to "' . realpath('run-tests.log') . '"', $command);
326
                    $this->ui->outputData('wrote log to "' . realpath('run-tests.log') . '"', $command);
327
                }
327
                }
328
            } elseif (file_exists('run-tests.log') && !is_dir('run-tests.log')) {
328
            } elseif (file_exists('run-tests.log') && !is_dir('run-tests.log')) {
329
                @unlink('run-tests.log');
329
                @unlink('run-tests.log');
330
            }
330
            }
331
        }
331
        }
332
        $this->ui->outputData('TOTAL TIME: ' . $total);
332
        $this->ui->outputData('TOTAL TIME: ' . $total);
333
        $this->ui->outputData(count($passed) . ' PASSED TESTS', $command);
333
        $this->ui->outputData(count($passed) . ' PASSED TESTS', $command);
334
        $this->ui->outputData(count($skipped) . ' SKIPPED TESTS', $command);
334
        $this->ui->outputData(count($skipped) . ' SKIPPED TESTS', $command);
335
        if (count($failed)) {
335
        if (count($failed)) {
336
                $this->ui->outputData(count($failed) . ' FAILED TESTS:', $command);
336
                $this->ui->outputData(count($failed) . ' FAILED TESTS:', $command);
337
                foreach ($failed as $failure) {
337
                foreach ($failed as $failure) {
338
                        $this->ui->outputData($failure, $command);
338
                        $this->ui->outputData($failure, $command);
339
                }
339
                }
340
        }
340
        }
341
341
342
        return true;
342
        return true;
343
    }
343
    }
344
    // }}}
344
    // }}}
345
}
345
}