Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions src/CodeCoverage.php
Original file line number Diff line number Diff line change
Expand Up @@ -619,11 +619,13 @@ private function initializeFilesThatAreSeenTheFirstTime(array $data): void
foreach ($fileData['lines'] as $lineNumber => $flag) {
if ($flag === Driver::LINE_NOT_EXECUTABLE) {
$this->data[$file]['lines'][$lineNumber] = null;
} else {
$this->addCoverageLinePathCovered($file, $lineNumber, false);
}

}

foreach ($fileData['functions'] as $functionName => $functionData) {
// @todo - should this have a helper to merge covered paths?
$this->data[$file]['paths'][$functionName] = $functionData['paths'];

foreach ($functionData['branches'] as $branchIndex => $branchData) {
Expand Down Expand Up @@ -837,7 +839,6 @@ private function addUncoveredFilesFromWhitelist(): void
for ($line = 1; $line <= $lines; $line++) {
$data[$uncoveredFile]['lines'][$line] = Driver::LINE_NOT_EXECUTED;
}
// @todo - do the same here with functions and paths
}

$this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST');
Expand Down Expand Up @@ -1190,9 +1191,9 @@ private function initializeData(): void
continue;
}

foreach (\array_keys($fileCoverage) as $key) {
if ($fileCoverage[$key] === Driver::LINE_EXECUTED) {
$fileCoverage[$key] = Driver::LINE_NOT_EXECUTED;
foreach (\array_keys($fileCoverage['lines']) as $key) {
if ($fileCoverage['lines'][$key] === Driver::LINE_EXECUTED) {
$fileCoverage['lines'][$key] = Driver::LINE_NOT_EXECUTED;
}
}

Expand Down
45 changes: 25 additions & 20 deletions src/Node/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -377,16 +377,21 @@ private function calculateStatistics(): void
unset($tokens);

foreach (\range(1, $this->linesOfCode['loc']) as $lineNumber) {
if (isset($this->coverageData['lines'][$lineNumber])) {
foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) {
$codeUnit['executableLines']++;
}
// Check to see if we've identified this line as executed, not executed, or not executable
if (\array_key_exists($lineNumber, $this->coverageData['lines'])) {
// If the element is null, that indicates this line is not executable
if ($this->coverageData['lines'][$lineNumber] !== null) {
foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) {
$codeUnit['executableLines']++;
}

unset($codeUnit);

unset($codeUnit);
$this->numExecutableLines++;
}

$this->numExecutableLines++;

if (\count($this->coverageData['lines'][$lineNumber]) > 0) {
if ($this->coverageData['lines'][$lineNumber]['pathCovered'] === true) {
foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) {
$codeUnit['executedLines']++;
}
Expand Down Expand Up @@ -472,6 +477,19 @@ private function calcAndApplyClassAggregate(
foreach ($classOrTrait['methods'] as &$method) {
$methodName = $method['methodName'];

if ($method['executableLines'] > 0) {
$method['coverage'] = ($method['executedLines'] / $method['executableLines']) * 100;
} else {
$method['coverage'] = 100;
}

$method['crap'] = $this->crap(
$method['ccn'],
$method['coverage']
);

$classOrTrait['ccn'] += $method['ccn'];

if (isset($this->coverageData['paths'])) {
$methodCoveragePath = $methodName;

Expand Down Expand Up @@ -499,19 +517,6 @@ private function calcAndApplyClassAggregate(
$this->numTestedPaths += $numExexutedPaths;
}

if ($method['executableLines'] > 0) {
$method['coverage'] = ($method['executedLines'] /
$method['executableLines']) * 100;
} else {
$method['coverage'] = 100;
}

$method['crap'] = $this->crap(
$method['ccn'],
$method['coverage']
);

$classOrTrait['ccn'] += $method['ccn'];
}

if (isset($this->coverageData['branches'])) {
Expand Down
159 changes: 134 additions & 25 deletions src/Report/Text.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,18 @@ final class Text
*/
private $showOnlySummary;

public function __construct(int $lowUpperBound = 50, int $highLowerBound = 90, bool $showUncoveredFiles = false, bool $showOnlySummary = false)
/**
* @var bool
*/
private $determineBranchCoverage;

public function __construct(int $lowUpperBound = 50, int $highLowerBound = 90, bool $showUncoveredFiles = false, bool $showOnlySummary = false, bool $determineBranchCoverage = false)
{
$this->lowUpperBound = $lowUpperBound;
$this->highLowerBound = $highLowerBound;
$this->showUncoveredFiles = $showUncoveredFiles;
$this->showOnlySummary = $showOnlySummary;
$this->lowUpperBound = $lowUpperBound;
$this->highLowerBound = $highLowerBound;
$this->showUncoveredFiles = $showUncoveredFiles;
$this->showOnlySummary = $showOnlySummary;
$this->determineBranchCoverage = $determineBranchCoverage;
}

public function process(CodeCoverage $coverage, bool $showColors = false): string
Expand All @@ -84,12 +90,14 @@ public function process(CodeCoverage $coverage, bool $showColors = false): strin
$report = $coverage->getReport();

$colors = [
'header' => '',
'classes' => '',
'methods' => '',
'lines' => '',
'reset' => '',
'eol' => '',
'header' => '',
'classes' => '',
'methods' => '',
'lines' => '',
'branches' => '',
'paths' => '',
'reset' => '',
'eol' => '',
];

if ($showColors) {
Expand All @@ -108,13 +116,25 @@ public function process(CodeCoverage $coverage, bool $showColors = false): strin
$report->getNumExecutableLines()
);

if ($this->determineBranchCoverage) {
$colors['branches'] = $this->getCoverageColor(
$report->getNumTestedBranches(),
$report->getNumBranches()
);

$colors['paths'] = $this->getCoverageColor(
$report->getNumTestedPaths(),
$report->getNumPaths()
);
}

$colors['reset'] = self::COLOR_RESET;
$colors['header'] = self::COLOR_HEADER;
$colors['eol'] = self::COLOR_EOL;
}

$classes = \sprintf(
' Classes: %6s (%d/%d)',
' Classes: %6s (%d/%d)',
Util::percent(
$report->getNumTestedClassesAndTraits(),
$report->getNumClassesAndTraits(),
Expand All @@ -125,7 +145,7 @@ public function process(CodeCoverage $coverage, bool $showColors = false): strin
);

$methods = \sprintf(
' Methods: %6s (%d/%d)',
' Methods: %6s (%d/%d)',
Util::percent(
$report->getNumTestedMethods(),
$report->getNumMethods(),
Expand All @@ -136,7 +156,7 @@ public function process(CodeCoverage $coverage, bool $showColors = false): strin
);

$lines = \sprintf(
' Lines: %6s (%d/%d)',
' Lines: %6s (%d/%d)',
Util::percent(
$report->getNumExecutedLines(),
$report->getNumExecutableLines(),
Expand All @@ -146,6 +166,33 @@ public function process(CodeCoverage $coverage, bool $showColors = false): strin
$report->getNumExecutableLines()
);

$paths = '';
$branches = '';

if ($this->determineBranchCoverage) {
$branches = \sprintf(
' Branches: %6s (%d/%d)',
Util::percent(
$report->getNumTestedBranches(),
$report->getNumBranches(),
true
),
$report->getNumTestedBranches(),
$report->getNumBranches()
);

$paths = \sprintf(
' Paths: %6s (%d/%d)',
Util::percent(
$report->getNumTestedPaths(),
$report->getNumPaths(),
true
),
$report->getNumTestedPaths(),
$report->getNumPaths()
);
}

$padding = \max(\array_map('strlen', [$classes, $methods, $lines]));

if ($this->showOnlySummary) {
Expand All @@ -167,12 +214,22 @@ public function process(CodeCoverage $coverage, bool $showColors = false): strin
$output .= $this->format($colors['methods'], $padding, $methods);
$output .= $this->format($colors['lines'], $padding, $lines);

if ($this->determineBranchCoverage) {
$output .= $this->format($colors['branches'], $padding, $branches);
$output .= $this->format($colors['paths'], $padding, $paths);
}

if ($this->showOnlySummary) {
return $output . \PHP_EOL;
}

$classCoverage = [];

$maxMethods = 0;
$maxLines = 0;
$maxBranches = 0;
$maxPaths = 0;

foreach ($report as $item) {
if (!$item instanceof File) {
continue;
Expand All @@ -185,21 +242,56 @@ public function process(CodeCoverage $coverage, bool $showColors = false): strin
$coveredClassStatements = 0;
$coveredMethods = 0;
$classMethods = 0;
$classPaths = 0;
$coveredClassPaths = 0;
$classBranches = 0;
$coveredClassBranches = 0;

foreach ($class['methods'] as $method) {
if ($method['executableLines'] == 0) {
if ($method['executableLines'] === 0) {
continue;
}

$classMethods++;
$classStatements += $method['executableLines'];
$coveredClassStatements += $method['executedLines'];

if ($method['coverage'] == 100) {
if ($this->determineBranchCoverage) {
$classPaths += $method['executablePaths'];
$coveredClassPaths += $method['executedPaths'];
$classBranches += $method['executableBranches'];
$coveredClassBranches += $method['executedBranches'];
}

if ($method['coverage'] === 100) {
$coveredMethods++;
}
}

$maxMethods = \max(
$maxMethods,
\strlen((string) $classMethods),
\strlen((string) $coveredMethods)
);
$maxLines = \max(
$maxLines,
\strlen((string) $classStatements),
\strlen((string) $coveredClassStatements)
);

if ($this->determineBranchCoverage) {
$maxBranches = \max(
$maxBranches,
\strlen((string) $classBranches),
\strlen((string) $coveredClassBranches)
);
$maxPaths = \max(
$maxPaths,
\strlen((string) $classPaths),
\strlen((string) $coveredClassPaths)
);
}

$namespace = '';

if (!empty($class['package']['namespace'])) {
Expand All @@ -215,27 +307,44 @@ public function process(CodeCoverage $coverage, bool $showColors = false): strin
'methodCount' => $classMethods,
'statementsCovered' => $coveredClassStatements,
'statementCount' => $classStatements,
'pathsCovered' => $coveredClassPaths,
'pathCount' => $classPaths,
'branchesCovered' => $coveredClassBranches,
'branchCount' => $classBranches,
];
}
}

\ksort($classCoverage);

$methodColor = '';
$linesColor = '';
$resetColor = '';
$methodColor = '';
$linesColor = '';
$resetColor = '';
$pathsColor = '';
$branchesColor = '';

foreach ($classCoverage as $fullQualifiedPath => $classInfo) {
if ($this->showUncoveredFiles || $classInfo['statementsCovered'] != 0) {
if ($this->showUncoveredFiles || $classInfo['statementsCovered'] !== 0) {
if ($showColors) {
$methodColor = $this->getCoverageColor($classInfo['methodsCovered'], $classInfo['methodCount']);
$linesColor = $this->getCoverageColor($classInfo['statementsCovered'], $classInfo['statementCount']);
$resetColor = $colors['reset'];
$methodColor = $this->getCoverageColor($classInfo['methodsCovered'], $classInfo['methodCount']);
$linesColor = $this->getCoverageColor($classInfo['statementsCovered'], $classInfo['statementCount']);

if ($this->determineBranchCoverage) {
$branchesColor = $this->getCoverageColor($classInfo['branchesCovered'], $classInfo['branchCount']);
$pathsColor = $this->getCoverageColor($classInfo['pathsCovered'], $classInfo['pathCount']);
}
$resetColor = $colors['reset'];
}

$output .= \PHP_EOL . $fullQualifiedPath . \PHP_EOL
. ' ' . $methodColor . 'Methods: ' . $this->printCoverageCounts($classInfo['methodsCovered'], $classInfo['methodCount'], 2) . $resetColor . ' '
. ' ' . $linesColor . 'Lines: ' . $this->printCoverageCounts($classInfo['statementsCovered'], $classInfo['statementCount'], 3) . $resetColor;
. ' ' . $methodColor . 'Methods: ' . $this->printCoverageCounts($classInfo['methodsCovered'], $classInfo['methodCount'], $maxMethods) . $resetColor . ' '
. ' ' . $linesColor . 'Lines: ' . $this->printCoverageCounts($classInfo['statementsCovered'], $classInfo['statementCount'], $maxLines) . $resetColor;

if ($this->determineBranchCoverage) {
$output .= ''
. ' ' . $branchesColor . 'Branches: ' . $this->printCoverageCounts($classInfo['branchesCovered'], $classInfo['branchCount'], $maxBranches) . $resetColor . ' '
. ' ' . $pathsColor . 'Paths: ' . $this->printCoverageCounts($classInfo['pathsCovered'], $classInfo['pathCount'], $maxPaths) . $resetColor;
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Util.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ final class Util
public static function percent(float $a, float $b, bool $asString = false, bool $fixedWidth = false)
{
if ($asString && $b == 0) {
return '';
return $fixedWidth ? ' ' : '';
}

$percent = 100;
Expand Down