]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - tests/PHPUnit/PHP/CodeCoverage/Report/HTML.php
Release 6.2.0
[Github/sugarcrm.git] / tests / PHPUnit / PHP / CodeCoverage / Report / HTML.php
1 <?php
2 /**
3  * PHP_CodeCoverage
4  *
5  * Copyright (c) 2009-2011, 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-2011 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 require_once 'PHP/CodeCoverage.php';
47 require_once 'PHP/CodeCoverage/Report/HTML/Node.php';
48 require_once 'Text/Template.php';
49
50 /**
51  * Generates an HTML report from an PHP_CodeCoverage object.
52  *
53  * @category   PHP
54  * @package    CodeCoverage
55  * @author     Sebastian Bergmann <sb@sebastian-bergmann.de>
56  * @copyright  2009-2011 Sebastian Bergmann <sb@sebastian-bergmann.de>
57  * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
58  * @version    Release: 1.0.4
59  * @link       http://github.com/sebastianbergmann/php-code-coverage
60  * @since      Class available since Release 1.0.0
61  */
62 class PHP_CodeCoverage_Report_HTML
63 {
64     /**
65      * @var string
66      */
67     public static $templatePath;
68
69     /**
70      * @var array
71      */
72     protected $options;
73
74     /**
75      * Constructor.
76      *
77      * @param array $options
78      */
79     public function __construct(array $options = array())
80     {
81         if (!isset($options['title'])) {
82             $options['title'] = '';
83         }
84
85         if (!isset($options['charset'])) {
86             $options['charset'] = 'UTF-8';
87         }
88
89         if (!isset($options['yui'])) {
90             $options['yui'] = TRUE;
91         }
92
93         if (!isset($options['highlight'])) {
94             $options['highlight'] = FALSE;
95         }
96
97         if (!isset($options['lowUpperBound'])) {
98             $options['lowUpperBound'] = 35;
99         }
100
101         if (!isset($options['highLowerBound'])) {
102             $options['highLowerBound'] = 70;
103         }
104
105         if (!isset($options['generator'])) {
106             $options['generator'] = '';
107         }
108
109         $this->options = $options;
110
111         self::$templatePath = sprintf(
112           '%s%sHTML%sTemplate%s',
113
114           dirname(__FILE__),
115           DIRECTORY_SEPARATOR,
116           DIRECTORY_SEPARATOR,
117           DIRECTORY_SEPARATOR
118         );
119     }
120
121     /**
122      * @param PHP_CodeCoverage $coverage
123      * @param string           $target
124      */
125     public function process(PHP_CodeCoverage $coverage, $target)
126     {
127         $target     = PHP_CodeCoverage_Util::getDirectory($target);
128         $files      = $coverage->getSummary();
129         $commonPath = PHP_CodeCoverage_Util::reducePaths($files);
130         $items      = PHP_CodeCoverage_Util::buildDirectoryStructure($files);
131         $root       = new PHP_CodeCoverage_Report_HTML_Node_Directory(
132                         $commonPath, NULL
133                       );
134
135         $this->addItems($root, $items);
136
137         $this->renderDashboard(
138           $root, $target . 'index.dashboard.html', $this->options['title']
139         );
140
141         foreach ($root as $node) {
142             if ($node instanceof PHP_CodeCoverage_Report_HTML_Node_Directory) {
143                 $this->renderDashboard(
144                   $node,
145                   $target . PHP_CodeCoverage_Util::getSafeFilename(
146                               $node->getId()
147                             ) . '.dashboard.html',
148                   $node->getName(TRUE)
149                 );
150             }
151         }
152
153         $root->render(
154           $target,
155           $this->options['title'],
156           $this->options['charset'],
157           $this->options['lowUpperBound'],
158           $this->options['highLowerBound'],
159           $this->options['generator']
160         );
161
162         $this->copyFiles($target);
163     }
164
165     /**
166      * @param PHP_CodeCoverage_Report_HTML_Node_Directory $root
167      * @param string                                      $file
168      * @param string                                      $title
169      */
170     protected function renderDashboard(PHP_CodeCoverage_Report_HTML_Node_Directory $root, $file, $title)
171     {
172         $classes  = $this->classes($root);
173         $template = new Text_Template(
174           PHP_CodeCoverage_Report_HTML::$templatePath . 'dashboard.html'
175         );
176
177         $template->setVar(
178           array(
179             'title'                  => $title,
180             'charset'                => $this->options['charset'],
181             'date'                   => date(
182                                           'D M j G:i:s T Y',
183                                           $_SERVER['REQUEST_TIME']
184                                         ),
185             'version'                => '1.0.4',
186             'php_version'            => PHP_VERSION,
187             'generator'              => $this->options['generator'],
188             'least_tested_methods'   => $this->leastTestedMethods($classes),
189             'top_project_risks'      => $this->topProjectRisks($classes),
190             'cc_values'              => $this->classComplexity($classes),
191             'ccd_values'             => $this->classCoverageDistribution($classes),
192             'backlink'               => basename(str_replace('.dashboard', '', $file))
193           )
194         );
195
196         $template->renderTo($file);
197     }
198
199     /**
200      * @param PHP_CodeCoverage_Report_HTML_Node_Directory $root
201      * @param array                                       $items
202      */
203     protected function addItems(PHP_CodeCoverage_Report_HTML_Node_Directory $root, array $items)
204     {
205         foreach ($items as $key => $value) {
206             if (substr($key, -2) == '/f') {
207                 try {
208                     $root->addFile(
209                       substr($key, 0, -2),
210                       $value,
211                       $this->options['yui'],
212                       $this->options['highlight']
213                     );
214                 }
215
216                 catch (RuntimeException $e) {
217                     continue;
218                 }
219             } else {
220                 $child = $root->addDirectory($key);
221                 $this->addItems($child, $value);
222             }
223         }
224     }
225
226     /**
227      * Returns the classes.
228      *
229      * @param  PHP_CodeCoverage_Report_HTML_Node_Directory $root
230      * @return array
231      */
232     protected function classes(PHP_CodeCoverage_Report_HTML_Node_Directory $root)
233     {
234         $classes = array();
235
236         foreach ($root as $node) {
237             if ($node instanceof PHP_CodeCoverage_Report_HTML_Node_File) {
238                 $classes = array_merge($classes, $node->getClasses());
239             }
240         }
241
242         if (isset($classes['*'])) {
243             unset($classes['*']);
244         }
245
246         return $classes;
247     }
248
249     /**
250      * Returns the data for the Class Complexity chart.
251      *
252      * @param  array $classes
253      * @return string
254      */
255     protected function classComplexity(array $classes)
256     {
257         $data = array();
258
259         foreach ($classes as $name => $class) {
260             $data[] = array($class['coverage'], $class['ccn'], 'blue', $name);
261         }
262
263         return json_encode($data);
264     }
265
266     /**
267      * Returns the data for the Class Coverage Distribution chart.
268      *
269      * @param  array $classes
270      * @return string
271      */
272     protected function classCoverageDistribution(array $classes)
273     {
274         $data = array(
275           '0%'      => 0,
276           '0-10%'   => 0,
277           '10-20%'  => 0,
278           '20-30%'  => 0,
279           '30-40%'  => 0,
280           '40-50%'  => 0,
281           '50-60%'  => 0,
282           '60-70%'  => 0,
283           '70-80%'  => 0,
284           '80-90%'  => 0,
285           '90-100%' => 0,
286           '100%'    => 0
287         );
288
289         foreach ($classes as $class) {
290             if ($class['coverage'] == 0) {
291                 $data['0%']++;
292             }
293
294             else if ($class['coverage'] == 100) {
295                 $data['100%']++;
296             }
297
298             else {
299                 $key = floor($class['coverage']/10)*10;
300                 $key = $key . '-' . ($key + 10) . '%';
301                 $data[$key]++;
302             }
303         }
304
305         return json_encode(array_values($data));
306     }
307
308     /**
309      * @param string $target
310      */
311     protected function copyFiles($target)
312     {
313         $files = array(
314           'butter.png',
315           'chameleon.png',
316           'close12_1.gif',
317           'container.css',
318           'container-min.js',
319           'directory.png',
320           'excanvas.compressed.js',
321           'file.png',
322           'glass.png',
323           'RGraph.bar.js',
324           'RGraph.common.core.js',
325           'RGraph.common.tooltips.js',
326           'RGraph.scatter.js',
327           'scarlet_red.png',
328           'snow.png',
329           'style.css',
330           'yahoo-dom-event.js'
331         );
332
333         foreach ($files as $file) {
334             copy(self::$templatePath . $file, $target . $file);
335         }
336     }
337
338     /**
339      * Returns the least tested methods.
340      *
341      * @param  array   $classes
342      * @param  integer $max
343      * @return string
344      */
345     protected function leastTestedMethods(array $classes, $max = 10)
346     {
347         $methods = array();
348
349         foreach ($classes as $className => $class) {
350             foreach ($class['methods'] as $methodName => $method) {
351                 if ($method['coverage'] < 100) {
352                     if ($className != '*') {
353                         $key = $className . '::' . $methodName;
354                     } else {
355                         $key = $methodName;
356                     }
357
358                     $methods[$key] = $method['coverage'];
359                 }
360             }
361         }
362
363         asort($methods);
364
365         $methods = array_slice($methods, 0, min($max, count($methods)));
366         $buffer  = '';
367
368         foreach ($methods as $name => $coverage) {
369             list($class, $method) = explode('::', $name);
370
371             $buffer .= sprintf(
372               '              <li><a href="%s">%s</a> (%d%%)</li>' . "\n",
373               $classes[$class]['methods'][$method]['file'],
374               $name,
375               $coverage
376             );
377         }
378
379         return $buffer;
380     }
381
382     /**
383      * Returns the top project risks according to the CRAP index.
384      *
385      * @param  array   $classes
386      * @param  integer $max
387      * @return string
388      */
389     protected function topProjectRisks(array $classes, $max = 10)
390     {
391         $risks = array();
392
393         foreach ($classes as $className => $class) {
394             if ($class['coverage'] < 100 &&
395                 $class['ccn'] > count($class['methods'])) {
396                 $risks[$className] = $class['crap'];
397             }
398         }
399
400         asort($risks);
401
402         $risks = array_reverse(
403           array_slice($risks, 0, min($max, count($risks)))
404         );
405
406         $buffer = '';
407
408         foreach ($risks as $name => $crap) {
409             $buffer .= sprintf(
410               '              <li><a href="%s">%s</a> (%d)</li>' . "\n",
411               $classes[$name]['file'],
412               $name,
413               $crap
414             );
415         }
416
417         return $buffer;
418     }
419 }