From e8f83afdf36f28b11d0a095cdaa3bb46e13f61b5 Mon Sep 17 00:00:00 2001 From: Daniel Kreuer Date: Tue, 7 Dec 2021 17:42:43 +0100 Subject: [PATCH 1/9] Upgrade phpunit.xml.dist --- phpunit.xml.dist | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 9a5a7fbe..a4cbdb6a 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -13,12 +13,12 @@ processIsolation="false" backupGlobals="false" > - - ./test/ComposerRequireCheckerTest - - - - ./src - - + + ./test/ComposerRequireCheckerTest + + + + ./src + + From 1f4a21ad7b55306e99b520ff8c9815dcb6ebf2c0 Mon Sep 17 00:00:00 2001 From: Daniel Kreuer Date: Tue, 7 Dec 2021 17:43:31 +0100 Subject: [PATCH 2/9] Require roave/infection-static-analysis-plugin as dev dependency --- composer.json | 1 + composer.lock | 1040 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 1021 insertions(+), 20 deletions(-) diff --git a/composer.json b/composer.json index 5ab3d463..9849c059 100644 --- a/composer.json +++ b/composer.json @@ -42,6 +42,7 @@ "phing/phing": "^2.17.0", "phpstan/phpstan": "^1.2.0", "phpunit/phpunit": "^9.5.10", + "roave/infection-static-analysis-plugin": "^1.12", "vimeo/psalm": "^4.14.0" }, "config": { diff --git a/composer.lock b/composer.lock index 94ca4532..ca71eb2b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f51ef8e7a2d6331dedb99df3642a6f75", + "content-hash": "24e6e5d8a8f6904bc7d25d91c7432e35", "packages": [ { "name": "nikic/php-parser", @@ -1808,6 +1808,369 @@ }, "time": "2021-02-22T14:02:09+00:00" }, + { + "name": "infection/abstract-testframework-adapter", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/infection/abstract-testframework-adapter.git", + "reference": "18925e20d15d1a5995bb85c9dc09e8751e1e069b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/infection/abstract-testframework-adapter/zipball/18925e20d15d1a5995bb85c9dc09e8751e1e069b", + "reference": "18925e20d15d1a5995bb85c9dc09e8751e1e069b", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.8", + "friendsofphp/php-cs-fixer": "^2.17", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Infection\\AbstractTestFramework\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Maks Rafalko", + "email": "maks.rafalko@gmail.com" + } + ], + "description": "Abstract Test Framework Adapter for Infection", + "support": { + "issues": "https://github.com/infection/abstract-testframework-adapter/issues", + "source": "https://github.com/infection/abstract-testframework-adapter/tree/0.5.0" + }, + "funding": [ + { + "url": "https://github.com/infection", + "type": "github" + }, + { + "url": "https://opencollective.com/infection", + "type": "open_collective" + } + ], + "time": "2021-08-17T18:49:12+00:00" + }, + { + "name": "infection/extension-installer", + "version": "0.1.1", + "source": { + "type": "git", + "url": "https://github.com/infection/extension-installer.git", + "reference": "ff30c0adffcdbc747c96adf92382ccbe271d0afd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/infection/extension-installer/zipball/ff30c0adffcdbc747c96adf92382ccbe271d0afd", + "reference": "ff30c0adffcdbc747c96adf92382ccbe271d0afd", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.1 || ^2.0" + }, + "require-dev": { + "composer/composer": "^1.9", + "friendsofphp/php-cs-fixer": "^2.16", + "infection/infection": "^0.15.2", + "php-coveralls/php-coveralls": "^2.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12.10", + "phpstan/phpstan-phpunit": "^0.12.6", + "phpstan/phpstan-strict-rules": "^0.12.2", + "phpstan/phpstan-webmozart-assert": "^0.12.2", + "phpunit/phpunit": "^8.5", + "vimeo/psalm": "^3.8" + }, + "type": "composer-plugin", + "extra": { + "class": "Infection\\ExtensionInstaller\\Plugin" + }, + "autoload": { + "psr-4": { + "Infection\\ExtensionInstaller\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Maks Rafalko", + "email": "maks.rafalko@gmail.com" + } + ], + "description": "Infection Extension Installer", + "support": { + "issues": "https://github.com/infection/extension-installer/issues", + "source": "https://github.com/infection/extension-installer/tree/0.1.1" + }, + "time": "2020-04-25T22:40:05+00:00" + }, + { + "name": "infection/include-interceptor", + "version": "0.2.5", + "source": { + "type": "git", + "url": "https://github.com/infection/include-interceptor.git", + "reference": "0cc76d95a79d9832d74e74492b0a30139904bdf7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/infection/include-interceptor/zipball/0cc76d95a79d9832d74e74492b0a30139904bdf7", + "reference": "0cc76d95a79d9832d74e74492b0a30139904bdf7", + "shasum": "" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.16", + "infection/infection": "^0.15.0", + "phan/phan": "^2.4 || ^3", + "php-coveralls/php-coveralls": "^2.2", + "phpstan/phpstan": "^0.12.8", + "phpunit/phpunit": "^8.5", + "vimeo/psalm": "^3.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Infection\\StreamWrapper\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Maks Rafalko", + "email": "maks.rafalko@gmail.com" + } + ], + "description": "Stream Wrapper: Include Interceptor. Allows to replace included (autoloaded) file with another one.", + "support": { + "issues": "https://github.com/infection/include-interceptor/issues", + "source": "https://github.com/infection/include-interceptor/tree/0.2.5" + }, + "funding": [ + { + "url": "https://github.com/infection", + "type": "github" + }, + { + "url": "https://opencollective.com/infection", + "type": "open_collective" + } + ], + "time": "2021-08-09T10:03:57+00:00" + }, + { + "name": "infection/infection", + "version": "0.25.3", + "source": { + "type": "git", + "url": "https://github.com/infection/infection.git", + "reference": "f7a1af285476eeef7e72cedcfd69fa11fbb61c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/infection/infection/zipball/f7a1af285476eeef7e72cedcfd69fa11fbb61c71", + "reference": "f7a1af285476eeef7e72cedcfd69fa11fbb61c71", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.0", + "composer/xdebug-handler": "^2.0", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "infection/abstract-testframework-adapter": "^0.5.0", + "infection/extension-installer": "^0.1.0", + "infection/include-interceptor": "^0.2.5", + "justinrainbow/json-schema": "^5.2.10", + "nikic/php-parser": "^4.10.3", + "ondram/ci-detector": "^3.3.0", + "php": "^7.4.7 || ^8.0", + "sanmai/later": "^0.1.1", + "sanmai/pipeline": "^5.1", + "sebastian/diff": "^3.0.2 || ^4.0", + "seld/jsonlint": "^1.7", + "symfony/console": "^3.4.29 || ^4.1.19 || ^5.0", + "symfony/filesystem": "^3.4.29 || ^4.1.19 || ^5.0", + "symfony/finder": "^3.4.29 || ^4.1.19 || ^5.0", + "symfony/process": "^3.4.29 || ^4.1.19 || ^5.0", + "thecodingmachine/safe": "^1.1.3", + "webmozart/assert": "^1.3", + "webmozart/path-util": "^2.3" + }, + "conflict": { + "phpunit/php-code-coverage": ">9 <9.1.4", + "symfony/console": "=4.1.5" + }, + "require-dev": { + "brianium/paratest": "^6.3", + "ext-simplexml": "*", + "helmich/phpunit-json-assert": "^3.0", + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12.8", + "phpstan/phpstan-phpunit": "^0.12.6", + "phpstan/phpstan-strict-rules": "^0.12.5", + "phpstan/phpstan-webmozart-assert": "^0.12.2", + "phpunit/phpunit": "^9.3.11", + "symfony/phpunit-bridge": "^4.4.18 || ^5.1.10", + "symfony/yaml": "^5.0", + "thecodingmachine/phpstan-safe-rule": "^1.0" + }, + "bin": [ + "bin/infection" + ], + "type": "library", + "autoload": { + "psr-4": { + "Infection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Maks Rafalko", + "email": "maks.rafalko@gmail.com", + "homepage": "https://twitter.com/maks_rafalko" + }, + { + "name": "Oleg Zhulnev", + "homepage": "https://github.com/sidz" + }, + { + "name": "Gert de Pagter", + "homepage": "https://github.com/BackEndTea" + }, + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com", + "homepage": "https://twitter.com/tfidry" + }, + { + "name": "Alexey Kopytko", + "email": "alexey@kopytko.com", + "homepage": "https://www.alexeykopytko.com" + }, + { + "name": "Andreas Möller", + "email": "am@localheinz.com", + "homepage": "https://localheinz.com" + } + ], + "description": "Infection is a Mutation Testing framework for PHP. The mutation adequacy score can be used to measure the effectiveness of a test set in terms of its ability to detect faults.", + "keywords": [ + "coverage", + "mutant", + "mutation framework", + "mutation testing", + "testing", + "unit testing" + ], + "support": { + "issues": "https://github.com/infection/infection/issues", + "source": "https://github.com/infection/infection/tree/0.25.3" + }, + "funding": [ + { + "url": "https://github.com/infection", + "type": "github" + }, + { + "url": "https://opencollective.com/infection", + "type": "open_collective" + } + ], + "time": "2021-10-02T13:16:05+00:00" + }, + { + "name": "justinrainbow/json-schema", + "version": "5.2.11", + "source": { + "type": "git", + "url": "https://github.com/justinrainbow/json-schema.git", + "reference": "2ab6744b7296ded80f8cc4f9509abbff393399aa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/2ab6744b7296ded80f8cc4f9509abbff393399aa", + "reference": "2ab6744b7296ded80f8cc4f9509abbff393399aa", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", + "json-schema/json-schema-test-suite": "1.2.0", + "phpunit/phpunit": "^4.8.35" + }, + "bin": [ + "bin/validate-json" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "JsonSchema\\": "src/JsonSchema/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bruno Prieto Reis", + "email": "bruno.p.reis@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rainbow@gmail.com" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Robert Schönthal", + "email": "seroscho@googlemail.com" + } + ], + "description": "A library to validate a json schema.", + "homepage": "https://github.com/justinrainbow/json-schema", + "keywords": [ + "json", + "schema" + ], + "support": { + "issues": "https://github.com/justinrainbow/json-schema/issues", + "source": "https://github.com/justinrainbow/json-schema/tree/5.2.11" + }, + "time": "2021-07-22T09:24:00+00:00" + }, { "name": "mikey179/vfsstream", "version": "v1.6.10", @@ -1968,6 +2331,78 @@ }, "time": "2020-12-01T19:48:11+00:00" }, + { + "name": "ondram/ci-detector", + "version": "3.5.1", + "source": { + "type": "git", + "url": "https://github.com/OndraM/ci-detector.git", + "reference": "594e61252843b68998bddd48078c5058fe9028bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/OndraM/ci-detector/zipball/594e61252843b68998bddd48078c5058fe9028bd", + "reference": "594e61252843b68998bddd48078c5058fe9028bd", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.2", + "lmc/coding-standard": "^1.3 || ^2.0", + "php-parallel-lint/php-parallel-lint": "^1.1", + "phpstan/extension-installer": "^1.0.3", + "phpstan/phpstan": "^0.12.0", + "phpstan/phpstan-phpunit": "^0.12.1", + "phpunit/phpunit": "^7.1 || ^8.0 || ^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "OndraM\\CiDetector\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ondřej Machulda", + "email": "ondrej.machulda@gmail.com" + } + ], + "description": "Detect continuous integration environment and provide unified access to properties of current build", + "keywords": [ + "CircleCI", + "Codeship", + "Wercker", + "adapter", + "appveyor", + "aws", + "aws codebuild", + "bamboo", + "bitbucket", + "buddy", + "ci-info", + "codebuild", + "continuous integration", + "continuousphp", + "drone", + "github", + "gitlab", + "interface", + "jenkins", + "teamcity", + "travis" + ], + "support": { + "issues": "https://github.com/OndraM/ci-detector/issues", + "source": "https://github.com/OndraM/ci-detector/tree/main" + }, + "time": "2020-09-04T11:21:14+00:00" + }, { "name": "openlss/lib-array2xml", "version": "1.0.0", @@ -3056,44 +3491,218 @@ "time": "2021-05-03T11:20:27+00:00" }, { - "name": "sebastian/cli-parser", - "version": "1.0.1", + "name": "roave/infection-static-analysis-plugin", + "version": "1.12.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + "url": "https://github.com/Roave/infection-static-analysis-plugin.git", + "reference": "8390a8ab36a4b73eef8ce3c270964c21e3c831b1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "url": "https://api.github.com/repos/Roave/infection-static-analysis-plugin/zipball/8390a8ab36a4b73eef8ce3c270964c21e3c831b1", + "reference": "8390a8ab36a4b73eef8ce3c270964c21e3c831b1", "shasum": "" }, "require": { - "php": ">=7.3" + "infection/infection": "0.25.3", + "ocramius/package-versions": "^1.9.0 || ^2.0.0", + "php": "~7.4.7|~8.0.0|~8.1.0", + "sanmai/later": "^0.1.2", + "vimeo/psalm": "^4.13.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "doctrine/coding-standard": "^9.0.0", + "phpunit/phpunit": "^9.5.9" }, + "bin": [ + "bin/roave-infection-static-analysis-plugin" + ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Roave\\InfectionStaticAnalysis\\": "src/Roave/InfectionStaticAnalysis" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "Static analysis on top of mutation testing - prevents escaped mutants from being invalid according to static analysis", + "support": { + "issues": "https://github.com/Roave/infection-static-analysis-plugin/issues", + "source": "https://github.com/Roave/infection-static-analysis-plugin/tree/1.12.0" + }, + "time": "2021-11-30T01:18:25+00:00" + }, + { + "name": "sanmai/later", + "version": "0.1.2", + "source": { + "type": "git", + "url": "https://github.com/sanmai/later.git", + "reference": "9b659fecef2030193fd02402955bc39629d5606f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sanmai/later/zipball/9b659fecef2030193fd02402955bc39629d5606f", + "reference": "9b659fecef2030193fd02402955bc39629d5606f", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.13", + "infection/infection": ">=0.10.5", + "phan/phan": ">=2", + "php-coveralls/php-coveralls": "^2.0", + "phpstan/phpstan": ">=0.10", + "phpunit/phpunit": ">=7.4", + "vimeo/psalm": ">=2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Later\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Alexey Kopytko", + "email": "alexey@kopytko.com" + } + ], + "description": "Later: deferred wrapper object", + "support": { + "issues": "https://github.com/sanmai/later/issues", + "source": "https://github.com/sanmai/later/tree/0.1.2" + }, + "funding": [ + { + "url": "https://github.com/sanmai", + "type": "github" + } + ], + "time": "2021-01-02T10:26:44+00:00" + }, + { + "name": "sanmai/pipeline", + "version": "v5.2.1", + "source": { + "type": "git", + "url": "https://github.com/sanmai/pipeline.git", + "reference": "2b5509a7635143165041109eb1c393c8515724f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sanmai/pipeline/zipball/2b5509a7635143165041109eb1c393c8515724f1", + "reference": "2b5509a7635143165041109eb1c393c8515724f1", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.8", + "friendsofphp/php-cs-fixer": "^3", + "infection/infection": ">=0.10.5", + "league/pipeline": "^1.0 || ^0.3", + "phan/phan": ">=1.1", + "php-coveralls/php-coveralls": "^2.4.1", + "phpstan/phpstan": ">=0.10", + "phpunit/phpunit": "^7.4 || ^8.1 || ^9.4", + "vimeo/psalm": ">=2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "v5.x-dev" + } + }, + "autoload": { + "psr-4": { + "Pipeline\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Alexey Kopytko", + "email": "alexey@kopytko.com" + } + ], + "description": "General-purpose collections pipeline", + "support": { + "issues": "https://github.com/sanmai/pipeline/issues", + "source": "https://github.com/sanmai/pipeline/tree/v5.2.1" + }, + "funding": [ + { + "url": "https://github.com/sanmai", + "type": "github" + } + ], + "time": "2021-11-01T10:09:55+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -4019,6 +4628,69 @@ ], "time": "2020-09-28T06:39:44+00:00" }, + { + "name": "seld/jsonlint", + "version": "1.8.3", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/jsonlint.git", + "reference": "9ad6ce79c342fbd44df10ea95511a1b24dee5b57" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/9ad6ce79c342fbd44df10ea95511a1b24dee5b57", + "reference": "9ad6ce79c342fbd44df10ea95511a1b24dee5b57", + "shasum": "" + }, + "require": { + "php": "^5.3 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + }, + "bin": [ + "bin/jsonlint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Seld\\JsonLint\\": "src/Seld/JsonLint/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "JSON Linter", + "keywords": [ + "json", + "linter", + "parser", + "validator" + ], + "support": { + "issues": "https://github.com/Seldaek/jsonlint/issues", + "source": "https://github.com/Seldaek/jsonlint/tree/1.8.3" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint", + "type": "tidelift" + } + ], + "time": "2020-11-11T09:19:24+00:00" + }, { "name": "slevomat/coding-standard", "version": "7.0.16", @@ -4136,6 +4808,334 @@ }, "time": "2021-10-11T04:00:11+00:00" }, + { + "name": "symfony/filesystem", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "731f917dc31edcffec2c6a777f3698c33bea8f01" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/731f917dc31edcffec2c6a777f3698c33bea8f01", + "reference": "731f917dc31edcffec2c6a777f3698c33bea8f01", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v5.4.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-10-28T13:39:27+00:00" + }, + { + "name": "symfony/finder", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "d2f29dac98e96a98be467627bd49c2efb1bc2590" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/d2f29dac98e96a98be467627bd49c2efb1bc2590", + "reference": "d2f29dac98e96a98be467627bd49c2efb1bc2590", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v5.4.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-11-28T15:25:38+00:00" + }, + { + "name": "symfony/process", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "5be20b3830f726e019162b26223110c8f47cf274" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/5be20b3830f726e019162b26223110c8f47cf274", + "reference": "5be20b3830f726e019162b26223110c8f47cf274", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v5.4.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-11-28T15:25:38+00:00" + }, + { + "name": "thecodingmachine/safe", + "version": "v1.3.3", + "source": { + "type": "git", + "url": "https://github.com/thecodingmachine/safe.git", + "reference": "a8ab0876305a4cdaef31b2350fcb9811b5608dbc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thecodingmachine/safe/zipball/a8ab0876305a4cdaef31b2350fcb9811b5608dbc", + "reference": "a8ab0876305a4cdaef31b2350fcb9811b5608dbc", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "require-dev": { + "phpstan/phpstan": "^0.12", + "squizlabs/php_codesniffer": "^3.2", + "thecodingmachine/phpstan-strict-rules": "^0.12" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.1-dev" + } + }, + "autoload": { + "psr-4": { + "Safe\\": [ + "lib/", + "deprecated/", + "generated/" + ] + }, + "files": [ + "deprecated/apc.php", + "deprecated/libevent.php", + "deprecated/mssql.php", + "deprecated/stats.php", + "lib/special_cases.php", + "generated/apache.php", + "generated/apcu.php", + "generated/array.php", + "generated/bzip2.php", + "generated/calendar.php", + "generated/classobj.php", + "generated/com.php", + "generated/cubrid.php", + "generated/curl.php", + "generated/datetime.php", + "generated/dir.php", + "generated/eio.php", + "generated/errorfunc.php", + "generated/exec.php", + "generated/fileinfo.php", + "generated/filesystem.php", + "generated/filter.php", + "generated/fpm.php", + "generated/ftp.php", + "generated/funchand.php", + "generated/gmp.php", + "generated/gnupg.php", + "generated/hash.php", + "generated/ibase.php", + "generated/ibmDb2.php", + "generated/iconv.php", + "generated/image.php", + "generated/imap.php", + "generated/info.php", + "generated/ingres-ii.php", + "generated/inotify.php", + "generated/json.php", + "generated/ldap.php", + "generated/libxml.php", + "generated/lzf.php", + "generated/mailparse.php", + "generated/mbstring.php", + "generated/misc.php", + "generated/msql.php", + "generated/mysql.php", + "generated/mysqli.php", + "generated/mysqlndMs.php", + "generated/mysqlndQc.php", + "generated/network.php", + "generated/oci8.php", + "generated/opcache.php", + "generated/openssl.php", + "generated/outcontrol.php", + "generated/password.php", + "generated/pcntl.php", + "generated/pcre.php", + "generated/pdf.php", + "generated/pgsql.php", + "generated/posix.php", + "generated/ps.php", + "generated/pspell.php", + "generated/readline.php", + "generated/rpminfo.php", + "generated/rrd.php", + "generated/sem.php", + "generated/session.php", + "generated/shmop.php", + "generated/simplexml.php", + "generated/sockets.php", + "generated/sodium.php", + "generated/solr.php", + "generated/spl.php", + "generated/sqlsrv.php", + "generated/ssdeep.php", + "generated/ssh2.php", + "generated/stream.php", + "generated/strings.php", + "generated/swoole.php", + "generated/uodbc.php", + "generated/uopz.php", + "generated/url.php", + "generated/var.php", + "generated/xdiff.php", + "generated/xml.php", + "generated/xmlrpc.php", + "generated/yaml.php", + "generated/yaz.php", + "generated/zip.php", + "generated/zlib.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHP core functions that throw exceptions instead of returning FALSE on error", + "support": { + "issues": "https://github.com/thecodingmachine/safe/issues", + "source": "https://github.com/thecodingmachine/safe/tree/v1.3.3" + }, + "time": "2020-10-28T17:51:34+00:00" + }, { "name": "theseer/tokenizer", "version": "1.2.1", From 114f93804e591bcc0ed062e186fa6e1743b1ae95 Mon Sep 17 00:00:00 2001 From: Daniel Kreuer Date: Tue, 7 Dec 2021 18:02:44 +0100 Subject: [PATCH 3/9] Add configuration for mutation testing with infection-php --- infection.json.dist | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 infection.json.dist diff --git a/infection.json.dist b/infection.json.dist new file mode 100644 index 00000000..be36448e --- /dev/null +++ b/infection.json.dist @@ -0,0 +1,16 @@ +{ + "$schema": "vendor/infection/infection/resources/schema.json", + "source": { + "directories": [ + "src" + ] + }, + "logs": { + "text": "php://stderr" + }, + "mutators": { + "@default": true + }, + "minMsi": 95, + "minCoveredMsi": 95 +} From 387d3999dc2381b2594e710aee9b0069bb975142 Mon Sep 17 00:00:00 2001 From: Daniel Kreuer Date: Tue, 7 Dec 2021 18:02:55 +0100 Subject: [PATCH 4/9] Add mutation testing pipeline --- .github/workflows/infection.yml | 70 +++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 .github/workflows/infection.yml diff --git a/.github/workflows/infection.yml b/.github/workflows/infection.yml new file mode 100644 index 00000000..422ccf78 --- /dev/null +++ b/.github/workflows/infection.yml @@ -0,0 +1,70 @@ +# https://help.github.com/en/categories/automating-your-workflow-with-github-actions + +name: "Mutation tests" + +on: + pull_request: + push: + branches: + - "master" + +jobs: + mutation-tests: + name: "Mutation tests" + + runs-on: ${{ matrix.operating-system }} + + strategy: + matrix: + dependencies: + - "locked" + php-version: + - "7.4" + - "8.0" + - "8.1" + operating-system: + - "ubuntu-latest" + + steps: + - name: "Checkout" + uses: "actions/checkout@v2" + + - name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + coverage: "pcov" + php-version: "${{ matrix.php-version }}" + ini-values: memory_limit=-1, zend.assertions=1 + + - name: Get Composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-dir)" + + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: composer-${{ runner.os }}-${{ matrix.php-version }}-${{ hashFiles('composer.*') }}-${{ matrix.dependencies }} + restore-keys: | + composer-${{ runner.os }}-${{ matrix.php-version }}-${{ hashFiles('composer.*') }}- + composer-${{ runner.os }}-${{ matrix.php-version }}- + composer-${{ runner.os }}- + composer- + + - name: "Install lowest dependencies" + if: ${{ matrix.dependencies == 'lowest' }} + run: "composer update --prefer-lowest --no-interaction --no-progress --no-suggest" + + - name: "Install highest dependencies" + if: ${{ matrix.dependencies == 'highest' }} + run: "composer update --no-interaction --no-progress --no-suggest" + + - name: "Install locked dependencies" + if: ${{ matrix.dependencies == 'locked' }} + run: "composer install --no-interaction --no-progress --no-suggest" + + - name: "Infection" + continue-on-error: true + run: "vendor/bin/roave-infection-static-analysis-plugin --threads=$(nproc)" + env: + INFECTION_BADGE_API_KEY: ${{ secrets.INFECTION_BADGE_API_KEY }} From 636ff58075c5b08ca2b10ef718480753c1a27acc Mon Sep 17 00:00:00 2001 From: Daniel Kreuer Date: Tue, 7 Dec 2021 18:26:00 +0100 Subject: [PATCH 5/9] Squash mutations --- src/ComposerRequireChecker/Cli/Options.php | 2 +- .../Cli/ApplicationTest.php | 8 ++++++++ .../Cli/OptionsTest.php | 20 +++++++++++++++++-- .../Cli/ResultsWriter/CliTextTest.php | 2 ++ 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/ComposerRequireChecker/Cli/Options.php b/src/ComposerRequireChecker/Cli/Options.php index 351f984e..f5fa70e9 100644 --- a/src/ComposerRequireChecker/Cli/Options.php +++ b/src/ComposerRequireChecker/Cli/Options.php @@ -122,6 +122,6 @@ public function setScanFiles(array $scanFiles): void private function getCamelCase(string $string): string { - return ucfirst(str_replace(' ', '', ucwords(str_replace('-', ' ', $string)))); + return str_replace(' ', '', ucwords(str_replace('-', ' ', $string))); } } diff --git a/test/ComposerRequireCheckerTest/Cli/ApplicationTest.php b/test/ComposerRequireCheckerTest/Cli/ApplicationTest.php index 21edc990..3e85b90f 100644 --- a/test/ComposerRequireCheckerTest/Cli/ApplicationTest.php +++ b/test/ComposerRequireCheckerTest/Cli/ApplicationTest.php @@ -22,4 +22,12 @@ public function testCheckCommandExists(): void $this->assertTrue($this->application->has('check')); $this->assertInstanceOf(Command::class, $this->application->get('check')); } + + public function testCheckCommandIsDefaultCommand(): void + { + self::assertStringContainsString( + 'check', + $this->application->getDefinition()->getOption('help')->getDescription() + ); + } } diff --git a/test/ComposerRequireCheckerTest/Cli/OptionsTest.php b/test/ComposerRequireCheckerTest/Cli/OptionsTest.php index 9ab0dfc2..b095d9c2 100644 --- a/test/ComposerRequireCheckerTest/Cli/OptionsTest.php +++ b/test/ComposerRequireCheckerTest/Cli/OptionsTest.php @@ -5,6 +5,7 @@ namespace ComposerRequireCheckerTest\Cli; use ComposerRequireChecker\Cli\Options; +use InvalidArgumentException; use PHPUnit\Framework\TestCase; use function file_get_contents; @@ -45,9 +46,24 @@ public function testOptionsFileRepresentsDefaults(): void public function testThrowsExceptionForUnknownOptions(): void { - $this->expectException('InvalidArgumentException'); - $options = new Options([ + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('foo-bar is not a known option - there is no method setFooBar'); + + new Options([ 'foo-bar' => ['foo', 'bar'], ]); } + + public function testPublicSetters(): void + { + $options = new Options(); + + $options->setSymbolWhitelist(['foo', 'bar']); + $options->setScanFiles(['one', 'two', 'three']); + $options->setPhpCoreExtensions(['ext-one', 'ext-two']); + + self::assertSame(['foo', 'bar'], $options->getSymbolWhitelist()); + self::assertSame(['one', 'two', 'three'], $options->getScanFiles()); + self::assertSame(['ext-one', 'ext-two'], $options->getPhpCoreExtensions()); + } } diff --git a/test/ComposerRequireCheckerTest/Cli/ResultsWriter/CliTextTest.php b/test/ComposerRequireCheckerTest/Cli/ResultsWriter/CliTextTest.php index 1399c480..b0a1b2e8 100644 --- a/test/ComposerRequireCheckerTest/Cli/ResultsWriter/CliTextTest.php +++ b/test/ComposerRequireCheckerTest/Cli/ResultsWriter/CliTextTest.php @@ -80,6 +80,8 @@ public function testWriteReportQuietWithWriteCallable(): void $buffer = $this->output->fetch(); self::assertStringContainsString('The following 3 unknown symbols were found:', $buffer); self::assertStringNotContainsString('Foo', $buffer); + self::assertStringContainsString('Unknown Symbol', $output); + self::assertStringContainsString('Guessed Dependency', $output); self::assertStringContainsString('Foo', $output); self::assertStringContainsString('| opcache_get_status', $output); self::assertStringContainsString('| ext-opcache', $output); From 796c40680797da7259acf2cf6f210ecd1fa6db0c Mon Sep 17 00:00:00 2001 From: Daniel Kreuer Date: Tue, 7 Dec 2021 19:11:59 +0100 Subject: [PATCH 6/9] Squash psalm errors to have perfect baseline for mutation testing --- baseline.xml | 13 --------- .../Cli/CheckCommand.php | 6 ++-- src/ComposerRequireChecker/Cli/Options.php | 5 +++- .../DefinedExtensionsResolver.php | 3 +- .../Exception/FileParseFailed.php | 7 ++++- ...erPackageDirectDependenciesSourceFiles.php | 24 ++++++---------- .../LocateComposerPackageSourceFiles.php | 26 ++++++++++++++++- src/ComposerRequireChecker/JsonLoader.php | 5 ++++ .../NodeVisitor/DefinedSymbolCollector.php | 17 +++++++++-- .../Exception/FileParseFailedTest.php | 28 +++++++++++++++++++ 10 files changed, 98 insertions(+), 36 deletions(-) create mode 100644 test/ComposerRequireCheckerTest/Exception/FileParseFailedTest.php diff --git a/baseline.xml b/baseline.xml index 55184d2c..cd52ff27 100644 --- a/baseline.xml +++ b/baseline.xml @@ -3,17 +3,4 @@ - - - $composerData['autoload']['classmap'] ?? [] - $composerData['autoload']['files'] ?? [] - $composerData['autoload']['psr-0'] ?? [] - $composerData['autoload']['psr-4'] ?? [] - - - - - $node->namespacedName - - diff --git a/src/ComposerRequireChecker/Cli/CheckCommand.php b/src/ComposerRequireChecker/Cli/CheckCommand.php index 7eee6d27..9d982b33 100644 --- a/src/ComposerRequireChecker/Cli/CheckCommand.php +++ b/src/ComposerRequireChecker/Cli/CheckCommand.php @@ -31,7 +31,6 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use Webmozart\Assert\Assert; use function array_combine; use function array_diff; @@ -46,6 +45,9 @@ use function realpath; use function sprintf; +/** + * @psalm-import-type ComposerData from LocateComposerPackageSourceFiles + */ class CheckCommand extends Command { public const NAME = 'check'; @@ -232,13 +234,13 @@ private function getCheckOptions(InputInterface $input): Options } $config = JsonLoader::getData($fileName); - Assert::isMap($config); return new Options($config); } /** * @return array + * @psalm-return ComposerData * * @throws InvalidJson * @throws NotReadable diff --git a/src/ComposerRequireChecker/Cli/Options.php b/src/ComposerRequireChecker/Cli/Options.php index f5fa70e9..4f4b159a 100644 --- a/src/ComposerRequireChecker/Cli/Options.php +++ b/src/ComposerRequireChecker/Cli/Options.php @@ -4,13 +4,16 @@ namespace ComposerRequireChecker\Cli; +use ComposerRequireChecker\FileLocator\LocateComposerPackageSourceFiles; use InvalidArgumentException; use function method_exists; use function str_replace; -use function ucfirst; use function ucwords; +/** + * @psalm-import-type ComposerData from LocateComposerPackageSourceFiles + */ class Options { /** @var array */ diff --git a/src/ComposerRequireChecker/DefinedExtensionsResolver/DefinedExtensionsResolver.php b/src/ComposerRequireChecker/DefinedExtensionsResolver/DefinedExtensionsResolver.php index e76dd36f..1b1be2c7 100644 --- a/src/ComposerRequireChecker/DefinedExtensionsResolver/DefinedExtensionsResolver.php +++ b/src/ComposerRequireChecker/DefinedExtensionsResolver/DefinedExtensionsResolver.php @@ -4,6 +4,7 @@ namespace ComposerRequireChecker\DefinedExtensionsResolver; +use function array_keys; use function array_merge; use function file_get_contents; use function json_decode; @@ -25,7 +26,7 @@ public function __invoke(string $composerJson, array $phpCoreExtensions = []): a $extensions = []; $addPhpCoreExtensions = false; - foreach ($requires as $require => $version) { + foreach (array_keys($requires) as $require) { if ($require === 'php' || $require === 'php-64bit') { $addPhpCoreExtensions = true; continue; diff --git a/src/ComposerRequireChecker/Exception/FileParseFailed.php b/src/ComposerRequireChecker/Exception/FileParseFailed.php index cd51114f..a1acb8fb 100755 --- a/src/ComposerRequireChecker/Exception/FileParseFailed.php +++ b/src/ComposerRequireChecker/Exception/FileParseFailed.php @@ -13,7 +13,12 @@ class FileParseFailed extends RuntimeException { public function __construct(string $file, ?Throwable $previous = null) { - $msg = sprintf('Parsing the file [%s] resulted in an error: %s', $file, $previous->getMessage()); + $msg = sprintf( + 'Parsing the file [%s] resulted in an error: %s', + $file, + $previous !== null ? $previous->getMessage() : '' + ); + parent::__construct($msg, 0, $previous); } } diff --git a/src/ComposerRequireChecker/FileLocator/LocateComposerPackageDirectDependenciesSourceFiles.php b/src/ComposerRequireChecker/FileLocator/LocateComposerPackageDirectDependenciesSourceFiles.php index 3ca69575..c29acb82 100644 --- a/src/ComposerRequireChecker/FileLocator/LocateComposerPackageDirectDependenciesSourceFiles.php +++ b/src/ComposerRequireChecker/FileLocator/LocateComposerPackageDirectDependenciesSourceFiles.php @@ -10,10 +10,12 @@ use Generator; use function array_key_exists; -use function assert; +use function array_keys; use function dirname; -use function is_string; +/** + * @psalm-import-type ComposerData from LocateComposerPackageSourceFiles + */ final class LocateComposerPackageDirectDependenciesSourceFiles { public function __invoke(string $composerJsonPath): Generator @@ -22,14 +24,9 @@ public function __invoke(string $composerJsonPath): Generator $composerJson = JsonLoader::getData($composerJsonPath); $configVendorDir = $composerJson['config']['vendor-dir'] ?? 'vendor'; - assert(is_string($configVendorDir)); - $vendorDirs = []; - - /** - * @var mixed $vendorRequiredVersion - */ - foreach ($composerJson['require'] ?? [] as $vendorName => $vendorRequiredVersion) { - assert(is_string($vendorName)); + $vendorDirs = []; + + foreach (array_keys($composerJson['require'] ?? []) as $vendorName) { $vendorDirs[$vendorName] = $packageDir . '/' . $configVendorDir . '/' . $vendorName; } @@ -47,7 +44,7 @@ public function __invoke(string $composerJsonPath): Generator /** * Lookup each vendor package's composer.json info from installed.json * - * @return array> Keys are the package name and value is the composer.json as an array + * @return array Keys are the package name and value is the composer.json as an array * * @throws DependenciesNotInstalled When composer install/update has not been run. */ @@ -63,12 +60,9 @@ private function getInstalledPackages(string $vendorDir): array $installedPackages = []; - /** @var array $packages */ + /** @var array $packages */ $packages = $installedData['packages'] ?? $installedData; - /** - * @var array{name: string} $vendorJson - */ foreach ($packages as $vendorJson) { $vendorName = $vendorJson['name']; $installedPackages[$vendorName] = $vendorJson; diff --git a/src/ComposerRequireChecker/FileLocator/LocateComposerPackageSourceFiles.php b/src/ComposerRequireChecker/FileLocator/LocateComposerPackageSourceFiles.php index 1bca76fc..023de867 100644 --- a/src/ComposerRequireChecker/FileLocator/LocateComposerPackageSourceFiles.php +++ b/src/ComposerRequireChecker/FileLocator/LocateComposerPackageSourceFiles.php @@ -16,15 +16,39 @@ use function ltrim; use function str_replace; +/** + * @psalm-type ComposerConfig = array{vendor-dir?: string} + * @psalm-type ComposerRequire = array + * @psalm-type ComposerAutoload = array{ + * exclude-from-classmap?: list, + * classmap?: list, + * files?: list, + * psr-0?: list, + * psr-4?: list + * } + * @psalm-type ComposerPackageData = array{ + * name: string, + * require?: ComposerConfig, + * autoload?: ComposerAutoload, + * config?: ComposerConfig + * } + * @psalm-type ComposerData = array{ + * name: string, + * require?: ComposerConfig, + * autoload?: ComposerAutoload, + * config?: ComposerConfig, + * packages?: list + * } + */ final class LocateComposerPackageSourceFiles { /** * @param mixed[] $composerData The contents of composer.json for a package * @param string $packageDir The path to package + * @psalm-param ComposerData $composerData The contents of composer.json for a package */ public function __invoke(array $composerData, string $packageDir): Generator { - /** @var array|null $blacklist */ $blacklist = $composerData['autoload']['exclude-from-classmap'] ?? null; yield from $this->locateFilesInClassmapDefinitions( diff --git a/src/ComposerRequireChecker/JsonLoader.php b/src/ComposerRequireChecker/JsonLoader.php index c998052b..d3d62695 100644 --- a/src/ComposerRequireChecker/JsonLoader.php +++ b/src/ComposerRequireChecker/JsonLoader.php @@ -6,6 +6,7 @@ use ComposerRequireChecker\Exception\InvalidJson; use ComposerRequireChecker\Exception\NotReadable; +use ComposerRequireChecker\FileLocator\LocateComposerPackageSourceFiles; use InvalidArgumentException; use Throwable; use Webmozart\Assert\Assert; @@ -17,11 +18,14 @@ /** * @internal + * + * @psalm-import-type ComposerData from LocateComposerPackageSourceFiles */ class JsonLoader { /** * @return array + * @psalm-return ComposerData * * @throws InvalidJson * @throws NotReadable @@ -37,6 +41,7 @@ public static function getData(string $path): array throw new InvalidJson('error parsing ' . $path . ': ' . $exception->getMessage(), 0, $exception); } + /** @psalm-var ComposerData $decodedData */ return $decodedData; } diff --git a/src/ComposerRequireChecker/NodeVisitor/DefinedSymbolCollector.php b/src/ComposerRequireChecker/NodeVisitor/DefinedSymbolCollector.php index 988a221d..b4b53363 100644 --- a/src/ComposerRequireChecker/NodeVisitor/DefinedSymbolCollector.php +++ b/src/ComposerRequireChecker/NodeVisitor/DefinedSymbolCollector.php @@ -10,6 +10,7 @@ use function array_keys; use function get_class; +use function property_exists; use function sprintf; final class DefinedSymbolCollector extends NodeVisitorAbstract @@ -126,6 +127,10 @@ private function recordDefinedConstDefinition(Node $node): void } } + if (! ($node->args[0] instanceof Node\Arg)) { + return; + } + if (! ($node->args[0]->value instanceof Node\Scalar\String_)) { return; } @@ -133,9 +138,17 @@ private function recordDefinedConstDefinition(Node $node): void $this->recordDefinitionOfStringSymbol($node->args[0]->value->value); } + /** + * @psalm-param Node\Stmt\Function_|Node\Stmt\ClassLike|Node\Const_ $node + */ private function recordDefinitionOf(Node $node): void { - if (! isset($node->namespacedName)) { + $namespacedName = null; + if (property_exists($node, 'namespacedName')) { + $namespacedName = $node->namespacedName; + } + + if ($namespacedName === null) { throw new UnexpectedValueException( sprintf( 'Given node of type "%s" (defined at line %s)does not have an assigned "namespacedName" property: ' @@ -146,7 +159,7 @@ private function recordDefinitionOf(Node $node): void ); } - $this->recordDefinitionOfStringSymbol((string) $node->namespacedName); + $this->recordDefinitionOfStringSymbol((string) $namespacedName); } private function recordDefinitionOfStringSymbol(string $symbolName): void diff --git a/test/ComposerRequireCheckerTest/Exception/FileParseFailedTest.php b/test/ComposerRequireCheckerTest/Exception/FileParseFailedTest.php new file mode 100644 index 00000000..6bfa5d76 --- /dev/null +++ b/test/ComposerRequireCheckerTest/Exception/FileParseFailedTest.php @@ -0,0 +1,28 @@ +getCode()); + self::assertSame( + 'Parsing the file [file] resulted in an error: Dummy Exception', + $subject->getMessage() + ); + } +} From 48cb4f0617e52fd14f77637b651430e4dc7eea84 Mon Sep 17 00:00:00 2001 From: Daniel Kreuer Date: Thu, 9 Dec 2021 10:26:58 +0100 Subject: [PATCH 7/9] Make pipeline fail on mutation tests --- .github/workflows/infection.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/infection.yml b/.github/workflows/infection.yml index 422ccf78..bd298a49 100644 --- a/.github/workflows/infection.yml +++ b/.github/workflows/infection.yml @@ -64,7 +64,6 @@ jobs: run: "composer install --no-interaction --no-progress --no-suggest" - name: "Infection" - continue-on-error: true run: "vendor/bin/roave-infection-static-analysis-plugin --threads=$(nproc)" env: INFECTION_BADGE_API_KEY: ${{ secrets.INFECTION_BADGE_API_KEY }} From 91ba74ef43602926e1ed61a965b774760882f981 Mon Sep 17 00:00:00 2001 From: Daniel Kreuer Date: Thu, 9 Dec 2021 10:30:02 +0100 Subject: [PATCH 8/9] Raise timeout for mutation tests The timeout must be raised currently to not have integration tests (which currently count to coverage) being skipped. --- infection.json.dist | 1 + 1 file changed, 1 insertion(+) diff --git a/infection.json.dist b/infection.json.dist index be36448e..c35e8541 100644 --- a/infection.json.dist +++ b/infection.json.dist @@ -5,6 +5,7 @@ "src" ] }, + "timeout": 30, "logs": { "text": "php://stderr" }, From 2c6fe9c82d77ddee74a6d5c732ccf0015c061d87 Mon Sep 17 00:00:00 2001 From: Daniel Kreuer Date: Thu, 9 Dec 2021 11:40:08 +0100 Subject: [PATCH 9/9] Squash mutants --- .../Cli/ApplicationHeaderWriter.php | 33 +++++++++++++++++ .../Cli/CheckCommand.php | 29 +++++++++------ .../LocateDefinedSymbolsFromASTRoots.php | 2 +- .../LocateComposerPackageSourceFiles.php | 2 +- src/ComposerRequireChecker/JsonLoader.php | 6 ++-- .../NodeVisitor/DefinedSymbolCollector.php | 2 +- .../NodeVisitor/UsedSymbolCollector.php | 2 +- .../Cli/ApplicationHeaderWriterTest.php | 36 +++++++++++++++++++ .../Cli/CheckCommandTest.php | 3 ++ .../NodeVisitor/UsedSymbolCollectorTest.php | 18 ++++++---- 10 files changed, 109 insertions(+), 24 deletions(-) create mode 100644 src/ComposerRequireChecker/Cli/ApplicationHeaderWriter.php create mode 100644 test/ComposerRequireCheckerTest/Cli/ApplicationHeaderWriterTest.php diff --git a/src/ComposerRequireChecker/Cli/ApplicationHeaderWriter.php b/src/ComposerRequireChecker/Cli/ApplicationHeaderWriter.php new file mode 100644 index 00000000..3fec2ad0 --- /dev/null +++ b/src/ComposerRequireChecker/Cli/ApplicationHeaderWriter.php @@ -0,0 +1,33 @@ +application = $application; + } + + public function __invoke(OutputInterface $output): void + { + if ($output->isQuiet()) { + return; + } + + if ($this->application === null) { + $output->writeln('Unknown version'); + + return; + } + + $output->writeln($this->application->getLongVersion()); + } +} diff --git a/src/ComposerRequireChecker/Cli/CheckCommand.php b/src/ComposerRequireChecker/Cli/CheckCommand.php index 9d982b33..96caf6e4 100644 --- a/src/ComposerRequireChecker/Cli/CheckCommand.php +++ b/src/ComposerRequireChecker/Cli/CheckCommand.php @@ -106,10 +106,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); } - if (! $output->isQuiet()) { - $application = $this->getApplication(); - $output->writeln($application !== null ? $application->getLongVersion() : 'Unknown version'); - } + (new ApplicationHeaderWriter($this->getApplication()))->__invoke($output); $composerJsonArgument = $input->getArgument('composer-json'); @@ -180,13 +177,27 @@ protected function execute(InputInterface $input, OutputInterface $output): int $options->getSymbolWhitelist() ); + // pcov which is used for coverage does not detect executed code in anonymous functions used as callable + // therefore we require to have closure class. + $outputWrapper = new class ($output) { + private OutputInterface $output; + + public function __construct(OutputInterface $output) + { + $this->output = $output; + } + + public function __invoke(string $string): void + { + $this->output->write($string, false, OutputInterface::VERBOSITY_QUIET); + } + }; + switch ($input->getOption('output')) { case 'json': $application = $this->getApplication(); $resultsWriter = new CliJson( - static function (string $string) use ($output): void { - $output->write($string, false, OutputInterface::VERBOSITY_QUIET | OutputInterface::OUTPUT_RAW); - }, + $outputWrapper, $application !== null ? $application->getVersion() : 'Unknown version', static fn () => new DateTimeImmutable() ); @@ -194,9 +205,7 @@ static function (string $string) use ($output): void { case 'text': $resultsWriter = new CliText( $output, - static function (string $string) use ($output): void { - $output->write($string, false, OutputInterface::VERBOSITY_QUIET | OutputInterface::OUTPUT_RAW); - } + $outputWrapper ); break; default: diff --git a/src/ComposerRequireChecker/DefinedSymbolsLocator/LocateDefinedSymbolsFromASTRoots.php b/src/ComposerRequireChecker/DefinedSymbolsLocator/LocateDefinedSymbolsFromASTRoots.php index 70baada9..4ae61526 100644 --- a/src/ComposerRequireChecker/DefinedSymbolsLocator/LocateDefinedSymbolsFromASTRoots.php +++ b/src/ComposerRequireChecker/DefinedSymbolsLocator/LocateDefinedSymbolsFromASTRoots.php @@ -19,7 +19,7 @@ final class LocateDefinedSymbolsFromASTRoots /** * @param Traversable> $ASTs a series of AST roots * - * @return string[] all the found symbols + * @return list all the found symbols */ public function __invoke(Traversable $ASTs): array { diff --git a/src/ComposerRequireChecker/FileLocator/LocateComposerPackageSourceFiles.php b/src/ComposerRequireChecker/FileLocator/LocateComposerPackageSourceFiles.php index 023de867..7d1d4a61 100644 --- a/src/ComposerRequireChecker/FileLocator/LocateComposerPackageSourceFiles.php +++ b/src/ComposerRequireChecker/FileLocator/LocateComposerPackageSourceFiles.php @@ -75,7 +75,7 @@ public function __invoke(array $composerData, string $packageDir): Generator /** * @param array $sourceDirs * - * @return array + * @return list */ private function getFilePaths(array $sourceDirs, string $packageDir): array { diff --git a/src/ComposerRequireChecker/JsonLoader.php b/src/ComposerRequireChecker/JsonLoader.php index d3d62695..9770491a 100644 --- a/src/ComposerRequireChecker/JsonLoader.php +++ b/src/ComposerRequireChecker/JsonLoader.php @@ -11,7 +11,9 @@ use Throwable; use Webmozart\Assert\Assert; +use function assert; use function file_get_contents; +use function is_string; use function json_decode; use const JSON_THROW_ON_ERROR; @@ -56,9 +58,7 @@ private static function getFileContentFromPath(string $path): string $content = file_get_contents($path); - if ($content === false) { - throw new NotReadable('unable to read ' . $path); - } + assert(is_string($content)); return $content; } diff --git a/src/ComposerRequireChecker/NodeVisitor/DefinedSymbolCollector.php b/src/ComposerRequireChecker/NodeVisitor/DefinedSymbolCollector.php index b4b53363..7b6dea4b 100644 --- a/src/ComposerRequireChecker/NodeVisitor/DefinedSymbolCollector.php +++ b/src/ComposerRequireChecker/NodeVisitor/DefinedSymbolCollector.php @@ -33,7 +33,7 @@ public function beforeTraverse(array $nodes) } /** - * @return array + * @return list */ public function getDefinedSymbols(): array { diff --git a/src/ComposerRequireChecker/NodeVisitor/UsedSymbolCollector.php b/src/ComposerRequireChecker/NodeVisitor/UsedSymbolCollector.php index 3028e04d..dfc9b8ec 100644 --- a/src/ComposerRequireChecker/NodeVisitor/UsedSymbolCollector.php +++ b/src/ComposerRequireChecker/NodeVisitor/UsedSymbolCollector.php @@ -21,7 +21,7 @@ public function __construct() } /** - * @return array + * @return list */ public function getCollectedSymbols(): array { diff --git a/test/ComposerRequireCheckerTest/Cli/ApplicationHeaderWriterTest.php b/test/ComposerRequireCheckerTest/Cli/ApplicationHeaderWriterTest.php new file mode 100644 index 00000000..7fadf8b4 --- /dev/null +++ b/test/ComposerRequireCheckerTest/Cli/ApplicationHeaderWriterTest.php @@ -0,0 +1,36 @@ +__invoke($output); + + self::assertStringContainsString('Unknown version', $output->fetch()); + } + + public function testWithApplication(): void + { + $output = new BufferedOutput(); + + $application = new Application('APPNAME', 'APPVERSION'); + + (new ApplicationHeaderWriter($application))->__invoke($output); + + self::assertStringContainsString('APPNAME APPVERSION', $output->fetch()); + } +} diff --git a/test/ComposerRequireCheckerTest/Cli/CheckCommandTest.php b/test/ComposerRequireCheckerTest/Cli/CheckCommandTest.php index 15be1368..34b55d2c 100644 --- a/test/ComposerRequireCheckerTest/Cli/CheckCommandTest.php +++ b/test/ComposerRequireCheckerTest/Cli/CheckCommandTest.php @@ -20,6 +20,7 @@ use function version_compare; use const JSON_THROW_ON_ERROR; +use const PHP_EOL; use const PHP_VERSION; final class CheckCommandTest extends TestCase @@ -112,6 +113,7 @@ public function testUnknownSymbolsFoundJsonReport(): void ], $actual['unknown-symbols'] ); + self::assertStringEndsNotWith(PHP_EOL, $display); } public function testUnknownSymbolsFoundTextReport(): void @@ -131,6 +133,7 @@ public function testUnknownSymbolsFoundTextReport(): void $this->assertStringContainsString('filter_var', $display); $this->assertStringContainsString('Foo\Bar\Baz', $display); $this->assertStringContainsString('libxml_clear_errors', $display); + $this->assertStringEndsNotWith(PHP_EOL . PHP_EOL, $display); } public function testSelfCheckShowsNoErrors(): void diff --git a/test/ComposerRequireCheckerTest/NodeVisitor/UsedSymbolCollectorTest.php b/test/ComposerRequireCheckerTest/NodeVisitor/UsedSymbolCollectorTest.php index 18e45241..375e979a 100644 --- a/test/ComposerRequireCheckerTest/NodeVisitor/UsedSymbolCollectorTest.php +++ b/test/ComposerRequireCheckerTest/NodeVisitor/UsedSymbolCollectorTest.php @@ -13,6 +13,7 @@ use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Expr\StaticPropertyFetch; use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Identifier; use PhpParser\Node\Name; use PhpParser\Node\Param; use PhpParser\Node\Stmt\Catch_; @@ -169,18 +170,21 @@ public function testFunctionCallUsage(): void public function testFunctionParameterType(): void { - $functionName = new Name('foo'); - $node = new Function_($functionName); - $node->name = $functionName; - $param = new Param(new Variable('bar')); - $param->type = new Name('Baz'); - $node->params = [$param]; + $functionName = new Name('foo'); + $node = new Function_($functionName); + $node->name = $functionName; + $param = new Param(new Variable('bar')); + $param->type = new Name('Baz'); + $anotherParam = new Param(new Variable('quux')); + $anotherParam->type = new Identifier('foo'); + $node->params = [$param, $anotherParam]; $this->visitor->enterNode($node); $symbols = $this->visitor->getCollectedSymbols(); - $this->assertCount(1, $symbols); + $this->assertCount(2, $symbols); $this->assertContains('Baz', $symbols); + $this->assertContains('foo', $symbols); } public function testMethodParameterType(): void