]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - tests/PHPUnit/Util/Filter.php
Added unit tests.
[Github/sugarcrm.git] / tests / PHPUnit / Util / Filter.php
1 <?php
2 /**
3  * PHPUnit
4  *
5  * Copyright (c) 2002-2009, 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   Testing
38  * @package    PHPUnit
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
42
43  * @link       http://www.phpunit.de/
44  * @since      File available since Release 2.0.0
45  */
46
47 require_once 'PHPUnit/Util/FilterIterator.php';
48
49 /**
50  * Utility class for code filtering.
51  *
52  * @category   Testing
53  * @package    PHPUnit
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
60  */
61 class PHPUnit_Util_Filter
62 {
63     /**
64      * @var    boolean
65      */
66     public static $addUncoveredFilesFromWhitelist = TRUE;
67
68     /**
69      * @var    boolean
70      */
71     public static $filterPHPUnit = TRUE;
72
73     /**
74      * @var    boolean
75      */
76     protected static $filter = TRUE;
77
78     /**
79      * Source files that are blacklisted.
80      *
81      * @var    array
82      */
83     protected static $blacklistedFiles = array(
84       'DEFAULT' => array(),
85       'PHPUNIT' => array(),
86       'TESTS' => array()
87     );
88
89     /**
90      * Source files that are whitelisted.
91      *
92      * @var    array
93      */
94     protected static $whitelistedFiles = array();
95
96     /**
97      * List of covered files.
98      *
99      * @var    array
100      */
101     protected static $coveredFiles = array();
102
103     /**
104      * Adds a directory to the blacklist (recursively).
105      *
106      * @param  string $directory
107      * @param  string $suffix
108      * @param  string $group
109      * @throws RuntimeException
110      * @since  Method available since Release 3.1.5
111      */
112     public static function addDirectoryToFilter($directory, $suffix = '.php', $group = 'DEFAULT')
113     {
114         if (file_exists($directory)) {
115             foreach (self::getIterator($directory, $suffix) as $file) {
116                 self::addFileToFilter($file->getPathName(), $group);
117             }
118         } else {
119             throw new RuntimeException($directory . ' does not exist');
120         }
121     }
122
123     /**
124      * Adds a new file to be filtered (blacklist).
125      *
126      * @param  string $filename
127      * @param  string $group
128      * @throws RuntimeException
129      * @since  Method available since Release 2.1.0
130      */
131     public static function addFileToFilter($filename, $group = 'DEFAULT')
132     {
133         if (file_exists($filename)) {
134             $filename = realpath($filename);
135
136             if (!isset(self::$blacklistedFiles[$group])) {
137                 self::$blacklistedFiles[$group] = array($filename);
138             }
139
140             else if (!in_array($filename, self::$blacklistedFiles[$group])) {
141                 self::$blacklistedFiles[$group][] = $filename;
142             }
143         } else {
144             throw new RuntimeException($filename . ' does not exist');
145         }
146     }
147
148     /**
149      * Removes a directory from the blacklist (recursively).
150      *
151      * @param  string $directory
152      * @param  string $suffix
153      * @param  string $group
154      * @throws RuntimeException
155      * @since  Method available since Release 3.1.5
156      */
157     public static function removeDirectoryFromFilter($directory, $suffix = '.php', $group = 'DEFAULT')
158     {
159         if (file_exists($directory)) {
160             foreach (self::getIterator($directory, $suffix) as $file) {
161                 self::removeFileFromFilter($file->getPathName(), $group);
162             }
163         } else {
164             throw new RuntimeException($directory . ' does not exist');
165         }
166     }
167
168     /**
169      * Removes a file from the filter (blacklist).
170      *
171      * @param  string $filename
172      * @param  string $group
173      * @throws RuntimeException
174      * @since  Method available since Release 2.1.0
175      */
176     public static function removeFileFromFilter($filename, $group = 'DEFAULT')
177     {
178         if (file_exists($filename)) {
179             if (isset(self::$blacklistedFiles[$group])) {
180                 $filename = realpath($filename);
181
182                 foreach (self::$blacklistedFiles[$group] as $key => $_filename) {
183                     if ($filename == $_filename) {
184                         unset(self::$blacklistedFiles[$group][$key]);
185                     }
186                 }
187             }
188         } else {
189             throw new RuntimeException($filename . ' does not exist');
190         }
191     }
192
193     /**
194      * Adds a directory to the whitelist (recursively).
195      *
196      * @param  string $directory
197      * @param  string $suffix
198      * @throws RuntimeException
199      * @since  Method available since Release 3.1.5
200      */
201     public static function addDirectoryToWhitelist($directory, $suffix = '.php')
202     {
203         if (file_exists($directory)) {
204             foreach (self::getIterator($directory, $suffix) as $file) {
205                 self::addFileToWhitelist($file->getPathName());
206             }
207         } else {
208             throw new RuntimeException($directory . ' does not exist');
209         }
210     }
211
212     /**
213      * Adds a new file to the whitelist.
214      *
215      * When the whitelist is empty (default), blacklisting is used.
216      * When the whitelist is not empty, whitelisting is used.
217      *
218      * @param  string $filename
219      * @throws RuntimeException
220      * @since  Method available since Release 3.1.0
221      */
222     public static function addFileToWhitelist($filename)
223     {
224         if (file_exists($filename)) {
225             $filename = realpath($filename);
226
227             if (!in_array($filename, self::$whitelistedFiles)) {
228                 self::$whitelistedFiles[] = $filename;
229             }
230         } else {
231             throw new RuntimeException($filename . ' does not exist');
232         }
233     }
234
235     /**
236      * Removes a directory from the whitelist (recursively).
237      *
238      * @param  string $directory
239      * @param  string $suffix
240      * @throws RuntimeException
241      * @since  Method available since Release 3.1.5
242      */
243     public static function removeDirectoryFromWhitelist($directory, $suffix = '.php')
244     {
245         if (file_exists($directory)) {
246             foreach (self::getIterator($directory, $suffix) as $file) {
247                 self::removeFileFromWhitelist($file->getPathName());
248             }
249         } else {
250             throw new RuntimeException($directory . ' does not exist');
251         }
252     }
253
254     /**
255      * Removes a file from the whitelist.
256      *
257      * @param  string $filename
258      * @throws RuntimeException
259      * @since  Method available since Release 3.1.0
260      */
261     public static function removeFileFromWhitelist($filename)
262     {
263         if (file_exists($filename)) {
264             $filename = realpath($filename);
265
266             foreach (self::$whitelistedFiles as $key => $_filename) {
267                 if ($filename == $_filename) {
268                     unset(self::$whitelistedFiles[$key]);
269                 }
270             }
271         } else {
272             throw new RuntimeException($filename . ' does not exist');
273         }
274     }
275
276     /**
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.
280      *
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.
283      *
284      * The second item is an array of filenames which are
285      * whitelisted but which are absent from the coverage information.
286      *
287      * @param  array   $codeCoverageInformation
288      * @param  boolean $filterTests
289      * @return array
290      */
291     public static function getFileCodeCoverageDisposition(array $codeCoverageInformation, $filterTests = TRUE)
292     {
293         if (!self::$filter) {
294             return array(array(), array());
295         }
296
297         $isFilteredCache = array();
298         $coveredFiles    = array();
299
300         foreach ($codeCoverageInformation as $k => $test) {
301             foreach (array_keys($test['executable']) as $file) {
302                 if (!isset($isFilteredCache[$file])) {
303                     $isFilteredCache[$file] = self::isFiltered(
304                       $file, $filterTests
305                     );
306                 }
307             }
308         }
309
310         $coveredFiles = array_keys($isFilteredCache);
311         $missedFiles  = array_diff(self::$whitelistedFiles, $coveredFiles);
312         $missedFiles  = array_filter($missedFiles, 'file_exists');
313
314         return array($isFilteredCache, $missedFiles);
315     }
316
317     /**
318      * @param  array   $codeCoverageInformation
319      * @param  boolean $filterTests
320      * @return array
321      */
322     public static function getFilteredCodeCoverage(array $codeCoverageInformation, $filterTests = TRUE)
323     {
324         if (self::$filter) {
325             list($isFilteredCache, $missedFiles) = self::getFileCodeCoverageDisposition(
326               $codeCoverageInformation, $filterTests
327             );
328
329             foreach ($codeCoverageInformation as $k => $test) {
330                 foreach (array_keys($test['files']) as $file) {
331                     if ($isFilteredCache[$file]) {
332                         unset($codeCoverageInformation[$k]['files'][$file]);
333                     }
334                 }
335
336                 foreach (array_keys($test['dead']) as $file) {
337                     if ($isFilteredCache[$file]) {
338                         unset($codeCoverageInformation[$k]['dead'][$file]);
339                     }
340                 }
341
342                 foreach (array_keys($test['executable']) as $file) {
343                     if ($isFilteredCache[$file]) {
344                         unset($codeCoverageInformation[$k]['executable'][$file]);
345                     }
346                 }
347             }
348
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();
357
358                             foreach ($coverage as $file => $fileCoverage)
359                             {
360                                 if (!in_array($file, self::$whitelistedFiles) || isset(self::$coveredFiles[$file]))
361                                     continue;
362
363                                 foreach ($fileCoverage as $line => $flag) {
364                                     if ($flag > 0) {
365                                         $fileCoverage[$line] = -1;
366                                     }
367                                 }
368
369                                 $codeCoverageInformation[] = array(
370                                   'test'  => NULL,
371                                   'files' => array(
372                                     $file => $fileCoverage
373                                   )
374                                 );
375
376                                 self::addCoveredFile($file);
377                             }
378                         }
379                     }
380                 }
381             }
382         }
383
384         return $codeCoverageInformation;
385     }
386
387     /**
388      * Filters stack frames from PHPUnit classes.
389      *
390      * @param  Exception $e
391      * @param  boolean   $filterTests
392      * @param  boolean   $asString
393      * @return string
394      */
395     public static function getFilteredStacktrace(Exception $e, $filterTests = TRUE, $asString = TRUE)
396     {
397         if ($asString === TRUE) {
398             $filteredStacktrace = '';
399         } else {
400             $filteredStacktrace = array();
401         }
402
403         $eTrace = $e->getTrace();
404
405         if (!self::frameExists($eTrace, $e->getFile(), $e->getLine())) {
406             array_unshift(
407               $eTrace, array('file' => $e->getFile(), 'line' => $e->getLine())
408             );
409         }
410
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(
415                       "%s:%s\n",
416
417                       $frame['file'],
418                       isset($frame['line']) ? $frame['line'] : '?'
419                     );
420                 } else {
421                     $filteredStacktrace[] = $frame;
422                 }
423             }
424         }
425
426         return $filteredStacktrace;
427     }
428
429     /**
430      * Activates or deactivates filtering.
431      *
432      * @param  boolean $filter
433      * @throws InvalidArgumentException
434      * @since  Method available since Release 3.0.0
435      */
436     public static function setFilter($filter)
437     {
438         if (is_bool($filter)) {
439             self::$filter = $filter;
440         } else {
441             throw new InvalidArgumentException;
442         }
443     }
444
445     /**
446      * Returns a PHPUnit_Util_FilterIterator that iterates
447      * over all files in the given directory that have the
448      * given suffix.
449      *
450      * @param  string $directory
451      * @param  string $suffix
452      * @return Iterator
453      * @since  Method available since Release 3.1.5
454      */
455     protected static function getIterator($directory, $suffix)
456     {
457         return new PHPUnit_Util_FilterIterator(
458           new RecursiveIteratorIterator(
459             new RecursiveDirectoryIterator($directory)
460           ),
461           $suffix
462         );
463     }
464
465     /**
466      * @param  string  $filename
467      * @param  boolean $filterTests
468      * @param  boolean $ignoreWhitelist
469      * @return boolean
470      * @since  Method available since Release 2.1.3
471      */
472     public static function isFiltered($filename, $filterTests = TRUE, $ignoreWhitelist = FALSE)
473     {
474         $filename = realpath($filename);
475
476         // Use blacklist.
477         if ($ignoreWhitelist || empty(self::$whitelistedFiles)) {
478             $blacklistedFiles = self::$blacklistedFiles['DEFAULT'];
479
480             if ($filterTests) {
481                 $blacklistedFiles = array_merge(
482                   $blacklistedFiles,
483                   self::$blacklistedFiles['TESTS']
484                 );
485             }
486
487             if (self::$filterPHPUnit) {
488                 $blacklistedFiles = array_merge(
489                   $blacklistedFiles,
490                   self::$blacklistedFiles['PHPUNIT']
491                 );
492             }
493
494             if (in_array($filename, $blacklistedFiles)) {
495                 return TRUE;
496             }
497
498             foreach ($blacklistedFiles as $filteredFile) {
499                 if (strpos($filename, $filteredFile) !== FALSE) {
500                     return TRUE;
501                 }
502             }
503
504             return FALSE;
505         }
506
507         // Use whitelist.
508         else
509         {
510             if (in_array($filename, self::$whitelistedFiles)) {
511                 return FALSE;
512             }
513
514             return TRUE;
515         }
516     }
517
518     /**
519      * Adds a file to the list of covered files.
520      *
521      * @param  string  $filename
522      * @since  Method available since Release 3.3.0
523      */
524     public static function addCoveredFile($filename)
525     {
526         self::$coveredFiles[$filename] = TRUE;
527     }
528
529     /**
530      * Returns the list of covered files.
531      *
532      * @return array
533      * @since  Method available since Release 3.3.0
534      */
535     public static function getCoveredFiles()
536     {
537         return self::$coveredFiles;
538     }
539
540     /**
541      * @param  array  $trace
542      * @param  string $file
543      * @param  int    $line
544      * @return boolean
545      * @since  Method available since Release 3.3.2
546      */
547     public static function frameExists(array $trace, $file, $line)
548     {
549         foreach ($trace as $frame) {
550             if (isset($frame['file']) && $frame['file'] == $file &&
551                 isset($frame['line']) && $frame['line'] == $line) {
552                 return TRUE;
553             }
554         }
555         
556         return FALSE;
557     }
558 }
559
560 PHPUnit_Util_Filter::addFileToFilter(__FILE__, 'PHPUNIT');
561 ?>