1
|
|
|
<?php |
2
|
|
|
namespace Agavi\Testing; |
3
|
|
|
|
4
|
|
|
// +---------------------------------------------------------------------------+ |
5
|
|
|
// | This file is part of the Agavi package. | |
6
|
|
|
// | Copyright (c) 2005-2011 the Agavi Project. | |
7
|
|
|
// | | |
8
|
|
|
// | For the full copyright and license information, please view the LICENSE | |
9
|
|
|
// | file that was distributed with this source code. You can also view the | |
10
|
|
|
// | LICENSE file online at http://www.agavi.org/LICENSE.txt | |
11
|
|
|
// | vi: set noexpandtab: | |
12
|
|
|
// | Local Variables: | |
13
|
|
|
// | indent-tabs-mode: t | |
14
|
|
|
// | End: | |
15
|
|
|
// +---------------------------------------------------------------------------+ |
16
|
|
|
|
17
|
|
|
use Agavi\Config\Config; |
18
|
|
|
use Agavi\Config\ConfigCache; |
19
|
|
|
use Agavi\Exception\AgaviException; |
20
|
|
|
use Agavi\Util\RecursiveDirectoryFilterIterator; |
21
|
|
|
use Agavi\Util\Toolkit; |
22
|
|
|
use PHP_CodeCoverage_Filter; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* Main framework class used for autoloading and initial bootstrapping of the |
26
|
|
|
* Agavi testing environment |
27
|
|
|
* |
28
|
|
|
* @package agavi |
29
|
|
|
* @subpackage testing |
30
|
|
|
* |
31
|
|
|
* @author Felix Gilcher <[email protected]> |
32
|
|
|
* @copyright The Agavi Project |
33
|
|
|
* |
34
|
|
|
* @since 1.0.0 |
35
|
|
|
* @deprecated 1.1.0 Use PhpUnitCli |
36
|
|
|
* |
37
|
|
|
* @version $Id$ |
38
|
|
|
*/ |
39
|
|
|
class AgaviTesting |
40
|
|
|
{ |
41
|
|
|
/** |
42
|
|
|
* @var PHP_CodeCoverage_Filter The code coverage filter for our tests. |
43
|
|
|
*/ |
44
|
|
|
public static $codeCoverageFilter = null; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* Get the code coverage filter instance we will use for tests. |
48
|
|
|
* When running PHPUnit 3.5, this will return the singleton instance. |
49
|
|
|
* When running PHPUnit 3.6, this will return the instance we hold internally; |
50
|
|
|
* this same instance will be passed to PHPUnit in AgaviTesting::dispatch(). |
51
|
|
|
* |
52
|
|
|
* @return PHP_CodeCoverage_Filter The code coverage filter for our tests. |
53
|
|
|
* |
54
|
|
|
* @author David Zülke <[email protected]> |
55
|
|
|
* @since 1.0.7 |
56
|
|
|
* @deprecated 1.1.0 Use PhpUnitCli |
57
|
|
|
*/ |
58
|
|
|
public static function getCodeCoverageFilter() |
59
|
|
|
{ |
60
|
|
|
if (self::$codeCoverageFilter === null) { |
61
|
|
|
// PHP_CodeCoverage doesn't expose any version info, we'll have to check if there is a static getInstance method |
62
|
|
|
self::$codeCoverageFilter = method_exists('PHP_CodeCoverage_Filter', 'getInstance') ? PHP_CodeCoverage_Filter::getInstance() : new PHP_CodeCoverage_Filter(); |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
return self::$codeCoverageFilter; |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* Startup the Agavi core |
70
|
|
|
* |
71
|
|
|
* @param string $environment environment the environment to use for this session. |
72
|
|
|
* |
73
|
|
|
* @author Felix Gilcher <[email protected]> |
74
|
|
|
* @since 1.0.0 |
75
|
|
|
* @deprecated 1.1.0 Use PhpUnitCli |
76
|
|
|
*/ |
77
|
|
|
public static function bootstrap($environment = null) |
78
|
|
|
{ |
79
|
|
|
PhpUnitCli::bootstrap($environment); |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* Dispatch the test run. |
84
|
|
|
* |
85
|
|
|
* @param array $arguments An array of arguments configuring PHPUnit behavior. |
86
|
|
|
* @param bool $exit Whether exit() should be called with an appropriate shell |
87
|
|
|
* exit status to indicate success or failures/errors. |
88
|
|
|
* |
89
|
|
|
* @return \PHPUnit_Framework_TestResult The PHPUnit result object. |
90
|
|
|
* |
91
|
|
|
* @author Felix Gilcher <[email protected]> |
92
|
|
|
* @author David Zülke <[email protected]> |
93
|
|
|
* @since 1.0.0 |
94
|
|
|
* @deprecated 1.1.0 Use PhpUnitCli |
95
|
|
|
*/ |
96
|
|
|
public static function dispatch($arguments = array(), $exit = true) |
97
|
|
|
{ |
98
|
|
|
|
99
|
|
|
$suites = include ConfigCache::checkConfig(Config::get('core.testing_dir').'/config/suites.xml'); |
100
|
|
|
$master_suite = new TestSuite('Master'); |
101
|
|
|
|
102
|
|
|
if (!empty($arguments['include-suite'])) { |
103
|
|
|
$names = explode(',', $arguments['include-suite']); |
104
|
|
|
unset($arguments['include-suite']); |
105
|
|
|
|
106
|
|
View Code Duplication |
foreach ($names as $name) { |
|
|
|
|
107
|
|
|
if (empty($suites[$name])) { |
108
|
|
|
throw new \InvalidArgumentException(sprintf('Invalid suite name %1$s.', $name)); |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
$master_suite->addTest(self::createSuite($name, $suites[$name])); |
|
|
|
|
112
|
|
|
} |
113
|
|
|
} else { |
114
|
|
|
$excludes = array(); |
115
|
|
|
if (!empty($arguments['exclude-suite'])) { |
116
|
|
|
$excludes = explode(',', $arguments['exclude-suite']); |
117
|
|
|
unset($arguments['exclude-suite']); |
118
|
|
|
} |
119
|
|
|
foreach ($suites as $name => $suite) { |
120
|
|
|
if (!in_array($name, $excludes)) { |
121
|
|
|
$master_suite->addTest(self::createSuite($name, $suite)); |
|
|
|
|
122
|
|
|
} |
123
|
|
|
} |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
if (version_compare(\PHPUnit_Runner_Version::id(), '3.6', '<')) { |
127
|
|
|
// PHP_CodeCoverage_Filter is a singleton |
128
|
|
|
$runner = new \PHPUnit_TextUI_TestRunner(); |
129
|
|
|
} else { |
130
|
|
|
// PHP_CodeCoverage_Filter instance must be passed to the test runner |
131
|
|
|
$runner = new \PHPUnit_TextUI_TestRunner(null, self::$codeCoverageFilter); |
132
|
|
|
} |
133
|
|
|
$result = $runner->doRun($master_suite, $arguments); |
134
|
|
|
if ($exit) { |
135
|
|
|
// bai |
136
|
|
|
exit(self::getExitStatus($result)); |
|
|
|
|
137
|
|
|
} else { |
138
|
|
|
// return result so calling code can use it |
139
|
|
|
return $result; |
140
|
|
|
} |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* Compute a shell exit status for the given result. |
145
|
|
|
* Behaves like PHPUnit_TextUI_Command. |
146
|
|
|
* |
147
|
|
|
* @param PHPUnit_Framework_TestResult The test result object. |
148
|
|
|
* |
149
|
|
|
* @return int The shell exit code. |
150
|
|
|
* @deprecated 1.1.0 Use PhpUnitCli |
151
|
|
|
*/ |
152
|
|
|
public static function getExitStatus(\PHPUnit_Framework_TestResult $result) |
153
|
|
|
{ |
154
|
|
|
if ($result->wasSuccessful()) { |
155
|
|
|
return \PHPUnit_TextUI_TestRunner::SUCCESS_EXIT; |
156
|
|
|
} elseif ($result->errorCount()) { |
157
|
|
|
return \PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT; |
158
|
|
|
} else { |
159
|
|
|
return \PHPUnit_TextUI_TestRunner::FAILURE_EXIT; |
160
|
|
|
} |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
/** |
164
|
|
|
* Initialize a suite from the given instructions and add registered tests. |
165
|
|
|
* |
166
|
|
|
* @param string Name of the suite |
167
|
|
|
* @param array An array containing information about the suite |
168
|
|
|
* |
169
|
|
|
* @return TestSuite The initialized test suite object. |
170
|
|
|
* |
171
|
|
|
* @author Felix Gilcher <[email protected]> |
172
|
|
|
* @since 1.0.0 |
173
|
|
|
* @deprecated 1.1.0 Use PhpUnitCli |
174
|
|
|
*/ |
175
|
|
|
protected static function createSuite($name, array $suite) |
176
|
|
|
{ |
177
|
|
|
$base = (null == $suite['base']) ? 'tests' : $suite['base']; |
178
|
|
|
if (!Toolkit::isPathAbsolute($base)) { |
179
|
|
|
$base = Config::get('core.testing_dir').'/'.$base; |
180
|
|
|
} |
181
|
|
|
$s = new $suite['class']($name); |
182
|
|
|
if (!empty($suite['includes'])) { |
183
|
|
|
foreach (new \RecursiveIteratorIterator( |
184
|
|
|
new RecursiveDirectoryFilterIterator( |
185
|
|
|
new \RecursiveDirectoryIterator($base), |
186
|
|
|
$suite['includes'], |
187
|
|
|
$suite['excludes'] |
188
|
|
|
), |
189
|
|
|
\RecursiveIteratorIterator::CHILD_FIRST |
190
|
|
|
) as $finfo) { |
191
|
|
|
if ($finfo->isFile()) { |
192
|
|
|
$s->addTestFile($finfo->getPathName()); |
193
|
|
|
} |
194
|
|
|
} |
195
|
|
|
} |
196
|
|
View Code Duplication |
foreach ($suite['testfiles'] as $file) { |
|
|
|
|
197
|
|
|
if (!Toolkit::isPathAbsolute($file)) { |
198
|
|
|
$file = $base.'/'.$file; |
199
|
|
|
} |
200
|
|
|
$s->addTestFile($file); |
201
|
|
|
} |
202
|
|
|
return $s; |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
/** |
206
|
|
|
* Handles the commandline arguments passed. |
207
|
|
|
* |
208
|
|
|
* @return array the commandline arguments |
209
|
|
|
* |
210
|
|
|
* @author Felix Gilcher <[email protected]> |
211
|
|
|
* @since 1.0.0 |
212
|
|
|
* @deprecated 1.1.0 Use PhpUnitCli |
213
|
|
|
*/ |
214
|
|
|
public static function processCommandlineOptions() |
|
|
|
|
215
|
|
|
{ |
216
|
|
|
$longOptions = array( |
217
|
|
|
'configuration=', |
218
|
|
|
'coverage-html=', |
219
|
|
|
'coverage-clover=', |
220
|
|
|
'coverage-source=', |
221
|
|
|
'coverage-xml=', |
222
|
|
|
'report=', |
223
|
|
|
'environment=', |
224
|
|
|
'help', |
225
|
|
|
'log-graphviz=', |
226
|
|
|
'log-json=', |
227
|
|
|
'log-metrics=', |
228
|
|
|
'log-pmd=', |
229
|
|
|
'log-tap=', |
230
|
|
|
'log-xml=', |
231
|
|
|
'include-suite=', |
232
|
|
|
'exclude-suite=', |
233
|
|
|
); |
234
|
|
|
|
235
|
|
|
try { |
236
|
|
|
$options = \PHPUnit_Util_Getopt::getopt( |
237
|
|
|
$_SERVER['argv'], |
238
|
|
|
'd:', |
239
|
|
|
$longOptions |
240
|
|
|
); |
241
|
|
|
} catch (\RuntimeException $e) { |
242
|
|
|
\PHPUnit_TextUI_TestRunner::showError($e->getMessage()); |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
$arguments = array(); |
246
|
|
|
|
247
|
|
|
foreach ($options[0] as $option) { |
248
|
|
|
switch ($option[0]) { |
249
|
|
|
case '--configuration': |
250
|
|
|
$arguments['configuration'] = $option[1]; |
251
|
|
|
break; |
252
|
|
|
|
253
|
|
|
case '--coverage-clover': |
254
|
|
|
case '--coverage-xml': |
255
|
|
|
if (self::checkCodeCoverageDeps()) { |
|
|
|
|
256
|
|
|
$arguments['coverageClover'] = $option[1]; |
257
|
|
|
} |
258
|
|
|
break; |
259
|
|
|
|
260
|
|
|
case '--coverage-source': |
261
|
|
|
if (self::checkCodeCoverageDeps()) { |
|
|
|
|
262
|
|
|
$arguments['coverageSource'] = $option[1]; |
263
|
|
|
} |
264
|
|
|
break; |
265
|
|
|
|
266
|
|
|
case '--coverage-html': |
267
|
|
|
case '--report': |
268
|
|
|
if (self::checkCodeCoverageDeps()) { |
|
|
|
|
269
|
|
|
$arguments['reportDirectory'] = $option[1]; |
270
|
|
|
} |
271
|
|
|
break; |
272
|
|
|
|
273
|
|
|
case '--environment': |
274
|
|
|
$arguments['environment'] = $option[1]; |
275
|
|
|
break; |
276
|
|
|
|
277
|
|
|
case '--help': |
278
|
|
|
self::showHelp(); |
|
|
|
|
279
|
|
|
exit(\PHPUnit_TextUI_TestRunner::SUCCESS_EXIT); |
|
|
|
|
280
|
|
|
break; |
|
|
|
|
281
|
|
|
|
282
|
|
|
case '--log-json': |
283
|
|
|
$arguments['jsonLogfile'] = $option[1]; |
284
|
|
|
break; |
285
|
|
|
|
286
|
|
|
case '--log-graphviz': |
287
|
|
|
if (\PHPUnit_Util_Filesystem::fileExistsInIncludePath('Image/GraphViz.php')) { |
288
|
|
|
$arguments['graphvizLogfile'] = $option[1]; |
289
|
|
|
} else { |
290
|
|
|
throw new \Exception('The Image_GraphViz package is not installed.'); |
291
|
|
|
} |
292
|
|
|
break; |
293
|
|
|
|
294
|
|
|
case '--log-tap': |
295
|
|
|
$arguments['tapLogfile'] = $option[1]; |
296
|
|
|
break; |
297
|
|
|
|
298
|
|
|
case '--log-xml': |
299
|
|
|
$arguments['xmlLogfile'] = $option[1]; |
300
|
|
|
break; |
301
|
|
|
|
302
|
|
|
case '--log-pmd': |
303
|
|
|
if (self::checkCodeCoverageDeps()) { |
|
|
|
|
304
|
|
|
$arguments['pmdXML'] = $option[1]; |
305
|
|
|
} |
306
|
|
|
break; |
307
|
|
|
|
308
|
|
|
case '--log-metrics': |
309
|
|
|
if (self::checkCodeCoverageDeps()) { |
|
|
|
|
310
|
|
|
$arguments['metricsXML'] = $option[1]; |
311
|
|
|
} |
312
|
|
|
break; |
313
|
|
|
|
314
|
|
|
case '--include-suite': |
315
|
|
|
$arguments['include-suite'] = $option[1]; |
316
|
|
|
break; |
317
|
|
|
|
318
|
|
|
case '--exclude-suite': |
319
|
|
|
$arguments['exclude-suite'] = $option[1]; |
320
|
|
|
break; |
321
|
|
|
} |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
return $arguments; |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
/** |
328
|
|
|
* Checks whether all dependencies for writing code coverage information |
329
|
|
|
* are met. |
330
|
|
|
* |
331
|
|
|
* @return true if all deps are met |
332
|
|
|
* @throws AgaviException if a dependency is missing |
333
|
|
|
* |
334
|
|
|
* @author Felix Gilcher <[email protected]> |
335
|
|
|
* @since 1.0.0 |
336
|
|
|
* @deprecated 1.1.0 Use PhpUnitCli |
337
|
|
|
*/ |
338
|
|
|
protected static function checkCodeCoverageDeps() |
339
|
|
|
{ |
340
|
|
|
if (extension_loaded('tokenizer') && extension_loaded('xdebug')) { |
341
|
|
|
return true; |
|
|
|
|
342
|
|
|
} else { |
343
|
|
|
if (!extension_loaded('tokenizer')) { |
344
|
|
|
throw new AgaviException('The tokenizer extension is not loaded.'); |
345
|
|
|
} else { |
346
|
|
|
throw new AgaviException('The Xdebug extension is not loaded.'); |
347
|
|
|
} |
348
|
|
|
} |
349
|
|
|
|
350
|
|
|
return false; |
|
|
|
|
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
/** |
354
|
|
|
* shows the help for the commandline call |
355
|
|
|
* |
356
|
|
|
* @author Felix Gilcher <[email protected]> |
357
|
|
|
* @since 1.0.0 |
358
|
|
|
* @deprecated 1.1.0 Use PhpUnitCli |
359
|
|
|
*/ |
360
|
|
|
protected static function showHelp() |
361
|
|
|
{ |
362
|
|
|
\PHPUnit_TextUI_TestRunner::printVersionString(); |
363
|
|
|
|
364
|
|
|
print <<<EOT |
365
|
|
|
Usage: run-tests.php [switches] |
366
|
|
|
|
367
|
|
|
--environment <envname> use environment named <envname> to run the tests. Defaults to "testing". |
368
|
|
|
|
369
|
|
|
--log-graphviz <file> Log test execution in GraphViz markup. |
370
|
|
|
--log-json <file> Log test execution in JSON format. |
371
|
|
|
--log-tap <file> Log test execution in TAP format to file. |
372
|
|
|
--log-xml <file> Log test execution in XML format to file. |
373
|
|
|
--log-metrics <file> Write metrics report in XML format. |
374
|
|
|
--log-pmd <file> Write violations report in PMD XML format. |
375
|
|
|
|
376
|
|
|
--configuration <file> PHPUnit XML configuration file to use. |
377
|
|
|
|
378
|
|
|
--coverage-html <dir> Generate code coverage report in HTML format. |
379
|
|
|
--coverage-clover <file> Write code coverage data in Clover XML format. |
380
|
|
|
--coverage-source <dir> Write code coverage / source data in XML format. |
381
|
|
|
|
382
|
|
|
--include-suite <suites> run only suites named <suite>, accepts a list of suites, comma separated. |
383
|
|
|
--exclude-suite <suites> run all but suites named <suite>, accepts a list of suites, comma separated. |
384
|
|
|
|
385
|
|
|
--help Prints this usage information. |
386
|
|
|
|
387
|
|
|
|
388
|
|
|
EOT; |
389
|
|
|
} |
390
|
|
|
} |
391
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.