commiting uncommited changes on live site
[weblabels.fsf.org.git] / crm.fsf.org / 20131203 / files / sites / all / modules-old / civicrm / packages / PHP / CodeCoverage.php
1 <?php
2 /**
3 * PHP_CodeCoverage
4 *
5 * Copyright (c) 2009-2012, Sebastian Bergmann <sb@sebastian-bergmann.de>.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
18 * distribution.
19 *
20 * * Neither the name of Sebastian Bergmann nor the names of his
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 *
37 * @category PHP
38 * @package CodeCoverage
39 * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
40 * @copyright 2009-2012 Sebastian Bergmann <sb@sebastian-bergmann.de>
41 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
42 * @link http://github.com/sebastianbergmann/php-code-coverage
43 * @since File available since Release 1.0.0
44 */
45
46 /**
47 * Provides collection functionality for PHP code coverage information.
48 *
49 * @category PHP
50 * @package CodeCoverage
51 * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
52 * @copyright 2009-2012 Sebastian Bergmann <sb@sebastian-bergmann.de>
53 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
54 * @version Release: 1.1.2
55 * @link http://github.com/sebastianbergmann/php-code-coverage
56 * @since Class available since Release 1.0.0
57 */
58 class PHP_CodeCoverage
59 {
60 /**
61 * @var PHP_CodeCoverage_Driver
62 */
63 protected $driver;
64
65 /**
66 * @var PHP_CodeCoverage_Filter
67 */
68 protected $filter;
69
70 /**
71 * @var boolean
72 */
73 protected $cacheTokens = TRUE;
74
75 /**
76 * @var boolean
77 */
78 protected $forceCoversAnnotation = FALSE;
79
80 /**
81 * @var boolean
82 */
83 protected $mapTestClassNameToCoveredClassName = FALSE;
84
85 /**
86 * @var boolean
87 */
88 protected $processUncoveredFilesFromWhitelist = TRUE;
89
90 /**
91 * @var mixed
92 */
93 protected $currentId;
94
95 /**
96 * Code coverage data.
97 *
98 * @var array
99 */
100 protected $data = array();
101
102 /**
103 * Test data.
104 *
105 * @var array
106 */
107 protected $tests = array();
108
109 /**
110 * Constructor.
111 *
112 * @param PHP_CodeCoverage_Driver $driver
113 * @param PHP_CodeCoverage_Filter $filter
114 * @throws InvalidArgumentException
115 */
116 public function __construct(PHP_CodeCoverage_Driver $driver = NULL, PHP_CodeCoverage_Filter $filter = NULL)
117 {
118 if ($driver === NULL) {
119 $driver = new PHP_CodeCoverage_Driver_Xdebug;
120 }
121
122 if ($filter === NULL) {
123 $filter = new PHP_CodeCoverage_Filter;
124 }
125
126 $this->driver = $driver;
127 $this->filter = $filter;
128 }
129
130 /**
131 * Returns the PHP_CodeCoverage_Report_Node_* object graph
132 * for this PHP_CodeCoverage object.
133 *
134 * @return PHP_CodeCoverage_Report_Node_Directory
135 * @since Method available since Release 1.1.0
136 */
137 public function getReport()
138 {
139 $factory = new PHP_CodeCoverage_Report_Factory;
140
141 return $factory->create($this);
142 }
143
144 /**
145 * Clears collected code coverage data.
146 */
147 public function clear()
148 {
149 $this->currentId = NULL;
150 $this->data = array();
151 $this->tests = array();
152 }
153
154 /**
155 * Returns the PHP_CodeCoverage_Filter used.
156 *
157 * @return PHP_CodeCoverage_Filter
158 */
159 public function filter()
160 {
161 return $this->filter;
162 }
163
164 /**
165 * Returns the collected code coverage data.
166 *
167 * @return array
168 * @since Method available since Release 1.1.0
169 */
170 public function getData()
171 {
172 if ($this->processUncoveredFilesFromWhitelist) {
173 $this->processUncoveredFilesFromWhitelist();
174 }
175
176 // We need to apply the blacklist filter a second time
177 // when no whitelist is used.
178 if (!$this->filter->hasWhitelist()) {
179 $this->applyListsFilter($this->data);
180 }
181
182 return $this->data;
183 }
184
185 /**
186 * Returns the test data.
187 *
188 * @return array
189 * @since Method available since Release 1.1.0
190 */
191 public function getTests()
192 {
193 return $this->tests;
194 }
195
196 /**
197 * Start collection of code coverage information.
198 *
199 * @param mixed $id
200 * @param boolean $clear
201 * @throws InvalidArgumentException
202 */
203 public function start($id, $clear = FALSE)
204 {
205 if (!is_bool($clear)) {
206 throw new InvalidArgumentException;
207 }
208
209 if ($clear) {
210 $this->clear();
211 }
212
213 $this->currentId = $id;
214
215 $this->driver->start();
216 }
217
218 /**
219 * Stop collection of code coverage information.
220 *
221 * @param boolean $append
222 * @return array
223 * @throws InvalidArgumentException
224 */
225 public function stop($append = TRUE)
226 {
227 if (!is_bool($append)) {
228 throw new InvalidArgumentException;
229 }
230
231 $data = $this->driver->stop();
232 $this->append($data, NULL, $append);
233
234 $this->currentId = NULL;
235
236 return $data;
237 }
238
239 /**
240 * Appends code coverage data.
241 *
242 * @param array $data
243 * @param mixed $id
244 * @param boolean $append
245 */
246 public function append(array $data, $id = NULL, $append = TRUE)
247 {
248 if ($id === NULL) {
249 $id = $this->currentId;
250 }
251
252 if ($id === NULL) {
253 throw new InvalidArgumentException;
254 }
255
256 $this->applyListsFilter($data);
257 $this->initializeFilesThatAreSeenTheFirstTime($data);
258
259 if (!$append) {
260 return;
261 }
262
263 if ($id != 'UNCOVERED_FILES_FROM_WHITELIST') {
264 $this->applyCoversAnnotationFilter($data, $id);
265 }
266
267 if (empty($data)) {
268 return;
269 }
270
271 $status = NULL;
272
273 if ($id instanceof PHPUnit_Framework_TestCase) {
274 $status = $id->getStatus();
275 $id = get_class($id) . '::' . $id->getName();
276 }
277
278 else if ($id instanceof PHPUnit_Extensions_PhptTestCase) {
279 $id = $id->getName();
280 }
281
282 $this->tests[$id] = $status;
283
284 foreach ($data as $file => $lines) {
285 if (!$this->filter->isFile($file)) {
286 continue;
287 }
288
289 foreach ($lines as $k => $v) {
290 if ($v == 1) {
291 $this->data[$file][$k][] = $id;
292 }
293 }
294 }
295 }
296
297 /**
298 * Merges the data from another instance of PHP_CodeCoverage.
299 *
300 * @param PHP_CodeCoverage $that
301 */
302 public function merge(PHP_CodeCoverage $that)
303 {
304 foreach ($that->data as $file => $lines) {
305 if (!isset($this->data[$file])) {
306 if (!$this->filter->isFiltered($file)) {
307 $this->data[$file] = $lines;
308 }
309
310 continue;
311 }
312
313 foreach ($lines as $line => $data) {
314 if ($data !== NULL) {
315 if (!isset($this->data[$file][$line])) {
316 $this->data[$file][$line] = $data;
317 } else {
318 $this->data[$file][$line] = array_unique(
319 array_merge($this->data[$file][$line], $data)
320 );
321 }
322 }
323 }
324 }
325
326 $this->tests = array_merge($this->tests, $that->getTests());
327 }
328
329 /**
330 * @param boolean $flag
331 * @throws InvalidArgumentException
332 * @since Method available since Release 1.1.0
333 */
334 public function setCacheTokens($flag)
335 {
336 if (!is_bool($flag)) {
337 throw new InvalidArgumentException;
338 }
339
340 $this->cacheTokens = $flag;
341 }
342
343 /**
344 * @param boolean $flag
345 * @throws InvalidArgumentException
346 * @since Method available since Release 1.1.0
347 */
348 public function getCacheTokens()
349 {
350 return $this->cacheTokens;
351 }
352
353 /**
354 * @param boolean $flag
355 * @throws InvalidArgumentException
356 */
357 public function setForceCoversAnnotation($flag)
358 {
359 if (!is_bool($flag)) {
360 throw new InvalidArgumentException;
361 }
362
363 $this->forceCoversAnnotation = $flag;
364 }
365
366 /**
367 * @param boolean $flag
368 * @throws InvalidArgumentException
369 */
370 public function setMapTestClassNameToCoveredClassName($flag)
371 {
372 if (!is_bool($flag)) {
373 throw new InvalidArgumentException;
374 }
375
376 $this->mapTestClassNameToCoveredClassName = $flag;
377 }
378
379 /**
380 * @param boolean $flag
381 * @throws InvalidArgumentException
382 */
383 public function setProcessUncoveredFilesFromWhitelist($flag)
384 {
385 if (!is_bool($flag)) {
386 throw new InvalidArgumentException;
387 }
388
389 $this->processUncoveredFilesFromWhitelist = $flag;
390 }
391
392 /**
393 * Applies the @covers annotation filtering.
394 *
395 * @param array $data
396 * @param mixed $id
397 */
398 protected function applyCoversAnnotationFilter(&$data, $id)
399 {
400 if ($id instanceof PHPUnit_Framework_TestCase) {
401 $testClassName = get_class($id);
402 $linesToBeCovered = PHP_CodeCoverage_Util::getLinesToBeCovered(
403 $testClassName, $id->getName()
404 );
405
406 if ($this->mapTestClassNameToCoveredClassName &&
407 empty($linesToBeCovered)) {
408 $testedClass = substr($testClassName, 0, -4);
409
410 if (class_exists($testedClass)) {
411 $class = new ReflectionClass($testedClass);
412
413 $linesToBeCovered = array(
414 $class->getFileName() => range(
415 $class->getStartLine(), $class->getEndLine()
416 )
417 );
418 }
419 }
420 } else {
421 $linesToBeCovered = array();
422 }
423
424 if (!empty($linesToBeCovered)) {
425 $data = array_intersect_key($data, $linesToBeCovered);
426
427 foreach (array_keys($data) as $filename) {
428 $data[$filename] = array_intersect_key(
429 $data[$filename], array_flip($linesToBeCovered[$filename])
430 );
431 }
432 }
433
434 else if ($this->forceCoversAnnotation) {
435 $data = array();
436 }
437 }
438
439 /**
440 * Applies the blacklist/whitelist filtering.
441 *
442 * @param array $data
443 */
444 protected function applyListsFilter(&$data)
445 {
446 foreach (array_keys($data) as $filename) {
447 if ($this->filter->isFiltered($filename)) {
448 unset($data[$filename]);
449 }
450 }
451 }
452
453 /**
454 * @since Method available since Release 1.1.0
455 */
456 protected function initializeFilesThatAreSeenTheFirstTime($data)
457 {
458 foreach ($data as $file => $lines) {
459 if ($this->filter->isFile($file) && !isset($this->data[$file])) {
460 $this->data[$file] = array();
461
462 foreach ($lines as $k => $v) {
463 $this->data[$file][$k] = $v == -2 ? NULL : array();
464 }
465 }
466 }
467 }
468
469 /**
470 * Processes whitelisted files that are not covered.
471 */
472 protected function processUncoveredFilesFromWhitelist()
473 {
474 $data = array();
475 $uncoveredFiles = array_diff(
476 $this->filter->getWhitelist(), array_keys($this->data)
477 );
478
479 foreach ($uncoveredFiles as $uncoveredFile) {
480 if (!file_exists($uncoveredFile)) {
481 continue;
482 }
483
484 if ($this->cacheTokens) {
485 $tokens = PHP_Token_Stream_CachingFactory::get($uncoveredFile);
486 } else {
487 $tokens = new PHP_Token_Stream($uncoveredFile);
488 }
489
490 $classes = $tokens->getClasses();
491 $interfaces = $tokens->getInterfaces();
492 $functions = $tokens->getFunctions();
493 unset($tokens);
494
495 foreach (array_keys($classes) as $class) {
496 if (class_exists($class, FALSE)) {
497 continue 2;
498 }
499 }
500
501 unset($classes);
502
503 foreach (array_keys($interfaces) as $interface) {
504 if (interface_exists($interface, FALSE)) {
505 continue 2;
506 }
507 }
508
509 unset($interfaces);
510
511 foreach (array_keys($functions) as $function) {
512 if (function_exists($function)) {
513 continue 2;
514 }
515 }
516
517 unset($functions);
518
519 $this->driver->start();
520 include_once $uncoveredFile;
521 $coverage = $this->driver->stop();
522
523 foreach ($coverage as $file => $fileCoverage) {
524 if (!isset($data[$file]) &&
525 in_array($file, $uncoveredFiles)) {
526 foreach (array_keys($fileCoverage) as $key) {
527 if ($fileCoverage[$key] == 1) {
528 $fileCoverage[$key] = -1;
529 }
530 }
531
532 $data[$file] = $fileCoverage;
533 }
534 }
535 }
536
537 $this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST');
538 }
539 }