add phpunit code coverage driver
This commit is contained in:
parent
2b8e344532
commit
09898ccbc8
|
@ -28,6 +28,7 @@
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpstan/phpstan": "1.10.3",
|
"phpstan/phpstan": "1.10.3",
|
||||||
"phpunit/phpunit": "9.5.16"
|
"phpunit/phpunit": "9.5.16",
|
||||||
|
"phpunit/php-code-coverage": "^9.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "cbbbfbdbf1c5f659b8e34307411bc751",
|
"content-hash": "d65a2e896d59d3d603fd6cda0db3b646",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "beberlei/assert",
|
"name": "beberlei/assert",
|
||||||
|
@ -2904,23 +2904,23 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/php-code-coverage",
|
"name": "phpunit/php-code-coverage",
|
||||||
"version": "9.2.15",
|
"version": "9.2.24",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||||
"reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f"
|
"reference": "2cf940ebc6355a9d430462811b5aaa308b174bed"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2e9da11878c4202f97915c1cb4bb1ca318a63f5f",
|
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2cf940ebc6355a9d430462811b5aaa308b174bed",
|
||||||
"reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f",
|
"reference": "2cf940ebc6355a9d430462811b5aaa308b174bed",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"ext-dom": "*",
|
"ext-dom": "*",
|
||||||
"ext-libxml": "*",
|
"ext-libxml": "*",
|
||||||
"ext-xmlwriter": "*",
|
"ext-xmlwriter": "*",
|
||||||
"nikic/php-parser": "^4.13.0",
|
"nikic/php-parser": "^4.14",
|
||||||
"php": ">=7.3",
|
"php": ">=7.3",
|
||||||
"phpunit/php-file-iterator": "^3.0.3",
|
"phpunit/php-file-iterator": "^3.0.3",
|
||||||
"phpunit/php-text-template": "^2.0.2",
|
"phpunit/php-text-template": "^2.0.2",
|
||||||
|
@ -2969,7 +2969,7 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.15"
|
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.24"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -2977,7 +2977,7 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-03-07T09:28:20+00:00"
|
"time": "2023-01-26T08:26:55+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/php-file-iterator",
|
"name": "phpunit/php-file-iterator",
|
||||||
|
|
|
@ -2073,24 +2073,24 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/php-code-coverage",
|
"name": "phpunit/php-code-coverage",
|
||||||
"version": "9.2.15",
|
"version": "9.2.24",
|
||||||
"version_normalized": "9.2.15.0",
|
"version_normalized": "9.2.24.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||||
"reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f"
|
"reference": "2cf940ebc6355a9d430462811b5aaa308b174bed"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2e9da11878c4202f97915c1cb4bb1ca318a63f5f",
|
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2cf940ebc6355a9d430462811b5aaa308b174bed",
|
||||||
"reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f",
|
"reference": "2cf940ebc6355a9d430462811b5aaa308b174bed",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"ext-dom": "*",
|
"ext-dom": "*",
|
||||||
"ext-libxml": "*",
|
"ext-libxml": "*",
|
||||||
"ext-xmlwriter": "*",
|
"ext-xmlwriter": "*",
|
||||||
"nikic/php-parser": "^4.13.0",
|
"nikic/php-parser": "^4.14",
|
||||||
"php": ">=7.3",
|
"php": ">=7.3",
|
||||||
"phpunit/php-file-iterator": "^3.0.3",
|
"phpunit/php-file-iterator": "^3.0.3",
|
||||||
"phpunit/php-text-template": "^2.0.2",
|
"phpunit/php-text-template": "^2.0.2",
|
||||||
|
@ -2108,7 +2108,7 @@
|
||||||
"ext-pcov": "*",
|
"ext-pcov": "*",
|
||||||
"ext-xdebug": "*"
|
"ext-xdebug": "*"
|
||||||
},
|
},
|
||||||
"time": "2022-03-07T09:28:20+00:00",
|
"time": "2023-01-26T08:26:55+00:00",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
|
@ -2141,7 +2141,7 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.15"
|
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.24"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
'name' => '__root__',
|
'name' => '__root__',
|
||||||
'pretty_version' => 'dev-master',
|
'pretty_version' => 'dev-master',
|
||||||
'version' => 'dev-master',
|
'version' => 'dev-master',
|
||||||
'reference' => '2b61052e8709283d89997e351173bcb43a3c2c61',
|
'reference' => '2b8e34453234b8b31ebc9e7020f8677bf3889898',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../../',
|
'install_path' => __DIR__ . '/../../',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
'__root__' => array(
|
'__root__' => array(
|
||||||
'pretty_version' => 'dev-master',
|
'pretty_version' => 'dev-master',
|
||||||
'version' => 'dev-master',
|
'version' => 'dev-master',
|
||||||
'reference' => '2b61052e8709283d89997e351173bcb43a3c2c61',
|
'reference' => '2b8e34453234b8b31ebc9e7020f8677bf3889898',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../../',
|
'install_path' => __DIR__ . '/../../',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
|
@ -306,9 +306,9 @@
|
||||||
'dev_requirement' => true,
|
'dev_requirement' => true,
|
||||||
),
|
),
|
||||||
'phpunit/php-code-coverage' => array(
|
'phpunit/php-code-coverage' => array(
|
||||||
'pretty_version' => '9.2.15',
|
'pretty_version' => '9.2.24',
|
||||||
'version' => '9.2.15.0',
|
'version' => '9.2.24.0',
|
||||||
'reference' => '2e9da11878c4202f97915c1cb4bb1ca318a63f5f',
|
'reference' => '2cf940ebc6355a9d430462811b5aaa308b174bed',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../phpunit/php-code-coverage',
|
'install_path' => __DIR__ . '/../phpunit/php-code-coverage',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
|
|
|
@ -2,6 +2,72 @@
|
||||||
|
|
||||||
All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
|
All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
|
||||||
|
|
||||||
|
## [9.2.24] - 2023-01-26
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* [#970](https://github.com/sebastianbergmann/php-code-coverage/issues/970): CSS and JavaScript assets are now referenced using `?v=%s` URLs in the HTML report to avoid cache issues
|
||||||
|
|
||||||
|
## [9.2.23] - 2022-12-28
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* [#971](https://github.com/sebastianbergmann/php-code-coverage/issues/971): PHP report does not handle serialized code coverage data larger than 2 GB
|
||||||
|
* [#974](https://github.com/sebastianbergmann/php-code-coverage/issues/974): Executable line analysis fails for declarations with enumerations and unions
|
||||||
|
|
||||||
|
## [9.2.22] - 2022-12-18
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* [#969](https://github.com/sebastianbergmann/php-code-coverage/pull/969): Fixed identifying line with `throw` as executable
|
||||||
|
|
||||||
|
## [9.2.21] - 2022-12-14
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* [#964](https://github.com/sebastianbergmann/php-code-coverage/pull/964): Changed how executable lines are identified
|
||||||
|
|
||||||
|
## [9.2.20] - 2022-12-13
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* [#960](https://github.com/sebastianbergmann/php-code-coverage/issues/960): New body font-size is way too big
|
||||||
|
|
||||||
|
## [9.2.19] - 2022-11-18
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* [#949](https://github.com/sebastianbergmann/php-code-coverage/pull/949): Various issues related to identifying executable lines
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Tweaked CSS for HTML report
|
||||||
|
* Updated bundled CSS/JavaScript components used for HTML report: Bootstrap 4.6.2 and jQuery 3.6.1
|
||||||
|
|
||||||
|
## [9.2.18] - 2022-10-27
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* [#935](https://github.com/sebastianbergmann/php-code-coverage/pull/935): Cobertura package name attribute is always empty
|
||||||
|
* [#946](https://github.com/sebastianbergmann/php-code-coverage/issues/946): `return` with multiline constant expression must only contain the last line
|
||||||
|
|
||||||
|
## [9.2.17] - 2022-08-30
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* [#928](https://github.com/sebastianbergmann/php-code-coverage/pull/928): Avoid unnecessary `is_file()` calls
|
||||||
|
* [#931](https://github.com/sebastianbergmann/php-code-coverage/pull/931): Use MD5 instead of CRC32 for static analysis cache file identifier
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* [#926](https://github.com/sebastianbergmann/php-code-coverage/pull/926): Static Analysis cache does not work with `open_basedir`
|
||||||
|
|
||||||
|
## [9.2.16] - 2022-08-20
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* [#926](https://github.com/sebastianbergmann/php-code-coverage/issues/926): File view has wrong colouring for the first column
|
||||||
|
|
||||||
## [9.2.15] - 2022-03-07
|
## [9.2.15] - 2022-03-07
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
@ -398,6 +464,15 @@ All notable changes are documented in this file using the [Keep a CHANGELOG](htt
|
||||||
|
|
||||||
* This component is no longer supported on PHP 7.1
|
* This component is no longer supported on PHP 7.1
|
||||||
|
|
||||||
|
[9.2.24]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.2.23...9.2.24
|
||||||
|
[9.2.23]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.2.22...9.2.23
|
||||||
|
[9.2.22]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.2.21...9.2.22
|
||||||
|
[9.2.21]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.2.20...9.2.21
|
||||||
|
[9.2.20]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.2.19...9.2.20
|
||||||
|
[9.2.19]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.2.18...9.2.19
|
||||||
|
[9.2.18]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.2.17...9.2.18
|
||||||
|
[9.2.17]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.2.16...9.2.17
|
||||||
|
[9.2.16]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.2.15...9.2.16
|
||||||
[9.2.15]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.2.14...9.2.15
|
[9.2.15]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.2.14...9.2.15
|
||||||
[9.2.14]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.2.13...9.2.14
|
[9.2.14]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.2.13...9.2.14
|
||||||
[9.2.13]: https://github.com/sebastianbergmann/php-code-coverage/compare/c011a0b6aaa4acd2f39b7f51fb4ad4442b6ec631...9.2.13
|
[9.2.13]: https://github.com/sebastianbergmann/php-code-coverage/compare/c011a0b6aaa4acd2f39b7f51fb4ad4442b6ec631...9.2.13
|
||||||
|
|
|
@ -1,33 +1,29 @@
|
||||||
php-code-coverage
|
BSD 3-Clause License
|
||||||
|
|
||||||
Copyright (c) 2009-2022, Sebastian Bergmann <sebastian@phpunit.de>.
|
Copyright (c) 2009-2023, Sebastian Bergmann
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions
|
modification, are permitted provided that the following conditions are met:
|
||||||
are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
notice, this list of conditions and the following disclaimer.
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
notice, this list of conditions and the following disclaimer in
|
this list of conditions and the following disclaimer in the documentation
|
||||||
the documentation and/or other materials provided with the
|
and/or other materials provided with the distribution.
|
||||||
distribution.
|
|
||||||
|
|
||||||
* Neither the name of Sebastian Bergmann nor the names of his
|
3. Neither the name of the copyright holder nor the names of its
|
||||||
contributors may be used to endorse or promote products derived
|
contributors may be used to endorse or promote products derived from
|
||||||
from this software without specific prior written permission.
|
this software without specific prior written permission.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
"ext-dom": "*",
|
"ext-dom": "*",
|
||||||
"ext-libxml": "*",
|
"ext-libxml": "*",
|
||||||
"ext-xmlwriter": "*",
|
"ext-xmlwriter": "*",
|
||||||
"nikic/php-parser": "^4.13.0",
|
"nikic/php-parser": "^4.14",
|
||||||
"phpunit/php-file-iterator": "^3.0.3",
|
"phpunit/php-file-iterator": "^3.0.3",
|
||||||
"phpunit/php-text-template": "^2.0.2",
|
"phpunit/php-text-template": "^2.0.2",
|
||||||
"sebastian/code-unit-reverse-lookup": "^2.0.2",
|
"sebastian/code-unit-reverse-lookup": "^2.0.2",
|
||||||
|
|
|
@ -20,7 +20,6 @@ use function count;
|
||||||
use function explode;
|
use function explode;
|
||||||
use function get_class;
|
use function get_class;
|
||||||
use function is_array;
|
use function is_array;
|
||||||
use function is_file;
|
|
||||||
use function sort;
|
use function sort;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use PHPUnit\Runner\PhptTestCase;
|
use PHPUnit\Runner\PhptTestCase;
|
||||||
|
@ -77,7 +76,7 @@ final class CodeCoverage
|
||||||
private $ignoreDeprecatedCode = false;
|
private $ignoreDeprecatedCode = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var PhptTestCase|string|TestCase
|
* @var null|PhptTestCase|string|TestCase
|
||||||
*/
|
*/
|
||||||
private $currentId;
|
private $currentId;
|
||||||
|
|
||||||
|
@ -486,9 +485,16 @@ final class CodeCoverage
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$linesToBranchMap = $this->analyser()->executableLinesIn($filename);
|
||||||
|
|
||||||
$data->keepLineCoverageDataOnlyForLines(
|
$data->keepLineCoverageDataOnlyForLines(
|
||||||
$filename,
|
$filename,
|
||||||
$this->analyser()->executableLinesIn($filename)
|
array_keys($linesToBranchMap)
|
||||||
|
);
|
||||||
|
|
||||||
|
$data->markExecutableLineByBranch(
|
||||||
|
$filename,
|
||||||
|
$linesToBranchMap
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -518,7 +524,7 @@ final class CodeCoverage
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach ($uncoveredFiles as $uncoveredFile) {
|
foreach ($uncoveredFiles as $uncoveredFile) {
|
||||||
if (is_file($uncoveredFile)) {
|
if ($this->filter->isFile($uncoveredFile)) {
|
||||||
$this->append(
|
$this->append(
|
||||||
RawCodeCoverageData::fromUncoveredFile(
|
RawCodeCoverageData::fromUncoveredFile(
|
||||||
$uncoveredFile,
|
$uncoveredFile,
|
||||||
|
@ -543,7 +549,7 @@ final class CodeCoverage
|
||||||
$this->driver->start();
|
$this->driver->start();
|
||||||
|
|
||||||
foreach ($uncoveredFiles as $uncoveredFile) {
|
foreach ($uncoveredFiles as $uncoveredFile) {
|
||||||
if (is_file($uncoveredFile)) {
|
if ($this->filter->isFile($uncoveredFile)) {
|
||||||
include_once $uncoveredFile;
|
include_once $uncoveredFile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -644,7 +650,7 @@ final class CodeCoverage
|
||||||
} catch (\ReflectionException $e) {
|
} catch (\ReflectionException $e) {
|
||||||
throw new ReflectionException(
|
throw new ReflectionException(
|
||||||
$e->getMessage(),
|
$e->getMessage(),
|
||||||
(int) $e->getCode(),
|
$e->getCode(),
|
||||||
$e
|
$e
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,11 +100,7 @@ final class Filter
|
||||||
|
|
||||||
public function isExcluded(string $filename): bool
|
public function isExcluded(string $filename): bool
|
||||||
{
|
{
|
||||||
if (!$this->isFile($filename)) {
|
return !isset($this->files[$filename]) || !$this->isFile($filename);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !isset($this->files[$filename]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -74,8 +74,6 @@ final class Iterator implements RecursiveIterator
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the sub iterator for the current element.
|
* Returns the sub iterator for the current element.
|
||||||
*
|
|
||||||
* @return Iterator
|
|
||||||
*/
|
*/
|
||||||
public function getChildren(): self
|
public function getChildren(): self
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,8 +15,12 @@ use function array_flip;
|
||||||
use function array_intersect;
|
use function array_intersect;
|
||||||
use function array_intersect_key;
|
use function array_intersect_key;
|
||||||
use function count;
|
use function count;
|
||||||
|
use function explode;
|
||||||
|
use function file_get_contents;
|
||||||
use function in_array;
|
use function in_array;
|
||||||
|
use function is_file;
|
||||||
use function range;
|
use function range;
|
||||||
|
use function trim;
|
||||||
use SebastianBergmann\CodeCoverage\Driver\Driver;
|
use SebastianBergmann\CodeCoverage\Driver\Driver;
|
||||||
use SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser;
|
use SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser;
|
||||||
|
|
||||||
|
@ -87,7 +91,7 @@ final class RawCodeCoverageData
|
||||||
{
|
{
|
||||||
$lineCoverage = [];
|
$lineCoverage = [];
|
||||||
|
|
||||||
foreach ($analyser->executableLinesIn($filename) as $line) {
|
foreach ($analyser->executableLinesIn($filename) as $line => $branch) {
|
||||||
$lineCoverage[$line] = Driver::LINE_NOT_EXECUTED;
|
$lineCoverage[$line] = Driver::LINE_NOT_EXECUTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,6 +141,42 @@ final class RawCodeCoverageData
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int[] $linesToBranchMap
|
||||||
|
*/
|
||||||
|
public function markExecutableLineByBranch(string $filename, array $linesToBranchMap): void
|
||||||
|
{
|
||||||
|
if (!isset($this->lineCoverage[$filename])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$linesByBranch = [];
|
||||||
|
|
||||||
|
foreach ($linesToBranchMap as $line => $branch) {
|
||||||
|
$linesByBranch[$branch][] = $line;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->lineCoverage[$filename] as $line => $lineStatus) {
|
||||||
|
if (!isset($linesToBranchMap[$line])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$branch = $linesToBranchMap[$line];
|
||||||
|
|
||||||
|
if (!isset($linesByBranch[$branch])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($linesByBranch[$branch] as $lineInBranch) {
|
||||||
|
$this->lineCoverage[$filename][$lineInBranch] = $lineStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Driver::LINE_EXECUTED === $lineStatus) {
|
||||||
|
unset($linesByBranch[$branch]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int[] $lines
|
* @param int[] $lines
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -9,10 +9,13 @@
|
||||||
*/
|
*/
|
||||||
namespace SebastianBergmann\CodeCoverage\Report;
|
namespace SebastianBergmann\CodeCoverage\Report;
|
||||||
|
|
||||||
|
use function basename;
|
||||||
use function count;
|
use function count;
|
||||||
use function dirname;
|
use function dirname;
|
||||||
use function file_put_contents;
|
use function file_put_contents;
|
||||||
|
use function preg_match;
|
||||||
use function range;
|
use function range;
|
||||||
|
use function str_replace;
|
||||||
use function time;
|
use function time;
|
||||||
use DOMImplementation;
|
use DOMImplementation;
|
||||||
use SebastianBergmann\CodeCoverage\CodeCoverage;
|
use SebastianBergmann\CodeCoverage\CodeCoverage;
|
||||||
|
@ -25,7 +28,7 @@ final class Cobertura
|
||||||
/**
|
/**
|
||||||
* @throws WriteOperationFailedException
|
* @throws WriteOperationFailedException
|
||||||
*/
|
*/
|
||||||
public function process(CodeCoverage $coverage, ?string $target = null, ?string $name = null): string
|
public function process(CodeCoverage $coverage, ?string $target = null): string
|
||||||
{
|
{
|
||||||
$time = (string) time();
|
$time = (string) time();
|
||||||
|
|
||||||
|
@ -84,9 +87,8 @@ final class Cobertura
|
||||||
|
|
||||||
$packageElement = $document->createElement('package');
|
$packageElement = $document->createElement('package');
|
||||||
$packageComplexity = 0;
|
$packageComplexity = 0;
|
||||||
$packageName = $name ?? '';
|
|
||||||
|
|
||||||
$packageElement->setAttribute('name', $packageName);
|
$packageElement->setAttribute('name', str_replace($report->pathAsString() . DIRECTORY_SEPARATOR, '', $item->pathAsString()));
|
||||||
|
|
||||||
$linesValid = $item->numberOfExecutableLines();
|
$linesValid = $item->numberOfExecutableLines();
|
||||||
$linesCovered = $item->numberOfExecutedLines();
|
$linesCovered = $item->numberOfExecutedLines();
|
||||||
|
|
|
@ -80,6 +80,8 @@ use const T_WHILE;
|
||||||
use const T_YIELD;
|
use const T_YIELD;
|
||||||
use const T_YIELD_FROM;
|
use const T_YIELD_FROM;
|
||||||
use function array_key_exists;
|
use function array_key_exists;
|
||||||
|
use function array_keys;
|
||||||
|
use function array_merge;
|
||||||
use function array_pop;
|
use function array_pop;
|
||||||
use function array_unique;
|
use function array_unique;
|
||||||
use function constant;
|
use function constant;
|
||||||
|
@ -89,6 +91,9 @@ use function explode;
|
||||||
use function file_get_contents;
|
use function file_get_contents;
|
||||||
use function htmlspecialchars;
|
use function htmlspecialchars;
|
||||||
use function is_string;
|
use function is_string;
|
||||||
|
use function ksort;
|
||||||
|
use function range;
|
||||||
|
use function sort;
|
||||||
use function sprintf;
|
use function sprintf;
|
||||||
use function str_replace;
|
use function str_replace;
|
||||||
use function substr;
|
use function substr;
|
||||||
|
@ -797,8 +802,15 @@ final class File extends Renderer
|
||||||
$singleLineTemplate = new Template($this->templatePath . 'line.html.dist', '{{', '}}');
|
$singleLineTemplate = new Template($this->templatePath . 'line.html.dist', '{{', '}}');
|
||||||
|
|
||||||
$lines = '';
|
$lines = '';
|
||||||
|
$first = true;
|
||||||
|
|
||||||
foreach ($path['path'] as $branchId) {
|
foreach ($path['path'] as $branchId) {
|
||||||
|
if ($first) {
|
||||||
|
$first = false;
|
||||||
|
} else {
|
||||||
|
$lines .= ' <tr><td colspan="2"> </td></tr>' . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
$branchLines = range($branches[$branchId]['line_start'], $branches[$branchId]['line_end']);
|
$branchLines = range($branches[$branchId]['line_start'], $branches[$branchId]['line_end']);
|
||||||
sort($branchLines); // sometimes end_line < start_line
|
sort($branchLines); // sometimes end_line < start_line
|
||||||
|
|
||||||
|
@ -834,6 +846,7 @@ final class File extends Renderer
|
||||||
|
|
||||||
$popoverContent .= $this->createPopoverContentForTest($test, $testData[$test]);
|
$popoverContent .= $this->createPopoverContentForTest($test, $testData[$test]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$trClass = $lineCss . ' popin';
|
$trClass = $lineCss . ' popin';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,4 +1,9 @@
|
||||||
body {
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: 1em;
|
||||||
|
font-kerning: normal;
|
||||||
|
font-variant-ligatures: common-ligatures;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +13,8 @@ body {
|
||||||
|
|
||||||
.octicon {
|
.octicon {
|
||||||
margin-right:.25em;
|
margin-right:.25em;
|
||||||
|
vertical-align: baseline;
|
||||||
|
width: 0.75em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-bordered>thead>tr>td {
|
.table-bordered>thead>tr>td {
|
||||||
|
@ -57,6 +64,7 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
td.big {
|
td.big {
|
||||||
|
vertical-align: middle;
|
||||||
width: 117px;
|
width: 117px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Dashboard for {{full_path}}</title>
|
<title>Dashboard for {{full_path}}</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link href="{{path_to_root}}_css/bootstrap.min.css" rel="stylesheet" type="text/css">
|
<link href="{{path_to_root}}_css/bootstrap.min.css?v={{version}}" rel="stylesheet" type="text/css">
|
||||||
<link href="{{path_to_root}}_css/nv.d3.min.css" rel="stylesheet" type="text/css">
|
<link href="{{path_to_root}}_css/nv.d3.min.css?v={{version}}" rel="stylesheet" type="text/css">
|
||||||
<link href="{{path_to_root}}_css/style.css" rel="stylesheet" type="text/css">
|
<link href="{{path_to_root}}_css/style.css?v={{version}}" rel="stylesheet" type="text/css">
|
||||||
<link href="{{path_to_root}}_css/custom.css" rel="stylesheet" type="text/css">
|
<link href="{{path_to_root}}_css/custom.css" rel="stylesheet" type="text/css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -137,9 +137,9 @@
|
||||||
</p>
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
<script src="{{path_to_root}}_js/jquery.min.js" type="text/javascript"></script>
|
<script src="{{path_to_root}}_js/jquery.min.js?v={{version}}" type="text/javascript"></script>
|
||||||
<script src="{{path_to_root}}_js/d3.min.js" type="text/javascript"></script>
|
<script src="{{path_to_root}}_js/d3.min.js?v={{version}}" type="text/javascript"></script>
|
||||||
<script src="{{path_to_root}}_js/nv.d3.min.js" type="text/javascript"></script>
|
<script src="{{path_to_root}}_js/nv.d3.min.js?v={{version}}" type="text/javascript"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
nv.addGraph(function() {
|
nv.addGraph(function() {
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Dashboard for {{full_path}}</title>
|
<title>Dashboard for {{full_path}}</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link href="{{path_to_root}}_css/bootstrap.min.css" rel="stylesheet" type="text/css">
|
<link href="{{path_to_root}}_css/bootstrap.min.css?v={{version}}" rel="stylesheet" type="text/css">
|
||||||
<link href="{{path_to_root}}_css/nv.d3.min.css" rel="stylesheet" type="text/css">
|
<link href="{{path_to_root}}_css/nv.d3.min.css?v={{version}}" rel="stylesheet" type="text/css">
|
||||||
<link href="{{path_to_root}}_css/style.css" rel="stylesheet" type="text/css">
|
<link href="{{path_to_root}}_css/style.css?v={{version}}" rel="stylesheet" type="text/css">
|
||||||
<link href="{{path_to_root}}_css/custom.css" rel="stylesheet" type="text/css">
|
<link href="{{path_to_root}}_css/custom.css" rel="stylesheet" type="text/css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -137,9 +137,9 @@
|
||||||
</p>
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
<script src="{{path_to_root}}_js/jquery.min.js" type="text/javascript"></script>
|
<script src="{{path_to_root}}_js/jquery.min.js?v={{version}}" type="text/javascript"></script>
|
||||||
<script src="{{path_to_root}}_js/d3.min.js" type="text/javascript"></script>
|
<script src="{{path_to_root}}_js/d3.min.js?v={{version}}" type="text/javascript"></script>
|
||||||
<script src="{{path_to_root}}_js/nv.d3.min.js" type="text/javascript"></script>
|
<script src="{{path_to_root}}_js/nv.d3.min.js?v={{version}}" type="text/javascript"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
nv.addGraph(function() {
|
nv.addGraph(function() {
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Code Coverage for {{full_path}}</title>
|
<title>Code Coverage for {{full_path}}</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link href="{{path_to_root}}_css/bootstrap.min.css" rel="stylesheet" type="text/css">
|
<link href="{{path_to_root}}_css/bootstrap.min.css?v={{version}}" rel="stylesheet" type="text/css">
|
||||||
<link href="{{path_to_root}}_css/octicons.css" rel="stylesheet" type="text/css">
|
<link href="{{path_to_root}}_css/octicons.css?v={{version}}" rel="stylesheet" type="text/css">
|
||||||
<link href="{{path_to_root}}_css/style.css" rel="stylesheet" type="text/css">
|
<link href="{{path_to_root}}_css/style.css?v={{version}}" rel="stylesheet" type="text/css">
|
||||||
<link href="{{path_to_root}}_css/custom.css" rel="stylesheet" type="text/css">
|
<link href="{{path_to_root}}_css/custom.css" rel="stylesheet" type="text/css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Code Coverage for {{full_path}}</title>
|
<title>Code Coverage for {{full_path}}</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link href="{{path_to_root}}_css/bootstrap.min.css" rel="stylesheet" type="text/css">
|
<link href="{{path_to_root}}_css/bootstrap.min.css?v={{version}}" rel="stylesheet" type="text/css">
|
||||||
<link href="{{path_to_root}}_css/octicons.css" rel="stylesheet" type="text/css">
|
<link href="{{path_to_root}}_css/octicons.css?v={{version}}" rel="stylesheet" type="text/css">
|
||||||
<link href="{{path_to_root}}_css/style.css" rel="stylesheet" type="text/css">
|
<link href="{{path_to_root}}_css/style.css?v={{version}}" rel="stylesheet" type="text/css">
|
||||||
<link href="{{path_to_root}}_css/custom.css" rel="stylesheet" type="text/css">
|
<link href="{{path_to_root}}_css/custom.css" rel="stylesheet" type="text/css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Code Coverage for {{full_path}}</title>
|
<title>Code Coverage for {{full_path}}</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link href="{{path_to_root}}_css/bootstrap.min.css" rel="stylesheet" type="text/css">
|
<link href="{{path_to_root}}_css/bootstrap.min.css?v={{version}}" rel="stylesheet" type="text/css">
|
||||||
<link href="{{path_to_root}}_css/octicons.css" rel="stylesheet" type="text/css">
|
<link href="{{path_to_root}}_css/octicons.css?v={{version}}" rel="stylesheet" type="text/css">
|
||||||
<link href="{{path_to_root}}_css/style.css" rel="stylesheet" type="text/css">
|
<link href="{{path_to_root}}_css/style.css?v={{version}}" rel="stylesheet" type="text/css">
|
||||||
<link href="{{path_to_root}}_css/custom.css" rel="stylesheet" type="text/css">
|
<link href="{{path_to_root}}_css/custom.css" rel="stylesheet" type="text/css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -57,9 +57,9 @@
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
<script src="{{path_to_root}}_js/jquery.min.js" type="text/javascript"></script>
|
<script src="{{path_to_root}}_js/jquery.min.js?v={{version}}" type="text/javascript"></script>
|
||||||
<script src="{{path_to_root}}_js/popper.min.js" type="text/javascript"></script>
|
<script src="{{path_to_root}}_js/popper.min.js?v={{version}}" type="text/javascript"></script>
|
||||||
<script src="{{path_to_root}}_js/bootstrap.min.js" type="text/javascript"></script>
|
<script src="{{path_to_root}}_js/bootstrap.min.js?v={{version}}" type="text/javascript"></script>
|
||||||
<script src="{{path_to_root}}_js/file.js" type="text/javascript"></script>
|
<script src="{{path_to_root}}_js/file.js?v={{version}}" type="text/javascript"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Code Coverage for {{full_path}}</title>
|
<title>Code Coverage for {{full_path}}</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link href="{{path_to_root}}_css/bootstrap.min.css" rel="stylesheet" type="text/css">
|
<link href="{{path_to_root}}_css/bootstrap.min.css?v={{version}}" rel="stylesheet" type="text/css">
|
||||||
<link href="{{path_to_root}}_css/octicons.css" rel="stylesheet" type="text/css">
|
<link href="{{path_to_root}}_css/octicons.css?v={{version}}" rel="stylesheet" type="text/css">
|
||||||
<link href="{{path_to_root}}_css/style.css" rel="stylesheet" type="text/css">
|
<link href="{{path_to_root}}_css/style.css?v={{version}}" rel="stylesheet" type="text/css">
|
||||||
<link href="{{path_to_root}}_css/custom.css" rel="stylesheet" type="text/css">
|
<link href="{{path_to_root}}_css/custom.css" rel="stylesheet" type="text/css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -59,9 +59,9 @@
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
<script src="{{path_to_root}}_js/jquery.min.js" type="text/javascript"></script>
|
<script src="{{path_to_root}}_js/jquery.min.js?v={{version}}" type="text/javascript"></script>
|
||||||
<script src="{{path_to_root}}_js/popper.min.js" type="text/javascript"></script>
|
<script src="{{path_to_root}}_js/popper.min.js?v={{version}}" type="text/javascript"></script>
|
||||||
<script src="{{path_to_root}}_js/bootstrap.min.js" type="text/javascript"></script>
|
<script src="{{path_to_root}}_js/bootstrap.min.js?v={{version}}" type="text/javascript"></script>
|
||||||
<script src="{{path_to_root}}_js/file.js" type="text/javascript"></script>
|
<script src="{{path_to_root}}_js/file.js?v={{version}}" type="text/javascript"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td class="{{classes_level}}">{{name}}</td>
|
<td class="{{lines_level}}">{{name}}</td>
|
||||||
<td class="{{lines_level}} big">{{lines_bar}}</td>
|
<td class="{{lines_level}} big">{{lines_bar}}</td>
|
||||||
<td class="{{lines_level}} small"><div align="right">{{lines_executed_percent}}</div></td>
|
<td class="{{lines_level}} small"><div align="right">{{lines_executed_percent}}</div></td>
|
||||||
<td class="{{lines_level}} small"><div align="right">{{lines_number}}</div></td>
|
<td class="{{lines_level}} small"><div align="right">{{lines_number}}</div></td>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td class="{{classes_level}}">{{name}}</td>
|
<td class="{{lines_level}}">{{name}}</td>
|
||||||
<td class="{{lines_level}} big">{{lines_bar}}</td>
|
<td class="{{lines_level}} big">{{lines_bar}}</td>
|
||||||
<td class="{{lines_level}} small"><div align="right">{{lines_executed_percent}}</div></td>
|
<td class="{{lines_level}} small"><div align="right">{{lines_executed_percent}}</div></td>
|
||||||
<td class="{{lines_level}} small"><div align="right">{{lines_number}}</div></td>
|
<td class="{{lines_level}} small"><div align="right">{{lines_number}}</div></td>
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,5 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td class="{{methods_level}}">{{name}}</td>
|
<td class="{{lines_level}}">{{name}}</td>
|
||||||
<td class="{{lines_level}} big">{{lines_bar}}</td>
|
<td class="{{lines_level}} big">{{lines_bar}}</td>
|
||||||
<td class="{{lines_level}} small"><div align="right">{{lines_executed_percent}}</div></td>
|
<td class="{{lines_level}} small"><div align="right">{{lines_executed_percent}}</div></td>
|
||||||
<td class="{{lines_level}} small"><div align="right">{{lines_number}}</div></td>
|
<td class="{{lines_level}} small"><div align="right">{{lines_number}}</div></td>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td class="{{methods_level}}">{{name}}</td>
|
<td class="{{lines_level}}">{{name}}</td>
|
||||||
<td class="{{lines_level}} big">{{lines_bar}}</td>
|
<td class="{{lines_level}} big">{{lines_bar}}</td>
|
||||||
<td class="{{lines_level}} small"><div align="right">{{lines_executed_percent}}</div></td>
|
<td class="{{lines_level}} small"><div align="right">{{lines_executed_percent}}</div></td>
|
||||||
<td class="{{lines_level}} small"><div align="right">{{lines_number}}</div></td>
|
<td class="{{lines_level}} small"><div align="right">{{lines_number}}</div></td>
|
||||||
|
|
|
@ -12,7 +12,6 @@ namespace SebastianBergmann\CodeCoverage\Report;
|
||||||
use function dirname;
|
use function dirname;
|
||||||
use function file_put_contents;
|
use function file_put_contents;
|
||||||
use function serialize;
|
use function serialize;
|
||||||
use function sprintf;
|
|
||||||
use SebastianBergmann\CodeCoverage\CodeCoverage;
|
use SebastianBergmann\CodeCoverage\CodeCoverage;
|
||||||
use SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException;
|
use SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException;
|
||||||
use SebastianBergmann\CodeCoverage\Util\Filesystem;
|
use SebastianBergmann\CodeCoverage\Util\Filesystem;
|
||||||
|
@ -21,14 +20,8 @@ final class PHP
|
||||||
{
|
{
|
||||||
public function process(CodeCoverage $coverage, ?string $target = null): string
|
public function process(CodeCoverage $coverage, ?string $target = null): string
|
||||||
{
|
{
|
||||||
$buffer = sprintf(
|
$buffer = "<?php
|
||||||
"<?php
|
return \unserialize(<<<'END_OF_COVERAGE_SERIALIZATION'" . PHP_EOL . serialize($coverage) . PHP_EOL . 'END_OF_COVERAGE_SERIALIZATION' . PHP_EOL . ');';
|
||||||
return \unserialize(<<<'END_OF_COVERAGE_SERIALIZATION'%s%s%sEND_OF_COVERAGE_SERIALIZATION%s);",
|
|
||||||
PHP_EOL,
|
|
||||||
serialize($coverage),
|
|
||||||
PHP_EOL,
|
|
||||||
PHP_EOL
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($target !== null) {
|
if ($target !== null) {
|
||||||
Filesystem::createDirectory(dirname($target));
|
Filesystem::createDirectory(dirname($target));
|
||||||
|
|
|
@ -37,7 +37,7 @@ final class Coverage
|
||||||
{
|
{
|
||||||
$this->contextNode = $context;
|
$this->contextNode = $context;
|
||||||
|
|
||||||
$this->writer = new XMLWriter();
|
$this->writer = new XMLWriter;
|
||||||
$this->writer->openMemory();
|
$this->writer->openMemory();
|
||||||
$this->writer->startElementNS(null, $context->nodeName, 'https://schema.phpunit.de/coverage/1.0');
|
$this->writer->startElementNS(null, $context->nodeName, 'https://schema.phpunit.de/coverage/1.0');
|
||||||
$this->writer->writeAttribute('nr', $line);
|
$this->writer->writeAttribute('nr', $line);
|
||||||
|
|
|
@ -20,7 +20,7 @@ final class Report extends File
|
||||||
{
|
{
|
||||||
public function __construct(string $name)
|
public function __construct(string $name)
|
||||||
{
|
{
|
||||||
$dom = new DOMDocument();
|
$dom = new DOMDocument;
|
||||||
$dom->loadXML('<?xml version="1.0" ?><phpunit xmlns="https://schema.phpunit.de/coverage/1.0"><file /></phpunit>');
|
$dom->loadXML('<?xml version="1.0" ?><phpunit xmlns="https://schema.phpunit.de/coverage/1.0"><file /></phpunit>');
|
||||||
|
|
||||||
$contextNode = $dom->getElementsByTagNameNS(
|
$contextNode = $dom->getElementsByTagNameNS(
|
||||||
|
|
|
@ -31,7 +31,7 @@ final class Source
|
||||||
{
|
{
|
||||||
$context = $this->context;
|
$context = $this->context;
|
||||||
|
|
||||||
$tokens = (new Tokenizer())->parse($source);
|
$tokens = (new Tokenizer)->parse($source);
|
||||||
$srcDom = (new XMLSerializer(new NamespaceUri($context->namespaceURI)))->toDom($tokens);
|
$srcDom = (new XMLSerializer(new NamespaceUri($context->namespaceURI)))->toDom($tokens);
|
||||||
|
|
||||||
$context->parentNode->replaceChild(
|
$context->parentNode->replaceChild(
|
||||||
|
|
|
@ -9,15 +9,15 @@
|
||||||
*/
|
*/
|
||||||
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
|
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
|
||||||
|
|
||||||
use function assert;
|
|
||||||
use function crc32;
|
|
||||||
use function file_get_contents;
|
use function file_get_contents;
|
||||||
use function file_put_contents;
|
use function file_put_contents;
|
||||||
|
use function implode;
|
||||||
use function is_file;
|
use function is_file;
|
||||||
|
use function md5;
|
||||||
use function serialize;
|
use function serialize;
|
||||||
use GlobIterator;
|
use function unserialize;
|
||||||
use SebastianBergmann\CodeCoverage\Util\Filesystem;
|
use SebastianBergmann\CodeCoverage\Util\Filesystem;
|
||||||
use SplFileInfo;
|
use SebastianBergmann\FileIterator\Facade as FileIteratorFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||||
|
@ -50,10 +50,6 @@ final class CachingFileAnalyser implements FileAnalyser
|
||||||
|
|
||||||
$this->analyser = $analyser;
|
$this->analyser = $analyser;
|
||||||
$this->directory = $directory;
|
$this->directory = $directory;
|
||||||
|
|
||||||
if (self::$cacheVersion === null) {
|
|
||||||
$this->calculateCacheVersion();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function classesIn(string $filename): array
|
public function classesIn(string $filename): array
|
||||||
|
@ -165,19 +161,24 @@ final class CachingFileAnalyser implements FileAnalyser
|
||||||
|
|
||||||
private function cacheFile(string $filename): string
|
private function cacheFile(string $filename): string
|
||||||
{
|
{
|
||||||
return $this->directory . DIRECTORY_SEPARATOR . hash('sha256', $filename . crc32(file_get_contents($filename)) . self::$cacheVersion);
|
return $this->directory . DIRECTORY_SEPARATOR . md5($filename . "\0" . file_get_contents($filename) . "\0" . self::cacheVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
private function calculateCacheVersion(): void
|
private static function cacheVersion(): string
|
||||||
{
|
{
|
||||||
$buffer = '';
|
if (self::$cacheVersion !== null) {
|
||||||
|
return self::$cacheVersion;
|
||||||
foreach (new GlobIterator(__DIR__ . '/*.php') as $file) {
|
|
||||||
assert($file instanceof SplFileInfo);
|
|
||||||
|
|
||||||
$buffer .= file_get_contents($file->getPathname());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self::$cacheVersion = (string) crc32($buffer);
|
$buffer = [];
|
||||||
|
|
||||||
|
foreach ((new FileIteratorFacade)->getFilesAsArray(__DIR__, '.php') as $file) {
|
||||||
|
$buffer[] = $file;
|
||||||
|
$buffer[] = file_get_contents($file);
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$cacheVersion = md5(implode("\0", $buffer));
|
||||||
|
|
||||||
|
return self::$cacheVersion;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
*/
|
*/
|
||||||
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
|
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
|
||||||
|
|
||||||
|
use function assert;
|
||||||
use function implode;
|
use function implode;
|
||||||
use function rtrim;
|
use function rtrim;
|
||||||
use function trim;
|
use function trim;
|
||||||
|
@ -314,6 +315,8 @@ final class CodeUnitFindingVisitor extends NodeVisitorAbstract
|
||||||
if ($_type instanceof Name) {
|
if ($_type instanceof Name) {
|
||||||
$types[] = $_type->toCodeString();
|
$types[] = $_type->toCodeString();
|
||||||
} else {
|
} else {
|
||||||
|
assert($_type instanceof Identifier);
|
||||||
|
|
||||||
$types[] = $_type->toString();
|
$types[] = $_type->toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,46 +9,19 @@
|
||||||
*/
|
*/
|
||||||
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
|
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
|
||||||
|
|
||||||
|
use function array_diff_key;
|
||||||
|
use function assert;
|
||||||
|
use function count;
|
||||||
|
use function current;
|
||||||
|
use function end;
|
||||||
|
use function explode;
|
||||||
|
use function max;
|
||||||
|
use function preg_match;
|
||||||
|
use function preg_quote;
|
||||||
|
use function range;
|
||||||
|
use function reset;
|
||||||
|
use function sprintf;
|
||||||
use PhpParser\Node;
|
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\PropertyFetch;
|
|
||||||
use PhpParser\Node\Expr\StaticPropertyFetch;
|
|
||||||
use PhpParser\Node\Expr\Ternary;
|
|
||||||
use PhpParser\Node\MatchArm;
|
|
||||||
use PhpParser\Node\Scalar\Encapsed;
|
|
||||||
use PhpParser\Node\Stmt\Break_;
|
|
||||||
use PhpParser\Node\Stmt\Case_;
|
|
||||||
use PhpParser\Node\Stmt\Catch_;
|
|
||||||
use PhpParser\Node\Stmt\Class_;
|
|
||||||
use PhpParser\Node\Stmt\ClassMethod;
|
|
||||||
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\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\NodeVisitorAbstract;
|
use PhpParser\NodeVisitorAbstract;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -57,217 +30,337 @@ use PhpParser\NodeVisitorAbstract;
|
||||||
final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
|
final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @psalm-var array<int, int>
|
* @var int
|
||||||
*/
|
*/
|
||||||
private $executableLines = [];
|
private $nextBranch = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @psalm-var array<int, int>
|
* @var string
|
||||||
*/
|
*/
|
||||||
private $propertyLines = [];
|
private $source;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @psalm-var array<int, Return_>
|
* @var array<int, int>
|
||||||
*/
|
*/
|
||||||
private $returns = [];
|
private $executableLinesGroupedByBranch = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<int, bool>
|
||||||
|
*/
|
||||||
|
private $unsets = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<int, string>
|
||||||
|
*/
|
||||||
|
private $commentsToCheckForUnset = [];
|
||||||
|
|
||||||
|
public function __construct(string $source)
|
||||||
|
{
|
||||||
|
$this->source = $source;
|
||||||
|
}
|
||||||
|
|
||||||
public function enterNode(Node $node): void
|
public function enterNode(Node $node): void
|
||||||
{
|
{
|
||||||
$this->savePropertyLines($node);
|
foreach ($node->getComments() as $comment) {
|
||||||
|
$commentLine = $comment->getStartLine();
|
||||||
|
|
||||||
|
if (!isset($this->executableLinesGroupedByBranch[$commentLine])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (explode("\n", $comment->getText()) as $text) {
|
||||||
|
$this->commentsToCheckForUnset[$commentLine] = $text;
|
||||||
|
$commentLine++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($node instanceof Node\Scalar\String_ ||
|
||||||
|
$node instanceof Node\Scalar\EncapsedStringPart) {
|
||||||
|
$startLine = $node->getStartLine() + 1;
|
||||||
|
$endLine = $node->getEndLine() - 1;
|
||||||
|
|
||||||
|
if ($startLine <= $endLine) {
|
||||||
|
foreach (range($startLine, $endLine) as $line) {
|
||||||
|
unset($this->executableLinesGroupedByBranch[$line]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!$this->isExecutable($node)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->getLines($node) as $line) {
|
if ($node instanceof Node\Stmt\Declare_ ||
|
||||||
if (isset($this->propertyLines[$line])) {
|
$node instanceof Node\Stmt\DeclareDeclare ||
|
||||||
|
$node instanceof Node\Stmt\Else_ ||
|
||||||
|
$node instanceof Node\Stmt\EnumCase ||
|
||||||
|
$node instanceof Node\Stmt\Finally_ ||
|
||||||
|
$node instanceof Node\Stmt\Interface_ ||
|
||||||
|
$node instanceof Node\Stmt\Label ||
|
||||||
|
$node instanceof Node\Stmt\Namespace_ ||
|
||||||
|
$node instanceof Node\Stmt\Nop ||
|
||||||
|
$node instanceof Node\Stmt\Switch_ ||
|
||||||
|
$node instanceof Node\Stmt\TryCatch ||
|
||||||
|
$node instanceof Node\Stmt\Use_ ||
|
||||||
|
$node instanceof Node\Stmt\UseUse ||
|
||||||
|
$node instanceof Node\Expr\ConstFetch ||
|
||||||
|
$node instanceof Node\Expr\Match_ ||
|
||||||
|
$node instanceof Node\Expr\Variable ||
|
||||||
|
$node instanceof Node\ComplexType ||
|
||||||
|
$node instanceof Node\Const_ ||
|
||||||
|
$node instanceof Node\Identifier ||
|
||||||
|
$node instanceof Node\Name ||
|
||||||
|
$node instanceof Node\Param ||
|
||||||
|
$node instanceof Node\Scalar) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->executableLines[$line] = $line;
|
if ($node instanceof Node\Stmt\Throw_) {
|
||||||
}
|
$this->setLineBranch($node->expr->getEndLine(), $node->expr->getEndLine(), ++$this->nextBranch);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @psalm-return array<int, int>
|
|
||||||
*/
|
|
||||||
public function executableLines(): array
|
|
||||||
{
|
|
||||||
$this->computeReturns();
|
|
||||||
|
|
||||||
sort($this->executableLines);
|
|
||||||
|
|
||||||
return $this->executableLines;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function savePropertyLines(Node $node): void
|
|
||||||
{
|
|
||||||
if (!$node instanceof Property && !$node instanceof Node\Stmt\ClassConst) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (range($node->getStartLine(), $node->getEndLine()) as $index) {
|
if ($node instanceof Node\Stmt\Enum_ ||
|
||||||
$this->propertyLines[$index] = $index;
|
$node instanceof Node\Stmt\Function_ ||
|
||||||
|
$node instanceof Node\Stmt\Class_ ||
|
||||||
|
$node instanceof Node\Stmt\ClassMethod ||
|
||||||
|
$node instanceof Node\Expr\Closure ||
|
||||||
|
$node instanceof Node\Stmt\Trait_) {
|
||||||
|
$isConcreteClassLike = $node instanceof Node\Stmt\Enum_ || $node instanceof Node\Stmt\Class_ || $node instanceof Node\Stmt\Trait_;
|
||||||
|
|
||||||
|
if (null !== $node->stmts) {
|
||||||
|
foreach ($node->stmts as $stmt) {
|
||||||
|
if ($stmt instanceof Node\Stmt\Nop) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (range($stmt->getStartLine(), $stmt->getEndLine()) as $line) {
|
||||||
|
unset($this->executableLinesGroupedByBranch[$line]);
|
||||||
|
|
||||||
|
if (
|
||||||
|
$isConcreteClassLike &&
|
||||||
|
!$stmt instanceof Node\Stmt\ClassMethod
|
||||||
|
) {
|
||||||
|
$this->unsets[$line] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function computeReturns(): void
|
if ($isConcreteClassLike) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$hasEmptyBody = [] === $node->stmts ||
|
||||||
|
null === $node->stmts ||
|
||||||
|
(
|
||||||
|
1 === count($node->stmts) &&
|
||||||
|
$node->stmts[0] instanceof Node\Stmt\Nop
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($hasEmptyBody) {
|
||||||
|
if ($node->getEndLine() === $node->getStartLine()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setLineBranch($node->getEndLine(), $node->getEndLine(), ++$this->nextBranch);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($node instanceof Node\Expr\ArrowFunction) {
|
||||||
|
$startLine = max(
|
||||||
|
$node->getStartLine() + 1,
|
||||||
|
$node->expr->getStartLine()
|
||||||
|
);
|
||||||
|
|
||||||
|
$endLine = $node->expr->getEndLine();
|
||||||
|
|
||||||
|
if ($endLine < $startLine) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setLineBranch($startLine, $endLine, ++$this->nextBranch);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($node instanceof Node\Expr\Ternary) {
|
||||||
|
if (null !== $node->if &&
|
||||||
|
$node->getStartLine() !== $node->if->getEndLine()) {
|
||||||
|
$this->setLineBranch($node->if->getStartLine(), $node->if->getEndLine(), ++$this->nextBranch);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($node->getStartLine() !== $node->else->getEndLine()) {
|
||||||
|
$this->setLineBranch($node->else->getStartLine(), $node->else->getEndLine(), ++$this->nextBranch);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($node instanceof Node\Expr\BinaryOp\Coalesce) {
|
||||||
|
if ($node->getStartLine() !== $node->getEndLine()) {
|
||||||
|
$this->setLineBranch($node->getEndLine(), $node->getEndLine(), ++$this->nextBranch);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($node instanceof Node\Stmt\If_ ||
|
||||||
|
$node instanceof Node\Stmt\ElseIf_ ||
|
||||||
|
$node instanceof Node\Stmt\Case_) {
|
||||||
|
if (null === $node->cond) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setLineBranch(
|
||||||
|
$node->cond->getStartLine(),
|
||||||
|
$node->cond->getStartLine(),
|
||||||
|
++$this->nextBranch
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($node instanceof Node\Stmt\For_) {
|
||||||
|
$startLine = null;
|
||||||
|
$endLine = null;
|
||||||
|
|
||||||
|
if ([] !== $node->init) {
|
||||||
|
$startLine = $node->init[0]->getStartLine();
|
||||||
|
|
||||||
|
end($node->init);
|
||||||
|
|
||||||
|
$endLine = current($node->init)->getEndLine();
|
||||||
|
|
||||||
|
reset($node->init);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([] !== $node->cond) {
|
||||||
|
if (null === $startLine) {
|
||||||
|
$startLine = $node->cond[0]->getStartLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
end($node->cond);
|
||||||
|
|
||||||
|
$endLine = current($node->cond)->getEndLine();
|
||||||
|
|
||||||
|
reset($node->cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([] !== $node->loop) {
|
||||||
|
if (null === $startLine) {
|
||||||
|
$startLine = $node->loop[0]->getStartLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
end($node->loop);
|
||||||
|
|
||||||
|
$endLine = current($node->loop)->getEndLine();
|
||||||
|
|
||||||
|
reset($node->loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $startLine || null === $endLine) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setLineBranch(
|
||||||
|
$startLine,
|
||||||
|
$endLine,
|
||||||
|
++$this->nextBranch
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($node instanceof Node\Stmt\Foreach_) {
|
||||||
|
$this->setLineBranch(
|
||||||
|
$node->expr->getStartLine(),
|
||||||
|
$node->valueVar->getEndLine(),
|
||||||
|
++$this->nextBranch
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($node instanceof Node\Stmt\While_ ||
|
||||||
|
$node instanceof Node\Stmt\Do_) {
|
||||||
|
$this->setLineBranch(
|
||||||
|
$node->cond->getStartLine(),
|
||||||
|
$node->cond->getEndLine(),
|
||||||
|
++$this->nextBranch
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($node instanceof Node\Stmt\Catch_) {
|
||||||
|
assert([] !== $node->types);
|
||||||
|
$startLine = $node->types[0]->getStartLine();
|
||||||
|
end($node->types);
|
||||||
|
$endLine = current($node->types)->getEndLine();
|
||||||
|
|
||||||
|
$this->setLineBranch(
|
||||||
|
$startLine,
|
||||||
|
$endLine,
|
||||||
|
++$this->nextBranch
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($node instanceof Node\Expr\CallLike) {
|
||||||
|
if (isset($this->executableLinesGroupedByBranch[$node->getStartLine()])) {
|
||||||
|
$branch = $this->executableLinesGroupedByBranch[$node->getStartLine()];
|
||||||
|
} else {
|
||||||
|
$branch = ++$this->nextBranch;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setLineBranch($node->getStartLine(), $node->getEndLine(), $branch);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->executableLinesGroupedByBranch[$node->getStartLine()])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setLineBranch($node->getStartLine(), $node->getEndLine(), ++$this->nextBranch);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function afterTraverse(array $nodes): void
|
||||||
{
|
{
|
||||||
foreach ($this->returns as $return) {
|
$lines = explode("\n", $this->source);
|
||||||
foreach (range($return->getStartLine(), $return->getEndLine()) as $loc) {
|
|
||||||
if (isset($this->executableLines[$loc])) {
|
|
||||||
continue 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$line = $return->getEndLine();
|
foreach ($lines as $lineNumber => $line) {
|
||||||
|
$lineNumber++;
|
||||||
|
|
||||||
if ($return->expr !== null) {
|
if (1 === preg_match('/^\s*$/', $line) ||
|
||||||
$line = $return->expr->getStartLine();
|
(
|
||||||
}
|
isset($this->commentsToCheckForUnset[$lineNumber]) &&
|
||||||
|
1 === preg_match(sprintf('/^\s*%s\s*$/', preg_quote($this->commentsToCheckForUnset[$lineNumber], '/')), $line)
|
||||||
$this->executableLines[$line] = $line;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return int[]
|
|
||||||
*/
|
|
||||||
private function getLines(Node $node): array
|
|
||||||
{
|
|
||||||
if ($node instanceof Cast ||
|
|
||||||
$node instanceof PropertyFetch ||
|
|
||||||
$node instanceof NullsafePropertyFetch ||
|
|
||||||
$node instanceof StaticPropertyFetch) {
|
|
||||||
return [$node->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 [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([] === $node->items) {
|
|
||||||
return [$node->getEndLine()];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($node->items[0] instanceof ArrayItem) {
|
|
||||||
return [$node->items[0]->getStartLine()];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($node instanceof ClassMethod) {
|
|
||||||
if ($node->name->name !== '__construct') {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$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());
|
|
||||||
}
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($node instanceof MethodCall) {
|
|
||||||
return [$node->name->getStartLine()];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($node instanceof Ternary) {
|
|
||||||
$lines = [$node->cond->getStartLine()];
|
|
||||||
|
|
||||||
if (null !== $node->if) {
|
|
||||||
$lines[] = $node->if->getStartLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
$lines[] = $node->else->getStartLine();
|
|
||||||
|
|
||||||
return $lines;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($node instanceof Match_) {
|
|
||||||
return [$node->cond->getStartLine()];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($node instanceof MatchArm) {
|
|
||||||
return [$node->body->getStartLine()];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($node instanceof Expression && (
|
|
||||||
$node->expr instanceof Cast ||
|
|
||||||
$node->expr instanceof Match_ ||
|
|
||||||
$node->expr instanceof MethodCall
|
|
||||||
)) {
|
)) {
|
||||||
return [];
|
unset($this->executableLinesGroupedByBranch[$lineNumber]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($node instanceof Return_) {
|
$this->executableLinesGroupedByBranch = array_diff_key(
|
||||||
$this->returns[] = $node;
|
$this->executableLinesGroupedByBranch,
|
||||||
|
$this->unsets
|
||||||
return [];
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return [$node->getStartLine()];
|
public function executableLinesGroupedByBranch(): array
|
||||||
}
|
|
||||||
|
|
||||||
private function isExecutable(Node $node): bool
|
|
||||||
{
|
{
|
||||||
return $node instanceof Assign ||
|
return $this->executableLinesGroupedByBranch;
|
||||||
$node instanceof ArrayDimFetch ||
|
}
|
||||||
$node instanceof Array_ ||
|
|
||||||
$node instanceof BinaryOp ||
|
private function setLineBranch(int $start, int $end, int $branch): void
|
||||||
$node instanceof Break_ ||
|
{
|
||||||
$node instanceof CallLike ||
|
foreach (range($start, $end) as $line) {
|
||||||
$node instanceof Case_ ||
|
$this->executableLinesGroupedByBranch[$line] = $branch;
|
||||||
$node instanceof Cast ||
|
}
|
||||||
$node instanceof Catch_ ||
|
|
||||||
$node instanceof ClassMethod ||
|
|
||||||
$node instanceof Closure ||
|
|
||||||
$node instanceof Continue_ ||
|
|
||||||
$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 Goto_ ||
|
|
||||||
$node instanceof If_ ||
|
|
||||||
$node instanceof Match_ ||
|
|
||||||
$node instanceof MatchArm ||
|
|
||||||
$node instanceof MethodCall ||
|
|
||||||
$node instanceof NullsafePropertyFetch ||
|
|
||||||
$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_;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,11 @@
|
||||||
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
|
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
|
||||||
|
|
||||||
use function array_merge;
|
use function array_merge;
|
||||||
|
use function assert;
|
||||||
use function range;
|
use function range;
|
||||||
use function strpos;
|
use function strpos;
|
||||||
use PhpParser\Node;
|
use PhpParser\Node;
|
||||||
|
use PhpParser\Node\Attribute;
|
||||||
use PhpParser\Node\Stmt\Class_;
|
use PhpParser\Node\Stmt\Class_;
|
||||||
use PhpParser\Node\Stmt\ClassMethod;
|
use PhpParser\Node\Stmt\ClassMethod;
|
||||||
use PhpParser\Node\Stmt\Function_;
|
use PhpParser\Node\Stmt\Function_;
|
||||||
|
@ -52,7 +54,8 @@ final class IgnoredLinesFindingVisitor extends NodeVisitorAbstract
|
||||||
!$node instanceof Trait_ &&
|
!$node instanceof Trait_ &&
|
||||||
!$node instanceof Interface_ &&
|
!$node instanceof Interface_ &&
|
||||||
!$node instanceof ClassMethod &&
|
!$node instanceof ClassMethod &&
|
||||||
!$node instanceof Function_) {
|
!$node instanceof Function_ &&
|
||||||
|
!$node instanceof Attribute) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,11 +63,16 @@ final class IgnoredLinesFindingVisitor extends NodeVisitorAbstract
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Workaround for https://bugs.xdebug.org/view.php?id=1798
|
|
||||||
if ($node instanceof Class_ ||
|
if ($node instanceof Class_ ||
|
||||||
$node instanceof Trait_ ||
|
$node instanceof Trait_ ||
|
||||||
$node instanceof Interface_) {
|
$node instanceof Interface_ ||
|
||||||
|
$node instanceof Attribute) {
|
||||||
$this->ignoredLines[] = $node->getStartLine();
|
$this->ignoredLines[] = $node->getStartLine();
|
||||||
|
|
||||||
|
assert($node->name !== null);
|
||||||
|
|
||||||
|
// Workaround for https://github.com/nikic/PHP-Parser/issues/886
|
||||||
|
$this->ignoredLines[] = $node->name->getStartLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$this->useAnnotationsForIgnoringCode) {
|
if (!$this->useAnnotationsForIgnoringCode) {
|
||||||
|
@ -75,6 +83,19 @@ final class IgnoredLinesFindingVisitor extends NodeVisitorAbstract
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->processDocComment($node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-return list<int>
|
||||||
|
*/
|
||||||
|
public function ignoredLines(): array
|
||||||
|
{
|
||||||
|
return $this->ignoredLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function processDocComment(Node $node): void
|
||||||
|
{
|
||||||
$docComment = $node->getDocComment();
|
$docComment = $node->getDocComment();
|
||||||
|
|
||||||
if ($docComment === null) {
|
if ($docComment === null) {
|
||||||
|
@ -95,12 +116,4 @@ final class IgnoredLinesFindingVisitor extends NodeVisitorAbstract
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @psalm-return list<int>
|
|
||||||
*/
|
|
||||||
public function ignoredLines(): array
|
|
||||||
{
|
|
||||||
return $this->ignoredLines;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,11 +9,14 @@
|
||||||
*/
|
*/
|
||||||
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
|
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
|
||||||
|
|
||||||
|
use function array_merge;
|
||||||
use function array_unique;
|
use function array_unique;
|
||||||
use function assert;
|
use function assert;
|
||||||
use function file_get_contents;
|
use function file_get_contents;
|
||||||
use function is_array;
|
use function is_array;
|
||||||
use function max;
|
use function max;
|
||||||
|
use function range;
|
||||||
|
use function sort;
|
||||||
use function sprintf;
|
use function sprintf;
|
||||||
use function substr_count;
|
use function substr_count;
|
||||||
use function token_get_all;
|
use function token_get_all;
|
||||||
|
@ -153,7 +156,7 @@ final class ParsingFileAnalyser implements FileAnalyser
|
||||||
$codeUnitFindingVisitor = new CodeUnitFindingVisitor;
|
$codeUnitFindingVisitor = new CodeUnitFindingVisitor;
|
||||||
$lineCountingVisitor = new LineCountingVisitor($linesOfCode);
|
$lineCountingVisitor = new LineCountingVisitor($linesOfCode);
|
||||||
$ignoredLinesFindingVisitor = new IgnoredLinesFindingVisitor($this->useAnnotationsForIgnoringCode, $this->ignoreDeprecatedCode);
|
$ignoredLinesFindingVisitor = new IgnoredLinesFindingVisitor($this->useAnnotationsForIgnoringCode, $this->ignoreDeprecatedCode);
|
||||||
$executableLinesFindingVisitor = new ExecutableLinesFindingVisitor;
|
$executableLinesFindingVisitor = new ExecutableLinesFindingVisitor($source);
|
||||||
|
|
||||||
$traverser->addVisitor(new NameResolver);
|
$traverser->addVisitor(new NameResolver);
|
||||||
$traverser->addVisitor(new ParentConnectingVisitor);
|
$traverser->addVisitor(new ParentConnectingVisitor);
|
||||||
|
@ -172,7 +175,7 @@ final class ParsingFileAnalyser implements FileAnalyser
|
||||||
$filename,
|
$filename,
|
||||||
$error->getMessage()
|
$error->getMessage()
|
||||||
),
|
),
|
||||||
(int) $error->getCode(),
|
$error->getCode(),
|
||||||
$error
|
$error
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -181,7 +184,7 @@ final class ParsingFileAnalyser implements FileAnalyser
|
||||||
$this->classes[$filename] = $codeUnitFindingVisitor->classes();
|
$this->classes[$filename] = $codeUnitFindingVisitor->classes();
|
||||||
$this->traits[$filename] = $codeUnitFindingVisitor->traits();
|
$this->traits[$filename] = $codeUnitFindingVisitor->traits();
|
||||||
$this->functions[$filename] = $codeUnitFindingVisitor->functions();
|
$this->functions[$filename] = $codeUnitFindingVisitor->functions();
|
||||||
$this->executableLines[$filename] = $executableLinesFindingVisitor->executableLines();
|
$this->executableLines[$filename] = $executableLinesFindingVisitor->executableLinesGroupedByBranch();
|
||||||
$this->ignoredLines[$filename] = [];
|
$this->ignoredLines[$filename] = [];
|
||||||
|
|
||||||
$this->findLinesIgnoredByLineBasedAnnotations($filename, $source, $this->useAnnotationsForIgnoringCode);
|
$this->findLinesIgnoredByLineBasedAnnotations($filename, $source, $this->useAnnotationsForIgnoringCode);
|
||||||
|
@ -206,45 +209,44 @@ final class ParsingFileAnalyser implements FileAnalyser
|
||||||
|
|
||||||
private function findLinesIgnoredByLineBasedAnnotations(string $filename, string $source, bool $useAnnotationsForIgnoringCode): void
|
private function findLinesIgnoredByLineBasedAnnotations(string $filename, string $source, bool $useAnnotationsForIgnoringCode): void
|
||||||
{
|
{
|
||||||
$ignore = false;
|
if (!$useAnnotationsForIgnoringCode) {
|
||||||
$stop = false;
|
return;
|
||||||
|
|
||||||
foreach (token_get_all($source) as $token) {
|
|
||||||
if (!is_array($token)) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($token[0]) {
|
$start = false;
|
||||||
case T_COMMENT:
|
|
||||||
case T_DOC_COMMENT:
|
foreach (token_get_all($source) as $token) {
|
||||||
if (!$useAnnotationsForIgnoringCode) {
|
if (!is_array($token) ||
|
||||||
break;
|
!(T_COMMENT === $token[0] || T_DOC_COMMENT === $token[0])) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$comment = trim($token[1]);
|
$comment = trim($token[1]);
|
||||||
|
|
||||||
if ($comment === '// @codeCoverageIgnore' ||
|
if ($comment === '// @codeCoverageIgnore' ||
|
||||||
$comment === '//@codeCoverageIgnore') {
|
$comment === '//@codeCoverageIgnore') {
|
||||||
$ignore = true;
|
|
||||||
$stop = true;
|
|
||||||
} elseif ($comment === '// @codeCoverageIgnoreStart' ||
|
|
||||||
$comment === '//@codeCoverageIgnoreStart') {
|
|
||||||
$ignore = true;
|
|
||||||
} elseif ($comment === '// @codeCoverageIgnoreEnd' ||
|
|
||||||
$comment === '//@codeCoverageIgnoreEnd') {
|
|
||||||
$stop = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($ignore) {
|
|
||||||
$this->ignoredLines[$filename][] = $token[2];
|
$this->ignoredLines[$filename][] = $token[2];
|
||||||
|
|
||||||
if ($stop) {
|
continue;
|
||||||
$ignore = false;
|
}
|
||||||
$stop = false;
|
|
||||||
}
|
if ($comment === '// @codeCoverageIgnoreStart' ||
|
||||||
|
$comment === '//@codeCoverageIgnoreStart') {
|
||||||
|
$start = $token[2];
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($comment === '// @codeCoverageIgnoreEnd' ||
|
||||||
|
$comment === '//@codeCoverageIgnoreEnd') {
|
||||||
|
if (false === $start) {
|
||||||
|
$start = $token[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->ignoredLines[$filename] = array_merge(
|
||||||
|
$this->ignoredLines[$filename],
|
||||||
|
range($start, $token[2])
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ final class Version
|
||||||
public static function id(): string
|
public static function id(): string
|
||||||
{
|
{
|
||||||
if (self::$version === null) {
|
if (self::$version === null) {
|
||||||
self::$version = (new VersionId('9.2.15', dirname(__DIR__)))->getVersion();
|
self::$version = (new VersionId('9.2.24', dirname(__DIR__)))->getVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::$version;
|
return self::$version;
|
||||||
|
|
Loading…
Reference in New Issue