diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 3e2ad19c0..d5f7b271e 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -119,9 +119,16 @@ 'multiline_whitespace_before_semicolons' => true, 'native_constant_invocation' => false, 'native_function_casing' => false, - 'native_function_invocation' => false, + 'native_function_invocation' => [ + 'include' => [ + '@internal', + ], + ], 'native_function_type_declaration_casing' => true, - 'new_with_braces' => false, + 'new_with_braces' => [ + 'named_class' => false, + 'anonymous_class' => false, + ], 'no_alias_functions' => true, 'no_alias_language_construct_call' => true, 'no_alternative_syntax' => true, @@ -158,6 +165,7 @@ 'no_unneeded_control_parentheses' => true, 'no_unneeded_curly_braces' => true, 'no_unneeded_final_method' => true, + 'no_unneeded_import_alias' => true, 'no_unreachable_default_argument_value' => true, 'no_unset_cast' => true, 'no_unset_on_property' => true, diff --git a/ChangeLog.md b/ChangeLog.md index 24764a3e5..c972cf032 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -4,6 +4,10 @@ All notable changes are documented in this file using the [Keep a CHANGELOG](htt ## [9.2.19] - 2022-MM-DD +### Fixed + +* [#949](https://github.com/sebastianbergmann/php-code-coverage/pull/949): Various issues related to identifying executable lines + ### Changed * Tweaked CSS for HTML report diff --git a/composer.json b/composer.json index b09bd6479..8bf89c5b5 100644 --- a/composer.json +++ b/composer.json @@ -62,7 +62,8 @@ }, "extra": { "branch-alias": { - "dev-master": "9.2-dev" + "dev-master": "9.2-dev", + "dev-improved-cobertura-report": "9.2-dev" } } } diff --git a/src/RawCodeCoverageData.php b/src/RawCodeCoverageData.php index 422742e28..c8d3eb75c 100644 --- a/src/RawCodeCoverageData.php +++ b/src/RawCodeCoverageData.php @@ -15,8 +15,12 @@ use function array_intersect; use function array_intersect_key; use function count; +use function explode; +use function file_get_contents; use function in_array; +use function is_file; use function range; +use function trim; use SebastianBergmann\CodeCoverage\Driver\Driver; use SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser; diff --git a/src/Report/Cobertura/CoberturaClass.php b/src/Report/Cobertura/CoberturaClass.php index 18a99c943..c08fc8eab 100644 --- a/src/Report/Cobertura/CoberturaClass.php +++ b/src/Report/Cobertura/CoberturaClass.php @@ -9,6 +9,8 @@ */ namespace SebastianBergmann\CodeCoverage\Report\Cobertura; +use function array_merge; +use function range; use DOMDocument; use DOMElement; diff --git a/src/Report/Cobertura/CoberturaCoverage.php b/src/Report/Cobertura/CoberturaCoverage.php index a25804b96..ad17061b9 100644 --- a/src/Report/Cobertura/CoberturaCoverage.php +++ b/src/Report/Cobertura/CoberturaCoverage.php @@ -9,6 +9,15 @@ */ namespace SebastianBergmann\CodeCoverage\Report\Cobertura; +use function array_reduce; +use function basename; +use function count; +use function date; +use function getcwd; +use function in_array; +use function sprintf; +use function str_replace; +use function time; use Composer\InstalledVersions; use DOMDocument; use DOMElement; diff --git a/src/Report/Cobertura/CoberturaLine.php b/src/Report/Cobertura/CoberturaLine.php index e25f30d35..bc31c02bf 100644 --- a/src/Report/Cobertura/CoberturaLine.php +++ b/src/Report/Cobertura/CoberturaLine.php @@ -9,6 +9,7 @@ */ namespace SebastianBergmann\CodeCoverage\Report\Cobertura; +use function count; use DOMDocument; use DOMElement; diff --git a/src/Report/Cobertura/CoberturaMethod.php b/src/Report/Cobertura/CoberturaMethod.php index 2947ae38f..cc0acfaf4 100644 --- a/src/Report/Cobertura/CoberturaMethod.php +++ b/src/Report/Cobertura/CoberturaMethod.php @@ -9,6 +9,7 @@ */ namespace SebastianBergmann\CodeCoverage\Report\Cobertura; +use function range; use DOMDocument; use DOMElement; diff --git a/src/Report/Cobertura/CoberturaPackage.php b/src/Report/Cobertura/CoberturaPackage.php index e04435f58..c204ded8a 100644 --- a/src/Report/Cobertura/CoberturaPackage.php +++ b/src/Report/Cobertura/CoberturaPackage.php @@ -9,6 +9,8 @@ */ namespace SebastianBergmann\CodeCoverage\Report\Cobertura; +use function array_reduce; +use function explode; use DOMDocument; use DOMElement; diff --git a/src/Report/Html/Renderer/File.php b/src/Report/Html/Renderer/File.php index d47267e33..101a9adae 100644 --- a/src/Report/Html/Renderer/File.php +++ b/src/Report/Html/Renderer/File.php @@ -80,6 +80,8 @@ use const T_YIELD; use const T_YIELD_FROM; use function array_key_exists; +use function array_keys; +use function array_merge; use function array_pop; use function array_unique; use function constant; @@ -89,6 +91,9 @@ use function file_get_contents; use function htmlspecialchars; use function is_string; +use function ksort; +use function range; +use function sort; use function sprintf; use function str_replace; use function substr; diff --git a/src/Report/Xml/Coverage.php b/src/Report/Xml/Coverage.php index 215f9b932..b556d8205 100644 --- a/src/Report/Xml/Coverage.php +++ b/src/Report/Xml/Coverage.php @@ -37,7 +37,7 @@ public function __construct(DOMElement $context, string $line) { $this->contextNode = $context; - $this->writer = new XMLWriter(); + $this->writer = new XMLWriter; $this->writer->openMemory(); $this->writer->startElementNS(null, $context->nodeName, 'https://schema.phpunit.de/coverage/1.0'); $this->writer->writeAttribute('nr', $line); diff --git a/src/Report/Xml/Report.php b/src/Report/Xml/Report.php index b53b08c00..7f2badaeb 100644 --- a/src/Report/Xml/Report.php +++ b/src/Report/Xml/Report.php @@ -20,7 +20,7 @@ final class Report extends File { public function __construct(string $name) { - $dom = new DOMDocument(); + $dom = new DOMDocument; $dom->loadXML(''); $contextNode = $dom->getElementsByTagNameNS( diff --git a/src/Report/Xml/Source.php b/src/Report/Xml/Source.php index 8fd20f58f..2b67ce1da 100644 --- a/src/Report/Xml/Source.php +++ b/src/Report/Xml/Source.php @@ -31,7 +31,7 @@ public function setSourceCode(string $source): void { $context = $this->context; - $tokens = (new Tokenizer())->parse($source); + $tokens = (new Tokenizer)->parse($source); $srcDom = (new XMLSerializer(new NamespaceUri($context->namespaceURI)))->toDom($tokens); $context->parentNode->replaceChild( diff --git a/src/StaticAnalysis/CachingFileAnalyser.php b/src/StaticAnalysis/CachingFileAnalyser.php index ceb39d51a..f53da078a 100644 --- a/src/StaticAnalysis/CachingFileAnalyser.php +++ b/src/StaticAnalysis/CachingFileAnalyser.php @@ -15,6 +15,7 @@ use function is_file; use function md5; use function serialize; +use function unserialize; use SebastianBergmann\CodeCoverage\Util\Filesystem; use SebastianBergmann\FileIterator\Facade as FileIteratorFacade; diff --git a/src/StaticAnalysis/CodeUnitFindingVisitor.php b/src/StaticAnalysis/CodeUnitFindingVisitor.php index 8d357b202..8a2003fa8 100644 --- a/src/StaticAnalysis/CodeUnitFindingVisitor.php +++ b/src/StaticAnalysis/CodeUnitFindingVisitor.php @@ -9,6 +9,7 @@ */ namespace SebastianBergmann\CodeCoverage\StaticAnalysis; +use function assert; use function implode; use function rtrim; use function trim; @@ -314,6 +315,8 @@ private function unionOrIntersectionAsString(ComplexType $type): string if ($_type instanceof Name) { $types[] = $_type->toCodeString(); } else { + assert($_type instanceof Identifier); + $types[] = $_type->toString(); } } diff --git a/src/StaticAnalysis/ExecutableLinesFindingVisitor.php b/src/StaticAnalysis/ExecutableLinesFindingVisitor.php index f69363caf..1ef8e59c5 100644 --- a/src/StaticAnalysis/ExecutableLinesFindingVisitor.php +++ b/src/StaticAnalysis/ExecutableLinesFindingVisitor.php @@ -9,18 +9,20 @@ */ namespace SebastianBergmann\CodeCoverage\StaticAnalysis; +use function array_reverse; +use function range; +use function sort; use PhpParser\Node; use PhpParser\Node\Expr\Array_; use PhpParser\Node\Expr\ArrayDimFetch; -use PhpParser\Node\Expr\ArrayItem; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\BinaryOp; use PhpParser\Node\Expr\CallLike; -use PhpParser\Node\Expr\Cast; use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\Match_; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\NullsafePropertyFetch; +use PhpParser\Node\Expr\Print_; use PhpParser\Node\Expr\PropertyFetch; use PhpParser\Node\Expr\StaticPropertyFetch; use PhpParser\Node\Expr\Ternary; @@ -34,21 +36,19 @@ use PhpParser\Node\Stmt\Continue_; use PhpParser\Node\Stmt\Do_; use PhpParser\Node\Stmt\Echo_; -use PhpParser\Node\Stmt\Else_; use PhpParser\Node\Stmt\ElseIf_; use PhpParser\Node\Stmt\Expression; -use PhpParser\Node\Stmt\Finally_; use PhpParser\Node\Stmt\For_; use PhpParser\Node\Stmt\Foreach_; +use PhpParser\Node\Stmt\Function_; use PhpParser\Node\Stmt\Goto_; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Property; use PhpParser\Node\Stmt\Return_; -use PhpParser\Node\Stmt\Switch_; use PhpParser\Node\Stmt\Throw_; -use PhpParser\Node\Stmt\TryCatch; use PhpParser\Node\Stmt\Unset_; use PhpParser\Node\Stmt\While_; +use PhpParser\NodeAbstract; use PhpParser\NodeVisitorAbstract; /** @@ -67,19 +67,23 @@ final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract private $propertyLines = []; /** - * @psalm-var array + * @psalm-var array */ private $returns = []; public function enterNode(Node $node): void { + if (!$node instanceof NodeAbstract) { + return; + } + $this->savePropertyLines($node); if (!$this->isExecutable($node)) { return; } - foreach ($this->getLines($node) as $line) { + foreach ($this->getLines($node, false) as $line) { if (isset($this->propertyLines[$line])) { return; } @@ -88,170 +92,225 @@ public function enterNode(Node $node): void } } - /** - * @psalm-return array - */ - public function executableLines(): array + public function afterTraverse(array $nodes): void { $this->computeReturns(); sort($this->executableLines); + } + /** + * @psalm-return array + */ + public function executableLines(): array + { return $this->executableLines; } private function savePropertyLines(Node $node): void { - if (!$node instanceof Property && !$node instanceof Node\Stmt\ClassConst) { - return; - } - - foreach (range($node->getStartLine(), $node->getEndLine()) as $index) { - $this->propertyLines[$index] = $index; + if ($node instanceof Property) { + foreach (range($node->getStartLine(), $node->getEndLine()) as $index) { + $this->propertyLines[$index] = $index; + } } } private function computeReturns(): void { - foreach ($this->returns as $return) { - foreach (range($return->getStartLine(), $return->getEndLine()) as $loc) { - if (isset($this->executableLines[$loc])) { - continue 2; + foreach (array_reverse($this->returns) as $node) { + foreach (range($node->getStartLine(), $node->getEndLine()) as $index) { + if (isset($this->executableLines[$index])) { + continue; } } - $line = $return->getEndLine(); - - if ($return->expr !== null) { - $line = $return->expr->getStartLine(); + foreach ($this->getLines($node, true) as $line) { + $this->executableLines[$line] = $line; } - - $this->executableLines[$line] = $line; } } /** * @return int[] */ - private function getLines(Node $node): array + private function getLines(NodeAbstract $node, bool $fromReturns): array { - if ($node instanceof BinaryOp) { - if (($node->left instanceof Node\Scalar || - $node->left instanceof Node\Expr\ConstFetch) && - ($node->right instanceof Node\Scalar || - $node->right instanceof Node\Expr\ConstFetch)) { - return [$node->right->getStartLine()]; - } - - return []; - } - - if ($node instanceof Cast || - $node instanceof PropertyFetch || - $node instanceof NullsafePropertyFetch || - $node instanceof StaticPropertyFetch) { - return [$node->getEndLine()]; - } + if ($node instanceof Function_ || + $node instanceof ClassMethod || + $node instanceof Return_ || + $node instanceof Expression || + $node instanceof Assign || + $node instanceof Array_ + ) { + if (!$fromReturns) { + $this->returns[] = $node; + + if ($node instanceof ClassMethod && $node->name->name === '__construct') { + $existsAPromotedProperty = false; + + foreach ($node->getParams() as $param) { + if (0 !== ($param->flags & Class_::VISIBILITY_MODIFIER_MASK)) { + $existsAPromotedProperty = true; + + break; + } + } + + if ($existsAPromotedProperty) { + // Only the line with `function` keyword should be listed here + // but `nikic/php-parser` doesn't provide a way to fetch it + return range($node->getStartLine(), $node->name->getEndLine()); + } + } - if ($node instanceof ArrayDimFetch) { - if (null === $node->dim) { return []; } - return [$node->dim->getStartLine()]; - } - - if ($node instanceof Array_) { - $startLine = $node->getStartLine(); - - if (isset($this->executableLines[$startLine])) { - return []; + // ugly fix for non-fully AST based processing + // self::afterTraverse()/self::computeReturns() should be rewritten using self::leaveNode() + foreach (range($node->getStartLine(), $node->getEndLine()) as $index) { + if (isset($this->executableLines[$index]) && !($node instanceof Assign)) { + return []; + } } - if ([] === $node->items) { + // empty function + if ($node instanceof Function_) { return [$node->getEndLine()]; } - if ($node->items[0] instanceof ArrayItem) { - return [$node->items[0]->getStartLine()]; + // empty method + if ($node instanceof ClassMethod) { + if (null === $node->stmts) { // method without body (interface prototype) + return []; + } + + return [$node->getEndLine()]; } } - if ($node instanceof ClassMethod) { - if ($node->name->name !== '__construct') { - return []; + if ($node instanceof Return_) { + if ($node->expr === null) { + return [$node->getEndLine()]; } - $existsAPromotedProperty = false; + return $this->getLines($node->expr, $fromReturns); + } - foreach ($node->getParams() as $param) { - if (0 !== ($param->flags & Class_::VISIBILITY_MODIFIER_MASK)) { - $existsAPromotedProperty = true; + if ($node instanceof Expression) { + return $this->getLines($node->expr, $fromReturns); + } - break; - } - } + if ($node instanceof Assign) { + return [$this->getNodeStartLine($node->var)]; + } - if ($existsAPromotedProperty) { - // Only the line with `function` keyword should be listed here - // but `nikic/php-parser` doesn't provide a way to fetch it - return range($node->getStartLine(), $node->name->getEndLine()); - } + if ($node instanceof BinaryOp) { + return $fromReturns ? $this->getLines($node->right, $fromReturns) : []; + } + + if ($node instanceof PropertyFetch || + $node instanceof NullsafePropertyFetch || + $node instanceof StaticPropertyFetch) { + return [$this->getNodeStartLine($node->name)]; + } - return []; + if ($node instanceof ArrayDimFetch && null !== $node->dim) { + return [$this->getNodeStartLine($node->dim)]; } if ($node instanceof MethodCall) { - return [$node->name->getStartLine()]; + return [$this->getNodeStartLine($node->name)]; } if ($node instanceof Ternary) { - $lines = [$node->cond->getStartLine()]; + $lines = [$this->getNodeStartLine($node->cond)]; if (null !== $node->if) { - $lines[] = $node->if->getStartLine(); + $lines[] = $this->getNodeStartLine($node->if); } - $lines[] = $node->else->getStartLine(); + $lines[] = $this->getNodeStartLine($node->else); return $lines; } if ($node instanceof Match_) { - return [$node->cond->getStartLine()]; + return [$this->getNodeStartLine($node->cond)]; } if ($node instanceof MatchArm) { - return [$node->body->getStartLine()]; + return [$this->getNodeStartLine($node->body)]; } - if ($node instanceof Expression && ( - $node->expr instanceof Cast || - $node->expr instanceof Match_ || - $node->expr instanceof MethodCall + // TODO this concept should be extended for every statement class like Foreach_, For_, ... + if ($node instanceof If_ || + $node instanceof ElseIf_ || + $node instanceof While_ || + $node instanceof Do_) { + return [$this->getNodeStartLine($node->cond)]; + } + + if ($node instanceof Case_) { + if (null === $node->cond) { // default case + return []; + } + + return [$this->getNodeStartLine($node->cond)]; + } + + if ($node instanceof Catch_) { + return [$this->getNodeStartLine($node->types[0])]; + } + + return [$this->getNodeStartLine($node)]; + } + + private function getNodeStartLine(NodeAbstract $node): int + { + if ($node instanceof Node\Expr\Cast || + $node instanceof Node\Expr\BooleanNot || + $node instanceof Node\Expr\UnaryMinus || + $node instanceof Node\Expr\UnaryPlus + ) { + return $this->getNodeStartLine($node->expr); + } + + if ($node instanceof BinaryOp) { + return $this->getNodeStartLine($node->right); + } + + if ($node instanceof Node\Scalar\String_ && ( + $node->getAttribute('kind') === Node\Scalar\String_::KIND_HEREDOC || + $node->getAttribute('kind') === Node\Scalar\String_::KIND_NOWDOC )) { - return []; + return $node->getStartLine() + 1; } - if ($node instanceof Return_) { - $this->returns[] = $node; + if ($node instanceof Array_) { + if ([] === $node->items || $node->items[0] === null) { + return $node->getEndLine(); + } + + return $this->getNodeStartLine($node->items[0]->value); + } - return []; + if ($node instanceof Assign) { + return $this->getNodeStartLine($node->expr); } - return [$node->getStartLine()]; + return $node->getStartLine(); // $node should be only a scalar here } private function isExecutable(Node $node): bool { return $node instanceof Assign || $node instanceof ArrayDimFetch || - $node instanceof Array_ || $node instanceof BinaryOp || $node instanceof Break_ || $node instanceof CallLike || $node instanceof Case_ || - $node instanceof Cast || $node instanceof Catch_ || $node instanceof ClassMethod || $node instanceof Closure || @@ -259,25 +318,23 @@ private function isExecutable(Node $node): bool $node instanceof Do_ || $node instanceof Echo_ || $node instanceof ElseIf_ || - $node instanceof Else_ || $node instanceof Encapsed || $node instanceof Expression || - $node instanceof Finally_ || $node instanceof For_ || $node instanceof Foreach_ || + $node instanceof Function_ || $node instanceof Goto_ || $node instanceof If_ || $node instanceof Match_ || $node instanceof MatchArm || $node instanceof MethodCall || $node instanceof NullsafePropertyFetch || + $node instanceof Print_ || $node instanceof PropertyFetch || $node instanceof Return_ || $node instanceof StaticPropertyFetch || - $node instanceof Switch_ || $node instanceof Ternary || $node instanceof Throw_ || - $node instanceof TryCatch || $node instanceof Unset_ || $node instanceof While_; } diff --git a/src/StaticAnalysis/ParsingFileAnalyser.php b/src/StaticAnalysis/ParsingFileAnalyser.php index 8edf973e2..8792cf3d9 100644 --- a/src/StaticAnalysis/ParsingFileAnalyser.php +++ b/src/StaticAnalysis/ParsingFileAnalyser.php @@ -9,11 +9,13 @@ */ namespace SebastianBergmann\CodeCoverage\StaticAnalysis; +use function array_merge; use function array_unique; use function assert; use function file_get_contents; use function is_array; use function max; +use function sort; use function sprintf; use function substr_count; use function token_get_all; diff --git a/tests/TestCase.php b/tests/TestCase.php index d2d045c3a..1ddb311f3 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -1353,7 +1353,6 @@ protected function getExpectedLineCoverageDataArrayForBankAccount(): array ], 13 => [], 14 => [], - 15 => [], 16 => [], 22 => [ 0 => 'BankAccountTest::testBalanceCannotBecomeNegative2', @@ -1384,7 +1383,6 @@ protected function getExpectedLineCoverageDataArrayForBankAccountInReverseOrder( ], 13 => [], 14 => [], - 15 => [], 16 => [], 22 => [ 0 => 'BankAccountTest::testBalanceCannotBecomeNegative2', diff --git a/tests/_files/BankAccount-clover-line.xml b/tests/_files/BankAccount-clover-line.xml index 02516b4a8..9a6fca706 100644 --- a/tests/_files/BankAccount-clover-line.xml +++ b/tests/_files/BankAccount-clover-line.xml @@ -3,14 +3,13 @@ - + - @@ -18,8 +17,8 @@ - + - + diff --git a/tests/_files/BankAccount-clover-path.xml b/tests/_files/BankAccount-clover-path.xml index a71babac5..b49f4c915 100644 --- a/tests/_files/BankAccount-clover-path.xml +++ b/tests/_files/BankAccount-clover-path.xml @@ -3,14 +3,13 @@ - + - @@ -18,8 +17,8 @@ - + - + diff --git a/tests/_files/BankAccount-cobertura-line.xml b/tests/_files/BankAccount-cobertura-line.xml index 856990350..85dcfaa8c 100644 --- a/tests/_files/BankAccount-cobertura-line.xml +++ b/tests/_files/BankAccount-cobertura-line.xml @@ -1,14 +1,14 @@ - + tests%e_files - + - + @@ -19,7 +19,6 @@ - @@ -40,7 +39,6 @@ - diff --git a/tests/_files/BankAccount-cobertura-path.xml b/tests/_files/BankAccount-cobertura-path.xml index 680b6817e..fecd9e63b 100644 --- a/tests/_files/BankAccount-cobertura-path.xml +++ b/tests/_files/BankAccount-cobertura-path.xml @@ -1,14 +1,14 @@ - + tests%e_files - + - + @@ -19,7 +19,6 @@ - @@ -40,7 +39,6 @@ - diff --git a/tests/_files/BankAccount-text-line.txt b/tests/_files/BankAccount-text-line.txt index 133c73797..4f188c76c 100644 --- a/tests/_files/BankAccount-text-line.txt +++ b/tests/_files/BankAccount-text-line.txt @@ -6,7 +6,7 @@ Code Coverage Report: Summary: Classes: 0.00% (0/1) Methods: 75.00% (3/4) - Lines: 55.56% (5/9) + Lines: 62.50% (5/8) BankAccount - Methods: 75.00% ( 3/ 4) Lines: 55.56% ( 5/ 9) + Methods: 75.00% ( 3/ 4) Lines: 62.50% ( 5/ 8) diff --git a/tests/_files/BankAccount-text-path.txt b/tests/_files/BankAccount-text-path.txt index 653f9ffed..488e34ff5 100644 --- a/tests/_files/BankAccount-text-path.txt +++ b/tests/_files/BankAccount-text-path.txt @@ -8,7 +8,7 @@ Code Coverage Report: Methods: 75.00% (3/4) Paths: 60.00% (3/5) Branches: 42.86% (3/7) - Lines: 55.56% (5/9) + Lines: 62.50% (5/8) BankAccount - Methods: 75.00% ( 3/ 4) Paths: 60.00% ( 3/ 5) Branches: 42.86% ( 3/ 7) Lines: 55.56% ( 5/ 9) + Methods: 75.00% ( 3/ 4) Paths: 60.00% ( 3/ 5) Branches: 42.86% ( 3/ 7) Lines: 62.50% ( 5/ 8) diff --git a/tests/_files/BankAccount-text-summary.txt b/tests/_files/BankAccount-text-summary.txt index 96390c4cd..952abd17b 100644 --- a/tests/_files/BankAccount-text-summary.txt +++ b/tests/_files/BankAccount-text-summary.txt @@ -3,5 +3,5 @@ Code Coverage Report Summary: Classes: 0.00% (0/1) Methods: 75.00% (3/4) - Lines: 55.56% (5/9) + Lines: 62.50% (5/8) diff --git a/tests/_files/BankAccountWithUncovered-text-line.txt b/tests/_files/BankAccountWithUncovered-text-line.txt index 4193b5ec7..85693d8af 100644 --- a/tests/_files/BankAccountWithUncovered-text-line.txt +++ b/tests/_files/BankAccountWithUncovered-text-line.txt @@ -6,7 +6,7 @@ Code Coverage Report: Summary: Classes: 0.00% (0/2) Methods: 37.50% (3/8) - Lines: 27.78% (5/18) + Lines: 31.25% (5/16) BankAccount - Methods: 75.00% ( 3/ 4) Lines: 55.56% ( 5/ 9) + Methods: 75.00% ( 3/ 4) Lines: 62.50% ( 5/ 8) diff --git a/tests/_files/BankAccountWithoutUncovered-text-line.txt b/tests/_files/BankAccountWithoutUncovered-text-line.txt index 133c73797..4f188c76c 100644 --- a/tests/_files/BankAccountWithoutUncovered-text-line.txt +++ b/tests/_files/BankAccountWithoutUncovered-text-line.txt @@ -6,7 +6,7 @@ Code Coverage Report: Summary: Classes: 0.00% (0/1) Methods: 75.00% (3/4) - Lines: 55.56% (5/9) + Lines: 62.50% (5/8) BankAccount - Methods: 75.00% ( 3/ 4) Lines: 55.56% ( 5/ 9) + Methods: 75.00% ( 3/ 4) Lines: 62.50% ( 5/ 8) diff --git a/tests/_files/NamespacedBankAccount-text.txt b/tests/_files/NamespacedBankAccount-text.txt index 4cd9eb18f..7cede93cb 100644 --- a/tests/_files/NamespacedBankAccount-text.txt +++ b/tests/_files/NamespacedBankAccount-text.txt @@ -6,9 +6,9 @@ Code Coverage Report: Summary: Classes: 0.00% (0/1) Methods: 75.00% (3/4) - Lines: 55.56% (5/9) + Lines: 62.50% (5/8) SomeNamespace\BankAccount Methods: ( 0/ 0) Lines: ( 0/ 0) SomeNamespace\BankAccountTrait - Methods: 75.00% ( 3/ 4) Lines: 55.56% ( 5/ 9) + Methods: 75.00% ( 3/ 4) Lines: 62.50% ( 5/ 8) diff --git a/tests/_files/Report/HTML/CoverageForBankAccount/BankAccount.php.html b/tests/_files/Report/HTML/CoverageForBankAccount/BankAccount.php.html index ee67f746d..fcbd196c3 100644 --- a/tests/_files/Report/HTML/CoverageForBankAccount/BankAccount.php.html +++ b/tests/_files/Report/HTML/CoverageForBankAccount/BankAccount.php.html @@ -44,13 +44,13 @@ Total
-
- 55.56% covered (warning) +
+ 62.50% covered (warning)
-
55.56%
-
5 / 9
+
62.50%
+
5 / 8
75.00% covered (warning) @@ -73,13 +73,13 @@ BankAccount
-
- 55.56% covered (warning) +
+ 62.50% covered (warning)
-
55.56%
-
5 / 9
+
62.50%
+
5 / 8
75.00% covered (warning) @@ -88,7 +88,7 @@
75.00%
3 / 4
- 7.19 + 6.32
0.00% covered (danger) @@ -130,7 +130,7 @@
0.00%
-
0 / 4
+
0 / 3
0.00% covered (danger) @@ -207,7 +207,7 @@ 12    { 13        if ($balance >= 0) { 14            $this->balance = $balance; - 15        } else { + 15        } else { 16            throw new RuntimeException; 17        } 18    } diff --git a/tests/_files/Report/HTML/CoverageForBankAccount/dashboard.html b/tests/_files/Report/HTML/CoverageForBankAccount/dashboard.html index fc20fb36e..13016f8cd 100644 --- a/tests/_files/Report/HTML/CoverageForBankAccount/dashboard.html +++ b/tests/_files/Report/HTML/CoverageForBankAccount/dashboard.html @@ -57,7 +57,7 @@

Insufficient Coverage

- BankAccount55% + BankAccount62% @@ -74,7 +74,7 @@

Project Risks

- BankAccount7 + BankAccount6 @@ -158,7 +158,7 @@

Project Risks

.yAxis.tickFormat(d3.format('d')); d3.select('#classCoverageDistribution svg') - .datum(getCoverageDistributionData([0,0,0,0,0,0,1,0,0,0,0,0], "Class Coverage")) + .datum(getCoverageDistributionData([0,0,0,0,0,0,0,1,0,0,0,0], "Class Coverage")) .transition().duration(500).call(chart); nv.utils.windowResize(chart.update); @@ -226,7 +226,7 @@

Project Risks

chart.yAxis.axisLabel('Cyclomatic Complexity'); d3.select('#classComplexity svg') - .datum(getComplexityData([[55.555555555556,5,"BankAccount<\/a>"]], 'Class Complexity')) + .datum(getComplexityData([[62.5,5,"BankAccount<\/a>"]], 'Class Complexity')) .transition() .duration(500) .call(chart); diff --git a/tests/_files/Report/HTML/CoverageForBankAccount/index.html b/tests/_files/Report/HTML/CoverageForBankAccount/index.html index 89ab27d54..d21ece67a 100644 --- a/tests/_files/Report/HTML/CoverageForBankAccount/index.html +++ b/tests/_files/Report/HTML/CoverageForBankAccount/index.html @@ -44,13 +44,13 @@ Total
-
- 55.56% covered (warning) +
+ 62.50% covered (warning)
-
55.56%
-
5 / 9
+
62.50%
+
5 / 8
75.00% covered (warning) @@ -72,13 +72,13 @@ BankAccount.php
-
- 55.56% covered (warning) +
+ 62.50% covered (warning)
-
55.56%
-
5 / 9
+
62.50%
+
5 / 8
75.00% covered (warning) diff --git a/tests/_files/Report/HTML/PathCoverageForBankAccount/BankAccount.php.html b/tests/_files/Report/HTML/PathCoverageForBankAccount/BankAccount.php.html index 15aec8765..5680ed189 100644 --- a/tests/_files/Report/HTML/PathCoverageForBankAccount/BankAccount.php.html +++ b/tests/_files/Report/HTML/PathCoverageForBankAccount/BankAccount.php.html @@ -46,13 +46,13 @@ Total
-
- 55.56% covered (warning) +
+ 62.50% covered (warning)
-
55.56%
-
5 / 9
+
62.50%
+
5 / 8
42.86% covered (danger) @@ -91,13 +91,13 @@ BankAccount
-
- 55.56% covered (warning) +
+ 62.50% covered (warning)
-
55.56%
-
5 / 9
+
62.50%
+
5 / 8
42.86% covered (danger) @@ -180,7 +180,7 @@
0.00%
-
0 / 4
+
0 / 3
0.00% covered (danger) @@ -305,7 +305,7 @@ 12    { 13        if ($balance >= 0) { 14            $this->balance = $balance; - 15        } else { + 15        } else { 16            throw new RuntimeException; 17        } 18    } diff --git a/tests/_files/Report/HTML/PathCoverageForBankAccount/BankAccount.php_branch.html b/tests/_files/Report/HTML/PathCoverageForBankAccount/BankAccount.php_branch.html index 6a89c22ec..fe4a8a066 100644 --- a/tests/_files/Report/HTML/PathCoverageForBankAccount/BankAccount.php_branch.html +++ b/tests/_files/Report/HTML/PathCoverageForBankAccount/BankAccount.php_branch.html @@ -46,13 +46,13 @@ Total
-
- 55.56% covered (warning) +
+ 62.50% covered (warning)
-
55.56%
-
5 / 9
+
62.50%
+
5 / 8
42.86% covered (danger) @@ -91,13 +91,13 @@ BankAccount
-
- 55.56% covered (warning) +
+ 62.50% covered (warning)
-
55.56%
-
5 / 9
+
62.50%
+
5 / 8
42.86% covered (danger) @@ -180,7 +180,7 @@
0.00%
-
0 / 4
+
0 / 3
0.00% covered (danger) diff --git a/tests/_files/Report/HTML/PathCoverageForBankAccount/BankAccount.php_path.html b/tests/_files/Report/HTML/PathCoverageForBankAccount/BankAccount.php_path.html index aa8f776b2..de13b9a4f 100644 --- a/tests/_files/Report/HTML/PathCoverageForBankAccount/BankAccount.php_path.html +++ b/tests/_files/Report/HTML/PathCoverageForBankAccount/BankAccount.php_path.html @@ -46,13 +46,13 @@ Total
-
- 55.56% covered (warning) +
+ 62.50% covered (warning)
-
55.56%
-
5 / 9
+
62.50%
+
5 / 8
42.86% covered (danger) @@ -91,13 +91,13 @@ BankAccount
-
- 55.56% covered (warning) +
+ 62.50% covered (warning)
-
55.56%
-
5 / 9
+
62.50%
+
5 / 8
42.86% covered (danger) @@ -180,7 +180,7 @@
0.00%
-
0 / 4
+
0 / 3
0.00% covered (danger) diff --git a/tests/_files/Report/HTML/PathCoverageForBankAccount/index.html b/tests/_files/Report/HTML/PathCoverageForBankAccount/index.html index c31d0b7a7..b3616eeec 100644 --- a/tests/_files/Report/HTML/PathCoverageForBankAccount/index.html +++ b/tests/_files/Report/HTML/PathCoverageForBankAccount/index.html @@ -46,13 +46,13 @@ Total
-
- 55.56% covered (warning) +
+ 62.50% covered (warning)
-
55.56%
-
5 / 9
+
62.50%
+
5 / 8
42.86% covered (danger) @@ -90,13 +90,13 @@ BankAccount.php [line] [branch] [path]
-
- 55.56% covered (warning) +
+ 62.50% covered (warning)
-
55.56%
-
5 / 9
+
62.50%
+
5 / 8
42.86% covered (danger) diff --git a/tests/_files/Report/XML/CoverageForBankAccount/BankAccount.php.xml b/tests/_files/Report/XML/CoverageForBankAccount/BankAccount.php.xml index c9209d0e6..b49cdf8ed 100644 --- a/tests/_files/Report/XML/CoverageForBankAccount/BankAccount.php.xml +++ b/tests/_files/Report/XML/CoverageForBankAccount/BankAccount.php.xml @@ -2,16 +2,16 @@ - + - + - + diff --git a/tests/_files/Report/XML/CoverageForBankAccount/index.xml b/tests/_files/Report/XML/CoverageForBankAccount/index.xml index ee3df1efc..a18d5d871 100644 --- a/tests/_files/Report/XML/CoverageForBankAccount/index.xml +++ b/tests/_files/Report/XML/CoverageForBankAccount/index.xml @@ -13,7 +13,7 @@ - + @@ -21,7 +21,7 @@ - + diff --git a/tests/_files/source_with_heavy_indentation.php b/tests/_files/source_with_heavy_indentation.php index 64cea4a10..15f50753e 100644 --- a/tests/_files/source_with_heavy_indentation.php +++ b/tests/_files/source_with_heavy_indentation.php @@ -1,11 +1,27 @@ isOne(); + $xb = 1; + $xc = $xa; + + $va + = + $vb + = + [ + $xa, + $xb, + 1 + + + $xb + + 1, + ]; + + [ + $v2, + $v3 + ] + = + u( + $xa, + $xb, + 1 + + + $xc + + + 1 + ); + + return $v2 === $v3; + } + + public function variableBinaryConcat(): bool + { + $xa = $this->isOne(); + $xb = 1; + $xc = $xa; + + $va + = + $vb + = + [ + $xa, + $xb, + 1 + . + $xb + . 1, + ]; + + [ + $v2, + $v3 + ] + = + u( + $xa, + $xb, + 1 + . + $xc + . + 1 + ); + + return $v2 === $v3; + } + + public function noElse(): int + { + $res = 0; + if (mt_rand() === 0) { + return 1; + } elseif (mt_rand() === 0) { + return 2; + } else { + $res = 3; + } + + return $res; + } + + public function noDo(): int + { + $res = 0; + do { + if (mt_rand() !== 0) { + $res++; + } + } while ( + $res + < + 10 + ); + + return $res; + } + + public function noTryNoFinally(): int + { + $res = 0; + try { + if (mt_rand() === 0) { + throw new \Exception(); + } + } catch ( + \x\y + | + \x\z + $e + ) { + $res = 1; + } catch (\Error $e) { + $res = 1; + } finally { + $res += 10; + } + + return $res; + } + + public function switch(): int + { + switch ( + 'x' + . + mt_rand() + . 'x' + ) { + case + << 'fi_v', + 'omega', + ] + [ + 'f' + . 'i' + ] + ; + } + + public function emptyArray(): array + { + return + ( + [ + ] + ) + ; + } + + public function complexAssociativityNa2(): bool + { + return + ! + ! + ! + <<<'EOF' + foo + foo + EOF + ; + } + + public function unaryMinusNowdoc(): float + { + return + - + <<<'EOF' + 1. + 2 + EOF + ; + } + + public + function + emptyMethod + ( + ) + : + void + { + } + + public function emptyMethodWithComment(): void + { + // empty method with comment + } + + public function simpleConstArrayEmpty(): array + { + return + [ + // empty array with comment + ]; + } + + public function nestedConstArrayEmpty(): array + { + return + [ + [ + // empty subarray with comment + ] + ]; + } + + public function nestedConstArrayOne(): array + { + return + [ + [ + <<<'EOF' + 1 + EOF, + ] + ]; + } + + public function nestedConstArrayTwo(): array + { + return + [ + [ + <<<'EOF' + 1 + EOF, + 2, + ] + ]; + } + + public function nestedArrayWithExecutableInKey(): array + { + return + [ + [ + 1, + phpversion() + => + 2, + ] + ]; + } } diff --git a/tests/_files/source_with_oneline_annotations.php b/tests/_files/source_with_oneline_annotations.php index 7b1cb1cbd..7d3981e0a 100644 --- a/tests/_files/source_with_oneline_annotations.php +++ b/tests/_files/source_with_oneline_annotations.php @@ -34,3 +34,7 @@ function baz() print '*'; } + +function xyz() +{ +} diff --git a/tests/tests/CodeCoverageTest.php b/tests/tests/CodeCoverageTest.php index 8de789819..80c8c8f92 100644 --- a/tests/tests/CodeCoverageTest.php +++ b/tests/tests/CodeCoverageTest.php @@ -9,6 +9,7 @@ */ namespace SebastianBergmann\CodeCoverage; +use function array_fill; use SebastianBergmann\CodeCoverage\Driver\Driver; use SebastianBergmann\CodeCoverage\Driver\Selector; use SebastianBergmann\Environment\Runtime; @@ -108,7 +109,6 @@ public function testExcludeNonExecutableLines(): void 8 => [], 13 => [], 14 => [], - 15 => [], 16 => [], 22 => [], 24 => [], diff --git a/tests/tests/Data/ProcessedCodeCoverageDataTest.php b/tests/tests/Data/ProcessedCodeCoverageDataTest.php index 13758f18a..206634041 100644 --- a/tests/tests/Data/ProcessedCodeCoverageDataTest.php +++ b/tests/tests/Data/ProcessedCodeCoverageDataTest.php @@ -40,7 +40,7 @@ public function testMergeWithPathCoverage(): void public function testMergeWithPathCoverageIntoEmpty(): void { - $coverage = new ProcessedCodeCoverageData(); + $coverage = new ProcessedCodeCoverageData; $coverage->merge($this->getPathCoverageForBankAccount()->getData()); diff --git a/tests/tests/Data/RawCodeCoverageDataTest.php b/tests/tests/Data/RawCodeCoverageDataTest.php index ae4e48803..43aab64a1 100644 --- a/tests/tests/Data/RawCodeCoverageDataTest.php +++ b/tests/tests/Data/RawCodeCoverageDataTest.php @@ -272,7 +272,6 @@ public function testUseStatementsAreUncovered(): void [ 12, 14, - 15, 16, 18, ], @@ -300,7 +299,6 @@ public function testInterfacesAreUncovered(): void [ 7, 9, - 10, 11, 13, ], @@ -314,6 +312,7 @@ public function testInlineCommentsKeepTheLine(): void $this->assertEquals( [ + 13, 19, 22, 26, @@ -322,6 +321,7 @@ public function testInlineCommentsKeepTheLine(): void 32, 33, 35, + 40, ], array_keys(RawCodeCoverageData::fromUncoveredFile($file, new ParsingFileAnalyser(true, true))->lineCoverage()[$file]) ); @@ -333,44 +333,98 @@ public function testHeavyIndentationIsHandledCorrectly(): void $this->assertEquals( [ - 9, - 12, - 16, - 18, + 14, 19, - 24, + // line 22 is unstable - not in xdebug output if script is cached by opcache 25, - 28, - 31, - 36, + // line 28 is unstable - const coverage depends on autoload order - https://github.com/sebastianbergmann/php-code-coverage/issues/889 + 32, + 34, + 35, 40, - 46, - 48, - 54, - 60, + 41, + 44, + 47, + 56, + 62, 64, - 71, - 83, - 85, + 70, + 76, 87, - 89, - 91, - 93, 95, + 96, 97, 99, 101, - 116, - 120, - 123, - 125, // This shouldn't be marked as LoC, but it's high unlikely to happen IRL to have $var = []; on multiple lines + // line 108 is unstable - variable has no coverage if it holds const expr - https://github.com/sebastianbergmann/php-code-coverage/issues/953 + 113, + // array destruct element, should be present 114 + 117, + 127, 132, - 135, - 139, - 143, - 149, - 153, - 159, + 133, + 134, + 136, + 138, + // line 145 is unstable - same reason as line 108 + 150, + // array destruct element, should be present 151 - same reason as line 114 + 154, + 164, + 169, + 170, + 171, + 172, + 173, + // line 174 is else statement, not in xdebug output (only if/elseif conditions are covered) + 175, + 178, + 183, + 185, + 186, + 191, + 194, + 199, + // line 200 is try statement, not in xdebug output (only catch condition is covered) + 201, + 202, + 205, + 210, + 211, + 212, + // line 213 is finally statement, not in xdebug output (only catch condition is covered) + 214, + 217, + 225, + 230, + 232, + 234, + 235, + 236, + 237, + 239, + // line 240 is default case, not in xdebug output (only cases with condition are covered) + 241, + 244, + 253, + 255, + 257, + 259, + 261, + 263, + 265, + 267, + 269, + 271, + 293, + 298, + 302, + 305, + 309, + 313, + 319, + 323, + 329, ], array_keys(RawCodeCoverageData::fromUncoveredFile($file, new ParsingFileAnalyser(true, true))->lineCoverage()[$file]) ); @@ -460,6 +514,40 @@ public function testReturnStatementWithConstantExprOnlyReturnTheLineOfLast(): vo 226, 235, 244, + 252, + 261, + 269, + 278, + 293, + 304, + 314, + 321, + 323, + 324, + 325, + 327, + 340, + 351, + 370, + 377, + 390, + 402, + 414, + 425, + 437, + 442, + 444, + 459, + 469, + 481, + 492, + 506, + 511, + 518, + 527, + 537, + 549, + 562, ], array_keys(RawCodeCoverageData::fromUncoveredFile($file, new ParsingFileAnalyser(true, true))->lineCoverage()[$file]) ); diff --git a/tests/tests/Node/BuilderTest.php b/tests/tests/Node/BuilderTest.php index dba2fd28e..681d189f3 100644 --- a/tests/tests/Node/BuilderTest.php +++ b/tests/tests/Node/BuilderTest.php @@ -32,7 +32,7 @@ public function testSomething(): void $expectedPath = rtrim(TEST_FILES_PATH, DIRECTORY_SEPARATOR); $this->assertEquals($expectedPath, $root->name()); $this->assertEquals($expectedPath, $root->pathAsString()); - $this->assertEquals(9, $root->numberOfExecutableLines()); + $this->assertEquals(8, $root->numberOfExecutableLines()); $this->assertEquals(5, $root->numberOfExecutedLines()); $this->assertEquals(1, $root->numberOfClasses()); $this->assertEquals(0, $root->numberOfTestedClasses()); @@ -40,7 +40,7 @@ public function testSomething(): void $this->assertEquals(3, $root->numberOfTestedMethods()); $this->assertEquals('0.00%', $root->percentageOfTestedClasses()->asString()); $this->assertEquals('75.00%', $root->percentageOfTestedMethods()->asString()); - $this->assertEquals('55.56%', $root->percentageOfExecutedLines()->asString()); + $this->assertEquals('62.50%', $root->percentageOfExecutedLines()->asString()); $this->assertEquals(0, $root->numberOfFunctions()); $this->assertEquals(0, $root->numberOfTestedFunctions()); $this->assertNull($root->parent()); @@ -73,7 +73,7 @@ public function testSomething(): void 'signature' => 'setBalance($balance)', 'startLine' => 11, 'endLine' => 18, - 'executableLines' => 4, + 'executableLines' => 3, 'executedLines' => 0, 'executableBranches' => 0, 'executedBranches' => 0, @@ -122,15 +122,15 @@ public function testSomething(): void ], ], 'startLine' => 2, - 'executableLines' => 9, + 'executableLines' => 8, 'executedLines' => 5, 'executableBranches' => 0, 'executedBranches' => 0, 'executablePaths' => 0, 'executedPaths' => 0, 'ccn' => 5, - 'coverage' => 55.55555555555556, - 'crap' => '7.19', + 'coverage' => 62.5, + 'crap' => '6.32', 'link' => 'BankAccount.php.html#2', 'className' => 'BankAccount', 'namespace' => '', diff --git a/tests/tests/Report/CoberturaTest.php b/tests/tests/Report/CoberturaTest.php index 7bfca02dd..131ef5a76 100644 --- a/tests/tests/Report/CoberturaTest.php +++ b/tests/tests/Report/CoberturaTest.php @@ -70,6 +70,6 @@ private function validateReport(string $coberturaReport): void { $document = (new DOMDocument); $this->assertTrue($document->loadXML($coberturaReport)); - $this->assertTrue($document->validate()); + $this->assertTrue(@$document->validate()); } } diff --git a/tests/tests/StaticAnalysis/CodeUnitFindingVisitorTest.php b/tests/tests/StaticAnalysis/CodeUnitFindingVisitorTest.php index ffe06c8df..1c9d3aa2b 100644 --- a/tests/tests/StaticAnalysis/CodeUnitFindingVisitorTest.php +++ b/tests/tests/StaticAnalysis/CodeUnitFindingVisitorTest.php @@ -9,6 +9,7 @@ */ namespace SebastianBergmann\CodeCoverage\StaticAnalysis; +use function assert; use function file_get_contents; use PhpParser\NodeTraverser; use PhpParser\NodeVisitor\NameResolver;