5 * Copyright (c) 2002-2009, Sebastian Bergmann <sb@sebastian-bergmann.de>.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
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
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.
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.
39 * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
40 * @copyright 2002-2009 Sebastian Bergmann <sb@sebastian-bergmann.de>
41 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @link http://www.phpunit.de/
44 * @since File available since Release 2.0.0
47 require_once 'PHPUnit/Util/FilterIterator.php';
50 * Utility class for code filtering.
54 * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
55 * @copyright 2002-2009 Sebastian Bergmann <sb@sebastian-bergmann.de>
56 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
57 * @version Release: 3.3.17
58 * @link http://www.phpunit.de/
59 * @since Class available since Release 2.0.0
61 class PHPUnit_Util_Filter
66 public static $addUncoveredFilesFromWhitelist = TRUE;
71 public static $filterPHPUnit = TRUE;
76 protected static $filter = TRUE;
79 * Source files that are blacklisted.
83 protected static $blacklistedFiles = array(
90 * Source files that are whitelisted.
94 protected static $whitelistedFiles = array();
97 * List of covered files.
101 protected static $coveredFiles = array();
104 * Adds a directory to the blacklist (recursively).
106 * @param string $directory
107 * @param string $suffix
108 * @param string $group
109 * @throws RuntimeException
110 * @since Method available since Release 3.1.5
112 public static function addDirectoryToFilter($directory, $suffix = '.php', $group = 'DEFAULT')
114 if (file_exists($directory)) {
115 foreach (self::getIterator($directory, $suffix) as $file) {
116 self::addFileToFilter($file->getPathName(), $group);
119 throw new RuntimeException($directory . ' does not exist');
124 * Adds a new file to be filtered (blacklist).
126 * @param string $filename
127 * @param string $group
128 * @throws RuntimeException
129 * @since Method available since Release 2.1.0
131 public static function addFileToFilter($filename, $group = 'DEFAULT')
133 if (file_exists($filename)) {
134 $filename = realpath($filename);
136 if (!isset(self::$blacklistedFiles[$group])) {
137 self::$blacklistedFiles[$group] = array($filename);
140 else if (!in_array($filename, self::$blacklistedFiles[$group])) {
141 self::$blacklistedFiles[$group][] = $filename;
144 throw new RuntimeException($filename . ' does not exist');
149 * Removes a directory from the blacklist (recursively).
151 * @param string $directory
152 * @param string $suffix
153 * @param string $group
154 * @throws RuntimeException
155 * @since Method available since Release 3.1.5
157 public static function removeDirectoryFromFilter($directory, $suffix = '.php', $group = 'DEFAULT')
159 if (file_exists($directory)) {
160 foreach (self::getIterator($directory, $suffix) as $file) {
161 self::removeFileFromFilter($file->getPathName(), $group);
164 throw new RuntimeException($directory . ' does not exist');
169 * Removes a file from the filter (blacklist).
171 * @param string $filename
172 * @param string $group
173 * @throws RuntimeException
174 * @since Method available since Release 2.1.0
176 public static function removeFileFromFilter($filename, $group = 'DEFAULT')
178 if (file_exists($filename)) {
179 if (isset(self::$blacklistedFiles[$group])) {
180 $filename = realpath($filename);
182 foreach (self::$blacklistedFiles[$group] as $key => $_filename) {
183 if ($filename == $_filename) {
184 unset(self::$blacklistedFiles[$group][$key]);
189 throw new RuntimeException($filename . ' does not exist');
194 * Adds a directory to the whitelist (recursively).
196 * @param string $directory
197 * @param string $suffix
198 * @throws RuntimeException
199 * @since Method available since Release 3.1.5
201 public static function addDirectoryToWhitelist($directory, $suffix = '.php')
203 if (file_exists($directory)) {
204 foreach (self::getIterator($directory, $suffix) as $file) {
205 self::addFileToWhitelist($file->getPathName());
208 throw new RuntimeException($directory . ' does not exist');
213 * Adds a new file to the whitelist.
215 * When the whitelist is empty (default), blacklisting is used.
216 * When the whitelist is not empty, whitelisting is used.
218 * @param string $filename
219 * @throws RuntimeException
220 * @since Method available since Release 3.1.0
222 public static function addFileToWhitelist($filename)
224 if (file_exists($filename)) {
225 $filename = realpath($filename);
227 if (!in_array($filename, self::$whitelistedFiles)) {
228 self::$whitelistedFiles[] = $filename;
231 throw new RuntimeException($filename . ' does not exist');
236 * Removes a directory from the whitelist (recursively).
238 * @param string $directory
239 * @param string $suffix
240 * @throws RuntimeException
241 * @since Method available since Release 3.1.5
243 public static function removeDirectoryFromWhitelist($directory, $suffix = '.php')
245 if (file_exists($directory)) {
246 foreach (self::getIterator($directory, $suffix) as $file) {
247 self::removeFileFromWhitelist($file->getPathName());
250 throw new RuntimeException($directory . ' does not exist');
255 * Removes a file from the whitelist.
257 * @param string $filename
258 * @throws RuntimeException
259 * @since Method available since Release 3.1.0
261 public static function removeFileFromWhitelist($filename)
263 if (file_exists($filename)) {
264 $filename = realpath($filename);
266 foreach (self::$whitelistedFiles as $key => $_filename) {
267 if ($filename == $_filename) {
268 unset(self::$whitelistedFiles[$key]);
272 throw new RuntimeException($filename . ' does not exist');
277 * Returns data about files within code coverage information, specifically
278 * which ones will be filtered out and which ones may be whitelisted but not
279 * touched by coverage.
281 * Returns a two-item array. The first item is an array indexed by filenames
282 * with a boolean payload of whether they should be filtered out.
284 * The second item is an array of filenames which are
285 * whitelisted but which are absent from the coverage information.
287 * @param array $codeCoverageInformation
288 * @param boolean $filterTests
291 public static function getFileCodeCoverageDisposition(array $codeCoverageInformation, $filterTests = TRUE)
293 if (!self::$filter) {
294 return array(array(), array());
297 $isFilteredCache = array();
298 $coveredFiles = array();
300 foreach ($codeCoverageInformation as $k => $test) {
301 foreach (array_keys($test['executable']) as $file) {
302 if (!isset($isFilteredCache[$file])) {
303 $isFilteredCache[$file] = self::isFiltered(
310 $coveredFiles = array_keys($isFilteredCache);
311 $missedFiles = array_diff(self::$whitelistedFiles, $coveredFiles);
312 $missedFiles = array_filter($missedFiles, 'file_exists');
314 return array($isFilteredCache, $missedFiles);
318 * @param array $codeCoverageInformation
319 * @param boolean $filterTests
322 public static function getFilteredCodeCoverage(array $codeCoverageInformation, $filterTests = TRUE)
325 list($isFilteredCache, $missedFiles) = self::getFileCodeCoverageDisposition(
326 $codeCoverageInformation, $filterTests
329 foreach ($codeCoverageInformation as $k => $test) {
330 foreach (array_keys($test['files']) as $file) {
331 if ($isFilteredCache[$file]) {
332 unset($codeCoverageInformation[$k]['files'][$file]);
336 foreach (array_keys($test['dead']) as $file) {
337 if ($isFilteredCache[$file]) {
338 unset($codeCoverageInformation[$k]['dead'][$file]);
342 foreach (array_keys($test['executable']) as $file) {
343 if ($isFilteredCache[$file]) {
344 unset($codeCoverageInformation[$k]['executable'][$file]);
349 if (self::$addUncoveredFilesFromWhitelist) {
350 foreach (self::$whitelistedFiles as $whitelistedFile) {
351 if (!isset(self::$coveredFiles[$whitelistedFile])) {
352 if (file_exists($whitelistedFile)) {
353 xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);
354 include_once $whitelistedFile;
355 $coverage = xdebug_get_code_coverage();
356 xdebug_stop_code_coverage();
358 foreach ($coverage as $file => $fileCoverage)
360 if (!in_array($file, self::$whitelistedFiles) || isset(self::$coveredFiles[$file]))
363 foreach ($fileCoverage as $line => $flag) {
365 $fileCoverage[$line] = -1;
369 $codeCoverageInformation[] = array(
372 $file => $fileCoverage
376 self::addCoveredFile($file);
384 return $codeCoverageInformation;
388 * Filters stack frames from PHPUnit classes.
390 * @param Exception $e
391 * @param boolean $filterTests
392 * @param boolean $asString
395 public static function getFilteredStacktrace(Exception $e, $filterTests = TRUE, $asString = TRUE)
397 if ($asString === TRUE) {
398 $filteredStacktrace = '';
400 $filteredStacktrace = array();
403 $eTrace = $e->getTrace();
405 if (!self::frameExists($eTrace, $e->getFile(), $e->getLine())) {
407 $eTrace, array('file' => $e->getFile(), 'line' => $e->getLine())
411 foreach ($eTrace as $frame) {
412 if (!self::$filter || (isset($frame['file']) && !self::isFiltered($frame['file'], $filterTests, TRUE))) {
413 if ($asString === TRUE) {
414 $filteredStacktrace .= sprintf(
418 isset($frame['line']) ? $frame['line'] : '?'
421 $filteredStacktrace[] = $frame;
426 return $filteredStacktrace;
430 * Activates or deactivates filtering.
432 * @param boolean $filter
433 * @throws InvalidArgumentException
434 * @since Method available since Release 3.0.0
436 public static function setFilter($filter)
438 if (is_bool($filter)) {
439 self::$filter = $filter;
441 throw new InvalidArgumentException;
446 * Returns a PHPUnit_Util_FilterIterator that iterates
447 * over all files in the given directory that have the
450 * @param string $directory
451 * @param string $suffix
453 * @since Method available since Release 3.1.5
455 protected static function getIterator($directory, $suffix)
457 return new PHPUnit_Util_FilterIterator(
458 new RecursiveIteratorIterator(
459 new RecursiveDirectoryIterator($directory)
466 * @param string $filename
467 * @param boolean $filterTests
468 * @param boolean $ignoreWhitelist
470 * @since Method available since Release 2.1.3
472 public static function isFiltered($filename, $filterTests = TRUE, $ignoreWhitelist = FALSE)
474 $filename = realpath($filename);
477 if ($ignoreWhitelist || empty(self::$whitelistedFiles)) {
478 $blacklistedFiles = self::$blacklistedFiles['DEFAULT'];
481 $blacklistedFiles = array_merge(
483 self::$blacklistedFiles['TESTS']
487 if (self::$filterPHPUnit) {
488 $blacklistedFiles = array_merge(
490 self::$blacklistedFiles['PHPUNIT']
494 if (in_array($filename, $blacklistedFiles)) {
498 foreach ($blacklistedFiles as $filteredFile) {
499 if (strpos($filename, $filteredFile) !== FALSE) {
510 if (in_array($filename, self::$whitelistedFiles)) {
519 * Adds a file to the list of covered files.
521 * @param string $filename
522 * @since Method available since Release 3.3.0
524 public static function addCoveredFile($filename)
526 self::$coveredFiles[$filename] = TRUE;
530 * Returns the list of covered files.
533 * @since Method available since Release 3.3.0
535 public static function getCoveredFiles()
537 return self::$coveredFiles;
541 * @param array $trace
542 * @param string $file
545 * @since Method available since Release 3.3.2
547 public static function frameExists(array $trace, $file, $line)
549 foreach ($trace as $frame) {
550 if (isset($frame['file']) && $frame['file'] == $file &&
551 isset($frame['line']) && $frame['line'] == $line) {
560 PHPUnit_Util_Filter::addFileToFilter(__FILE__, 'PHPUNIT');