File.php

File

drupal/core/vendor/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Node/File.php
View source
<?php

/**
 * PHP_CodeCoverage
 *
 * Copyright (c) 2009-2013, Sebastian Bergmann <sebastian@phpunit.de>.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *
 *   * Neither the name of Sebastian Bergmann nor the names of his
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * @category   PHP
 * @package    CodeCoverage
 * @author     Sebastian Bergmann <sebastian@phpunit.de>
 * @copyright  2009-2013 Sebastian Bergmann <sebastian@phpunit.de>
 * @license    http://www.opensource.org/licenses/BSD-3-Clause  The BSD 3-Clause License
 * @link       http://github.com/sebastianbergmann/php-code-coverage
 * @since      File available since Release 1.1.0
 */

/**
 * Represents a file in the code coverage information tree.
 *
 * @category   PHP
 * @package    CodeCoverage
 * @author     Sebastian Bergmann <sebastian@phpunit.de>
 * @copyright  2009-2013 Sebastian Bergmann <sebastian@phpunit.de>
 * @license    http://www.opensource.org/licenses/BSD-3-Clause  The BSD 3-Clause License
 * @link       http://github.com/sebastianbergmann/php-code-coverage
 * @since      Class available since Release 1.1.0
 */
class PHP_CodeCoverage_Report_Node_File extends PHP_CodeCoverage_Report_Node {

  /**
   * @var array
   */
  protected $coverageData;

  /**
   * @var array
   */
  protected $testData;

  /**
   * @var array
   */
  protected $ignoredLines;

  /**
   * @var integer
   */
  protected $numExecutableLines = 0;

  /**
   * @var integer
   */
  protected $numExecutedLines = 0;

  /**
   * @var array
   */
  protected $classes = array();

  /**
   * @var array
   */
  protected $traits = array();

  /**
   * @var array
   */
  protected $functions = array();

  /**
   * @var array
   */
  protected $linesOfCode = array();

  /**
   * @var integer
   */
  protected $numTestedTraits = 0;

  /**
   * @var integer
   */
  protected $numTestedClasses = 0;

  /**
   * @var integer
   */
  protected $numMethods = NULL;

  /**
   * @var integer
   */
  protected $numTestedMethods = NULL;

  /**
   * @var integer
   */
  protected $numTestedFunctions = NULL;

  /**
   * @var array
   */
  protected $startLines = array();

  /**
   * @var array
   */
  protected $endLines = array();

  /**
   * @var boolean
   */
  protected $cacheTokens;

  /**
   * Constructor.
   *
   * @param  string                       $name
   * @param  PHP_CodeCoverage_Report_Node $parent
   * @param  array                        $coverageData
   * @param  array                        $testData
   * @param  boolean                      $cacheTokens
   * @throws PHP_CodeCoverage_Exception
   */
  public function __construct($name, PHP_CodeCoverage_Report_Node $parent, array $coverageData, array $testData, $cacheTokens) {
    if (!is_bool($cacheTokens)) {
      throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory(1, 'boolean');
    }
    parent::__construct($name, $parent);
    $this->coverageData = $coverageData;
    $this->testData = $testData;
    $this->ignoredLines = PHP_CodeCoverage_Util::getLinesToBeIgnored($this
      ->getPath(), $cacheTokens);
    $this->cacheTokens = $cacheTokens;
    $this
      ->calculateStatistics();
  }

  /**
   * Returns the number of files in/under this node.
   *
   * @return integer
   */
  public function count() {
    return 1;
  }

  /**
   * Returns the code coverage data of this node.
   *
   * @return array
   */
  public function getCoverageData() {
    return $this->coverageData;
  }

  /**
   * Returns the test data of this node.
   *
   * @return array
   */
  public function getTestData() {
    return $this->testData;
  }

  /**
   * @return array
   */
  public function getIgnoredLines() {
    return $this->ignoredLines;
  }

  /**
   * Returns the classes of this node.
   *
   * @return array
   */
  public function getClasses() {
    return $this->classes;
  }

  /**
   * Returns the traits of this node.
   *
   * @return array
   */
  public function getTraits() {
    return $this->traits;
  }

  /**
   * Returns the functions of this node.
   *
   * @return array
   */
  public function getFunctions() {
    return $this->functions;
  }

  /**
   * Returns the LOC/CLOC/NCLOC of this node.
   *
   * @return array
   */
  public function getLinesOfCode() {
    return $this->linesOfCode;
  }

  /**
   * Returns the number of executable lines.
   *
   * @return integer
   */
  public function getNumExecutableLines() {
    return $this->numExecutableLines;
  }

  /**
   * Returns the number of executed lines.
   *
   * @return integer
   */
  public function getNumExecutedLines() {
    return $this->numExecutedLines;
  }

  /**
   * Returns the number of classes.
   *
   * @return integer
   */
  public function getNumClasses() {
    return count($this->classes);
  }

  /**
   * Returns the number of tested classes.
   *
   * @return integer
   */
  public function getNumTestedClasses() {
    return $this->numTestedClasses;
  }

  /**
   * Returns the number of traits.
   *
   * @return integer
   */
  public function getNumTraits() {
    return count($this->traits);
  }

  /**
   * Returns the number of tested traits.
   *
   * @return integer
   */
  public function getNumTestedTraits() {
    return $this->numTestedTraits;
  }

  /**
   * Returns the number of methods.
   *
   * @return integer
   */
  public function getNumMethods() {
    if ($this->numMethods === NULL) {
      $this->numMethods = 0;
      foreach ($this->classes as $class) {
        foreach ($class['methods'] as $method) {
          if ($method['executableLines'] > 0) {
            $this->numMethods++;
          }
        }
      }
      foreach ($this->traits as $trait) {
        foreach ($trait['methods'] as $method) {
          if ($method['executableLines'] > 0) {
            $this->numMethods++;
          }
        }
      }
    }
    return $this->numMethods;
  }

  /**
   * Returns the number of tested methods.
   *
   * @return integer
   */
  public function getNumTestedMethods() {
    if ($this->numTestedMethods === NULL) {
      $this->numTestedMethods = 0;
      foreach ($this->classes as $class) {
        foreach ($class['methods'] as $method) {
          if ($method['executableLines'] > 0 && $method['coverage'] == 100) {
            $this->numTestedMethods++;
          }
        }
      }
      foreach ($this->traits as $trait) {
        foreach ($trait['methods'] as $method) {
          if ($method['executableLines'] > 0 && $method['coverage'] == 100) {
            $this->numTestedMethods++;
          }
        }
      }
    }
    return $this->numTestedMethods;
  }

  /**
   * Returns the number of functions.
   *
   * @return integer
   */
  public function getNumFunctions() {
    return count($this->functions);
  }

  /**
   * Returns the number of tested functions.
   *
   * @return integer
   */
  public function getNumTestedFunctions() {
    if ($this->numTestedFunctions === NULL) {
      $this->numTestedFunctions = 0;
      foreach ($this->functions as $function) {
        if ($function['executableLines'] > 0 && $function['coverage'] == 100) {
          $this->numTestedFunctions++;
        }
      }
    }
    return $this->numTestedFunctions;
  }

  /**
   * Calculates coverage statistics for the file.
   */
  protected function calculateStatistics() {
    if ($this->cacheTokens) {
      $tokens = PHP_Token_Stream_CachingFactory::get($this
        ->getPath());
    }
    else {
      $tokens = new PHP_Token_Stream($this
        ->getPath());
    }
    $this
      ->processClasses($tokens);
    $this
      ->processTraits($tokens);
    $this
      ->processFunctions($tokens);
    $this->linesOfCode = $tokens
      ->getLinesOfCode();
    unset($tokens);
    for ($lineNumber = 1; $lineNumber <= $this->linesOfCode['loc']; $lineNumber++) {
      if (isset($this->startLines[$lineNumber])) {

        // Start line of a class.
        if (isset($this->startLines[$lineNumber]['className'])) {
          $currentClass =& $this->startLines[$lineNumber];
        }
        else {
          if (isset($this->startLines[$lineNumber]['traitName'])) {
            $currentTrait =& $this->startLines[$lineNumber];
          }
          else {
            if (isset($this->startLines[$lineNumber]['methodName'])) {
              $currentMethod =& $this->startLines[$lineNumber];
            }
            else {
              if (isset($this->startLines[$lineNumber]['functionName'])) {
                $currentFunction =& $this->startLines[$lineNumber];
              }
            }
          }
        }
      }
      if (!isset($this->ignoredLines[$lineNumber]) && isset($this->coverageData[$lineNumber]) && $this->coverageData[$lineNumber] !== NULL) {
        if (isset($currentClass)) {
          $currentClass['executableLines']++;
        }
        if (isset($currentTrait)) {
          $currentTrait['executableLines']++;
        }
        if (isset($currentMethod)) {
          $currentMethod['executableLines']++;
        }
        if (isset($currentFunction)) {
          $currentFunction['executableLines']++;
        }
        $this->numExecutableLines++;
        if (count($this->coverageData[$lineNumber]) > 0 || isset($this->ignoredLines[$lineNumber])) {
          if (isset($currentClass)) {
            $currentClass['executedLines']++;
          }
          if (isset($currentTrait)) {
            $currentTrait['executedLines']++;
          }
          if (isset($currentMethod)) {
            $currentMethod['executedLines']++;
          }
          if (isset($currentFunction)) {
            $currentFunction['executedLines']++;
          }
          $this->numExecutedLines++;
        }
      }
      if (isset($this->endLines[$lineNumber])) {

        // End line of a class.
        if (isset($this->endLines[$lineNumber]['className'])) {
          unset($currentClass);
        }
        else {
          if (isset($this->endLines[$lineNumber]['traitName'])) {
            unset($currentTrait);
          }
          else {
            if (isset($this->endLines[$lineNumber]['methodName'])) {
              unset($currentMethod);
            }
            else {
              if (isset($this->endLines[$lineNumber]['functionName'])) {
                unset($currentFunction);
              }
            }
          }
        }
      }
    }
    foreach ($this->traits as &$trait) {
      foreach ($trait['methods'] as &$method) {
        if ($method['executableLines'] > 0) {
          $method['coverage'] = $method['executedLines'] / $method['executableLines'] * 100;
        }
        else {
          $method['coverage'] = 100;
        }
        $method['crap'] = $this
          ->crap($method['ccn'], $method['coverage']);
        $trait['ccn'] += $method['ccn'];
      }
      if ($trait['executableLines'] > 0) {
        $trait['coverage'] = $trait['executedLines'] / $trait['executableLines'] * 100;
      }
      else {
        $trait['coverage'] = 100;
      }
      if ($trait['coverage'] == 100) {
        $this->numTestedClasses++;
      }
      $trait['crap'] = $this
        ->crap($trait['ccn'], $trait['coverage']);
    }
    foreach ($this->classes as &$class) {
      foreach ($class['methods'] as &$method) {
        if ($method['executableLines'] > 0) {
          $method['coverage'] = $method['executedLines'] / $method['executableLines'] * 100;
        }
        else {
          $method['coverage'] = 100;
        }
        $method['crap'] = $this
          ->crap($method['ccn'], $method['coverage']);
        $class['ccn'] += $method['ccn'];
      }
      if ($class['executableLines'] > 0) {
        $class['coverage'] = $class['executedLines'] / $class['executableLines'] * 100;
      }
      else {
        $class['coverage'] = 100;
      }
      if ($class['coverage'] == 100) {
        $this->numTestedClasses++;
      }
      $class['crap'] = $this
        ->crap($class['ccn'], $class['coverage']);
    }
  }

  /**
   * @param PHP_Token_Stream $tokens
   */
  protected function processClasses(PHP_Token_Stream $tokens) {
    $classes = $tokens
      ->getClasses();
    unset($tokens);
    $link = $this
      ->getId() . '.html#';
    foreach ($classes as $className => $class) {
      $this->classes[$className] = array(
        'className' => $className,
        'methods' => array(),
        'startLine' => $class['startLine'],
        'executableLines' => 0,
        'executedLines' => 0,
        'ccn' => 0,
        'coverage' => 0,
        'crap' => 0,
        'package' => $class['package'],
        'link' => $link . $class['startLine'],
      );
      $this->startLines[$class['startLine']] =& $this->classes[$className];
      $this->endLines[$class['endLine']] =& $this->classes[$className];
      foreach ($class['methods'] as $methodName => $method) {
        $this->classes[$className]['methods'][$methodName] = array(
          'methodName' => $methodName,
          'signature' => $method['signature'],
          'startLine' => $method['startLine'],
          'endLine' => $method['endLine'],
          'executableLines' => 0,
          'executedLines' => 0,
          'ccn' => $method['ccn'],
          'coverage' => 0,
          'crap' => 0,
          'link' => $link . $method['startLine'],
        );
        $this->startLines[$method['startLine']] =& $this->classes[$className]['methods'][$methodName];
        $this->endLines[$method['endLine']] =& $this->classes[$className]['methods'][$methodName];
      }
    }
  }

  /**
   * @param PHP_Token_Stream $tokens
   */
  protected function processTraits(PHP_Token_Stream $tokens) {
    $traits = $tokens
      ->getTraits();
    unset($tokens);
    $link = $this
      ->getId() . '.html#';
    foreach ($traits as $traitName => $trait) {
      $this->traits[$traitName] = array(
        'traitName' => $traitName,
        'methods' => array(),
        'startLine' => $trait['startLine'],
        'executableLines' => 0,
        'executedLines' => 0,
        'ccn' => 0,
        'coverage' => 0,
        'crap' => 0,
        'package' => $trait['package'],
        'link' => $link . $trait['startLine'],
      );
      $this->startLines[$trait['startLine']] =& $this->traits[$traitName];
      $this->endLines[$trait['endLine']] =& $this->traits[$traitName];
      foreach ($trait['methods'] as $methodName => $method) {
        $this->traits[$traitName]['methods'][$methodName] = array(
          'methodName' => $methodName,
          'signature' => $method['signature'],
          'startLine' => $method['startLine'],
          'endLine' => $method['endLine'],
          'executableLines' => 0,
          'executedLines' => 0,
          'ccn' => $method['ccn'],
          'coverage' => 0,
          'crap' => 0,
          'link' => $link . $method['startLine'],
        );
        $this->startLines[$method['startLine']] =& $this->traits[$traitName]['methods'][$methodName];
        $this->endLines[$method['endLine']] =& $this->traits[$traitName]['methods'][$methodName];
      }
    }
  }

  /**
   * @param PHP_Token_Stream $tokens
   */
  protected function processFunctions(PHP_Token_Stream $tokens) {
    $functions = $tokens
      ->getFunctions();
    unset($tokens);
    $link = $this
      ->getId() . '.html#';
    foreach ($functions as $functionName => $function) {
      $this->functions[$functionName] = array(
        'functionName' => $functionName,
        'signature' => $function['signature'],
        'startLine' => $function['startLine'],
        'executableLines' => 0,
        'executedLines' => 0,
        'ccn' => $function['ccn'],
        'coverage' => 0,
        'crap' => 0,
        'link' => $link . $function['startLine'],
      );
      $this->startLines[$function['startLine']] =& $this->functions[$functionName];
      $this->endLines[$function['endLine']] =& $this->functions[$functionName];
    }
  }

  /**
   * Calculates the Change Risk Anti-Patterns (CRAP) index for a unit of code
   * based on its cyclomatic complexity and percentage of code coverage.
   *
   * @param  integer $ccn
   * @param  float   $coverage
   * @return string
   * @since  Method available since Release 1.2.0
   */
  protected function crap($ccn, $coverage) {
    if ($coverage == 0) {
      return (string) pow($ccn, 2) + $ccn;
    }
    if ($coverage >= 95) {
      return (string) $ccn;
    }
    return sprintf('%01.2F', pow($ccn, 2) * pow(1 - $coverage / 100, 3) + $ccn);
  }

}

Classes

Namesort descending Description
PHP_CodeCoverage_Report_Node_File Represents a file in the code coverage information tree.