Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 331 lines (301 sloc) 9.468 kB
e7f3c31 @gwoo going lithium
gwoo authored
1 <?php
2 /**
3 * Lithium: the most rad php framework
4 *
1d68573 @davidpersson Happy 2015.
davidpersson authored
5 * @copyright Copyright 2015, Union of RAD (http://union-of-rad.org)
e7f3c31 @gwoo going lithium
gwoo authored
6 * @license http://opensource.org/licenses/bsd-license.php The BSD License
7 */
8
ad490d8 @gwoo singularizing some namespaces, merging 0.3-console and some other cha…
gwoo authored
9 namespace lithium\console\command;
e7f3c31 @gwoo going lithium
gwoo authored
10
690f4c3 @nateabele Fixing formatting in console classes. Adding test coverage for `Extra…
nateabele authored
11 use lithium\core\Libraries;
12 use lithium\test\Dispatcher;
2d52c6a @davidpersson Test command can now find and run test for a given file.
davidpersson authored
13 use lithium\test\Unit;
e7f3c31 @gwoo going lithium
gwoo authored
14
15 /**
7d2ddd8 @davidpersson Updating output of `Test` command.
davidpersson authored
16 * Runs a given set of tests and outputs the results.
14992e3 @davidpersson Adding reference to test namespace in `Test` command.
davidpersson authored
17 *
18 * @see lithium\test
e7f3c31 @gwoo going lithium
gwoo authored
19 */
20 class Test extends \lithium\console\Command {
21
22 /**
4e2771f @davidpersson Differ in exit codes.
davidpersson authored
23 * Used as the exit code for errors where no test was mapped to file.
24 */
25 const EXIT_NO_TEST = 4;
26
27 /**
de39498 @davidpersson Explain filters option for help command.
davidpersson authored
28 * List of filters to apply before/during/after test run, separated by commas.
e7f3c31 @gwoo going lithium
gwoo authored
29 *
2cba57b @gwoo moving help to a separate command. for example
gwoo authored
30 * For example:
fb1cb03 @davidpersson Fixing formatting of codeblocks.
davidpersson authored
31 * ```sh
7ccbecc @davidpersson API: `Test` command now takes a path to a directory/file with tests.
davidpersson authored
32 * lithium test lithium/tests/cases/core/ObjectTest.php --filters=Coverage
078c816 @davidpersson Documenting and fixing filters option for `Test` command.
davidpersson authored
33 * lithium test lithium/tests/cases/core/ObjectTest.php --filters=Coverage,Profiler
68fc84c @davidpersson Replace markdown curly braces with triple ticks and remove link hack.
davidpersson authored
34 * ```
7d2ddd8 @davidpersson Updating output of `Test` command.
davidpersson authored
35 *
078c816 @davidpersson Documenting and fixing filters option for `Test` command.
davidpersson authored
36 * @var string Name of a filter or a comma separated list of filter names. Builtin filters:
37 * - `Affected`: Adds tests to the run affected by the classes covered by current tests.
38 * - `Complexity`: Calculates the cyclomatic complexity of class methods, and shows
39 * worst-offenders and statistics.
40 * - `Coverage`: Runs code coverage analysis for the executed tests.
41 * - `Profiler`: Tracks timing and memory usage information for each test method.
e7f3c31 @gwoo going lithium
gwoo authored
42 */
078c816 @davidpersson Documenting and fixing filters option for `Test` command.
davidpersson authored
43 public $filters;
e7f3c31 @gwoo going lithium
gwoo authored
44
45 /**
2368ec1 @davidpersson Adding support for JSON output to console test command.
davidpersson authored
46 * Format to use for rendering results. Any other format than `txt` will
31525a3 @davidpersson Fixing typo.
davidpersson authored
47 * cause the command to enter quiet mode, surpressing headers and any other
2368ec1 @davidpersson Adding support for JSON output to console test command.
davidpersson authored
48 * decoration.
49 *
50 * @var string Either `txt` or `json`.
51 */
52 public $format = 'txt';
53
54 /**
17b4811 @davidpersson Updating test command to use new reporter.
davidpersson authored
55 * Enable verbose output especially for the `txt` format.
56 *
57 * @var boolean
58 */
59 public $verbose = false;
60
61 /**
198614a @davidpersson Typo.
davidpersson authored
62 * Enable plain mode to prevent any headers or similar decoration being output.
52fc3a6 @davidpersson Introducting plain mode for test command.
davidpersson authored
63 * Good for command calls embedded into other scripts.
64 *
65 * @var boolean
66 */
67 public $plain = false;
68
69 /**
ea0abeb @nateabele Refactoring the `Test` command to use typed output handlers, and cons…
nateabele authored
70 * An array of closures, mapped by type, which are set up to handle different test output
71 * formats.
72 *
73 * @var array
74 */
75 protected $_handlers = array();
76
77 /**
78 * Initializes the output handlers.
79 *
80 * @see lithium\console\command\Test::$_handlers
81 * @return void
82 */
83 protected function _init() {
84 parent::_init();
952d9a5 @davidpersson Initial implementation of console test progress.
davidpersson authored
85 $command = $this;
ea0abeb @nateabele Refactoring the `Test` command to use typed output handlers, and cons…
nateabele authored
86
952d9a5 @davidpersson Initial implementation of console test progress.
davidpersson authored
87 $this->_handlers += array(
88 'txt' => function($runner, $path) use ($command) {
52fc3a6 @davidpersson Introducting plain mode for test command.
davidpersson authored
89 if (!$command->plain) {
90 $command->header('Test');
91 $command->out(null, 1);
92 }
17b4811 @davidpersson Updating test command to use new reporter.
davidpersson authored
93 $colorize = function($result) {
94 switch (trim($result)) {
95 case '.':
96 return $result;
97 case 'pass':
98 return "{:green}{$result}{:end}";
99 case 'F':
100 case 'fail':
101 return "{:red}{$result}{:end}";
b4e0099 @davidpersson Adding exception to the expected types.
davidpersson authored
102 case 'E':
103 case 'exception':
104 return "{:purple}{$result}{:end}";
17b4811 @davidpersson Updating test command to use new reporter.
davidpersson authored
105 case 'S':
106 case 'skip':
107 return "{:cyan}{$result}{:end}";
108 default:
109 return "{:yellow}{$result}{:end}";
952d9a5 @davidpersson Initial implementation of console test progress.
davidpersson authored
110 }
17b4811 @davidpersson Updating test command to use new reporter.
davidpersson authored
111 };
112
113 if ($command->verbose) {
114 $reporter = function($result) use ($command, $colorize) {
115 $command->out(sprintf(
feec693 @davidpersson Fixing printf template for possible strings.
davidpersson authored
116 '[%s] on line %4s in %s::%s()',
b4e0099 @davidpersson Adding exception to the expected types.
davidpersson authored
117 $colorize(sprintf('%9s', $result['result'])),
553eac5 @davidpersson Adding line number in verbose test command output.
davidpersson authored
118 isset($result['line']) ? $result['line'] : '??',
138e8e3 @davidpersson Skips do not have class or method info.
davidpersson authored
119 isset($result['class']) ? $result['class'] : '??',
120 isset($result['method']) ? $result['method'] : '??'
17b4811 @davidpersson Updating test command to use new reporter.
davidpersson authored
121 ));
122 };
123 } else {
124 $i = 0;
125 $columns = 60;
126
127 $reporter = function($result) use ($command, &$i, $columns, $colorize) {
b4e0099 @davidpersson Adding exception to the expected types.
davidpersson authored
128 $shorten = array('fail', 'skip', 'exception');
17b4811 @davidpersson Updating test command to use new reporter.
davidpersson authored
129
78504c4 @jails Fix typo according to li3_quality.
jails authored
130 if ($result['result'] === 'pass') {
17b4811 @davidpersson Updating test command to use new reporter.
davidpersson authored
131 $symbol = '.';
132 } elseif (in_array($result['result'], $shorten)) {
133 $symbol = strtoupper($result['result'][0]);
134 } else {
135 $symbol = '?';
136 }
137 $command->out($colorize($symbol), false);
138
139 $i++;
140 if ($i % $columns === 0) {
141 $command->out();
142 }
143 };
144 }
145 $report = $runner(compact('reporter'));
7b7eebc @davidpersson Adding general OK/FAIL to end of test run.
davidpersson authored
146
52fc3a6 @davidpersson Introducting plain mode for test command.
davidpersson authored
147 if (!$command->plain) {
148 $stats = $report->stats();
149
150 $command->out(null, 2);
151 $command->out($report->render('result', $stats));
152 $command->out($report->render('errors', $stats));
ea0abeb @nateabele Refactoring the `Test` command to use typed output handlers, and cons…
nateabele authored
153
52fc3a6 @davidpersson Introducting plain mode for test command.
davidpersson authored
154 if ($command->verbose) {
155 $command->out($report->render('skips', $stats));
156 }
157
158 foreach ($report->filters() as $filter => $options) {
159 $data = $report->results['filters'][$filter];
160 $command->out($report->render($options['name'], compact('data')));
161 }
ea0abeb @nateabele Refactoring the `Test` command to use typed output handlers, and cons…
nateabele authored
162 }
163 return $report;
164 },
952d9a5 @davidpersson Initial implementation of console test progress.
davidpersson authored
165 'json' => function($runner, $path) use ($command) {
ea0abeb @nateabele Refactoring the `Test` command to use typed output handlers, and cons…
nateabele authored
166 $report = $runner();
167
168 if ($results = $report->filters()) {
169 $filters = array();
170
171 foreach ($results as $filter => $options) {
172 $filters[$options['name']] = $report->results['filters'][$filter];
173 }
174 }
952d9a5 @davidpersson Initial implementation of console test progress.
davidpersson authored
175 $command->out($report->render('stats', $report->stats() + compact('filters')));
ea0abeb @nateabele Refactoring the `Test` command to use typed output handlers, and cons…
nateabele authored
176 return $report;
177 }
178 );
179 }
180
181 /**
7ccbecc @davidpersson API: `Test` command now takes a path to a directory/file with tests.
davidpersson authored
182 * Runs tests given a path to a directory or file containing tests. The path to the
ea0abeb @nateabele Refactoring the `Test` command to use typed output handlers, and cons…
nateabele authored
183 * test(s) may be absolute or relative to the current working directory.
b391084 @jperras Updating doc blocks for lithium\console\command\Test::run().
jperras authored
184 *
fb1cb03 @davidpersson Fixing formatting of codeblocks.
davidpersson authored
185 * ```sh
ea0abeb @nateabele Refactoring the `Test` command to use typed output handlers, and cons…
nateabele authored
186 * li3 test lithium/tests/cases/core/ObjectTest.php
187 * li3 test lithium/tests/cases/core
68fc84c @davidpersson Replace markdown curly braces with triple ticks and remove link hack.
davidpersson authored
188 * ```
ea0abeb @nateabele Refactoring the `Test` command to use typed output handlers, and cons…
nateabele authored
189 *
190 * If you are in the working directory of an application or plugin and wish to run all tests,
191 * simply execute the following:
192 *
fb1cb03 @davidpersson Fixing formatting of codeblocks.
davidpersson authored
193 * ```sh
ea0abeb @nateabele Refactoring the `Test` command to use typed output handlers, and cons…
nateabele authored
194 * li3 test tests/cases
68fc84c @davidpersson Replace markdown curly braces with triple ticks and remove link hack.
davidpersson authored
195 * ```
e7f3c31 @gwoo going lithium
gwoo authored
196 *
fda2f70 @gwoo refactoring the test shell to handle more use cases.
gwoo authored
197 * If you are in the working directory of an application and wish to run a plugin, execute one
198 * of the following:
199 *
fb1cb03 @davidpersson Fixing formatting of codeblocks.
davidpersson authored
200 * ```sh
fda2f70 @gwoo refactoring the test shell to handle more use cases.
gwoo authored
201 * li3 test libraries/<plugin>/tests/cases
202 * li3 test <plugin>/tests/cases
68fc84c @davidpersson Replace markdown curly braces with triple ticks and remove link hack.
davidpersson authored
203 * ```
fda2f70 @gwoo refactoring the test shell to handle more use cases.
gwoo authored
204 *
2d52c6a @davidpersson Test command can now find and run test for a given file.
davidpersson authored
205 *
206 * This will run `<library>/tests/cases/<package>/<class>Test.php`:
207 *
fb1cb03 @davidpersson Fixing formatting of codeblocks.
davidpersson authored
208 * ```sh
2d52c6a @davidpersson Test command can now find and run test for a given file.
davidpersson authored
209 * li3 test <library>/<package>/<class>.php
68fc84c @davidpersson Replace markdown curly braces with triple ticks and remove link hack.
davidpersson authored
210 * ```
2d52c6a @davidpersson Test command can now find and run test for a given file.
davidpersson authored
211 *
212 * @param string $path Absolute or relative path to tests or a file which
213 * corresponding test should be run.
58accb2 @davidpersson QA and documenation.
davidpersson authored
214 * @return integer|boolean Will (indirectly) exit with status `1` if one or more tests
215 * failed otherwise with `0`.
e7f3c31 @gwoo going lithium
gwoo authored
216 */
7ccbecc @davidpersson API: `Test` command now takes a path to a directory/file with tests.
davidpersson authored
217 public function run($path = null) {
ea0abeb @nateabele Refactoring the `Test` command to use typed output handlers, and cons…
nateabele authored
218 if (!$path = $this->_path($path)) {
7ccbecc @davidpersson API: `Test` command now takes a path to a directory/file with tests.
davidpersson authored
219 return false;
e7f3c31 @gwoo going lithium
gwoo authored
220 }
6e347f6 @davidpersson Better filter files to try to map.
davidpersson authored
221 if (!preg_match('/(tests|Test\.php)/', $path)) {
2d52c6a @davidpersson Test command can now find and run test for a given file.
davidpersson authored
222 if (!$path = Unit::get($path)) {
223 $this->error('Cannot map path to test path.');
4e2771f @davidpersson Differ in exit codes.
davidpersson authored
224 return static::EXIT_NO_TEST;
2d52c6a @davidpersson Test command can now find and run test for a given file.
davidpersson authored
225 }
226 }
ea0abeb @nateabele Refactoring the `Test` command to use typed output handlers, and cons…
nateabele authored
227 $handlers = $this->_handlers;
40cdef2 @davidpersson Adding mistakenly removed newline back.
davidpersson authored
228
ea0abeb @nateabele Refactoring the `Test` command to use typed output handlers, and cons…
nateabele authored
229 if (!isset($handlers[$this->format]) || !is_callable($handlers[$this->format])) {
230 $this->error(sprintf('No handler for format `%s`... ', $this->format));
7ccbecc @davidpersson API: `Test` command now takes a path to a directory/file with tests.
davidpersson authored
231 return false;
232 }
ea0abeb @nateabele Refactoring the `Test` command to use typed output handlers, and cons…
nateabele authored
233 $filters = $this->filters ? array_map('trim', explode(',', $this->filters)) : array();
17b4811 @davidpersson Updating test command to use new reporter.
davidpersson authored
234 $params = compact('filters') + array('format' => $this->format);
65eddeb @davidpersson Making parameter in test runner optional.
davidpersson authored
235 $runner = function($options = array()) use ($path, $params) {
952d9a5 @davidpersson Initial implementation of console test progress.
davidpersson authored
236 return Dispatcher::run($path, $params + $options);
ea0abeb @nateabele Refactoring the `Test` command to use typed output handlers, and cons…
nateabele authored
237 };
238 $report = $handlers[$this->format]($runner, $path);
de5e554 @davidpersson Updating test command to exit with a non-zero status on fails.
davidpersson authored
239 $stats = $report->stats();
240 return $stats['success'];
e7f3c31 @gwoo going lithium
gwoo authored
241 }
47d83ae @gwoo Removes annoying prompts after the unit test run.
gwoo authored
242
e7f3c31 @gwoo going lithium
gwoo authored
243 /**
7ccbecc @davidpersson API: `Test` command now takes a path to a directory/file with tests.
davidpersson authored
244 * Finds a library for given path.
e7f3c31 @gwoo going lithium
gwoo authored
245 *
7ccbecc @davidpersson API: `Test` command now takes a path to a directory/file with tests.
davidpersson authored
246 * @param string $path Normalized (to slashes) absolute or relative path.
fda2f70 @gwoo refactoring the test shell to handle more use cases.
gwoo authored
247 * @return string the name of the library
e7f3c31 @gwoo going lithium
gwoo authored
248 */
7ccbecc @davidpersson API: `Test` command now takes a path to a directory/file with tests.
davidpersson authored
249 protected function _library($path) {
0adeaed @jails Fixes #454
jails authored
250 $result = null;
251 $match = '';
7ccbecc @davidpersson API: `Test` command now takes a path to a directory/file with tests.
davidpersson authored
252 foreach (Libraries::get() as $name => $library) {
ea0abeb @nateabele Refactoring the `Test` command to use typed output handlers, and cons…
nateabele authored
253 if (strpos($path, $library['path']) !== 0) {
254 continue;
e7f3c31 @gwoo going lithium
gwoo authored
255 }
0adeaed @jails Fixes #454
jails authored
256 if (strlen($library['path']) > strlen($match)) {
257 $result = $name;
258 $match = $library['path'];
259 }
ea0abeb @nateabele Refactoring the `Test` command to use typed output handlers, and cons…
nateabele authored
260 }
0adeaed @jails Fixes #454
jails authored
261 return $result;
ea0abeb @nateabele Refactoring the `Test` command to use typed output handlers, and cons…
nateabele authored
262 }
263
264 /**
dd3b3c6 @davidpersson Updating path mapping in test command to allow non-test paths.
davidpersson authored
265 * Validates and gets a fully-namespaced class path from an absolute or
266 * relative physical path to a directory or file. The final class path may
267 * be partial in that in doesn't contain the class name.
ea0abeb @nateabele Refactoring the `Test` command to use typed output handlers, and cons…
nateabele authored
268 *
dd3b3c6 @davidpersson Updating path mapping in test command to allow non-test paths.
davidpersson authored
269 * This method can be thought of the reverse of `Libraries::path()`.
270 *
68fc84c @davidpersson Replace markdown curly braces with triple ticks and remove link hack.
davidpersson authored
271 * ```
dd3b3c6 @davidpersson Updating path mapping in test command to allow non-test paths.
davidpersson authored
272 * lithium/tests/cases/core/ObjectTest.php -> lithium\tests\cases\core\ObjectTest
273 * lithium/tests/cases/core -> lithium\tests\cases\core
274 * lithium/core/Object.php -> lithium\core\Object
275 * lithium/core/ -> lithium\core
276 * lithium/core -> lithium\core
68fc84c @davidpersson Replace markdown curly braces with triple ticks and remove link hack.
davidpersson authored
277 * ```
dd3b3c6 @davidpersson Updating path mapping in test command to allow non-test paths.
davidpersson authored
278 *
279 * @see lithium\core\Libraries::path()
280 * @param string $path The directory of or file path to one or more classes.
281 * @return string Returns a fully-namespaced class path, or `false`, if an error occurs.
ea0abeb @nateabele Refactoring the `Test` command to use typed output handlers, and cons…
nateabele authored
282 */
283 protected function _path($path) {
a73f55a @davidpersson Normalizing test paths trimming trailing slash.
davidpersson authored
284 $path = rtrim(str_replace('\\', '/', $path), '/');
ea0abeb @nateabele Refactoring the `Test` command to use typed output handlers, and cons…
nateabele authored
285
286 if (!$path) {
287 $this->error('Please provide a path to tests.');
288 return false;
289 }
78504c4 @jails Fix typo according to li3_quality.
jails authored
290 if ($path[0] === '/') {
fda2f70 @gwoo refactoring the test shell to handle more use cases.
gwoo authored
291 $library = $this->_library($path);
292 }
78504c4 @jails Fix typo according to li3_quality.
jails authored
293 if ($path[0] !== '/') {
dd3b3c6 @davidpersson Updating path mapping in test command to allow non-test paths.
davidpersson authored
294 $libraries = array_reduce(Libraries::get(), function($v, $w) {
295 $v[] = basename($w['path']);
296 return $v;
297 });
298
0adeaed @jails Fixes #454
jails authored
299 $library = $this->_library($this->request->env('working') . '/' . $path);
fda2f70 @gwoo refactoring the test shell to handle more use cases.
gwoo authored
300 $parts = explode('/', str_replace("../", "", $path));
301 $plugin = array_shift($parts);
302
78504c4 @jails Fix typo according to li3_quality.
jails authored
303 if ($plugin === 'libraries') {
fda2f70 @gwoo refactoring the test shell to handle more use cases.
gwoo authored
304 $plugin = array_shift($parts);
305 }
dd3b3c6 @davidpersson Updating path mapping in test command to allow non-test paths.
davidpersson authored
306 if (in_array($plugin, $libraries)) {
fda2f70 @gwoo refactoring the test shell to handle more use cases.
gwoo authored
307 $library = $plugin;
308 $path = join('/', $parts);
309 }
310 }
311 if (empty($library)) {
fbab6e4 @davidpersson Formatting error messages in test command.
davidpersson authored
312 $this->error("No library found in path `{$path}`.");
fda2f70 @gwoo refactoring the test shell to handle more use cases.
gwoo authored
313 return false;
ea0abeb @nateabele Refactoring the `Test` command to use typed output handlers, and cons…
nateabele authored
314 }
fda2f70 @gwoo refactoring the test shell to handle more use cases.
gwoo authored
315 if (!$config = Libraries::get($library)) {
316 $this->error("Library `{$library}` does not exist.");
ea0abeb @nateabele Refactoring the `Test` command to use typed output handlers, and cons…
nateabele authored
317 return false;
318 }
fda2f70 @gwoo refactoring the test shell to handle more use cases.
gwoo authored
319 $path = str_replace($config['path'], null, $path);
320 $realpath = $config['path'] . '/' . $path;
ea0abeb @nateabele Refactoring the `Test` command to use typed output handlers, and cons…
nateabele authored
321
fda2f70 @gwoo refactoring the test shell to handle more use cases.
gwoo authored
322 if (!realpath($realpath)) {
fbab6e4 @davidpersson Formatting error messages in test command.
davidpersson authored
323 $this->error("Path `{$realpath}` not found.");
ea0abeb @nateabele Refactoring the `Test` command to use typed output handlers, and cons…
nateabele authored
324 return false;
e7f3c31 @gwoo going lithium
gwoo authored
325 }
fda2f70 @gwoo refactoring the test shell to handle more use cases.
gwoo authored
326 $class = str_replace(".php", "", str_replace('/', '\\', ltrim($path, '/')));
327 return $config['prefix'] . $class;
e7f3c31 @gwoo going lithium
gwoo authored
328 }
329 }
330
331 ?>
Something went wrong with that request. Please try again.